All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v8 0/8] imx25 adc and touchscreen driver
@ 2015-11-16 12:01 ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam, Markus Pargmann

Hi,

welcome to the next round of the never ending series ;). The last version v7
was sent roughly 8 months ago. The first version at least 1.5 years ago.

This series adds two drivers for ADC and TSC of the imx25 and a MFD driver to
connect these.

Due to the larger changes in this version I had to remove some of the acks
again.

@Hartmut:
Thanks for your comments. I updated the drivers accordingly.

Changes in v8:
 - scale was defined as a shared property of the ADC although the scale can be
   different for each ADC channel. v8 fixes that.
 - Some smaller modifications of the code style in gcq code.
 - Redesign of the regulator handling in the gcq code. The driver does now
   handle regulators "vref-ext", "vref-xp" and "vref-yp" as positive voltage
   reference.  Also the calculation of the scale property based on these
   voltage reference regulators was fixed. The code now uses
   regulator_get_optional() to avoid dummy regulators.
 - The missing regulator_disable() was added.

 Thanks to Jürgen the time behavior of the units is now fixed and within specs:
 - Fixed clock rates for the whole unit regarding to the reference manual.
 - Fixed clock/time calculations for settling times in the touchscreen driver.

Best Regards,

Markus


Denis Carikli (2):
  ARM: dts: imx25: Add TSC and ADC support
  ARM: imx_v4_v5_defconfig: Add I.MX25 Touchscreen controller and ADC
    support.

Markus Pargmann (6):
  ARM: dt: Binding documentation for imx25 ADC/TSC
  ARM: dt: Binding documentation for imx25 GCQ
  ARM: dt: Binding documentation for imx25 touchscreen controller
  mfd: fsl imx25 Touchscreen ADC driver
  iio: adc: fsl,imx25-gcq driver
  input: touchscreen: imx25 tcq driver

 .../devicetree/bindings/iio/adc/fsl,imx25-gcq.txt  |  58 ++
 .../bindings/input/touchscreen/fsl-mx25-tcq.txt    |  29 +
 .../devicetree/bindings/mfd/fsl-imx25-tsadc.txt    |  46 ++
 arch/arm/boot/dts/imx25.dtsi                       |  30 +-
 arch/arm/configs/imx_v4_v5_defconfig               |   4 +
 drivers/iio/adc/Kconfig                            |   7 +
 drivers/iio/adc/Makefile                           |   1 +
 drivers/iio/adc/fsl-imx25-gcq.c                    | 415 ++++++++++++++
 drivers/input/touchscreen/Kconfig                  |   6 +
 drivers/input/touchscreen/Makefile                 |   1 +
 drivers/input/touchscreen/fsl-imx25-tcq.c          | 600 +++++++++++++++++++++
 drivers/mfd/Kconfig                                |   9 +
 drivers/mfd/Makefile                               |   2 +
 drivers/mfd/fsl-imx25-tsadc.c                      | 204 +++++++
 include/dt-bindings/iio/adc/fsl-imx25-gcq.h        |  18 +
 include/linux/mfd/imx25-tsadc.h                    | 140 +++++
 16 files changed, 1567 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
 create mode 100644 Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
 create mode 100644 drivers/iio/adc/fsl-imx25-gcq.c
 create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
 create mode 100644 drivers/mfd/fsl-imx25-tsadc.c
 create mode 100644 include/dt-bindings/iio/adc/fsl-imx25-gcq.h
 create mode 100644 include/linux/mfd/imx25-tsadc.h

-- 
2.6.1

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v8 0/8] imx25 adc and touchscreen driver
@ 2015-11-16 12:01 ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam, Markus Pargmann

Hi,

welcome to the next round of the never ending series ;). The last version v7
was sent roughly 8 months ago. The first version at least 1.5 years ago.

This series adds two drivers for ADC and TSC of the imx25 and a MFD driver to
connect these.

Due to the larger changes in this version I had to remove some of the acks
again.

@Hartmut:
Thanks for your comments. I updated the drivers accordingly.

Changes in v8:
 - scale was defined as a shared property of the ADC although the scale can be
   different for each ADC channel. v8 fixes that.
 - Some smaller modifications of the code style in gcq code.
 - Redesign of the regulator handling in the gcq code. The driver does now
   handle regulators "vref-ext", "vref-xp" and "vref-yp" as positive voltage
   reference.  Also the calculation of the scale property based on these
   voltage reference regulators was fixed. The code now uses
   regulator_get_optional() to avoid dummy regulators.
 - The missing regulator_disable() was added.

 Thanks to Jürgen the time behavior of the units is now fixed and within specs:
 - Fixed clock rates for the whole unit regarding to the reference manual.
 - Fixed clock/time calculations for settling times in the touchscreen driver.

Best Regards,

Markus


Denis Carikli (2):
  ARM: dts: imx25: Add TSC and ADC support
  ARM: imx_v4_v5_defconfig: Add I.MX25 Touchscreen controller and ADC
    support.

Markus Pargmann (6):
  ARM: dt: Binding documentation for imx25 ADC/TSC
  ARM: dt: Binding documentation for imx25 GCQ
  ARM: dt: Binding documentation for imx25 touchscreen controller
  mfd: fsl imx25 Touchscreen ADC driver
  iio: adc: fsl,imx25-gcq driver
  input: touchscreen: imx25 tcq driver

 .../devicetree/bindings/iio/adc/fsl,imx25-gcq.txt  |  58 ++
 .../bindings/input/touchscreen/fsl-mx25-tcq.txt    |  29 +
 .../devicetree/bindings/mfd/fsl-imx25-tsadc.txt    |  46 ++
 arch/arm/boot/dts/imx25.dtsi                       |  30 +-
 arch/arm/configs/imx_v4_v5_defconfig               |   4 +
 drivers/iio/adc/Kconfig                            |   7 +
 drivers/iio/adc/Makefile                           |   1 +
 drivers/iio/adc/fsl-imx25-gcq.c                    | 415 ++++++++++++++
 drivers/input/touchscreen/Kconfig                  |   6 +
 drivers/input/touchscreen/Makefile                 |   1 +
 drivers/input/touchscreen/fsl-imx25-tcq.c          | 600 +++++++++++++++++++++
 drivers/mfd/Kconfig                                |   9 +
 drivers/mfd/Makefile                               |   2 +
 drivers/mfd/fsl-imx25-tsadc.c                      | 204 +++++++
 include/dt-bindings/iio/adc/fsl-imx25-gcq.h        |  18 +
 include/linux/mfd/imx25-tsadc.h                    | 140 +++++
 16 files changed, 1567 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
 create mode 100644 Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
 create mode 100644 drivers/iio/adc/fsl-imx25-gcq.c
 create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
 create mode 100644 drivers/mfd/fsl-imx25-tsadc.c
 create mode 100644 include/dt-bindings/iio/adc/fsl-imx25-gcq.h
 create mode 100644 include/linux/mfd/imx25-tsadc.h

-- 
2.6.1

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

* [PATCH v8 0/8] imx25 adc and touchscreen driver
@ 2015-11-16 12:01 ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

welcome to the next round of the never ending series ;). The last version v7
was sent roughly 8 months ago. The first version at least 1.5 years ago.

This series adds two drivers for ADC and TSC of the imx25 and a MFD driver to
connect these.

Due to the larger changes in this version I had to remove some of the acks
again.

@Hartmut:
Thanks for your comments. I updated the drivers accordingly.

Changes in v8:
 - scale was defined as a shared property of the ADC although the scale can be
   different for each ADC channel. v8 fixes that.
 - Some smaller modifications of the code style in gcq code.
 - Redesign of the regulator handling in the gcq code. The driver does now
   handle regulators "vref-ext", "vref-xp" and "vref-yp" as positive voltage
   reference.  Also the calculation of the scale property based on these
   voltage reference regulators was fixed. The code now uses
   regulator_get_optional() to avoid dummy regulators.
 - The missing regulator_disable() was added.

 Thanks to J?rgen the time behavior of the units is now fixed and within specs:
 - Fixed clock rates for the whole unit regarding to the reference manual.
 - Fixed clock/time calculations for settling times in the touchscreen driver.

Best Regards,

Markus


Denis Carikli (2):
  ARM: dts: imx25: Add TSC and ADC support
  ARM: imx_v4_v5_defconfig: Add I.MX25 Touchscreen controller and ADC
    support.

Markus Pargmann (6):
  ARM: dt: Binding documentation for imx25 ADC/TSC
  ARM: dt: Binding documentation for imx25 GCQ
  ARM: dt: Binding documentation for imx25 touchscreen controller
  mfd: fsl imx25 Touchscreen ADC driver
  iio: adc: fsl,imx25-gcq driver
  input: touchscreen: imx25 tcq driver

 .../devicetree/bindings/iio/adc/fsl,imx25-gcq.txt  |  58 ++
 .../bindings/input/touchscreen/fsl-mx25-tcq.txt    |  29 +
 .../devicetree/bindings/mfd/fsl-imx25-tsadc.txt    |  46 ++
 arch/arm/boot/dts/imx25.dtsi                       |  30 +-
 arch/arm/configs/imx_v4_v5_defconfig               |   4 +
 drivers/iio/adc/Kconfig                            |   7 +
 drivers/iio/adc/Makefile                           |   1 +
 drivers/iio/adc/fsl-imx25-gcq.c                    | 415 ++++++++++++++
 drivers/input/touchscreen/Kconfig                  |   6 +
 drivers/input/touchscreen/Makefile                 |   1 +
 drivers/input/touchscreen/fsl-imx25-tcq.c          | 600 +++++++++++++++++++++
 drivers/mfd/Kconfig                                |   9 +
 drivers/mfd/Makefile                               |   2 +
 drivers/mfd/fsl-imx25-tsadc.c                      | 204 +++++++
 include/dt-bindings/iio/adc/fsl-imx25-gcq.h        |  18 +
 include/linux/mfd/imx25-tsadc.h                    | 140 +++++
 16 files changed, 1567 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
 create mode 100644 Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
 create mode 100644 drivers/iio/adc/fsl-imx25-gcq.c
 create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
 create mode 100644 drivers/mfd/fsl-imx25-tsadc.c
 create mode 100644 include/dt-bindings/iio/adc/fsl-imx25-gcq.h
 create mode 100644 include/linux/mfd/imx25-tsadc.h

-- 
2.6.1

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

* [PATCH v8 1/8] ARM: dt: Binding documentation for imx25 ADC/TSC
  2015-11-16 12:01 ` Markus Pargmann
@ 2015-11-16 12:01   ` Markus Pargmann
  -1 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam, Markus Pargmann

This documentation describes the devicetree bindings for the
ADC/Touchscreen unit of the i.MX25 SoC.

Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
Acked-by: Jonathan Cameron <jic23@kernel.org>
---

Notes:
    Changes in v6:
     - Removed adc-ref property and replaced it with refp and refn for positive and
       negative references. The properties are optional now as the default
       behaviour is a positive internal reference voltage and ADC GND as negative
       reference.

 .../devicetree/bindings/mfd/fsl-imx25-tsadc.txt    | 46 ++++++++++++++++++++++
 1 file changed, 46 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt

diff --git a/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
new file mode 100644
index 000000000000..a857af0eb68c
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
@@ -0,0 +1,46 @@
+Freescale mx25 ADC/TSC multifunction device
+
+This device combines two general purpose conversion queues one used for general
+ADC and the other used for touchscreens.
+
+Required properties:
+ - compatible: Should be "fsl,imx25-tsadc".
+ - reg: Memory range of the device.
+ - interrupts: Interrupt for this device as described in
+   interrupts/interrupts.txt
+ - clocks: An 'ipg' clock defined as described in clocks/clock.txt
+ - interrupt-controller: This device is an interrupt controller. It controls
+   the interrupts of both conversion queues.
+ - #interrupt-cells: Should be '<1>'.
+ - #address-cells: Should be '<1>'.
+ - #size-cells: Should be '<1>'.
+ - ranges
+
+This device includes two conversion queues which can be added as subnodes.
+The first queue is for the touchscreen, the second for general purpose ADC.
+
+Example:
+	tscadc: tscadc@50030000 {
+		compatible = "fsl,imx25-tsadc";
+		reg = <0x50030000 0xc>;
+		interrupts = <46>;
+		clocks = <&clks 119>;
+		clock-names = "ipg";
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		tsc: tcq@50030400 {
+			compatible = "fsl,imx25-tcq";
+			reg = <0x50030400 0x60>;
+			...
+		};
+
+		adc: gcq@50030800 {
+			compatible = "fsl,imx25-gcq";
+			reg = <0x50030800 0x60>;
+			...
+		};
+	};
-- 
2.6.1


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

* [PATCH v8 1/8] ARM: dt: Binding documentation for imx25 ADC/TSC
@ 2015-11-16 12:01   ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: linux-arm-kernel

This documentation describes the devicetree bindings for the
ADC/Touchscreen unit of the i.MX25 SoC.

Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
Acked-by: Jonathan Cameron <jic23@kernel.org>
---

Notes:
    Changes in v6:
     - Removed adc-ref property and replaced it with refp and refn for positive and
       negative references. The properties are optional now as the default
       behaviour is a positive internal reference voltage and ADC GND as negative
       reference.

 .../devicetree/bindings/mfd/fsl-imx25-tsadc.txt    | 46 ++++++++++++++++++++++
 1 file changed, 46 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt

diff --git a/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
new file mode 100644
index 000000000000..a857af0eb68c
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
@@ -0,0 +1,46 @@
+Freescale mx25 ADC/TSC multifunction device
+
+This device combines two general purpose conversion queues one used for general
+ADC and the other used for touchscreens.
+
+Required properties:
+ - compatible: Should be "fsl,imx25-tsadc".
+ - reg: Memory range of the device.
+ - interrupts: Interrupt for this device as described in
+   interrupts/interrupts.txt
+ - clocks: An 'ipg' clock defined as described in clocks/clock.txt
+ - interrupt-controller: This device is an interrupt controller. It controls
+   the interrupts of both conversion queues.
+ - #interrupt-cells: Should be '<1>'.
+ - #address-cells: Should be '<1>'.
+ - #size-cells: Should be '<1>'.
+ - ranges
+
+This device includes two conversion queues which can be added as subnodes.
+The first queue is for the touchscreen, the second for general purpose ADC.
+
+Example:
+	tscadc: tscadc at 50030000 {
+		compatible = "fsl,imx25-tsadc";
+		reg = <0x50030000 0xc>;
+		interrupts = <46>;
+		clocks = <&clks 119>;
+		clock-names = "ipg";
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		tsc: tcq at 50030400 {
+			compatible = "fsl,imx25-tcq";
+			reg = <0x50030400 0x60>;
+			...
+		};
+
+		adc: gcq at 50030800 {
+			compatible = "fsl,imx25-gcq";
+			reg = <0x50030800 0x60>;
+			...
+		};
+	};
-- 
2.6.1

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

* [PATCH v8 2/8] ARM: dt: Binding documentation for imx25 GCQ
  2015-11-16 12:01 ` Markus Pargmann
@ 2015-11-16 12:01   ` Markus Pargmann
  -1 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam, Markus Pargmann

The documentation describes the bindings for the imx25 GCQ unit which is
essentially a generic conversion queue using the imx25 ADC.

Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
---

Notes:
    Changes in v6:
     - Changed bindings to use adc-refp and adc-refn. Also a bit of cleanup in the
       setup routine.
    
    Changes in v5:
     - Fixed locking
     - Removed module owner

 .../devicetree/bindings/iio/adc/fsl,imx25-gcq.txt  | 58 ++++++++++++++++++++++
 1 file changed, 58 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt

diff --git a/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt b/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
new file mode 100644
index 000000000000..b0866d36a307
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
@@ -0,0 +1,58 @@
+Freescale i.MX25 ADC GCQ device
+
+This is a generic conversion queue device that can convert any of the
+analog inputs using the ADC unit of the i.MX25.
+
+Required properties:
+ - compatible: Should be "fsl,imx25-gcq".
+ - reg: Should be the register range of the module.
+ - interrupts: Should be the interrupt number of the module.
+   Typically this is <1>.
+ - interrupt-parent: phandle to the tsadc module of the i.MX25.
+ - #address-cells: Should be <1> (setting for the subnodes)
+ - #size-cells: Should be <0> (setting for the subnodes)
+
+Optional properties:
+ - vref-ext-supply: The regulator supplying the ADC reference voltage.
+   Required when at least one subnode uses the this reference.
+ - vref-xp-supply: The regulator supplying the ADC reference voltage on pin XP.
+   Required when at least one subnode uses this reference.
+ - vref-yp-supply: The regulator supplying the ADC reference voltage on pin YP.
+   Required when at least one subnode uses this reference.
+
+Sub-nodes:
+Optionally you can define subnodes which define the reference voltage
+for the analog inputs.
+
+Required properties for subnodes:
+ - reg: Should be the number of the analog input.
+     0: xp
+     1: yp
+     2: xn
+     3: yn
+     4: wiper
+     5: inaux0
+     6: inaux1
+     7: inaux2
+Optional properties for subnodes:
+ - fsl,adc-refp: specifies the positive reference input as defined in
+     <dt-bindings/iio/adc/fsl-imx25-gcq.h>
+ - fsl,adc-refn: specifies the negative reference input as defined in
+     <dt-bindings/iio/adc/fsl-imx25-gcq.h>
+
+Example:
+
+	adc: adc@50030800 {
+		compatible = "fsl,imx25-gcq";
+		reg = <0x50030800 0x60>;
+		interrupt-parent = <&tscadc>;
+		interrupts = <1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		inaux@5 {
+			reg = <5>;
+			fsl,adc-refp = <MX25_ADC_REFP_INT>;
+			fsl,adc-refn = <MX25_ADC_REFN_NGND>;
+		};
+	};
-- 
2.6.1


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

* [PATCH v8 2/8] ARM: dt: Binding documentation for imx25 GCQ
@ 2015-11-16 12:01   ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: linux-arm-kernel

The documentation describes the bindings for the imx25 GCQ unit which is
essentially a generic conversion queue using the imx25 ADC.

Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
---

Notes:
    Changes in v6:
     - Changed bindings to use adc-refp and adc-refn. Also a bit of cleanup in the
       setup routine.
    
    Changes in v5:
     - Fixed locking
     - Removed module owner

 .../devicetree/bindings/iio/adc/fsl,imx25-gcq.txt  | 58 ++++++++++++++++++++++
 1 file changed, 58 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt

diff --git a/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt b/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
new file mode 100644
index 000000000000..b0866d36a307
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
@@ -0,0 +1,58 @@
+Freescale i.MX25 ADC GCQ device
+
+This is a generic conversion queue device that can convert any of the
+analog inputs using the ADC unit of the i.MX25.
+
+Required properties:
+ - compatible: Should be "fsl,imx25-gcq".
+ - reg: Should be the register range of the module.
+ - interrupts: Should be the interrupt number of the module.
+   Typically this is <1>.
+ - interrupt-parent: phandle to the tsadc module of the i.MX25.
+ - #address-cells: Should be <1> (setting for the subnodes)
+ - #size-cells: Should be <0> (setting for the subnodes)
+
+Optional properties:
+ - vref-ext-supply: The regulator supplying the ADC reference voltage.
+   Required when at least one subnode uses the this reference.
+ - vref-xp-supply: The regulator supplying the ADC reference voltage on pin XP.
+   Required when at least one subnode uses this reference.
+ - vref-yp-supply: The regulator supplying the ADC reference voltage on pin YP.
+   Required when at least one subnode uses this reference.
+
+Sub-nodes:
+Optionally you can define subnodes which define the reference voltage
+for the analog inputs.
+
+Required properties for subnodes:
+ - reg: Should be the number of the analog input.
+     0: xp
+     1: yp
+     2: xn
+     3: yn
+     4: wiper
+     5: inaux0
+     6: inaux1
+     7: inaux2
+Optional properties for subnodes:
+ - fsl,adc-refp: specifies the positive reference input as defined in
+     <dt-bindings/iio/adc/fsl-imx25-gcq.h>
+ - fsl,adc-refn: specifies the negative reference input as defined in
+     <dt-bindings/iio/adc/fsl-imx25-gcq.h>
+
+Example:
+
+	adc: adc at 50030800 {
+		compatible = "fsl,imx25-gcq";
+		reg = <0x50030800 0x60>;
+		interrupt-parent = <&tscadc>;
+		interrupts = <1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		inaux at 5 {
+			reg = <5>;
+			fsl,adc-refp = <MX25_ADC_REFP_INT>;
+			fsl,adc-refn = <MX25_ADC_REFN_NGND>;
+		};
+	};
-- 
2.6.1

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

* [PATCH v8 3/8] ARM: dt: Binding documentation for imx25 touchscreen controller
  2015-11-16 12:01 ` Markus Pargmann
@ 2015-11-16 12:01   ` Markus Pargmann
  -1 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam, Markus Pargmann

This is the touchscreen conversion queue binding documentation. It uses
the shared imx25 ADC.

Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
---

Notes:
    Changes in v5:
     - Fix signed/unsigned comparison
     - Fix unused variable settling_time by putting it in the correct argument list
     - Use continous conversion queue with the repeat feature and a proper
       repeat-wait. Previously the touchscreen caused massive number of interrupts.

 .../bindings/input/touchscreen/fsl-mx25-tcq.txt    | 29 ++++++++++++++++++++++
 1 file changed, 29 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt

diff --git a/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
new file mode 100644
index 000000000000..89ab47a3acc1
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
@@ -0,0 +1,29 @@
+Freescale mx25 TS conversion queue module
+
+mx25 touchscreen conversion queue module which controls the ADC unit of the
+mx25 for attached touchscreens.
+
+Required properties:
+ - compatible: Should be "fsl,imx25-tcq".
+ - reg: Memory range of the device.
+ - interrupts: Should be the interrupt number associated with this module within
+   the tscadc unit (<0>).
+ - interrupt-parent: Should be a phandle to the tscadc unit.
+ - fsl,wires: Should be '<4>' or '<5>'
+
+Optional properties:
+ - fsl,pen-debounce: Pen debounce time in nanoseconds.
+ - fsl,pen-threshold: Pen-down threshold for the touchscreen.
+ - fsl,settling-time: Settling time in nanoseconds.
+
+This device includes two conversion queues which can be added as subnodes.
+The first queue is for the touchscreen, the second for general purpose ADC.
+
+Example:
+	tsc: tcq@50030400 {
+		compatible = "fsl,imx25-tcq";
+		reg = <0x50030400 0x60>;
+		interrupt-parent = <&tscadc>;
+		interrupts = <0>;
+		fsl,wires = <4>;
+	};
-- 
2.6.1


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

* [PATCH v8 3/8] ARM: dt: Binding documentation for imx25 touchscreen controller
@ 2015-11-16 12:01   ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: linux-arm-kernel

This is the touchscreen conversion queue binding documentation. It uses
the shared imx25 ADC.

Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
---

Notes:
    Changes in v5:
     - Fix signed/unsigned comparison
     - Fix unused variable settling_time by putting it in the correct argument list
     - Use continous conversion queue with the repeat feature and a proper
       repeat-wait. Previously the touchscreen caused massive number of interrupts.

 .../bindings/input/touchscreen/fsl-mx25-tcq.txt    | 29 ++++++++++++++++++++++
 1 file changed, 29 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt

diff --git a/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
new file mode 100644
index 000000000000..89ab47a3acc1
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
@@ -0,0 +1,29 @@
+Freescale mx25 TS conversion queue module
+
+mx25 touchscreen conversion queue module which controls the ADC unit of the
+mx25 for attached touchscreens.
+
+Required properties:
+ - compatible: Should be "fsl,imx25-tcq".
+ - reg: Memory range of the device.
+ - interrupts: Should be the interrupt number associated with this module within
+   the tscadc unit (<0>).
+ - interrupt-parent: Should be a phandle to the tscadc unit.
+ - fsl,wires: Should be '<4>' or '<5>'
+
+Optional properties:
+ - fsl,pen-debounce: Pen debounce time in nanoseconds.
+ - fsl,pen-threshold: Pen-down threshold for the touchscreen.
+ - fsl,settling-time: Settling time in nanoseconds.
+
+This device includes two conversion queues which can be added as subnodes.
+The first queue is for the touchscreen, the second for general purpose ADC.
+
+Example:
+	tsc: tcq at 50030400 {
+		compatible = "fsl,imx25-tcq";
+		reg = <0x50030400 0x60>;
+		interrupt-parent = <&tscadc>;
+		interrupts = <0>;
+		fsl,wires = <4>;
+	};
-- 
2.6.1

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

* [PATCH v8 4/8] mfd: fsl imx25 Touchscreen ADC driver
  2015-11-16 12:01 ` Markus Pargmann
@ 2015-11-16 12:01   ` Markus Pargmann
  -1 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam, Markus Pargmann, Juergen Borleis

This is the core driver for imx25 touchscreen/adc driver. The module
has one shared ADC and two different conversion queues which use the
ADC. The two queues are identical. Both can be used for general purpose
ADC but one is meant to be used for touchscreens.

This driver is the core which manages the central components and
registers of the TSC/ADC unit. It manages the IRQs and forwards them to
the correct components.

Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
Signed-off-by: Denis Carikli <denis@eukrea.com>

[ensure correct ADC clock depending on the IPG clock]
Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
---

Notes:
    Changes in v7:
     - Cleanup bit defines in header files to be more readable
     - Fix irq check to return with an error for irq <= 0
     - Add COMPILE_TEST in Kconfig file
    
    Changes in v5:
     - Remove ifdef CONFIG_OF as this driver is only for DT usage
     - Remove module owner
     - Add Kconfig dependencies ARCH_MX25 and OF
    
    @Jonathan Cameron:
    I left your acked-by on the patch as these were small changes. If it should be
    removed, please say so. Thanks

 drivers/mfd/Kconfig             |   9 ++
 drivers/mfd/Makefile            |   2 +
 drivers/mfd/fsl-imx25-tsadc.c   | 204 ++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/imx25-tsadc.h | 140 +++++++++++++++++++++++++++
 4 files changed, 355 insertions(+)
 create mode 100644 drivers/mfd/fsl-imx25-tsadc.c
 create mode 100644 include/linux/mfd/imx25-tsadc.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 4d92df6ef9fe..4222e202ad2b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -271,6 +271,15 @@ config MFD_MC13XXX_I2C
 	help
 	  Select this if your MC13xxx is connected via an I2C bus.
 
+config MFD_MX25_TSADC
+	tristate "Freescale i.MX25 integrated Touchscreen and ADC unit"
+	select REGMAP_MMIO
+	depends on (SOC_IMX25 && OF) || COMPILE_TEST
+	help
+	  Enable support for the integrated Touchscreen and ADC unit of the
+	  i.MX25 processors. They consist of a conversion queue for general
+	  purpose ADC and a queue for Touchscreens.
+
 config MFD_HI6421_PMIC
 	tristate "HiSilicon Hi6421 PMU/Codec IC"
 	depends on OF
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index a8b76b81b467..5741be88c173 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -81,6 +81,8 @@ obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
 obj-$(CONFIG_MFD_TWL4030_AUDIO)	+= twl4030-audio.o
 obj-$(CONFIG_TWL6040_CORE)	+= twl6040.o
 
+obj-$(CONFIG_MFD_MX25_TSADC)	+= fsl-imx25-tsadc.o
+
 obj-$(CONFIG_MFD_MC13XXX)	+= mc13xxx-core.o
 obj-$(CONFIG_MFD_MC13XXX_SPI)	+= mc13xxx-spi.o
 obj-$(CONFIG_MFD_MC13XXX_I2C)	+= mc13xxx-i2c.o
diff --git a/drivers/mfd/fsl-imx25-tsadc.c b/drivers/mfd/fsl-imx25-tsadc.c
new file mode 100644
index 000000000000..e67d5ca81e10
--- /dev/null
+++ b/drivers/mfd/fsl-imx25-tsadc.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdesc.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/mfd/imx25-tsadc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+static struct regmap_config mx25_tsadc_regmap_config = {
+	.fast_io = true,
+	.max_register = 8,
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+static void mx25_tsadc_irq_handler(struct irq_desc *desc)
+{
+	struct mx25_tsadc *tsadc = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	u32 status;
+
+	chained_irq_enter(chip, desc);
+
+	regmap_read(tsadc->regs, MX25_TSC_TGSR, &status);
+
+	if (status & MX25_TGSR_GCQ_INT)
+		generic_handle_irq(irq_find_mapping(tsadc->domain, 1));
+
+	if (status & MX25_TGSR_TCQ_INT)
+		generic_handle_irq(irq_find_mapping(tsadc->domain, 0));
+
+	chained_irq_exit(chip, desc);
+}
+
+static int mx25_tsadc_domain_map(struct irq_domain *d, unsigned int irq,
+				 irq_hw_number_t hwirq)
+{
+	struct mx25_tsadc *tsadc = d->host_data;
+
+	irq_set_chip_data(irq, tsadc);
+	irq_set_chip_and_handler(irq, &dummy_irq_chip,
+				 handle_level_irq);
+	irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE);
+
+	return 0;
+}
+
+static struct irq_domain_ops mx25_tsadc_domain_ops = {
+	.map = mx25_tsadc_domain_map,
+	.xlate = irq_domain_xlate_onecell,
+};
+
+static int mx25_tsadc_setup_irq(struct platform_device *pdev,
+				struct mx25_tsadc *tsadc)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	int irq;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq <= 0) {
+		dev_err(dev, "Failed to get irq\n");
+		return irq;
+	}
+
+	tsadc->domain = irq_domain_add_simple(np, 2, 0, &mx25_tsadc_domain_ops,
+					      tsadc);
+	if (!tsadc->domain) {
+		dev_err(dev, "Failed to add irq domain\n");
+		return -ENOMEM;
+	}
+
+	irq_set_chained_handler(irq, mx25_tsadc_irq_handler);
+	irq_set_handler_data(irq, tsadc);
+
+	return 0;
+}
+
+static void mx25_tsadc_setup_clk(struct platform_device *pdev,
+				 struct mx25_tsadc *tsadc)
+{
+	unsigned clk_div;
+
+	/*
+	 * According to the datasheet the ADC clock should never
+	 * exceed 1,75 MHz. Base clock is the IPG and the ADC unit uses
+	 * a funny clock divider. To keep the time constant the ADC needs to do
+	 * one conversion, adapt the ADC internal clock divider to
+	 * the available IPG
+	 */
+
+	dev_dbg(&pdev->dev, "Found master clock at %lu Hz\n",
+		clk_get_rate(tsadc->clk));
+
+	clk_div = DIV_ROUND_UP(clk_get_rate(tsadc->clk), 1750000);
+	dev_dbg(&pdev->dev, "Setting up ADC clock divider to %u\n", clk_div);
+
+	/* adc clock = IPG clock / (2 * div + 2) */
+	clk_div -= 2;
+	clk_div /= 2;
+
+	/*
+	 * the ADC clock divider changes its behaviour when values below 4
+	 * are used: it is fixed to "/ 10" in this case
+	 */
+	clk_div = max_t(unsigned, 4, clk_div);
+
+	dev_dbg(&pdev->dev, "Resulting ADC conversion clock at %lu Hz\n",
+		clk_get_rate(tsadc->clk) / (2 * clk_div + 2));
+
+	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR,
+			   MX25_TGCR_ADCCLKCFG(0x1f),
+			   MX25_TGCR_ADCCLKCFG(clk_div));
+}
+
+static int mx25_tsadc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct mx25_tsadc *tsadc;
+	struct resource *res;
+	int ret;
+	void __iomem *iomem;
+
+	tsadc = devm_kzalloc(dev, sizeof(*tsadc), GFP_KERNEL);
+	if (!tsadc)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	iomem = devm_ioremap_resource(dev, res);
+	if (IS_ERR(iomem))
+		return PTR_ERR(iomem);
+
+	tsadc->regs = devm_regmap_init_mmio(dev, iomem,
+					    &mx25_tsadc_regmap_config);
+	if (IS_ERR(tsadc->regs)) {
+		dev_err(dev, "Failed to initialize regmap\n");
+		return PTR_ERR(tsadc->regs);
+	}
+
+	tsadc->clk = devm_clk_get(dev, "ipg");
+	if (IS_ERR(tsadc->clk)) {
+		dev_err(dev, "Failed to get ipg clock\n");
+		return PTR_ERR(tsadc->clk);
+	}
+
+	/* setup clock according to the datasheet */
+	mx25_tsadc_setup_clk(pdev, tsadc);
+
+	/* Enable clock and reset the component */
+	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_CLK_EN,
+			   MX25_TGCR_CLK_EN);
+	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_TSC_RST,
+			   MX25_TGCR_TSC_RST);
+
+	/* Setup powersaving mode, but enable internal reference voltage */
+	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_POWERMODE_MASK,
+			   MX25_TGCR_POWERMODE_SAVE);
+	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_INTREFEN,
+			   MX25_TGCR_INTREFEN);
+
+	ret = mx25_tsadc_setup_irq(pdev, tsadc);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, tsadc);
+
+	of_platform_populate(np, NULL, NULL, dev);
+
+	return 0;
+}
+
+static const struct of_device_id mx25_tsadc_ids[] = {
+	{ .compatible = "fsl,imx25-tsadc" },
+	{ /* Sentinel */ }
+};
+
+static struct platform_driver mx25_tsadc_driver = {
+	.driver = {
+		.name = "mx25-tsadc",
+		.of_match_table = of_match_ptr(mx25_tsadc_ids),
+	},
+	.probe = mx25_tsadc_probe,
+};
+module_platform_driver(mx25_tsadc_driver);
+
+MODULE_DESCRIPTION("MFD for ADC/TSC for Freescale mx25");
+MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mx25-tsadc");
diff --git a/include/linux/mfd/imx25-tsadc.h b/include/linux/mfd/imx25-tsadc.h
new file mode 100644
index 000000000000..7fe4b8c3baac
--- /dev/null
+++ b/include/linux/mfd/imx25-tsadc.h
@@ -0,0 +1,140 @@
+#ifndef _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
+#define _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
+
+struct regmap;
+struct clk;
+
+struct mx25_tsadc {
+	struct regmap *regs;
+	struct irq_domain *domain;
+	struct clk *clk;
+};
+
+#define MX25_TSC_TGCR			0x00
+#define MX25_TSC_TGSR			0x04
+#define MX25_TSC_TICR			0x08
+
+/* The same register layout for TC and GC queue */
+#define MX25_ADCQ_FIFO			0x00
+#define MX25_ADCQ_CR			0x04
+#define MX25_ADCQ_SR			0x08
+#define MX25_ADCQ_MR			0x0c
+#define MX25_ADCQ_ITEM_7_0		0x20
+#define MX25_ADCQ_ITEM_15_8		0x24
+#define MX25_ADCQ_CFG(n)		(0x40 + ((n) * 0x4))
+
+#define MX25_ADCQ_MR_MASK		0xffffffff
+
+/* TGCR */
+#define MX25_TGCR_PDBTIME(x)		((x) << 25)
+#define MX25_TGCR_PDBTIME_MASK		GENMASK(31, 25)
+#define MX25_TGCR_PDBEN			BIT(24)
+#define MX25_TGCR_PDEN			BIT(23)
+#define MX25_TGCR_ADCCLKCFG(x)		((x) << 16)
+#define MX25_TGCR_GET_ADCCLK(x)		(((x) >> 16) & 0x1f)
+#define MX25_TGCR_INTREFEN		BIT(10)
+#define MX25_TGCR_POWERMODE_MASK	GENMASK(9, 8)
+#define MX25_TGCR_POWERMODE_SAVE	(1 << 8)
+#define MX25_TGCR_POWERMODE_ON		(2 << 8)
+#define MX25_TGCR_STLC			BIT(5)
+#define MX25_TGCR_SLPC			BIT(4)
+#define MX25_TGCR_FUNC_RST		BIT(2)
+#define MX25_TGCR_TSC_RST		BIT(1)
+#define MX25_TGCR_CLK_EN		BIT(0)
+
+/* TGSR */
+#define MX25_TGSR_SLP_INT		BIT(2)
+#define MX25_TGSR_GCQ_INT		BIT(1)
+#define MX25_TGSR_TCQ_INT		BIT(0)
+
+/* ADCQ_ITEM_* */
+#define _MX25_ADCQ_ITEM(item, x)	((x) << ((item) * 4))
+#define MX25_ADCQ_ITEM(item, x)		((item) >= 8 ? \
+		_MX25_ADCQ_ITEM((item) - 8, (x)) : _MX25_ADCQ_ITEM((item), (x)))
+
+/* ADCQ_FIFO (TCQFIFO and GCQFIFO) */
+#define MX25_ADCQ_FIFO_DATA(x)		(((x) >> 4) & 0xfff)
+#define MX25_ADCQ_FIFO_ID(x)		((x) & 0xf)
+
+/* ADCQ_CR (TCQR and GCQR) */
+#define MX25_ADCQ_CR_PDCFG_LEVEL	BIT(19)
+#define MX25_ADCQ_CR_PDMSK		BIT(18)
+#define MX25_ADCQ_CR_FRST		BIT(17)
+#define MX25_ADCQ_CR_QRST		BIT(16)
+#define MX25_ADCQ_CR_RWAIT_MASK		GENMASK(15, 12)
+#define MX25_ADCQ_CR_RWAIT(x)		((x) << 12)
+#define MX25_ADCQ_CR_WMRK_MASK		GENMASK(11, 8)
+#define MX25_ADCQ_CR_WMRK(x)		((x) << 8)
+#define MX25_ADCQ_CR_LITEMID_MASK	(0xf << 4)
+#define MX25_ADCQ_CR_LITEMID(x)		((x) << 4)
+#define MX25_ADCQ_CR_RPT		BIT(3)
+#define MX25_ADCQ_CR_FQS		BIT(2)
+#define MX25_ADCQ_CR_QSM_MASK		GENMASK(1, 0)
+#define MX25_ADCQ_CR_QSM_PD		0x1
+#define MX25_ADCQ_CR_QSM_FQS		0x2
+#define MX25_ADCQ_CR_QSM_FQS_PD		0x3
+
+/* ADCQ_SR (TCQSR and GCQSR) */
+#define MX25_ADCQ_SR_FDRY		BIT(15)
+#define MX25_ADCQ_SR_FULL		BIT(14)
+#define MX25_ADCQ_SR_EMPT		BIT(13)
+#define MX25_ADCQ_SR_FDN(x)		(((x) >> 8) & 0x1f)
+#define MX25_ADCQ_SR_FRR		BIT(6)
+#define MX25_ADCQ_SR_FUR		BIT(5)
+#define MX25_ADCQ_SR_FOR		BIT(4)
+#define MX25_ADCQ_SR_EOQ		BIT(1)
+#define MX25_ADCQ_SR_PD			BIT(0)
+
+/* ADCQ_MR (TCQMR and GCQMR) */
+#define MX25_ADCQ_MR_FDRY_DMA		BIT(31)
+#define MX25_ADCQ_MR_FER_DMA		BIT(22)
+#define MX25_ADCQ_MR_FUR_DMA		BIT(21)
+#define MX25_ADCQ_MR_FOR_DMA		BIT(20)
+#define MX25_ADCQ_MR_EOQ_DMA		BIT(17)
+#define MX25_ADCQ_MR_PD_DMA		BIT(16)
+#define MX25_ADCQ_MR_FDRY_IRQ		BIT(15)
+#define MX25_ADCQ_MR_FER_IRQ		BIT(6)
+#define MX25_ADCQ_MR_FUR_IRQ		BIT(5)
+#define MX25_ADCQ_MR_FOR_IRQ		BIT(4)
+#define MX25_ADCQ_MR_EOQ_IRQ		BIT(1)
+#define MX25_ADCQ_MR_PD_IRQ		BIT(0)
+
+/* ADCQ_CFG (TICR, TCC0-7,GCC0-7) */
+#define MX25_ADCQ_CFG_SETTLING_TIME(x)	((x) << 24)
+#define MX25_ADCQ_CFG_IGS		(1 << 20)
+#define MX25_ADCQ_CFG_NOS_MASK		GENMASK(19, 16)
+#define MX25_ADCQ_CFG_NOS(x)		(((x) - 1) << 16)
+#define MX25_ADCQ_CFG_WIPER		(1 << 15)
+#define MX25_ADCQ_CFG_YNLR		(1 << 14)
+#define MX25_ADCQ_CFG_YPLL_HIGH		(0 << 12)
+#define MX25_ADCQ_CFG_YPLL_OFF		(1 << 12)
+#define MX25_ADCQ_CFG_YPLL_LOW		(3 << 12)
+#define MX25_ADCQ_CFG_XNUR_HIGH		(0 << 10)
+#define MX25_ADCQ_CFG_XNUR_OFF		(1 << 10)
+#define MX25_ADCQ_CFG_XNUR_LOW		(3 << 10)
+#define MX25_ADCQ_CFG_XPUL_HIGH		(0 << 9)
+#define MX25_ADCQ_CFG_XPUL_OFF		(1 << 9)
+#define MX25_ADCQ_CFG_REFP(sel)		((sel) << 7)
+#define MX25_ADCQ_CFG_REFP_YP		MX25_ADCQ_CFG_REFP(0)
+#define MX25_ADCQ_CFG_REFP_XP		MX25_ADCQ_CFG_REFP(1)
+#define MX25_ADCQ_CFG_REFP_EXT		MX25_ADCQ_CFG_REFP(2)
+#define MX25_ADCQ_CFG_REFP_INT		MX25_ADCQ_CFG_REFP(3)
+#define MX25_ADCQ_CFG_REFP_MASK		GENMASK(8, 7)
+#define MX25_ADCQ_CFG_IN(sel)		((sel) << 4)
+#define MX25_ADCQ_CFG_IN_XP		MX25_ADCQ_CFG_IN(0)
+#define MX25_ADCQ_CFG_IN_YP		MX25_ADCQ_CFG_IN(1)
+#define MX25_ADCQ_CFG_IN_XN		MX25_ADCQ_CFG_IN(2)
+#define MX25_ADCQ_CFG_IN_YN		MX25_ADCQ_CFG_IN(3)
+#define MX25_ADCQ_CFG_IN_WIPER		MX25_ADCQ_CFG_IN(4)
+#define MX25_ADCQ_CFG_IN_AUX0		MX25_ADCQ_CFG_IN(5)
+#define MX25_ADCQ_CFG_IN_AUX1		MX25_ADCQ_CFG_IN(6)
+#define MX25_ADCQ_CFG_IN_AUX2		MX25_ADCQ_CFG_IN(7)
+#define MX25_ADCQ_CFG_REFN(sel)		((sel) << 2)
+#define MX25_ADCQ_CFG_REFN_XN		MX25_ADCQ_CFG_REFN(0)
+#define MX25_ADCQ_CFG_REFN_YN		MX25_ADCQ_CFG_REFN(1)
+#define MX25_ADCQ_CFG_REFN_NGND		MX25_ADCQ_CFG_REFN(2)
+#define MX25_ADCQ_CFG_REFN_NGND2	MX25_ADCQ_CFG_REFN(3)
+#define MX25_ADCQ_CFG_REFN_MASK		GENMASK(3, 2)
+#define MX25_ADCQ_CFG_PENIACK		(1 << 1)
+
+#endif  /* _LINUX_INCLUDE_MFD_IMX25_TSADC_H_ */
-- 
2.6.1


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

* [PATCH v8 4/8] mfd: fsl imx25 Touchscreen ADC driver
@ 2015-11-16 12:01   ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: linux-arm-kernel

This is the core driver for imx25 touchscreen/adc driver. The module
has one shared ADC and two different conversion queues which use the
ADC. The two queues are identical. Both can be used for general purpose
ADC but one is meant to be used for touchscreens.

This driver is the core which manages the central components and
registers of the TSC/ADC unit. It manages the IRQs and forwards them to
the correct components.

Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
Signed-off-by: Denis Carikli <denis@eukrea.com>

[ensure correct ADC clock depending on the IPG clock]
Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
---

Notes:
    Changes in v7:
     - Cleanup bit defines in header files to be more readable
     - Fix irq check to return with an error for irq <= 0
     - Add COMPILE_TEST in Kconfig file
    
    Changes in v5:
     - Remove ifdef CONFIG_OF as this driver is only for DT usage
     - Remove module owner
     - Add Kconfig dependencies ARCH_MX25 and OF
    
    @Jonathan Cameron:
    I left your acked-by on the patch as these were small changes. If it should be
    removed, please say so. Thanks

 drivers/mfd/Kconfig             |   9 ++
 drivers/mfd/Makefile            |   2 +
 drivers/mfd/fsl-imx25-tsadc.c   | 204 ++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/imx25-tsadc.h | 140 +++++++++++++++++++++++++++
 4 files changed, 355 insertions(+)
 create mode 100644 drivers/mfd/fsl-imx25-tsadc.c
 create mode 100644 include/linux/mfd/imx25-tsadc.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 4d92df6ef9fe..4222e202ad2b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -271,6 +271,15 @@ config MFD_MC13XXX_I2C
 	help
 	  Select this if your MC13xxx is connected via an I2C bus.
 
+config MFD_MX25_TSADC
+	tristate "Freescale i.MX25 integrated Touchscreen and ADC unit"
+	select REGMAP_MMIO
+	depends on (SOC_IMX25 && OF) || COMPILE_TEST
+	help
+	  Enable support for the integrated Touchscreen and ADC unit of the
+	  i.MX25 processors. They consist of a conversion queue for general
+	  purpose ADC and a queue for Touchscreens.
+
 config MFD_HI6421_PMIC
 	tristate "HiSilicon Hi6421 PMU/Codec IC"
 	depends on OF
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index a8b76b81b467..5741be88c173 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -81,6 +81,8 @@ obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
 obj-$(CONFIG_MFD_TWL4030_AUDIO)	+= twl4030-audio.o
 obj-$(CONFIG_TWL6040_CORE)	+= twl6040.o
 
+obj-$(CONFIG_MFD_MX25_TSADC)	+= fsl-imx25-tsadc.o
+
 obj-$(CONFIG_MFD_MC13XXX)	+= mc13xxx-core.o
 obj-$(CONFIG_MFD_MC13XXX_SPI)	+= mc13xxx-spi.o
 obj-$(CONFIG_MFD_MC13XXX_I2C)	+= mc13xxx-i2c.o
diff --git a/drivers/mfd/fsl-imx25-tsadc.c b/drivers/mfd/fsl-imx25-tsadc.c
new file mode 100644
index 000000000000..e67d5ca81e10
--- /dev/null
+++ b/drivers/mfd/fsl-imx25-tsadc.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdesc.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/mfd/imx25-tsadc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+static struct regmap_config mx25_tsadc_regmap_config = {
+	.fast_io = true,
+	.max_register = 8,
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+static void mx25_tsadc_irq_handler(struct irq_desc *desc)
+{
+	struct mx25_tsadc *tsadc = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	u32 status;
+
+	chained_irq_enter(chip, desc);
+
+	regmap_read(tsadc->regs, MX25_TSC_TGSR, &status);
+
+	if (status & MX25_TGSR_GCQ_INT)
+		generic_handle_irq(irq_find_mapping(tsadc->domain, 1));
+
+	if (status & MX25_TGSR_TCQ_INT)
+		generic_handle_irq(irq_find_mapping(tsadc->domain, 0));
+
+	chained_irq_exit(chip, desc);
+}
+
+static int mx25_tsadc_domain_map(struct irq_domain *d, unsigned int irq,
+				 irq_hw_number_t hwirq)
+{
+	struct mx25_tsadc *tsadc = d->host_data;
+
+	irq_set_chip_data(irq, tsadc);
+	irq_set_chip_and_handler(irq, &dummy_irq_chip,
+				 handle_level_irq);
+	irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE);
+
+	return 0;
+}
+
+static struct irq_domain_ops mx25_tsadc_domain_ops = {
+	.map = mx25_tsadc_domain_map,
+	.xlate = irq_domain_xlate_onecell,
+};
+
+static int mx25_tsadc_setup_irq(struct platform_device *pdev,
+				struct mx25_tsadc *tsadc)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	int irq;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq <= 0) {
+		dev_err(dev, "Failed to get irq\n");
+		return irq;
+	}
+
+	tsadc->domain = irq_domain_add_simple(np, 2, 0, &mx25_tsadc_domain_ops,
+					      tsadc);
+	if (!tsadc->domain) {
+		dev_err(dev, "Failed to add irq domain\n");
+		return -ENOMEM;
+	}
+
+	irq_set_chained_handler(irq, mx25_tsadc_irq_handler);
+	irq_set_handler_data(irq, tsadc);
+
+	return 0;
+}
+
+static void mx25_tsadc_setup_clk(struct platform_device *pdev,
+				 struct mx25_tsadc *tsadc)
+{
+	unsigned clk_div;
+
+	/*
+	 * According to the datasheet the ADC clock should never
+	 * exceed 1,75 MHz. Base clock is the IPG and the ADC unit uses
+	 * a funny clock divider. To keep the time constant the ADC needs to do
+	 * one conversion, adapt the ADC internal clock divider to
+	 * the available IPG
+	 */
+
+	dev_dbg(&pdev->dev, "Found master clock at %lu Hz\n",
+		clk_get_rate(tsadc->clk));
+
+	clk_div = DIV_ROUND_UP(clk_get_rate(tsadc->clk), 1750000);
+	dev_dbg(&pdev->dev, "Setting up ADC clock divider to %u\n", clk_div);
+
+	/* adc clock = IPG clock / (2 * div + 2) */
+	clk_div -= 2;
+	clk_div /= 2;
+
+	/*
+	 * the ADC clock divider changes its behaviour when values below 4
+	 * are used: it is fixed to "/ 10" in this case
+	 */
+	clk_div = max_t(unsigned, 4, clk_div);
+
+	dev_dbg(&pdev->dev, "Resulting ADC conversion clock at %lu Hz\n",
+		clk_get_rate(tsadc->clk) / (2 * clk_div + 2));
+
+	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR,
+			   MX25_TGCR_ADCCLKCFG(0x1f),
+			   MX25_TGCR_ADCCLKCFG(clk_div));
+}
+
+static int mx25_tsadc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct mx25_tsadc *tsadc;
+	struct resource *res;
+	int ret;
+	void __iomem *iomem;
+
+	tsadc = devm_kzalloc(dev, sizeof(*tsadc), GFP_KERNEL);
+	if (!tsadc)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	iomem = devm_ioremap_resource(dev, res);
+	if (IS_ERR(iomem))
+		return PTR_ERR(iomem);
+
+	tsadc->regs = devm_regmap_init_mmio(dev, iomem,
+					    &mx25_tsadc_regmap_config);
+	if (IS_ERR(tsadc->regs)) {
+		dev_err(dev, "Failed to initialize regmap\n");
+		return PTR_ERR(tsadc->regs);
+	}
+
+	tsadc->clk = devm_clk_get(dev, "ipg");
+	if (IS_ERR(tsadc->clk)) {
+		dev_err(dev, "Failed to get ipg clock\n");
+		return PTR_ERR(tsadc->clk);
+	}
+
+	/* setup clock according to the datasheet */
+	mx25_tsadc_setup_clk(pdev, tsadc);
+
+	/* Enable clock and reset the component */
+	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_CLK_EN,
+			   MX25_TGCR_CLK_EN);
+	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_TSC_RST,
+			   MX25_TGCR_TSC_RST);
+
+	/* Setup powersaving mode, but enable internal reference voltage */
+	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_POWERMODE_MASK,
+			   MX25_TGCR_POWERMODE_SAVE);
+	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_INTREFEN,
+			   MX25_TGCR_INTREFEN);
+
+	ret = mx25_tsadc_setup_irq(pdev, tsadc);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, tsadc);
+
+	of_platform_populate(np, NULL, NULL, dev);
+
+	return 0;
+}
+
+static const struct of_device_id mx25_tsadc_ids[] = {
+	{ .compatible = "fsl,imx25-tsadc" },
+	{ /* Sentinel */ }
+};
+
+static struct platform_driver mx25_tsadc_driver = {
+	.driver = {
+		.name = "mx25-tsadc",
+		.of_match_table = of_match_ptr(mx25_tsadc_ids),
+	},
+	.probe = mx25_tsadc_probe,
+};
+module_platform_driver(mx25_tsadc_driver);
+
+MODULE_DESCRIPTION("MFD for ADC/TSC for Freescale mx25");
+MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mx25-tsadc");
diff --git a/include/linux/mfd/imx25-tsadc.h b/include/linux/mfd/imx25-tsadc.h
new file mode 100644
index 000000000000..7fe4b8c3baac
--- /dev/null
+++ b/include/linux/mfd/imx25-tsadc.h
@@ -0,0 +1,140 @@
+#ifndef _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
+#define _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
+
+struct regmap;
+struct clk;
+
+struct mx25_tsadc {
+	struct regmap *regs;
+	struct irq_domain *domain;
+	struct clk *clk;
+};
+
+#define MX25_TSC_TGCR			0x00
+#define MX25_TSC_TGSR			0x04
+#define MX25_TSC_TICR			0x08
+
+/* The same register layout for TC and GC queue */
+#define MX25_ADCQ_FIFO			0x00
+#define MX25_ADCQ_CR			0x04
+#define MX25_ADCQ_SR			0x08
+#define MX25_ADCQ_MR			0x0c
+#define MX25_ADCQ_ITEM_7_0		0x20
+#define MX25_ADCQ_ITEM_15_8		0x24
+#define MX25_ADCQ_CFG(n)		(0x40 + ((n) * 0x4))
+
+#define MX25_ADCQ_MR_MASK		0xffffffff
+
+/* TGCR */
+#define MX25_TGCR_PDBTIME(x)		((x) << 25)
+#define MX25_TGCR_PDBTIME_MASK		GENMASK(31, 25)
+#define MX25_TGCR_PDBEN			BIT(24)
+#define MX25_TGCR_PDEN			BIT(23)
+#define MX25_TGCR_ADCCLKCFG(x)		((x) << 16)
+#define MX25_TGCR_GET_ADCCLK(x)		(((x) >> 16) & 0x1f)
+#define MX25_TGCR_INTREFEN		BIT(10)
+#define MX25_TGCR_POWERMODE_MASK	GENMASK(9, 8)
+#define MX25_TGCR_POWERMODE_SAVE	(1 << 8)
+#define MX25_TGCR_POWERMODE_ON		(2 << 8)
+#define MX25_TGCR_STLC			BIT(5)
+#define MX25_TGCR_SLPC			BIT(4)
+#define MX25_TGCR_FUNC_RST		BIT(2)
+#define MX25_TGCR_TSC_RST		BIT(1)
+#define MX25_TGCR_CLK_EN		BIT(0)
+
+/* TGSR */
+#define MX25_TGSR_SLP_INT		BIT(2)
+#define MX25_TGSR_GCQ_INT		BIT(1)
+#define MX25_TGSR_TCQ_INT		BIT(0)
+
+/* ADCQ_ITEM_* */
+#define _MX25_ADCQ_ITEM(item, x)	((x) << ((item) * 4))
+#define MX25_ADCQ_ITEM(item, x)		((item) >= 8 ? \
+		_MX25_ADCQ_ITEM((item) - 8, (x)) : _MX25_ADCQ_ITEM((item), (x)))
+
+/* ADCQ_FIFO (TCQFIFO and GCQFIFO) */
+#define MX25_ADCQ_FIFO_DATA(x)		(((x) >> 4) & 0xfff)
+#define MX25_ADCQ_FIFO_ID(x)		((x) & 0xf)
+
+/* ADCQ_CR (TCQR and GCQR) */
+#define MX25_ADCQ_CR_PDCFG_LEVEL	BIT(19)
+#define MX25_ADCQ_CR_PDMSK		BIT(18)
+#define MX25_ADCQ_CR_FRST		BIT(17)
+#define MX25_ADCQ_CR_QRST		BIT(16)
+#define MX25_ADCQ_CR_RWAIT_MASK		GENMASK(15, 12)
+#define MX25_ADCQ_CR_RWAIT(x)		((x) << 12)
+#define MX25_ADCQ_CR_WMRK_MASK		GENMASK(11, 8)
+#define MX25_ADCQ_CR_WMRK(x)		((x) << 8)
+#define MX25_ADCQ_CR_LITEMID_MASK	(0xf << 4)
+#define MX25_ADCQ_CR_LITEMID(x)		((x) << 4)
+#define MX25_ADCQ_CR_RPT		BIT(3)
+#define MX25_ADCQ_CR_FQS		BIT(2)
+#define MX25_ADCQ_CR_QSM_MASK		GENMASK(1, 0)
+#define MX25_ADCQ_CR_QSM_PD		0x1
+#define MX25_ADCQ_CR_QSM_FQS		0x2
+#define MX25_ADCQ_CR_QSM_FQS_PD		0x3
+
+/* ADCQ_SR (TCQSR and GCQSR) */
+#define MX25_ADCQ_SR_FDRY		BIT(15)
+#define MX25_ADCQ_SR_FULL		BIT(14)
+#define MX25_ADCQ_SR_EMPT		BIT(13)
+#define MX25_ADCQ_SR_FDN(x)		(((x) >> 8) & 0x1f)
+#define MX25_ADCQ_SR_FRR		BIT(6)
+#define MX25_ADCQ_SR_FUR		BIT(5)
+#define MX25_ADCQ_SR_FOR		BIT(4)
+#define MX25_ADCQ_SR_EOQ		BIT(1)
+#define MX25_ADCQ_SR_PD			BIT(0)
+
+/* ADCQ_MR (TCQMR and GCQMR) */
+#define MX25_ADCQ_MR_FDRY_DMA		BIT(31)
+#define MX25_ADCQ_MR_FER_DMA		BIT(22)
+#define MX25_ADCQ_MR_FUR_DMA		BIT(21)
+#define MX25_ADCQ_MR_FOR_DMA		BIT(20)
+#define MX25_ADCQ_MR_EOQ_DMA		BIT(17)
+#define MX25_ADCQ_MR_PD_DMA		BIT(16)
+#define MX25_ADCQ_MR_FDRY_IRQ		BIT(15)
+#define MX25_ADCQ_MR_FER_IRQ		BIT(6)
+#define MX25_ADCQ_MR_FUR_IRQ		BIT(5)
+#define MX25_ADCQ_MR_FOR_IRQ		BIT(4)
+#define MX25_ADCQ_MR_EOQ_IRQ		BIT(1)
+#define MX25_ADCQ_MR_PD_IRQ		BIT(0)
+
+/* ADCQ_CFG (TICR, TCC0-7,GCC0-7) */
+#define MX25_ADCQ_CFG_SETTLING_TIME(x)	((x) << 24)
+#define MX25_ADCQ_CFG_IGS		(1 << 20)
+#define MX25_ADCQ_CFG_NOS_MASK		GENMASK(19, 16)
+#define MX25_ADCQ_CFG_NOS(x)		(((x) - 1) << 16)
+#define MX25_ADCQ_CFG_WIPER		(1 << 15)
+#define MX25_ADCQ_CFG_YNLR		(1 << 14)
+#define MX25_ADCQ_CFG_YPLL_HIGH		(0 << 12)
+#define MX25_ADCQ_CFG_YPLL_OFF		(1 << 12)
+#define MX25_ADCQ_CFG_YPLL_LOW		(3 << 12)
+#define MX25_ADCQ_CFG_XNUR_HIGH		(0 << 10)
+#define MX25_ADCQ_CFG_XNUR_OFF		(1 << 10)
+#define MX25_ADCQ_CFG_XNUR_LOW		(3 << 10)
+#define MX25_ADCQ_CFG_XPUL_HIGH		(0 << 9)
+#define MX25_ADCQ_CFG_XPUL_OFF		(1 << 9)
+#define MX25_ADCQ_CFG_REFP(sel)		((sel) << 7)
+#define MX25_ADCQ_CFG_REFP_YP		MX25_ADCQ_CFG_REFP(0)
+#define MX25_ADCQ_CFG_REFP_XP		MX25_ADCQ_CFG_REFP(1)
+#define MX25_ADCQ_CFG_REFP_EXT		MX25_ADCQ_CFG_REFP(2)
+#define MX25_ADCQ_CFG_REFP_INT		MX25_ADCQ_CFG_REFP(3)
+#define MX25_ADCQ_CFG_REFP_MASK		GENMASK(8, 7)
+#define MX25_ADCQ_CFG_IN(sel)		((sel) << 4)
+#define MX25_ADCQ_CFG_IN_XP		MX25_ADCQ_CFG_IN(0)
+#define MX25_ADCQ_CFG_IN_YP		MX25_ADCQ_CFG_IN(1)
+#define MX25_ADCQ_CFG_IN_XN		MX25_ADCQ_CFG_IN(2)
+#define MX25_ADCQ_CFG_IN_YN		MX25_ADCQ_CFG_IN(3)
+#define MX25_ADCQ_CFG_IN_WIPER		MX25_ADCQ_CFG_IN(4)
+#define MX25_ADCQ_CFG_IN_AUX0		MX25_ADCQ_CFG_IN(5)
+#define MX25_ADCQ_CFG_IN_AUX1		MX25_ADCQ_CFG_IN(6)
+#define MX25_ADCQ_CFG_IN_AUX2		MX25_ADCQ_CFG_IN(7)
+#define MX25_ADCQ_CFG_REFN(sel)		((sel) << 2)
+#define MX25_ADCQ_CFG_REFN_XN		MX25_ADCQ_CFG_REFN(0)
+#define MX25_ADCQ_CFG_REFN_YN		MX25_ADCQ_CFG_REFN(1)
+#define MX25_ADCQ_CFG_REFN_NGND		MX25_ADCQ_CFG_REFN(2)
+#define MX25_ADCQ_CFG_REFN_NGND2	MX25_ADCQ_CFG_REFN(3)
+#define MX25_ADCQ_CFG_REFN_MASK		GENMASK(3, 2)
+#define MX25_ADCQ_CFG_PENIACK		(1 << 1)
+
+#endif  /* _LINUX_INCLUDE_MFD_IMX25_TSADC_H_ */
-- 
2.6.1

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

* [PATCH v8 5/8] iio: adc: fsl,imx25-gcq driver
  2015-11-16 12:01 ` Markus Pargmann
  (?)
@ 2015-11-16 12:01     ` Markus Pargmann
  -1 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Hartmut Knaack, Fabio Estevam, Markus Pargmann

This is a conversion queue driver for the mx25 SoC. It uses the central
ADC which is used by two seperate independent queues. This driver
prepares different conversion configurations for each possible input.
For a conversion it creates a conversionqueue of one item with the
correct configuration for the chosen channel. It then executes the queue
once and disables the conversion queue afterwards.

The reference voltages are configurable through devicetree subnodes,
depending on the connections of the ADC inputs.

Signed-off-by: Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Signed-off-by: Denis Carikli <denis-fO0SIAKYzcbQT0dZR+AlfA@public.gmane.org>
---

Notes:
    Changes in v7:
     - Remove separate functions mx25_gcq_disable/enable_eoq() as they were used at
       only one position
     - Enforce an external reference regulator if one of the conversions uses it as
       reference. The devm_regulator_get() call was moved into
       mx25_gcq_setup_cfgs() to be able to acquire the reference regulator when
       necessary.
     - Store indio_dev as platform driver data instead of the private data. This
       was changed in probe() and remove().
    
    Changes in v6:
     - Added defines for a complete list of references in the dt binding macros

 drivers/iio/adc/Kconfig                     |   7 +
 drivers/iio/adc/Makefile                    |   1 +
 drivers/iio/adc/fsl-imx25-gcq.c             | 415 ++++++++++++++++++++++++++++
 include/dt-bindings/iio/adc/fsl-imx25-gcq.h |  18 ++
 4 files changed, 441 insertions(+)
 create mode 100644 drivers/iio/adc/fsl-imx25-gcq.c
 create mode 100644 include/dt-bindings/iio/adc/fsl-imx25-gcq.h

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7868c744fd4b..73145c53ec2c 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -183,6 +183,13 @@ config EXYNOS_ADC
 	  To compile this driver as a module, choose M here: the module will be
 	  called exynos_adc.
 
+config FSL_MX25_ADC
+	tristate "Freescale MX25 ADC driver"
+	depends on MFD_MX25_TSADC
+	help
+	  Generic Conversion Queue driver used for general purpose ADC in the
+	  MX25. This driver supports single measurements using the MX25 ADC.
+
 config HI8435
 	tristate "Holt Integrated Circuits HI-8435 threshold detector"
 	select IIO_TRIGGERED_EVENT
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 99b37a963a1e..2fe9b78e4b02 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
 obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
 obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
+obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
 obj-$(CONFIG_HI8435) += hi8435.o
 obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
 obj-$(CONFIG_MAX1027) += max1027.o
diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c
new file mode 100644
index 000000000000..eb9570876291
--- /dev/null
+++ b/drivers/iio/adc/fsl-imx25-gcq.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ * This is the driver for the imx25 GCQ (Generic Conversion Queue)
+ * connected to the imx25 ADC.
+ */
+
+#include <dt-bindings/iio/adc/fsl-imx25-gcq.h>
+#include <linux/clk.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/imx25-tsadc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define MX25_GCQ_TIMEOUT (msecs_to_jiffies(2000))
+
+static const char * const driver_name = "mx25-gcq";
+
+enum mx25_gcq_cfgs {
+	MX25_CFG_XP = 0,
+	MX25_CFG_YP,
+	MX25_CFG_XN,
+	MX25_CFG_YN,
+	MX25_CFG_WIPER,
+	MX25_CFG_INAUX0,
+	MX25_CFG_INAUX1,
+	MX25_CFG_INAUX2,
+	MX25_NUM_CFGS,
+};
+
+struct mx25_gcq_priv {
+	struct regmap *regs;
+	struct completion completed;
+	struct clk *clk;
+	int irq;
+	struct regulator *vref[4];
+	u32 channel_vref_mv[MX25_NUM_CFGS];
+};
+
+#define MX25_CQG_CHAN(chan, id) {\
+	.type = IIO_VOLTAGE,\
+	.indexed = 1,\
+	.channel = chan,\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+			      BIT(IIO_CHAN_INFO_SCALE),\
+	.datasheet_name = id,\
+}
+
+static const struct iio_chan_spec mx25_gcq_channels[MX25_NUM_CFGS] = {
+	MX25_CQG_CHAN(MX25_CFG_XP, "xp"),
+	MX25_CQG_CHAN(MX25_CFG_YP, "yp"),
+	MX25_CQG_CHAN(MX25_CFG_XN, "xn"),
+	MX25_CQG_CHAN(MX25_CFG_YN, "yn"),
+	MX25_CQG_CHAN(MX25_CFG_WIPER, "wiper"),
+	MX25_CQG_CHAN(MX25_CFG_INAUX0, "inaux0"),
+	MX25_CQG_CHAN(MX25_CFG_INAUX1, "inaux1"),
+	MX25_CQG_CHAN(MX25_CFG_INAUX2, "inaux2"),
+};
+
+static const char * const mx25_gcq_refp_names[] = {
+	[MX25_ADC_REFP_YP] = "yp",
+	[MX25_ADC_REFP_XP] = "xp",
+	[MX25_ADC_REFP_INT] = "int",
+	[MX25_ADC_REFP_EXT] = "ext",
+};
+
+static irqreturn_t mx25_gcq_irq(int irq, void *data)
+{
+	struct mx25_gcq_priv *priv = data;
+	u32 stats;
+
+	regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
+
+	if (stats & MX25_ADCQ_SR_EOQ) {
+		regmap_update_bits(priv->regs, MX25_ADCQ_MR,
+				   MX25_ADCQ_MR_EOQ_IRQ, MX25_ADCQ_MR_EOQ_IRQ);
+		complete(&priv->completed);
+	}
+
+	/* Disable conversion queue run */
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS, 0);
+
+	/* Acknowledge all possible irqs */
+	regmap_write(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
+		     MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
+		     MX25_ADCQ_SR_EOQ | MX25_ADCQ_SR_PD);
+
+	return IRQ_HANDLED;
+}
+
+static int mx25_gcq_get_raw_value(struct device *dev,
+				  struct iio_chan_spec const *chan,
+				  struct mx25_gcq_priv *priv,
+				  int *val)
+{
+	long timeout;
+	u32 data;
+
+	/* Setup the configuration we want to use */
+	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
+		     MX25_ADCQ_ITEM(0, chan->channel));
+
+	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_EOQ_IRQ, 0);
+
+	/* Trigger queue for one run */
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS,
+			   MX25_ADCQ_CR_FQS);
+
+	timeout = wait_for_completion_interruptible_timeout(
+		&priv->completed, MX25_GCQ_TIMEOUT);
+	if (timeout < 0) {
+		dev_err(dev, "ADC wait for measurement failed\n");
+		return timeout;
+	} else if (timeout == 0) {
+		dev_err(dev, "ADC timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	regmap_read(priv->regs, MX25_ADCQ_FIFO, &data);
+
+	*val = MX25_ADCQ_FIFO_DATA(data);
+
+	return IIO_VAL_INT;
+}
+
+static int mx25_gcq_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan, int *val,
+			     int *val2, long mask)
+{
+	struct mx25_gcq_priv *priv = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&indio_dev->mlock);
+		ret = mx25_gcq_get_raw_value(&indio_dev->dev, chan, priv, val);
+		mutex_unlock(&indio_dev->mlock);
+		return ret;
+
+	case IIO_CHAN_INFO_SCALE:
+		*val = priv->channel_vref_mv[chan->channel];
+		*val2 = 12;
+		return IIO_VAL_FRACTIONAL_LOG2;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info mx25_gcq_iio_info = {
+	.read_raw = mx25_gcq_read_raw,
+};
+
+static const struct regmap_config mx25_gcq_regconfig = {
+	.max_register = 0x5c,
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
+			       struct mx25_gcq_priv *priv)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *child;
+	struct device *dev = &pdev->dev;
+	unsigned int refp_used[4] = {};
+	int ret, i;
+
+	/*
+	 * Setup all configurations registers with a default conversion
+	 * configuration for each input
+	 */
+	for (i = 0; i < MX25_NUM_CFGS; ++i)
+		regmap_write(priv->regs, MX25_ADCQ_CFG(i),
+			     MX25_ADCQ_CFG_YPLL_OFF |
+			     MX25_ADCQ_CFG_XNUR_OFF |
+			     MX25_ADCQ_CFG_XPUL_OFF |
+			     MX25_ADCQ_CFG_REFP_INT |
+			     MX25_ADCQ_CFG_IN(i) |
+			     MX25_ADCQ_CFG_REFN_NGND2);
+
+	/*
+	 * First get all regulators to store them in channel_vref_mv if
+	 * necessary. Later we use that information for proper IIO scale
+	 * information.
+	 */
+	priv->vref[MX25_ADC_REFP_INT] = NULL;
+	priv->vref[MX25_ADC_REFP_EXT] =
+		devm_regulator_get_optional(&pdev->dev, "vref-ext");
+	priv->vref[MX25_ADC_REFP_XP] =
+		devm_regulator_get_optional(&pdev->dev, "vref-xp");
+	priv->vref[MX25_ADC_REFP_YP] =
+		devm_regulator_get_optional(&pdev->dev, "vref-yp");
+
+	for_each_child_of_node(np, child) {
+		u32 reg;
+		u32 refp = MX25_ADCQ_CFG_REFP_INT;
+		u32 refn = MX25_ADCQ_CFG_REFN_NGND2;
+
+		ret = of_property_read_u32(child, "reg", &reg);
+		if (ret) {
+			dev_err(dev, "Failed to get reg property\n");
+			return ret;
+		}
+
+		if (reg >= MX25_NUM_CFGS) {
+			dev_err(dev,
+				"reg value is greater than the number of available configuration registers\n");
+			return -EINVAL;
+		}
+
+		of_property_read_u32(child, "fsl,adc-refp", &refp);
+		of_property_read_u32(child, "fsl,adc-refn", &refn);
+
+		switch (refp) {
+		case MX25_ADC_REFP_EXT:
+		case MX25_ADC_REFP_XP:
+		case MX25_ADC_REFP_YP:
+			if (IS_ERR(priv->vref[refp])) {
+				dev_err(dev, "Error, trying to use external voltage reference without a vref-%s regulator.",
+					mx25_gcq_refp_names[refp]);
+				return PTR_ERR(priv->vref[refp]);
+			}
+			priv->channel_vref_mv[reg] =
+				regulator_get_voltage(priv->vref[refp]);
+			/* Conversion from uV to mV */
+			do_div(priv->channel_vref_mv[reg], 1000);
+			break;
+		case MX25_ADC_REFP_INT:
+			priv->channel_vref_mv[reg] = 2500;
+			break;
+		default:
+			dev_err(dev, "Invalid positive reference %d\n", refp);
+			return -EINVAL;
+		}
+
+		++refp_used[refp];
+
+		/*
+		 * Shift the read values to the correct positions within the
+		 * register.
+		 */
+		refp = MX25_ADCQ_CFG_REFP(refp);
+		refn = MX25_ADCQ_CFG_REFN(refn);
+
+		if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) {
+			dev_err(dev, "Invalid fsl,adc-refp property value\n");
+			return -EINVAL;
+		}
+		if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) {
+			dev_err(dev, "Invalid fsl,adc-refn property value\n");
+			return -EINVAL;
+		}
+
+		regmap_update_bits(priv->regs, MX25_ADCQ_CFG(reg),
+				   MX25_ADCQ_CFG_REFP_MASK |
+				   MX25_ADCQ_CFG_REFN_MASK,
+				   refp | refn);
+	}
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST,
+			   MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST);
+
+	regmap_write(priv->regs, MX25_ADCQ_CR,
+		     MX25_ADCQ_CR_PDMSK | MX25_ADCQ_CR_QSM_FQS);
+
+	/* Remove unused regulators */
+	for (i = 0; i != 4; ++i) {
+		if (!refp_used[i]) {
+			if (!IS_ERR_OR_NULL(priv->vref[i]))
+				devm_regulator_put(priv->vref[i]);
+			priv->vref[i] = NULL;
+		}
+	}
+
+	return 0;
+}
+
+static int mx25_gcq_probe(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev;
+	struct mx25_gcq_priv *priv;
+	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	void __iomem *mem;
+	int ret;
+	int i;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	priv = iio_priv(indio_dev);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mem = devm_ioremap_resource(dev, res);
+	if (IS_ERR(mem))
+		return PTR_ERR(mem);
+
+	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_gcq_regconfig);
+	if (IS_ERR(priv->regs)) {
+		dev_err(dev, "Failed to initialize regmap\n");
+		return PTR_ERR(priv->regs);
+	}
+
+	init_completion(&priv->completed);
+
+	ret = mx25_gcq_setup_cfgs(pdev, priv);
+	if (ret)
+		return ret;
+
+	for (i = 0; i != 4; ++i) {
+		if (!priv->vref[i])
+			continue;
+
+		ret = regulator_enable(priv->vref[i]);
+		if (ret)
+			goto err_regulator_disable;
+	}
+
+	priv->clk = tsadc->clk;
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(dev, "Failed to enable clock\n");
+		goto err_vref_disable;
+	}
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (priv->irq <= 0) {
+		dev_err(dev, "Failed to get IRQ\n");
+		ret = priv->irq;
+		goto err_clk_unprepare;
+	}
+
+	ret = request_irq(priv->irq, mx25_gcq_irq, 0, pdev->name, priv);
+	if (ret) {
+		dev_err(dev, "Failed requesting IRQ\n");
+		goto err_clk_unprepare;
+	}
+
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->channels = mx25_gcq_channels;
+	indio_dev->num_channels = ARRAY_SIZE(mx25_gcq_channels);
+	indio_dev->info = &mx25_gcq_iio_info;
+	indio_dev->name = driver_name;
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(dev, "Failed to register iio device\n");
+		goto err_irq_free;
+	}
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	return 0;
+
+err_irq_free:
+	free_irq(priv->irq, (void *)priv);
+err_clk_unprepare:
+	clk_disable_unprepare(priv->clk);
+err_vref_disable:
+	i = 4;
+err_regulator_disable:
+	for (; i-- > 0;) {
+		if (priv->vref[i])
+			regulator_disable(priv->vref[i]);
+	}
+	return ret;
+}
+
+static int mx25_gcq_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct mx25_gcq_priv *priv = iio_priv(indio_dev);
+	int i;
+
+	iio_device_unregister(indio_dev);
+	free_irq(priv->irq, priv);
+	clk_disable_unprepare(priv->clk);
+	for (i = 4; i-- > 0;) {
+		if (priv->vref[i])
+			regulator_disable(priv->vref[i]);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id mx25_gcq_ids[] = {
+	{ .compatible = "fsl,imx25-gcq", },
+	{ /* Sentinel */ }
+};
+
+static struct platform_driver mx25_gcq_driver = {
+	.driver		= {
+		.name	= "mx25-gcq",
+		.of_match_table = mx25_gcq_ids,
+	},
+	.probe		= mx25_gcq_probe,
+	.remove		= mx25_gcq_remove,
+};
+module_platform_driver(mx25_gcq_driver);
+
+MODULE_DESCRIPTION("ADC driver for Freescale mx25");
+MODULE_AUTHOR("Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/dt-bindings/iio/adc/fsl-imx25-gcq.h b/include/dt-bindings/iio/adc/fsl-imx25-gcq.h
new file mode 100644
index 000000000000..87abdd4a7674
--- /dev/null
+++ b/include/dt-bindings/iio/adc/fsl-imx25-gcq.h
@@ -0,0 +1,18 @@
+/*
+ * This header provides constants for configuring the I.MX25 ADC
+ */
+
+#ifndef _DT_BINDINGS_IIO_ADC_FS_IMX25_GCQ_H
+#define _DT_BINDINGS_IIO_ADC_FS_IMX25_GCQ_H
+
+#define MX25_ADC_REFP_YP	0 /* YP voltage reference */
+#define MX25_ADC_REFP_XP	1 /* XP voltage reference */
+#define MX25_ADC_REFP_EXT	2 /* External voltage reference */
+#define MX25_ADC_REFP_INT	3 /* Internal voltage reference */
+
+#define MX25_ADC_REFN_XN	0 /* XN ground reference */
+#define MX25_ADC_REFN_YN	1 /* YN ground reference */
+#define MX25_ADC_REFN_NGND	2 /* Internal ground reference */
+#define MX25_ADC_REFN_NGND2	3 /* External ground reference */
+
+#endif
-- 
2.6.1

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

* [PATCH v8 5/8] iio: adc: fsl,imx25-gcq driver
@ 2015-11-16 12:01     ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam, Markus Pargmann

This is a conversion queue driver for the mx25 SoC. It uses the central
ADC which is used by two seperate independent queues. This driver
prepares different conversion configurations for each possible input.
For a conversion it creates a conversionqueue of one item with the
correct configuration for the chosen channel. It then executes the queue
once and disables the conversion queue afterwards.

The reference voltages are configurable through devicetree subnodes,
depending on the connections of the ADC inputs.

Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
Signed-off-by: Denis Carikli <denis@eukrea.com>
---

Notes:
    Changes in v7:
     - Remove separate functions mx25_gcq_disable/enable_eoq() as they were used at
       only one position
     - Enforce an external reference regulator if one of the conversions uses it as
       reference. The devm_regulator_get() call was moved into
       mx25_gcq_setup_cfgs() to be able to acquire the reference regulator when
       necessary.
     - Store indio_dev as platform driver data instead of the private data. This
       was changed in probe() and remove().
    
    Changes in v6:
     - Added defines for a complete list of references in the dt binding macros

 drivers/iio/adc/Kconfig                     |   7 +
 drivers/iio/adc/Makefile                    |   1 +
 drivers/iio/adc/fsl-imx25-gcq.c             | 415 ++++++++++++++++++++++++++++
 include/dt-bindings/iio/adc/fsl-imx25-gcq.h |  18 ++
 4 files changed, 441 insertions(+)
 create mode 100644 drivers/iio/adc/fsl-imx25-gcq.c
 create mode 100644 include/dt-bindings/iio/adc/fsl-imx25-gcq.h

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7868c744fd4b..73145c53ec2c 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -183,6 +183,13 @@ config EXYNOS_ADC
 	  To compile this driver as a module, choose M here: the module will be
 	  called exynos_adc.
 
+config FSL_MX25_ADC
+	tristate "Freescale MX25 ADC driver"
+	depends on MFD_MX25_TSADC
+	help
+	  Generic Conversion Queue driver used for general purpose ADC in the
+	  MX25. This driver supports single measurements using the MX25 ADC.
+
 config HI8435
 	tristate "Holt Integrated Circuits HI-8435 threshold detector"
 	select IIO_TRIGGERED_EVENT
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 99b37a963a1e..2fe9b78e4b02 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
 obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
 obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
+obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
 obj-$(CONFIG_HI8435) += hi8435.o
 obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
 obj-$(CONFIG_MAX1027) += max1027.o
diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c
new file mode 100644
index 000000000000..eb9570876291
--- /dev/null
+++ b/drivers/iio/adc/fsl-imx25-gcq.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ * This is the driver for the imx25 GCQ (Generic Conversion Queue)
+ * connected to the imx25 ADC.
+ */
+
+#include <dt-bindings/iio/adc/fsl-imx25-gcq.h>
+#include <linux/clk.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/imx25-tsadc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define MX25_GCQ_TIMEOUT (msecs_to_jiffies(2000))
+
+static const char * const driver_name = "mx25-gcq";
+
+enum mx25_gcq_cfgs {
+	MX25_CFG_XP = 0,
+	MX25_CFG_YP,
+	MX25_CFG_XN,
+	MX25_CFG_YN,
+	MX25_CFG_WIPER,
+	MX25_CFG_INAUX0,
+	MX25_CFG_INAUX1,
+	MX25_CFG_INAUX2,
+	MX25_NUM_CFGS,
+};
+
+struct mx25_gcq_priv {
+	struct regmap *regs;
+	struct completion completed;
+	struct clk *clk;
+	int irq;
+	struct regulator *vref[4];
+	u32 channel_vref_mv[MX25_NUM_CFGS];
+};
+
+#define MX25_CQG_CHAN(chan, id) {\
+	.type = IIO_VOLTAGE,\
+	.indexed = 1,\
+	.channel = chan,\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+			      BIT(IIO_CHAN_INFO_SCALE),\
+	.datasheet_name = id,\
+}
+
+static const struct iio_chan_spec mx25_gcq_channels[MX25_NUM_CFGS] = {
+	MX25_CQG_CHAN(MX25_CFG_XP, "xp"),
+	MX25_CQG_CHAN(MX25_CFG_YP, "yp"),
+	MX25_CQG_CHAN(MX25_CFG_XN, "xn"),
+	MX25_CQG_CHAN(MX25_CFG_YN, "yn"),
+	MX25_CQG_CHAN(MX25_CFG_WIPER, "wiper"),
+	MX25_CQG_CHAN(MX25_CFG_INAUX0, "inaux0"),
+	MX25_CQG_CHAN(MX25_CFG_INAUX1, "inaux1"),
+	MX25_CQG_CHAN(MX25_CFG_INAUX2, "inaux2"),
+};
+
+static const char * const mx25_gcq_refp_names[] = {
+	[MX25_ADC_REFP_YP] = "yp",
+	[MX25_ADC_REFP_XP] = "xp",
+	[MX25_ADC_REFP_INT] = "int",
+	[MX25_ADC_REFP_EXT] = "ext",
+};
+
+static irqreturn_t mx25_gcq_irq(int irq, void *data)
+{
+	struct mx25_gcq_priv *priv = data;
+	u32 stats;
+
+	regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
+
+	if (stats & MX25_ADCQ_SR_EOQ) {
+		regmap_update_bits(priv->regs, MX25_ADCQ_MR,
+				   MX25_ADCQ_MR_EOQ_IRQ, MX25_ADCQ_MR_EOQ_IRQ);
+		complete(&priv->completed);
+	}
+
+	/* Disable conversion queue run */
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS, 0);
+
+	/* Acknowledge all possible irqs */
+	regmap_write(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
+		     MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
+		     MX25_ADCQ_SR_EOQ | MX25_ADCQ_SR_PD);
+
+	return IRQ_HANDLED;
+}
+
+static int mx25_gcq_get_raw_value(struct device *dev,
+				  struct iio_chan_spec const *chan,
+				  struct mx25_gcq_priv *priv,
+				  int *val)
+{
+	long timeout;
+	u32 data;
+
+	/* Setup the configuration we want to use */
+	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
+		     MX25_ADCQ_ITEM(0, chan->channel));
+
+	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_EOQ_IRQ, 0);
+
+	/* Trigger queue for one run */
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS,
+			   MX25_ADCQ_CR_FQS);
+
+	timeout = wait_for_completion_interruptible_timeout(
+		&priv->completed, MX25_GCQ_TIMEOUT);
+	if (timeout < 0) {
+		dev_err(dev, "ADC wait for measurement failed\n");
+		return timeout;
+	} else if (timeout == 0) {
+		dev_err(dev, "ADC timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	regmap_read(priv->regs, MX25_ADCQ_FIFO, &data);
+
+	*val = MX25_ADCQ_FIFO_DATA(data);
+
+	return IIO_VAL_INT;
+}
+
+static int mx25_gcq_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan, int *val,
+			     int *val2, long mask)
+{
+	struct mx25_gcq_priv *priv = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&indio_dev->mlock);
+		ret = mx25_gcq_get_raw_value(&indio_dev->dev, chan, priv, val);
+		mutex_unlock(&indio_dev->mlock);
+		return ret;
+
+	case IIO_CHAN_INFO_SCALE:
+		*val = priv->channel_vref_mv[chan->channel];
+		*val2 = 12;
+		return IIO_VAL_FRACTIONAL_LOG2;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info mx25_gcq_iio_info = {
+	.read_raw = mx25_gcq_read_raw,
+};
+
+static const struct regmap_config mx25_gcq_regconfig = {
+	.max_register = 0x5c,
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
+			       struct mx25_gcq_priv *priv)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *child;
+	struct device *dev = &pdev->dev;
+	unsigned int refp_used[4] = {};
+	int ret, i;
+
+	/*
+	 * Setup all configurations registers with a default conversion
+	 * configuration for each input
+	 */
+	for (i = 0; i < MX25_NUM_CFGS; ++i)
+		regmap_write(priv->regs, MX25_ADCQ_CFG(i),
+			     MX25_ADCQ_CFG_YPLL_OFF |
+			     MX25_ADCQ_CFG_XNUR_OFF |
+			     MX25_ADCQ_CFG_XPUL_OFF |
+			     MX25_ADCQ_CFG_REFP_INT |
+			     MX25_ADCQ_CFG_IN(i) |
+			     MX25_ADCQ_CFG_REFN_NGND2);
+
+	/*
+	 * First get all regulators to store them in channel_vref_mv if
+	 * necessary. Later we use that information for proper IIO scale
+	 * information.
+	 */
+	priv->vref[MX25_ADC_REFP_INT] = NULL;
+	priv->vref[MX25_ADC_REFP_EXT] =
+		devm_regulator_get_optional(&pdev->dev, "vref-ext");
+	priv->vref[MX25_ADC_REFP_XP] =
+		devm_regulator_get_optional(&pdev->dev, "vref-xp");
+	priv->vref[MX25_ADC_REFP_YP] =
+		devm_regulator_get_optional(&pdev->dev, "vref-yp");
+
+	for_each_child_of_node(np, child) {
+		u32 reg;
+		u32 refp = MX25_ADCQ_CFG_REFP_INT;
+		u32 refn = MX25_ADCQ_CFG_REFN_NGND2;
+
+		ret = of_property_read_u32(child, "reg", &reg);
+		if (ret) {
+			dev_err(dev, "Failed to get reg property\n");
+			return ret;
+		}
+
+		if (reg >= MX25_NUM_CFGS) {
+			dev_err(dev,
+				"reg value is greater than the number of available configuration registers\n");
+			return -EINVAL;
+		}
+
+		of_property_read_u32(child, "fsl,adc-refp", &refp);
+		of_property_read_u32(child, "fsl,adc-refn", &refn);
+
+		switch (refp) {
+		case MX25_ADC_REFP_EXT:
+		case MX25_ADC_REFP_XP:
+		case MX25_ADC_REFP_YP:
+			if (IS_ERR(priv->vref[refp])) {
+				dev_err(dev, "Error, trying to use external voltage reference without a vref-%s regulator.",
+					mx25_gcq_refp_names[refp]);
+				return PTR_ERR(priv->vref[refp]);
+			}
+			priv->channel_vref_mv[reg] =
+				regulator_get_voltage(priv->vref[refp]);
+			/* Conversion from uV to mV */
+			do_div(priv->channel_vref_mv[reg], 1000);
+			break;
+		case MX25_ADC_REFP_INT:
+			priv->channel_vref_mv[reg] = 2500;
+			break;
+		default:
+			dev_err(dev, "Invalid positive reference %d\n", refp);
+			return -EINVAL;
+		}
+
+		++refp_used[refp];
+
+		/*
+		 * Shift the read values to the correct positions within the
+		 * register.
+		 */
+		refp = MX25_ADCQ_CFG_REFP(refp);
+		refn = MX25_ADCQ_CFG_REFN(refn);
+
+		if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) {
+			dev_err(dev, "Invalid fsl,adc-refp property value\n");
+			return -EINVAL;
+		}
+		if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) {
+			dev_err(dev, "Invalid fsl,adc-refn property value\n");
+			return -EINVAL;
+		}
+
+		regmap_update_bits(priv->regs, MX25_ADCQ_CFG(reg),
+				   MX25_ADCQ_CFG_REFP_MASK |
+				   MX25_ADCQ_CFG_REFN_MASK,
+				   refp | refn);
+	}
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST,
+			   MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST);
+
+	regmap_write(priv->regs, MX25_ADCQ_CR,
+		     MX25_ADCQ_CR_PDMSK | MX25_ADCQ_CR_QSM_FQS);
+
+	/* Remove unused regulators */
+	for (i = 0; i != 4; ++i) {
+		if (!refp_used[i]) {
+			if (!IS_ERR_OR_NULL(priv->vref[i]))
+				devm_regulator_put(priv->vref[i]);
+			priv->vref[i] = NULL;
+		}
+	}
+
+	return 0;
+}
+
+static int mx25_gcq_probe(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev;
+	struct mx25_gcq_priv *priv;
+	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	void __iomem *mem;
+	int ret;
+	int i;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	priv = iio_priv(indio_dev);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mem = devm_ioremap_resource(dev, res);
+	if (IS_ERR(mem))
+		return PTR_ERR(mem);
+
+	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_gcq_regconfig);
+	if (IS_ERR(priv->regs)) {
+		dev_err(dev, "Failed to initialize regmap\n");
+		return PTR_ERR(priv->regs);
+	}
+
+	init_completion(&priv->completed);
+
+	ret = mx25_gcq_setup_cfgs(pdev, priv);
+	if (ret)
+		return ret;
+
+	for (i = 0; i != 4; ++i) {
+		if (!priv->vref[i])
+			continue;
+
+		ret = regulator_enable(priv->vref[i]);
+		if (ret)
+			goto err_regulator_disable;
+	}
+
+	priv->clk = tsadc->clk;
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(dev, "Failed to enable clock\n");
+		goto err_vref_disable;
+	}
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (priv->irq <= 0) {
+		dev_err(dev, "Failed to get IRQ\n");
+		ret = priv->irq;
+		goto err_clk_unprepare;
+	}
+
+	ret = request_irq(priv->irq, mx25_gcq_irq, 0, pdev->name, priv);
+	if (ret) {
+		dev_err(dev, "Failed requesting IRQ\n");
+		goto err_clk_unprepare;
+	}
+
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->channels = mx25_gcq_channels;
+	indio_dev->num_channels = ARRAY_SIZE(mx25_gcq_channels);
+	indio_dev->info = &mx25_gcq_iio_info;
+	indio_dev->name = driver_name;
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(dev, "Failed to register iio device\n");
+		goto err_irq_free;
+	}
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	return 0;
+
+err_irq_free:
+	free_irq(priv->irq, (void *)priv);
+err_clk_unprepare:
+	clk_disable_unprepare(priv->clk);
+err_vref_disable:
+	i = 4;
+err_regulator_disable:
+	for (; i-- > 0;) {
+		if (priv->vref[i])
+			regulator_disable(priv->vref[i]);
+	}
+	return ret;
+}
+
+static int mx25_gcq_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct mx25_gcq_priv *priv = iio_priv(indio_dev);
+	int i;
+
+	iio_device_unregister(indio_dev);
+	free_irq(priv->irq, priv);
+	clk_disable_unprepare(priv->clk);
+	for (i = 4; i-- > 0;) {
+		if (priv->vref[i])
+			regulator_disable(priv->vref[i]);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id mx25_gcq_ids[] = {
+	{ .compatible = "fsl,imx25-gcq", },
+	{ /* Sentinel */ }
+};
+
+static struct platform_driver mx25_gcq_driver = {
+	.driver		= {
+		.name	= "mx25-gcq",
+		.of_match_table = mx25_gcq_ids,
+	},
+	.probe		= mx25_gcq_probe,
+	.remove		= mx25_gcq_remove,
+};
+module_platform_driver(mx25_gcq_driver);
+
+MODULE_DESCRIPTION("ADC driver for Freescale mx25");
+MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/dt-bindings/iio/adc/fsl-imx25-gcq.h b/include/dt-bindings/iio/adc/fsl-imx25-gcq.h
new file mode 100644
index 000000000000..87abdd4a7674
--- /dev/null
+++ b/include/dt-bindings/iio/adc/fsl-imx25-gcq.h
@@ -0,0 +1,18 @@
+/*
+ * This header provides constants for configuring the I.MX25 ADC
+ */
+
+#ifndef _DT_BINDINGS_IIO_ADC_FS_IMX25_GCQ_H
+#define _DT_BINDINGS_IIO_ADC_FS_IMX25_GCQ_H
+
+#define MX25_ADC_REFP_YP	0 /* YP voltage reference */
+#define MX25_ADC_REFP_XP	1 /* XP voltage reference */
+#define MX25_ADC_REFP_EXT	2 /* External voltage reference */
+#define MX25_ADC_REFP_INT	3 /* Internal voltage reference */
+
+#define MX25_ADC_REFN_XN	0 /* XN ground reference */
+#define MX25_ADC_REFN_YN	1 /* YN ground reference */
+#define MX25_ADC_REFN_NGND	2 /* Internal ground reference */
+#define MX25_ADC_REFN_NGND2	3 /* External ground reference */
+
+#endif
-- 
2.6.1


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

* [PATCH v8 5/8] iio: adc: fsl,imx25-gcq driver
@ 2015-11-16 12:01     ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: linux-arm-kernel

This is a conversion queue driver for the mx25 SoC. It uses the central
ADC which is used by two seperate independent queues. This driver
prepares different conversion configurations for each possible input.
For a conversion it creates a conversionqueue of one item with the
correct configuration for the chosen channel. It then executes the queue
once and disables the conversion queue afterwards.

The reference voltages are configurable through devicetree subnodes,
depending on the connections of the ADC inputs.

Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
Signed-off-by: Denis Carikli <denis@eukrea.com>
---

Notes:
    Changes in v7:
     - Remove separate functions mx25_gcq_disable/enable_eoq() as they were used at
       only one position
     - Enforce an external reference regulator if one of the conversions uses it as
       reference. The devm_regulator_get() call was moved into
       mx25_gcq_setup_cfgs() to be able to acquire the reference regulator when
       necessary.
     - Store indio_dev as platform driver data instead of the private data. This
       was changed in probe() and remove().
    
    Changes in v6:
     - Added defines for a complete list of references in the dt binding macros

 drivers/iio/adc/Kconfig                     |   7 +
 drivers/iio/adc/Makefile                    |   1 +
 drivers/iio/adc/fsl-imx25-gcq.c             | 415 ++++++++++++++++++++++++++++
 include/dt-bindings/iio/adc/fsl-imx25-gcq.h |  18 ++
 4 files changed, 441 insertions(+)
 create mode 100644 drivers/iio/adc/fsl-imx25-gcq.c
 create mode 100644 include/dt-bindings/iio/adc/fsl-imx25-gcq.h

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7868c744fd4b..73145c53ec2c 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -183,6 +183,13 @@ config EXYNOS_ADC
 	  To compile this driver as a module, choose M here: the module will be
 	  called exynos_adc.
 
+config FSL_MX25_ADC
+	tristate "Freescale MX25 ADC driver"
+	depends on MFD_MX25_TSADC
+	help
+	  Generic Conversion Queue driver used for general purpose ADC in the
+	  MX25. This driver supports single measurements using the MX25 ADC.
+
 config HI8435
 	tristate "Holt Integrated Circuits HI-8435 threshold detector"
 	select IIO_TRIGGERED_EVENT
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 99b37a963a1e..2fe9b78e4b02 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
 obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
 obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
+obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
 obj-$(CONFIG_HI8435) += hi8435.o
 obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
 obj-$(CONFIG_MAX1027) += max1027.o
diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c
new file mode 100644
index 000000000000..eb9570876291
--- /dev/null
+++ b/drivers/iio/adc/fsl-imx25-gcq.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ * This is the driver for the imx25 GCQ (Generic Conversion Queue)
+ * connected to the imx25 ADC.
+ */
+
+#include <dt-bindings/iio/adc/fsl-imx25-gcq.h>
+#include <linux/clk.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/imx25-tsadc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define MX25_GCQ_TIMEOUT (msecs_to_jiffies(2000))
+
+static const char * const driver_name = "mx25-gcq";
+
+enum mx25_gcq_cfgs {
+	MX25_CFG_XP = 0,
+	MX25_CFG_YP,
+	MX25_CFG_XN,
+	MX25_CFG_YN,
+	MX25_CFG_WIPER,
+	MX25_CFG_INAUX0,
+	MX25_CFG_INAUX1,
+	MX25_CFG_INAUX2,
+	MX25_NUM_CFGS,
+};
+
+struct mx25_gcq_priv {
+	struct regmap *regs;
+	struct completion completed;
+	struct clk *clk;
+	int irq;
+	struct regulator *vref[4];
+	u32 channel_vref_mv[MX25_NUM_CFGS];
+};
+
+#define MX25_CQG_CHAN(chan, id) {\
+	.type = IIO_VOLTAGE,\
+	.indexed = 1,\
+	.channel = chan,\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+			      BIT(IIO_CHAN_INFO_SCALE),\
+	.datasheet_name = id,\
+}
+
+static const struct iio_chan_spec mx25_gcq_channels[MX25_NUM_CFGS] = {
+	MX25_CQG_CHAN(MX25_CFG_XP, "xp"),
+	MX25_CQG_CHAN(MX25_CFG_YP, "yp"),
+	MX25_CQG_CHAN(MX25_CFG_XN, "xn"),
+	MX25_CQG_CHAN(MX25_CFG_YN, "yn"),
+	MX25_CQG_CHAN(MX25_CFG_WIPER, "wiper"),
+	MX25_CQG_CHAN(MX25_CFG_INAUX0, "inaux0"),
+	MX25_CQG_CHAN(MX25_CFG_INAUX1, "inaux1"),
+	MX25_CQG_CHAN(MX25_CFG_INAUX2, "inaux2"),
+};
+
+static const char * const mx25_gcq_refp_names[] = {
+	[MX25_ADC_REFP_YP] = "yp",
+	[MX25_ADC_REFP_XP] = "xp",
+	[MX25_ADC_REFP_INT] = "int",
+	[MX25_ADC_REFP_EXT] = "ext",
+};
+
+static irqreturn_t mx25_gcq_irq(int irq, void *data)
+{
+	struct mx25_gcq_priv *priv = data;
+	u32 stats;
+
+	regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
+
+	if (stats & MX25_ADCQ_SR_EOQ) {
+		regmap_update_bits(priv->regs, MX25_ADCQ_MR,
+				   MX25_ADCQ_MR_EOQ_IRQ, MX25_ADCQ_MR_EOQ_IRQ);
+		complete(&priv->completed);
+	}
+
+	/* Disable conversion queue run */
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS, 0);
+
+	/* Acknowledge all possible irqs */
+	regmap_write(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
+		     MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
+		     MX25_ADCQ_SR_EOQ | MX25_ADCQ_SR_PD);
+
+	return IRQ_HANDLED;
+}
+
+static int mx25_gcq_get_raw_value(struct device *dev,
+				  struct iio_chan_spec const *chan,
+				  struct mx25_gcq_priv *priv,
+				  int *val)
+{
+	long timeout;
+	u32 data;
+
+	/* Setup the configuration we want to use */
+	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
+		     MX25_ADCQ_ITEM(0, chan->channel));
+
+	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_EOQ_IRQ, 0);
+
+	/* Trigger queue for one run */
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS,
+			   MX25_ADCQ_CR_FQS);
+
+	timeout = wait_for_completion_interruptible_timeout(
+		&priv->completed, MX25_GCQ_TIMEOUT);
+	if (timeout < 0) {
+		dev_err(dev, "ADC wait for measurement failed\n");
+		return timeout;
+	} else if (timeout == 0) {
+		dev_err(dev, "ADC timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	regmap_read(priv->regs, MX25_ADCQ_FIFO, &data);
+
+	*val = MX25_ADCQ_FIFO_DATA(data);
+
+	return IIO_VAL_INT;
+}
+
+static int mx25_gcq_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan, int *val,
+			     int *val2, long mask)
+{
+	struct mx25_gcq_priv *priv = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&indio_dev->mlock);
+		ret = mx25_gcq_get_raw_value(&indio_dev->dev, chan, priv, val);
+		mutex_unlock(&indio_dev->mlock);
+		return ret;
+
+	case IIO_CHAN_INFO_SCALE:
+		*val = priv->channel_vref_mv[chan->channel];
+		*val2 = 12;
+		return IIO_VAL_FRACTIONAL_LOG2;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info mx25_gcq_iio_info = {
+	.read_raw = mx25_gcq_read_raw,
+};
+
+static const struct regmap_config mx25_gcq_regconfig = {
+	.max_register = 0x5c,
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
+			       struct mx25_gcq_priv *priv)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *child;
+	struct device *dev = &pdev->dev;
+	unsigned int refp_used[4] = {};
+	int ret, i;
+
+	/*
+	 * Setup all configurations registers with a default conversion
+	 * configuration for each input
+	 */
+	for (i = 0; i < MX25_NUM_CFGS; ++i)
+		regmap_write(priv->regs, MX25_ADCQ_CFG(i),
+			     MX25_ADCQ_CFG_YPLL_OFF |
+			     MX25_ADCQ_CFG_XNUR_OFF |
+			     MX25_ADCQ_CFG_XPUL_OFF |
+			     MX25_ADCQ_CFG_REFP_INT |
+			     MX25_ADCQ_CFG_IN(i) |
+			     MX25_ADCQ_CFG_REFN_NGND2);
+
+	/*
+	 * First get all regulators to store them in channel_vref_mv if
+	 * necessary. Later we use that information for proper IIO scale
+	 * information.
+	 */
+	priv->vref[MX25_ADC_REFP_INT] = NULL;
+	priv->vref[MX25_ADC_REFP_EXT] =
+		devm_regulator_get_optional(&pdev->dev, "vref-ext");
+	priv->vref[MX25_ADC_REFP_XP] =
+		devm_regulator_get_optional(&pdev->dev, "vref-xp");
+	priv->vref[MX25_ADC_REFP_YP] =
+		devm_regulator_get_optional(&pdev->dev, "vref-yp");
+
+	for_each_child_of_node(np, child) {
+		u32 reg;
+		u32 refp = MX25_ADCQ_CFG_REFP_INT;
+		u32 refn = MX25_ADCQ_CFG_REFN_NGND2;
+
+		ret = of_property_read_u32(child, "reg", &reg);
+		if (ret) {
+			dev_err(dev, "Failed to get reg property\n");
+			return ret;
+		}
+
+		if (reg >= MX25_NUM_CFGS) {
+			dev_err(dev,
+				"reg value is greater than the number of available configuration registers\n");
+			return -EINVAL;
+		}
+
+		of_property_read_u32(child, "fsl,adc-refp", &refp);
+		of_property_read_u32(child, "fsl,adc-refn", &refn);
+
+		switch (refp) {
+		case MX25_ADC_REFP_EXT:
+		case MX25_ADC_REFP_XP:
+		case MX25_ADC_REFP_YP:
+			if (IS_ERR(priv->vref[refp])) {
+				dev_err(dev, "Error, trying to use external voltage reference without a vref-%s regulator.",
+					mx25_gcq_refp_names[refp]);
+				return PTR_ERR(priv->vref[refp]);
+			}
+			priv->channel_vref_mv[reg] =
+				regulator_get_voltage(priv->vref[refp]);
+			/* Conversion from uV to mV */
+			do_div(priv->channel_vref_mv[reg], 1000);
+			break;
+		case MX25_ADC_REFP_INT:
+			priv->channel_vref_mv[reg] = 2500;
+			break;
+		default:
+			dev_err(dev, "Invalid positive reference %d\n", refp);
+			return -EINVAL;
+		}
+
+		++refp_used[refp];
+
+		/*
+		 * Shift the read values to the correct positions within the
+		 * register.
+		 */
+		refp = MX25_ADCQ_CFG_REFP(refp);
+		refn = MX25_ADCQ_CFG_REFN(refn);
+
+		if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) {
+			dev_err(dev, "Invalid fsl,adc-refp property value\n");
+			return -EINVAL;
+		}
+		if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) {
+			dev_err(dev, "Invalid fsl,adc-refn property value\n");
+			return -EINVAL;
+		}
+
+		regmap_update_bits(priv->regs, MX25_ADCQ_CFG(reg),
+				   MX25_ADCQ_CFG_REFP_MASK |
+				   MX25_ADCQ_CFG_REFN_MASK,
+				   refp | refn);
+	}
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST,
+			   MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST);
+
+	regmap_write(priv->regs, MX25_ADCQ_CR,
+		     MX25_ADCQ_CR_PDMSK | MX25_ADCQ_CR_QSM_FQS);
+
+	/* Remove unused regulators */
+	for (i = 0; i != 4; ++i) {
+		if (!refp_used[i]) {
+			if (!IS_ERR_OR_NULL(priv->vref[i]))
+				devm_regulator_put(priv->vref[i]);
+			priv->vref[i] = NULL;
+		}
+	}
+
+	return 0;
+}
+
+static int mx25_gcq_probe(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev;
+	struct mx25_gcq_priv *priv;
+	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	void __iomem *mem;
+	int ret;
+	int i;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	priv = iio_priv(indio_dev);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mem = devm_ioremap_resource(dev, res);
+	if (IS_ERR(mem))
+		return PTR_ERR(mem);
+
+	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_gcq_regconfig);
+	if (IS_ERR(priv->regs)) {
+		dev_err(dev, "Failed to initialize regmap\n");
+		return PTR_ERR(priv->regs);
+	}
+
+	init_completion(&priv->completed);
+
+	ret = mx25_gcq_setup_cfgs(pdev, priv);
+	if (ret)
+		return ret;
+
+	for (i = 0; i != 4; ++i) {
+		if (!priv->vref[i])
+			continue;
+
+		ret = regulator_enable(priv->vref[i]);
+		if (ret)
+			goto err_regulator_disable;
+	}
+
+	priv->clk = tsadc->clk;
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(dev, "Failed to enable clock\n");
+		goto err_vref_disable;
+	}
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (priv->irq <= 0) {
+		dev_err(dev, "Failed to get IRQ\n");
+		ret = priv->irq;
+		goto err_clk_unprepare;
+	}
+
+	ret = request_irq(priv->irq, mx25_gcq_irq, 0, pdev->name, priv);
+	if (ret) {
+		dev_err(dev, "Failed requesting IRQ\n");
+		goto err_clk_unprepare;
+	}
+
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->channels = mx25_gcq_channels;
+	indio_dev->num_channels = ARRAY_SIZE(mx25_gcq_channels);
+	indio_dev->info = &mx25_gcq_iio_info;
+	indio_dev->name = driver_name;
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(dev, "Failed to register iio device\n");
+		goto err_irq_free;
+	}
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	return 0;
+
+err_irq_free:
+	free_irq(priv->irq, (void *)priv);
+err_clk_unprepare:
+	clk_disable_unprepare(priv->clk);
+err_vref_disable:
+	i = 4;
+err_regulator_disable:
+	for (; i-- > 0;) {
+		if (priv->vref[i])
+			regulator_disable(priv->vref[i]);
+	}
+	return ret;
+}
+
+static int mx25_gcq_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct mx25_gcq_priv *priv = iio_priv(indio_dev);
+	int i;
+
+	iio_device_unregister(indio_dev);
+	free_irq(priv->irq, priv);
+	clk_disable_unprepare(priv->clk);
+	for (i = 4; i-- > 0;) {
+		if (priv->vref[i])
+			regulator_disable(priv->vref[i]);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id mx25_gcq_ids[] = {
+	{ .compatible = "fsl,imx25-gcq", },
+	{ /* Sentinel */ }
+};
+
+static struct platform_driver mx25_gcq_driver = {
+	.driver		= {
+		.name	= "mx25-gcq",
+		.of_match_table = mx25_gcq_ids,
+	},
+	.probe		= mx25_gcq_probe,
+	.remove		= mx25_gcq_remove,
+};
+module_platform_driver(mx25_gcq_driver);
+
+MODULE_DESCRIPTION("ADC driver for Freescale mx25");
+MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/dt-bindings/iio/adc/fsl-imx25-gcq.h b/include/dt-bindings/iio/adc/fsl-imx25-gcq.h
new file mode 100644
index 000000000000..87abdd4a7674
--- /dev/null
+++ b/include/dt-bindings/iio/adc/fsl-imx25-gcq.h
@@ -0,0 +1,18 @@
+/*
+ * This header provides constants for configuring the I.MX25 ADC
+ */
+
+#ifndef _DT_BINDINGS_IIO_ADC_FS_IMX25_GCQ_H
+#define _DT_BINDINGS_IIO_ADC_FS_IMX25_GCQ_H
+
+#define MX25_ADC_REFP_YP	0 /* YP voltage reference */
+#define MX25_ADC_REFP_XP	1 /* XP voltage reference */
+#define MX25_ADC_REFP_EXT	2 /* External voltage reference */
+#define MX25_ADC_REFP_INT	3 /* Internal voltage reference */
+
+#define MX25_ADC_REFN_XN	0 /* XN ground reference */
+#define MX25_ADC_REFN_YN	1 /* YN ground reference */
+#define MX25_ADC_REFN_NGND	2 /* Internal ground reference */
+#define MX25_ADC_REFN_NGND2	3 /* External ground reference */
+
+#endif
-- 
2.6.1

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

* [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
  2015-11-16 12:01 ` Markus Pargmann
  (?)
@ 2015-11-16 12:01     ` Markus Pargmann
  -1 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Hartmut Knaack, Fabio Estevam, Markus Pargmann, Juergen Borleis

This is a driver for the imx25 ADC/TSC module. It controls the
touchscreen conversion queue and creates a touchscreen input device.
The driver currently only supports 4 wire touchscreens. The driver uses
a simple conversion queue of precharge, touch detection, X measurement,
Y measurement, precharge and another touch detection.

This driver uses the regmap from the parent to setup some touch specific
settings in the core driver and setup a idle configuration with touch
detection.

Signed-off-by: Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Signed-off-by: Denis Carikli <denis-fO0SIAKYzcbQT0dZR+AlfA@public.gmane.org>

[fix clock's period calculation]
[fix calculation of the 'settling' value]
Signed-off-by: Juergen Borleis <jbe-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---

Notes:
    Changes in v7:
     - Moved clk_prepare_enable() and mx25_tcq_init() into mx25_tcq_open(). This
       was done to be able to use devm_request_threaded_irq().
     - Cleanup of the probe function through above change
     - Removed mx25_tcq_remove(), not necessary now

 drivers/input/touchscreen/Kconfig         |   6 +
 drivers/input/touchscreen/Makefile        |   1 +
 drivers/input/touchscreen/fsl-imx25-tcq.c | 600 ++++++++++++++++++++++++++++++
 3 files changed, 607 insertions(+)
 create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index ae33da7ab51f..b44651d33080 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -811,6 +811,12 @@ config TOUCHSCREEN_USB_COMPOSITE
 	  To compile this driver as a module, choose M here: the
 	  module will be called usbtouchscreen.
 
+config TOUCHSCREEN_MX25
+	tristate "Freescale i.MX25 touchscreen input driver"
+	depends on MFD_MX25_TSADC
+	help
+	  Enable support for touchscreen connected to your i.MX25.
+
 config TOUCHSCREEN_MC13783
 	tristate "Freescale MC13783 touchscreen input driver"
 	depends on MFD_MC13XXX
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index cbaa6abb08da..77a2ac54101a 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
 obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
 obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
new file mode 100644
index 000000000000..c833cd814972
--- /dev/null
+++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ * Based on driver from 2011:
+ *   Juergen Beisert, Pengutronix <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
+ *
+ * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
+ * connected to the imx25 ADC.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/imx25-tsadc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+static const char mx25_tcq_name[] = "mx25-tcq";
+
+enum mx25_tcq_mode {
+	MX25_TS_4WIRE,
+};
+
+struct mx25_tcq_priv {
+	struct regmap *regs;
+	struct regmap *core_regs;
+	struct input_dev *idev;
+	enum mx25_tcq_mode mode;
+	unsigned int pen_threshold;
+	unsigned int sample_count;
+	unsigned int expected_samples;
+	unsigned int pen_debounce;
+	unsigned int settling_time;
+	struct clk *clk;
+	int irq;
+};
+
+static struct regmap_config mx25_tcq_regconfig = {
+	.fast_io = true,
+	.max_register = 0x5c,
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+static const struct of_device_id mx25_tcq_ids[] = {
+	{ .compatible = "fsl,imx25-tcq", },
+	{ /* Sentinel */ }
+};
+
+#define TSC_4WIRE_PRE_INDEX 0
+#define TSC_4WIRE_X_INDEX 1
+#define TSC_4WIRE_Y_INDEX 2
+#define TSC_4WIRE_POST_INDEX 3
+#define TSC_4WIRE_LEAVE 4
+
+#define MX25_TSC_DEF_THRESHOLD 80
+#define TSC_MAX_SAMPLES 16
+
+#define MX25_TSC_REPEAT_WAIT 14
+
+enum mx25_adc_configurations {
+	MX25_CFG_PRECHARGE = 0,
+	MX25_CFG_TOUCH_DETECT,
+	MX25_CFG_X_MEASUREMENT,
+	MX25_CFG_Y_MEASUREMENT,
+};
+
+#define MX25_PRECHARGE_VALUE (\
+			MX25_ADCQ_CFG_YPLL_OFF | \
+			MX25_ADCQ_CFG_XNUR_OFF | \
+			MX25_ADCQ_CFG_XPUL_HIGH | \
+			MX25_ADCQ_CFG_REFP_INT | \
+			MX25_ADCQ_CFG_IN_XP | \
+			MX25_ADCQ_CFG_REFN_NGND2 | \
+			MX25_ADCQ_CFG_IGS)
+
+#define MX25_TOUCH_DETECT_VALUE (\
+			MX25_ADCQ_CFG_YNLR | \
+			MX25_ADCQ_CFG_YPLL_OFF | \
+			MX25_ADCQ_CFG_XNUR_OFF | \
+			MX25_ADCQ_CFG_XPUL_OFF | \
+			MX25_ADCQ_CFG_REFP_INT | \
+			MX25_ADCQ_CFG_IN_XP | \
+			MX25_ADCQ_CFG_REFN_NGND2 | \
+			MX25_ADCQ_CFG_PENIACK)
+
+static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv,
+				   unsigned int settling_cnt)
+{
+	u32 precharge_cfg =
+			MX25_PRECHARGE_VALUE |
+			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
+	u32 touch_detect_cfg =
+			MX25_TOUCH_DETECT_VALUE |
+			MX25_ADCQ_CFG_NOS(1) |
+			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
+
+	regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg);
+
+	/* PRECHARGE */
+	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE),
+		     precharge_cfg);
+
+	/* TOUCH_DETECT */
+	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT),
+		     touch_detect_cfg);
+
+	/* X Measurement */
+	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT),
+		     MX25_ADCQ_CFG_YPLL_OFF |
+		     MX25_ADCQ_CFG_XNUR_LOW |
+		     MX25_ADCQ_CFG_XPUL_HIGH |
+		     MX25_ADCQ_CFG_REFP_XP |
+		     MX25_ADCQ_CFG_IN_YP |
+		     MX25_ADCQ_CFG_REFN_XN |
+		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
+		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
+
+	/* Y Measurement */
+	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT),
+		     MX25_ADCQ_CFG_YNLR |
+		     MX25_ADCQ_CFG_YPLL_HIGH |
+		     MX25_ADCQ_CFG_XNUR_OFF |
+		     MX25_ADCQ_CFG_XPUL_OFF |
+		     MX25_ADCQ_CFG_REFP_YP |
+		     MX25_ADCQ_CFG_IN_XP |
+		     MX25_ADCQ_CFG_REFN_YN |
+		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
+		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
+
+	/* Enable the touch detection right now */
+	regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg |
+		     MX25_ADCQ_CFG_IGS);
+}
+
+static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv,
+				   unsigned settling_cnt, int *items)
+{
+	imx25_setup_queue_cfgs(priv, settling_cnt);
+
+	/* Setup the conversion queue */
+	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
+		     MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) |
+		     MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) |
+		     MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) |
+		     MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) |
+		     MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) |
+		     MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT));
+
+	/*
+	 * We measure X/Y with 'sample_count' number of samples and execute a
+	 * touch detection twice, with 1 sample each
+	 */
+	priv->expected_samples = priv->sample_count * 2 + 2;
+	*items = 6;
+
+	return 0;
+}
+
+static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK,
+			   MX25_ADCQ_CR_PDMSK);
+}
+
+static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0);
+}
+
+static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ,
+			   MX25_ADCQ_MR_FDRY_IRQ);
+}
+
+static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0);
+}
+
+static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_FQS,
+			   MX25_ADCQ_CR_FQS);
+}
+
+static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_FQS, 0);
+}
+
+static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv)
+{
+	u32 tcqcr;
+
+	regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr);
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST,
+			   MX25_ADCQ_CR_FRST);
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0);
+	regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr);
+}
+
+static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv)
+{
+	/* stop the queue from looping */
+	mx25_tcq_force_queue_stop(priv);
+
+	/* for a clean touch detection, preload the X plane */
+	regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE);
+
+	/* waste some time now to pre-load the X plate to high voltage */
+	mx25_tcq_fifo_reset(priv);
+
+	/* re-enable the detection right now */
+	regmap_write(priv->core_regs, MX25_TSC_TICR,
+		     MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS);
+
+	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD,
+			   MX25_ADCQ_SR_PD);
+
+	/* enable the pen down event to be a source for the interrupt */
+	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0);
+
+	/* lets fire the next IRQ if someone touches the touchscreen */
+	mx25_tcq_enable_touch_irq(priv);
+}
+
+static int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,
+					   u32 *sample_buf,
+					   unsigned int samples)
+{
+	unsigned int x_pos = 0;
+	unsigned int y_pos = 0;
+	unsigned int touch_pre = 0;
+	unsigned int touch_post = 0;
+	unsigned int i;
+	int ret = 0;
+
+	for (i = 0; i < samples; i++) {
+		unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]);
+		unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]);
+
+		switch (index) {
+		case 1:
+			touch_pre = val;
+			break;
+		case 2:
+			x_pos = val;
+			break;
+		case 3:
+			y_pos = val;
+			break;
+		case 5:
+			touch_post = val;
+			break;
+		default:
+			ret = -EINVAL;
+			break;
+		}
+	}
+
+	if (ret == 0 && samples != 0) {
+		/*
+		 * only if both touch measures are below a threshold,
+		 * the position is valid
+		 */
+		if (touch_pre < priv->pen_threshold &&
+		    touch_post < priv->pen_threshold) {
+			/* valid samples, generate a report */
+			x_pos /= priv->sample_count;
+			y_pos /= priv->sample_count;
+			input_report_abs(priv->idev, ABS_X, x_pos);
+			input_report_abs(priv->idev, ABS_Y, y_pos);
+			input_report_key(priv->idev, BTN_TOUCH, 1);
+			input_sync(priv->idev);
+
+			/* get next sample */
+			mx25_tcq_enable_fifo_irq(priv);
+		} else if (touch_pre >= priv->pen_threshold &&
+			   touch_post >= priv->pen_threshold) {
+			/*
+			 * if both samples are invalid,
+			 * generate a release report
+			 */
+			input_report_key(priv->idev, BTN_TOUCH, 0);
+			input_sync(priv->idev);
+			mx25_tcq_re_enable_touch_detection(priv);
+		} else {
+			/*
+			 * if only one of both touch measurements are
+			 * below the threshold, still some bouncing
+			 * happens. Take additional samples in this
+			 * case to be sure
+			 */
+			mx25_tcq_enable_fifo_irq(priv);
+		}
+	}
+
+	return ret;
+}
+
+static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
+{
+	struct mx25_tcq_priv *priv = dev_id;
+	u32 sample_buf[TSC_MAX_SAMPLES];
+	unsigned int samples = 0;
+
+	/* read all samples */
+	while (1) {
+		u32 stats;
+
+		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
+		if (stats & MX25_ADCQ_SR_EMPT)
+			break;
+
+		if (samples < TSC_MAX_SAMPLES) {
+			regmap_read(priv->regs, MX25_ADCQ_FIFO,
+				    &sample_buf[samples]);
+			++samples;
+		} else {
+			u32 discarded;
+			/* discard samples */
+			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);
+		}
+	}
+
+	mx25_tcq_create_event_for_4wire(priv, sample_buf, samples);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mx25_tcq_irq(int irq, void *dev_id)
+{
+	struct mx25_tcq_priv *priv = dev_id;
+	u32 stat;
+	int ret = IRQ_HANDLED;
+
+	regmap_read(priv->regs, MX25_ADCQ_SR, &stat);
+
+	if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR))
+		mx25_tcq_fifo_reset(priv);
+
+	if (stat & MX25_ADCQ_SR_PD) {
+		mx25_tcq_disable_touch_irq(priv);
+		mx25_tcq_force_queue_start(priv);
+		mx25_tcq_enable_fifo_irq(priv);
+	}
+
+	if (stat & MX25_ADCQ_SR_FDRY) {
+		mx25_tcq_disable_fifo_irq(priv);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
+			   MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
+			   MX25_ADCQ_SR_PD | MX25_ADCQ_SR_EOQ,
+			   MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR |
+			   MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
+			   MX25_ADCQ_SR_EOQ);
+
+	return ret;
+}
+
+/* configure the statemachine for a 4-wire touchscreen */
+static int mx25_tcq_init(struct mx25_tcq_priv *priv)
+{
+	u32 tgcr;
+	unsigned int ipg_div;
+	unsigned int adc_period;
+	unsigned int debounce_cnt;
+	unsigned int settling_cnt;
+	int itemct;
+	int ret;
+
+	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
+	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
+	adc_period = USEC_PER_SEC * ipg_div * 2 + 2;
+	adc_period /= clk_get_rate(priv->clk) / 1000 + 1;
+	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
+	settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1;
+
+	/* Reset */
+	regmap_write(priv->regs, MX25_ADCQ_CR,
+		     MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST);
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0);
+
+	/* up to 128 * 8 ADC clocks are possible */
+	if (debounce_cnt > 127)
+		debounce_cnt = 127;
+
+	/* up to 255 * 8 ADC clocks are possible */
+	if (settling_cnt > 255)
+		settling_cnt = 255;
+
+	ret = imx25_setup_queue_4wire(priv, settling_cnt, &itemct);
+	if (ret)
+		return ret;
+
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK,
+			   MX25_ADCQ_CR_LITEMID(itemct - 1) |
+			   MX25_ADCQ_CR_WMRK(priv->expected_samples - 1));
+
+	/* setup debounce count */
+	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR,
+			   MX25_TGCR_PDBTIME_MASK,
+			   MX25_TGCR_PDBTIME(debounce_cnt));
+
+	/* enable debounce */
+	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN,
+			   MX25_TGCR_PDBEN);
+	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN,
+			   MX25_TGCR_PDEN);
+
+	/* enable the engine on demand */
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK,
+			   MX25_ADCQ_CR_QSM_FQS);
+
+	/* Enable repeat and repeat wait */
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK,
+			   MX25_ADCQ_CR_RPT |
+			   MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT));
+
+	mx25_tcq_re_enable_touch_detection(priv);
+
+	return 0;
+}
+
+static int mx25_tcq_parse_dt(struct platform_device *pdev,
+			     struct mx25_tcq_priv *priv)
+{
+	struct device_node *np = pdev->dev.of_node;
+	u32 wires;
+	int ret;
+
+	/* Setup defaults */
+	priv->pen_threshold = 500;
+	priv->sample_count = 3;
+	priv->pen_debounce = 1000000;
+	priv->settling_time = 250000;
+
+	ret = of_property_read_u32(np, "fsl,wires", &wires);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
+		return ret;
+	}
+
+	if (wires == 4) {
+		priv->mode = MX25_TS_4WIRE;
+	} else {
+		dev_err(&pdev->dev, "%u-wire mode not supported\n", wires);
+		return -EINVAL;
+	}
+
+	/* These are optional, we don't care about the return values */
+	of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold);
+	of_property_read_u32(np, "fsl,settling-time", &priv->settling_time);
+	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
+
+	return 0;
+}
+
+static int mx25_tcq_open(struct input_dev *idev)
+{
+	struct device *dev = &idev->dev;
+	struct mx25_tcq_priv *priv = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(dev, "Failed to enable ipg clock\n");
+		return ret;
+	}
+
+	ret = mx25_tcq_init(priv);
+	if (ret) {
+		dev_err(dev, "Failed to init tcq\n");
+		clk_disable_unprepare(priv->clk);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void mx25_tcq_close(struct input_dev *idev)
+{
+	struct mx25_tcq_priv *priv = input_get_drvdata(idev);
+
+	mx25_tcq_force_queue_stop(priv);
+	mx25_tcq_disable_touch_irq(priv);
+	mx25_tcq_disable_fifo_irq(priv);
+	clk_disable_unprepare(priv->clk);
+}
+
+static int mx25_tcq_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct input_dev *idev;
+	struct mx25_tcq_priv *priv;
+	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
+	struct resource *res;
+	void __iomem *mem;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mem = devm_ioremap_resource(dev, res);
+	if (!mem)
+		return -ENOMEM;
+
+	ret = mx25_tcq_parse_dt(pdev, priv);
+	if (ret)
+		return ret;
+
+	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig);
+	if (IS_ERR(priv->regs)) {
+		dev_err(dev, "Failed to initialize regmap\n");
+		return PTR_ERR(priv->regs);
+	}
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (priv->irq <= 0) {
+		dev_err(dev, "Failed to get IRQ\n");
+		return priv->irq;
+	}
+
+	idev = devm_input_allocate_device(dev);
+	if (!idev) {
+		dev_err(dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	idev->name = mx25_tcq_name;
+	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
+	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
+
+	idev->id.bustype = BUS_HOST;
+	idev->open = mx25_tcq_open;
+	idev->close = mx25_tcq_close;
+
+	priv->idev = idev;
+	input_set_drvdata(idev, priv);
+
+	priv->core_regs = tsadc->regs;
+	if (!priv->core_regs)
+		return -EINVAL;
+
+	priv->clk = tsadc->clk;
+	if (!priv->clk)
+		return -EINVAL;
+
+	platform_set_drvdata(pdev, priv);
+
+	ret = input_register_device(idev);
+	if (ret) {
+		dev_err(dev, "Failed to register input device\n");
+		return ret;
+	}
+
+	ret = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq,
+					mx25_tcq_irq_thread, IRQF_ONESHOT,
+					pdev->name, priv);
+	if (ret) {
+		dev_err(dev, "Failed requesting IRQ\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct platform_driver mx25_tcq_driver = {
+	.driver		= {
+		.name	= "mx25-tcq",
+		.of_match_table = mx25_tcq_ids,
+	},
+	.probe		= mx25_tcq_probe,
+};
+module_platform_driver(mx25_tcq_driver);
+
+MODULE_DESCRIPTION("TS input driver for Freescale mx25");
+MODULE_AUTHOR("Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
-- 
2.6.1

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

* [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
@ 2015-11-16 12:01     ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam, Markus Pargmann, Juergen Borleis

This is a driver for the imx25 ADC/TSC module. It controls the
touchscreen conversion queue and creates a touchscreen input device.
The driver currently only supports 4 wire touchscreens. The driver uses
a simple conversion queue of precharge, touch detection, X measurement,
Y measurement, precharge and another touch detection.

This driver uses the regmap from the parent to setup some touch specific
settings in the core driver and setup a idle configuration with touch
detection.

Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
Signed-off-by: Denis Carikli <denis@eukrea.com>

[fix clock's period calculation]
[fix calculation of the 'settling' value]
Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
---

Notes:
    Changes in v7:
     - Moved clk_prepare_enable() and mx25_tcq_init() into mx25_tcq_open(). This
       was done to be able to use devm_request_threaded_irq().
     - Cleanup of the probe function through above change
     - Removed mx25_tcq_remove(), not necessary now

 drivers/input/touchscreen/Kconfig         |   6 +
 drivers/input/touchscreen/Makefile        |   1 +
 drivers/input/touchscreen/fsl-imx25-tcq.c | 600 ++++++++++++++++++++++++++++++
 3 files changed, 607 insertions(+)
 create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index ae33da7ab51f..b44651d33080 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -811,6 +811,12 @@ config TOUCHSCREEN_USB_COMPOSITE
 	  To compile this driver as a module, choose M here: the
 	  module will be called usbtouchscreen.
 
+config TOUCHSCREEN_MX25
+	tristate "Freescale i.MX25 touchscreen input driver"
+	depends on MFD_MX25_TSADC
+	help
+	  Enable support for touchscreen connected to your i.MX25.
+
 config TOUCHSCREEN_MC13783
 	tristate "Freescale MC13783 touchscreen input driver"
 	depends on MFD_MC13XXX
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index cbaa6abb08da..77a2ac54101a 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
 obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
 obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
new file mode 100644
index 000000000000..c833cd814972
--- /dev/null
+++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ * Based on driver from 2011:
+ *   Juergen Beisert, Pengutronix <kernel@pengutronix.de>
+ *
+ * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
+ * connected to the imx25 ADC.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/imx25-tsadc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+static const char mx25_tcq_name[] = "mx25-tcq";
+
+enum mx25_tcq_mode {
+	MX25_TS_4WIRE,
+};
+
+struct mx25_tcq_priv {
+	struct regmap *regs;
+	struct regmap *core_regs;
+	struct input_dev *idev;
+	enum mx25_tcq_mode mode;
+	unsigned int pen_threshold;
+	unsigned int sample_count;
+	unsigned int expected_samples;
+	unsigned int pen_debounce;
+	unsigned int settling_time;
+	struct clk *clk;
+	int irq;
+};
+
+static struct regmap_config mx25_tcq_regconfig = {
+	.fast_io = true,
+	.max_register = 0x5c,
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+static const struct of_device_id mx25_tcq_ids[] = {
+	{ .compatible = "fsl,imx25-tcq", },
+	{ /* Sentinel */ }
+};
+
+#define TSC_4WIRE_PRE_INDEX 0
+#define TSC_4WIRE_X_INDEX 1
+#define TSC_4WIRE_Y_INDEX 2
+#define TSC_4WIRE_POST_INDEX 3
+#define TSC_4WIRE_LEAVE 4
+
+#define MX25_TSC_DEF_THRESHOLD 80
+#define TSC_MAX_SAMPLES 16
+
+#define MX25_TSC_REPEAT_WAIT 14
+
+enum mx25_adc_configurations {
+	MX25_CFG_PRECHARGE = 0,
+	MX25_CFG_TOUCH_DETECT,
+	MX25_CFG_X_MEASUREMENT,
+	MX25_CFG_Y_MEASUREMENT,
+};
+
+#define MX25_PRECHARGE_VALUE (\
+			MX25_ADCQ_CFG_YPLL_OFF | \
+			MX25_ADCQ_CFG_XNUR_OFF | \
+			MX25_ADCQ_CFG_XPUL_HIGH | \
+			MX25_ADCQ_CFG_REFP_INT | \
+			MX25_ADCQ_CFG_IN_XP | \
+			MX25_ADCQ_CFG_REFN_NGND2 | \
+			MX25_ADCQ_CFG_IGS)
+
+#define MX25_TOUCH_DETECT_VALUE (\
+			MX25_ADCQ_CFG_YNLR | \
+			MX25_ADCQ_CFG_YPLL_OFF | \
+			MX25_ADCQ_CFG_XNUR_OFF | \
+			MX25_ADCQ_CFG_XPUL_OFF | \
+			MX25_ADCQ_CFG_REFP_INT | \
+			MX25_ADCQ_CFG_IN_XP | \
+			MX25_ADCQ_CFG_REFN_NGND2 | \
+			MX25_ADCQ_CFG_PENIACK)
+
+static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv,
+				   unsigned int settling_cnt)
+{
+	u32 precharge_cfg =
+			MX25_PRECHARGE_VALUE |
+			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
+	u32 touch_detect_cfg =
+			MX25_TOUCH_DETECT_VALUE |
+			MX25_ADCQ_CFG_NOS(1) |
+			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
+
+	regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg);
+
+	/* PRECHARGE */
+	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE),
+		     precharge_cfg);
+
+	/* TOUCH_DETECT */
+	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT),
+		     touch_detect_cfg);
+
+	/* X Measurement */
+	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT),
+		     MX25_ADCQ_CFG_YPLL_OFF |
+		     MX25_ADCQ_CFG_XNUR_LOW |
+		     MX25_ADCQ_CFG_XPUL_HIGH |
+		     MX25_ADCQ_CFG_REFP_XP |
+		     MX25_ADCQ_CFG_IN_YP |
+		     MX25_ADCQ_CFG_REFN_XN |
+		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
+		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
+
+	/* Y Measurement */
+	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT),
+		     MX25_ADCQ_CFG_YNLR |
+		     MX25_ADCQ_CFG_YPLL_HIGH |
+		     MX25_ADCQ_CFG_XNUR_OFF |
+		     MX25_ADCQ_CFG_XPUL_OFF |
+		     MX25_ADCQ_CFG_REFP_YP |
+		     MX25_ADCQ_CFG_IN_XP |
+		     MX25_ADCQ_CFG_REFN_YN |
+		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
+		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
+
+	/* Enable the touch detection right now */
+	regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg |
+		     MX25_ADCQ_CFG_IGS);
+}
+
+static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv,
+				   unsigned settling_cnt, int *items)
+{
+	imx25_setup_queue_cfgs(priv, settling_cnt);
+
+	/* Setup the conversion queue */
+	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
+		     MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) |
+		     MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) |
+		     MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) |
+		     MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) |
+		     MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) |
+		     MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT));
+
+	/*
+	 * We measure X/Y with 'sample_count' number of samples and execute a
+	 * touch detection twice, with 1 sample each
+	 */
+	priv->expected_samples = priv->sample_count * 2 + 2;
+	*items = 6;
+
+	return 0;
+}
+
+static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK,
+			   MX25_ADCQ_CR_PDMSK);
+}
+
+static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0);
+}
+
+static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ,
+			   MX25_ADCQ_MR_FDRY_IRQ);
+}
+
+static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0);
+}
+
+static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_FQS,
+			   MX25_ADCQ_CR_FQS);
+}
+
+static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_FQS, 0);
+}
+
+static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv)
+{
+	u32 tcqcr;
+
+	regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr);
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST,
+			   MX25_ADCQ_CR_FRST);
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0);
+	regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr);
+}
+
+static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv)
+{
+	/* stop the queue from looping */
+	mx25_tcq_force_queue_stop(priv);
+
+	/* for a clean touch detection, preload the X plane */
+	regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE);
+
+	/* waste some time now to pre-load the X plate to high voltage */
+	mx25_tcq_fifo_reset(priv);
+
+	/* re-enable the detection right now */
+	regmap_write(priv->core_regs, MX25_TSC_TICR,
+		     MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS);
+
+	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD,
+			   MX25_ADCQ_SR_PD);
+
+	/* enable the pen down event to be a source for the interrupt */
+	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0);
+
+	/* lets fire the next IRQ if someone touches the touchscreen */
+	mx25_tcq_enable_touch_irq(priv);
+}
+
+static int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,
+					   u32 *sample_buf,
+					   unsigned int samples)
+{
+	unsigned int x_pos = 0;
+	unsigned int y_pos = 0;
+	unsigned int touch_pre = 0;
+	unsigned int touch_post = 0;
+	unsigned int i;
+	int ret = 0;
+
+	for (i = 0; i < samples; i++) {
+		unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]);
+		unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]);
+
+		switch (index) {
+		case 1:
+			touch_pre = val;
+			break;
+		case 2:
+			x_pos = val;
+			break;
+		case 3:
+			y_pos = val;
+			break;
+		case 5:
+			touch_post = val;
+			break;
+		default:
+			ret = -EINVAL;
+			break;
+		}
+	}
+
+	if (ret == 0 && samples != 0) {
+		/*
+		 * only if both touch measures are below a threshold,
+		 * the position is valid
+		 */
+		if (touch_pre < priv->pen_threshold &&
+		    touch_post < priv->pen_threshold) {
+			/* valid samples, generate a report */
+			x_pos /= priv->sample_count;
+			y_pos /= priv->sample_count;
+			input_report_abs(priv->idev, ABS_X, x_pos);
+			input_report_abs(priv->idev, ABS_Y, y_pos);
+			input_report_key(priv->idev, BTN_TOUCH, 1);
+			input_sync(priv->idev);
+
+			/* get next sample */
+			mx25_tcq_enable_fifo_irq(priv);
+		} else if (touch_pre >= priv->pen_threshold &&
+			   touch_post >= priv->pen_threshold) {
+			/*
+			 * if both samples are invalid,
+			 * generate a release report
+			 */
+			input_report_key(priv->idev, BTN_TOUCH, 0);
+			input_sync(priv->idev);
+			mx25_tcq_re_enable_touch_detection(priv);
+		} else {
+			/*
+			 * if only one of both touch measurements are
+			 * below the threshold, still some bouncing
+			 * happens. Take additional samples in this
+			 * case to be sure
+			 */
+			mx25_tcq_enable_fifo_irq(priv);
+		}
+	}
+
+	return ret;
+}
+
+static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
+{
+	struct mx25_tcq_priv *priv = dev_id;
+	u32 sample_buf[TSC_MAX_SAMPLES];
+	unsigned int samples = 0;
+
+	/* read all samples */
+	while (1) {
+		u32 stats;
+
+		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
+		if (stats & MX25_ADCQ_SR_EMPT)
+			break;
+
+		if (samples < TSC_MAX_SAMPLES) {
+			regmap_read(priv->regs, MX25_ADCQ_FIFO,
+				    &sample_buf[samples]);
+			++samples;
+		} else {
+			u32 discarded;
+			/* discard samples */
+			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);
+		}
+	}
+
+	mx25_tcq_create_event_for_4wire(priv, sample_buf, samples);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mx25_tcq_irq(int irq, void *dev_id)
+{
+	struct mx25_tcq_priv *priv = dev_id;
+	u32 stat;
+	int ret = IRQ_HANDLED;
+
+	regmap_read(priv->regs, MX25_ADCQ_SR, &stat);
+
+	if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR))
+		mx25_tcq_fifo_reset(priv);
+
+	if (stat & MX25_ADCQ_SR_PD) {
+		mx25_tcq_disable_touch_irq(priv);
+		mx25_tcq_force_queue_start(priv);
+		mx25_tcq_enable_fifo_irq(priv);
+	}
+
+	if (stat & MX25_ADCQ_SR_FDRY) {
+		mx25_tcq_disable_fifo_irq(priv);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
+			   MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
+			   MX25_ADCQ_SR_PD | MX25_ADCQ_SR_EOQ,
+			   MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR |
+			   MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
+			   MX25_ADCQ_SR_EOQ);
+
+	return ret;
+}
+
+/* configure the statemachine for a 4-wire touchscreen */
+static int mx25_tcq_init(struct mx25_tcq_priv *priv)
+{
+	u32 tgcr;
+	unsigned int ipg_div;
+	unsigned int adc_period;
+	unsigned int debounce_cnt;
+	unsigned int settling_cnt;
+	int itemct;
+	int ret;
+
+	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
+	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
+	adc_period = USEC_PER_SEC * ipg_div * 2 + 2;
+	adc_period /= clk_get_rate(priv->clk) / 1000 + 1;
+	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
+	settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1;
+
+	/* Reset */
+	regmap_write(priv->regs, MX25_ADCQ_CR,
+		     MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST);
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0);
+
+	/* up to 128 * 8 ADC clocks are possible */
+	if (debounce_cnt > 127)
+		debounce_cnt = 127;
+
+	/* up to 255 * 8 ADC clocks are possible */
+	if (settling_cnt > 255)
+		settling_cnt = 255;
+
+	ret = imx25_setup_queue_4wire(priv, settling_cnt, &itemct);
+	if (ret)
+		return ret;
+
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK,
+			   MX25_ADCQ_CR_LITEMID(itemct - 1) |
+			   MX25_ADCQ_CR_WMRK(priv->expected_samples - 1));
+
+	/* setup debounce count */
+	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR,
+			   MX25_TGCR_PDBTIME_MASK,
+			   MX25_TGCR_PDBTIME(debounce_cnt));
+
+	/* enable debounce */
+	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN,
+			   MX25_TGCR_PDBEN);
+	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN,
+			   MX25_TGCR_PDEN);
+
+	/* enable the engine on demand */
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK,
+			   MX25_ADCQ_CR_QSM_FQS);
+
+	/* Enable repeat and repeat wait */
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK,
+			   MX25_ADCQ_CR_RPT |
+			   MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT));
+
+	mx25_tcq_re_enable_touch_detection(priv);
+
+	return 0;
+}
+
+static int mx25_tcq_parse_dt(struct platform_device *pdev,
+			     struct mx25_tcq_priv *priv)
+{
+	struct device_node *np = pdev->dev.of_node;
+	u32 wires;
+	int ret;
+
+	/* Setup defaults */
+	priv->pen_threshold = 500;
+	priv->sample_count = 3;
+	priv->pen_debounce = 1000000;
+	priv->settling_time = 250000;
+
+	ret = of_property_read_u32(np, "fsl,wires", &wires);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
+		return ret;
+	}
+
+	if (wires == 4) {
+		priv->mode = MX25_TS_4WIRE;
+	} else {
+		dev_err(&pdev->dev, "%u-wire mode not supported\n", wires);
+		return -EINVAL;
+	}
+
+	/* These are optional, we don't care about the return values */
+	of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold);
+	of_property_read_u32(np, "fsl,settling-time", &priv->settling_time);
+	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
+
+	return 0;
+}
+
+static int mx25_tcq_open(struct input_dev *idev)
+{
+	struct device *dev = &idev->dev;
+	struct mx25_tcq_priv *priv = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(dev, "Failed to enable ipg clock\n");
+		return ret;
+	}
+
+	ret = mx25_tcq_init(priv);
+	if (ret) {
+		dev_err(dev, "Failed to init tcq\n");
+		clk_disable_unprepare(priv->clk);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void mx25_tcq_close(struct input_dev *idev)
+{
+	struct mx25_tcq_priv *priv = input_get_drvdata(idev);
+
+	mx25_tcq_force_queue_stop(priv);
+	mx25_tcq_disable_touch_irq(priv);
+	mx25_tcq_disable_fifo_irq(priv);
+	clk_disable_unprepare(priv->clk);
+}
+
+static int mx25_tcq_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct input_dev *idev;
+	struct mx25_tcq_priv *priv;
+	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
+	struct resource *res;
+	void __iomem *mem;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mem = devm_ioremap_resource(dev, res);
+	if (!mem)
+		return -ENOMEM;
+
+	ret = mx25_tcq_parse_dt(pdev, priv);
+	if (ret)
+		return ret;
+
+	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig);
+	if (IS_ERR(priv->regs)) {
+		dev_err(dev, "Failed to initialize regmap\n");
+		return PTR_ERR(priv->regs);
+	}
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (priv->irq <= 0) {
+		dev_err(dev, "Failed to get IRQ\n");
+		return priv->irq;
+	}
+
+	idev = devm_input_allocate_device(dev);
+	if (!idev) {
+		dev_err(dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	idev->name = mx25_tcq_name;
+	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
+	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
+
+	idev->id.bustype = BUS_HOST;
+	idev->open = mx25_tcq_open;
+	idev->close = mx25_tcq_close;
+
+	priv->idev = idev;
+	input_set_drvdata(idev, priv);
+
+	priv->core_regs = tsadc->regs;
+	if (!priv->core_regs)
+		return -EINVAL;
+
+	priv->clk = tsadc->clk;
+	if (!priv->clk)
+		return -EINVAL;
+
+	platform_set_drvdata(pdev, priv);
+
+	ret = input_register_device(idev);
+	if (ret) {
+		dev_err(dev, "Failed to register input device\n");
+		return ret;
+	}
+
+	ret = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq,
+					mx25_tcq_irq_thread, IRQF_ONESHOT,
+					pdev->name, priv);
+	if (ret) {
+		dev_err(dev, "Failed requesting IRQ\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct platform_driver mx25_tcq_driver = {
+	.driver		= {
+		.name	= "mx25-tcq",
+		.of_match_table = mx25_tcq_ids,
+	},
+	.probe		= mx25_tcq_probe,
+};
+module_platform_driver(mx25_tcq_driver);
+
+MODULE_DESCRIPTION("TS input driver for Freescale mx25");
+MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
-- 
2.6.1


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

* [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
@ 2015-11-16 12:01     ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: linux-arm-kernel

This is a driver for the imx25 ADC/TSC module. It controls the
touchscreen conversion queue and creates a touchscreen input device.
The driver currently only supports 4 wire touchscreens. The driver uses
a simple conversion queue of precharge, touch detection, X measurement,
Y measurement, precharge and another touch detection.

This driver uses the regmap from the parent to setup some touch specific
settings in the core driver and setup a idle configuration with touch
detection.

Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
Signed-off-by: Denis Carikli <denis@eukrea.com>

[fix clock's period calculation]
[fix calculation of the 'settling' value]
Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
---

Notes:
    Changes in v7:
     - Moved clk_prepare_enable() and mx25_tcq_init() into mx25_tcq_open(). This
       was done to be able to use devm_request_threaded_irq().
     - Cleanup of the probe function through above change
     - Removed mx25_tcq_remove(), not necessary now

 drivers/input/touchscreen/Kconfig         |   6 +
 drivers/input/touchscreen/Makefile        |   1 +
 drivers/input/touchscreen/fsl-imx25-tcq.c | 600 ++++++++++++++++++++++++++++++
 3 files changed, 607 insertions(+)
 create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index ae33da7ab51f..b44651d33080 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -811,6 +811,12 @@ config TOUCHSCREEN_USB_COMPOSITE
 	  To compile this driver as a module, choose M here: the
 	  module will be called usbtouchscreen.
 
+config TOUCHSCREEN_MX25
+	tristate "Freescale i.MX25 touchscreen input driver"
+	depends on MFD_MX25_TSADC
+	help
+	  Enable support for touchscreen connected to your i.MX25.
+
 config TOUCHSCREEN_MC13783
 	tristate "Freescale MC13783 touchscreen input driver"
 	depends on MFD_MC13XXX
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index cbaa6abb08da..77a2ac54101a 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
 obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
 obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
new file mode 100644
index 000000000000..c833cd814972
--- /dev/null
+++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ * Based on driver from 2011:
+ *   Juergen Beisert, Pengutronix <kernel@pengutronix.de>
+ *
+ * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
+ * connected to the imx25 ADC.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/imx25-tsadc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+static const char mx25_tcq_name[] = "mx25-tcq";
+
+enum mx25_tcq_mode {
+	MX25_TS_4WIRE,
+};
+
+struct mx25_tcq_priv {
+	struct regmap *regs;
+	struct regmap *core_regs;
+	struct input_dev *idev;
+	enum mx25_tcq_mode mode;
+	unsigned int pen_threshold;
+	unsigned int sample_count;
+	unsigned int expected_samples;
+	unsigned int pen_debounce;
+	unsigned int settling_time;
+	struct clk *clk;
+	int irq;
+};
+
+static struct regmap_config mx25_tcq_regconfig = {
+	.fast_io = true,
+	.max_register = 0x5c,
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+static const struct of_device_id mx25_tcq_ids[] = {
+	{ .compatible = "fsl,imx25-tcq", },
+	{ /* Sentinel */ }
+};
+
+#define TSC_4WIRE_PRE_INDEX 0
+#define TSC_4WIRE_X_INDEX 1
+#define TSC_4WIRE_Y_INDEX 2
+#define TSC_4WIRE_POST_INDEX 3
+#define TSC_4WIRE_LEAVE 4
+
+#define MX25_TSC_DEF_THRESHOLD 80
+#define TSC_MAX_SAMPLES 16
+
+#define MX25_TSC_REPEAT_WAIT 14
+
+enum mx25_adc_configurations {
+	MX25_CFG_PRECHARGE = 0,
+	MX25_CFG_TOUCH_DETECT,
+	MX25_CFG_X_MEASUREMENT,
+	MX25_CFG_Y_MEASUREMENT,
+};
+
+#define MX25_PRECHARGE_VALUE (\
+			MX25_ADCQ_CFG_YPLL_OFF | \
+			MX25_ADCQ_CFG_XNUR_OFF | \
+			MX25_ADCQ_CFG_XPUL_HIGH | \
+			MX25_ADCQ_CFG_REFP_INT | \
+			MX25_ADCQ_CFG_IN_XP | \
+			MX25_ADCQ_CFG_REFN_NGND2 | \
+			MX25_ADCQ_CFG_IGS)
+
+#define MX25_TOUCH_DETECT_VALUE (\
+			MX25_ADCQ_CFG_YNLR | \
+			MX25_ADCQ_CFG_YPLL_OFF | \
+			MX25_ADCQ_CFG_XNUR_OFF | \
+			MX25_ADCQ_CFG_XPUL_OFF | \
+			MX25_ADCQ_CFG_REFP_INT | \
+			MX25_ADCQ_CFG_IN_XP | \
+			MX25_ADCQ_CFG_REFN_NGND2 | \
+			MX25_ADCQ_CFG_PENIACK)
+
+static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv,
+				   unsigned int settling_cnt)
+{
+	u32 precharge_cfg =
+			MX25_PRECHARGE_VALUE |
+			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
+	u32 touch_detect_cfg =
+			MX25_TOUCH_DETECT_VALUE |
+			MX25_ADCQ_CFG_NOS(1) |
+			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
+
+	regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg);
+
+	/* PRECHARGE */
+	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE),
+		     precharge_cfg);
+
+	/* TOUCH_DETECT */
+	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT),
+		     touch_detect_cfg);
+
+	/* X Measurement */
+	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT),
+		     MX25_ADCQ_CFG_YPLL_OFF |
+		     MX25_ADCQ_CFG_XNUR_LOW |
+		     MX25_ADCQ_CFG_XPUL_HIGH |
+		     MX25_ADCQ_CFG_REFP_XP |
+		     MX25_ADCQ_CFG_IN_YP |
+		     MX25_ADCQ_CFG_REFN_XN |
+		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
+		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
+
+	/* Y Measurement */
+	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT),
+		     MX25_ADCQ_CFG_YNLR |
+		     MX25_ADCQ_CFG_YPLL_HIGH |
+		     MX25_ADCQ_CFG_XNUR_OFF |
+		     MX25_ADCQ_CFG_XPUL_OFF |
+		     MX25_ADCQ_CFG_REFP_YP |
+		     MX25_ADCQ_CFG_IN_XP |
+		     MX25_ADCQ_CFG_REFN_YN |
+		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
+		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
+
+	/* Enable the touch detection right now */
+	regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg |
+		     MX25_ADCQ_CFG_IGS);
+}
+
+static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv,
+				   unsigned settling_cnt, int *items)
+{
+	imx25_setup_queue_cfgs(priv, settling_cnt);
+
+	/* Setup the conversion queue */
+	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
+		     MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) |
+		     MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) |
+		     MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) |
+		     MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) |
+		     MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) |
+		     MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT));
+
+	/*
+	 * We measure X/Y with 'sample_count' number of samples and execute a
+	 * touch detection twice, with 1 sample each
+	 */
+	priv->expected_samples = priv->sample_count * 2 + 2;
+	*items = 6;
+
+	return 0;
+}
+
+static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK,
+			   MX25_ADCQ_CR_PDMSK);
+}
+
+static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0);
+}
+
+static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ,
+			   MX25_ADCQ_MR_FDRY_IRQ);
+}
+
+static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0);
+}
+
+static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_FQS,
+			   MX25_ADCQ_CR_FQS);
+}
+
+static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv)
+{
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_FQS, 0);
+}
+
+static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv)
+{
+	u32 tcqcr;
+
+	regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr);
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST,
+			   MX25_ADCQ_CR_FRST);
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0);
+	regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr);
+}
+
+static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv)
+{
+	/* stop the queue from looping */
+	mx25_tcq_force_queue_stop(priv);
+
+	/* for a clean touch detection, preload the X plane */
+	regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE);
+
+	/* waste some time now to pre-load the X plate to high voltage */
+	mx25_tcq_fifo_reset(priv);
+
+	/* re-enable the detection right now */
+	regmap_write(priv->core_regs, MX25_TSC_TICR,
+		     MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS);
+
+	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD,
+			   MX25_ADCQ_SR_PD);
+
+	/* enable the pen down event to be a source for the interrupt */
+	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0);
+
+	/* lets fire the next IRQ if someone touches the touchscreen */
+	mx25_tcq_enable_touch_irq(priv);
+}
+
+static int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,
+					   u32 *sample_buf,
+					   unsigned int samples)
+{
+	unsigned int x_pos = 0;
+	unsigned int y_pos = 0;
+	unsigned int touch_pre = 0;
+	unsigned int touch_post = 0;
+	unsigned int i;
+	int ret = 0;
+
+	for (i = 0; i < samples; i++) {
+		unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]);
+		unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]);
+
+		switch (index) {
+		case 1:
+			touch_pre = val;
+			break;
+		case 2:
+			x_pos = val;
+			break;
+		case 3:
+			y_pos = val;
+			break;
+		case 5:
+			touch_post = val;
+			break;
+		default:
+			ret = -EINVAL;
+			break;
+		}
+	}
+
+	if (ret == 0 && samples != 0) {
+		/*
+		 * only if both touch measures are below a threshold,
+		 * the position is valid
+		 */
+		if (touch_pre < priv->pen_threshold &&
+		    touch_post < priv->pen_threshold) {
+			/* valid samples, generate a report */
+			x_pos /= priv->sample_count;
+			y_pos /= priv->sample_count;
+			input_report_abs(priv->idev, ABS_X, x_pos);
+			input_report_abs(priv->idev, ABS_Y, y_pos);
+			input_report_key(priv->idev, BTN_TOUCH, 1);
+			input_sync(priv->idev);
+
+			/* get next sample */
+			mx25_tcq_enable_fifo_irq(priv);
+		} else if (touch_pre >= priv->pen_threshold &&
+			   touch_post >= priv->pen_threshold) {
+			/*
+			 * if both samples are invalid,
+			 * generate a release report
+			 */
+			input_report_key(priv->idev, BTN_TOUCH, 0);
+			input_sync(priv->idev);
+			mx25_tcq_re_enable_touch_detection(priv);
+		} else {
+			/*
+			 * if only one of both touch measurements are
+			 * below the threshold, still some bouncing
+			 * happens. Take additional samples in this
+			 * case to be sure
+			 */
+			mx25_tcq_enable_fifo_irq(priv);
+		}
+	}
+
+	return ret;
+}
+
+static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
+{
+	struct mx25_tcq_priv *priv = dev_id;
+	u32 sample_buf[TSC_MAX_SAMPLES];
+	unsigned int samples = 0;
+
+	/* read all samples */
+	while (1) {
+		u32 stats;
+
+		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
+		if (stats & MX25_ADCQ_SR_EMPT)
+			break;
+
+		if (samples < TSC_MAX_SAMPLES) {
+			regmap_read(priv->regs, MX25_ADCQ_FIFO,
+				    &sample_buf[samples]);
+			++samples;
+		} else {
+			u32 discarded;
+			/* discard samples */
+			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);
+		}
+	}
+
+	mx25_tcq_create_event_for_4wire(priv, sample_buf, samples);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mx25_tcq_irq(int irq, void *dev_id)
+{
+	struct mx25_tcq_priv *priv = dev_id;
+	u32 stat;
+	int ret = IRQ_HANDLED;
+
+	regmap_read(priv->regs, MX25_ADCQ_SR, &stat);
+
+	if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR))
+		mx25_tcq_fifo_reset(priv);
+
+	if (stat & MX25_ADCQ_SR_PD) {
+		mx25_tcq_disable_touch_irq(priv);
+		mx25_tcq_force_queue_start(priv);
+		mx25_tcq_enable_fifo_irq(priv);
+	}
+
+	if (stat & MX25_ADCQ_SR_FDRY) {
+		mx25_tcq_disable_fifo_irq(priv);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
+			   MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
+			   MX25_ADCQ_SR_PD | MX25_ADCQ_SR_EOQ,
+			   MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR |
+			   MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
+			   MX25_ADCQ_SR_EOQ);
+
+	return ret;
+}
+
+/* configure the statemachine for a 4-wire touchscreen */
+static int mx25_tcq_init(struct mx25_tcq_priv *priv)
+{
+	u32 tgcr;
+	unsigned int ipg_div;
+	unsigned int adc_period;
+	unsigned int debounce_cnt;
+	unsigned int settling_cnt;
+	int itemct;
+	int ret;
+
+	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
+	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
+	adc_period = USEC_PER_SEC * ipg_div * 2 + 2;
+	adc_period /= clk_get_rate(priv->clk) / 1000 + 1;
+	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
+	settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1;
+
+	/* Reset */
+	regmap_write(priv->regs, MX25_ADCQ_CR,
+		     MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST);
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0);
+
+	/* up to 128 * 8 ADC clocks are possible */
+	if (debounce_cnt > 127)
+		debounce_cnt = 127;
+
+	/* up to 255 * 8 ADC clocks are possible */
+	if (settling_cnt > 255)
+		settling_cnt = 255;
+
+	ret = imx25_setup_queue_4wire(priv, settling_cnt, &itemct);
+	if (ret)
+		return ret;
+
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK,
+			   MX25_ADCQ_CR_LITEMID(itemct - 1) |
+			   MX25_ADCQ_CR_WMRK(priv->expected_samples - 1));
+
+	/* setup debounce count */
+	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR,
+			   MX25_TGCR_PDBTIME_MASK,
+			   MX25_TGCR_PDBTIME(debounce_cnt));
+
+	/* enable debounce */
+	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN,
+			   MX25_TGCR_PDBEN);
+	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN,
+			   MX25_TGCR_PDEN);
+
+	/* enable the engine on demand */
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK,
+			   MX25_ADCQ_CR_QSM_FQS);
+
+	/* Enable repeat and repeat wait */
+	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+			   MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK,
+			   MX25_ADCQ_CR_RPT |
+			   MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT));
+
+	mx25_tcq_re_enable_touch_detection(priv);
+
+	return 0;
+}
+
+static int mx25_tcq_parse_dt(struct platform_device *pdev,
+			     struct mx25_tcq_priv *priv)
+{
+	struct device_node *np = pdev->dev.of_node;
+	u32 wires;
+	int ret;
+
+	/* Setup defaults */
+	priv->pen_threshold = 500;
+	priv->sample_count = 3;
+	priv->pen_debounce = 1000000;
+	priv->settling_time = 250000;
+
+	ret = of_property_read_u32(np, "fsl,wires", &wires);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
+		return ret;
+	}
+
+	if (wires == 4) {
+		priv->mode = MX25_TS_4WIRE;
+	} else {
+		dev_err(&pdev->dev, "%u-wire mode not supported\n", wires);
+		return -EINVAL;
+	}
+
+	/* These are optional, we don't care about the return values */
+	of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold);
+	of_property_read_u32(np, "fsl,settling-time", &priv->settling_time);
+	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
+
+	return 0;
+}
+
+static int mx25_tcq_open(struct input_dev *idev)
+{
+	struct device *dev = &idev->dev;
+	struct mx25_tcq_priv *priv = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(dev, "Failed to enable ipg clock\n");
+		return ret;
+	}
+
+	ret = mx25_tcq_init(priv);
+	if (ret) {
+		dev_err(dev, "Failed to init tcq\n");
+		clk_disable_unprepare(priv->clk);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void mx25_tcq_close(struct input_dev *idev)
+{
+	struct mx25_tcq_priv *priv = input_get_drvdata(idev);
+
+	mx25_tcq_force_queue_stop(priv);
+	mx25_tcq_disable_touch_irq(priv);
+	mx25_tcq_disable_fifo_irq(priv);
+	clk_disable_unprepare(priv->clk);
+}
+
+static int mx25_tcq_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct input_dev *idev;
+	struct mx25_tcq_priv *priv;
+	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
+	struct resource *res;
+	void __iomem *mem;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mem = devm_ioremap_resource(dev, res);
+	if (!mem)
+		return -ENOMEM;
+
+	ret = mx25_tcq_parse_dt(pdev, priv);
+	if (ret)
+		return ret;
+
+	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig);
+	if (IS_ERR(priv->regs)) {
+		dev_err(dev, "Failed to initialize regmap\n");
+		return PTR_ERR(priv->regs);
+	}
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (priv->irq <= 0) {
+		dev_err(dev, "Failed to get IRQ\n");
+		return priv->irq;
+	}
+
+	idev = devm_input_allocate_device(dev);
+	if (!idev) {
+		dev_err(dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	idev->name = mx25_tcq_name;
+	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
+	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
+
+	idev->id.bustype = BUS_HOST;
+	idev->open = mx25_tcq_open;
+	idev->close = mx25_tcq_close;
+
+	priv->idev = idev;
+	input_set_drvdata(idev, priv);
+
+	priv->core_regs = tsadc->regs;
+	if (!priv->core_regs)
+		return -EINVAL;
+
+	priv->clk = tsadc->clk;
+	if (!priv->clk)
+		return -EINVAL;
+
+	platform_set_drvdata(pdev, priv);
+
+	ret = input_register_device(idev);
+	if (ret) {
+		dev_err(dev, "Failed to register input device\n");
+		return ret;
+	}
+
+	ret = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq,
+					mx25_tcq_irq_thread, IRQF_ONESHOT,
+					pdev->name, priv);
+	if (ret) {
+		dev_err(dev, "Failed requesting IRQ\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct platform_driver mx25_tcq_driver = {
+	.driver		= {
+		.name	= "mx25-tcq",
+		.of_match_table = mx25_tcq_ids,
+	},
+	.probe		= mx25_tcq_probe,
+};
+module_platform_driver(mx25_tcq_driver);
+
+MODULE_DESCRIPTION("TS input driver for Freescale mx25");
+MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
-- 
2.6.1

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

* [PATCH v8 7/8] ARM: dts: imx25: Add TSC and ADC support
  2015-11-16 12:01 ` Markus Pargmann
@ 2015-11-16 12:01   ` Markus Pargmann
  -1 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam, Markus Pargmann

From: Denis Carikli <denis@eukrea.com>

Signed-off-by: Denis Carikli <denis@eukrea.com>
Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
---
 arch/arm/boot/dts/imx25.dtsi | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/arch/arm/boot/dts/imx25.dtsi b/arch/arm/boot/dts/imx25.dtsi
index 677f81d9dcd5..7b7fc7dd10bd 100644
--- a/arch/arm/boot/dts/imx25.dtsi
+++ b/arch/arm/boot/dts/imx25.dtsi
@@ -265,13 +265,37 @@
 				status = "disabled";
 			};
 
-			tsc: tsc@50030000 {
-				compatible = "fsl,imx25-adc", "fsl,imx21-tsc";
-				reg = <0x50030000 0x4000>;
+			tscadc: tscadc@50030000 {
+				compatible = "fsl,imx25-tsadc";
+				reg = <0x50030000 0xc>;
 				interrupts = <46>;
 				clocks = <&clks 119>;
 				clock-names = "ipg";
+				interrupt-controller;
+				#interrupt-cells = <1>;
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges;
 				status = "disabled";
+
+				adc: adc@50030800 {
+					compatible = "fsl,imx25-gcq";
+					reg = <0x50030800 0x60>;
+					interrupt-parent = <&tscadc>;
+					interrupts = <1>;
+					#address-cells = <1>;
+					#size-cells = <0>;
+					status = "disabled";
+				};
+
+				tsc: tcq@50030400 {
+					compatible = "fsl,imx25-tcq";
+					reg = <0x50030400 0x60>;
+					interrupt-parent = <&tscadc>;
+					interrupts = <0>;
+					fsl,wires = <4>;
+					status = "disabled";
+				};
 			};
 
 			ssi1: ssi@50034000 {
-- 
2.6.1


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

* [PATCH v8 7/8] ARM: dts: imx25: Add TSC and ADC support
@ 2015-11-16 12:01   ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: linux-arm-kernel

From: Denis Carikli <denis@eukrea.com>

Signed-off-by: Denis Carikli <denis@eukrea.com>
Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
---
 arch/arm/boot/dts/imx25.dtsi | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/arch/arm/boot/dts/imx25.dtsi b/arch/arm/boot/dts/imx25.dtsi
index 677f81d9dcd5..7b7fc7dd10bd 100644
--- a/arch/arm/boot/dts/imx25.dtsi
+++ b/arch/arm/boot/dts/imx25.dtsi
@@ -265,13 +265,37 @@
 				status = "disabled";
 			};
 
-			tsc: tsc at 50030000 {
-				compatible = "fsl,imx25-adc", "fsl,imx21-tsc";
-				reg = <0x50030000 0x4000>;
+			tscadc: tscadc at 50030000 {
+				compatible = "fsl,imx25-tsadc";
+				reg = <0x50030000 0xc>;
 				interrupts = <46>;
 				clocks = <&clks 119>;
 				clock-names = "ipg";
+				interrupt-controller;
+				#interrupt-cells = <1>;
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges;
 				status = "disabled";
+
+				adc: adc at 50030800 {
+					compatible = "fsl,imx25-gcq";
+					reg = <0x50030800 0x60>;
+					interrupt-parent = <&tscadc>;
+					interrupts = <1>;
+					#address-cells = <1>;
+					#size-cells = <0>;
+					status = "disabled";
+				};
+
+				tsc: tcq at 50030400 {
+					compatible = "fsl,imx25-tcq";
+					reg = <0x50030400 0x60>;
+					interrupt-parent = <&tscadc>;
+					interrupts = <0>;
+					fsl,wires = <4>;
+					status = "disabled";
+				};
 			};
 
 			ssi1: ssi at 50034000 {
-- 
2.6.1

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

* [PATCH v8 8/8] ARM: imx_v4_v5_defconfig: Add I.MX25 Touchscreen controller and ADC support.
  2015-11-16 12:01 ` Markus Pargmann
  (?)
@ 2015-11-16 12:01     ` Markus Pargmann
  -1 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Hartmut Knaack, Fabio Estevam, Markus Pargmann

From: Denis Carikli <denis-fO0SIAKYzcbQT0dZR+AlfA@public.gmane.org>

Signed-off-by: Denis Carikli <denis-fO0SIAKYzcbQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---
 arch/arm/configs/imx_v4_v5_defconfig | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm/configs/imx_v4_v5_defconfig b/arch/arm/configs/imx_v4_v5_defconfig
index d3a8018639de..d6754ac47788 100644
--- a/arch/arm/configs/imx_v4_v5_defconfig
+++ b/arch/arm/configs/imx_v4_v5_defconfig
@@ -88,6 +88,7 @@ CONFIG_KEYBOARD_IMX=y
 # CONFIG_INPUT_MOUSE is not set
 CONFIG_INPUT_TOUCHSCREEN=y
 CONFIG_TOUCHSCREEN_ADS7846=m
+CONFIG_TOUCHSCREEN_MX25=y
 CONFIG_TOUCHSCREEN_MC13783=y
 # CONFIG_LEGACY_PTYS is not set
 CONFIG_SERIAL_8250=m
@@ -107,6 +108,7 @@ CONFIG_HWMON=m
 CONFIG_SENSORS_MC13783_ADC=m
 CONFIG_WATCHDOG=y
 CONFIG_IMX2_WDT=y
+CONFIG_MFD_MX25_TSADC=y
 CONFIG_MFD_MC13XXX_SPI=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -172,6 +174,8 @@ CONFIG_DMADEVICES=y
 CONFIG_IMX_SDMA=y
 CONFIG_IMX_DMA=y
 # CONFIG_IOMMU_SUPPORT is not set
+CONFIG_IIO=y
+CONFIG_FSL_MX25_ADC=y
 CONFIG_EXT2_FS=y
 CONFIG_EXT3_FS=y
 CONFIG_EXT4_FS=y
-- 
2.6.1

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

* [PATCH v8 8/8] ARM: imx_v4_v5_defconfig: Add I.MX25 Touchscreen controller and ADC support.
@ 2015-11-16 12:01     ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam, Markus Pargmann

From: Denis Carikli <denis@eukrea.com>

Signed-off-by: Denis Carikli <denis@eukrea.com>
Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
---
 arch/arm/configs/imx_v4_v5_defconfig | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm/configs/imx_v4_v5_defconfig b/arch/arm/configs/imx_v4_v5_defconfig
index d3a8018639de..d6754ac47788 100644
--- a/arch/arm/configs/imx_v4_v5_defconfig
+++ b/arch/arm/configs/imx_v4_v5_defconfig
@@ -88,6 +88,7 @@ CONFIG_KEYBOARD_IMX=y
 # CONFIG_INPUT_MOUSE is not set
 CONFIG_INPUT_TOUCHSCREEN=y
 CONFIG_TOUCHSCREEN_ADS7846=m
+CONFIG_TOUCHSCREEN_MX25=y
 CONFIG_TOUCHSCREEN_MC13783=y
 # CONFIG_LEGACY_PTYS is not set
 CONFIG_SERIAL_8250=m
@@ -107,6 +108,7 @@ CONFIG_HWMON=m
 CONFIG_SENSORS_MC13783_ADC=m
 CONFIG_WATCHDOG=y
 CONFIG_IMX2_WDT=y
+CONFIG_MFD_MX25_TSADC=y
 CONFIG_MFD_MC13XXX_SPI=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -172,6 +174,8 @@ CONFIG_DMADEVICES=y
 CONFIG_IMX_SDMA=y
 CONFIG_IMX_DMA=y
 # CONFIG_IOMMU_SUPPORT is not set
+CONFIG_IIO=y
+CONFIG_FSL_MX25_ADC=y
 CONFIG_EXT2_FS=y
 CONFIG_EXT3_FS=y
 CONFIG_EXT4_FS=y
-- 
2.6.1


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

* [PATCH v8 8/8] ARM: imx_v4_v5_defconfig: Add I.MX25 Touchscreen controller and ADC support.
@ 2015-11-16 12:01     ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-16 12:01 UTC (permalink / raw)
  To: linux-arm-kernel

From: Denis Carikli <denis@eukrea.com>

Signed-off-by: Denis Carikli <denis@eukrea.com>
Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
---
 arch/arm/configs/imx_v4_v5_defconfig | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm/configs/imx_v4_v5_defconfig b/arch/arm/configs/imx_v4_v5_defconfig
index d3a8018639de..d6754ac47788 100644
--- a/arch/arm/configs/imx_v4_v5_defconfig
+++ b/arch/arm/configs/imx_v4_v5_defconfig
@@ -88,6 +88,7 @@ CONFIG_KEYBOARD_IMX=y
 # CONFIG_INPUT_MOUSE is not set
 CONFIG_INPUT_TOUCHSCREEN=y
 CONFIG_TOUCHSCREEN_ADS7846=m
+CONFIG_TOUCHSCREEN_MX25=y
 CONFIG_TOUCHSCREEN_MC13783=y
 # CONFIG_LEGACY_PTYS is not set
 CONFIG_SERIAL_8250=m
@@ -107,6 +108,7 @@ CONFIG_HWMON=m
 CONFIG_SENSORS_MC13783_ADC=m
 CONFIG_WATCHDOG=y
 CONFIG_IMX2_WDT=y
+CONFIG_MFD_MX25_TSADC=y
 CONFIG_MFD_MC13XXX_SPI=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -172,6 +174,8 @@ CONFIG_DMADEVICES=y
 CONFIG_IMX_SDMA=y
 CONFIG_IMX_DMA=y
 # CONFIG_IOMMU_SUPPORT is not set
+CONFIG_IIO=y
+CONFIG_FSL_MX25_ADC=y
 CONFIG_EXT2_FS=y
 CONFIG_EXT3_FS=y
 CONFIG_EXT4_FS=y
-- 
2.6.1

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

* Re: [PATCH v8 1/8] ARM: dt: Binding documentation for imx25 ADC/TSC
  2015-11-16 12:01   ` Markus Pargmann
  (?)
@ 2015-11-16 14:01       ` Rob Herring
  -1 siblings, 0 replies; 67+ messages in thread
From: Rob Herring @ 2015-11-16 14:01 UTC (permalink / raw)
  To: Markus Pargmann
  Cc: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones,
	Denis Carikli, Eric Bénard, Sascha Hauer,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Hartmut Knaack, Fabio Estevam

On Mon, Nov 16, 2015 at 01:01:02PM +0100, Markus Pargmann wrote:
> This documentation describes the devicetree bindings for the
> ADC/Touchscreen unit of the i.MX25 SoC.
> 
> Signed-off-by: Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> Acked-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

> ---
> 
> Notes:
>     Changes in v6:
>      - Removed adc-ref property and replaced it with refp and refn for positive and
>        negative references. The properties are optional now as the default
>        behaviour is a positive internal reference voltage and ADC GND as negative
>        reference.
> 
>  .../devicetree/bindings/mfd/fsl-imx25-tsadc.txt    | 46 ++++++++++++++++++++++
>  1 file changed, 46 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> new file mode 100644
> index 000000000000..a857af0eb68c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> @@ -0,0 +1,46 @@
> +Freescale mx25 ADC/TSC multifunction device
> +
> +This device combines two general purpose conversion queues one used for general
> +ADC and the other used for touchscreens.
> +
> +Required properties:
> + - compatible: Should be "fsl,imx25-tsadc".
> + - reg: Memory range of the device.
> + - interrupts: Interrupt for this device as described in
> +   interrupts/interrupts.txt
> + - clocks: An 'ipg' clock defined as described in clocks/clock.txt
> + - interrupt-controller: This device is an interrupt controller. It controls
> +   the interrupts of both conversion queues.
> + - #interrupt-cells: Should be '<1>'.
> + - #address-cells: Should be '<1>'.
> + - #size-cells: Should be '<1>'.
> + - ranges
> +
> +This device includes two conversion queues which can be added as subnodes.
> +The first queue is for the touchscreen, the second for general purpose ADC.
> +
> +Example:
> +	tscadc: tscadc@50030000 {
> +		compatible = "fsl,imx25-tsadc";
> +		reg = <0x50030000 0xc>;
> +		interrupts = <46>;
> +		clocks = <&clks 119>;
> +		clock-names = "ipg";
> +		interrupt-controller;
> +		#interrupt-cells = <1>;
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		ranges;
> +
> +		tsc: tcq@50030400 {
> +			compatible = "fsl,imx25-tcq";
> +			reg = <0x50030400 0x60>;
> +			...
> +		};
> +
> +		adc: gcq@50030800 {
> +			compatible = "fsl,imx25-gcq";
> +			reg = <0x50030800 0x60>;
> +			...
> +		};
> +	};
> -- 
> 2.6.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v8 1/8] ARM: dt: Binding documentation for imx25 ADC/TSC
@ 2015-11-16 14:01       ` Rob Herring
  0 siblings, 0 replies; 67+ messages in thread
From: Rob Herring @ 2015-11-16 14:01 UTC (permalink / raw)
  To: Markus Pargmann
  Cc: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones,
	Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam

On Mon, Nov 16, 2015 at 01:01:02PM +0100, Markus Pargmann wrote:
> This documentation describes the devicetree bindings for the
> ADC/Touchscreen unit of the i.MX25 SoC.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> Acked-by: Jonathan Cameron <jic23@kernel.org>

Acked-by: Rob Herring <robh@kernel.org>

> ---
> 
> Notes:
>     Changes in v6:
>      - Removed adc-ref property and replaced it with refp and refn for positive and
>        negative references. The properties are optional now as the default
>        behaviour is a positive internal reference voltage and ADC GND as negative
>        reference.
> 
>  .../devicetree/bindings/mfd/fsl-imx25-tsadc.txt    | 46 ++++++++++++++++++++++
>  1 file changed, 46 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> new file mode 100644
> index 000000000000..a857af0eb68c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> @@ -0,0 +1,46 @@
> +Freescale mx25 ADC/TSC multifunction device
> +
> +This device combines two general purpose conversion queues one used for general
> +ADC and the other used for touchscreens.
> +
> +Required properties:
> + - compatible: Should be "fsl,imx25-tsadc".
> + - reg: Memory range of the device.
> + - interrupts: Interrupt for this device as described in
> +   interrupts/interrupts.txt
> + - clocks: An 'ipg' clock defined as described in clocks/clock.txt
> + - interrupt-controller: This device is an interrupt controller. It controls
> +   the interrupts of both conversion queues.
> + - #interrupt-cells: Should be '<1>'.
> + - #address-cells: Should be '<1>'.
> + - #size-cells: Should be '<1>'.
> + - ranges
> +
> +This device includes two conversion queues which can be added as subnodes.
> +The first queue is for the touchscreen, the second for general purpose ADC.
> +
> +Example:
> +	tscadc: tscadc@50030000 {
> +		compatible = "fsl,imx25-tsadc";
> +		reg = <0x50030000 0xc>;
> +		interrupts = <46>;
> +		clocks = <&clks 119>;
> +		clock-names = "ipg";
> +		interrupt-controller;
> +		#interrupt-cells = <1>;
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		ranges;
> +
> +		tsc: tcq@50030400 {
> +			compatible = "fsl,imx25-tcq";
> +			reg = <0x50030400 0x60>;
> +			...
> +		};
> +
> +		adc: gcq@50030800 {
> +			compatible = "fsl,imx25-gcq";
> +			reg = <0x50030800 0x60>;
> +			...
> +		};
> +	};
> -- 
> 2.6.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v8 1/8] ARM: dt: Binding documentation for imx25 ADC/TSC
@ 2015-11-16 14:01       ` Rob Herring
  0 siblings, 0 replies; 67+ messages in thread
From: Rob Herring @ 2015-11-16 14:01 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Nov 16, 2015 at 01:01:02PM +0100, Markus Pargmann wrote:
> This documentation describes the devicetree bindings for the
> ADC/Touchscreen unit of the i.MX25 SoC.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> Acked-by: Jonathan Cameron <jic23@kernel.org>

Acked-by: Rob Herring <robh@kernel.org>

> ---
> 
> Notes:
>     Changes in v6:
>      - Removed adc-ref property and replaced it with refp and refn for positive and
>        negative references. The properties are optional now as the default
>        behaviour is a positive internal reference voltage and ADC GND as negative
>        reference.
> 
>  .../devicetree/bindings/mfd/fsl-imx25-tsadc.txt    | 46 ++++++++++++++++++++++
>  1 file changed, 46 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> new file mode 100644
> index 000000000000..a857af0eb68c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> @@ -0,0 +1,46 @@
> +Freescale mx25 ADC/TSC multifunction device
> +
> +This device combines two general purpose conversion queues one used for general
> +ADC and the other used for touchscreens.
> +
> +Required properties:
> + - compatible: Should be "fsl,imx25-tsadc".
> + - reg: Memory range of the device.
> + - interrupts: Interrupt for this device as described in
> +   interrupts/interrupts.txt
> + - clocks: An 'ipg' clock defined as described in clocks/clock.txt
> + - interrupt-controller: This device is an interrupt controller. It controls
> +   the interrupts of both conversion queues.
> + - #interrupt-cells: Should be '<1>'.
> + - #address-cells: Should be '<1>'.
> + - #size-cells: Should be '<1>'.
> + - ranges
> +
> +This device includes two conversion queues which can be added as subnodes.
> +The first queue is for the touchscreen, the second for general purpose ADC.
> +
> +Example:
> +	tscadc: tscadc at 50030000 {
> +		compatible = "fsl,imx25-tsadc";
> +		reg = <0x50030000 0xc>;
> +		interrupts = <46>;
> +		clocks = <&clks 119>;
> +		clock-names = "ipg";
> +		interrupt-controller;
> +		#interrupt-cells = <1>;
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		ranges;
> +
> +		tsc: tcq at 50030400 {
> +			compatible = "fsl,imx25-tcq";
> +			reg = <0x50030400 0x60>;
> +			...
> +		};
> +
> +		adc: gcq at 50030800 {
> +			compatible = "fsl,imx25-gcq";
> +			reg = <0x50030800 0x60>;
> +			...
> +		};
> +	};
> -- 
> 2.6.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v8 3/8] ARM: dt: Binding documentation for imx25 touchscreen controller
  2015-11-16 12:01   ` Markus Pargmann
@ 2015-11-16 14:16     ` Rob Herring
  -1 siblings, 0 replies; 67+ messages in thread
From: Rob Herring @ 2015-11-16 14:16 UTC (permalink / raw)
  To: Markus Pargmann
  Cc: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones,
	Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam

On Mon, Nov 16, 2015 at 01:01:04PM +0100, Markus Pargmann wrote:
> This is the touchscreen conversion queue binding documentation. It uses
> the shared imx25 ADC.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> ---
> 
> Notes:
>     Changes in v5:
>      - Fix signed/unsigned comparison
>      - Fix unused variable settling_time by putting it in the correct argument list
>      - Use continous conversion queue with the repeat feature and a proper
>        repeat-wait. Previously the touchscreen caused massive number of interrupts.
> 
>  .../bindings/input/touchscreen/fsl-mx25-tcq.txt    | 29 ++++++++++++++++++++++
>  1 file changed, 29 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> 
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> new file mode 100644
> index 000000000000..89ab47a3acc1
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> @@ -0,0 +1,29 @@
> +Freescale mx25 TS conversion queue module
> +
> +mx25 touchscreen conversion queue module which controls the ADC unit of the
> +mx25 for attached touchscreens.
> +
> +Required properties:
> + - compatible: Should be "fsl,imx25-tcq".
> + - reg: Memory range of the device.
> + - interrupts: Should be the interrupt number associated with this module within
> +   the tscadc unit (<0>).
> + - interrupt-parent: Should be a phandle to the tscadc unit.
> + - fsl,wires: Should be '<4>' or '<5>'
> +
> +Optional properties:
> + - fsl,pen-debounce: Pen debounce time in nanoseconds.
> + - fsl,pen-threshold: Pen-down threshold for the touchscreen.
> + - fsl,settling-time: Settling time in nanoseconds.

Don't we have standard properties for these? If not, these should have 
units.

I don't understand the difference between debounce and settling times. 
Please make that clear in the doc.

Rob


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

* [PATCH v8 3/8] ARM: dt: Binding documentation for imx25 touchscreen controller
@ 2015-11-16 14:16     ` Rob Herring
  0 siblings, 0 replies; 67+ messages in thread
From: Rob Herring @ 2015-11-16 14:16 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Nov 16, 2015 at 01:01:04PM +0100, Markus Pargmann wrote:
> This is the touchscreen conversion queue binding documentation. It uses
> the shared imx25 ADC.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> ---
> 
> Notes:
>     Changes in v5:
>      - Fix signed/unsigned comparison
>      - Fix unused variable settling_time by putting it in the correct argument list
>      - Use continous conversion queue with the repeat feature and a proper
>        repeat-wait. Previously the touchscreen caused massive number of interrupts.
> 
>  .../bindings/input/touchscreen/fsl-mx25-tcq.txt    | 29 ++++++++++++++++++++++
>  1 file changed, 29 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> 
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> new file mode 100644
> index 000000000000..89ab47a3acc1
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> @@ -0,0 +1,29 @@
> +Freescale mx25 TS conversion queue module
> +
> +mx25 touchscreen conversion queue module which controls the ADC unit of the
> +mx25 for attached touchscreens.
> +
> +Required properties:
> + - compatible: Should be "fsl,imx25-tcq".
> + - reg: Memory range of the device.
> + - interrupts: Should be the interrupt number associated with this module within
> +   the tscadc unit (<0>).
> + - interrupt-parent: Should be a phandle to the tscadc unit.
> + - fsl,wires: Should be '<4>' or '<5>'
> +
> +Optional properties:
> + - fsl,pen-debounce: Pen debounce time in nanoseconds.
> + - fsl,pen-threshold: Pen-down threshold for the touchscreen.
> + - fsl,settling-time: Settling time in nanoseconds.

Don't we have standard properties for these? If not, these should have 
units.

I don't understand the difference between debounce and settling times. 
Please make that clear in the doc.

Rob

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

* Re: [PATCH v8 2/8] ARM: dt: Binding documentation for imx25 GCQ
  2015-11-16 12:01   ` Markus Pargmann
@ 2015-11-16 15:12     ` Rob Herring
  -1 siblings, 0 replies; 67+ messages in thread
From: Rob Herring @ 2015-11-16 15:12 UTC (permalink / raw)
  To: Markus Pargmann
  Cc: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Lee Jones,
	Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam

On Mon, Nov 16, 2015 at 01:01:03PM +0100, Markus Pargmann wrote:
> The documentation describes the bindings for the imx25 GCQ unit which is
> essentially a generic conversion queue using the imx25 ADC.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> ---
> 
> Notes:
>     Changes in v6:
>      - Changed bindings to use adc-refp and adc-refn. Also a bit of cleanup in the
>        setup routine.
>     
>     Changes in v5:
>      - Fixed locking
>      - Removed module owner
> 
>  .../devicetree/bindings/iio/adc/fsl,imx25-gcq.txt  | 58 ++++++++++++++++++++++
>  1 file changed, 58 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt b/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
> new file mode 100644
> index 000000000000..b0866d36a307
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt


> +Optional properties for subnodes:
> + - fsl,adc-refp: specifies the positive reference input as defined in
> +     <dt-bindings/iio/adc/fsl-imx25-gcq.h>
> + - fsl,adc-refn: specifies the negative reference input as defined in
> +     <dt-bindings/iio/adc/fsl-imx25-gcq.h>

This header should be part of this commit.

> +
> +Example:
> +
> +	adc: adc@50030800 {
> +		compatible = "fsl,imx25-gcq";
> +		reg = <0x50030800 0x60>;
> +		interrupt-parent = <&tscadc>;
> +		interrupts = <1>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		inaux@5 {
> +			reg = <5>;
> +			fsl,adc-refp = <MX25_ADC_REFP_INT>;
> +			fsl,adc-refn = <MX25_ADC_REFN_NGND>;
> +		};
> +	};
> -- 
> 2.6.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v8 2/8] ARM: dt: Binding documentation for imx25 GCQ
@ 2015-11-16 15:12     ` Rob Herring
  0 siblings, 0 replies; 67+ messages in thread
From: Rob Herring @ 2015-11-16 15:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Nov 16, 2015 at 01:01:03PM +0100, Markus Pargmann wrote:
> The documentation describes the bindings for the imx25 GCQ unit which is
> essentially a generic conversion queue using the imx25 ADC.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> ---
> 
> Notes:
>     Changes in v6:
>      - Changed bindings to use adc-refp and adc-refn. Also a bit of cleanup in the
>        setup routine.
>     
>     Changes in v5:
>      - Fixed locking
>      - Removed module owner
> 
>  .../devicetree/bindings/iio/adc/fsl,imx25-gcq.txt  | 58 ++++++++++++++++++++++
>  1 file changed, 58 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt b/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
> new file mode 100644
> index 000000000000..b0866d36a307
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt


> +Optional properties for subnodes:
> + - fsl,adc-refp: specifies the positive reference input as defined in
> +     <dt-bindings/iio/adc/fsl-imx25-gcq.h>
> + - fsl,adc-refn: specifies the negative reference input as defined in
> +     <dt-bindings/iio/adc/fsl-imx25-gcq.h>

This header should be part of this commit.

> +
> +Example:
> +
> +	adc: adc at 50030800 {
> +		compatible = "fsl,imx25-gcq";
> +		reg = <0x50030800 0x60>;
> +		interrupt-parent = <&tscadc>;
> +		interrupts = <1>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		inaux at 5 {
> +			reg = <5>;
> +			fsl,adc-refp = <MX25_ADC_REFP_INT>;
> +			fsl,adc-refn = <MX25_ADC_REFN_NGND>;
> +		};
> +	};
> -- 
> 2.6.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v8 2/8] ARM: dt: Binding documentation for imx25 GCQ
  2015-11-16 15:12     ` Rob Herring
  (?)
@ 2015-11-17  7:46       ` Markus Pargmann
  -1 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-17  7:46 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Fabio Estevam,
	Sascha Hauer, linux-iio-u79uwXL29TY76Z2rM5mHXA, Shawn Guo,
	Dmitry Torokhov, Denis Carikli, Eric Bénard,
	linux-input-u79uwXL29TY76Z2rM5mHXA, Lee Jones, Jonathan Cameron,
	Hartmut Knaack

[-- Attachment #1: Type: text/plain, Size: 2478 bytes --]

On Monday 16 November 2015 09:12:55 Rob Herring wrote:
> On Mon, Nov 16, 2015 at 01:01:03PM +0100, Markus Pargmann wrote:
> > The documentation describes the bindings for the imx25 GCQ unit which is
> > essentially a generic conversion queue using the imx25 ADC.
> > 
> > Signed-off-by: Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> > ---
> > 
> > Notes:
> >     Changes in v6:
> >      - Changed bindings to use adc-refp and adc-refn. Also a bit of cleanup in the
> >        setup routine.
> >     
> >     Changes in v5:
> >      - Fixed locking
> >      - Removed module owner
> > 
> >  .../devicetree/bindings/iio/adc/fsl,imx25-gcq.txt  | 58 ++++++++++++++++++++++
> >  1 file changed, 58 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt b/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
> > new file mode 100644
> > index 000000000000..b0866d36a307
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
> 
> 
> > +Optional properties for subnodes:
> > + - fsl,adc-refp: specifies the positive reference input as defined in
> > +     <dt-bindings/iio/adc/fsl-imx25-gcq.h>
> > + - fsl,adc-refn: specifies the negative reference input as defined in
> > +     <dt-bindings/iio/adc/fsl-imx25-gcq.h>
> 
> This header should be part of this commit.

Thanks, will fix that.

Best Regards,

Markus

> 
> > +
> > +Example:
> > +
> > +	adc: adc@50030800 {
> > +		compatible = "fsl,imx25-gcq";
> > +		reg = <0x50030800 0x60>;
> > +		interrupt-parent = <&tscadc>;
> > +		interrupts = <1>;
> > +		#address-cells = <1>;
> > +		#size-cells = <0>;
> > +
> > +		inaux@5 {
> > +			reg = <5>;
> > +			fsl,adc-refp = <MX25_ADC_REFP_INT>;
> > +			fsl,adc-refn = <MX25_ADC_REFN_NGND>;
> > +		};
> > +	};
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v8 2/8] ARM: dt: Binding documentation for imx25 GCQ
@ 2015-11-17  7:46       ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-17  7:46 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Rob Herring, devicetree, Fabio Estevam, Sascha Hauer, linux-iio,
	Shawn Guo, Dmitry Torokhov, Denis Carikli, Eric Bénard,
	linux-input, Lee Jones, Jonathan Cameron, Hartmut Knaack

[-- Attachment #1: Type: text/plain, Size: 2423 bytes --]

On Monday 16 November 2015 09:12:55 Rob Herring wrote:
> On Mon, Nov 16, 2015 at 01:01:03PM +0100, Markus Pargmann wrote:
> > The documentation describes the bindings for the imx25 GCQ unit which is
> > essentially a generic conversion queue using the imx25 ADC.
> > 
> > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > ---
> > 
> > Notes:
> >     Changes in v6:
> >      - Changed bindings to use adc-refp and adc-refn. Also a bit of cleanup in the
> >        setup routine.
> >     
> >     Changes in v5:
> >      - Fixed locking
> >      - Removed module owner
> > 
> >  .../devicetree/bindings/iio/adc/fsl,imx25-gcq.txt  | 58 ++++++++++++++++++++++
> >  1 file changed, 58 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt b/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
> > new file mode 100644
> > index 000000000000..b0866d36a307
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
> 
> 
> > +Optional properties for subnodes:
> > + - fsl,adc-refp: specifies the positive reference input as defined in
> > +     <dt-bindings/iio/adc/fsl-imx25-gcq.h>
> > + - fsl,adc-refn: specifies the negative reference input as defined in
> > +     <dt-bindings/iio/adc/fsl-imx25-gcq.h>
> 
> This header should be part of this commit.

Thanks, will fix that.

Best Regards,

Markus

> 
> > +
> > +Example:
> > +
> > +	adc: adc@50030800 {
> > +		compatible = "fsl,imx25-gcq";
> > +		reg = <0x50030800 0x60>;
> > +		interrupt-parent = <&tscadc>;
> > +		interrupts = <1>;
> > +		#address-cells = <1>;
> > +		#size-cells = <0>;
> > +
> > +		inaux@5 {
> > +			reg = <5>;
> > +			fsl,adc-refp = <MX25_ADC_REFP_INT>;
> > +			fsl,adc-refn = <MX25_ADC_REFN_NGND>;
> > +		};
> > +	};
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* [PATCH v8 2/8] ARM: dt: Binding documentation for imx25 GCQ
@ 2015-11-17  7:46       ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-17  7:46 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 16 November 2015 09:12:55 Rob Herring wrote:
> On Mon, Nov 16, 2015 at 01:01:03PM +0100, Markus Pargmann wrote:
> > The documentation describes the bindings for the imx25 GCQ unit which is
> > essentially a generic conversion queue using the imx25 ADC.
> > 
> > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > ---
> > 
> > Notes:
> >     Changes in v6:
> >      - Changed bindings to use adc-refp and adc-refn. Also a bit of cleanup in the
> >        setup routine.
> >     
> >     Changes in v5:
> >      - Fixed locking
> >      - Removed module owner
> > 
> >  .../devicetree/bindings/iio/adc/fsl,imx25-gcq.txt  | 58 ++++++++++++++++++++++
> >  1 file changed, 58 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt b/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
> > new file mode 100644
> > index 000000000000..b0866d36a307
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/adc/fsl,imx25-gcq.txt
> 
> 
> > +Optional properties for subnodes:
> > + - fsl,adc-refp: specifies the positive reference input as defined in
> > +     <dt-bindings/iio/adc/fsl-imx25-gcq.h>
> > + - fsl,adc-refn: specifies the negative reference input as defined in
> > +     <dt-bindings/iio/adc/fsl-imx25-gcq.h>
> 
> This header should be part of this commit.

Thanks, will fix that.

Best Regards,

Markus

> 
> > +
> > +Example:
> > +
> > +	adc: adc at 50030800 {
> > +		compatible = "fsl,imx25-gcq";
> > +		reg = <0x50030800 0x60>;
> > +		interrupt-parent = <&tscadc>;
> > +		interrupts = <1>;
> > +		#address-cells = <1>;
> > +		#size-cells = <0>;
> > +
> > +		inaux at 5 {
> > +			reg = <5>;
> > +			fsl,adc-refp = <MX25_ADC_REFP_INT>;
> > +			fsl,adc-refn = <MX25_ADC_REFN_NGND>;
> > +		};
> > +	};
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: This is a digitally signed message part.
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20151117/767fe754/attachment.sig>

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

* Re: [PATCH v8 3/8] ARM: dt: Binding documentation for imx25 touchscreen controller
  2015-11-16 14:16     ` Rob Herring
  (?)
@ 2015-11-17  8:54       ` Markus Pargmann
  -1 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-17  8:54 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Fabio Estevam,
	Sascha Hauer, linux-iio-u79uwXL29TY76Z2rM5mHXA, Shawn Guo,
	Dmitry Torokhov, Denis Carikli, Eric Bénard,
	linux-input-u79uwXL29TY76Z2rM5mHXA, Lee Jones, Jonathan Cameron,
	Hartmut Knaack

[-- Attachment #1: Type: text/plain, Size: 3383 bytes --]

Hi,

On Monday 16 November 2015 08:16:09 Rob Herring wrote:
> On Mon, Nov 16, 2015 at 01:01:04PM +0100, Markus Pargmann wrote:
> > This is the touchscreen conversion queue binding documentation. It uses
> > the shared imx25 ADC.
> > 
> > Signed-off-by: Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> > ---
> > 
> > Notes:
> >     Changes in v5:
> >      - Fix signed/unsigned comparison
> >      - Fix unused variable settling_time by putting it in the correct argument list
> >      - Use continous conversion queue with the repeat feature and a proper
> >        repeat-wait. Previously the touchscreen caused massive number of interrupts.
> > 
> >  .../bindings/input/touchscreen/fsl-mx25-tcq.txt    | 29 ++++++++++++++++++++++
> >  1 file changed, 29 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > new file mode 100644
> > index 000000000000..89ab47a3acc1
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > @@ -0,0 +1,29 @@
> > +Freescale mx25 TS conversion queue module
> > +
> > +mx25 touchscreen conversion queue module which controls the ADC unit of the
> > +mx25 for attached touchscreens.
> > +
> > +Required properties:
> > + - compatible: Should be "fsl,imx25-tcq".
> > + - reg: Memory range of the device.
> > + - interrupts: Should be the interrupt number associated with this module within
> > +   the tscadc unit (<0>).
> > + - interrupt-parent: Should be a phandle to the tscadc unit.
> > + - fsl,wires: Should be '<4>' or '<5>'
> > +
> > +Optional properties:
> > + - fsl,pen-debounce: Pen debounce time in nanoseconds.
> > + - fsl,pen-threshold: Pen-down threshold for the touchscreen.
> > + - fsl,settling-time: Settling time in nanoseconds.
> 
> Don't we have standard properties for these? If not, these should have 
> units.

I just found properties like 'debounce_timeout' without a vendor prefix but
they don't seem to be standard properties:
	Documentation/devicetree/bindings/input/touchscreen/brcm,iproc-touchscreen.txt

For fsl,pen-threshold there is no real unit. It is the ratio between the
internal voltage reference and the measured voltage after one of the
touchscreen plates was precharged. However there are limits, I will add a
better description.

> 
> I don't understand the difference between debounce and settling times. 
> Please make that clear in the doc.

The settling time is the time waited after charging the touchscreen plates. The
pen-debounce time is a delay after a pen down has been detected.

Best Regards,

Markus

> 
> Rob
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v8 3/8] ARM: dt: Binding documentation for imx25 touchscreen controller
@ 2015-11-17  8:54       ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-17  8:54 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Rob Herring, devicetree, Fabio Estevam, Sascha Hauer, linux-iio,
	Shawn Guo, Dmitry Torokhov, Denis Carikli, Eric Bénard,
	linux-input, Lee Jones, Jonathan Cameron, Hartmut Knaack

[-- Attachment #1: Type: text/plain, Size: 3328 bytes --]

Hi,

On Monday 16 November 2015 08:16:09 Rob Herring wrote:
> On Mon, Nov 16, 2015 at 01:01:04PM +0100, Markus Pargmann wrote:
> > This is the touchscreen conversion queue binding documentation. It uses
> > the shared imx25 ADC.
> > 
> > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > ---
> > 
> > Notes:
> >     Changes in v5:
> >      - Fix signed/unsigned comparison
> >      - Fix unused variable settling_time by putting it in the correct argument list
> >      - Use continous conversion queue with the repeat feature and a proper
> >        repeat-wait. Previously the touchscreen caused massive number of interrupts.
> > 
> >  .../bindings/input/touchscreen/fsl-mx25-tcq.txt    | 29 ++++++++++++++++++++++
> >  1 file changed, 29 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > new file mode 100644
> > index 000000000000..89ab47a3acc1
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > @@ -0,0 +1,29 @@
> > +Freescale mx25 TS conversion queue module
> > +
> > +mx25 touchscreen conversion queue module which controls the ADC unit of the
> > +mx25 for attached touchscreens.
> > +
> > +Required properties:
> > + - compatible: Should be "fsl,imx25-tcq".
> > + - reg: Memory range of the device.
> > + - interrupts: Should be the interrupt number associated with this module within
> > +   the tscadc unit (<0>).
> > + - interrupt-parent: Should be a phandle to the tscadc unit.
> > + - fsl,wires: Should be '<4>' or '<5>'
> > +
> > +Optional properties:
> > + - fsl,pen-debounce: Pen debounce time in nanoseconds.
> > + - fsl,pen-threshold: Pen-down threshold for the touchscreen.
> > + - fsl,settling-time: Settling time in nanoseconds.
> 
> Don't we have standard properties for these? If not, these should have 
> units.

I just found properties like 'debounce_timeout' without a vendor prefix but
they don't seem to be standard properties:
	Documentation/devicetree/bindings/input/touchscreen/brcm,iproc-touchscreen.txt

For fsl,pen-threshold there is no real unit. It is the ratio between the
internal voltage reference and the measured voltage after one of the
touchscreen plates was precharged. However there are limits, I will add a
better description.

> 
> I don't understand the difference between debounce and settling times. 
> Please make that clear in the doc.

The settling time is the time waited after charging the touchscreen plates. The
pen-debounce time is a delay after a pen down has been detected.

Best Regards,

Markus

> 
> Rob
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* [PATCH v8 3/8] ARM: dt: Binding documentation for imx25 touchscreen controller
@ 2015-11-17  8:54       ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-17  8:54 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Monday 16 November 2015 08:16:09 Rob Herring wrote:
> On Mon, Nov 16, 2015 at 01:01:04PM +0100, Markus Pargmann wrote:
> > This is the touchscreen conversion queue binding documentation. It uses
> > the shared imx25 ADC.
> > 
> > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > ---
> > 
> > Notes:
> >     Changes in v5:
> >      - Fix signed/unsigned comparison
> >      - Fix unused variable settling_time by putting it in the correct argument list
> >      - Use continous conversion queue with the repeat feature and a proper
> >        repeat-wait. Previously the touchscreen caused massive number of interrupts.
> > 
> >  .../bindings/input/touchscreen/fsl-mx25-tcq.txt    | 29 ++++++++++++++++++++++
> >  1 file changed, 29 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > new file mode 100644
> > index 000000000000..89ab47a3acc1
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > @@ -0,0 +1,29 @@
> > +Freescale mx25 TS conversion queue module
> > +
> > +mx25 touchscreen conversion queue module which controls the ADC unit of the
> > +mx25 for attached touchscreens.
> > +
> > +Required properties:
> > + - compatible: Should be "fsl,imx25-tcq".
> > + - reg: Memory range of the device.
> > + - interrupts: Should be the interrupt number associated with this module within
> > +   the tscadc unit (<0>).
> > + - interrupt-parent: Should be a phandle to the tscadc unit.
> > + - fsl,wires: Should be '<4>' or '<5>'
> > +
> > +Optional properties:
> > + - fsl,pen-debounce: Pen debounce time in nanoseconds.
> > + - fsl,pen-threshold: Pen-down threshold for the touchscreen.
> > + - fsl,settling-time: Settling time in nanoseconds.
> 
> Don't we have standard properties for these? If not, these should have 
> units.

I just found properties like 'debounce_timeout' without a vendor prefix but
they don't seem to be standard properties:
	Documentation/devicetree/bindings/input/touchscreen/brcm,iproc-touchscreen.txt

For fsl,pen-threshold there is no real unit. It is the ratio between the
internal voltage reference and the measured voltage after one of the
touchscreen plates was precharged. However there are limits, I will add a
better description.

> 
> I don't understand the difference between debounce and settling times. 
> Please make that clear in the doc.

The settling time is the time waited after charging the touchscreen plates. The
pen-debounce time is a delay after a pen down has been detected.

Best Regards,

Markus

> 
> Rob
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: This is a digitally signed message part.
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20151117/097d61ed/attachment.sig>

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

* Re: [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
  2015-11-16 12:01     ` Markus Pargmann
  (?)
@ 2015-11-17 18:17         ` Dmitry Torokhov
  -1 siblings, 0 replies; 67+ messages in thread
From: Dmitry Torokhov @ 2015-11-17 18:17 UTC (permalink / raw)
  To: Markus Pargmann
  Cc: Shawn Guo, Jonathan Cameron, Lee Jones, Denis Carikli,
	Eric Bénard, Sascha Hauer,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Hartmut Knaack, Fabio Estevam, Juergen Borleis

On Mon, Nov 16, 2015 at 01:01:07PM +0100, Markus Pargmann wrote:
> This is a driver for the imx25 ADC/TSC module. It controls the
> touchscreen conversion queue and creates a touchscreen input device.
> The driver currently only supports 4 wire touchscreens. The driver uses
> a simple conversion queue of precharge, touch detection, X measurement,
> Y measurement, precharge and another touch detection.
> 
> This driver uses the regmap from the parent to setup some touch specific
> settings in the core driver and setup a idle configuration with touch
> detection.
> 
> Signed-off-by: Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> Signed-off-by: Denis Carikli <denis-fO0SIAKYzcbQT0dZR+AlfA@public.gmane.org>
> 
> [fix clock's period calculation]
> [fix calculation of the 'settling' value]
> Signed-off-by: Juergen Borleis <jbe-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> ---
> 
> Notes:
>     Changes in v7:
>      - Moved clk_prepare_enable() and mx25_tcq_init() into mx25_tcq_open(). This
>        was done to be able to use devm_request_threaded_irq().
>      - Cleanup of the probe function through above change
>      - Removed mx25_tcq_remove(), not necessary now
> 
>  drivers/input/touchscreen/Kconfig         |   6 +
>  drivers/input/touchscreen/Makefile        |   1 +
>  drivers/input/touchscreen/fsl-imx25-tcq.c | 600 ++++++++++++++++++++++++++++++
>  3 files changed, 607 insertions(+)
>  create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index ae33da7ab51f..b44651d33080 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -811,6 +811,12 @@ config TOUCHSCREEN_USB_COMPOSITE
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called usbtouchscreen.
>  
> +config TOUCHSCREEN_MX25
> +	tristate "Freescale i.MX25 touchscreen input driver"
> +	depends on MFD_MX25_TSADC
> +	help
> +	  Enable support for touchscreen connected to your i.MX25.
> +

	  To compile this driver as a module...

>  config TOUCHSCREEN_MC13783
>  	tristate "Freescale MC13783 touchscreen input driver"
>  	depends on MFD_MC13XXX
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index cbaa6abb08da..77a2ac54101a 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
>  obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
>  obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
> +obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
>  obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
> diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
> new file mode 100644
> index 000000000000..c833cd814972
> --- /dev/null
> +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
> @@ -0,0 +1,600 @@
> +/*
> + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + * Based on driver from 2011:
> + *   Juergen Beisert, Pengutronix <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> + *
> + * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
> + * connected to the imx25 ADC.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/imx25-tsadc.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +static const char mx25_tcq_name[] = "mx25-tcq";
> +
> +enum mx25_tcq_mode {
> +	MX25_TS_4WIRE,
> +};
> +
> +struct mx25_tcq_priv {
> +	struct regmap *regs;
> +	struct regmap *core_regs;
> +	struct input_dev *idev;
> +	enum mx25_tcq_mode mode;
> +	unsigned int pen_threshold;
> +	unsigned int sample_count;
> +	unsigned int expected_samples;
> +	unsigned int pen_debounce;
> +	unsigned int settling_time;
> +	struct clk *clk;
> +	int irq;
> +};
> +
> +static struct regmap_config mx25_tcq_regconfig = {
> +	.fast_io = true,
> +	.max_register = 0x5c,
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +};
> +
> +static const struct of_device_id mx25_tcq_ids[] = {
> +	{ .compatible = "fsl,imx25-tcq", },
> +	{ /* Sentinel */ }
> +};
> +
> +#define TSC_4WIRE_PRE_INDEX 0
> +#define TSC_4WIRE_X_INDEX 1
> +#define TSC_4WIRE_Y_INDEX 2
> +#define TSC_4WIRE_POST_INDEX 3
> +#define TSC_4WIRE_LEAVE 4
> +
> +#define MX25_TSC_DEF_THRESHOLD 80
> +#define TSC_MAX_SAMPLES 16
> +
> +#define MX25_TSC_REPEAT_WAIT 14
> +
> +enum mx25_adc_configurations {
> +	MX25_CFG_PRECHARGE = 0,
> +	MX25_CFG_TOUCH_DETECT,
> +	MX25_CFG_X_MEASUREMENT,
> +	MX25_CFG_Y_MEASUREMENT,
> +};
> +
> +#define MX25_PRECHARGE_VALUE (\
> +			MX25_ADCQ_CFG_YPLL_OFF | \
> +			MX25_ADCQ_CFG_XNUR_OFF | \
> +			MX25_ADCQ_CFG_XPUL_HIGH | \
> +			MX25_ADCQ_CFG_REFP_INT | \
> +			MX25_ADCQ_CFG_IN_XP | \
> +			MX25_ADCQ_CFG_REFN_NGND2 | \
> +			MX25_ADCQ_CFG_IGS)
> +
> +#define MX25_TOUCH_DETECT_VALUE (\
> +			MX25_ADCQ_CFG_YNLR | \
> +			MX25_ADCQ_CFG_YPLL_OFF | \
> +			MX25_ADCQ_CFG_XNUR_OFF | \
> +			MX25_ADCQ_CFG_XPUL_OFF | \
> +			MX25_ADCQ_CFG_REFP_INT | \
> +			MX25_ADCQ_CFG_IN_XP | \
> +			MX25_ADCQ_CFG_REFN_NGND2 | \
> +			MX25_ADCQ_CFG_PENIACK)
> +
> +static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv,
> +				   unsigned int settling_cnt)
> +{
> +	u32 precharge_cfg =
> +			MX25_PRECHARGE_VALUE |
> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> +	u32 touch_detect_cfg =
> +			MX25_TOUCH_DETECT_VALUE |
> +			MX25_ADCQ_CFG_NOS(1) |
> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> +
> +	regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg);
> +
> +	/* PRECHARGE */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE),
> +		     precharge_cfg);
> +
> +	/* TOUCH_DETECT */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT),
> +		     touch_detect_cfg);
> +
> +	/* X Measurement */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT),
> +		     MX25_ADCQ_CFG_YPLL_OFF |
> +		     MX25_ADCQ_CFG_XNUR_LOW |
> +		     MX25_ADCQ_CFG_XPUL_HIGH |
> +		     MX25_ADCQ_CFG_REFP_XP |
> +		     MX25_ADCQ_CFG_IN_YP |
> +		     MX25_ADCQ_CFG_REFN_XN |
> +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> +
> +	/* Y Measurement */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT),
> +		     MX25_ADCQ_CFG_YNLR |
> +		     MX25_ADCQ_CFG_YPLL_HIGH |
> +		     MX25_ADCQ_CFG_XNUR_OFF |
> +		     MX25_ADCQ_CFG_XPUL_OFF |
> +		     MX25_ADCQ_CFG_REFP_YP |
> +		     MX25_ADCQ_CFG_IN_XP |
> +		     MX25_ADCQ_CFG_REFN_YN |
> +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> +
> +	/* Enable the touch detection right now */
> +	regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg |
> +		     MX25_ADCQ_CFG_IGS);
> +}
> +
> +static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv,
> +				   unsigned settling_cnt, int *items)
> +{
> +	imx25_setup_queue_cfgs(priv, settling_cnt);
> +
> +	/* Setup the conversion queue */
> +	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
> +		     MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) |
> +		     MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) |
> +		     MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) |
> +		     MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) |
> +		     MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) |
> +		     MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT));
> +
> +	/*
> +	 * We measure X/Y with 'sample_count' number of samples and execute a
> +	 * touch detection twice, with 1 sample each
> +	 */
> +	priv->expected_samples = priv->sample_count * 2 + 2;
> +	*items = 6;
> +
> +	return 0;
> +}
> +
> +static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK,
> +			   MX25_ADCQ_CR_PDMSK);
> +}
> +
> +static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0);
> +}
> +
> +static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ,
> +			   MX25_ADCQ_MR_FDRY_IRQ);
> +}
> +
> +static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0);
> +}
> +
> +static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_FQS,
> +			   MX25_ADCQ_CR_FQS);
> +}
> +
> +static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_FQS, 0);
> +}
> +
> +static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv)
> +{
> +	u32 tcqcr;
> +
> +	regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr);
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST,
> +			   MX25_ADCQ_CR_FRST);
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0);
> +	regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr);
> +}
> +
> +static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv)
> +{
> +	/* stop the queue from looping */
> +	mx25_tcq_force_queue_stop(priv);
> +
> +	/* for a clean touch detection, preload the X plane */
> +	regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE);
> +
> +	/* waste some time now to pre-load the X plate to high voltage */
> +	mx25_tcq_fifo_reset(priv);
> +
> +	/* re-enable the detection right now */
> +	regmap_write(priv->core_regs, MX25_TSC_TICR,
> +		     MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS);
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD,
> +			   MX25_ADCQ_SR_PD);
> +
> +	/* enable the pen down event to be a source for the interrupt */
> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0);
> +
> +	/* lets fire the next IRQ if someone touches the touchscreen */
> +	mx25_tcq_enable_touch_irq(priv);
> +}
> +
> +static int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,

Why not void? What callers are supposed to do with the value?

> +					   u32 *sample_buf,
> +					   unsigned int samples)
> +{
> +	unsigned int x_pos = 0;
> +	unsigned int y_pos = 0;
> +	unsigned int touch_pre = 0;
> +	unsigned int touch_post = 0;
> +	unsigned int i;
> +	int ret = 0;
> +
> +	for (i = 0; i < samples; i++) {
> +		unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]);
> +		unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]);
> +
> +		switch (index) {
> +		case 1:
> +			touch_pre = val;
> +			break;
> +		case 2:
> +			x_pos = val;
> +			break;
> +		case 3:
> +			y_pos = val;
> +			break;
> +		case 5:
> +			touch_post = val;
> +			break;
> +		default:
> +			ret = -EINVAL;
> +			break;
> +		}
> +	}
> +
> +	if (ret == 0 && samples != 0) {

Where do we set ret to anything other than 0?

> +		/*
> +		 * only if both touch measures are below a threshold,
> +		 * the position is valid
> +		 */
> +		if (touch_pre < priv->pen_threshold &&
> +		    touch_post < priv->pen_threshold) {
> +			/* valid samples, generate a report */
> +			x_pos /= priv->sample_count;
> +			y_pos /= priv->sample_count;
> +			input_report_abs(priv->idev, ABS_X, x_pos);
> +			input_report_abs(priv->idev, ABS_Y, y_pos);
> +			input_report_key(priv->idev, BTN_TOUCH, 1);
> +			input_sync(priv->idev);
> +
> +			/* get next sample */
> +			mx25_tcq_enable_fifo_irq(priv);
> +		} else if (touch_pre >= priv->pen_threshold &&
> +			   touch_post >= priv->pen_threshold) {
> +			/*
> +			 * if both samples are invalid,
> +			 * generate a release report
> +			 */
> +			input_report_key(priv->idev, BTN_TOUCH, 0);
> +			input_sync(priv->idev);
> +			mx25_tcq_re_enable_touch_detection(priv);
> +		} else {
> +			/*
> +			 * if only one of both touch measurements are
> +			 * below the threshold, still some bouncing
> +			 * happens. Take additional samples in this
> +			 * case to be sure
> +			 */
> +			mx25_tcq_enable_fifo_irq(priv);
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
> +{
> +	struct mx25_tcq_priv *priv = dev_id;
> +	u32 sample_buf[TSC_MAX_SAMPLES];
> +	unsigned int samples = 0;
> +
> +	/* read all samples */
> +	while (1) {
> +		u32 stats;
> +
> +		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
> +		if (stats & MX25_ADCQ_SR_EMPT)
> +			break;
> +
> +		if (samples < TSC_MAX_SAMPLES) {
> +			regmap_read(priv->regs, MX25_ADCQ_FIFO,
> +				    &sample_buf[samples]);
> +			++samples;
> +		} else {
> +			u32 discarded;
> +			/* discard samples */
> +			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);
> +		}
> +	}
> +
> +	mx25_tcq_create_event_for_4wire(priv, sample_buf, samples);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mx25_tcq_irq(int irq, void *dev_id)
> +{
> +	struct mx25_tcq_priv *priv = dev_id;
> +	u32 stat;
> +	int ret = IRQ_HANDLED;
> +
> +	regmap_read(priv->regs, MX25_ADCQ_SR, &stat);

Is there any concern that these reads will fail?

> +
> +	if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR))
> +		mx25_tcq_fifo_reset(priv);
> +
> +	if (stat & MX25_ADCQ_SR_PD) {
> +		mx25_tcq_disable_touch_irq(priv);
> +		mx25_tcq_force_queue_start(priv);
> +		mx25_tcq_enable_fifo_irq(priv);
> +	}
> +
> +	if (stat & MX25_ADCQ_SR_FDRY) {
> +		mx25_tcq_disable_fifo_irq(priv);
> +		ret = IRQ_WAKE_THREAD;
> +	}
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
> +			   MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
> +			   MX25_ADCQ_SR_PD | MX25_ADCQ_SR_EOQ,
> +			   MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR |
> +			   MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
> +			   MX25_ADCQ_SR_EOQ);
> +
> +	return ret;
> +}
> +
> +/* configure the statemachine for a 4-wire touchscreen */
> +static int mx25_tcq_init(struct mx25_tcq_priv *priv)
> +{
> +	u32 tgcr;
> +	unsigned int ipg_div;
> +	unsigned int adc_period;
> +	unsigned int debounce_cnt;
> +	unsigned int settling_cnt;
> +	int itemct;
> +	int ret;
> +
> +	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
> +	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
> +	adc_period = USEC_PER_SEC * ipg_div * 2 + 2;
> +	adc_period /= clk_get_rate(priv->clk) / 1000 + 1;
> +	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
> +	settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1;
> +
> +	/* Reset */
> +	regmap_write(priv->regs, MX25_ADCQ_CR,
> +		     MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST);
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0);
> +
> +	/* up to 128 * 8 ADC clocks are possible */
> +	if (debounce_cnt > 127)
> +		debounce_cnt = 127;
> +
> +	/* up to 255 * 8 ADC clocks are possible */
> +	if (settling_cnt > 255)
> +		settling_cnt = 255;
> +
> +	ret = imx25_setup_queue_4wire(priv, settling_cnt, &itemct);
> +	if (ret)
> +		return ret;
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK,
> +			   MX25_ADCQ_CR_LITEMID(itemct - 1) |
> +			   MX25_ADCQ_CR_WMRK(priv->expected_samples - 1));
> +
> +	/* setup debounce count */
> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR,
> +			   MX25_TGCR_PDBTIME_MASK,
> +			   MX25_TGCR_PDBTIME(debounce_cnt));
> +
> +	/* enable debounce */
> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN,
> +			   MX25_TGCR_PDBEN);
> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN,
> +			   MX25_TGCR_PDEN);
> +
> +	/* enable the engine on demand */
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK,
> +			   MX25_ADCQ_CR_QSM_FQS);
> +
> +	/* Enable repeat and repeat wait */
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK,
> +			   MX25_ADCQ_CR_RPT |
> +			   MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT));
> +
> +	mx25_tcq_re_enable_touch_detection(priv);
> +
> +	return 0;
> +}
> +
> +static int mx25_tcq_parse_dt(struct platform_device *pdev,
> +			     struct mx25_tcq_priv *priv)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	u32 wires;
> +	int ret;
> +
> +	/* Setup defaults */
> +	priv->pen_threshold = 500;
> +	priv->sample_count = 3;
> +	priv->pen_debounce = 1000000;
> +	priv->settling_time = 250000;
> +
> +	ret = of_property_read_u32(np, "fsl,wires", &wires);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
> +		return ret;
> +	}
> +
> +	if (wires == 4) {
> +		priv->mode = MX25_TS_4WIRE;
> +	} else {
> +		dev_err(&pdev->dev, "%u-wire mode not supported\n", wires);
> +		return -EINVAL;
> +	}
> +
> +	/* These are optional, we don't care about the return values */
> +	of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold);
> +	of_property_read_u32(np, "fsl,settling-time", &priv->settling_time);
> +	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
> +
> +	return 0;
> +}
> +
> +static int mx25_tcq_open(struct input_dev *idev)
> +{
> +	struct device *dev = &idev->dev;
> +	struct mx25_tcq_priv *priv = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = clk_prepare_enable(priv->clk);
> +	if (ret) {
> +		dev_err(dev, "Failed to enable ipg clock\n");
> +		return ret;
> +	}
> +
> +	ret = mx25_tcq_init(priv);
> +	if (ret) {
> +		dev_err(dev, "Failed to init tcq\n");
> +		clk_disable_unprepare(priv->clk);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mx25_tcq_close(struct input_dev *idev)
> +{
> +	struct mx25_tcq_priv *priv = input_get_drvdata(idev);
> +
> +	mx25_tcq_force_queue_stop(priv);
> +	mx25_tcq_disable_touch_irq(priv);
> +	mx25_tcq_disable_fifo_irq(priv);
> +	clk_disable_unprepare(priv->clk);
> +}
> +
> +static int mx25_tcq_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct input_dev *idev;
> +	struct mx25_tcq_priv *priv;
> +	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
> +	struct resource *res;
> +	void __iomem *mem;
> +	int ret;

Personal preference: can we call variables that hold error codes and not
returned in success path (i.e. when we do explicit "return 0:' for
success) be called "error"?
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	mem = devm_ioremap_resource(dev, res);
> +	if (!mem)

devm_ioremap_resource() returns ERR_PTR-encoded pointer, you should not
test it for NULL bit rather for IS_ERR.

> +		return -ENOMEM;
> +
> +	ret = mx25_tcq_parse_dt(pdev, priv);
> +	if (ret)
> +		return ret;
> +
> +	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig);
> +	if (IS_ERR(priv->regs)) {
> +		dev_err(dev, "Failed to initialize regmap\n");
> +		return PTR_ERR(priv->regs);
> +	}
> +
> +	priv->irq = platform_get_irq(pdev, 0);
> +	if (priv->irq <= 0) {
> +		dev_err(dev, "Failed to get IRQ\n");
> +		return priv->irq;
> +	}
> +
> +	idev = devm_input_allocate_device(dev);
> +	if (!idev) {
> +		dev_err(dev, "Failed to allocate input device\n");
> +		return -ENOMEM;
> +	}
> +
> +	idev->name = mx25_tcq_name;
> +	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> +	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);

Replace the 2 lines above with:

	input_set_capability(idev, EV_BTN_TOUCH);

> +	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
> +	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
> +
> +	idev->id.bustype = BUS_HOST;
> +	idev->open = mx25_tcq_open;
> +	idev->close = mx25_tcq_close;
> +
> +	priv->idev = idev;
> +	input_set_drvdata(idev, priv);
> +
> +	priv->core_regs = tsadc->regs;
> +	if (!priv->core_regs)
> +		return -EINVAL;
> +
> +	priv->clk = tsadc->clk;
> +	if (!priv->clk)
> +		return -EINVAL;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	ret = input_register_device(idev);
> +	if (ret) {
> +		dev_err(dev, "Failed to register input device\n");
> +		return ret;
> +	}
> +
> +	ret = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq,
> +					mx25_tcq_irq_thread, IRQF_ONESHOT,
> +					pdev->name, priv);
> +	if (ret) {
> +		dev_err(dev, "Failed requesting IRQ\n");
> +		return ret;
> +	}

Is it possible that we enable interrupt generation in the controller (in
mx25_tcq_open) before we request IRQ and then (if someone touches the
screen) we'll get a spurious IRQ? I'd rather we swapped
devm_request_threaded_irq() and input_register_device(): it is perfectly
safe to use (send events) to allocated but not registered input device.

> +
> +	return 0;
> +}
> +
> +static struct platform_driver mx25_tcq_driver = {
> +	.driver		= {
> +		.name	= "mx25-tcq",
> +		.of_match_table = mx25_tcq_ids,
> +	},
> +	.probe		= mx25_tcq_probe,
> +};
> +module_platform_driver(mx25_tcq_driver);
> +
> +MODULE_DESCRIPTION("TS input driver for Freescale mx25");
> +MODULE_AUTHOR("Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.6.1
> 

Thanks.

-- 
Dmitry

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

* Re: [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
@ 2015-11-17 18:17         ` Dmitry Torokhov
  0 siblings, 0 replies; 67+ messages in thread
From: Dmitry Torokhov @ 2015-11-17 18:17 UTC (permalink / raw)
  To: Markus Pargmann
  Cc: Shawn Guo, Jonathan Cameron, Lee Jones, Denis Carikli,
	Eric Bénard, Sascha Hauer, devicetree, linux-input,
	linux-iio, linux-arm-kernel, Hartmut Knaack, Fabio Estevam,
	Juergen Borleis

On Mon, Nov 16, 2015 at 01:01:07PM +0100, Markus Pargmann wrote:
> This is a driver for the imx25 ADC/TSC module. It controls the
> touchscreen conversion queue and creates a touchscreen input device.
> The driver currently only supports 4 wire touchscreens. The driver uses
> a simple conversion queue of precharge, touch detection, X measurement,
> Y measurement, precharge and another touch detection.
> 
> This driver uses the regmap from the parent to setup some touch specific
> settings in the core driver and setup a idle configuration with touch
> detection.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> Signed-off-by: Denis Carikli <denis@eukrea.com>
> 
> [fix clock's period calculation]
> [fix calculation of the 'settling' value]
> Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
> ---
> 
> Notes:
>     Changes in v7:
>      - Moved clk_prepare_enable() and mx25_tcq_init() into mx25_tcq_open(). This
>        was done to be able to use devm_request_threaded_irq().
>      - Cleanup of the probe function through above change
>      - Removed mx25_tcq_remove(), not necessary now
> 
>  drivers/input/touchscreen/Kconfig         |   6 +
>  drivers/input/touchscreen/Makefile        |   1 +
>  drivers/input/touchscreen/fsl-imx25-tcq.c | 600 ++++++++++++++++++++++++++++++
>  3 files changed, 607 insertions(+)
>  create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index ae33da7ab51f..b44651d33080 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -811,6 +811,12 @@ config TOUCHSCREEN_USB_COMPOSITE
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called usbtouchscreen.
>  
> +config TOUCHSCREEN_MX25
> +	tristate "Freescale i.MX25 touchscreen input driver"
> +	depends on MFD_MX25_TSADC
> +	help
> +	  Enable support for touchscreen connected to your i.MX25.
> +

	  To compile this driver as a module...

>  config TOUCHSCREEN_MC13783
>  	tristate "Freescale MC13783 touchscreen input driver"
>  	depends on MFD_MC13XXX
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index cbaa6abb08da..77a2ac54101a 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
>  obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
>  obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
> +obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
>  obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
> diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
> new file mode 100644
> index 000000000000..c833cd814972
> --- /dev/null
> +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
> @@ -0,0 +1,600 @@
> +/*
> + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + * Based on driver from 2011:
> + *   Juergen Beisert, Pengutronix <kernel@pengutronix.de>
> + *
> + * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
> + * connected to the imx25 ADC.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/imx25-tsadc.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +static const char mx25_tcq_name[] = "mx25-tcq";
> +
> +enum mx25_tcq_mode {
> +	MX25_TS_4WIRE,
> +};
> +
> +struct mx25_tcq_priv {
> +	struct regmap *regs;
> +	struct regmap *core_regs;
> +	struct input_dev *idev;
> +	enum mx25_tcq_mode mode;
> +	unsigned int pen_threshold;
> +	unsigned int sample_count;
> +	unsigned int expected_samples;
> +	unsigned int pen_debounce;
> +	unsigned int settling_time;
> +	struct clk *clk;
> +	int irq;
> +};
> +
> +static struct regmap_config mx25_tcq_regconfig = {
> +	.fast_io = true,
> +	.max_register = 0x5c,
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +};
> +
> +static const struct of_device_id mx25_tcq_ids[] = {
> +	{ .compatible = "fsl,imx25-tcq", },
> +	{ /* Sentinel */ }
> +};
> +
> +#define TSC_4WIRE_PRE_INDEX 0
> +#define TSC_4WIRE_X_INDEX 1
> +#define TSC_4WIRE_Y_INDEX 2
> +#define TSC_4WIRE_POST_INDEX 3
> +#define TSC_4WIRE_LEAVE 4
> +
> +#define MX25_TSC_DEF_THRESHOLD 80
> +#define TSC_MAX_SAMPLES 16
> +
> +#define MX25_TSC_REPEAT_WAIT 14
> +
> +enum mx25_adc_configurations {
> +	MX25_CFG_PRECHARGE = 0,
> +	MX25_CFG_TOUCH_DETECT,
> +	MX25_CFG_X_MEASUREMENT,
> +	MX25_CFG_Y_MEASUREMENT,
> +};
> +
> +#define MX25_PRECHARGE_VALUE (\
> +			MX25_ADCQ_CFG_YPLL_OFF | \
> +			MX25_ADCQ_CFG_XNUR_OFF | \
> +			MX25_ADCQ_CFG_XPUL_HIGH | \
> +			MX25_ADCQ_CFG_REFP_INT | \
> +			MX25_ADCQ_CFG_IN_XP | \
> +			MX25_ADCQ_CFG_REFN_NGND2 | \
> +			MX25_ADCQ_CFG_IGS)
> +
> +#define MX25_TOUCH_DETECT_VALUE (\
> +			MX25_ADCQ_CFG_YNLR | \
> +			MX25_ADCQ_CFG_YPLL_OFF | \
> +			MX25_ADCQ_CFG_XNUR_OFF | \
> +			MX25_ADCQ_CFG_XPUL_OFF | \
> +			MX25_ADCQ_CFG_REFP_INT | \
> +			MX25_ADCQ_CFG_IN_XP | \
> +			MX25_ADCQ_CFG_REFN_NGND2 | \
> +			MX25_ADCQ_CFG_PENIACK)
> +
> +static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv,
> +				   unsigned int settling_cnt)
> +{
> +	u32 precharge_cfg =
> +			MX25_PRECHARGE_VALUE |
> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> +	u32 touch_detect_cfg =
> +			MX25_TOUCH_DETECT_VALUE |
> +			MX25_ADCQ_CFG_NOS(1) |
> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> +
> +	regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg);
> +
> +	/* PRECHARGE */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE),
> +		     precharge_cfg);
> +
> +	/* TOUCH_DETECT */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT),
> +		     touch_detect_cfg);
> +
> +	/* X Measurement */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT),
> +		     MX25_ADCQ_CFG_YPLL_OFF |
> +		     MX25_ADCQ_CFG_XNUR_LOW |
> +		     MX25_ADCQ_CFG_XPUL_HIGH |
> +		     MX25_ADCQ_CFG_REFP_XP |
> +		     MX25_ADCQ_CFG_IN_YP |
> +		     MX25_ADCQ_CFG_REFN_XN |
> +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> +
> +	/* Y Measurement */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT),
> +		     MX25_ADCQ_CFG_YNLR |
> +		     MX25_ADCQ_CFG_YPLL_HIGH |
> +		     MX25_ADCQ_CFG_XNUR_OFF |
> +		     MX25_ADCQ_CFG_XPUL_OFF |
> +		     MX25_ADCQ_CFG_REFP_YP |
> +		     MX25_ADCQ_CFG_IN_XP |
> +		     MX25_ADCQ_CFG_REFN_YN |
> +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> +
> +	/* Enable the touch detection right now */
> +	regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg |
> +		     MX25_ADCQ_CFG_IGS);
> +}
> +
> +static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv,
> +				   unsigned settling_cnt, int *items)
> +{
> +	imx25_setup_queue_cfgs(priv, settling_cnt);
> +
> +	/* Setup the conversion queue */
> +	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
> +		     MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) |
> +		     MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) |
> +		     MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) |
> +		     MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) |
> +		     MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) |
> +		     MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT));
> +
> +	/*
> +	 * We measure X/Y with 'sample_count' number of samples and execute a
> +	 * touch detection twice, with 1 sample each
> +	 */
> +	priv->expected_samples = priv->sample_count * 2 + 2;
> +	*items = 6;
> +
> +	return 0;
> +}
> +
> +static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK,
> +			   MX25_ADCQ_CR_PDMSK);
> +}
> +
> +static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0);
> +}
> +
> +static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ,
> +			   MX25_ADCQ_MR_FDRY_IRQ);
> +}
> +
> +static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0);
> +}
> +
> +static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_FQS,
> +			   MX25_ADCQ_CR_FQS);
> +}
> +
> +static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_FQS, 0);
> +}
> +
> +static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv)
> +{
> +	u32 tcqcr;
> +
> +	regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr);
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST,
> +			   MX25_ADCQ_CR_FRST);
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0);
> +	regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr);
> +}
> +
> +static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv)
> +{
> +	/* stop the queue from looping */
> +	mx25_tcq_force_queue_stop(priv);
> +
> +	/* for a clean touch detection, preload the X plane */
> +	regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE);
> +
> +	/* waste some time now to pre-load the X plate to high voltage */
> +	mx25_tcq_fifo_reset(priv);
> +
> +	/* re-enable the detection right now */
> +	regmap_write(priv->core_regs, MX25_TSC_TICR,
> +		     MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS);
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD,
> +			   MX25_ADCQ_SR_PD);
> +
> +	/* enable the pen down event to be a source for the interrupt */
> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0);
> +
> +	/* lets fire the next IRQ if someone touches the touchscreen */
> +	mx25_tcq_enable_touch_irq(priv);
> +}
> +
> +static int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,

Why not void? What callers are supposed to do with the value?

> +					   u32 *sample_buf,
> +					   unsigned int samples)
> +{
> +	unsigned int x_pos = 0;
> +	unsigned int y_pos = 0;
> +	unsigned int touch_pre = 0;
> +	unsigned int touch_post = 0;
> +	unsigned int i;
> +	int ret = 0;
> +
> +	for (i = 0; i < samples; i++) {
> +		unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]);
> +		unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]);
> +
> +		switch (index) {
> +		case 1:
> +			touch_pre = val;
> +			break;
> +		case 2:
> +			x_pos = val;
> +			break;
> +		case 3:
> +			y_pos = val;
> +			break;
> +		case 5:
> +			touch_post = val;
> +			break;
> +		default:
> +			ret = -EINVAL;
> +			break;
> +		}
> +	}
> +
> +	if (ret == 0 && samples != 0) {

Where do we set ret to anything other than 0?

> +		/*
> +		 * only if both touch measures are below a threshold,
> +		 * the position is valid
> +		 */
> +		if (touch_pre < priv->pen_threshold &&
> +		    touch_post < priv->pen_threshold) {
> +			/* valid samples, generate a report */
> +			x_pos /= priv->sample_count;
> +			y_pos /= priv->sample_count;
> +			input_report_abs(priv->idev, ABS_X, x_pos);
> +			input_report_abs(priv->idev, ABS_Y, y_pos);
> +			input_report_key(priv->idev, BTN_TOUCH, 1);
> +			input_sync(priv->idev);
> +
> +			/* get next sample */
> +			mx25_tcq_enable_fifo_irq(priv);
> +		} else if (touch_pre >= priv->pen_threshold &&
> +			   touch_post >= priv->pen_threshold) {
> +			/*
> +			 * if both samples are invalid,
> +			 * generate a release report
> +			 */
> +			input_report_key(priv->idev, BTN_TOUCH, 0);
> +			input_sync(priv->idev);
> +			mx25_tcq_re_enable_touch_detection(priv);
> +		} else {
> +			/*
> +			 * if only one of both touch measurements are
> +			 * below the threshold, still some bouncing
> +			 * happens. Take additional samples in this
> +			 * case to be sure
> +			 */
> +			mx25_tcq_enable_fifo_irq(priv);
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
> +{
> +	struct mx25_tcq_priv *priv = dev_id;
> +	u32 sample_buf[TSC_MAX_SAMPLES];
> +	unsigned int samples = 0;
> +
> +	/* read all samples */
> +	while (1) {
> +		u32 stats;
> +
> +		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
> +		if (stats & MX25_ADCQ_SR_EMPT)
> +			break;
> +
> +		if (samples < TSC_MAX_SAMPLES) {
> +			regmap_read(priv->regs, MX25_ADCQ_FIFO,
> +				    &sample_buf[samples]);
> +			++samples;
> +		} else {
> +			u32 discarded;
> +			/* discard samples */
> +			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);
> +		}
> +	}
> +
> +	mx25_tcq_create_event_for_4wire(priv, sample_buf, samples);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mx25_tcq_irq(int irq, void *dev_id)
> +{
> +	struct mx25_tcq_priv *priv = dev_id;
> +	u32 stat;
> +	int ret = IRQ_HANDLED;
> +
> +	regmap_read(priv->regs, MX25_ADCQ_SR, &stat);

Is there any concern that these reads will fail?

> +
> +	if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR))
> +		mx25_tcq_fifo_reset(priv);
> +
> +	if (stat & MX25_ADCQ_SR_PD) {
> +		mx25_tcq_disable_touch_irq(priv);
> +		mx25_tcq_force_queue_start(priv);
> +		mx25_tcq_enable_fifo_irq(priv);
> +	}
> +
> +	if (stat & MX25_ADCQ_SR_FDRY) {
> +		mx25_tcq_disable_fifo_irq(priv);
> +		ret = IRQ_WAKE_THREAD;
> +	}
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
> +			   MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
> +			   MX25_ADCQ_SR_PD | MX25_ADCQ_SR_EOQ,
> +			   MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR |
> +			   MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
> +			   MX25_ADCQ_SR_EOQ);
> +
> +	return ret;
> +}
> +
> +/* configure the statemachine for a 4-wire touchscreen */
> +static int mx25_tcq_init(struct mx25_tcq_priv *priv)
> +{
> +	u32 tgcr;
> +	unsigned int ipg_div;
> +	unsigned int adc_period;
> +	unsigned int debounce_cnt;
> +	unsigned int settling_cnt;
> +	int itemct;
> +	int ret;
> +
> +	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
> +	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
> +	adc_period = USEC_PER_SEC * ipg_div * 2 + 2;
> +	adc_period /= clk_get_rate(priv->clk) / 1000 + 1;
> +	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
> +	settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1;
> +
> +	/* Reset */
> +	regmap_write(priv->regs, MX25_ADCQ_CR,
> +		     MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST);
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0);
> +
> +	/* up to 128 * 8 ADC clocks are possible */
> +	if (debounce_cnt > 127)
> +		debounce_cnt = 127;
> +
> +	/* up to 255 * 8 ADC clocks are possible */
> +	if (settling_cnt > 255)
> +		settling_cnt = 255;
> +
> +	ret = imx25_setup_queue_4wire(priv, settling_cnt, &itemct);
> +	if (ret)
> +		return ret;
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK,
> +			   MX25_ADCQ_CR_LITEMID(itemct - 1) |
> +			   MX25_ADCQ_CR_WMRK(priv->expected_samples - 1));
> +
> +	/* setup debounce count */
> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR,
> +			   MX25_TGCR_PDBTIME_MASK,
> +			   MX25_TGCR_PDBTIME(debounce_cnt));
> +
> +	/* enable debounce */
> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN,
> +			   MX25_TGCR_PDBEN);
> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN,
> +			   MX25_TGCR_PDEN);
> +
> +	/* enable the engine on demand */
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK,
> +			   MX25_ADCQ_CR_QSM_FQS);
> +
> +	/* Enable repeat and repeat wait */
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK,
> +			   MX25_ADCQ_CR_RPT |
> +			   MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT));
> +
> +	mx25_tcq_re_enable_touch_detection(priv);
> +
> +	return 0;
> +}
> +
> +static int mx25_tcq_parse_dt(struct platform_device *pdev,
> +			     struct mx25_tcq_priv *priv)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	u32 wires;
> +	int ret;
> +
> +	/* Setup defaults */
> +	priv->pen_threshold = 500;
> +	priv->sample_count = 3;
> +	priv->pen_debounce = 1000000;
> +	priv->settling_time = 250000;
> +
> +	ret = of_property_read_u32(np, "fsl,wires", &wires);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
> +		return ret;
> +	}
> +
> +	if (wires == 4) {
> +		priv->mode = MX25_TS_4WIRE;
> +	} else {
> +		dev_err(&pdev->dev, "%u-wire mode not supported\n", wires);
> +		return -EINVAL;
> +	}
> +
> +	/* These are optional, we don't care about the return values */
> +	of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold);
> +	of_property_read_u32(np, "fsl,settling-time", &priv->settling_time);
> +	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
> +
> +	return 0;
> +}
> +
> +static int mx25_tcq_open(struct input_dev *idev)
> +{
> +	struct device *dev = &idev->dev;
> +	struct mx25_tcq_priv *priv = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = clk_prepare_enable(priv->clk);
> +	if (ret) {
> +		dev_err(dev, "Failed to enable ipg clock\n");
> +		return ret;
> +	}
> +
> +	ret = mx25_tcq_init(priv);
> +	if (ret) {
> +		dev_err(dev, "Failed to init tcq\n");
> +		clk_disable_unprepare(priv->clk);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mx25_tcq_close(struct input_dev *idev)
> +{
> +	struct mx25_tcq_priv *priv = input_get_drvdata(idev);
> +
> +	mx25_tcq_force_queue_stop(priv);
> +	mx25_tcq_disable_touch_irq(priv);
> +	mx25_tcq_disable_fifo_irq(priv);
> +	clk_disable_unprepare(priv->clk);
> +}
> +
> +static int mx25_tcq_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct input_dev *idev;
> +	struct mx25_tcq_priv *priv;
> +	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
> +	struct resource *res;
> +	void __iomem *mem;
> +	int ret;

Personal preference: can we call variables that hold error codes and not
returned in success path (i.e. when we do explicit "return 0:' for
success) be called "error"?
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	mem = devm_ioremap_resource(dev, res);
> +	if (!mem)

devm_ioremap_resource() returns ERR_PTR-encoded pointer, you should not
test it for NULL bit rather for IS_ERR.

> +		return -ENOMEM;
> +
> +	ret = mx25_tcq_parse_dt(pdev, priv);
> +	if (ret)
> +		return ret;
> +
> +	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig);
> +	if (IS_ERR(priv->regs)) {
> +		dev_err(dev, "Failed to initialize regmap\n");
> +		return PTR_ERR(priv->regs);
> +	}
> +
> +	priv->irq = platform_get_irq(pdev, 0);
> +	if (priv->irq <= 0) {
> +		dev_err(dev, "Failed to get IRQ\n");
> +		return priv->irq;
> +	}
> +
> +	idev = devm_input_allocate_device(dev);
> +	if (!idev) {
> +		dev_err(dev, "Failed to allocate input device\n");
> +		return -ENOMEM;
> +	}
> +
> +	idev->name = mx25_tcq_name;
> +	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> +	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);

Replace the 2 lines above with:

	input_set_capability(idev, EV_BTN_TOUCH);

> +	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
> +	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
> +
> +	idev->id.bustype = BUS_HOST;
> +	idev->open = mx25_tcq_open;
> +	idev->close = mx25_tcq_close;
> +
> +	priv->idev = idev;
> +	input_set_drvdata(idev, priv);
> +
> +	priv->core_regs = tsadc->regs;
> +	if (!priv->core_regs)
> +		return -EINVAL;
> +
> +	priv->clk = tsadc->clk;
> +	if (!priv->clk)
> +		return -EINVAL;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	ret = input_register_device(idev);
> +	if (ret) {
> +		dev_err(dev, "Failed to register input device\n");
> +		return ret;
> +	}
> +
> +	ret = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq,
> +					mx25_tcq_irq_thread, IRQF_ONESHOT,
> +					pdev->name, priv);
> +	if (ret) {
> +		dev_err(dev, "Failed requesting IRQ\n");
> +		return ret;
> +	}

Is it possible that we enable interrupt generation in the controller (in
mx25_tcq_open) before we request IRQ and then (if someone touches the
screen) we'll get a spurious IRQ? I'd rather we swapped
devm_request_threaded_irq() and input_register_device(): it is perfectly
safe to use (send events) to allocated but not registered input device.

> +
> +	return 0;
> +}
> +
> +static struct platform_driver mx25_tcq_driver = {
> +	.driver		= {
> +		.name	= "mx25-tcq",
> +		.of_match_table = mx25_tcq_ids,
> +	},
> +	.probe		= mx25_tcq_probe,
> +};
> +module_platform_driver(mx25_tcq_driver);
> +
> +MODULE_DESCRIPTION("TS input driver for Freescale mx25");
> +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.6.1
> 

Thanks.

-- 
Dmitry

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

* [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
@ 2015-11-17 18:17         ` Dmitry Torokhov
  0 siblings, 0 replies; 67+ messages in thread
From: Dmitry Torokhov @ 2015-11-17 18:17 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Nov 16, 2015 at 01:01:07PM +0100, Markus Pargmann wrote:
> This is a driver for the imx25 ADC/TSC module. It controls the
> touchscreen conversion queue and creates a touchscreen input device.
> The driver currently only supports 4 wire touchscreens. The driver uses
> a simple conversion queue of precharge, touch detection, X measurement,
> Y measurement, precharge and another touch detection.
> 
> This driver uses the regmap from the parent to setup some touch specific
> settings in the core driver and setup a idle configuration with touch
> detection.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> Signed-off-by: Denis Carikli <denis@eukrea.com>
> 
> [fix clock's period calculation]
> [fix calculation of the 'settling' value]
> Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
> ---
> 
> Notes:
>     Changes in v7:
>      - Moved clk_prepare_enable() and mx25_tcq_init() into mx25_tcq_open(). This
>        was done to be able to use devm_request_threaded_irq().
>      - Cleanup of the probe function through above change
>      - Removed mx25_tcq_remove(), not necessary now
> 
>  drivers/input/touchscreen/Kconfig         |   6 +
>  drivers/input/touchscreen/Makefile        |   1 +
>  drivers/input/touchscreen/fsl-imx25-tcq.c | 600 ++++++++++++++++++++++++++++++
>  3 files changed, 607 insertions(+)
>  create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index ae33da7ab51f..b44651d33080 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -811,6 +811,12 @@ config TOUCHSCREEN_USB_COMPOSITE
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called usbtouchscreen.
>  
> +config TOUCHSCREEN_MX25
> +	tristate "Freescale i.MX25 touchscreen input driver"
> +	depends on MFD_MX25_TSADC
> +	help
> +	  Enable support for touchscreen connected to your i.MX25.
> +

	  To compile this driver as a module...

>  config TOUCHSCREEN_MC13783
>  	tristate "Freescale MC13783 touchscreen input driver"
>  	depends on MFD_MC13XXX
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index cbaa6abb08da..77a2ac54101a 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
>  obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
>  obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
> +obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
>  obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
> diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
> new file mode 100644
> index 000000000000..c833cd814972
> --- /dev/null
> +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
> @@ -0,0 +1,600 @@
> +/*
> + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + * Based on driver from 2011:
> + *   Juergen Beisert, Pengutronix <kernel@pengutronix.de>
> + *
> + * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
> + * connected to the imx25 ADC.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/imx25-tsadc.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +static const char mx25_tcq_name[] = "mx25-tcq";
> +
> +enum mx25_tcq_mode {
> +	MX25_TS_4WIRE,
> +};
> +
> +struct mx25_tcq_priv {
> +	struct regmap *regs;
> +	struct regmap *core_regs;
> +	struct input_dev *idev;
> +	enum mx25_tcq_mode mode;
> +	unsigned int pen_threshold;
> +	unsigned int sample_count;
> +	unsigned int expected_samples;
> +	unsigned int pen_debounce;
> +	unsigned int settling_time;
> +	struct clk *clk;
> +	int irq;
> +};
> +
> +static struct regmap_config mx25_tcq_regconfig = {
> +	.fast_io = true,
> +	.max_register = 0x5c,
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +};
> +
> +static const struct of_device_id mx25_tcq_ids[] = {
> +	{ .compatible = "fsl,imx25-tcq", },
> +	{ /* Sentinel */ }
> +};
> +
> +#define TSC_4WIRE_PRE_INDEX 0
> +#define TSC_4WIRE_X_INDEX 1
> +#define TSC_4WIRE_Y_INDEX 2
> +#define TSC_4WIRE_POST_INDEX 3
> +#define TSC_4WIRE_LEAVE 4
> +
> +#define MX25_TSC_DEF_THRESHOLD 80
> +#define TSC_MAX_SAMPLES 16
> +
> +#define MX25_TSC_REPEAT_WAIT 14
> +
> +enum mx25_adc_configurations {
> +	MX25_CFG_PRECHARGE = 0,
> +	MX25_CFG_TOUCH_DETECT,
> +	MX25_CFG_X_MEASUREMENT,
> +	MX25_CFG_Y_MEASUREMENT,
> +};
> +
> +#define MX25_PRECHARGE_VALUE (\
> +			MX25_ADCQ_CFG_YPLL_OFF | \
> +			MX25_ADCQ_CFG_XNUR_OFF | \
> +			MX25_ADCQ_CFG_XPUL_HIGH | \
> +			MX25_ADCQ_CFG_REFP_INT | \
> +			MX25_ADCQ_CFG_IN_XP | \
> +			MX25_ADCQ_CFG_REFN_NGND2 | \
> +			MX25_ADCQ_CFG_IGS)
> +
> +#define MX25_TOUCH_DETECT_VALUE (\
> +			MX25_ADCQ_CFG_YNLR | \
> +			MX25_ADCQ_CFG_YPLL_OFF | \
> +			MX25_ADCQ_CFG_XNUR_OFF | \
> +			MX25_ADCQ_CFG_XPUL_OFF | \
> +			MX25_ADCQ_CFG_REFP_INT | \
> +			MX25_ADCQ_CFG_IN_XP | \
> +			MX25_ADCQ_CFG_REFN_NGND2 | \
> +			MX25_ADCQ_CFG_PENIACK)
> +
> +static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv,
> +				   unsigned int settling_cnt)
> +{
> +	u32 precharge_cfg =
> +			MX25_PRECHARGE_VALUE |
> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> +	u32 touch_detect_cfg =
> +			MX25_TOUCH_DETECT_VALUE |
> +			MX25_ADCQ_CFG_NOS(1) |
> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> +
> +	regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg);
> +
> +	/* PRECHARGE */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE),
> +		     precharge_cfg);
> +
> +	/* TOUCH_DETECT */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT),
> +		     touch_detect_cfg);
> +
> +	/* X Measurement */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT),
> +		     MX25_ADCQ_CFG_YPLL_OFF |
> +		     MX25_ADCQ_CFG_XNUR_LOW |
> +		     MX25_ADCQ_CFG_XPUL_HIGH |
> +		     MX25_ADCQ_CFG_REFP_XP |
> +		     MX25_ADCQ_CFG_IN_YP |
> +		     MX25_ADCQ_CFG_REFN_XN |
> +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> +
> +	/* Y Measurement */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT),
> +		     MX25_ADCQ_CFG_YNLR |
> +		     MX25_ADCQ_CFG_YPLL_HIGH |
> +		     MX25_ADCQ_CFG_XNUR_OFF |
> +		     MX25_ADCQ_CFG_XPUL_OFF |
> +		     MX25_ADCQ_CFG_REFP_YP |
> +		     MX25_ADCQ_CFG_IN_XP |
> +		     MX25_ADCQ_CFG_REFN_YN |
> +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> +
> +	/* Enable the touch detection right now */
> +	regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg |
> +		     MX25_ADCQ_CFG_IGS);
> +}
> +
> +static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv,
> +				   unsigned settling_cnt, int *items)
> +{
> +	imx25_setup_queue_cfgs(priv, settling_cnt);
> +
> +	/* Setup the conversion queue */
> +	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
> +		     MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) |
> +		     MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) |
> +		     MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) |
> +		     MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) |
> +		     MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) |
> +		     MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT));
> +
> +	/*
> +	 * We measure X/Y with 'sample_count' number of samples and execute a
> +	 * touch detection twice, with 1 sample each
> +	 */
> +	priv->expected_samples = priv->sample_count * 2 + 2;
> +	*items = 6;
> +
> +	return 0;
> +}
> +
> +static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK,
> +			   MX25_ADCQ_CR_PDMSK);
> +}
> +
> +static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0);
> +}
> +
> +static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ,
> +			   MX25_ADCQ_MR_FDRY_IRQ);
> +}
> +
> +static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0);
> +}
> +
> +static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_FQS,
> +			   MX25_ADCQ_CR_FQS);
> +}
> +
> +static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_FQS, 0);
> +}
> +
> +static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv)
> +{
> +	u32 tcqcr;
> +
> +	regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr);
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST,
> +			   MX25_ADCQ_CR_FRST);
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0);
> +	regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr);
> +}
> +
> +static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv)
> +{
> +	/* stop the queue from looping */
> +	mx25_tcq_force_queue_stop(priv);
> +
> +	/* for a clean touch detection, preload the X plane */
> +	regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE);
> +
> +	/* waste some time now to pre-load the X plate to high voltage */
> +	mx25_tcq_fifo_reset(priv);
> +
> +	/* re-enable the detection right now */
> +	regmap_write(priv->core_regs, MX25_TSC_TICR,
> +		     MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS);
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD,
> +			   MX25_ADCQ_SR_PD);
> +
> +	/* enable the pen down event to be a source for the interrupt */
> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0);
> +
> +	/* lets fire the next IRQ if someone touches the touchscreen */
> +	mx25_tcq_enable_touch_irq(priv);
> +}
> +
> +static int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,

Why not void? What callers are supposed to do with the value?

> +					   u32 *sample_buf,
> +					   unsigned int samples)
> +{
> +	unsigned int x_pos = 0;
> +	unsigned int y_pos = 0;
> +	unsigned int touch_pre = 0;
> +	unsigned int touch_post = 0;
> +	unsigned int i;
> +	int ret = 0;
> +
> +	for (i = 0; i < samples; i++) {
> +		unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]);
> +		unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]);
> +
> +		switch (index) {
> +		case 1:
> +			touch_pre = val;
> +			break;
> +		case 2:
> +			x_pos = val;
> +			break;
> +		case 3:
> +			y_pos = val;
> +			break;
> +		case 5:
> +			touch_post = val;
> +			break;
> +		default:
> +			ret = -EINVAL;
> +			break;
> +		}
> +	}
> +
> +	if (ret == 0 && samples != 0) {

Where do we set ret to anything other than 0?

> +		/*
> +		 * only if both touch measures are below a threshold,
> +		 * the position is valid
> +		 */
> +		if (touch_pre < priv->pen_threshold &&
> +		    touch_post < priv->pen_threshold) {
> +			/* valid samples, generate a report */
> +			x_pos /= priv->sample_count;
> +			y_pos /= priv->sample_count;
> +			input_report_abs(priv->idev, ABS_X, x_pos);
> +			input_report_abs(priv->idev, ABS_Y, y_pos);
> +			input_report_key(priv->idev, BTN_TOUCH, 1);
> +			input_sync(priv->idev);
> +
> +			/* get next sample */
> +			mx25_tcq_enable_fifo_irq(priv);
> +		} else if (touch_pre >= priv->pen_threshold &&
> +			   touch_post >= priv->pen_threshold) {
> +			/*
> +			 * if both samples are invalid,
> +			 * generate a release report
> +			 */
> +			input_report_key(priv->idev, BTN_TOUCH, 0);
> +			input_sync(priv->idev);
> +			mx25_tcq_re_enable_touch_detection(priv);
> +		} else {
> +			/*
> +			 * if only one of both touch measurements are
> +			 * below the threshold, still some bouncing
> +			 * happens. Take additional samples in this
> +			 * case to be sure
> +			 */
> +			mx25_tcq_enable_fifo_irq(priv);
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
> +{
> +	struct mx25_tcq_priv *priv = dev_id;
> +	u32 sample_buf[TSC_MAX_SAMPLES];
> +	unsigned int samples = 0;
> +
> +	/* read all samples */
> +	while (1) {
> +		u32 stats;
> +
> +		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
> +		if (stats & MX25_ADCQ_SR_EMPT)
> +			break;
> +
> +		if (samples < TSC_MAX_SAMPLES) {
> +			regmap_read(priv->regs, MX25_ADCQ_FIFO,
> +				    &sample_buf[samples]);
> +			++samples;
> +		} else {
> +			u32 discarded;
> +			/* discard samples */
> +			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);
> +		}
> +	}
> +
> +	mx25_tcq_create_event_for_4wire(priv, sample_buf, samples);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mx25_tcq_irq(int irq, void *dev_id)
> +{
> +	struct mx25_tcq_priv *priv = dev_id;
> +	u32 stat;
> +	int ret = IRQ_HANDLED;
> +
> +	regmap_read(priv->regs, MX25_ADCQ_SR, &stat);

Is there any concern that these reads will fail?

> +
> +	if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR))
> +		mx25_tcq_fifo_reset(priv);
> +
> +	if (stat & MX25_ADCQ_SR_PD) {
> +		mx25_tcq_disable_touch_irq(priv);
> +		mx25_tcq_force_queue_start(priv);
> +		mx25_tcq_enable_fifo_irq(priv);
> +	}
> +
> +	if (stat & MX25_ADCQ_SR_FDRY) {
> +		mx25_tcq_disable_fifo_irq(priv);
> +		ret = IRQ_WAKE_THREAD;
> +	}
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
> +			   MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
> +			   MX25_ADCQ_SR_PD | MX25_ADCQ_SR_EOQ,
> +			   MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR |
> +			   MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
> +			   MX25_ADCQ_SR_EOQ);
> +
> +	return ret;
> +}
> +
> +/* configure the statemachine for a 4-wire touchscreen */
> +static int mx25_tcq_init(struct mx25_tcq_priv *priv)
> +{
> +	u32 tgcr;
> +	unsigned int ipg_div;
> +	unsigned int adc_period;
> +	unsigned int debounce_cnt;
> +	unsigned int settling_cnt;
> +	int itemct;
> +	int ret;
> +
> +	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
> +	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
> +	adc_period = USEC_PER_SEC * ipg_div * 2 + 2;
> +	adc_period /= clk_get_rate(priv->clk) / 1000 + 1;
> +	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
> +	settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1;
> +
> +	/* Reset */
> +	regmap_write(priv->regs, MX25_ADCQ_CR,
> +		     MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST);
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0);
> +
> +	/* up to 128 * 8 ADC clocks are possible */
> +	if (debounce_cnt > 127)
> +		debounce_cnt = 127;
> +
> +	/* up to 255 * 8 ADC clocks are possible */
> +	if (settling_cnt > 255)
> +		settling_cnt = 255;
> +
> +	ret = imx25_setup_queue_4wire(priv, settling_cnt, &itemct);
> +	if (ret)
> +		return ret;
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK,
> +			   MX25_ADCQ_CR_LITEMID(itemct - 1) |
> +			   MX25_ADCQ_CR_WMRK(priv->expected_samples - 1));
> +
> +	/* setup debounce count */
> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR,
> +			   MX25_TGCR_PDBTIME_MASK,
> +			   MX25_TGCR_PDBTIME(debounce_cnt));
> +
> +	/* enable debounce */
> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN,
> +			   MX25_TGCR_PDBEN);
> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN,
> +			   MX25_TGCR_PDEN);
> +
> +	/* enable the engine on demand */
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK,
> +			   MX25_ADCQ_CR_QSM_FQS);
> +
> +	/* Enable repeat and repeat wait */
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK,
> +			   MX25_ADCQ_CR_RPT |
> +			   MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT));
> +
> +	mx25_tcq_re_enable_touch_detection(priv);
> +
> +	return 0;
> +}
> +
> +static int mx25_tcq_parse_dt(struct platform_device *pdev,
> +			     struct mx25_tcq_priv *priv)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	u32 wires;
> +	int ret;
> +
> +	/* Setup defaults */
> +	priv->pen_threshold = 500;
> +	priv->sample_count = 3;
> +	priv->pen_debounce = 1000000;
> +	priv->settling_time = 250000;
> +
> +	ret = of_property_read_u32(np, "fsl,wires", &wires);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
> +		return ret;
> +	}
> +
> +	if (wires == 4) {
> +		priv->mode = MX25_TS_4WIRE;
> +	} else {
> +		dev_err(&pdev->dev, "%u-wire mode not supported\n", wires);
> +		return -EINVAL;
> +	}
> +
> +	/* These are optional, we don't care about the return values */
> +	of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold);
> +	of_property_read_u32(np, "fsl,settling-time", &priv->settling_time);
> +	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
> +
> +	return 0;
> +}
> +
> +static int mx25_tcq_open(struct input_dev *idev)
> +{
> +	struct device *dev = &idev->dev;
> +	struct mx25_tcq_priv *priv = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = clk_prepare_enable(priv->clk);
> +	if (ret) {
> +		dev_err(dev, "Failed to enable ipg clock\n");
> +		return ret;
> +	}
> +
> +	ret = mx25_tcq_init(priv);
> +	if (ret) {
> +		dev_err(dev, "Failed to init tcq\n");
> +		clk_disable_unprepare(priv->clk);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mx25_tcq_close(struct input_dev *idev)
> +{
> +	struct mx25_tcq_priv *priv = input_get_drvdata(idev);
> +
> +	mx25_tcq_force_queue_stop(priv);
> +	mx25_tcq_disable_touch_irq(priv);
> +	mx25_tcq_disable_fifo_irq(priv);
> +	clk_disable_unprepare(priv->clk);
> +}
> +
> +static int mx25_tcq_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct input_dev *idev;
> +	struct mx25_tcq_priv *priv;
> +	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
> +	struct resource *res;
> +	void __iomem *mem;
> +	int ret;

Personal preference: can we call variables that hold error codes and not
returned in success path (i.e. when we do explicit "return 0:' for
success) be called "error"?
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	mem = devm_ioremap_resource(dev, res);
> +	if (!mem)

devm_ioremap_resource() returns ERR_PTR-encoded pointer, you should not
test it for NULL bit rather for IS_ERR.

> +		return -ENOMEM;
> +
> +	ret = mx25_tcq_parse_dt(pdev, priv);
> +	if (ret)
> +		return ret;
> +
> +	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig);
> +	if (IS_ERR(priv->regs)) {
> +		dev_err(dev, "Failed to initialize regmap\n");
> +		return PTR_ERR(priv->regs);
> +	}
> +
> +	priv->irq = platform_get_irq(pdev, 0);
> +	if (priv->irq <= 0) {
> +		dev_err(dev, "Failed to get IRQ\n");
> +		return priv->irq;
> +	}
> +
> +	idev = devm_input_allocate_device(dev);
> +	if (!idev) {
> +		dev_err(dev, "Failed to allocate input device\n");
> +		return -ENOMEM;
> +	}
> +
> +	idev->name = mx25_tcq_name;
> +	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> +	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);

Replace the 2 lines above with:

	input_set_capability(idev, EV_BTN_TOUCH);

> +	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
> +	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
> +
> +	idev->id.bustype = BUS_HOST;
> +	idev->open = mx25_tcq_open;
> +	idev->close = mx25_tcq_close;
> +
> +	priv->idev = idev;
> +	input_set_drvdata(idev, priv);
> +
> +	priv->core_regs = tsadc->regs;
> +	if (!priv->core_regs)
> +		return -EINVAL;
> +
> +	priv->clk = tsadc->clk;
> +	if (!priv->clk)
> +		return -EINVAL;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	ret = input_register_device(idev);
> +	if (ret) {
> +		dev_err(dev, "Failed to register input device\n");
> +		return ret;
> +	}
> +
> +	ret = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq,
> +					mx25_tcq_irq_thread, IRQF_ONESHOT,
> +					pdev->name, priv);
> +	if (ret) {
> +		dev_err(dev, "Failed requesting IRQ\n");
> +		return ret;
> +	}

Is it possible that we enable interrupt generation in the controller (in
mx25_tcq_open) before we request IRQ and then (if someone touches the
screen) we'll get a spurious IRQ? I'd rather we swapped
devm_request_threaded_irq() and input_register_device(): it is perfectly
safe to use (send events) to allocated but not registered input device.

> +
> +	return 0;
> +}
> +
> +static struct platform_driver mx25_tcq_driver = {
> +	.driver		= {
> +		.name	= "mx25-tcq",
> +		.of_match_table = mx25_tcq_ids,
> +	},
> +	.probe		= mx25_tcq_probe,
> +};
> +module_platform_driver(mx25_tcq_driver);
> +
> +MODULE_DESCRIPTION("TS input driver for Freescale mx25");
> +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.6.1
> 

Thanks.

-- 
Dmitry

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

* Re: [PATCH v8 3/8] ARM: dt: Binding documentation for imx25 touchscreen controller
  2015-11-16 14:16     ` Rob Herring
  (?)
@ 2015-11-17 18:21       ` Dmitry Torokhov
  -1 siblings, 0 replies; 67+ messages in thread
From: Dmitry Torokhov @ 2015-11-17 18:21 UTC (permalink / raw)
  To: Rob Herring
  Cc: Markus Pargmann, Shawn Guo, Jonathan Cameron, Lee Jones,
	Denis Carikli, Eric Bénard, Sascha Hauer,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Hartmut Knaack, Fabio Estevam

On Mon, Nov 16, 2015 at 08:16:09AM -0600, Rob Herring wrote:
> On Mon, Nov 16, 2015 at 01:01:04PM +0100, Markus Pargmann wrote:
> > This is the touchscreen conversion queue binding documentation. It uses
> > the shared imx25 ADC.
> > 
> > Signed-off-by: Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> > ---
> > 
> > Notes:
> >     Changes in v5:
> >      - Fix signed/unsigned comparison
> >      - Fix unused variable settling_time by putting it in the correct argument list
> >      - Use continous conversion queue with the repeat feature and a proper
> >        repeat-wait. Previously the touchscreen caused massive number of interrupts.
> > 
> >  .../bindings/input/touchscreen/fsl-mx25-tcq.txt    | 29 ++++++++++++++++++++++
> >  1 file changed, 29 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > new file mode 100644
> > index 000000000000..89ab47a3acc1
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > @@ -0,0 +1,29 @@
> > +Freescale mx25 TS conversion queue module
> > +
> > +mx25 touchscreen conversion queue module which controls the ADC unit of the
> > +mx25 for attached touchscreens.
> > +
> > +Required properties:
> > + - compatible: Should be "fsl,imx25-tcq".
> > + - reg: Memory range of the device.
> > + - interrupts: Should be the interrupt number associated with this module within
> > +   the tscadc unit (<0>).
> > + - interrupt-parent: Should be a phandle to the tscadc unit.
> > + - fsl,wires: Should be '<4>' or '<5>'
> > +
> > +Optional properties:
> > + - fsl,pen-debounce: Pen debounce time in nanoseconds.
> > + - fsl,pen-threshold: Pen-down threshold for the touchscreen.
> > + - fsl,settling-time: Settling time in nanoseconds.
> 
> Don't we have standard properties for these?

These are usually controller-specific so no.

Thanks.

-- 
Dmitry

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

* Re: [PATCH v8 3/8] ARM: dt: Binding documentation for imx25 touchscreen controller
@ 2015-11-17 18:21       ` Dmitry Torokhov
  0 siblings, 0 replies; 67+ messages in thread
From: Dmitry Torokhov @ 2015-11-17 18:21 UTC (permalink / raw)
  To: Rob Herring
  Cc: Markus Pargmann, Shawn Guo, Jonathan Cameron, Lee Jones,
	Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam

On Mon, Nov 16, 2015 at 08:16:09AM -0600, Rob Herring wrote:
> On Mon, Nov 16, 2015 at 01:01:04PM +0100, Markus Pargmann wrote:
> > This is the touchscreen conversion queue binding documentation. It uses
> > the shared imx25 ADC.
> > 
> > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > ---
> > 
> > Notes:
> >     Changes in v5:
> >      - Fix signed/unsigned comparison
> >      - Fix unused variable settling_time by putting it in the correct argument list
> >      - Use continous conversion queue with the repeat feature and a proper
> >        repeat-wait. Previously the touchscreen caused massive number of interrupts.
> > 
> >  .../bindings/input/touchscreen/fsl-mx25-tcq.txt    | 29 ++++++++++++++++++++++
> >  1 file changed, 29 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > new file mode 100644
> > index 000000000000..89ab47a3acc1
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > @@ -0,0 +1,29 @@
> > +Freescale mx25 TS conversion queue module
> > +
> > +mx25 touchscreen conversion queue module which controls the ADC unit of the
> > +mx25 for attached touchscreens.
> > +
> > +Required properties:
> > + - compatible: Should be "fsl,imx25-tcq".
> > + - reg: Memory range of the device.
> > + - interrupts: Should be the interrupt number associated with this module within
> > +   the tscadc unit (<0>).
> > + - interrupt-parent: Should be a phandle to the tscadc unit.
> > + - fsl,wires: Should be '<4>' or '<5>'
> > +
> > +Optional properties:
> > + - fsl,pen-debounce: Pen debounce time in nanoseconds.
> > + - fsl,pen-threshold: Pen-down threshold for the touchscreen.
> > + - fsl,settling-time: Settling time in nanoseconds.
> 
> Don't we have standard properties for these?

These are usually controller-specific so no.

Thanks.

-- 
Dmitry

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

* [PATCH v8 3/8] ARM: dt: Binding documentation for imx25 touchscreen controller
@ 2015-11-17 18:21       ` Dmitry Torokhov
  0 siblings, 0 replies; 67+ messages in thread
From: Dmitry Torokhov @ 2015-11-17 18:21 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Nov 16, 2015 at 08:16:09AM -0600, Rob Herring wrote:
> On Mon, Nov 16, 2015 at 01:01:04PM +0100, Markus Pargmann wrote:
> > This is the touchscreen conversion queue binding documentation. It uses
> > the shared imx25 ADC.
> > 
> > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > ---
> > 
> > Notes:
> >     Changes in v5:
> >      - Fix signed/unsigned comparison
> >      - Fix unused variable settling_time by putting it in the correct argument list
> >      - Use continous conversion queue with the repeat feature and a proper
> >        repeat-wait. Previously the touchscreen caused massive number of interrupts.
> > 
> >  .../bindings/input/touchscreen/fsl-mx25-tcq.txt    | 29 ++++++++++++++++++++++
> >  1 file changed, 29 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > new file mode 100644
> > index 000000000000..89ab47a3acc1
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> > @@ -0,0 +1,29 @@
> > +Freescale mx25 TS conversion queue module
> > +
> > +mx25 touchscreen conversion queue module which controls the ADC unit of the
> > +mx25 for attached touchscreens.
> > +
> > +Required properties:
> > + - compatible: Should be "fsl,imx25-tcq".
> > + - reg: Memory range of the device.
> > + - interrupts: Should be the interrupt number associated with this module within
> > +   the tscadc unit (<0>).
> > + - interrupt-parent: Should be a phandle to the tscadc unit.
> > + - fsl,wires: Should be '<4>' or '<5>'
> > +
> > +Optional properties:
> > + - fsl,pen-debounce: Pen debounce time in nanoseconds.
> > + - fsl,pen-threshold: Pen-down threshold for the touchscreen.
> > + - fsl,settling-time: Settling time in nanoseconds.
> 
> Don't we have standard properties for these?

These are usually controller-specific so no.

Thanks.

-- 
Dmitry

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

* Re: [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
  2015-11-17 18:17         ` Dmitry Torokhov
  (?)
@ 2015-11-20 13:33           ` Markus Pargmann
  -1 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-20 13:33 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Shawn Guo, Jonathan Cameron, Lee Jones, Denis Carikli,
	Eric Bénard, Sascha Hauer,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Hartmut Knaack, Fabio Estevam, Juergen Borleis

[-- Attachment #1: Type: text/plain, Size: 23968 bytes --]

Hi,

On Tuesday 17 November 2015 10:17:20 Dmitry Torokhov wrote:
> On Mon, Nov 16, 2015 at 01:01:07PM +0100, Markus Pargmann wrote:
> > This is a driver for the imx25 ADC/TSC module. It controls the
> > touchscreen conversion queue and creates a touchscreen input device.
> > The driver currently only supports 4 wire touchscreens. The driver uses
> > a simple conversion queue of precharge, touch detection, X measurement,
> > Y measurement, precharge and another touch detection.
> > 
> > This driver uses the regmap from the parent to setup some touch specific
> > settings in the core driver and setup a idle configuration with touch
> > detection.
> > 
> > Signed-off-by: Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> > Signed-off-by: Denis Carikli <denis-fO0SIAKYzcbQT0dZR+AlfA@public.gmane.org>
> > 
> > [fix clock's period calculation]
> > [fix calculation of the 'settling' value]
> > Signed-off-by: Juergen Borleis <jbe-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> > ---
> > 
> > Notes:
> >     Changes in v7:
> >      - Moved clk_prepare_enable() and mx25_tcq_init() into mx25_tcq_open(). This
> >        was done to be able to use devm_request_threaded_irq().
> >      - Cleanup of the probe function through above change
> >      - Removed mx25_tcq_remove(), not necessary now
> > 
> >  drivers/input/touchscreen/Kconfig         |   6 +
> >  drivers/input/touchscreen/Makefile        |   1 +
> >  drivers/input/touchscreen/fsl-imx25-tcq.c | 600 ++++++++++++++++++++++++++++++
> >  3 files changed, 607 insertions(+)
> >  create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
> > 
> > diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> > index ae33da7ab51f..b44651d33080 100644
> > --- a/drivers/input/touchscreen/Kconfig
> > +++ b/drivers/input/touchscreen/Kconfig
> > @@ -811,6 +811,12 @@ config TOUCHSCREEN_USB_COMPOSITE
> >  	  To compile this driver as a module, choose M here: the
> >  	  module will be called usbtouchscreen.
> >  
> > +config TOUCHSCREEN_MX25
> > +	tristate "Freescale i.MX25 touchscreen input driver"
> > +	depends on MFD_MX25_TSADC
> > +	help
> > +	  Enable support for touchscreen connected to your i.MX25.
> > +
> 
> 	  To compile this driver as a module...
> 
> >  config TOUCHSCREEN_MC13783
> >  	tristate "Freescale MC13783 touchscreen input driver"
> >  	depends on MFD_MC13XXX
> > diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> > index cbaa6abb08da..77a2ac54101a 100644
> > --- a/drivers/input/touchscreen/Makefile
> > +++ b/drivers/input/touchscreen/Makefile
> > @@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
> >  obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
> >  obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
> >  obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
> > +obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
> >  obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
> >  obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
> >  obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
> > diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
> > new file mode 100644
> > index 000000000000..c833cd814972
> > --- /dev/null
> > +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
> > @@ -0,0 +1,600 @@
> > +/*
> > + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it under
> > + * the terms of the GNU General Public License version 2 as published by the
> > + * Free Software Foundation.
> > + *
> > + * Based on driver from 2011:
> > + *   Juergen Beisert, Pengutronix <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> > + *
> > + * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
> > + * connected to the imx25 ADC.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/device.h>
> > +#include <linux/input.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/mfd/imx25-tsadc.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +
> > +static const char mx25_tcq_name[] = "mx25-tcq";
> > +
> > +enum mx25_tcq_mode {
> > +	MX25_TS_4WIRE,
> > +};
> > +
> > +struct mx25_tcq_priv {
> > +	struct regmap *regs;
> > +	struct regmap *core_regs;
> > +	struct input_dev *idev;
> > +	enum mx25_tcq_mode mode;
> > +	unsigned int pen_threshold;
> > +	unsigned int sample_count;
> > +	unsigned int expected_samples;
> > +	unsigned int pen_debounce;
> > +	unsigned int settling_time;
> > +	struct clk *clk;
> > +	int irq;
> > +};
> > +
> > +static struct regmap_config mx25_tcq_regconfig = {
> > +	.fast_io = true,
> > +	.max_register = 0x5c,
> > +	.reg_bits = 32,
> > +	.val_bits = 32,
> > +	.reg_stride = 4,
> > +};
> > +
> > +static const struct of_device_id mx25_tcq_ids[] = {
> > +	{ .compatible = "fsl,imx25-tcq", },
> > +	{ /* Sentinel */ }
> > +};
> > +
> > +#define TSC_4WIRE_PRE_INDEX 0
> > +#define TSC_4WIRE_X_INDEX 1
> > +#define TSC_4WIRE_Y_INDEX 2
> > +#define TSC_4WIRE_POST_INDEX 3
> > +#define TSC_4WIRE_LEAVE 4
> > +
> > +#define MX25_TSC_DEF_THRESHOLD 80
> > +#define TSC_MAX_SAMPLES 16
> > +
> > +#define MX25_TSC_REPEAT_WAIT 14
> > +
> > +enum mx25_adc_configurations {
> > +	MX25_CFG_PRECHARGE = 0,
> > +	MX25_CFG_TOUCH_DETECT,
> > +	MX25_CFG_X_MEASUREMENT,
> > +	MX25_CFG_Y_MEASUREMENT,
> > +};
> > +
> > +#define MX25_PRECHARGE_VALUE (\
> > +			MX25_ADCQ_CFG_YPLL_OFF | \
> > +			MX25_ADCQ_CFG_XNUR_OFF | \
> > +			MX25_ADCQ_CFG_XPUL_HIGH | \
> > +			MX25_ADCQ_CFG_REFP_INT | \
> > +			MX25_ADCQ_CFG_IN_XP | \
> > +			MX25_ADCQ_CFG_REFN_NGND2 | \
> > +			MX25_ADCQ_CFG_IGS)
> > +
> > +#define MX25_TOUCH_DETECT_VALUE (\
> > +			MX25_ADCQ_CFG_YNLR | \
> > +			MX25_ADCQ_CFG_YPLL_OFF | \
> > +			MX25_ADCQ_CFG_XNUR_OFF | \
> > +			MX25_ADCQ_CFG_XPUL_OFF | \
> > +			MX25_ADCQ_CFG_REFP_INT | \
> > +			MX25_ADCQ_CFG_IN_XP | \
> > +			MX25_ADCQ_CFG_REFN_NGND2 | \
> > +			MX25_ADCQ_CFG_PENIACK)
> > +
> > +static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv,
> > +				   unsigned int settling_cnt)
> > +{
> > +	u32 precharge_cfg =
> > +			MX25_PRECHARGE_VALUE |
> > +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> > +	u32 touch_detect_cfg =
> > +			MX25_TOUCH_DETECT_VALUE |
> > +			MX25_ADCQ_CFG_NOS(1) |
> > +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> > +
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg);
> > +
> > +	/* PRECHARGE */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE),
> > +		     precharge_cfg);
> > +
> > +	/* TOUCH_DETECT */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT),
> > +		     touch_detect_cfg);
> > +
> > +	/* X Measurement */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT),
> > +		     MX25_ADCQ_CFG_YPLL_OFF |
> > +		     MX25_ADCQ_CFG_XNUR_LOW |
> > +		     MX25_ADCQ_CFG_XPUL_HIGH |
> > +		     MX25_ADCQ_CFG_REFP_XP |
> > +		     MX25_ADCQ_CFG_IN_YP |
> > +		     MX25_ADCQ_CFG_REFN_XN |
> > +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> > +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> > +
> > +	/* Y Measurement */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT),
> > +		     MX25_ADCQ_CFG_YNLR |
> > +		     MX25_ADCQ_CFG_YPLL_HIGH |
> > +		     MX25_ADCQ_CFG_XNUR_OFF |
> > +		     MX25_ADCQ_CFG_XPUL_OFF |
> > +		     MX25_ADCQ_CFG_REFP_YP |
> > +		     MX25_ADCQ_CFG_IN_XP |
> > +		     MX25_ADCQ_CFG_REFN_YN |
> > +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> > +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> > +
> > +	/* Enable the touch detection right now */
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg |
> > +		     MX25_ADCQ_CFG_IGS);
> > +}
> > +
> > +static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv,
> > +				   unsigned settling_cnt, int *items)
> > +{
> > +	imx25_setup_queue_cfgs(priv, settling_cnt);
> > +
> > +	/* Setup the conversion queue */
> > +	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
> > +		     MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) |
> > +		     MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) |
> > +		     MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) |
> > +		     MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) |
> > +		     MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) |
> > +		     MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT));
> > +
> > +	/*
> > +	 * We measure X/Y with 'sample_count' number of samples and execute a
> > +	 * touch detection twice, with 1 sample each
> > +	 */
> > +	priv->expected_samples = priv->sample_count * 2 + 2;
> > +	*items = 6;
> > +
> > +	return 0;
> > +}
> > +
> > +static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK,
> > +			   MX25_ADCQ_CR_PDMSK);
> > +}
> > +
> > +static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0);
> > +}
> > +
> > +static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ,
> > +			   MX25_ADCQ_MR_FDRY_IRQ);
> > +}
> > +
> > +static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0);
> > +}
> > +
> > +static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_FQS,
> > +			   MX25_ADCQ_CR_FQS);
> > +}
> > +
> > +static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_FQS, 0);
> > +}
> > +
> > +static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv)
> > +{
> > +	u32 tcqcr;
> > +
> > +	regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr);
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST,
> > +			   MX25_ADCQ_CR_FRST);
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0);
> > +	regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr);
> > +}
> > +
> > +static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv)
> > +{
> > +	/* stop the queue from looping */
> > +	mx25_tcq_force_queue_stop(priv);
> > +
> > +	/* for a clean touch detection, preload the X plane */
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE);
> > +
> > +	/* waste some time now to pre-load the X plate to high voltage */
> > +	mx25_tcq_fifo_reset(priv);
> > +
> > +	/* re-enable the detection right now */
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR,
> > +		     MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS);
> > +
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD,
> > +			   MX25_ADCQ_SR_PD);
> > +
> > +	/* enable the pen down event to be a source for the interrupt */
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0);
> > +
> > +	/* lets fire the next IRQ if someone touches the touchscreen */
> > +	mx25_tcq_enable_touch_irq(priv);
> > +}
> > +
> > +static int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,
> 
> Why not void? What callers are supposed to do with the value?

Good point.

> 
> > +					   u32 *sample_buf,
> > +					   unsigned int samples)
> > +{
> > +	unsigned int x_pos = 0;
> > +	unsigned int y_pos = 0;
> > +	unsigned int touch_pre = 0;
> > +	unsigned int touch_post = 0;
> > +	unsigned int i;
> > +	int ret = 0;
> > +
> > +	for (i = 0; i < samples; i++) {
> > +		unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]);
> > +		unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]);
> > +
> > +		switch (index) {
> > +		case 1:
> > +			touch_pre = val;
> > +			break;
> > +		case 2:
> > +			x_pos = val;
> > +			break;
> > +		case 3:
> > +			y_pos = val;
> > +			break;
> > +		case 5:
> > +			touch_post = val;
> > +			break;
> > +		default:
> > +			ret = -EINVAL;
> > +			break;
> > +		}
> > +	}
> > +
> > +	if (ret == 0 && samples != 0) {
> 
> Where do we set ret to anything other than 0?

In the default case above.
I replaced the return -EINVAL with a debug message. This case shouldn't happen
but just in case it does..

> 
> > +		/*
> > +		 * only if both touch measures are below a threshold,
> > +		 * the position is valid
> > +		 */
> > +		if (touch_pre < priv->pen_threshold &&
> > +		    touch_post < priv->pen_threshold) {
> > +			/* valid samples, generate a report */
> > +			x_pos /= priv->sample_count;
> > +			y_pos /= priv->sample_count;
> > +			input_report_abs(priv->idev, ABS_X, x_pos);
> > +			input_report_abs(priv->idev, ABS_Y, y_pos);
> > +			input_report_key(priv->idev, BTN_TOUCH, 1);
> > +			input_sync(priv->idev);
> > +
> > +			/* get next sample */
> > +			mx25_tcq_enable_fifo_irq(priv);
> > +		} else if (touch_pre >= priv->pen_threshold &&
> > +			   touch_post >= priv->pen_threshold) {
> > +			/*
> > +			 * if both samples are invalid,
> > +			 * generate a release report
> > +			 */
> > +			input_report_key(priv->idev, BTN_TOUCH, 0);
> > +			input_sync(priv->idev);
> > +			mx25_tcq_re_enable_touch_detection(priv);
> > +		} else {
> > +			/*
> > +			 * if only one of both touch measurements are
> > +			 * below the threshold, still some bouncing
> > +			 * happens. Take additional samples in this
> > +			 * case to be sure
> > +			 */
> > +			mx25_tcq_enable_fifo_irq(priv);
> > +		}
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
> > +{
> > +	struct mx25_tcq_priv *priv = dev_id;
> > +	u32 sample_buf[TSC_MAX_SAMPLES];
> > +	unsigned int samples = 0;
> > +
> > +	/* read all samples */
> > +	while (1) {
> > +		u32 stats;
> > +
> > +		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
> > +		if (stats & MX25_ADCQ_SR_EMPT)
> > +			break;
> > +
> > +		if (samples < TSC_MAX_SAMPLES) {
> > +			regmap_read(priv->regs, MX25_ADCQ_FIFO,
> > +				    &sample_buf[samples]);
> > +			++samples;
> > +		} else {
> > +			u32 discarded;
> > +			/* discard samples */
> > +			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);
> > +		}
> > +	}
> > +
> > +	mx25_tcq_create_event_for_4wire(priv, sample_buf, samples);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t mx25_tcq_irq(int irq, void *dev_id)
> > +{
> > +	struct mx25_tcq_priv *priv = dev_id;
> > +	u32 stat;
> > +	int ret = IRQ_HANDLED;
> > +
> > +	regmap_read(priv->regs, MX25_ADCQ_SR, &stat);
> 
> Is there any concern that these reads will fail?

This is always using mmio and we do not use the register clock feature. So it
shouldn't fail.

> 
> > +
> > +	if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR))
> > +		mx25_tcq_fifo_reset(priv);
> > +
> > +	if (stat & MX25_ADCQ_SR_PD) {
> > +		mx25_tcq_disable_touch_irq(priv);
> > +		mx25_tcq_force_queue_start(priv);
> > +		mx25_tcq_enable_fifo_irq(priv);
> > +	}
> > +
> > +	if (stat & MX25_ADCQ_SR_FDRY) {
> > +		mx25_tcq_disable_fifo_irq(priv);
> > +		ret = IRQ_WAKE_THREAD;
> > +	}
> > +
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
> > +			   MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
> > +			   MX25_ADCQ_SR_PD | MX25_ADCQ_SR_EOQ,
> > +			   MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR |
> > +			   MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
> > +			   MX25_ADCQ_SR_EOQ);
> > +
> > +	return ret;
> > +}
> > +
> > +/* configure the statemachine for a 4-wire touchscreen */
> > +static int mx25_tcq_init(struct mx25_tcq_priv *priv)
> > +{
> > +	u32 tgcr;
> > +	unsigned int ipg_div;
> > +	unsigned int adc_period;
> > +	unsigned int debounce_cnt;
> > +	unsigned int settling_cnt;
> > +	int itemct;
> > +	int ret;
> > +
> > +	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
> > +	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
> > +	adc_period = USEC_PER_SEC * ipg_div * 2 + 2;
> > +	adc_period /= clk_get_rate(priv->clk) / 1000 + 1;
> > +	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
> > +	settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1;
> > +
> > +	/* Reset */
> > +	regmap_write(priv->regs, MX25_ADCQ_CR,
> > +		     MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST);
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0);
> > +
> > +	/* up to 128 * 8 ADC clocks are possible */
> > +	if (debounce_cnt > 127)
> > +		debounce_cnt = 127;
> > +
> > +	/* up to 255 * 8 ADC clocks are possible */
> > +	if (settling_cnt > 255)
> > +		settling_cnt = 255;
> > +
> > +	ret = imx25_setup_queue_4wire(priv, settling_cnt, &itemct);
> > +	if (ret)
> > +		return ret;
> > +
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK,
> > +			   MX25_ADCQ_CR_LITEMID(itemct - 1) |
> > +			   MX25_ADCQ_CR_WMRK(priv->expected_samples - 1));
> > +
> > +	/* setup debounce count */
> > +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR,
> > +			   MX25_TGCR_PDBTIME_MASK,
> > +			   MX25_TGCR_PDBTIME(debounce_cnt));
> > +
> > +	/* enable debounce */
> > +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN,
> > +			   MX25_TGCR_PDBEN);
> > +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN,
> > +			   MX25_TGCR_PDEN);
> > +
> > +	/* enable the engine on demand */
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK,
> > +			   MX25_ADCQ_CR_QSM_FQS);
> > +
> > +	/* Enable repeat and repeat wait */
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK,
> > +			   MX25_ADCQ_CR_RPT |
> > +			   MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT));
> > +
> > +	mx25_tcq_re_enable_touch_detection(priv);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mx25_tcq_parse_dt(struct platform_device *pdev,
> > +			     struct mx25_tcq_priv *priv)
> > +{
> > +	struct device_node *np = pdev->dev.of_node;
> > +	u32 wires;
> > +	int ret;
> > +
> > +	/* Setup defaults */
> > +	priv->pen_threshold = 500;
> > +	priv->sample_count = 3;
> > +	priv->pen_debounce = 1000000;
> > +	priv->settling_time = 250000;
> > +
> > +	ret = of_property_read_u32(np, "fsl,wires", &wires);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
> > +		return ret;
> > +	}
> > +
> > +	if (wires == 4) {
> > +		priv->mode = MX25_TS_4WIRE;
> > +	} else {
> > +		dev_err(&pdev->dev, "%u-wire mode not supported\n", wires);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* These are optional, we don't care about the return values */
> > +	of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold);
> > +	of_property_read_u32(np, "fsl,settling-time", &priv->settling_time);
> > +	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mx25_tcq_open(struct input_dev *idev)
> > +{
> > +	struct device *dev = &idev->dev;
> > +	struct mx25_tcq_priv *priv = dev_get_drvdata(dev);
> > +	int ret;
> > +
> > +	ret = clk_prepare_enable(priv->clk);
> > +	if (ret) {
> > +		dev_err(dev, "Failed to enable ipg clock\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = mx25_tcq_init(priv);
> > +	if (ret) {
> > +		dev_err(dev, "Failed to init tcq\n");
> > +		clk_disable_unprepare(priv->clk);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void mx25_tcq_close(struct input_dev *idev)
> > +{
> > +	struct mx25_tcq_priv *priv = input_get_drvdata(idev);
> > +
> > +	mx25_tcq_force_queue_stop(priv);
> > +	mx25_tcq_disable_touch_irq(priv);
> > +	mx25_tcq_disable_fifo_irq(priv);
> > +	clk_disable_unprepare(priv->clk);
> > +}
> > +
> > +static int mx25_tcq_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct input_dev *idev;
> > +	struct mx25_tcq_priv *priv;
> > +	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
> > +	struct resource *res;
> > +	void __iomem *mem;
> > +	int ret;
> 
> Personal preference: can we call variables that hold error codes and not
> returned in success path (i.e. when we do explicit "return 0:' for
> success) be called "error"?

Yes that's fine for me, changed it for most functions in this driver.

> > +
> > +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > +	if (!priv)
> > +		return -ENOMEM;
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	mem = devm_ioremap_resource(dev, res);
> > +	if (!mem)
> 
> devm_ioremap_resource() returns ERR_PTR-encoded pointer, you should not
> test it for NULL bit rather for IS_ERR.

Fixed, thanks.

> 
> > +		return -ENOMEM;
> > +
> > +	ret = mx25_tcq_parse_dt(pdev, priv);
> > +	if (ret)
> > +		return ret;
> > +
> > +	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig);
> > +	if (IS_ERR(priv->regs)) {
> > +		dev_err(dev, "Failed to initialize regmap\n");
> > +		return PTR_ERR(priv->regs);
> > +	}
> > +
> > +	priv->irq = platform_get_irq(pdev, 0);
> > +	if (priv->irq <= 0) {
> > +		dev_err(dev, "Failed to get IRQ\n");
> > +		return priv->irq;
> > +	}
> > +
> > +	idev = devm_input_allocate_device(dev);
> > +	if (!idev) {
> > +		dev_err(dev, "Failed to allocate input device\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	idev->name = mx25_tcq_name;
> > +	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> > +	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
> 
> Replace the 2 lines above with:
> 
> 	input_set_capability(idev, EV_BTN_TOUCH);

I assume you meant
	input_set_capability(idev, EV_KEY, BTN_TOUCH);
and changed it to that.

> 
> > +	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
> > +	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
> > +
> > +	idev->id.bustype = BUS_HOST;
> > +	idev->open = mx25_tcq_open;
> > +	idev->close = mx25_tcq_close;
> > +
> > +	priv->idev = idev;
> > +	input_set_drvdata(idev, priv);
> > +
> > +	priv->core_regs = tsadc->regs;
> > +	if (!priv->core_regs)
> > +		return -EINVAL;
> > +
> > +	priv->clk = tsadc->clk;
> > +	if (!priv->clk)
> > +		return -EINVAL;
> > +
> > +	platform_set_drvdata(pdev, priv);
> > +
> > +	ret = input_register_device(idev);
> > +	if (ret) {
> > +		dev_err(dev, "Failed to register input device\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq,
> > +					mx25_tcq_irq_thread, IRQF_ONESHOT,
> > +					pdev->name, priv);
> > +	if (ret) {
> > +		dev_err(dev, "Failed requesting IRQ\n");
> > +		return ret;
> > +	}
> 
> Is it possible that we enable interrupt generation in the controller (in
> mx25_tcq_open) before we request IRQ and then (if someone touches the
> screen) we'll get a spurious IRQ? I'd rather we swapped
> devm_request_threaded_irq() and input_register_device(): it is perfectly
> safe to use (send events) to allocated but not registered input device.

Ok, if it is safe to send events for an allocated device I can't see why the
irq request shouldn't be first. I swapped these.

Thanks,

Markus

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
@ 2015-11-20 13:33           ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-20 13:33 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Shawn Guo, Jonathan Cameron, Lee Jones, Denis Carikli,
	Eric Bénard, Sascha Hauer, devicetree, linux-input,
	linux-iio, linux-arm-kernel, Hartmut Knaack, Fabio Estevam,
	Juergen Borleis

[-- Attachment #1: Type: text/plain, Size: 23864 bytes --]

Hi,

On Tuesday 17 November 2015 10:17:20 Dmitry Torokhov wrote:
> On Mon, Nov 16, 2015 at 01:01:07PM +0100, Markus Pargmann wrote:
> > This is a driver for the imx25 ADC/TSC module. It controls the
> > touchscreen conversion queue and creates a touchscreen input device.
> > The driver currently only supports 4 wire touchscreens. The driver uses
> > a simple conversion queue of precharge, touch detection, X measurement,
> > Y measurement, precharge and another touch detection.
> > 
> > This driver uses the regmap from the parent to setup some touch specific
> > settings in the core driver and setup a idle configuration with touch
> > detection.
> > 
> > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > Signed-off-by: Denis Carikli <denis@eukrea.com>
> > 
> > [fix clock's period calculation]
> > [fix calculation of the 'settling' value]
> > Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
> > ---
> > 
> > Notes:
> >     Changes in v7:
> >      - Moved clk_prepare_enable() and mx25_tcq_init() into mx25_tcq_open(). This
> >        was done to be able to use devm_request_threaded_irq().
> >      - Cleanup of the probe function through above change
> >      - Removed mx25_tcq_remove(), not necessary now
> > 
> >  drivers/input/touchscreen/Kconfig         |   6 +
> >  drivers/input/touchscreen/Makefile        |   1 +
> >  drivers/input/touchscreen/fsl-imx25-tcq.c | 600 ++++++++++++++++++++++++++++++
> >  3 files changed, 607 insertions(+)
> >  create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
> > 
> > diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> > index ae33da7ab51f..b44651d33080 100644
> > --- a/drivers/input/touchscreen/Kconfig
> > +++ b/drivers/input/touchscreen/Kconfig
> > @@ -811,6 +811,12 @@ config TOUCHSCREEN_USB_COMPOSITE
> >  	  To compile this driver as a module, choose M here: the
> >  	  module will be called usbtouchscreen.
> >  
> > +config TOUCHSCREEN_MX25
> > +	tristate "Freescale i.MX25 touchscreen input driver"
> > +	depends on MFD_MX25_TSADC
> > +	help
> > +	  Enable support for touchscreen connected to your i.MX25.
> > +
> 
> 	  To compile this driver as a module...
> 
> >  config TOUCHSCREEN_MC13783
> >  	tristate "Freescale MC13783 touchscreen input driver"
> >  	depends on MFD_MC13XXX
> > diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> > index cbaa6abb08da..77a2ac54101a 100644
> > --- a/drivers/input/touchscreen/Makefile
> > +++ b/drivers/input/touchscreen/Makefile
> > @@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
> >  obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
> >  obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
> >  obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
> > +obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
> >  obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
> >  obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
> >  obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
> > diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
> > new file mode 100644
> > index 000000000000..c833cd814972
> > --- /dev/null
> > +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
> > @@ -0,0 +1,600 @@
> > +/*
> > + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it under
> > + * the terms of the GNU General Public License version 2 as published by the
> > + * Free Software Foundation.
> > + *
> > + * Based on driver from 2011:
> > + *   Juergen Beisert, Pengutronix <kernel@pengutronix.de>
> > + *
> > + * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
> > + * connected to the imx25 ADC.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/device.h>
> > +#include <linux/input.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/mfd/imx25-tsadc.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +
> > +static const char mx25_tcq_name[] = "mx25-tcq";
> > +
> > +enum mx25_tcq_mode {
> > +	MX25_TS_4WIRE,
> > +};
> > +
> > +struct mx25_tcq_priv {
> > +	struct regmap *regs;
> > +	struct regmap *core_regs;
> > +	struct input_dev *idev;
> > +	enum mx25_tcq_mode mode;
> > +	unsigned int pen_threshold;
> > +	unsigned int sample_count;
> > +	unsigned int expected_samples;
> > +	unsigned int pen_debounce;
> > +	unsigned int settling_time;
> > +	struct clk *clk;
> > +	int irq;
> > +};
> > +
> > +static struct regmap_config mx25_tcq_regconfig = {
> > +	.fast_io = true,
> > +	.max_register = 0x5c,
> > +	.reg_bits = 32,
> > +	.val_bits = 32,
> > +	.reg_stride = 4,
> > +};
> > +
> > +static const struct of_device_id mx25_tcq_ids[] = {
> > +	{ .compatible = "fsl,imx25-tcq", },
> > +	{ /* Sentinel */ }
> > +};
> > +
> > +#define TSC_4WIRE_PRE_INDEX 0
> > +#define TSC_4WIRE_X_INDEX 1
> > +#define TSC_4WIRE_Y_INDEX 2
> > +#define TSC_4WIRE_POST_INDEX 3
> > +#define TSC_4WIRE_LEAVE 4
> > +
> > +#define MX25_TSC_DEF_THRESHOLD 80
> > +#define TSC_MAX_SAMPLES 16
> > +
> > +#define MX25_TSC_REPEAT_WAIT 14
> > +
> > +enum mx25_adc_configurations {
> > +	MX25_CFG_PRECHARGE = 0,
> > +	MX25_CFG_TOUCH_DETECT,
> > +	MX25_CFG_X_MEASUREMENT,
> > +	MX25_CFG_Y_MEASUREMENT,
> > +};
> > +
> > +#define MX25_PRECHARGE_VALUE (\
> > +			MX25_ADCQ_CFG_YPLL_OFF | \
> > +			MX25_ADCQ_CFG_XNUR_OFF | \
> > +			MX25_ADCQ_CFG_XPUL_HIGH | \
> > +			MX25_ADCQ_CFG_REFP_INT | \
> > +			MX25_ADCQ_CFG_IN_XP | \
> > +			MX25_ADCQ_CFG_REFN_NGND2 | \
> > +			MX25_ADCQ_CFG_IGS)
> > +
> > +#define MX25_TOUCH_DETECT_VALUE (\
> > +			MX25_ADCQ_CFG_YNLR | \
> > +			MX25_ADCQ_CFG_YPLL_OFF | \
> > +			MX25_ADCQ_CFG_XNUR_OFF | \
> > +			MX25_ADCQ_CFG_XPUL_OFF | \
> > +			MX25_ADCQ_CFG_REFP_INT | \
> > +			MX25_ADCQ_CFG_IN_XP | \
> > +			MX25_ADCQ_CFG_REFN_NGND2 | \
> > +			MX25_ADCQ_CFG_PENIACK)
> > +
> > +static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv,
> > +				   unsigned int settling_cnt)
> > +{
> > +	u32 precharge_cfg =
> > +			MX25_PRECHARGE_VALUE |
> > +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> > +	u32 touch_detect_cfg =
> > +			MX25_TOUCH_DETECT_VALUE |
> > +			MX25_ADCQ_CFG_NOS(1) |
> > +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> > +
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg);
> > +
> > +	/* PRECHARGE */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE),
> > +		     precharge_cfg);
> > +
> > +	/* TOUCH_DETECT */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT),
> > +		     touch_detect_cfg);
> > +
> > +	/* X Measurement */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT),
> > +		     MX25_ADCQ_CFG_YPLL_OFF |
> > +		     MX25_ADCQ_CFG_XNUR_LOW |
> > +		     MX25_ADCQ_CFG_XPUL_HIGH |
> > +		     MX25_ADCQ_CFG_REFP_XP |
> > +		     MX25_ADCQ_CFG_IN_YP |
> > +		     MX25_ADCQ_CFG_REFN_XN |
> > +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> > +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> > +
> > +	/* Y Measurement */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT),
> > +		     MX25_ADCQ_CFG_YNLR |
> > +		     MX25_ADCQ_CFG_YPLL_HIGH |
> > +		     MX25_ADCQ_CFG_XNUR_OFF |
> > +		     MX25_ADCQ_CFG_XPUL_OFF |
> > +		     MX25_ADCQ_CFG_REFP_YP |
> > +		     MX25_ADCQ_CFG_IN_XP |
> > +		     MX25_ADCQ_CFG_REFN_YN |
> > +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> > +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> > +
> > +	/* Enable the touch detection right now */
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg |
> > +		     MX25_ADCQ_CFG_IGS);
> > +}
> > +
> > +static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv,
> > +				   unsigned settling_cnt, int *items)
> > +{
> > +	imx25_setup_queue_cfgs(priv, settling_cnt);
> > +
> > +	/* Setup the conversion queue */
> > +	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
> > +		     MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) |
> > +		     MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) |
> > +		     MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) |
> > +		     MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) |
> > +		     MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) |
> > +		     MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT));
> > +
> > +	/*
> > +	 * We measure X/Y with 'sample_count' number of samples and execute a
> > +	 * touch detection twice, with 1 sample each
> > +	 */
> > +	priv->expected_samples = priv->sample_count * 2 + 2;
> > +	*items = 6;
> > +
> > +	return 0;
> > +}
> > +
> > +static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK,
> > +			   MX25_ADCQ_CR_PDMSK);
> > +}
> > +
> > +static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0);
> > +}
> > +
> > +static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ,
> > +			   MX25_ADCQ_MR_FDRY_IRQ);
> > +}
> > +
> > +static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0);
> > +}
> > +
> > +static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_FQS,
> > +			   MX25_ADCQ_CR_FQS);
> > +}
> > +
> > +static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_FQS, 0);
> > +}
> > +
> > +static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv)
> > +{
> > +	u32 tcqcr;
> > +
> > +	regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr);
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST,
> > +			   MX25_ADCQ_CR_FRST);
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0);
> > +	regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr);
> > +}
> > +
> > +static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv)
> > +{
> > +	/* stop the queue from looping */
> > +	mx25_tcq_force_queue_stop(priv);
> > +
> > +	/* for a clean touch detection, preload the X plane */
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE);
> > +
> > +	/* waste some time now to pre-load the X plate to high voltage */
> > +	mx25_tcq_fifo_reset(priv);
> > +
> > +	/* re-enable the detection right now */
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR,
> > +		     MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS);
> > +
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD,
> > +			   MX25_ADCQ_SR_PD);
> > +
> > +	/* enable the pen down event to be a source for the interrupt */
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0);
> > +
> > +	/* lets fire the next IRQ if someone touches the touchscreen */
> > +	mx25_tcq_enable_touch_irq(priv);
> > +}
> > +
> > +static int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,
> 
> Why not void? What callers are supposed to do with the value?

Good point.

> 
> > +					   u32 *sample_buf,
> > +					   unsigned int samples)
> > +{
> > +	unsigned int x_pos = 0;
> > +	unsigned int y_pos = 0;
> > +	unsigned int touch_pre = 0;
> > +	unsigned int touch_post = 0;
> > +	unsigned int i;
> > +	int ret = 0;
> > +
> > +	for (i = 0; i < samples; i++) {
> > +		unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]);
> > +		unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]);
> > +
> > +		switch (index) {
> > +		case 1:
> > +			touch_pre = val;
> > +			break;
> > +		case 2:
> > +			x_pos = val;
> > +			break;
> > +		case 3:
> > +			y_pos = val;
> > +			break;
> > +		case 5:
> > +			touch_post = val;
> > +			break;
> > +		default:
> > +			ret = -EINVAL;
> > +			break;
> > +		}
> > +	}
> > +
> > +	if (ret == 0 && samples != 0) {
> 
> Where do we set ret to anything other than 0?

In the default case above.
I replaced the return -EINVAL with a debug message. This case shouldn't happen
but just in case it does..

> 
> > +		/*
> > +		 * only if both touch measures are below a threshold,
> > +		 * the position is valid
> > +		 */
> > +		if (touch_pre < priv->pen_threshold &&
> > +		    touch_post < priv->pen_threshold) {
> > +			/* valid samples, generate a report */
> > +			x_pos /= priv->sample_count;
> > +			y_pos /= priv->sample_count;
> > +			input_report_abs(priv->idev, ABS_X, x_pos);
> > +			input_report_abs(priv->idev, ABS_Y, y_pos);
> > +			input_report_key(priv->idev, BTN_TOUCH, 1);
> > +			input_sync(priv->idev);
> > +
> > +			/* get next sample */
> > +			mx25_tcq_enable_fifo_irq(priv);
> > +		} else if (touch_pre >= priv->pen_threshold &&
> > +			   touch_post >= priv->pen_threshold) {
> > +			/*
> > +			 * if both samples are invalid,
> > +			 * generate a release report
> > +			 */
> > +			input_report_key(priv->idev, BTN_TOUCH, 0);
> > +			input_sync(priv->idev);
> > +			mx25_tcq_re_enable_touch_detection(priv);
> > +		} else {
> > +			/*
> > +			 * if only one of both touch measurements are
> > +			 * below the threshold, still some bouncing
> > +			 * happens. Take additional samples in this
> > +			 * case to be sure
> > +			 */
> > +			mx25_tcq_enable_fifo_irq(priv);
> > +		}
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
> > +{
> > +	struct mx25_tcq_priv *priv = dev_id;
> > +	u32 sample_buf[TSC_MAX_SAMPLES];
> > +	unsigned int samples = 0;
> > +
> > +	/* read all samples */
> > +	while (1) {
> > +		u32 stats;
> > +
> > +		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
> > +		if (stats & MX25_ADCQ_SR_EMPT)
> > +			break;
> > +
> > +		if (samples < TSC_MAX_SAMPLES) {
> > +			regmap_read(priv->regs, MX25_ADCQ_FIFO,
> > +				    &sample_buf[samples]);
> > +			++samples;
> > +		} else {
> > +			u32 discarded;
> > +			/* discard samples */
> > +			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);
> > +		}
> > +	}
> > +
> > +	mx25_tcq_create_event_for_4wire(priv, sample_buf, samples);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t mx25_tcq_irq(int irq, void *dev_id)
> > +{
> > +	struct mx25_tcq_priv *priv = dev_id;
> > +	u32 stat;
> > +	int ret = IRQ_HANDLED;
> > +
> > +	regmap_read(priv->regs, MX25_ADCQ_SR, &stat);
> 
> Is there any concern that these reads will fail?

This is always using mmio and we do not use the register clock feature. So it
shouldn't fail.

> 
> > +
> > +	if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR))
> > +		mx25_tcq_fifo_reset(priv);
> > +
> > +	if (stat & MX25_ADCQ_SR_PD) {
> > +		mx25_tcq_disable_touch_irq(priv);
> > +		mx25_tcq_force_queue_start(priv);
> > +		mx25_tcq_enable_fifo_irq(priv);
> > +	}
> > +
> > +	if (stat & MX25_ADCQ_SR_FDRY) {
> > +		mx25_tcq_disable_fifo_irq(priv);
> > +		ret = IRQ_WAKE_THREAD;
> > +	}
> > +
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
> > +			   MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
> > +			   MX25_ADCQ_SR_PD | MX25_ADCQ_SR_EOQ,
> > +			   MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR |
> > +			   MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
> > +			   MX25_ADCQ_SR_EOQ);
> > +
> > +	return ret;
> > +}
> > +
> > +/* configure the statemachine for a 4-wire touchscreen */
> > +static int mx25_tcq_init(struct mx25_tcq_priv *priv)
> > +{
> > +	u32 tgcr;
> > +	unsigned int ipg_div;
> > +	unsigned int adc_period;
> > +	unsigned int debounce_cnt;
> > +	unsigned int settling_cnt;
> > +	int itemct;
> > +	int ret;
> > +
> > +	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
> > +	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
> > +	adc_period = USEC_PER_SEC * ipg_div * 2 + 2;
> > +	adc_period /= clk_get_rate(priv->clk) / 1000 + 1;
> > +	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
> > +	settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1;
> > +
> > +	/* Reset */
> > +	regmap_write(priv->regs, MX25_ADCQ_CR,
> > +		     MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST);
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0);
> > +
> > +	/* up to 128 * 8 ADC clocks are possible */
> > +	if (debounce_cnt > 127)
> > +		debounce_cnt = 127;
> > +
> > +	/* up to 255 * 8 ADC clocks are possible */
> > +	if (settling_cnt > 255)
> > +		settling_cnt = 255;
> > +
> > +	ret = imx25_setup_queue_4wire(priv, settling_cnt, &itemct);
> > +	if (ret)
> > +		return ret;
> > +
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK,
> > +			   MX25_ADCQ_CR_LITEMID(itemct - 1) |
> > +			   MX25_ADCQ_CR_WMRK(priv->expected_samples - 1));
> > +
> > +	/* setup debounce count */
> > +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR,
> > +			   MX25_TGCR_PDBTIME_MASK,
> > +			   MX25_TGCR_PDBTIME(debounce_cnt));
> > +
> > +	/* enable debounce */
> > +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN,
> > +			   MX25_TGCR_PDBEN);
> > +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN,
> > +			   MX25_TGCR_PDEN);
> > +
> > +	/* enable the engine on demand */
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK,
> > +			   MX25_ADCQ_CR_QSM_FQS);
> > +
> > +	/* Enable repeat and repeat wait */
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK,
> > +			   MX25_ADCQ_CR_RPT |
> > +			   MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT));
> > +
> > +	mx25_tcq_re_enable_touch_detection(priv);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mx25_tcq_parse_dt(struct platform_device *pdev,
> > +			     struct mx25_tcq_priv *priv)
> > +{
> > +	struct device_node *np = pdev->dev.of_node;
> > +	u32 wires;
> > +	int ret;
> > +
> > +	/* Setup defaults */
> > +	priv->pen_threshold = 500;
> > +	priv->sample_count = 3;
> > +	priv->pen_debounce = 1000000;
> > +	priv->settling_time = 250000;
> > +
> > +	ret = of_property_read_u32(np, "fsl,wires", &wires);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
> > +		return ret;
> > +	}
> > +
> > +	if (wires == 4) {
> > +		priv->mode = MX25_TS_4WIRE;
> > +	} else {
> > +		dev_err(&pdev->dev, "%u-wire mode not supported\n", wires);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* These are optional, we don't care about the return values */
> > +	of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold);
> > +	of_property_read_u32(np, "fsl,settling-time", &priv->settling_time);
> > +	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mx25_tcq_open(struct input_dev *idev)
> > +{
> > +	struct device *dev = &idev->dev;
> > +	struct mx25_tcq_priv *priv = dev_get_drvdata(dev);
> > +	int ret;
> > +
> > +	ret = clk_prepare_enable(priv->clk);
> > +	if (ret) {
> > +		dev_err(dev, "Failed to enable ipg clock\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = mx25_tcq_init(priv);
> > +	if (ret) {
> > +		dev_err(dev, "Failed to init tcq\n");
> > +		clk_disable_unprepare(priv->clk);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void mx25_tcq_close(struct input_dev *idev)
> > +{
> > +	struct mx25_tcq_priv *priv = input_get_drvdata(idev);
> > +
> > +	mx25_tcq_force_queue_stop(priv);
> > +	mx25_tcq_disable_touch_irq(priv);
> > +	mx25_tcq_disable_fifo_irq(priv);
> > +	clk_disable_unprepare(priv->clk);
> > +}
> > +
> > +static int mx25_tcq_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct input_dev *idev;
> > +	struct mx25_tcq_priv *priv;
> > +	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
> > +	struct resource *res;
> > +	void __iomem *mem;
> > +	int ret;
> 
> Personal preference: can we call variables that hold error codes and not
> returned in success path (i.e. when we do explicit "return 0:' for
> success) be called "error"?

Yes that's fine for me, changed it for most functions in this driver.

> > +
> > +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > +	if (!priv)
> > +		return -ENOMEM;
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	mem = devm_ioremap_resource(dev, res);
> > +	if (!mem)
> 
> devm_ioremap_resource() returns ERR_PTR-encoded pointer, you should not
> test it for NULL bit rather for IS_ERR.

Fixed, thanks.

> 
> > +		return -ENOMEM;
> > +
> > +	ret = mx25_tcq_parse_dt(pdev, priv);
> > +	if (ret)
> > +		return ret;
> > +
> > +	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig);
> > +	if (IS_ERR(priv->regs)) {
> > +		dev_err(dev, "Failed to initialize regmap\n");
> > +		return PTR_ERR(priv->regs);
> > +	}
> > +
> > +	priv->irq = platform_get_irq(pdev, 0);
> > +	if (priv->irq <= 0) {
> > +		dev_err(dev, "Failed to get IRQ\n");
> > +		return priv->irq;
> > +	}
> > +
> > +	idev = devm_input_allocate_device(dev);
> > +	if (!idev) {
> > +		dev_err(dev, "Failed to allocate input device\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	idev->name = mx25_tcq_name;
> > +	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> > +	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
> 
> Replace the 2 lines above with:
> 
> 	input_set_capability(idev, EV_BTN_TOUCH);

I assume you meant
	input_set_capability(idev, EV_KEY, BTN_TOUCH);
and changed it to that.

> 
> > +	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
> > +	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
> > +
> > +	idev->id.bustype = BUS_HOST;
> > +	idev->open = mx25_tcq_open;
> > +	idev->close = mx25_tcq_close;
> > +
> > +	priv->idev = idev;
> > +	input_set_drvdata(idev, priv);
> > +
> > +	priv->core_regs = tsadc->regs;
> > +	if (!priv->core_regs)
> > +		return -EINVAL;
> > +
> > +	priv->clk = tsadc->clk;
> > +	if (!priv->clk)
> > +		return -EINVAL;
> > +
> > +	platform_set_drvdata(pdev, priv);
> > +
> > +	ret = input_register_device(idev);
> > +	if (ret) {
> > +		dev_err(dev, "Failed to register input device\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq,
> > +					mx25_tcq_irq_thread, IRQF_ONESHOT,
> > +					pdev->name, priv);
> > +	if (ret) {
> > +		dev_err(dev, "Failed requesting IRQ\n");
> > +		return ret;
> > +	}
> 
> Is it possible that we enable interrupt generation in the controller (in
> mx25_tcq_open) before we request IRQ and then (if someone touches the
> screen) we'll get a spurious IRQ? I'd rather we swapped
> devm_request_threaded_irq() and input_register_device(): it is perfectly
> safe to use (send events) to allocated but not registered input device.

Ok, if it is safe to send events for an allocated device I can't see why the
irq request shouldn't be first. I swapped these.

Thanks,

Markus

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
@ 2015-11-20 13:33           ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-20 13:33 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Tuesday 17 November 2015 10:17:20 Dmitry Torokhov wrote:
> On Mon, Nov 16, 2015 at 01:01:07PM +0100, Markus Pargmann wrote:
> > This is a driver for the imx25 ADC/TSC module. It controls the
> > touchscreen conversion queue and creates a touchscreen input device.
> > The driver currently only supports 4 wire touchscreens. The driver uses
> > a simple conversion queue of precharge, touch detection, X measurement,
> > Y measurement, precharge and another touch detection.
> > 
> > This driver uses the regmap from the parent to setup some touch specific
> > settings in the core driver and setup a idle configuration with touch
> > detection.
> > 
> > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > Signed-off-by: Denis Carikli <denis@eukrea.com>
> > 
> > [fix clock's period calculation]
> > [fix calculation of the 'settling' value]
> > Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
> > ---
> > 
> > Notes:
> >     Changes in v7:
> >      - Moved clk_prepare_enable() and mx25_tcq_init() into mx25_tcq_open(). This
> >        was done to be able to use devm_request_threaded_irq().
> >      - Cleanup of the probe function through above change
> >      - Removed mx25_tcq_remove(), not necessary now
> > 
> >  drivers/input/touchscreen/Kconfig         |   6 +
> >  drivers/input/touchscreen/Makefile        |   1 +
> >  drivers/input/touchscreen/fsl-imx25-tcq.c | 600 ++++++++++++++++++++++++++++++
> >  3 files changed, 607 insertions(+)
> >  create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
> > 
> > diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> > index ae33da7ab51f..b44651d33080 100644
> > --- a/drivers/input/touchscreen/Kconfig
> > +++ b/drivers/input/touchscreen/Kconfig
> > @@ -811,6 +811,12 @@ config TOUCHSCREEN_USB_COMPOSITE
> >  	  To compile this driver as a module, choose M here: the
> >  	  module will be called usbtouchscreen.
> >  
> > +config TOUCHSCREEN_MX25
> > +	tristate "Freescale i.MX25 touchscreen input driver"
> > +	depends on MFD_MX25_TSADC
> > +	help
> > +	  Enable support for touchscreen connected to your i.MX25.
> > +
> 
> 	  To compile this driver as a module...
> 
> >  config TOUCHSCREEN_MC13783
> >  	tristate "Freescale MC13783 touchscreen input driver"
> >  	depends on MFD_MC13XXX
> > diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> > index cbaa6abb08da..77a2ac54101a 100644
> > --- a/drivers/input/touchscreen/Makefile
> > +++ b/drivers/input/touchscreen/Makefile
> > @@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
> >  obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
> >  obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
> >  obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
> > +obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
> >  obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
> >  obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
> >  obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
> > diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
> > new file mode 100644
> > index 000000000000..c833cd814972
> > --- /dev/null
> > +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
> > @@ -0,0 +1,600 @@
> > +/*
> > + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it under
> > + * the terms of the GNU General Public License version 2 as published by the
> > + * Free Software Foundation.
> > + *
> > + * Based on driver from 2011:
> > + *   Juergen Beisert, Pengutronix <kernel@pengutronix.de>
> > + *
> > + * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
> > + * connected to the imx25 ADC.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/device.h>
> > +#include <linux/input.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/mfd/imx25-tsadc.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +
> > +static const char mx25_tcq_name[] = "mx25-tcq";
> > +
> > +enum mx25_tcq_mode {
> > +	MX25_TS_4WIRE,
> > +};
> > +
> > +struct mx25_tcq_priv {
> > +	struct regmap *regs;
> > +	struct regmap *core_regs;
> > +	struct input_dev *idev;
> > +	enum mx25_tcq_mode mode;
> > +	unsigned int pen_threshold;
> > +	unsigned int sample_count;
> > +	unsigned int expected_samples;
> > +	unsigned int pen_debounce;
> > +	unsigned int settling_time;
> > +	struct clk *clk;
> > +	int irq;
> > +};
> > +
> > +static struct regmap_config mx25_tcq_regconfig = {
> > +	.fast_io = true,
> > +	.max_register = 0x5c,
> > +	.reg_bits = 32,
> > +	.val_bits = 32,
> > +	.reg_stride = 4,
> > +};
> > +
> > +static const struct of_device_id mx25_tcq_ids[] = {
> > +	{ .compatible = "fsl,imx25-tcq", },
> > +	{ /* Sentinel */ }
> > +};
> > +
> > +#define TSC_4WIRE_PRE_INDEX 0
> > +#define TSC_4WIRE_X_INDEX 1
> > +#define TSC_4WIRE_Y_INDEX 2
> > +#define TSC_4WIRE_POST_INDEX 3
> > +#define TSC_4WIRE_LEAVE 4
> > +
> > +#define MX25_TSC_DEF_THRESHOLD 80
> > +#define TSC_MAX_SAMPLES 16
> > +
> > +#define MX25_TSC_REPEAT_WAIT 14
> > +
> > +enum mx25_adc_configurations {
> > +	MX25_CFG_PRECHARGE = 0,
> > +	MX25_CFG_TOUCH_DETECT,
> > +	MX25_CFG_X_MEASUREMENT,
> > +	MX25_CFG_Y_MEASUREMENT,
> > +};
> > +
> > +#define MX25_PRECHARGE_VALUE (\
> > +			MX25_ADCQ_CFG_YPLL_OFF | \
> > +			MX25_ADCQ_CFG_XNUR_OFF | \
> > +			MX25_ADCQ_CFG_XPUL_HIGH | \
> > +			MX25_ADCQ_CFG_REFP_INT | \
> > +			MX25_ADCQ_CFG_IN_XP | \
> > +			MX25_ADCQ_CFG_REFN_NGND2 | \
> > +			MX25_ADCQ_CFG_IGS)
> > +
> > +#define MX25_TOUCH_DETECT_VALUE (\
> > +			MX25_ADCQ_CFG_YNLR | \
> > +			MX25_ADCQ_CFG_YPLL_OFF | \
> > +			MX25_ADCQ_CFG_XNUR_OFF | \
> > +			MX25_ADCQ_CFG_XPUL_OFF | \
> > +			MX25_ADCQ_CFG_REFP_INT | \
> > +			MX25_ADCQ_CFG_IN_XP | \
> > +			MX25_ADCQ_CFG_REFN_NGND2 | \
> > +			MX25_ADCQ_CFG_PENIACK)
> > +
> > +static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv,
> > +				   unsigned int settling_cnt)
> > +{
> > +	u32 precharge_cfg =
> > +			MX25_PRECHARGE_VALUE |
> > +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> > +	u32 touch_detect_cfg =
> > +			MX25_TOUCH_DETECT_VALUE |
> > +			MX25_ADCQ_CFG_NOS(1) |
> > +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> > +
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg);
> > +
> > +	/* PRECHARGE */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE),
> > +		     precharge_cfg);
> > +
> > +	/* TOUCH_DETECT */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT),
> > +		     touch_detect_cfg);
> > +
> > +	/* X Measurement */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT),
> > +		     MX25_ADCQ_CFG_YPLL_OFF |
> > +		     MX25_ADCQ_CFG_XNUR_LOW |
> > +		     MX25_ADCQ_CFG_XPUL_HIGH |
> > +		     MX25_ADCQ_CFG_REFP_XP |
> > +		     MX25_ADCQ_CFG_IN_YP |
> > +		     MX25_ADCQ_CFG_REFN_XN |
> > +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> > +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> > +
> > +	/* Y Measurement */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT),
> > +		     MX25_ADCQ_CFG_YNLR |
> > +		     MX25_ADCQ_CFG_YPLL_HIGH |
> > +		     MX25_ADCQ_CFG_XNUR_OFF |
> > +		     MX25_ADCQ_CFG_XPUL_OFF |
> > +		     MX25_ADCQ_CFG_REFP_YP |
> > +		     MX25_ADCQ_CFG_IN_XP |
> > +		     MX25_ADCQ_CFG_REFN_YN |
> > +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> > +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> > +
> > +	/* Enable the touch detection right now */
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg |
> > +		     MX25_ADCQ_CFG_IGS);
> > +}
> > +
> > +static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv,
> > +				   unsigned settling_cnt, int *items)
> > +{
> > +	imx25_setup_queue_cfgs(priv, settling_cnt);
> > +
> > +	/* Setup the conversion queue */
> > +	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
> > +		     MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) |
> > +		     MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) |
> > +		     MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) |
> > +		     MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) |
> > +		     MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) |
> > +		     MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT));
> > +
> > +	/*
> > +	 * We measure X/Y with 'sample_count' number of samples and execute a
> > +	 * touch detection twice, with 1 sample each
> > +	 */
> > +	priv->expected_samples = priv->sample_count * 2 + 2;
> > +	*items = 6;
> > +
> > +	return 0;
> > +}
> > +
> > +static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK,
> > +			   MX25_ADCQ_CR_PDMSK);
> > +}
> > +
> > +static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0);
> > +}
> > +
> > +static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ,
> > +			   MX25_ADCQ_MR_FDRY_IRQ);
> > +}
> > +
> > +static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0);
> > +}
> > +
> > +static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_FQS,
> > +			   MX25_ADCQ_CR_FQS);
> > +}
> > +
> > +static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_FQS, 0);
> > +}
> > +
> > +static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv)
> > +{
> > +	u32 tcqcr;
> > +
> > +	regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr);
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST,
> > +			   MX25_ADCQ_CR_FRST);
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0);
> > +	regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr);
> > +}
> > +
> > +static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv)
> > +{
> > +	/* stop the queue from looping */
> > +	mx25_tcq_force_queue_stop(priv);
> > +
> > +	/* for a clean touch detection, preload the X plane */
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE);
> > +
> > +	/* waste some time now to pre-load the X plate to high voltage */
> > +	mx25_tcq_fifo_reset(priv);
> > +
> > +	/* re-enable the detection right now */
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR,
> > +		     MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS);
> > +
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD,
> > +			   MX25_ADCQ_SR_PD);
> > +
> > +	/* enable the pen down event to be a source for the interrupt */
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0);
> > +
> > +	/* lets fire the next IRQ if someone touches the touchscreen */
> > +	mx25_tcq_enable_touch_irq(priv);
> > +}
> > +
> > +static int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,
> 
> Why not void? What callers are supposed to do with the value?

Good point.

> 
> > +					   u32 *sample_buf,
> > +					   unsigned int samples)
> > +{
> > +	unsigned int x_pos = 0;
> > +	unsigned int y_pos = 0;
> > +	unsigned int touch_pre = 0;
> > +	unsigned int touch_post = 0;
> > +	unsigned int i;
> > +	int ret = 0;
> > +
> > +	for (i = 0; i < samples; i++) {
> > +		unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]);
> > +		unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]);
> > +
> > +		switch (index) {
> > +		case 1:
> > +			touch_pre = val;
> > +			break;
> > +		case 2:
> > +			x_pos = val;
> > +			break;
> > +		case 3:
> > +			y_pos = val;
> > +			break;
> > +		case 5:
> > +			touch_post = val;
> > +			break;
> > +		default:
> > +			ret = -EINVAL;
> > +			break;
> > +		}
> > +	}
> > +
> > +	if (ret == 0 && samples != 0) {
> 
> Where do we set ret to anything other than 0?

In the default case above.
I replaced the return -EINVAL with a debug message. This case shouldn't happen
but just in case it does..

> 
> > +		/*
> > +		 * only if both touch measures are below a threshold,
> > +		 * the position is valid
> > +		 */
> > +		if (touch_pre < priv->pen_threshold &&
> > +		    touch_post < priv->pen_threshold) {
> > +			/* valid samples, generate a report */
> > +			x_pos /= priv->sample_count;
> > +			y_pos /= priv->sample_count;
> > +			input_report_abs(priv->idev, ABS_X, x_pos);
> > +			input_report_abs(priv->idev, ABS_Y, y_pos);
> > +			input_report_key(priv->idev, BTN_TOUCH, 1);
> > +			input_sync(priv->idev);
> > +
> > +			/* get next sample */
> > +			mx25_tcq_enable_fifo_irq(priv);
> > +		} else if (touch_pre >= priv->pen_threshold &&
> > +			   touch_post >= priv->pen_threshold) {
> > +			/*
> > +			 * if both samples are invalid,
> > +			 * generate a release report
> > +			 */
> > +			input_report_key(priv->idev, BTN_TOUCH, 0);
> > +			input_sync(priv->idev);
> > +			mx25_tcq_re_enable_touch_detection(priv);
> > +		} else {
> > +			/*
> > +			 * if only one of both touch measurements are
> > +			 * below the threshold, still some bouncing
> > +			 * happens. Take additional samples in this
> > +			 * case to be sure
> > +			 */
> > +			mx25_tcq_enable_fifo_irq(priv);
> > +		}
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
> > +{
> > +	struct mx25_tcq_priv *priv = dev_id;
> > +	u32 sample_buf[TSC_MAX_SAMPLES];
> > +	unsigned int samples = 0;
> > +
> > +	/* read all samples */
> > +	while (1) {
> > +		u32 stats;
> > +
> > +		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
> > +		if (stats & MX25_ADCQ_SR_EMPT)
> > +			break;
> > +
> > +		if (samples < TSC_MAX_SAMPLES) {
> > +			regmap_read(priv->regs, MX25_ADCQ_FIFO,
> > +				    &sample_buf[samples]);
> > +			++samples;
> > +		} else {
> > +			u32 discarded;
> > +			/* discard samples */
> > +			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);
> > +		}
> > +	}
> > +
> > +	mx25_tcq_create_event_for_4wire(priv, sample_buf, samples);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t mx25_tcq_irq(int irq, void *dev_id)
> > +{
> > +	struct mx25_tcq_priv *priv = dev_id;
> > +	u32 stat;
> > +	int ret = IRQ_HANDLED;
> > +
> > +	regmap_read(priv->regs, MX25_ADCQ_SR, &stat);
> 
> Is there any concern that these reads will fail?

This is always using mmio and we do not use the register clock feature. So it
shouldn't fail.

> 
> > +
> > +	if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR))
> > +		mx25_tcq_fifo_reset(priv);
> > +
> > +	if (stat & MX25_ADCQ_SR_PD) {
> > +		mx25_tcq_disable_touch_irq(priv);
> > +		mx25_tcq_force_queue_start(priv);
> > +		mx25_tcq_enable_fifo_irq(priv);
> > +	}
> > +
> > +	if (stat & MX25_ADCQ_SR_FDRY) {
> > +		mx25_tcq_disable_fifo_irq(priv);
> > +		ret = IRQ_WAKE_THREAD;
> > +	}
> > +
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
> > +			   MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
> > +			   MX25_ADCQ_SR_PD | MX25_ADCQ_SR_EOQ,
> > +			   MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR |
> > +			   MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
> > +			   MX25_ADCQ_SR_EOQ);
> > +
> > +	return ret;
> > +}
> > +
> > +/* configure the statemachine for a 4-wire touchscreen */
> > +static int mx25_tcq_init(struct mx25_tcq_priv *priv)
> > +{
> > +	u32 tgcr;
> > +	unsigned int ipg_div;
> > +	unsigned int adc_period;
> > +	unsigned int debounce_cnt;
> > +	unsigned int settling_cnt;
> > +	int itemct;
> > +	int ret;
> > +
> > +	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
> > +	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
> > +	adc_period = USEC_PER_SEC * ipg_div * 2 + 2;
> > +	adc_period /= clk_get_rate(priv->clk) / 1000 + 1;
> > +	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
> > +	settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1;
> > +
> > +	/* Reset */
> > +	regmap_write(priv->regs, MX25_ADCQ_CR,
> > +		     MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST);
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0);
> > +
> > +	/* up to 128 * 8 ADC clocks are possible */
> > +	if (debounce_cnt > 127)
> > +		debounce_cnt = 127;
> > +
> > +	/* up to 255 * 8 ADC clocks are possible */
> > +	if (settling_cnt > 255)
> > +		settling_cnt = 255;
> > +
> > +	ret = imx25_setup_queue_4wire(priv, settling_cnt, &itemct);
> > +	if (ret)
> > +		return ret;
> > +
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK,
> > +			   MX25_ADCQ_CR_LITEMID(itemct - 1) |
> > +			   MX25_ADCQ_CR_WMRK(priv->expected_samples - 1));
> > +
> > +	/* setup debounce count */
> > +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR,
> > +			   MX25_TGCR_PDBTIME_MASK,
> > +			   MX25_TGCR_PDBTIME(debounce_cnt));
> > +
> > +	/* enable debounce */
> > +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN,
> > +			   MX25_TGCR_PDBEN);
> > +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN,
> > +			   MX25_TGCR_PDEN);
> > +
> > +	/* enable the engine on demand */
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK,
> > +			   MX25_ADCQ_CR_QSM_FQS);
> > +
> > +	/* Enable repeat and repeat wait */
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK,
> > +			   MX25_ADCQ_CR_RPT |
> > +			   MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT));
> > +
> > +	mx25_tcq_re_enable_touch_detection(priv);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mx25_tcq_parse_dt(struct platform_device *pdev,
> > +			     struct mx25_tcq_priv *priv)
> > +{
> > +	struct device_node *np = pdev->dev.of_node;
> > +	u32 wires;
> > +	int ret;
> > +
> > +	/* Setup defaults */
> > +	priv->pen_threshold = 500;
> > +	priv->sample_count = 3;
> > +	priv->pen_debounce = 1000000;
> > +	priv->settling_time = 250000;
> > +
> > +	ret = of_property_read_u32(np, "fsl,wires", &wires);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
> > +		return ret;
> > +	}
> > +
> > +	if (wires == 4) {
> > +		priv->mode = MX25_TS_4WIRE;
> > +	} else {
> > +		dev_err(&pdev->dev, "%u-wire mode not supported\n", wires);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* These are optional, we don't care about the return values */
> > +	of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold);
> > +	of_property_read_u32(np, "fsl,settling-time", &priv->settling_time);
> > +	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mx25_tcq_open(struct input_dev *idev)
> > +{
> > +	struct device *dev = &idev->dev;
> > +	struct mx25_tcq_priv *priv = dev_get_drvdata(dev);
> > +	int ret;
> > +
> > +	ret = clk_prepare_enable(priv->clk);
> > +	if (ret) {
> > +		dev_err(dev, "Failed to enable ipg clock\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = mx25_tcq_init(priv);
> > +	if (ret) {
> > +		dev_err(dev, "Failed to init tcq\n");
> > +		clk_disable_unprepare(priv->clk);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void mx25_tcq_close(struct input_dev *idev)
> > +{
> > +	struct mx25_tcq_priv *priv = input_get_drvdata(idev);
> > +
> > +	mx25_tcq_force_queue_stop(priv);
> > +	mx25_tcq_disable_touch_irq(priv);
> > +	mx25_tcq_disable_fifo_irq(priv);
> > +	clk_disable_unprepare(priv->clk);
> > +}
> > +
> > +static int mx25_tcq_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct input_dev *idev;
> > +	struct mx25_tcq_priv *priv;
> > +	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
> > +	struct resource *res;
> > +	void __iomem *mem;
> > +	int ret;
> 
> Personal preference: can we call variables that hold error codes and not
> returned in success path (i.e. when we do explicit "return 0:' for
> success) be called "error"?

Yes that's fine for me, changed it for most functions in this driver.

> > +
> > +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > +	if (!priv)
> > +		return -ENOMEM;
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	mem = devm_ioremap_resource(dev, res);
> > +	if (!mem)
> 
> devm_ioremap_resource() returns ERR_PTR-encoded pointer, you should not
> test it for NULL bit rather for IS_ERR.

Fixed, thanks.

> 
> > +		return -ENOMEM;
> > +
> > +	ret = mx25_tcq_parse_dt(pdev, priv);
> > +	if (ret)
> > +		return ret;
> > +
> > +	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig);
> > +	if (IS_ERR(priv->regs)) {
> > +		dev_err(dev, "Failed to initialize regmap\n");
> > +		return PTR_ERR(priv->regs);
> > +	}
> > +
> > +	priv->irq = platform_get_irq(pdev, 0);
> > +	if (priv->irq <= 0) {
> > +		dev_err(dev, "Failed to get IRQ\n");
> > +		return priv->irq;
> > +	}
> > +
> > +	idev = devm_input_allocate_device(dev);
> > +	if (!idev) {
> > +		dev_err(dev, "Failed to allocate input device\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	idev->name = mx25_tcq_name;
> > +	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> > +	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
> 
> Replace the 2 lines above with:
> 
> 	input_set_capability(idev, EV_BTN_TOUCH);

I assume you meant
	input_set_capability(idev, EV_KEY, BTN_TOUCH);
and changed it to that.

> 
> > +	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
> > +	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
> > +
> > +	idev->id.bustype = BUS_HOST;
> > +	idev->open = mx25_tcq_open;
> > +	idev->close = mx25_tcq_close;
> > +
> > +	priv->idev = idev;
> > +	input_set_drvdata(idev, priv);
> > +
> > +	priv->core_regs = tsadc->regs;
> > +	if (!priv->core_regs)
> > +		return -EINVAL;
> > +
> > +	priv->clk = tsadc->clk;
> > +	if (!priv->clk)
> > +		return -EINVAL;
> > +
> > +	platform_set_drvdata(pdev, priv);
> > +
> > +	ret = input_register_device(idev);
> > +	if (ret) {
> > +		dev_err(dev, "Failed to register input device\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq,
> > +					mx25_tcq_irq_thread, IRQF_ONESHOT,
> > +					pdev->name, priv);
> > +	if (ret) {
> > +		dev_err(dev, "Failed requesting IRQ\n");
> > +		return ret;
> > +	}
> 
> Is it possible that we enable interrupt generation in the controller (in
> mx25_tcq_open) before we request IRQ and then (if someone touches the
> screen) we'll get a spurious IRQ? I'd rather we swapped
> devm_request_threaded_irq() and input_register_device(): it is perfectly
> safe to use (send events) to allocated but not registered input device.

Ok, if it is safe to send events for an allocated device I can't see why the
irq request shouldn't be first. I swapped these.

Thanks,

Markus

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: This is a digitally signed message part.
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20151120/b19006d4/attachment-0001.sig>

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

* Re: [PATCH v8 4/8] mfd: fsl imx25 Touchscreen ADC driver
  2015-11-16 12:01   ` Markus Pargmann
@ 2015-11-21 17:02     ` Jonathan Cameron
  -1 siblings, 0 replies; 67+ messages in thread
From: Jonathan Cameron @ 2015-11-21 17:02 UTC (permalink / raw)
  To: Markus Pargmann, Shawn Guo, Dmitry Torokhov, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam, Juergen Borleis

On 16/11/15 12:01, Markus Pargmann wrote:
> This is the core driver for imx25 touchscreen/adc driver. The module
> has one shared ADC and two different conversion queues which use the
> ADC. The two queues are identical. Both can be used for general purpose
> ADC but one is meant to be used for touchscreens.
> 
> This driver is the core which manages the central components and
> registers of the TSC/ADC unit. It manages the IRQs and forwards them to
> the correct components.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> Signed-off-by: Denis Carikli <denis@eukrea.com>
> 
> [ensure correct ADC clock depending on the IPG clock]
> Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
Looks good to me - one query on meaning of a comment inline.

Acked-by: Jonathan Cameron <jic23@kernel.org>

I'm taking the view this series wants to go through the MFD tree
but don't mind if it goes through IIO or input instead
if there is a good reason to do so.

Jonathan
> ---
> 
> Notes:
>     Changes in v7:
>      - Cleanup bit defines in header files to be more readable
>      - Fix irq check to return with an error for irq <= 0
>      - Add COMPILE_TEST in Kconfig file
>     
>     Changes in v5:
>      - Remove ifdef CONFIG_OF as this driver is only for DT usage
>      - Remove module owner
>      - Add Kconfig dependencies ARCH_MX25 and OF
>     
>     @Jonathan Cameron:
>     I left your acked-by on the patch as these were small changes. If it should be
>     removed, please say so. Thanks
> 
>  drivers/mfd/Kconfig             |   9 ++
>  drivers/mfd/Makefile            |   2 +
>  drivers/mfd/fsl-imx25-tsadc.c   | 204 ++++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/imx25-tsadc.h | 140 +++++++++++++++++++++++++++
>  4 files changed, 355 insertions(+)
>  create mode 100644 drivers/mfd/fsl-imx25-tsadc.c
>  create mode 100644 include/linux/mfd/imx25-tsadc.h
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 4d92df6ef9fe..4222e202ad2b 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -271,6 +271,15 @@ config MFD_MC13XXX_I2C
>  	help
>  	  Select this if your MC13xxx is connected via an I2C bus.
>  
> +config MFD_MX25_TSADC
> +	tristate "Freescale i.MX25 integrated Touchscreen and ADC unit"
> +	select REGMAP_MMIO
> +	depends on (SOC_IMX25 && OF) || COMPILE_TEST
> +	help
> +	  Enable support for the integrated Touchscreen and ADC unit of the
> +	  i.MX25 processors. They consist of a conversion queue for general
> +	  purpose ADC and a queue for Touchscreens.
> +
>  config MFD_HI6421_PMIC
>  	tristate "HiSilicon Hi6421 PMU/Codec IC"
>  	depends on OF
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index a8b76b81b467..5741be88c173 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -81,6 +81,8 @@ obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
>  obj-$(CONFIG_MFD_TWL4030_AUDIO)	+= twl4030-audio.o
>  obj-$(CONFIG_TWL6040_CORE)	+= twl6040.o
>  
> +obj-$(CONFIG_MFD_MX25_TSADC)	+= fsl-imx25-tsadc.o
> +
>  obj-$(CONFIG_MFD_MC13XXX)	+= mc13xxx-core.o
>  obj-$(CONFIG_MFD_MC13XXX_SPI)	+= mc13xxx-spi.o
>  obj-$(CONFIG_MFD_MC13XXX_I2C)	+= mc13xxx-i2c.o
> diff --git a/drivers/mfd/fsl-imx25-tsadc.c b/drivers/mfd/fsl-imx25-tsadc.c
> new file mode 100644
> index 000000000000..e67d5ca81e10
> --- /dev/null
> +++ b/drivers/mfd/fsl-imx25-tsadc.c
> @@ -0,0 +1,204 @@
> +/*
> + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqdesc.h>
> +#include <linux/irqdomain.h>
> +#include <linux/irq.h>
> +#include <linux/mfd/imx25-tsadc.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +static struct regmap_config mx25_tsadc_regmap_config = {
> +	.fast_io = true,
> +	.max_register = 8,
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +};
> +
> +static void mx25_tsadc_irq_handler(struct irq_desc *desc)
> +{
> +	struct mx25_tsadc *tsadc = irq_desc_get_handler_data(desc);
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	u32 status;
> +
> +	chained_irq_enter(chip, desc);
> +
> +	regmap_read(tsadc->regs, MX25_TSC_TGSR, &status);
> +
> +	if (status & MX25_TGSR_GCQ_INT)
> +		generic_handle_irq(irq_find_mapping(tsadc->domain, 1));
> +
> +	if (status & MX25_TGSR_TCQ_INT)
> +		generic_handle_irq(irq_find_mapping(tsadc->domain, 0));
> +
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static int mx25_tsadc_domain_map(struct irq_domain *d, unsigned int irq,
> +				 irq_hw_number_t hwirq)
> +{
> +	struct mx25_tsadc *tsadc = d->host_data;
> +
> +	irq_set_chip_data(irq, tsadc);
> +	irq_set_chip_and_handler(irq, &dummy_irq_chip,
> +				 handle_level_irq);
> +	irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE);
> +
> +	return 0;
> +}
> +
> +static struct irq_domain_ops mx25_tsadc_domain_ops = {
> +	.map = mx25_tsadc_domain_map,
> +	.xlate = irq_domain_xlate_onecell,
> +};
> +
> +static int mx25_tsadc_setup_irq(struct platform_device *pdev,
> +				struct mx25_tsadc *tsadc)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	int irq;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq <= 0) {
> +		dev_err(dev, "Failed to get irq\n");
> +		return irq;
> +	}
> +
> +	tsadc->domain = irq_domain_add_simple(np, 2, 0, &mx25_tsadc_domain_ops,
> +					      tsadc);
> +	if (!tsadc->domain) {
> +		dev_err(dev, "Failed to add irq domain\n");
> +		return -ENOMEM;
> +	}
> +
> +	irq_set_chained_handler(irq, mx25_tsadc_irq_handler);
> +	irq_set_handler_data(irq, tsadc);
> +
> +	return 0;
> +}
> +
> +static void mx25_tsadc_setup_clk(struct platform_device *pdev,
> +				 struct mx25_tsadc *tsadc)
> +{
> +	unsigned clk_div;
> +
> +	/*
> +	 * According to the datasheet the ADC clock should never
> +	 * exceed 1,75 MHz. Base clock is the IPG and the ADC unit uses
> +	 * a funny clock divider. To keep the time constant the ADC needs to do
> +	 * one conversion, adapt the ADC internal clock divider to
> +	 * the available IPG
I'm not entirely following this explanation...
maybe

...one conversion, then adapt the ADC...

> +	 */
> +
> +	dev_dbg(&pdev->dev, "Found master clock at %lu Hz\n",
> +		clk_get_rate(tsadc->clk));
> +
> +	clk_div = DIV_ROUND_UP(clk_get_rate(tsadc->clk), 1750000);
> +	dev_dbg(&pdev->dev, "Setting up ADC clock divider to %u\n", clk_div);
> +
> +	/* adc clock = IPG clock / (2 * div + 2) */
> +	clk_div -= 2;
> +	clk_div /= 2;
> +
> +	/*
> +	 * the ADC clock divider changes its behaviour when values below 4
> +	 * are used: it is fixed to "/ 10" in this case
> +	 */
> +	clk_div = max_t(unsigned, 4, clk_div);
> +
> +	dev_dbg(&pdev->dev, "Resulting ADC conversion clock at %lu Hz\n",
> +		clk_get_rate(tsadc->clk) / (2 * clk_div + 2));
> +
> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR,
> +			   MX25_TGCR_ADCCLKCFG(0x1f),
> +			   MX25_TGCR_ADCCLKCFG(clk_div));
> +}
> +
> +static int mx25_tsadc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	struct mx25_tsadc *tsadc;
> +	struct resource *res;
> +	int ret;
> +	void __iomem *iomem;
> +
> +	tsadc = devm_kzalloc(dev, sizeof(*tsadc), GFP_KERNEL);
> +	if (!tsadc)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	iomem = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(iomem))
> +		return PTR_ERR(iomem);
> +
> +	tsadc->regs = devm_regmap_init_mmio(dev, iomem,
> +					    &mx25_tsadc_regmap_config);
> +	if (IS_ERR(tsadc->regs)) {
> +		dev_err(dev, "Failed to initialize regmap\n");
> +		return PTR_ERR(tsadc->regs);
> +	}
> +
> +	tsadc->clk = devm_clk_get(dev, "ipg");
> +	if (IS_ERR(tsadc->clk)) {
> +		dev_err(dev, "Failed to get ipg clock\n");
> +		return PTR_ERR(tsadc->clk);
> +	}
> +
> +	/* setup clock according to the datasheet */
> +	mx25_tsadc_setup_clk(pdev, tsadc);
> +
> +	/* Enable clock and reset the component */
> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_CLK_EN,
> +			   MX25_TGCR_CLK_EN);
> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_TSC_RST,
> +			   MX25_TGCR_TSC_RST);
> +
> +	/* Setup powersaving mode, but enable internal reference voltage */
> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_POWERMODE_MASK,
> +			   MX25_TGCR_POWERMODE_SAVE);
> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_INTREFEN,
> +			   MX25_TGCR_INTREFEN);
> +
> +	ret = mx25_tsadc_setup_irq(pdev, tsadc);
> +	if (ret)
> +		return ret;
> +
> +	platform_set_drvdata(pdev, tsadc);
> +
> +	of_platform_populate(np, NULL, NULL, dev);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id mx25_tsadc_ids[] = {
> +	{ .compatible = "fsl,imx25-tsadc" },
> +	{ /* Sentinel */ }
> +};
> +
> +static struct platform_driver mx25_tsadc_driver = {
> +	.driver = {
> +		.name = "mx25-tsadc",
> +		.of_match_table = of_match_ptr(mx25_tsadc_ids),
> +	},
> +	.probe = mx25_tsadc_probe,
> +};
> +module_platform_driver(mx25_tsadc_driver);
> +
> +MODULE_DESCRIPTION("MFD for ADC/TSC for Freescale mx25");
> +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:mx25-tsadc");
> diff --git a/include/linux/mfd/imx25-tsadc.h b/include/linux/mfd/imx25-tsadc.h
> new file mode 100644
> index 000000000000..7fe4b8c3baac
> --- /dev/null
> +++ b/include/linux/mfd/imx25-tsadc.h
> @@ -0,0 +1,140 @@
> +#ifndef _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
> +#define _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
> +
> +struct regmap;
> +struct clk;
> +
> +struct mx25_tsadc {
> +	struct regmap *regs;
> +	struct irq_domain *domain;
> +	struct clk *clk;
> +};
> +
> +#define MX25_TSC_TGCR			0x00
> +#define MX25_TSC_TGSR			0x04
> +#define MX25_TSC_TICR			0x08
> +
> +/* The same register layout for TC and GC queue */
> +#define MX25_ADCQ_FIFO			0x00
> +#define MX25_ADCQ_CR			0x04
> +#define MX25_ADCQ_SR			0x08
> +#define MX25_ADCQ_MR			0x0c
> +#define MX25_ADCQ_ITEM_7_0		0x20
> +#define MX25_ADCQ_ITEM_15_8		0x24
> +#define MX25_ADCQ_CFG(n)		(0x40 + ((n) * 0x4))
> +
> +#define MX25_ADCQ_MR_MASK		0xffffffff
> +
> +/* TGCR */
> +#define MX25_TGCR_PDBTIME(x)		((x) << 25)
> +#define MX25_TGCR_PDBTIME_MASK		GENMASK(31, 25)
> +#define MX25_TGCR_PDBEN			BIT(24)
> +#define MX25_TGCR_PDEN			BIT(23)
> +#define MX25_TGCR_ADCCLKCFG(x)		((x) << 16)
> +#define MX25_TGCR_GET_ADCCLK(x)		(((x) >> 16) & 0x1f)
> +#define MX25_TGCR_INTREFEN		BIT(10)
> +#define MX25_TGCR_POWERMODE_MASK	GENMASK(9, 8)
> +#define MX25_TGCR_POWERMODE_SAVE	(1 << 8)
> +#define MX25_TGCR_POWERMODE_ON		(2 << 8)
> +#define MX25_TGCR_STLC			BIT(5)
> +#define MX25_TGCR_SLPC			BIT(4)
> +#define MX25_TGCR_FUNC_RST		BIT(2)
> +#define MX25_TGCR_TSC_RST		BIT(1)
> +#define MX25_TGCR_CLK_EN		BIT(0)
> +
> +/* TGSR */
> +#define MX25_TGSR_SLP_INT		BIT(2)
> +#define MX25_TGSR_GCQ_INT		BIT(1)
> +#define MX25_TGSR_TCQ_INT		BIT(0)
> +
> +/* ADCQ_ITEM_* */
> +#define _MX25_ADCQ_ITEM(item, x)	((x) << ((item) * 4))
> +#define MX25_ADCQ_ITEM(item, x)		((item) >= 8 ? \
> +		_MX25_ADCQ_ITEM((item) - 8, (x)) : _MX25_ADCQ_ITEM((item), (x)))
> +
> +/* ADCQ_FIFO (TCQFIFO and GCQFIFO) */
> +#define MX25_ADCQ_FIFO_DATA(x)		(((x) >> 4) & 0xfff)
> +#define MX25_ADCQ_FIFO_ID(x)		((x) & 0xf)
> +
> +/* ADCQ_CR (TCQR and GCQR) */
> +#define MX25_ADCQ_CR_PDCFG_LEVEL	BIT(19)
> +#define MX25_ADCQ_CR_PDMSK		BIT(18)
> +#define MX25_ADCQ_CR_FRST		BIT(17)
> +#define MX25_ADCQ_CR_QRST		BIT(16)
> +#define MX25_ADCQ_CR_RWAIT_MASK		GENMASK(15, 12)
> +#define MX25_ADCQ_CR_RWAIT(x)		((x) << 12)
> +#define MX25_ADCQ_CR_WMRK_MASK		GENMASK(11, 8)
> +#define MX25_ADCQ_CR_WMRK(x)		((x) << 8)
> +#define MX25_ADCQ_CR_LITEMID_MASK	(0xf << 4)
> +#define MX25_ADCQ_CR_LITEMID(x)		((x) << 4)
> +#define MX25_ADCQ_CR_RPT		BIT(3)
> +#define MX25_ADCQ_CR_FQS		BIT(2)
> +#define MX25_ADCQ_CR_QSM_MASK		GENMASK(1, 0)
> +#define MX25_ADCQ_CR_QSM_PD		0x1
> +#define MX25_ADCQ_CR_QSM_FQS		0x2
> +#define MX25_ADCQ_CR_QSM_FQS_PD		0x3
> +
> +/* ADCQ_SR (TCQSR and GCQSR) */
> +#define MX25_ADCQ_SR_FDRY		BIT(15)
> +#define MX25_ADCQ_SR_FULL		BIT(14)
> +#define MX25_ADCQ_SR_EMPT		BIT(13)
> +#define MX25_ADCQ_SR_FDN(x)		(((x) >> 8) & 0x1f)
> +#define MX25_ADCQ_SR_FRR		BIT(6)
> +#define MX25_ADCQ_SR_FUR		BIT(5)
> +#define MX25_ADCQ_SR_FOR		BIT(4)
> +#define MX25_ADCQ_SR_EOQ		BIT(1)
> +#define MX25_ADCQ_SR_PD			BIT(0)
> +
> +/* ADCQ_MR (TCQMR and GCQMR) */
> +#define MX25_ADCQ_MR_FDRY_DMA		BIT(31)
> +#define MX25_ADCQ_MR_FER_DMA		BIT(22)
> +#define MX25_ADCQ_MR_FUR_DMA		BIT(21)
> +#define MX25_ADCQ_MR_FOR_DMA		BIT(20)
> +#define MX25_ADCQ_MR_EOQ_DMA		BIT(17)
> +#define MX25_ADCQ_MR_PD_DMA		BIT(16)
> +#define MX25_ADCQ_MR_FDRY_IRQ		BIT(15)
> +#define MX25_ADCQ_MR_FER_IRQ		BIT(6)
> +#define MX25_ADCQ_MR_FUR_IRQ		BIT(5)
> +#define MX25_ADCQ_MR_FOR_IRQ		BIT(4)
> +#define MX25_ADCQ_MR_EOQ_IRQ		BIT(1)
> +#define MX25_ADCQ_MR_PD_IRQ		BIT(0)
> +
> +/* ADCQ_CFG (TICR, TCC0-7,GCC0-7) */
> +#define MX25_ADCQ_CFG_SETTLING_TIME(x)	((x) << 24)
> +#define MX25_ADCQ_CFG_IGS		(1 << 20)
> +#define MX25_ADCQ_CFG_NOS_MASK		GENMASK(19, 16)
> +#define MX25_ADCQ_CFG_NOS(x)		(((x) - 1) << 16)
> +#define MX25_ADCQ_CFG_WIPER		(1 << 15)
> +#define MX25_ADCQ_CFG_YNLR		(1 << 14)
> +#define MX25_ADCQ_CFG_YPLL_HIGH		(0 << 12)
> +#define MX25_ADCQ_CFG_YPLL_OFF		(1 << 12)
> +#define MX25_ADCQ_CFG_YPLL_LOW		(3 << 12)
> +#define MX25_ADCQ_CFG_XNUR_HIGH		(0 << 10)
> +#define MX25_ADCQ_CFG_XNUR_OFF		(1 << 10)
> +#define MX25_ADCQ_CFG_XNUR_LOW		(3 << 10)
> +#define MX25_ADCQ_CFG_XPUL_HIGH		(0 << 9)
> +#define MX25_ADCQ_CFG_XPUL_OFF		(1 << 9)
> +#define MX25_ADCQ_CFG_REFP(sel)		((sel) << 7)
> +#define MX25_ADCQ_CFG_REFP_YP		MX25_ADCQ_CFG_REFP(0)
> +#define MX25_ADCQ_CFG_REFP_XP		MX25_ADCQ_CFG_REFP(1)
> +#define MX25_ADCQ_CFG_REFP_EXT		MX25_ADCQ_CFG_REFP(2)
> +#define MX25_ADCQ_CFG_REFP_INT		MX25_ADCQ_CFG_REFP(3)
> +#define MX25_ADCQ_CFG_REFP_MASK		GENMASK(8, 7)
> +#define MX25_ADCQ_CFG_IN(sel)		((sel) << 4)
> +#define MX25_ADCQ_CFG_IN_XP		MX25_ADCQ_CFG_IN(0)
> +#define MX25_ADCQ_CFG_IN_YP		MX25_ADCQ_CFG_IN(1)
> +#define MX25_ADCQ_CFG_IN_XN		MX25_ADCQ_CFG_IN(2)
> +#define MX25_ADCQ_CFG_IN_YN		MX25_ADCQ_CFG_IN(3)
> +#define MX25_ADCQ_CFG_IN_WIPER		MX25_ADCQ_CFG_IN(4)
> +#define MX25_ADCQ_CFG_IN_AUX0		MX25_ADCQ_CFG_IN(5)
> +#define MX25_ADCQ_CFG_IN_AUX1		MX25_ADCQ_CFG_IN(6)
> +#define MX25_ADCQ_CFG_IN_AUX2		MX25_ADCQ_CFG_IN(7)
> +#define MX25_ADCQ_CFG_REFN(sel)		((sel) << 2)
> +#define MX25_ADCQ_CFG_REFN_XN		MX25_ADCQ_CFG_REFN(0)
> +#define MX25_ADCQ_CFG_REFN_YN		MX25_ADCQ_CFG_REFN(1)
> +#define MX25_ADCQ_CFG_REFN_NGND		MX25_ADCQ_CFG_REFN(2)
> +#define MX25_ADCQ_CFG_REFN_NGND2	MX25_ADCQ_CFG_REFN(3)
> +#define MX25_ADCQ_CFG_REFN_MASK		GENMASK(3, 2)
> +#define MX25_ADCQ_CFG_PENIACK		(1 << 1)
> +
> +#endif  /* _LINUX_INCLUDE_MFD_IMX25_TSADC_H_ */
> 


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

* [PATCH v8 4/8] mfd: fsl imx25 Touchscreen ADC driver
@ 2015-11-21 17:02     ` Jonathan Cameron
  0 siblings, 0 replies; 67+ messages in thread
From: Jonathan Cameron @ 2015-11-21 17:02 UTC (permalink / raw)
  To: linux-arm-kernel

On 16/11/15 12:01, Markus Pargmann wrote:
> This is the core driver for imx25 touchscreen/adc driver. The module
> has one shared ADC and two different conversion queues which use the
> ADC. The two queues are identical. Both can be used for general purpose
> ADC but one is meant to be used for touchscreens.
> 
> This driver is the core which manages the central components and
> registers of the TSC/ADC unit. It manages the IRQs and forwards them to
> the correct components.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> Signed-off-by: Denis Carikli <denis@eukrea.com>
> 
> [ensure correct ADC clock depending on the IPG clock]
> Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
Looks good to me - one query on meaning of a comment inline.

Acked-by: Jonathan Cameron <jic23@kernel.org>

I'm taking the view this series wants to go through the MFD tree
but don't mind if it goes through IIO or input instead
if there is a good reason to do so.

Jonathan
> ---
> 
> Notes:
>     Changes in v7:
>      - Cleanup bit defines in header files to be more readable
>      - Fix irq check to return with an error for irq <= 0
>      - Add COMPILE_TEST in Kconfig file
>     
>     Changes in v5:
>      - Remove ifdef CONFIG_OF as this driver is only for DT usage
>      - Remove module owner
>      - Add Kconfig dependencies ARCH_MX25 and OF
>     
>     @Jonathan Cameron:
>     I left your acked-by on the patch as these were small changes. If it should be
>     removed, please say so. Thanks
> 
>  drivers/mfd/Kconfig             |   9 ++
>  drivers/mfd/Makefile            |   2 +
>  drivers/mfd/fsl-imx25-tsadc.c   | 204 ++++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/imx25-tsadc.h | 140 +++++++++++++++++++++++++++
>  4 files changed, 355 insertions(+)
>  create mode 100644 drivers/mfd/fsl-imx25-tsadc.c
>  create mode 100644 include/linux/mfd/imx25-tsadc.h
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 4d92df6ef9fe..4222e202ad2b 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -271,6 +271,15 @@ config MFD_MC13XXX_I2C
>  	help
>  	  Select this if your MC13xxx is connected via an I2C bus.
>  
> +config MFD_MX25_TSADC
> +	tristate "Freescale i.MX25 integrated Touchscreen and ADC unit"
> +	select REGMAP_MMIO
> +	depends on (SOC_IMX25 && OF) || COMPILE_TEST
> +	help
> +	  Enable support for the integrated Touchscreen and ADC unit of the
> +	  i.MX25 processors. They consist of a conversion queue for general
> +	  purpose ADC and a queue for Touchscreens.
> +
>  config MFD_HI6421_PMIC
>  	tristate "HiSilicon Hi6421 PMU/Codec IC"
>  	depends on OF
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index a8b76b81b467..5741be88c173 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -81,6 +81,8 @@ obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
>  obj-$(CONFIG_MFD_TWL4030_AUDIO)	+= twl4030-audio.o
>  obj-$(CONFIG_TWL6040_CORE)	+= twl6040.o
>  
> +obj-$(CONFIG_MFD_MX25_TSADC)	+= fsl-imx25-tsadc.o
> +
>  obj-$(CONFIG_MFD_MC13XXX)	+= mc13xxx-core.o
>  obj-$(CONFIG_MFD_MC13XXX_SPI)	+= mc13xxx-spi.o
>  obj-$(CONFIG_MFD_MC13XXX_I2C)	+= mc13xxx-i2c.o
> diff --git a/drivers/mfd/fsl-imx25-tsadc.c b/drivers/mfd/fsl-imx25-tsadc.c
> new file mode 100644
> index 000000000000..e67d5ca81e10
> --- /dev/null
> +++ b/drivers/mfd/fsl-imx25-tsadc.c
> @@ -0,0 +1,204 @@
> +/*
> + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqdesc.h>
> +#include <linux/irqdomain.h>
> +#include <linux/irq.h>
> +#include <linux/mfd/imx25-tsadc.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +static struct regmap_config mx25_tsadc_regmap_config = {
> +	.fast_io = true,
> +	.max_register = 8,
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +};
> +
> +static void mx25_tsadc_irq_handler(struct irq_desc *desc)
> +{
> +	struct mx25_tsadc *tsadc = irq_desc_get_handler_data(desc);
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	u32 status;
> +
> +	chained_irq_enter(chip, desc);
> +
> +	regmap_read(tsadc->regs, MX25_TSC_TGSR, &status);
> +
> +	if (status & MX25_TGSR_GCQ_INT)
> +		generic_handle_irq(irq_find_mapping(tsadc->domain, 1));
> +
> +	if (status & MX25_TGSR_TCQ_INT)
> +		generic_handle_irq(irq_find_mapping(tsadc->domain, 0));
> +
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static int mx25_tsadc_domain_map(struct irq_domain *d, unsigned int irq,
> +				 irq_hw_number_t hwirq)
> +{
> +	struct mx25_tsadc *tsadc = d->host_data;
> +
> +	irq_set_chip_data(irq, tsadc);
> +	irq_set_chip_and_handler(irq, &dummy_irq_chip,
> +				 handle_level_irq);
> +	irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE);
> +
> +	return 0;
> +}
> +
> +static struct irq_domain_ops mx25_tsadc_domain_ops = {
> +	.map = mx25_tsadc_domain_map,
> +	.xlate = irq_domain_xlate_onecell,
> +};
> +
> +static int mx25_tsadc_setup_irq(struct platform_device *pdev,
> +				struct mx25_tsadc *tsadc)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	int irq;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq <= 0) {
> +		dev_err(dev, "Failed to get irq\n");
> +		return irq;
> +	}
> +
> +	tsadc->domain = irq_domain_add_simple(np, 2, 0, &mx25_tsadc_domain_ops,
> +					      tsadc);
> +	if (!tsadc->domain) {
> +		dev_err(dev, "Failed to add irq domain\n");
> +		return -ENOMEM;
> +	}
> +
> +	irq_set_chained_handler(irq, mx25_tsadc_irq_handler);
> +	irq_set_handler_data(irq, tsadc);
> +
> +	return 0;
> +}
> +
> +static void mx25_tsadc_setup_clk(struct platform_device *pdev,
> +				 struct mx25_tsadc *tsadc)
> +{
> +	unsigned clk_div;
> +
> +	/*
> +	 * According to the datasheet the ADC clock should never
> +	 * exceed 1,75 MHz. Base clock is the IPG and the ADC unit uses
> +	 * a funny clock divider. To keep the time constant the ADC needs to do
> +	 * one conversion, adapt the ADC internal clock divider to
> +	 * the available IPG
I'm not entirely following this explanation...
maybe

...one conversion, then adapt the ADC...

> +	 */
> +
> +	dev_dbg(&pdev->dev, "Found master clock at %lu Hz\n",
> +		clk_get_rate(tsadc->clk));
> +
> +	clk_div = DIV_ROUND_UP(clk_get_rate(tsadc->clk), 1750000);
> +	dev_dbg(&pdev->dev, "Setting up ADC clock divider to %u\n", clk_div);
> +
> +	/* adc clock = IPG clock / (2 * div + 2) */
> +	clk_div -= 2;
> +	clk_div /= 2;
> +
> +	/*
> +	 * the ADC clock divider changes its behaviour when values below 4
> +	 * are used: it is fixed to "/ 10" in this case
> +	 */
> +	clk_div = max_t(unsigned, 4, clk_div);
> +
> +	dev_dbg(&pdev->dev, "Resulting ADC conversion clock at %lu Hz\n",
> +		clk_get_rate(tsadc->clk) / (2 * clk_div + 2));
> +
> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR,
> +			   MX25_TGCR_ADCCLKCFG(0x1f),
> +			   MX25_TGCR_ADCCLKCFG(clk_div));
> +}
> +
> +static int mx25_tsadc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	struct mx25_tsadc *tsadc;
> +	struct resource *res;
> +	int ret;
> +	void __iomem *iomem;
> +
> +	tsadc = devm_kzalloc(dev, sizeof(*tsadc), GFP_KERNEL);
> +	if (!tsadc)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	iomem = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(iomem))
> +		return PTR_ERR(iomem);
> +
> +	tsadc->regs = devm_regmap_init_mmio(dev, iomem,
> +					    &mx25_tsadc_regmap_config);
> +	if (IS_ERR(tsadc->regs)) {
> +		dev_err(dev, "Failed to initialize regmap\n");
> +		return PTR_ERR(tsadc->regs);
> +	}
> +
> +	tsadc->clk = devm_clk_get(dev, "ipg");
> +	if (IS_ERR(tsadc->clk)) {
> +		dev_err(dev, "Failed to get ipg clock\n");
> +		return PTR_ERR(tsadc->clk);
> +	}
> +
> +	/* setup clock according to the datasheet */
> +	mx25_tsadc_setup_clk(pdev, tsadc);
> +
> +	/* Enable clock and reset the component */
> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_CLK_EN,
> +			   MX25_TGCR_CLK_EN);
> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_TSC_RST,
> +			   MX25_TGCR_TSC_RST);
> +
> +	/* Setup powersaving mode, but enable internal reference voltage */
> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_POWERMODE_MASK,
> +			   MX25_TGCR_POWERMODE_SAVE);
> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_INTREFEN,
> +			   MX25_TGCR_INTREFEN);
> +
> +	ret = mx25_tsadc_setup_irq(pdev, tsadc);
> +	if (ret)
> +		return ret;
> +
> +	platform_set_drvdata(pdev, tsadc);
> +
> +	of_platform_populate(np, NULL, NULL, dev);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id mx25_tsadc_ids[] = {
> +	{ .compatible = "fsl,imx25-tsadc" },
> +	{ /* Sentinel */ }
> +};
> +
> +static struct platform_driver mx25_tsadc_driver = {
> +	.driver = {
> +		.name = "mx25-tsadc",
> +		.of_match_table = of_match_ptr(mx25_tsadc_ids),
> +	},
> +	.probe = mx25_tsadc_probe,
> +};
> +module_platform_driver(mx25_tsadc_driver);
> +
> +MODULE_DESCRIPTION("MFD for ADC/TSC for Freescale mx25");
> +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:mx25-tsadc");
> diff --git a/include/linux/mfd/imx25-tsadc.h b/include/linux/mfd/imx25-tsadc.h
> new file mode 100644
> index 000000000000..7fe4b8c3baac
> --- /dev/null
> +++ b/include/linux/mfd/imx25-tsadc.h
> @@ -0,0 +1,140 @@
> +#ifndef _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
> +#define _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
> +
> +struct regmap;
> +struct clk;
> +
> +struct mx25_tsadc {
> +	struct regmap *regs;
> +	struct irq_domain *domain;
> +	struct clk *clk;
> +};
> +
> +#define MX25_TSC_TGCR			0x00
> +#define MX25_TSC_TGSR			0x04
> +#define MX25_TSC_TICR			0x08
> +
> +/* The same register layout for TC and GC queue */
> +#define MX25_ADCQ_FIFO			0x00
> +#define MX25_ADCQ_CR			0x04
> +#define MX25_ADCQ_SR			0x08
> +#define MX25_ADCQ_MR			0x0c
> +#define MX25_ADCQ_ITEM_7_0		0x20
> +#define MX25_ADCQ_ITEM_15_8		0x24
> +#define MX25_ADCQ_CFG(n)		(0x40 + ((n) * 0x4))
> +
> +#define MX25_ADCQ_MR_MASK		0xffffffff
> +
> +/* TGCR */
> +#define MX25_TGCR_PDBTIME(x)		((x) << 25)
> +#define MX25_TGCR_PDBTIME_MASK		GENMASK(31, 25)
> +#define MX25_TGCR_PDBEN			BIT(24)
> +#define MX25_TGCR_PDEN			BIT(23)
> +#define MX25_TGCR_ADCCLKCFG(x)		((x) << 16)
> +#define MX25_TGCR_GET_ADCCLK(x)		(((x) >> 16) & 0x1f)
> +#define MX25_TGCR_INTREFEN		BIT(10)
> +#define MX25_TGCR_POWERMODE_MASK	GENMASK(9, 8)
> +#define MX25_TGCR_POWERMODE_SAVE	(1 << 8)
> +#define MX25_TGCR_POWERMODE_ON		(2 << 8)
> +#define MX25_TGCR_STLC			BIT(5)
> +#define MX25_TGCR_SLPC			BIT(4)
> +#define MX25_TGCR_FUNC_RST		BIT(2)
> +#define MX25_TGCR_TSC_RST		BIT(1)
> +#define MX25_TGCR_CLK_EN		BIT(0)
> +
> +/* TGSR */
> +#define MX25_TGSR_SLP_INT		BIT(2)
> +#define MX25_TGSR_GCQ_INT		BIT(1)
> +#define MX25_TGSR_TCQ_INT		BIT(0)
> +
> +/* ADCQ_ITEM_* */
> +#define _MX25_ADCQ_ITEM(item, x)	((x) << ((item) * 4))
> +#define MX25_ADCQ_ITEM(item, x)		((item) >= 8 ? \
> +		_MX25_ADCQ_ITEM((item) - 8, (x)) : _MX25_ADCQ_ITEM((item), (x)))
> +
> +/* ADCQ_FIFO (TCQFIFO and GCQFIFO) */
> +#define MX25_ADCQ_FIFO_DATA(x)		(((x) >> 4) & 0xfff)
> +#define MX25_ADCQ_FIFO_ID(x)		((x) & 0xf)
> +
> +/* ADCQ_CR (TCQR and GCQR) */
> +#define MX25_ADCQ_CR_PDCFG_LEVEL	BIT(19)
> +#define MX25_ADCQ_CR_PDMSK		BIT(18)
> +#define MX25_ADCQ_CR_FRST		BIT(17)
> +#define MX25_ADCQ_CR_QRST		BIT(16)
> +#define MX25_ADCQ_CR_RWAIT_MASK		GENMASK(15, 12)
> +#define MX25_ADCQ_CR_RWAIT(x)		((x) << 12)
> +#define MX25_ADCQ_CR_WMRK_MASK		GENMASK(11, 8)
> +#define MX25_ADCQ_CR_WMRK(x)		((x) << 8)
> +#define MX25_ADCQ_CR_LITEMID_MASK	(0xf << 4)
> +#define MX25_ADCQ_CR_LITEMID(x)		((x) << 4)
> +#define MX25_ADCQ_CR_RPT		BIT(3)
> +#define MX25_ADCQ_CR_FQS		BIT(2)
> +#define MX25_ADCQ_CR_QSM_MASK		GENMASK(1, 0)
> +#define MX25_ADCQ_CR_QSM_PD		0x1
> +#define MX25_ADCQ_CR_QSM_FQS		0x2
> +#define MX25_ADCQ_CR_QSM_FQS_PD		0x3
> +
> +/* ADCQ_SR (TCQSR and GCQSR) */
> +#define MX25_ADCQ_SR_FDRY		BIT(15)
> +#define MX25_ADCQ_SR_FULL		BIT(14)
> +#define MX25_ADCQ_SR_EMPT		BIT(13)
> +#define MX25_ADCQ_SR_FDN(x)		(((x) >> 8) & 0x1f)
> +#define MX25_ADCQ_SR_FRR		BIT(6)
> +#define MX25_ADCQ_SR_FUR		BIT(5)
> +#define MX25_ADCQ_SR_FOR		BIT(4)
> +#define MX25_ADCQ_SR_EOQ		BIT(1)
> +#define MX25_ADCQ_SR_PD			BIT(0)
> +
> +/* ADCQ_MR (TCQMR and GCQMR) */
> +#define MX25_ADCQ_MR_FDRY_DMA		BIT(31)
> +#define MX25_ADCQ_MR_FER_DMA		BIT(22)
> +#define MX25_ADCQ_MR_FUR_DMA		BIT(21)
> +#define MX25_ADCQ_MR_FOR_DMA		BIT(20)
> +#define MX25_ADCQ_MR_EOQ_DMA		BIT(17)
> +#define MX25_ADCQ_MR_PD_DMA		BIT(16)
> +#define MX25_ADCQ_MR_FDRY_IRQ		BIT(15)
> +#define MX25_ADCQ_MR_FER_IRQ		BIT(6)
> +#define MX25_ADCQ_MR_FUR_IRQ		BIT(5)
> +#define MX25_ADCQ_MR_FOR_IRQ		BIT(4)
> +#define MX25_ADCQ_MR_EOQ_IRQ		BIT(1)
> +#define MX25_ADCQ_MR_PD_IRQ		BIT(0)
> +
> +/* ADCQ_CFG (TICR, TCC0-7,GCC0-7) */
> +#define MX25_ADCQ_CFG_SETTLING_TIME(x)	((x) << 24)
> +#define MX25_ADCQ_CFG_IGS		(1 << 20)
> +#define MX25_ADCQ_CFG_NOS_MASK		GENMASK(19, 16)
> +#define MX25_ADCQ_CFG_NOS(x)		(((x) - 1) << 16)
> +#define MX25_ADCQ_CFG_WIPER		(1 << 15)
> +#define MX25_ADCQ_CFG_YNLR		(1 << 14)
> +#define MX25_ADCQ_CFG_YPLL_HIGH		(0 << 12)
> +#define MX25_ADCQ_CFG_YPLL_OFF		(1 << 12)
> +#define MX25_ADCQ_CFG_YPLL_LOW		(3 << 12)
> +#define MX25_ADCQ_CFG_XNUR_HIGH		(0 << 10)
> +#define MX25_ADCQ_CFG_XNUR_OFF		(1 << 10)
> +#define MX25_ADCQ_CFG_XNUR_LOW		(3 << 10)
> +#define MX25_ADCQ_CFG_XPUL_HIGH		(0 << 9)
> +#define MX25_ADCQ_CFG_XPUL_OFF		(1 << 9)
> +#define MX25_ADCQ_CFG_REFP(sel)		((sel) << 7)
> +#define MX25_ADCQ_CFG_REFP_YP		MX25_ADCQ_CFG_REFP(0)
> +#define MX25_ADCQ_CFG_REFP_XP		MX25_ADCQ_CFG_REFP(1)
> +#define MX25_ADCQ_CFG_REFP_EXT		MX25_ADCQ_CFG_REFP(2)
> +#define MX25_ADCQ_CFG_REFP_INT		MX25_ADCQ_CFG_REFP(3)
> +#define MX25_ADCQ_CFG_REFP_MASK		GENMASK(8, 7)
> +#define MX25_ADCQ_CFG_IN(sel)		((sel) << 4)
> +#define MX25_ADCQ_CFG_IN_XP		MX25_ADCQ_CFG_IN(0)
> +#define MX25_ADCQ_CFG_IN_YP		MX25_ADCQ_CFG_IN(1)
> +#define MX25_ADCQ_CFG_IN_XN		MX25_ADCQ_CFG_IN(2)
> +#define MX25_ADCQ_CFG_IN_YN		MX25_ADCQ_CFG_IN(3)
> +#define MX25_ADCQ_CFG_IN_WIPER		MX25_ADCQ_CFG_IN(4)
> +#define MX25_ADCQ_CFG_IN_AUX0		MX25_ADCQ_CFG_IN(5)
> +#define MX25_ADCQ_CFG_IN_AUX1		MX25_ADCQ_CFG_IN(6)
> +#define MX25_ADCQ_CFG_IN_AUX2		MX25_ADCQ_CFG_IN(7)
> +#define MX25_ADCQ_CFG_REFN(sel)		((sel) << 2)
> +#define MX25_ADCQ_CFG_REFN_XN		MX25_ADCQ_CFG_REFN(0)
> +#define MX25_ADCQ_CFG_REFN_YN		MX25_ADCQ_CFG_REFN(1)
> +#define MX25_ADCQ_CFG_REFN_NGND		MX25_ADCQ_CFG_REFN(2)
> +#define MX25_ADCQ_CFG_REFN_NGND2	MX25_ADCQ_CFG_REFN(3)
> +#define MX25_ADCQ_CFG_REFN_MASK		GENMASK(3, 2)
> +#define MX25_ADCQ_CFG_PENIACK		(1 << 1)
> +
> +#endif  /* _LINUX_INCLUDE_MFD_IMX25_TSADC_H_ */
> 

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

* Re: [PATCH v8 4/8] mfd: fsl imx25 Touchscreen ADC driver
  2015-11-21 17:02     ` Jonathan Cameron
  (?)
@ 2015-11-21 17:26         ` Jonathan Cameron
  -1 siblings, 0 replies; 67+ messages in thread
From: Jonathan Cameron @ 2015-11-21 17:26 UTC (permalink / raw)
  To: Markus Pargmann, Shawn Guo, Dmitry Torokhov, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Hartmut Knaack, Fabio Estevam, Juergen Borleis

On 21/11/15 17:02, Jonathan Cameron wrote:
> On 16/11/15 12:01, Markus Pargmann wrote:
>> This is the core driver for imx25 touchscreen/adc driver. The module
>> has one shared ADC and two different conversion queues which use the
>> ADC. The two queues are identical. Both can be used for general purpose
>> ADC but one is meant to be used for touchscreens.
>>
>> This driver is the core which manages the central components and
>> registers of the TSC/ADC unit. It manages the IRQs and forwards them to
>> the correct components.
>>
>> Signed-off-by: Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
>> Signed-off-by: Denis Carikli <denis-fO0SIAKYzcbQT0dZR+AlfA@public.gmane.org>
>>
>> [ensure correct ADC clock depending on the IPG clock]
>> Signed-off-by: Juergen Borleis <jbe-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> Looks good to me - one query on meaning of a comment inline.
> 
> Acked-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> 
> I'm taking the view this series wants to go through the MFD tree
> but don't mind if it goes through IIO or input instead
> if there is a good reason to do so.
> 
> Jonathan
>> ---
>>
>> Notes:
>>     Changes in v7:
>>      - Cleanup bit defines in header files to be more readable
>>      - Fix irq check to return with an error for irq <= 0
>>      - Add COMPILE_TEST in Kconfig file
>>     
>>     Changes in v5:
>>      - Remove ifdef CONFIG_OF as this driver is only for DT usage
>>      - Remove module owner
>>      - Add Kconfig dependencies ARCH_MX25 and OF
>>     
>>     @Jonathan Cameron:
>>     I left your acked-by on the patch as these were small changes. If it should be
>>     removed, please say so. Thanks
>>
>>  drivers/mfd/Kconfig             |   9 ++
>>  drivers/mfd/Makefile            |   2 +
>>  drivers/mfd/fsl-imx25-tsadc.c   | 204 ++++++++++++++++++++++++++++++++++++++++
>>  include/linux/mfd/imx25-tsadc.h | 140 +++++++++++++++++++++++++++
>>  4 files changed, 355 insertions(+)
>>  create mode 100644 drivers/mfd/fsl-imx25-tsadc.c
>>  create mode 100644 include/linux/mfd/imx25-tsadc.h
>>
>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>> index 4d92df6ef9fe..4222e202ad2b 100644
>> --- a/drivers/mfd/Kconfig
>> +++ b/drivers/mfd/Kconfig
>> @@ -271,6 +271,15 @@ config MFD_MC13XXX_I2C
>>  	help
>>  	  Select this if your MC13xxx is connected via an I2C bus.
>>  
>> +config MFD_MX25_TSADC
>> +	tristate "Freescale i.MX25 integrated Touchscreen and ADC unit"
>> +	select REGMAP_MMIO
>> +	depends on (SOC_IMX25 && OF) || COMPILE_TEST
>> +	help
>> +	  Enable support for the integrated Touchscreen and ADC unit of the
>> +	  i.MX25 processors. They consist of a conversion queue for general
>> +	  purpose ADC and a queue for Touchscreens.
>> +
>>  config MFD_HI6421_PMIC
>>  	tristate "HiSilicon Hi6421 PMU/Codec IC"
>>  	depends on OF
>> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
>> index a8b76b81b467..5741be88c173 100644
>> --- a/drivers/mfd/Makefile
>> +++ b/drivers/mfd/Makefile
>> @@ -81,6 +81,8 @@ obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
>>  obj-$(CONFIG_MFD_TWL4030_AUDIO)	+= twl4030-audio.o
>>  obj-$(CONFIG_TWL6040_CORE)	+= twl6040.o
>>  
>> +obj-$(CONFIG_MFD_MX25_TSADC)	+= fsl-imx25-tsadc.o
>> +
>>  obj-$(CONFIG_MFD_MC13XXX)	+= mc13xxx-core.o
>>  obj-$(CONFIG_MFD_MC13XXX_SPI)	+= mc13xxx-spi.o
>>  obj-$(CONFIG_MFD_MC13XXX_I2C)	+= mc13xxx-i2c.o
>> diff --git a/drivers/mfd/fsl-imx25-tsadc.c b/drivers/mfd/fsl-imx25-tsadc.c
>> new file mode 100644
>> index 000000000000..e67d5ca81e10
>> --- /dev/null
>> +++ b/drivers/mfd/fsl-imx25-tsadc.c
>> @@ -0,0 +1,204 @@
>> +/*
>> + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
>> + *
>> + * This program is free software; you can redistribute it and/or modify it under
>> + * the terms of the GNU General Public License version 2 as published by the
>> + * Free Software Foundation.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +#include <linux/irqdesc.h>
>> +#include <linux/irqdomain.h>
>> +#include <linux/irq.h>
>> +#include <linux/mfd/imx25-tsadc.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +
>> +static struct regmap_config mx25_tsadc_regmap_config = {
>> +	.fast_io = true,
>> +	.max_register = 8,
>> +	.reg_bits = 32,
>> +	.val_bits = 32,
>> +	.reg_stride = 4,
>> +};
>> +
>> +static void mx25_tsadc_irq_handler(struct irq_desc *desc)
>> +{
>> +	struct mx25_tsadc *tsadc = irq_desc_get_handler_data(desc);
>> +	struct irq_chip *chip = irq_desc_get_chip(desc);
>> +	u32 status;
>> +
>> +	chained_irq_enter(chip, desc);
>> +
>> +	regmap_read(tsadc->regs, MX25_TSC_TGSR, &status);
>> +
>> +	if (status & MX25_TGSR_GCQ_INT)
>> +		generic_handle_irq(irq_find_mapping(tsadc->domain, 1));
>> +
>> +	if (status & MX25_TGSR_TCQ_INT)
>> +		generic_handle_irq(irq_find_mapping(tsadc->domain, 0));
>> +
>> +	chained_irq_exit(chip, desc);
>> +}
>> +
>> +static int mx25_tsadc_domain_map(struct irq_domain *d, unsigned int irq,
>> +				 irq_hw_number_t hwirq)
>> +{
>> +	struct mx25_tsadc *tsadc = d->host_data;
>> +
>> +	irq_set_chip_data(irq, tsadc);
>> +	irq_set_chip_and_handler(irq, &dummy_irq_chip,
>> +				 handle_level_irq);
>> +	irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct irq_domain_ops mx25_tsadc_domain_ops = {
>> +	.map = mx25_tsadc_domain_map,
>> +	.xlate = irq_domain_xlate_onecell,
>> +};
>> +
>> +static int mx25_tsadc_setup_irq(struct platform_device *pdev,
>> +				struct mx25_tsadc *tsadc)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct device_node *np = dev->of_node;
>> +	int irq;
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (irq <= 0) {
>> +		dev_err(dev, "Failed to get irq\n");
>> +		return irq;
>> +	}
>> +
>> +	tsadc->domain = irq_domain_add_simple(np, 2, 0, &mx25_tsadc_domain_ops,
>> +					      tsadc);
>> +	if (!tsadc->domain) {
>> +		dev_err(dev, "Failed to add irq domain\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	irq_set_chained_handler(irq, mx25_tsadc_irq_handler);
>> +	irq_set_handler_data(irq, tsadc);
>> +
>> +	return 0;
>> +}
>> +
>> +static void mx25_tsadc_setup_clk(struct platform_device *pdev,
>> +				 struct mx25_tsadc *tsadc)
>> +{
>> +	unsigned clk_div;
>> +
>> +	/*
>> +	 * According to the datasheet the ADC clock should never
>> +	 * exceed 1,75 MHz. Base clock is the IPG and the ADC unit uses
>> +	 * a funny clock divider. To keep the time constant the ADC needs to do
>> +	 * one conversion, adapt the ADC internal clock divider to
>> +	 * the available IPG
> I'm not entirely following this explanation...
> maybe
> 
> ...one conversion, then adapt the ADC...
> 
>> +	 */
>> +
>> +	dev_dbg(&pdev->dev, "Found master clock at %lu Hz\n",
>> +		clk_get_rate(tsadc->clk));
>> +
>> +	clk_div = DIV_ROUND_UP(clk_get_rate(tsadc->clk), 1750000);
>> +	dev_dbg(&pdev->dev, "Setting up ADC clock divider to %u\n", clk_div);
>> +
>> +	/* adc clock = IPG clock / (2 * div + 2) */
>> +	clk_div -= 2;
>> +	clk_div /= 2;
>> +
>> +	/*
>> +	 * the ADC clock divider changes its behaviour when values below 4
>> +	 * are used: it is fixed to "/ 10" in this case
>> +	 */
>> +	clk_div = max_t(unsigned, 4, clk_div);
>> +
>> +	dev_dbg(&pdev->dev, "Resulting ADC conversion clock at %lu Hz\n",
>> +		clk_get_rate(tsadc->clk) / (2 * clk_div + 2));
>> +
>> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR,
>> +			   MX25_TGCR_ADCCLKCFG(0x1f),
>> +			   MX25_TGCR_ADCCLKCFG(clk_div));
>> +}
>> +
>> +static int mx25_tsadc_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct device_node *np = dev->of_node;
>> +	struct mx25_tsadc *tsadc;
>> +	struct resource *res;
>> +	int ret;
>> +	void __iomem *iomem;
>> +
>> +	tsadc = devm_kzalloc(dev, sizeof(*tsadc), GFP_KERNEL);
>> +	if (!tsadc)
>> +		return -ENOMEM;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	iomem = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR(iomem))
>> +		return PTR_ERR(iomem);
>> +
>> +	tsadc->regs = devm_regmap_init_mmio(dev, iomem,
>> +					    &mx25_tsadc_regmap_config);
>> +	if (IS_ERR(tsadc->regs)) {
>> +		dev_err(dev, "Failed to initialize regmap\n");
>> +		return PTR_ERR(tsadc->regs);
>> +	}
>> +
>> +	tsadc->clk = devm_clk_get(dev, "ipg");
>> +	if (IS_ERR(tsadc->clk)) {
>> +		dev_err(dev, "Failed to get ipg clock\n");
>> +		return PTR_ERR(tsadc->clk);
>> +	}
>> +
>> +	/* setup clock according to the datasheet */
>> +	mx25_tsadc_setup_clk(pdev, tsadc);
>> +
>> +	/* Enable clock and reset the component */
>> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_CLK_EN,
>> +			   MX25_TGCR_CLK_EN);
>> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_TSC_RST,
>> +			   MX25_TGCR_TSC_RST);
>> +
>> +	/* Setup powersaving mode, but enable internal reference voltage */
>> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_POWERMODE_MASK,
>> +			   MX25_TGCR_POWERMODE_SAVE);
>> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_INTREFEN,
>> +			   MX25_TGCR_INTREFEN);
>> +
>> +	ret = mx25_tsadc_setup_irq(pdev, tsadc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	platform_set_drvdata(pdev, tsadc);
>> +
>> +	of_platform_populate(np, NULL, NULL, dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id mx25_tsadc_ids[] = {
>> +	{ .compatible = "fsl,imx25-tsadc" },
>> +	{ /* Sentinel */ }
>> +};
>> +
>> +static struct platform_driver mx25_tsadc_driver = {
>> +	.driver = {
>> +		.name = "mx25-tsadc",
>> +		.of_match_table = of_match_ptr(mx25_tsadc_ids),
>> +	},
>> +	.probe = mx25_tsadc_probe,
>> +};
>> +module_platform_driver(mx25_tsadc_driver);
>> +
>> +MODULE_DESCRIPTION("MFD for ADC/TSC for Freescale mx25");
>> +MODULE_AUTHOR("Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:mx25-tsadc");
>> diff --git a/include/linux/mfd/imx25-tsadc.h b/include/linux/mfd/imx25-tsadc.h
>> new file mode 100644
>> index 000000000000..7fe4b8c3baac
>> --- /dev/null
>> +++ b/include/linux/mfd/imx25-tsadc.h
>> @@ -0,0 +1,140 @@
>> +#ifndef _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
>> +#define _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
>> +
>> +struct regmap;
>> +struct clk;
>> +
>> +struct mx25_tsadc {
>> +	struct regmap *regs;
>> +	struct irq_domain *domain;
>> +	struct clk *clk;
>> +};
>> +
>> +#define MX25_TSC_TGCR			0x00
>> +#define MX25_TSC_TGSR			0x04
>> +#define MX25_TSC_TICR			0x08
>> +
>> +/* The same register layout for TC and GC queue */
>> +#define MX25_ADCQ_FIFO			0x00
>> +#define MX25_ADCQ_CR			0x04
>> +#define MX25_ADCQ_SR			0x08
>> +#define MX25_ADCQ_MR			0x0c
>> +#define MX25_ADCQ_ITEM_7_0		0x20
>> +#define MX25_ADCQ_ITEM_15_8		0x24
>> +#define MX25_ADCQ_CFG(n)		(0x40 + ((n) * 0x4))
>> +
>> +#define MX25_ADCQ_MR_MASK		0xffffffff
>> +
>> +/* TGCR */
>> +#define MX25_TGCR_PDBTIME(x)		((x) << 25)
>> +#define MX25_TGCR_PDBTIME_MASK		GENMASK(31, 25)
>> +#define MX25_TGCR_PDBEN			BIT(24)
>> +#define MX25_TGCR_PDEN			BIT(23)
>> +#define MX25_TGCR_ADCCLKCFG(x)		((x) << 16)
>> +#define MX25_TGCR_GET_ADCCLK(x)		(((x) >> 16) & 0x1f)
>> +#define MX25_TGCR_INTREFEN		BIT(10)
>> +#define MX25_TGCR_POWERMODE_MASK	GENMASK(9, 8)
>> +#define MX25_TGCR_POWERMODE_SAVE	(1 << 8)
>> +#define MX25_TGCR_POWERMODE_ON		(2 << 8)
>> +#define MX25_TGCR_STLC			BIT(5)
>> +#define MX25_TGCR_SLPC			BIT(4)
>> +#define MX25_TGCR_FUNC_RST		BIT(2)
>> +#define MX25_TGCR_TSC_RST		BIT(1)
>> +#define MX25_TGCR_CLK_EN		BIT(0)
>> +
>> +/* TGSR */
>> +#define MX25_TGSR_SLP_INT		BIT(2)
>> +#define MX25_TGSR_GCQ_INT		BIT(1)
>> +#define MX25_TGSR_TCQ_INT		BIT(0)
>> +
>> +/* ADCQ_ITEM_* */
>> +#define _MX25_ADCQ_ITEM(item, x)	((x) << ((item) * 4))
>> +#define MX25_ADCQ_ITEM(item, x)		((item) >= 8 ? \
>> +		_MX25_ADCQ_ITEM((item) - 8, (x)) : _MX25_ADCQ_ITEM((item), (x)))
>> +
>> +/* ADCQ_FIFO (TCQFIFO and GCQFIFO) */
>> +#define MX25_ADCQ_FIFO_DATA(x)		(((x) >> 4) & 0xfff)
>> +#define MX25_ADCQ_FIFO_ID(x)		((x) & 0xf)
>> +
>> +/* ADCQ_CR (TCQR and GCQR) */
>> +#define MX25_ADCQ_CR_PDCFG_LEVEL	BIT(19)
>> +#define MX25_ADCQ_CR_PDMSK		BIT(18)
>> +#define MX25_ADCQ_CR_FRST		BIT(17)
>> +#define MX25_ADCQ_CR_QRST		BIT(16)
>> +#define MX25_ADCQ_CR_RWAIT_MASK		GENMASK(15, 12)
>> +#define MX25_ADCQ_CR_RWAIT(x)		((x) << 12)
>> +#define MX25_ADCQ_CR_WMRK_MASK		GENMASK(11, 8)
>> +#define MX25_ADCQ_CR_WMRK(x)		((x) << 8)
>> +#define MX25_ADCQ_CR_LITEMID_MASK	(0xf << 4)
>> +#define MX25_ADCQ_CR_LITEMID(x)		((x) << 4)
>> +#define MX25_ADCQ_CR_RPT		BIT(3)
>> +#define MX25_ADCQ_CR_FQS		BIT(2)
>> +#define MX25_ADCQ_CR_QSM_MASK		GENMASK(1, 0)
>> +#define MX25_ADCQ_CR_QSM_PD		0x1
>> +#define MX25_ADCQ_CR_QSM_FQS		0x2
>> +#define MX25_ADCQ_CR_QSM_FQS_PD		0x3
>> +
>> +/* ADCQ_SR (TCQSR and GCQSR) */
>> +#define MX25_ADCQ_SR_FDRY		BIT(15)
>> +#define MX25_ADCQ_SR_FULL		BIT(14)
>> +#define MX25_ADCQ_SR_EMPT		BIT(13)
>> +#define MX25_ADCQ_SR_FDN(x)		(((x) >> 8) & 0x1f)
>> +#define MX25_ADCQ_SR_FRR		BIT(6)
>> +#define MX25_ADCQ_SR_FUR		BIT(5)
>> +#define MX25_ADCQ_SR_FOR		BIT(4)
>> +#define MX25_ADCQ_SR_EOQ		BIT(1)
>> +#define MX25_ADCQ_SR_PD			BIT(0)
>> +
>> +/* ADCQ_MR (TCQMR and GCQMR) */
>> +#define MX25_ADCQ_MR_FDRY_DMA		BIT(31)
>> +#define MX25_ADCQ_MR_FER_DMA		BIT(22)
>> +#define MX25_ADCQ_MR_FUR_DMA		BIT(21)
>> +#define MX25_ADCQ_MR_FOR_DMA		BIT(20)
>> +#define MX25_ADCQ_MR_EOQ_DMA		BIT(17)
>> +#define MX25_ADCQ_MR_PD_DMA		BIT(16)
>> +#define MX25_ADCQ_MR_FDRY_IRQ		BIT(15)
>> +#define MX25_ADCQ_MR_FER_IRQ		BIT(6)
>> +#define MX25_ADCQ_MR_FUR_IRQ		BIT(5)
>> +#define MX25_ADCQ_MR_FOR_IRQ		BIT(4)
>> +#define MX25_ADCQ_MR_EOQ_IRQ		BIT(1)
>> +#define MX25_ADCQ_MR_PD_IRQ		BIT(0)
>> +
>> +/* ADCQ_CFG (TICR, TCC0-7,GCC0-7) */
>> +#define MX25_ADCQ_CFG_SETTLING_TIME(x)	((x) << 24)
>> +#define MX25_ADCQ_CFG_IGS		(1 << 20)
>> +#define MX25_ADCQ_CFG_NOS_MASK		GENMASK(19, 16)
>> +#define MX25_ADCQ_CFG_NOS(x)		(((x) - 1) << 16)
>> +#define MX25_ADCQ_CFG_WIPER		(1 << 15)
>> +#define MX25_ADCQ_CFG_YNLR		(1 << 14)
>> +#define MX25_ADCQ_CFG_YPLL_HIGH		(0 << 12)
>> +#define MX25_ADCQ_CFG_YPLL_OFF		(1 << 12)
>> +#define MX25_ADCQ_CFG_YPLL_LOW		(3 << 12)
>> +#define MX25_ADCQ_CFG_XNUR_HIGH		(0 << 10)
>> +#define MX25_ADCQ_CFG_XNUR_OFF		(1 << 10)
>> +#define MX25_ADCQ_CFG_XNUR_LOW		(3 << 10)
>> +#define MX25_ADCQ_CFG_XPUL_HIGH		(0 << 9)
>> +#define MX25_ADCQ_CFG_XPUL_OFF		(1 << 9)
>> +#define MX25_ADCQ_CFG_REFP(sel)		((sel) << 7)
>> +#define MX25_ADCQ_CFG_REFP_YP		MX25_ADCQ_CFG_REFP(0)
>> +#define MX25_ADCQ_CFG_REFP_XP		MX25_ADCQ_CFG_REFP(1)
>> +#define MX25_ADCQ_CFG_REFP_EXT		MX25_ADCQ_CFG_REFP(2)
>> +#define MX25_ADCQ_CFG_REFP_INT		MX25_ADCQ_CFG_REFP(3)
>> +#define MX25_ADCQ_CFG_REFP_MASK		GENMASK(8, 7)
>> +#define MX25_ADCQ_CFG_IN(sel)		((sel) << 4)
>> +#define MX25_ADCQ_CFG_IN_XP		MX25_ADCQ_CFG_IN(0)
>> +#define MX25_ADCQ_CFG_IN_YP		MX25_ADCQ_CFG_IN(1)
>> +#define MX25_ADCQ_CFG_IN_XN		MX25_ADCQ_CFG_IN(2)
>> +#define MX25_ADCQ_CFG_IN_YN		MX25_ADCQ_CFG_IN(3)
>> +#define MX25_ADCQ_CFG_IN_WIPER		MX25_ADCQ_CFG_IN(4)
>> +#define MX25_ADCQ_CFG_IN_AUX0		MX25_ADCQ_CFG_IN(5)
>> +#define MX25_ADCQ_CFG_IN_AUX1		MX25_ADCQ_CFG_IN(6)
>> +#define MX25_ADCQ_CFG_IN_AUX2		MX25_ADCQ_CFG_IN(7)
>> +#define MX25_ADCQ_CFG_REFN(sel)		((sel) << 2)
>> +#define MX25_ADCQ_CFG_REFN_XN		MX25_ADCQ_CFG_REFN(0)
>> +#define MX25_ADCQ_CFG_REFN_YN		MX25_ADCQ_CFG_REFN(1)
>> +#define MX25_ADCQ_CFG_REFN_NGND		MX25_ADCQ_CFG_REFN(2)
>> +#define MX25_ADCQ_CFG_REFN_NGND2	MX25_ADCQ_CFG_REFN(3)
>> +#define MX25_ADCQ_CFG_REFN_MASK		GENMASK(3, 2)
>> +#define MX25_ADCQ_CFG_PENIACK		(1 << 1)
>> +
>> +#endif  /* _LINUX_INCLUDE_MFD_IMX25_TSADC_H_ */
>>
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH v8 4/8] mfd: fsl imx25 Touchscreen ADC driver
@ 2015-11-21 17:26         ` Jonathan Cameron
  0 siblings, 0 replies; 67+ messages in thread
From: Jonathan Cameron @ 2015-11-21 17:26 UTC (permalink / raw)
  To: Markus Pargmann, Shawn Guo, Dmitry Torokhov, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam, Juergen Borleis

On 21/11/15 17:02, Jonathan Cameron wrote:
> On 16/11/15 12:01, Markus Pargmann wrote:
>> This is the core driver for imx25 touchscreen/adc driver. The module
>> has one shared ADC and two different conversion queues which use the
>> ADC. The two queues are identical. Both can be used for general purpose
>> ADC but one is meant to be used for touchscreens.
>>
>> This driver is the core which manages the central components and
>> registers of the TSC/ADC unit. It manages the IRQs and forwards them to
>> the correct components.
>>
>> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
>> Signed-off-by: Denis Carikli <denis@eukrea.com>
>>
>> [ensure correct ADC clock depending on the IPG clock]
>> Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
> Looks good to me - one query on meaning of a comment inline.
> 
> Acked-by: Jonathan Cameron <jic23@kernel.org>
> 
> I'm taking the view this series wants to go through the MFD tree
> but don't mind if it goes through IIO or input instead
> if there is a good reason to do so.
> 
> Jonathan
>> ---
>>
>> Notes:
>>     Changes in v7:
>>      - Cleanup bit defines in header files to be more readable
>>      - Fix irq check to return with an error for irq <= 0
>>      - Add COMPILE_TEST in Kconfig file
>>     
>>     Changes in v5:
>>      - Remove ifdef CONFIG_OF as this driver is only for DT usage
>>      - Remove module owner
>>      - Add Kconfig dependencies ARCH_MX25 and OF
>>     
>>     @Jonathan Cameron:
>>     I left your acked-by on the patch as these were small changes. If it should be
>>     removed, please say so. Thanks
>>
>>  drivers/mfd/Kconfig             |   9 ++
>>  drivers/mfd/Makefile            |   2 +
>>  drivers/mfd/fsl-imx25-tsadc.c   | 204 ++++++++++++++++++++++++++++++++++++++++
>>  include/linux/mfd/imx25-tsadc.h | 140 +++++++++++++++++++++++++++
>>  4 files changed, 355 insertions(+)
>>  create mode 100644 drivers/mfd/fsl-imx25-tsadc.c
>>  create mode 100644 include/linux/mfd/imx25-tsadc.h
>>
>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>> index 4d92df6ef9fe..4222e202ad2b 100644
>> --- a/drivers/mfd/Kconfig
>> +++ b/drivers/mfd/Kconfig
>> @@ -271,6 +271,15 @@ config MFD_MC13XXX_I2C
>>  	help
>>  	  Select this if your MC13xxx is connected via an I2C bus.
>>  
>> +config MFD_MX25_TSADC
>> +	tristate "Freescale i.MX25 integrated Touchscreen and ADC unit"
>> +	select REGMAP_MMIO
>> +	depends on (SOC_IMX25 && OF) || COMPILE_TEST
>> +	help
>> +	  Enable support for the integrated Touchscreen and ADC unit of the
>> +	  i.MX25 processors. They consist of a conversion queue for general
>> +	  purpose ADC and a queue for Touchscreens.
>> +
>>  config MFD_HI6421_PMIC
>>  	tristate "HiSilicon Hi6421 PMU/Codec IC"
>>  	depends on OF
>> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
>> index a8b76b81b467..5741be88c173 100644
>> --- a/drivers/mfd/Makefile
>> +++ b/drivers/mfd/Makefile
>> @@ -81,6 +81,8 @@ obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
>>  obj-$(CONFIG_MFD_TWL4030_AUDIO)	+= twl4030-audio.o
>>  obj-$(CONFIG_TWL6040_CORE)	+= twl6040.o
>>  
>> +obj-$(CONFIG_MFD_MX25_TSADC)	+= fsl-imx25-tsadc.o
>> +
>>  obj-$(CONFIG_MFD_MC13XXX)	+= mc13xxx-core.o
>>  obj-$(CONFIG_MFD_MC13XXX_SPI)	+= mc13xxx-spi.o
>>  obj-$(CONFIG_MFD_MC13XXX_I2C)	+= mc13xxx-i2c.o
>> diff --git a/drivers/mfd/fsl-imx25-tsadc.c b/drivers/mfd/fsl-imx25-tsadc.c
>> new file mode 100644
>> index 000000000000..e67d5ca81e10
>> --- /dev/null
>> +++ b/drivers/mfd/fsl-imx25-tsadc.c
>> @@ -0,0 +1,204 @@
>> +/*
>> + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
>> + *
>> + * This program is free software; you can redistribute it and/or modify it under
>> + * the terms of the GNU General Public License version 2 as published by the
>> + * Free Software Foundation.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +#include <linux/irqdesc.h>
>> +#include <linux/irqdomain.h>
>> +#include <linux/irq.h>
>> +#include <linux/mfd/imx25-tsadc.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +
>> +static struct regmap_config mx25_tsadc_regmap_config = {
>> +	.fast_io = true,
>> +	.max_register = 8,
>> +	.reg_bits = 32,
>> +	.val_bits = 32,
>> +	.reg_stride = 4,
>> +};
>> +
>> +static void mx25_tsadc_irq_handler(struct irq_desc *desc)
>> +{
>> +	struct mx25_tsadc *tsadc = irq_desc_get_handler_data(desc);
>> +	struct irq_chip *chip = irq_desc_get_chip(desc);
>> +	u32 status;
>> +
>> +	chained_irq_enter(chip, desc);
>> +
>> +	regmap_read(tsadc->regs, MX25_TSC_TGSR, &status);
>> +
>> +	if (status & MX25_TGSR_GCQ_INT)
>> +		generic_handle_irq(irq_find_mapping(tsadc->domain, 1));
>> +
>> +	if (status & MX25_TGSR_TCQ_INT)
>> +		generic_handle_irq(irq_find_mapping(tsadc->domain, 0));
>> +
>> +	chained_irq_exit(chip, desc);
>> +}
>> +
>> +static int mx25_tsadc_domain_map(struct irq_domain *d, unsigned int irq,
>> +				 irq_hw_number_t hwirq)
>> +{
>> +	struct mx25_tsadc *tsadc = d->host_data;
>> +
>> +	irq_set_chip_data(irq, tsadc);
>> +	irq_set_chip_and_handler(irq, &dummy_irq_chip,
>> +				 handle_level_irq);
>> +	irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct irq_domain_ops mx25_tsadc_domain_ops = {
>> +	.map = mx25_tsadc_domain_map,
>> +	.xlate = irq_domain_xlate_onecell,
>> +};
>> +
>> +static int mx25_tsadc_setup_irq(struct platform_device *pdev,
>> +				struct mx25_tsadc *tsadc)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct device_node *np = dev->of_node;
>> +	int irq;
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (irq <= 0) {
>> +		dev_err(dev, "Failed to get irq\n");
>> +		return irq;
>> +	}
>> +
>> +	tsadc->domain = irq_domain_add_simple(np, 2, 0, &mx25_tsadc_domain_ops,
>> +					      tsadc);
>> +	if (!tsadc->domain) {
>> +		dev_err(dev, "Failed to add irq domain\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	irq_set_chained_handler(irq, mx25_tsadc_irq_handler);
>> +	irq_set_handler_data(irq, tsadc);
>> +
>> +	return 0;
>> +}
>> +
>> +static void mx25_tsadc_setup_clk(struct platform_device *pdev,
>> +				 struct mx25_tsadc *tsadc)
>> +{
>> +	unsigned clk_div;
>> +
>> +	/*
>> +	 * According to the datasheet the ADC clock should never
>> +	 * exceed 1,75 MHz. Base clock is the IPG and the ADC unit uses
>> +	 * a funny clock divider. To keep the time constant the ADC needs to do
>> +	 * one conversion, adapt the ADC internal clock divider to
>> +	 * the available IPG
> I'm not entirely following this explanation...
> maybe
> 
> ...one conversion, then adapt the ADC...
> 
>> +	 */
>> +
>> +	dev_dbg(&pdev->dev, "Found master clock at %lu Hz\n",
>> +		clk_get_rate(tsadc->clk));
>> +
>> +	clk_div = DIV_ROUND_UP(clk_get_rate(tsadc->clk), 1750000);
>> +	dev_dbg(&pdev->dev, "Setting up ADC clock divider to %u\n", clk_div);
>> +
>> +	/* adc clock = IPG clock / (2 * div + 2) */
>> +	clk_div -= 2;
>> +	clk_div /= 2;
>> +
>> +	/*
>> +	 * the ADC clock divider changes its behaviour when values below 4
>> +	 * are used: it is fixed to "/ 10" in this case
>> +	 */
>> +	clk_div = max_t(unsigned, 4, clk_div);
>> +
>> +	dev_dbg(&pdev->dev, "Resulting ADC conversion clock at %lu Hz\n",
>> +		clk_get_rate(tsadc->clk) / (2 * clk_div + 2));
>> +
>> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR,
>> +			   MX25_TGCR_ADCCLKCFG(0x1f),
>> +			   MX25_TGCR_ADCCLKCFG(clk_div));
>> +}
>> +
>> +static int mx25_tsadc_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct device_node *np = dev->of_node;
>> +	struct mx25_tsadc *tsadc;
>> +	struct resource *res;
>> +	int ret;
>> +	void __iomem *iomem;
>> +
>> +	tsadc = devm_kzalloc(dev, sizeof(*tsadc), GFP_KERNEL);
>> +	if (!tsadc)
>> +		return -ENOMEM;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	iomem = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR(iomem))
>> +		return PTR_ERR(iomem);
>> +
>> +	tsadc->regs = devm_regmap_init_mmio(dev, iomem,
>> +					    &mx25_tsadc_regmap_config);
>> +	if (IS_ERR(tsadc->regs)) {
>> +		dev_err(dev, "Failed to initialize regmap\n");
>> +		return PTR_ERR(tsadc->regs);
>> +	}
>> +
>> +	tsadc->clk = devm_clk_get(dev, "ipg");
>> +	if (IS_ERR(tsadc->clk)) {
>> +		dev_err(dev, "Failed to get ipg clock\n");
>> +		return PTR_ERR(tsadc->clk);
>> +	}
>> +
>> +	/* setup clock according to the datasheet */
>> +	mx25_tsadc_setup_clk(pdev, tsadc);
>> +
>> +	/* Enable clock and reset the component */
>> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_CLK_EN,
>> +			   MX25_TGCR_CLK_EN);
>> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_TSC_RST,
>> +			   MX25_TGCR_TSC_RST);
>> +
>> +	/* Setup powersaving mode, but enable internal reference voltage */
>> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_POWERMODE_MASK,
>> +			   MX25_TGCR_POWERMODE_SAVE);
>> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_INTREFEN,
>> +			   MX25_TGCR_INTREFEN);
>> +
>> +	ret = mx25_tsadc_setup_irq(pdev, tsadc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	platform_set_drvdata(pdev, tsadc);
>> +
>> +	of_platform_populate(np, NULL, NULL, dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id mx25_tsadc_ids[] = {
>> +	{ .compatible = "fsl,imx25-tsadc" },
>> +	{ /* Sentinel */ }
>> +};
>> +
>> +static struct platform_driver mx25_tsadc_driver = {
>> +	.driver = {
>> +		.name = "mx25-tsadc",
>> +		.of_match_table = of_match_ptr(mx25_tsadc_ids),
>> +	},
>> +	.probe = mx25_tsadc_probe,
>> +};
>> +module_platform_driver(mx25_tsadc_driver);
>> +
>> +MODULE_DESCRIPTION("MFD for ADC/TSC for Freescale mx25");
>> +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:mx25-tsadc");
>> diff --git a/include/linux/mfd/imx25-tsadc.h b/include/linux/mfd/imx25-tsadc.h
>> new file mode 100644
>> index 000000000000..7fe4b8c3baac
>> --- /dev/null
>> +++ b/include/linux/mfd/imx25-tsadc.h
>> @@ -0,0 +1,140 @@
>> +#ifndef _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
>> +#define _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
>> +
>> +struct regmap;
>> +struct clk;
>> +
>> +struct mx25_tsadc {
>> +	struct regmap *regs;
>> +	struct irq_domain *domain;
>> +	struct clk *clk;
>> +};
>> +
>> +#define MX25_TSC_TGCR			0x00
>> +#define MX25_TSC_TGSR			0x04
>> +#define MX25_TSC_TICR			0x08
>> +
>> +/* The same register layout for TC and GC queue */
>> +#define MX25_ADCQ_FIFO			0x00
>> +#define MX25_ADCQ_CR			0x04
>> +#define MX25_ADCQ_SR			0x08
>> +#define MX25_ADCQ_MR			0x0c
>> +#define MX25_ADCQ_ITEM_7_0		0x20
>> +#define MX25_ADCQ_ITEM_15_8		0x24
>> +#define MX25_ADCQ_CFG(n)		(0x40 + ((n) * 0x4))
>> +
>> +#define MX25_ADCQ_MR_MASK		0xffffffff
>> +
>> +/* TGCR */
>> +#define MX25_TGCR_PDBTIME(x)		((x) << 25)
>> +#define MX25_TGCR_PDBTIME_MASK		GENMASK(31, 25)
>> +#define MX25_TGCR_PDBEN			BIT(24)
>> +#define MX25_TGCR_PDEN			BIT(23)
>> +#define MX25_TGCR_ADCCLKCFG(x)		((x) << 16)
>> +#define MX25_TGCR_GET_ADCCLK(x)		(((x) >> 16) & 0x1f)
>> +#define MX25_TGCR_INTREFEN		BIT(10)
>> +#define MX25_TGCR_POWERMODE_MASK	GENMASK(9, 8)
>> +#define MX25_TGCR_POWERMODE_SAVE	(1 << 8)
>> +#define MX25_TGCR_POWERMODE_ON		(2 << 8)
>> +#define MX25_TGCR_STLC			BIT(5)
>> +#define MX25_TGCR_SLPC			BIT(4)
>> +#define MX25_TGCR_FUNC_RST		BIT(2)
>> +#define MX25_TGCR_TSC_RST		BIT(1)
>> +#define MX25_TGCR_CLK_EN		BIT(0)
>> +
>> +/* TGSR */
>> +#define MX25_TGSR_SLP_INT		BIT(2)
>> +#define MX25_TGSR_GCQ_INT		BIT(1)
>> +#define MX25_TGSR_TCQ_INT		BIT(0)
>> +
>> +/* ADCQ_ITEM_* */
>> +#define _MX25_ADCQ_ITEM(item, x)	((x) << ((item) * 4))
>> +#define MX25_ADCQ_ITEM(item, x)		((item) >= 8 ? \
>> +		_MX25_ADCQ_ITEM((item) - 8, (x)) : _MX25_ADCQ_ITEM((item), (x)))
>> +
>> +/* ADCQ_FIFO (TCQFIFO and GCQFIFO) */
>> +#define MX25_ADCQ_FIFO_DATA(x)		(((x) >> 4) & 0xfff)
>> +#define MX25_ADCQ_FIFO_ID(x)		((x) & 0xf)
>> +
>> +/* ADCQ_CR (TCQR and GCQR) */
>> +#define MX25_ADCQ_CR_PDCFG_LEVEL	BIT(19)
>> +#define MX25_ADCQ_CR_PDMSK		BIT(18)
>> +#define MX25_ADCQ_CR_FRST		BIT(17)
>> +#define MX25_ADCQ_CR_QRST		BIT(16)
>> +#define MX25_ADCQ_CR_RWAIT_MASK		GENMASK(15, 12)
>> +#define MX25_ADCQ_CR_RWAIT(x)		((x) << 12)
>> +#define MX25_ADCQ_CR_WMRK_MASK		GENMASK(11, 8)
>> +#define MX25_ADCQ_CR_WMRK(x)		((x) << 8)
>> +#define MX25_ADCQ_CR_LITEMID_MASK	(0xf << 4)
>> +#define MX25_ADCQ_CR_LITEMID(x)		((x) << 4)
>> +#define MX25_ADCQ_CR_RPT		BIT(3)
>> +#define MX25_ADCQ_CR_FQS		BIT(2)
>> +#define MX25_ADCQ_CR_QSM_MASK		GENMASK(1, 0)
>> +#define MX25_ADCQ_CR_QSM_PD		0x1
>> +#define MX25_ADCQ_CR_QSM_FQS		0x2
>> +#define MX25_ADCQ_CR_QSM_FQS_PD		0x3
>> +
>> +/* ADCQ_SR (TCQSR and GCQSR) */
>> +#define MX25_ADCQ_SR_FDRY		BIT(15)
>> +#define MX25_ADCQ_SR_FULL		BIT(14)
>> +#define MX25_ADCQ_SR_EMPT		BIT(13)
>> +#define MX25_ADCQ_SR_FDN(x)		(((x) >> 8) & 0x1f)
>> +#define MX25_ADCQ_SR_FRR		BIT(6)
>> +#define MX25_ADCQ_SR_FUR		BIT(5)
>> +#define MX25_ADCQ_SR_FOR		BIT(4)
>> +#define MX25_ADCQ_SR_EOQ		BIT(1)
>> +#define MX25_ADCQ_SR_PD			BIT(0)
>> +
>> +/* ADCQ_MR (TCQMR and GCQMR) */
>> +#define MX25_ADCQ_MR_FDRY_DMA		BIT(31)
>> +#define MX25_ADCQ_MR_FER_DMA		BIT(22)
>> +#define MX25_ADCQ_MR_FUR_DMA		BIT(21)
>> +#define MX25_ADCQ_MR_FOR_DMA		BIT(20)
>> +#define MX25_ADCQ_MR_EOQ_DMA		BIT(17)
>> +#define MX25_ADCQ_MR_PD_DMA		BIT(16)
>> +#define MX25_ADCQ_MR_FDRY_IRQ		BIT(15)
>> +#define MX25_ADCQ_MR_FER_IRQ		BIT(6)
>> +#define MX25_ADCQ_MR_FUR_IRQ		BIT(5)
>> +#define MX25_ADCQ_MR_FOR_IRQ		BIT(4)
>> +#define MX25_ADCQ_MR_EOQ_IRQ		BIT(1)
>> +#define MX25_ADCQ_MR_PD_IRQ		BIT(0)
>> +
>> +/* ADCQ_CFG (TICR, TCC0-7,GCC0-7) */
>> +#define MX25_ADCQ_CFG_SETTLING_TIME(x)	((x) << 24)
>> +#define MX25_ADCQ_CFG_IGS		(1 << 20)
>> +#define MX25_ADCQ_CFG_NOS_MASK		GENMASK(19, 16)
>> +#define MX25_ADCQ_CFG_NOS(x)		(((x) - 1) << 16)
>> +#define MX25_ADCQ_CFG_WIPER		(1 << 15)
>> +#define MX25_ADCQ_CFG_YNLR		(1 << 14)
>> +#define MX25_ADCQ_CFG_YPLL_HIGH		(0 << 12)
>> +#define MX25_ADCQ_CFG_YPLL_OFF		(1 << 12)
>> +#define MX25_ADCQ_CFG_YPLL_LOW		(3 << 12)
>> +#define MX25_ADCQ_CFG_XNUR_HIGH		(0 << 10)
>> +#define MX25_ADCQ_CFG_XNUR_OFF		(1 << 10)
>> +#define MX25_ADCQ_CFG_XNUR_LOW		(3 << 10)
>> +#define MX25_ADCQ_CFG_XPUL_HIGH		(0 << 9)
>> +#define MX25_ADCQ_CFG_XPUL_OFF		(1 << 9)
>> +#define MX25_ADCQ_CFG_REFP(sel)		((sel) << 7)
>> +#define MX25_ADCQ_CFG_REFP_YP		MX25_ADCQ_CFG_REFP(0)
>> +#define MX25_ADCQ_CFG_REFP_XP		MX25_ADCQ_CFG_REFP(1)
>> +#define MX25_ADCQ_CFG_REFP_EXT		MX25_ADCQ_CFG_REFP(2)
>> +#define MX25_ADCQ_CFG_REFP_INT		MX25_ADCQ_CFG_REFP(3)
>> +#define MX25_ADCQ_CFG_REFP_MASK		GENMASK(8, 7)
>> +#define MX25_ADCQ_CFG_IN(sel)		((sel) << 4)
>> +#define MX25_ADCQ_CFG_IN_XP		MX25_ADCQ_CFG_IN(0)
>> +#define MX25_ADCQ_CFG_IN_YP		MX25_ADCQ_CFG_IN(1)
>> +#define MX25_ADCQ_CFG_IN_XN		MX25_ADCQ_CFG_IN(2)
>> +#define MX25_ADCQ_CFG_IN_YN		MX25_ADCQ_CFG_IN(3)
>> +#define MX25_ADCQ_CFG_IN_WIPER		MX25_ADCQ_CFG_IN(4)
>> +#define MX25_ADCQ_CFG_IN_AUX0		MX25_ADCQ_CFG_IN(5)
>> +#define MX25_ADCQ_CFG_IN_AUX1		MX25_ADCQ_CFG_IN(6)
>> +#define MX25_ADCQ_CFG_IN_AUX2		MX25_ADCQ_CFG_IN(7)
>> +#define MX25_ADCQ_CFG_REFN(sel)		((sel) << 2)
>> +#define MX25_ADCQ_CFG_REFN_XN		MX25_ADCQ_CFG_REFN(0)
>> +#define MX25_ADCQ_CFG_REFN_YN		MX25_ADCQ_CFG_REFN(1)
>> +#define MX25_ADCQ_CFG_REFN_NGND		MX25_ADCQ_CFG_REFN(2)
>> +#define MX25_ADCQ_CFG_REFN_NGND2	MX25_ADCQ_CFG_REFN(3)
>> +#define MX25_ADCQ_CFG_REFN_MASK		GENMASK(3, 2)
>> +#define MX25_ADCQ_CFG_PENIACK		(1 << 1)
>> +
>> +#endif  /* _LINUX_INCLUDE_MFD_IMX25_TSADC_H_ */
>>
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


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

* [PATCH v8 4/8] mfd: fsl imx25 Touchscreen ADC driver
@ 2015-11-21 17:26         ` Jonathan Cameron
  0 siblings, 0 replies; 67+ messages in thread
From: Jonathan Cameron @ 2015-11-21 17:26 UTC (permalink / raw)
  To: linux-arm-kernel

On 21/11/15 17:02, Jonathan Cameron wrote:
> On 16/11/15 12:01, Markus Pargmann wrote:
>> This is the core driver for imx25 touchscreen/adc driver. The module
>> has one shared ADC and two different conversion queues which use the
>> ADC. The two queues are identical. Both can be used for general purpose
>> ADC but one is meant to be used for touchscreens.
>>
>> This driver is the core which manages the central components and
>> registers of the TSC/ADC unit. It manages the IRQs and forwards them to
>> the correct components.
>>
>> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
>> Signed-off-by: Denis Carikli <denis@eukrea.com>
>>
>> [ensure correct ADC clock depending on the IPG clock]
>> Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
> Looks good to me - one query on meaning of a comment inline.
> 
> Acked-by: Jonathan Cameron <jic23@kernel.org>
> 
> I'm taking the view this series wants to go through the MFD tree
> but don't mind if it goes through IIO or input instead
> if there is a good reason to do so.
> 
> Jonathan
>> ---
>>
>> Notes:
>>     Changes in v7:
>>      - Cleanup bit defines in header files to be more readable
>>      - Fix irq check to return with an error for irq <= 0
>>      - Add COMPILE_TEST in Kconfig file
>>     
>>     Changes in v5:
>>      - Remove ifdef CONFIG_OF as this driver is only for DT usage
>>      - Remove module owner
>>      - Add Kconfig dependencies ARCH_MX25 and OF
>>     
>>     @Jonathan Cameron:
>>     I left your acked-by on the patch as these were small changes. If it should be
>>     removed, please say so. Thanks
>>
>>  drivers/mfd/Kconfig             |   9 ++
>>  drivers/mfd/Makefile            |   2 +
>>  drivers/mfd/fsl-imx25-tsadc.c   | 204 ++++++++++++++++++++++++++++++++++++++++
>>  include/linux/mfd/imx25-tsadc.h | 140 +++++++++++++++++++++++++++
>>  4 files changed, 355 insertions(+)
>>  create mode 100644 drivers/mfd/fsl-imx25-tsadc.c
>>  create mode 100644 include/linux/mfd/imx25-tsadc.h
>>
>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>> index 4d92df6ef9fe..4222e202ad2b 100644
>> --- a/drivers/mfd/Kconfig
>> +++ b/drivers/mfd/Kconfig
>> @@ -271,6 +271,15 @@ config MFD_MC13XXX_I2C
>>  	help
>>  	  Select this if your MC13xxx is connected via an I2C bus.
>>  
>> +config MFD_MX25_TSADC
>> +	tristate "Freescale i.MX25 integrated Touchscreen and ADC unit"
>> +	select REGMAP_MMIO
>> +	depends on (SOC_IMX25 && OF) || COMPILE_TEST
>> +	help
>> +	  Enable support for the integrated Touchscreen and ADC unit of the
>> +	  i.MX25 processors. They consist of a conversion queue for general
>> +	  purpose ADC and a queue for Touchscreens.
>> +
>>  config MFD_HI6421_PMIC
>>  	tristate "HiSilicon Hi6421 PMU/Codec IC"
>>  	depends on OF
>> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
>> index a8b76b81b467..5741be88c173 100644
>> --- a/drivers/mfd/Makefile
>> +++ b/drivers/mfd/Makefile
>> @@ -81,6 +81,8 @@ obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
>>  obj-$(CONFIG_MFD_TWL4030_AUDIO)	+= twl4030-audio.o
>>  obj-$(CONFIG_TWL6040_CORE)	+= twl6040.o
>>  
>> +obj-$(CONFIG_MFD_MX25_TSADC)	+= fsl-imx25-tsadc.o
>> +
>>  obj-$(CONFIG_MFD_MC13XXX)	+= mc13xxx-core.o
>>  obj-$(CONFIG_MFD_MC13XXX_SPI)	+= mc13xxx-spi.o
>>  obj-$(CONFIG_MFD_MC13XXX_I2C)	+= mc13xxx-i2c.o
>> diff --git a/drivers/mfd/fsl-imx25-tsadc.c b/drivers/mfd/fsl-imx25-tsadc.c
>> new file mode 100644
>> index 000000000000..e67d5ca81e10
>> --- /dev/null
>> +++ b/drivers/mfd/fsl-imx25-tsadc.c
>> @@ -0,0 +1,204 @@
>> +/*
>> + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
>> + *
>> + * This program is free software; you can redistribute it and/or modify it under
>> + * the terms of the GNU General Public License version 2 as published by the
>> + * Free Software Foundation.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +#include <linux/irqdesc.h>
>> +#include <linux/irqdomain.h>
>> +#include <linux/irq.h>
>> +#include <linux/mfd/imx25-tsadc.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +
>> +static struct regmap_config mx25_tsadc_regmap_config = {
>> +	.fast_io = true,
>> +	.max_register = 8,
>> +	.reg_bits = 32,
>> +	.val_bits = 32,
>> +	.reg_stride = 4,
>> +};
>> +
>> +static void mx25_tsadc_irq_handler(struct irq_desc *desc)
>> +{
>> +	struct mx25_tsadc *tsadc = irq_desc_get_handler_data(desc);
>> +	struct irq_chip *chip = irq_desc_get_chip(desc);
>> +	u32 status;
>> +
>> +	chained_irq_enter(chip, desc);
>> +
>> +	regmap_read(tsadc->regs, MX25_TSC_TGSR, &status);
>> +
>> +	if (status & MX25_TGSR_GCQ_INT)
>> +		generic_handle_irq(irq_find_mapping(tsadc->domain, 1));
>> +
>> +	if (status & MX25_TGSR_TCQ_INT)
>> +		generic_handle_irq(irq_find_mapping(tsadc->domain, 0));
>> +
>> +	chained_irq_exit(chip, desc);
>> +}
>> +
>> +static int mx25_tsadc_domain_map(struct irq_domain *d, unsigned int irq,
>> +				 irq_hw_number_t hwirq)
>> +{
>> +	struct mx25_tsadc *tsadc = d->host_data;
>> +
>> +	irq_set_chip_data(irq, tsadc);
>> +	irq_set_chip_and_handler(irq, &dummy_irq_chip,
>> +				 handle_level_irq);
>> +	irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct irq_domain_ops mx25_tsadc_domain_ops = {
>> +	.map = mx25_tsadc_domain_map,
>> +	.xlate = irq_domain_xlate_onecell,
>> +};
>> +
>> +static int mx25_tsadc_setup_irq(struct platform_device *pdev,
>> +				struct mx25_tsadc *tsadc)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct device_node *np = dev->of_node;
>> +	int irq;
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (irq <= 0) {
>> +		dev_err(dev, "Failed to get irq\n");
>> +		return irq;
>> +	}
>> +
>> +	tsadc->domain = irq_domain_add_simple(np, 2, 0, &mx25_tsadc_domain_ops,
>> +					      tsadc);
>> +	if (!tsadc->domain) {
>> +		dev_err(dev, "Failed to add irq domain\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	irq_set_chained_handler(irq, mx25_tsadc_irq_handler);
>> +	irq_set_handler_data(irq, tsadc);
>> +
>> +	return 0;
>> +}
>> +
>> +static void mx25_tsadc_setup_clk(struct platform_device *pdev,
>> +				 struct mx25_tsadc *tsadc)
>> +{
>> +	unsigned clk_div;
>> +
>> +	/*
>> +	 * According to the datasheet the ADC clock should never
>> +	 * exceed 1,75 MHz. Base clock is the IPG and the ADC unit uses
>> +	 * a funny clock divider. To keep the time constant the ADC needs to do
>> +	 * one conversion, adapt the ADC internal clock divider to
>> +	 * the available IPG
> I'm not entirely following this explanation...
> maybe
> 
> ...one conversion, then adapt the ADC...
> 
>> +	 */
>> +
>> +	dev_dbg(&pdev->dev, "Found master clock at %lu Hz\n",
>> +		clk_get_rate(tsadc->clk));
>> +
>> +	clk_div = DIV_ROUND_UP(clk_get_rate(tsadc->clk), 1750000);
>> +	dev_dbg(&pdev->dev, "Setting up ADC clock divider to %u\n", clk_div);
>> +
>> +	/* adc clock = IPG clock / (2 * div + 2) */
>> +	clk_div -= 2;
>> +	clk_div /= 2;
>> +
>> +	/*
>> +	 * the ADC clock divider changes its behaviour when values below 4
>> +	 * are used: it is fixed to "/ 10" in this case
>> +	 */
>> +	clk_div = max_t(unsigned, 4, clk_div);
>> +
>> +	dev_dbg(&pdev->dev, "Resulting ADC conversion clock at %lu Hz\n",
>> +		clk_get_rate(tsadc->clk) / (2 * clk_div + 2));
>> +
>> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR,
>> +			   MX25_TGCR_ADCCLKCFG(0x1f),
>> +			   MX25_TGCR_ADCCLKCFG(clk_div));
>> +}
>> +
>> +static int mx25_tsadc_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct device_node *np = dev->of_node;
>> +	struct mx25_tsadc *tsadc;
>> +	struct resource *res;
>> +	int ret;
>> +	void __iomem *iomem;
>> +
>> +	tsadc = devm_kzalloc(dev, sizeof(*tsadc), GFP_KERNEL);
>> +	if (!tsadc)
>> +		return -ENOMEM;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	iomem = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR(iomem))
>> +		return PTR_ERR(iomem);
>> +
>> +	tsadc->regs = devm_regmap_init_mmio(dev, iomem,
>> +					    &mx25_tsadc_regmap_config);
>> +	if (IS_ERR(tsadc->regs)) {
>> +		dev_err(dev, "Failed to initialize regmap\n");
>> +		return PTR_ERR(tsadc->regs);
>> +	}
>> +
>> +	tsadc->clk = devm_clk_get(dev, "ipg");
>> +	if (IS_ERR(tsadc->clk)) {
>> +		dev_err(dev, "Failed to get ipg clock\n");
>> +		return PTR_ERR(tsadc->clk);
>> +	}
>> +
>> +	/* setup clock according to the datasheet */
>> +	mx25_tsadc_setup_clk(pdev, tsadc);
>> +
>> +	/* Enable clock and reset the component */
>> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_CLK_EN,
>> +			   MX25_TGCR_CLK_EN);
>> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_TSC_RST,
>> +			   MX25_TGCR_TSC_RST);
>> +
>> +	/* Setup powersaving mode, but enable internal reference voltage */
>> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_POWERMODE_MASK,
>> +			   MX25_TGCR_POWERMODE_SAVE);
>> +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_INTREFEN,
>> +			   MX25_TGCR_INTREFEN);
>> +
>> +	ret = mx25_tsadc_setup_irq(pdev, tsadc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	platform_set_drvdata(pdev, tsadc);
>> +
>> +	of_platform_populate(np, NULL, NULL, dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id mx25_tsadc_ids[] = {
>> +	{ .compatible = "fsl,imx25-tsadc" },
>> +	{ /* Sentinel */ }
>> +};
>> +
>> +static struct platform_driver mx25_tsadc_driver = {
>> +	.driver = {
>> +		.name = "mx25-tsadc",
>> +		.of_match_table = of_match_ptr(mx25_tsadc_ids),
>> +	},
>> +	.probe = mx25_tsadc_probe,
>> +};
>> +module_platform_driver(mx25_tsadc_driver);
>> +
>> +MODULE_DESCRIPTION("MFD for ADC/TSC for Freescale mx25");
>> +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:mx25-tsadc");
>> diff --git a/include/linux/mfd/imx25-tsadc.h b/include/linux/mfd/imx25-tsadc.h
>> new file mode 100644
>> index 000000000000..7fe4b8c3baac
>> --- /dev/null
>> +++ b/include/linux/mfd/imx25-tsadc.h
>> @@ -0,0 +1,140 @@
>> +#ifndef _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
>> +#define _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
>> +
>> +struct regmap;
>> +struct clk;
>> +
>> +struct mx25_tsadc {
>> +	struct regmap *regs;
>> +	struct irq_domain *domain;
>> +	struct clk *clk;
>> +};
>> +
>> +#define MX25_TSC_TGCR			0x00
>> +#define MX25_TSC_TGSR			0x04
>> +#define MX25_TSC_TICR			0x08
>> +
>> +/* The same register layout for TC and GC queue */
>> +#define MX25_ADCQ_FIFO			0x00
>> +#define MX25_ADCQ_CR			0x04
>> +#define MX25_ADCQ_SR			0x08
>> +#define MX25_ADCQ_MR			0x0c
>> +#define MX25_ADCQ_ITEM_7_0		0x20
>> +#define MX25_ADCQ_ITEM_15_8		0x24
>> +#define MX25_ADCQ_CFG(n)		(0x40 + ((n) * 0x4))
>> +
>> +#define MX25_ADCQ_MR_MASK		0xffffffff
>> +
>> +/* TGCR */
>> +#define MX25_TGCR_PDBTIME(x)		((x) << 25)
>> +#define MX25_TGCR_PDBTIME_MASK		GENMASK(31, 25)
>> +#define MX25_TGCR_PDBEN			BIT(24)
>> +#define MX25_TGCR_PDEN			BIT(23)
>> +#define MX25_TGCR_ADCCLKCFG(x)		((x) << 16)
>> +#define MX25_TGCR_GET_ADCCLK(x)		(((x) >> 16) & 0x1f)
>> +#define MX25_TGCR_INTREFEN		BIT(10)
>> +#define MX25_TGCR_POWERMODE_MASK	GENMASK(9, 8)
>> +#define MX25_TGCR_POWERMODE_SAVE	(1 << 8)
>> +#define MX25_TGCR_POWERMODE_ON		(2 << 8)
>> +#define MX25_TGCR_STLC			BIT(5)
>> +#define MX25_TGCR_SLPC			BIT(4)
>> +#define MX25_TGCR_FUNC_RST		BIT(2)
>> +#define MX25_TGCR_TSC_RST		BIT(1)
>> +#define MX25_TGCR_CLK_EN		BIT(0)
>> +
>> +/* TGSR */
>> +#define MX25_TGSR_SLP_INT		BIT(2)
>> +#define MX25_TGSR_GCQ_INT		BIT(1)
>> +#define MX25_TGSR_TCQ_INT		BIT(0)
>> +
>> +/* ADCQ_ITEM_* */
>> +#define _MX25_ADCQ_ITEM(item, x)	((x) << ((item) * 4))
>> +#define MX25_ADCQ_ITEM(item, x)		((item) >= 8 ? \
>> +		_MX25_ADCQ_ITEM((item) - 8, (x)) : _MX25_ADCQ_ITEM((item), (x)))
>> +
>> +/* ADCQ_FIFO (TCQFIFO and GCQFIFO) */
>> +#define MX25_ADCQ_FIFO_DATA(x)		(((x) >> 4) & 0xfff)
>> +#define MX25_ADCQ_FIFO_ID(x)		((x) & 0xf)
>> +
>> +/* ADCQ_CR (TCQR and GCQR) */
>> +#define MX25_ADCQ_CR_PDCFG_LEVEL	BIT(19)
>> +#define MX25_ADCQ_CR_PDMSK		BIT(18)
>> +#define MX25_ADCQ_CR_FRST		BIT(17)
>> +#define MX25_ADCQ_CR_QRST		BIT(16)
>> +#define MX25_ADCQ_CR_RWAIT_MASK		GENMASK(15, 12)
>> +#define MX25_ADCQ_CR_RWAIT(x)		((x) << 12)
>> +#define MX25_ADCQ_CR_WMRK_MASK		GENMASK(11, 8)
>> +#define MX25_ADCQ_CR_WMRK(x)		((x) << 8)
>> +#define MX25_ADCQ_CR_LITEMID_MASK	(0xf << 4)
>> +#define MX25_ADCQ_CR_LITEMID(x)		((x) << 4)
>> +#define MX25_ADCQ_CR_RPT		BIT(3)
>> +#define MX25_ADCQ_CR_FQS		BIT(2)
>> +#define MX25_ADCQ_CR_QSM_MASK		GENMASK(1, 0)
>> +#define MX25_ADCQ_CR_QSM_PD		0x1
>> +#define MX25_ADCQ_CR_QSM_FQS		0x2
>> +#define MX25_ADCQ_CR_QSM_FQS_PD		0x3
>> +
>> +/* ADCQ_SR (TCQSR and GCQSR) */
>> +#define MX25_ADCQ_SR_FDRY		BIT(15)
>> +#define MX25_ADCQ_SR_FULL		BIT(14)
>> +#define MX25_ADCQ_SR_EMPT		BIT(13)
>> +#define MX25_ADCQ_SR_FDN(x)		(((x) >> 8) & 0x1f)
>> +#define MX25_ADCQ_SR_FRR		BIT(6)
>> +#define MX25_ADCQ_SR_FUR		BIT(5)
>> +#define MX25_ADCQ_SR_FOR		BIT(4)
>> +#define MX25_ADCQ_SR_EOQ		BIT(1)
>> +#define MX25_ADCQ_SR_PD			BIT(0)
>> +
>> +/* ADCQ_MR (TCQMR and GCQMR) */
>> +#define MX25_ADCQ_MR_FDRY_DMA		BIT(31)
>> +#define MX25_ADCQ_MR_FER_DMA		BIT(22)
>> +#define MX25_ADCQ_MR_FUR_DMA		BIT(21)
>> +#define MX25_ADCQ_MR_FOR_DMA		BIT(20)
>> +#define MX25_ADCQ_MR_EOQ_DMA		BIT(17)
>> +#define MX25_ADCQ_MR_PD_DMA		BIT(16)
>> +#define MX25_ADCQ_MR_FDRY_IRQ		BIT(15)
>> +#define MX25_ADCQ_MR_FER_IRQ		BIT(6)
>> +#define MX25_ADCQ_MR_FUR_IRQ		BIT(5)
>> +#define MX25_ADCQ_MR_FOR_IRQ		BIT(4)
>> +#define MX25_ADCQ_MR_EOQ_IRQ		BIT(1)
>> +#define MX25_ADCQ_MR_PD_IRQ		BIT(0)
>> +
>> +/* ADCQ_CFG (TICR, TCC0-7,GCC0-7) */
>> +#define MX25_ADCQ_CFG_SETTLING_TIME(x)	((x) << 24)
>> +#define MX25_ADCQ_CFG_IGS		(1 << 20)
>> +#define MX25_ADCQ_CFG_NOS_MASK		GENMASK(19, 16)
>> +#define MX25_ADCQ_CFG_NOS(x)		(((x) - 1) << 16)
>> +#define MX25_ADCQ_CFG_WIPER		(1 << 15)
>> +#define MX25_ADCQ_CFG_YNLR		(1 << 14)
>> +#define MX25_ADCQ_CFG_YPLL_HIGH		(0 << 12)
>> +#define MX25_ADCQ_CFG_YPLL_OFF		(1 << 12)
>> +#define MX25_ADCQ_CFG_YPLL_LOW		(3 << 12)
>> +#define MX25_ADCQ_CFG_XNUR_HIGH		(0 << 10)
>> +#define MX25_ADCQ_CFG_XNUR_OFF		(1 << 10)
>> +#define MX25_ADCQ_CFG_XNUR_LOW		(3 << 10)
>> +#define MX25_ADCQ_CFG_XPUL_HIGH		(0 << 9)
>> +#define MX25_ADCQ_CFG_XPUL_OFF		(1 << 9)
>> +#define MX25_ADCQ_CFG_REFP(sel)		((sel) << 7)
>> +#define MX25_ADCQ_CFG_REFP_YP		MX25_ADCQ_CFG_REFP(0)
>> +#define MX25_ADCQ_CFG_REFP_XP		MX25_ADCQ_CFG_REFP(1)
>> +#define MX25_ADCQ_CFG_REFP_EXT		MX25_ADCQ_CFG_REFP(2)
>> +#define MX25_ADCQ_CFG_REFP_INT		MX25_ADCQ_CFG_REFP(3)
>> +#define MX25_ADCQ_CFG_REFP_MASK		GENMASK(8, 7)
>> +#define MX25_ADCQ_CFG_IN(sel)		((sel) << 4)
>> +#define MX25_ADCQ_CFG_IN_XP		MX25_ADCQ_CFG_IN(0)
>> +#define MX25_ADCQ_CFG_IN_YP		MX25_ADCQ_CFG_IN(1)
>> +#define MX25_ADCQ_CFG_IN_XN		MX25_ADCQ_CFG_IN(2)
>> +#define MX25_ADCQ_CFG_IN_YN		MX25_ADCQ_CFG_IN(3)
>> +#define MX25_ADCQ_CFG_IN_WIPER		MX25_ADCQ_CFG_IN(4)
>> +#define MX25_ADCQ_CFG_IN_AUX0		MX25_ADCQ_CFG_IN(5)
>> +#define MX25_ADCQ_CFG_IN_AUX1		MX25_ADCQ_CFG_IN(6)
>> +#define MX25_ADCQ_CFG_IN_AUX2		MX25_ADCQ_CFG_IN(7)
>> +#define MX25_ADCQ_CFG_REFN(sel)		((sel) << 2)
>> +#define MX25_ADCQ_CFG_REFN_XN		MX25_ADCQ_CFG_REFN(0)
>> +#define MX25_ADCQ_CFG_REFN_YN		MX25_ADCQ_CFG_REFN(1)
>> +#define MX25_ADCQ_CFG_REFN_NGND		MX25_ADCQ_CFG_REFN(2)
>> +#define MX25_ADCQ_CFG_REFN_NGND2	MX25_ADCQ_CFG_REFN(3)
>> +#define MX25_ADCQ_CFG_REFN_MASK		GENMASK(3, 2)
>> +#define MX25_ADCQ_CFG_PENIACK		(1 << 1)
>> +
>> +#endif  /* _LINUX_INCLUDE_MFD_IMX25_TSADC_H_ */
>>
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
  2015-11-16 12:01     ` Markus Pargmann
@ 2015-11-21 17:48       ` Jonathan Cameron
  -1 siblings, 0 replies; 67+ messages in thread
From: Jonathan Cameron @ 2015-11-21 17:48 UTC (permalink / raw)
  To: Markus Pargmann, Shawn Guo, Dmitry Torokhov, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam, Juergen Borleis

On 16/11/15 12:01, Markus Pargmann wrote:
> This is a driver for the imx25 ADC/TSC module. It controls the
> touchscreen conversion queue and creates a touchscreen input device.
> The driver currently only supports 4 wire touchscreens. The driver uses
> a simple conversion queue of precharge, touch detection, X measurement,
> Y measurement, precharge and another touch detection.
> 
> This driver uses the regmap from the parent to setup some touch specific
> settings in the core driver and setup a idle configuration with touch
> detection.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> Signed-off-by: Denis Carikli <denis@eukrea.com>
> 
> [fix clock's period calculation]
> [fix calculation of the 'settling' value]
> Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
I read this out of curiousity to see how you had handled the touchscreen
usecase of this hardware differently from the generic version.

Anyhow, a few little bits and pieces inline from me as a result

Jonathan
> ---
> 
> Notes:
>     Changes in v7:
>      - Moved clk_prepare_enable() and mx25_tcq_init() into mx25_tcq_open(). This
>        was done to be able to use devm_request_threaded_irq().
>      - Cleanup of the probe function through above change
>      - Removed mx25_tcq_remove(), not necessary now
> 
>  drivers/input/touchscreen/Kconfig         |   6 +
>  drivers/input/touchscreen/Makefile        |   1 +
>  drivers/input/touchscreen/fsl-imx25-tcq.c | 600 ++++++++++++++++++++++++++++++
>  3 files changed, 607 insertions(+)
>  create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index ae33da7ab51f..b44651d33080 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -811,6 +811,12 @@ config TOUCHSCREEN_USB_COMPOSITE
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called usbtouchscreen.
>  
> +config TOUCHSCREEN_MX25
> +	tristate "Freescale i.MX25 touchscreen input driver"
> +	depends on MFD_MX25_TSADC
> +	help
> +	  Enable support for touchscreen connected to your i.MX25.
> +
>  config TOUCHSCREEN_MC13783
>  	tristate "Freescale MC13783 touchscreen input driver"
>  	depends on MFD_MC13XXX
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index cbaa6abb08da..77a2ac54101a 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
>  obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
>  obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
> +obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
>  obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
> diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
> new file mode 100644
> index 000000000000..c833cd814972
> --- /dev/null
> +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
> @@ -0,0 +1,600 @@
> +/*
> + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + * Based on driver from 2011:
> + *   Juergen Beisert, Pengutronix <kernel@pengutronix.de>
> + *
> + * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
> + * connected to the imx25 ADC.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/imx25-tsadc.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +static const char mx25_tcq_name[] = "mx25-tcq";
> +
> +enum mx25_tcq_mode {
> +	MX25_TS_4WIRE,
> +};
> +
> +struct mx25_tcq_priv {
> +	struct regmap *regs;
> +	struct regmap *core_regs;
> +	struct input_dev *idev;
> +	enum mx25_tcq_mode mode;
> +	unsigned int pen_threshold;
> +	unsigned int sample_count;
> +	unsigned int expected_samples;
> +	unsigned int pen_debounce;
> +	unsigned int settling_time;
> +	struct clk *clk;
> +	int irq;
> +};
> +
> +static struct regmap_config mx25_tcq_regconfig = {
> +	.fast_io = true,
> +	.max_register = 0x5c,
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +};
> +
> +static const struct of_device_id mx25_tcq_ids[] = {
> +	{ .compatible = "fsl,imx25-tcq", },
> +	{ /* Sentinel */ }
> +};
> +
> +#define TSC_4WIRE_PRE_INDEX 0
> +#define TSC_4WIRE_X_INDEX 1
> +#define TSC_4WIRE_Y_INDEX 2
> +#define TSC_4WIRE_POST_INDEX 3
> +#define TSC_4WIRE_LEAVE 4
> +
> +#define MX25_TSC_DEF_THRESHOLD 80
> +#define TSC_MAX_SAMPLES 16
> +
> +#define MX25_TSC_REPEAT_WAIT 14
> +
> +enum mx25_adc_configurations {
> +	MX25_CFG_PRECHARGE = 0,
> +	MX25_CFG_TOUCH_DETECT,
> +	MX25_CFG_X_MEASUREMENT,
> +	MX25_CFG_Y_MEASUREMENT,
> +};
> +
> +#define MX25_PRECHARGE_VALUE (\
> +			MX25_ADCQ_CFG_YPLL_OFF | \
> +			MX25_ADCQ_CFG_XNUR_OFF | \
> +			MX25_ADCQ_CFG_XPUL_HIGH | \
> +			MX25_ADCQ_CFG_REFP_INT | \
> +			MX25_ADCQ_CFG_IN_XP | \
> +			MX25_ADCQ_CFG_REFN_NGND2 | \
> +			MX25_ADCQ_CFG_IGS)
> +
> +#define MX25_TOUCH_DETECT_VALUE (\
> +			MX25_ADCQ_CFG_YNLR | \
> +			MX25_ADCQ_CFG_YPLL_OFF | \
> +			MX25_ADCQ_CFG_XNUR_OFF | \
> +			MX25_ADCQ_CFG_XPUL_OFF | \
> +			MX25_ADCQ_CFG_REFP_INT | \
> +			MX25_ADCQ_CFG_IN_XP | \
> +			MX25_ADCQ_CFG_REFN_NGND2 | \
> +			MX25_ADCQ_CFG_PENIACK)
> +
> +static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv,
> +				   unsigned int settling_cnt)
> +{
> +	u32 precharge_cfg =
> +			MX25_PRECHARGE_VALUE |
> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> +	u32 touch_detect_cfg =
> +			MX25_TOUCH_DETECT_VALUE |
> +			MX25_ADCQ_CFG_NOS(1) |
> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> +
> +	regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg);
> +
> +	/* PRECHARGE */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE),
> +		     precharge_cfg);
> +
> +	/* TOUCH_DETECT */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT),
> +		     touch_detect_cfg);
> +
> +	/* X Measurement */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT),
> +		     MX25_ADCQ_CFG_YPLL_OFF |
> +		     MX25_ADCQ_CFG_XNUR_LOW |
> +		     MX25_ADCQ_CFG_XPUL_HIGH |
> +		     MX25_ADCQ_CFG_REFP_XP |
> +		     MX25_ADCQ_CFG_IN_YP |
> +		     MX25_ADCQ_CFG_REFN_XN |
> +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> +
> +	/* Y Measurement */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT),
> +		     MX25_ADCQ_CFG_YNLR |
> +		     MX25_ADCQ_CFG_YPLL_HIGH |
> +		     MX25_ADCQ_CFG_XNUR_OFF |
> +		     MX25_ADCQ_CFG_XPUL_OFF |
> +		     MX25_ADCQ_CFG_REFP_YP |
> +		     MX25_ADCQ_CFG_IN_XP |
> +		     MX25_ADCQ_CFG_REFN_YN |
> +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> +
> +	/* Enable the touch detection right now */
> +	regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg |
> +		     MX25_ADCQ_CFG_IGS);
> +}
> +
> +static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv,
> +				   unsigned settling_cnt, int *items)
> +{
> +	imx25_setup_queue_cfgs(priv, settling_cnt);
> +
> +	/* Setup the conversion queue */
> +	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
> +		     MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) |
> +		     MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) |
> +		     MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) |
> +		     MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) |
> +		     MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) |
> +		     MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT));
> +
> +	/*
> +	 * We measure X/Y with 'sample_count' number of samples and execute a
> +	 * touch detection twice, with 1 sample each
> +	 */
> +	priv->expected_samples = priv->sample_count * 2 + 2;
> +	*items = 6;
> +
> +	return 0;
> +}
> +
Another personal preference. I'd not bother wrapping these single line
calls up but rather just make them inline.  They don't in of
themselves add much to my mind.  Still this one is very much up to you
as far as I'm concerned.
> +static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK,
> +			   MX25_ADCQ_CR_PDMSK);
> +}
> +
> +static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0);
> +}
> +
> +static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ,
> +			   MX25_ADCQ_MR_FDRY_IRQ);
> +}
> +
> +static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0);
> +}
> +
> +static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_FQS,
> +			   MX25_ADCQ_CR_FQS);
> +}
> +
> +static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_FQS, 0);
> +}
> +
> +static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv)
> +{
> +	u32 tcqcr;
> +
> +	regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr);
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST,
> +			   MX25_ADCQ_CR_FRST);
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0);
> +	regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr);
> +}
> +
> +static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv)
> +{
> +	/* stop the queue from looping */
> +	mx25_tcq_force_queue_stop(priv);
> +
> +	/* for a clean touch detection, preload the X plane */
> +	regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE);
> +
> +	/* waste some time now to pre-load the X plate to high voltage */
> +	mx25_tcq_fifo_reset(priv);
> +
> +	/* re-enable the detection right now */
> +	regmap_write(priv->core_regs, MX25_TSC_TICR,
> +		     MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS);
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD,
> +			   MX25_ADCQ_SR_PD);
> +
> +	/* enable the pen down event to be a source for the interrupt */
> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0);
> +
> +	/* lets fire the next IRQ if someone touches the touchscreen */
> +	mx25_tcq_enable_touch_irq(priv);
> +}
> +
> +static int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,
> +					   u32 *sample_buf,
> +					   unsigned int samples)
> +{
> +	unsigned int x_pos = 0;
> +	unsigned int y_pos = 0;
> +	unsigned int touch_pre = 0;
> +	unsigned int touch_post = 0;
> +	unsigned int i;
> +	int ret = 0;
> +
> +	for (i = 0; i < samples; i++) {
> +		unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]);
> +		unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]);
> +
> +		switch (index) {
> +		case 1:
> +			touch_pre = val;
> +			break;
> +		case 2:
> +			x_pos = val;
> +			break;
> +		case 3:
> +			y_pos = val;
> +			break;
> +		case 5:
> +			touch_post = val;
> +			break;
> +		default:
> +			ret = -EINVAL;
> +			break;
> +		}
> +	}
> +
> +	if (ret == 0 && samples != 0) {
> +		/*
> +		 * only if both touch measures are below a threshold,
> +		 * the position is valid
> +		 */
> +		if (touch_pre < priv->pen_threshold &&
> +		    touch_post < priv->pen_threshold) {
> +			/* valid samples, generate a report */
> +			x_pos /= priv->sample_count;
> +			y_pos /= priv->sample_count;
> +			input_report_abs(priv->idev, ABS_X, x_pos);
> +			input_report_abs(priv->idev, ABS_Y, y_pos);
> +			input_report_key(priv->idev, BTN_TOUCH, 1);
> +			input_sync(priv->idev);
> +
> +			/* get next sample */
> +			mx25_tcq_enable_fifo_irq(priv);
> +		} else if (touch_pre >= priv->pen_threshold &&
> +			   touch_post >= priv->pen_threshold) {
> +			/*
> +			 * if both samples are invalid,
> +			 * generate a release report
> +			 */
> +			input_report_key(priv->idev, BTN_TOUCH, 0);
> +			input_sync(priv->idev);
> +			mx25_tcq_re_enable_touch_detection(priv);
> +		} else {
> +			/*
> +			 * if only one of both touch measurements are
> +			 * below the threshold, still some bouncing
> +			 * happens. Take additional samples in this
> +			 * case to be sure
> +			 */
> +			mx25_tcq_enable_fifo_irq(priv);
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
> +{
> +	struct mx25_tcq_priv *priv = dev_id;
> +	u32 sample_buf[TSC_MAX_SAMPLES];
> +	unsigned int samples = 0;
> +
> +	/* read all samples */
It's not a terribly big fifo, so I'm not convinced personally
of the merits of having the separate thread for this interrupt...

> +	while (1) {
> +		u32 stats;
> +
> +		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
> +		if (stats & MX25_ADCQ_SR_EMPT)
> +			break;
> +
> +		if (samples < TSC_MAX_SAMPLES) {
> +			regmap_read(priv->regs, MX25_ADCQ_FIFO,
> +				    &sample_buf[samples]);
> +			++samples;
> +		} else {
> +			u32 discarded;
> +			/* discard samples */
> +			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);
Comment on how this could happen would be good.

> +		}
> +	}
> +
> +	mx25_tcq_create_event_for_4wire(priv, sample_buf, samples);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mx25_tcq_irq(int irq, void *dev_id)
> +{
> +	struct mx25_tcq_priv *priv = dev_id;
> +	u32 stat;
> +	int ret = IRQ_HANDLED;
> +
> +	regmap_read(priv->regs, MX25_ADCQ_SR, &stat);
> +
> +	if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR))
> +		mx25_tcq_fifo_reset(priv);
Again, currious cross comparison with the adc driver (hardware is pretty
much the same ;)  In there you don't reset the fifo if you get one
of these.  Why not?  Now you should never get one as you only ever schedule
a single reading, but best to be consistent.

> +
> +	if (stat & MX25_ADCQ_SR_PD) {
> +		mx25_tcq_disable_touch_irq(priv);
> +		mx25_tcq_force_queue_start(priv);
> +		mx25_tcq_enable_fifo_irq(priv);
> +	}
> +
> +	if (stat & MX25_ADCQ_SR_FDRY) {
> +		mx25_tcq_disable_fifo_irq(priv);
I'm missing something I think.. In a oneshot irq the irq will be masked
anyway till the thread is done.  Why the explicit disable as well?

> +		ret = IRQ_WAKE_THREAD;
> +	}
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
> +			   MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
> +			   MX25_ADCQ_SR_PD | MX25_ADCQ_SR_EOQ,
> +			   MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR |
> +			   MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
> +			   MX25_ADCQ_SR_EOQ);
As with the adc driver you are clearing at least one bit that should
not I think ever be set... EOQ.  Why?
> +
> +	return ret;
> +}
> +
> +/* configure the statemachine for a 4-wire touchscreen */
state machine
> +static int mx25_tcq_init(struct mx25_tcq_priv *priv)
> +{
> +	u32 tgcr;
> +	unsigned int ipg_div;
> +	unsigned int adc_period;
> +	unsigned int debounce_cnt;
> +	unsigned int settling_cnt;
> +	int itemct;
> +	int ret;
> +
> +	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
> +	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
> +	adc_period = USEC_PER_SEC * ipg_div * 2 + 2;
> +	adc_period /= clk_get_rate(priv->clk) / 1000 + 1;
> +	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
> +	settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1;
> +
> +	/* Reset */
> +	regmap_write(priv->regs, MX25_ADCQ_CR,
> +		     MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST);
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0);
> +
> +	/* up to 128 * 8 ADC clocks are possible */
> +	if (debounce_cnt > 127)
> +		debounce_cnt = 127;
> +
> +	/* up to 255 * 8 ADC clocks are possible */
> +	if (settling_cnt > 255)
> +		settling_cnt = 255;
> +
> +	ret = imx25_setup_queue_4wire(priv, settling_cnt, &itemct);
> +	if (ret)
> +		return ret;
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK,
> +			   MX25_ADCQ_CR_LITEMID(itemct - 1) |
> +			   MX25_ADCQ_CR_WMRK(priv->expected_samples - 1));
> +
> +	/* setup debounce count */
> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR,
> +			   MX25_TGCR_PDBTIME_MASK,
> +			   MX25_TGCR_PDBTIME(debounce_cnt));
> +
> +	/* enable debounce */
> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN,
> +			   MX25_TGCR_PDBEN);
> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN,
> +			   MX25_TGCR_PDEN);
> +
> +	/* enable the engine on demand */
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK,
> +			   MX25_ADCQ_CR_QSM_FQS);
> +
> +	/* Enable repeat and repeat wait */
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK,
> +			   MX25_ADCQ_CR_RPT |
> +			   MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT));
> +
> +	mx25_tcq_re_enable_touch_detection(priv);
> +
> +	return 0;
> +}
> +
> +static int mx25_tcq_parse_dt(struct platform_device *pdev,
> +			     struct mx25_tcq_priv *priv)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	u32 wires;
> +	int ret;
> +
> +	/* Setup defaults */
> +	priv->pen_threshold = 500;
> +	priv->sample_count = 3;
> +	priv->pen_debounce = 1000000;
> +	priv->settling_time = 250000;
> +
> +	ret = of_property_read_u32(np, "fsl,wires", &wires);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
> +		return ret;
> +	}
> +
> +	if (wires == 4) {
> +		priv->mode = MX25_TS_4WIRE;
> +	} else {
> +		dev_err(&pdev->dev, "%u-wire mode not supported\n", wires);
> +		return -EINVAL;
> +	}
> +
> +	/* These are optional, we don't care about the return values */
> +	of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold);
> +	of_property_read_u32(np, "fsl,settling-time", &priv->settling_time);
> +	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
> +
> +	return 0;
> +}
> +
> +static int mx25_tcq_open(struct input_dev *idev)
> +{
> +	struct device *dev = &idev->dev;
> +	struct mx25_tcq_priv *priv = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = clk_prepare_enable(priv->clk);
> +	if (ret) {
> +		dev_err(dev, "Failed to enable ipg clock\n");
> +		return ret;
> +	}
> +
> +	ret = mx25_tcq_init(priv);
> +	if (ret) {
There is a certain missbalance in naming at least between the open
and the close.   I'd be tempted to reorganise the two so that
they obviously balance..  Either split up the init into the various
things such as enabling the interrupts and bringing the queue up
(so the oposite of the remove) or wrap the remove calls up in an
_exit which can easily be compared to the init)

This definitely comes in the category of writing the code so it
is 'obviously correct', rather than making review harder!

There is also some stuff in that init - like enabling debounce that
isn't undone in the close - to my mind that suggests it doesn't
belong in this function, but rather in device startup, but I may
well be missing some interactions in the hardware that prevent this.

> +		dev_err(dev, "Failed to init tcq\n");
> +		clk_disable_unprepare(priv->clk);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mx25_tcq_close(struct input_dev *idev)
> +{
> +	struct mx25_tcq_priv *priv = input_get_drvdata(idev);
> +
> +	mx25_tcq_force_queue_stop(priv);
> +	mx25_tcq_disable_touch_irq(priv);
> +	mx25_tcq_disable_fifo_irq(priv);
> +	clk_disable_unprepare(priv->clk);
> +}
> +
> +static int mx25_tcq_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct input_dev *idev;
> +	struct mx25_tcq_priv *priv;
> +	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
> +	struct resource *res;
> +	void __iomem *mem;
> +	int ret;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	mem = devm_ioremap_resource(dev, res);
> +	if (!mem)
> +		return -ENOMEM;
> +
> +	ret = mx25_tcq_parse_dt(pdev, priv);
> +	if (ret)
> +		return ret;
> +
> +	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig);
> +	if (IS_ERR(priv->regs)) {
> +		dev_err(dev, "Failed to initialize regmap\n");
> +		return PTR_ERR(priv->regs);
> +	}
> +
> +	priv->irq = platform_get_irq(pdev, 0);
> +	if (priv->irq <= 0) {
> +		dev_err(dev, "Failed to get IRQ\n");
> +		return priv->irq;
> +	}
> +
> +	idev = devm_input_allocate_device(dev);
> +	if (!idev) {
> +		dev_err(dev, "Failed to allocate input device\n");
> +		return -ENOMEM;
> +	}
> +
> +	idev->name = mx25_tcq_name;
> +	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> +	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
> +	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
> +	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
> +
> +	idev->id.bustype = BUS_HOST;
> +	idev->open = mx25_tcq_open;
> +	idev->close = mx25_tcq_close;
> +
> +	priv->idev = idev;
> +	input_set_drvdata(idev, priv);
> +
> +	priv->core_regs = tsadc->regs;
> +	if (!priv->core_regs)
> +		return -EINVAL;
> +
> +	priv->clk = tsadc->clk;
> +	if (!priv->clk)
> +		return -EINVAL;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	ret = input_register_device(idev);
I'm not 100% sure of whether input works the way I think it does ;)
but I'd imagine this just exposed the userspace interface.  Hence from
here on you can open the device and cause the interrupt to be enabled.
It doesn't have a handler until after the request_threaded_irq device
below...  So nasty if unlikely race condition here.

> +	if (ret) {
> +		dev_err(dev, "Failed to register input device\n");
> +		return ret;
> +	}
> +
> +	ret = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq,
> +					mx25_tcq_irq_thread, IRQF_ONESHOT,
> +					pdev->name, priv);
currious difference in approach here.  Why a devm call in this driver
and not in the adc one?  I assumed general paranoia with devm irq requests
and the various obscure ways they can go wrong so didn't mention it there...
> +	if (ret) {
> +		dev_err(dev, "Failed requesting IRQ\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct platform_driver mx25_tcq_driver = {
> +	.driver		= {
> +		.name	= "mx25-tcq",
> +		.of_match_table = mx25_tcq_ids,
> +	},
> +	.probe		= mx25_tcq_probe,
> +};
> +module_platform_driver(mx25_tcq_driver);
> +
> +MODULE_DESCRIPTION("TS input driver for Freescale mx25");
> +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
> +MODULE_LICENSE("GPL v2");
> 


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

* [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
@ 2015-11-21 17:48       ` Jonathan Cameron
  0 siblings, 0 replies; 67+ messages in thread
From: Jonathan Cameron @ 2015-11-21 17:48 UTC (permalink / raw)
  To: linux-arm-kernel

On 16/11/15 12:01, Markus Pargmann wrote:
> This is a driver for the imx25 ADC/TSC module. It controls the
> touchscreen conversion queue and creates a touchscreen input device.
> The driver currently only supports 4 wire touchscreens. The driver uses
> a simple conversion queue of precharge, touch detection, X measurement,
> Y measurement, precharge and another touch detection.
> 
> This driver uses the regmap from the parent to setup some touch specific
> settings in the core driver and setup a idle configuration with touch
> detection.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> Signed-off-by: Denis Carikli <denis@eukrea.com>
> 
> [fix clock's period calculation]
> [fix calculation of the 'settling' value]
> Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
I read this out of curiousity to see how you had handled the touchscreen
usecase of this hardware differently from the generic version.

Anyhow, a few little bits and pieces inline from me as a result

Jonathan
> ---
> 
> Notes:
>     Changes in v7:
>      - Moved clk_prepare_enable() and mx25_tcq_init() into mx25_tcq_open(). This
>        was done to be able to use devm_request_threaded_irq().
>      - Cleanup of the probe function through above change
>      - Removed mx25_tcq_remove(), not necessary now
> 
>  drivers/input/touchscreen/Kconfig         |   6 +
>  drivers/input/touchscreen/Makefile        |   1 +
>  drivers/input/touchscreen/fsl-imx25-tcq.c | 600 ++++++++++++++++++++++++++++++
>  3 files changed, 607 insertions(+)
>  create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index ae33da7ab51f..b44651d33080 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -811,6 +811,12 @@ config TOUCHSCREEN_USB_COMPOSITE
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called usbtouchscreen.
>  
> +config TOUCHSCREEN_MX25
> +	tristate "Freescale i.MX25 touchscreen input driver"
> +	depends on MFD_MX25_TSADC
> +	help
> +	  Enable support for touchscreen connected to your i.MX25.
> +
>  config TOUCHSCREEN_MC13783
>  	tristate "Freescale MC13783 touchscreen input driver"
>  	depends on MFD_MC13XXX
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index cbaa6abb08da..77a2ac54101a 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
>  obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
>  obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
> +obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
>  obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
> diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
> new file mode 100644
> index 000000000000..c833cd814972
> --- /dev/null
> +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
> @@ -0,0 +1,600 @@
> +/*
> + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + * Based on driver from 2011:
> + *   Juergen Beisert, Pengutronix <kernel@pengutronix.de>
> + *
> + * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
> + * connected to the imx25 ADC.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/imx25-tsadc.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +static const char mx25_tcq_name[] = "mx25-tcq";
> +
> +enum mx25_tcq_mode {
> +	MX25_TS_4WIRE,
> +};
> +
> +struct mx25_tcq_priv {
> +	struct regmap *regs;
> +	struct regmap *core_regs;
> +	struct input_dev *idev;
> +	enum mx25_tcq_mode mode;
> +	unsigned int pen_threshold;
> +	unsigned int sample_count;
> +	unsigned int expected_samples;
> +	unsigned int pen_debounce;
> +	unsigned int settling_time;
> +	struct clk *clk;
> +	int irq;
> +};
> +
> +static struct regmap_config mx25_tcq_regconfig = {
> +	.fast_io = true,
> +	.max_register = 0x5c,
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +};
> +
> +static const struct of_device_id mx25_tcq_ids[] = {
> +	{ .compatible = "fsl,imx25-tcq", },
> +	{ /* Sentinel */ }
> +};
> +
> +#define TSC_4WIRE_PRE_INDEX 0
> +#define TSC_4WIRE_X_INDEX 1
> +#define TSC_4WIRE_Y_INDEX 2
> +#define TSC_4WIRE_POST_INDEX 3
> +#define TSC_4WIRE_LEAVE 4
> +
> +#define MX25_TSC_DEF_THRESHOLD 80
> +#define TSC_MAX_SAMPLES 16
> +
> +#define MX25_TSC_REPEAT_WAIT 14
> +
> +enum mx25_adc_configurations {
> +	MX25_CFG_PRECHARGE = 0,
> +	MX25_CFG_TOUCH_DETECT,
> +	MX25_CFG_X_MEASUREMENT,
> +	MX25_CFG_Y_MEASUREMENT,
> +};
> +
> +#define MX25_PRECHARGE_VALUE (\
> +			MX25_ADCQ_CFG_YPLL_OFF | \
> +			MX25_ADCQ_CFG_XNUR_OFF | \
> +			MX25_ADCQ_CFG_XPUL_HIGH | \
> +			MX25_ADCQ_CFG_REFP_INT | \
> +			MX25_ADCQ_CFG_IN_XP | \
> +			MX25_ADCQ_CFG_REFN_NGND2 | \
> +			MX25_ADCQ_CFG_IGS)
> +
> +#define MX25_TOUCH_DETECT_VALUE (\
> +			MX25_ADCQ_CFG_YNLR | \
> +			MX25_ADCQ_CFG_YPLL_OFF | \
> +			MX25_ADCQ_CFG_XNUR_OFF | \
> +			MX25_ADCQ_CFG_XPUL_OFF | \
> +			MX25_ADCQ_CFG_REFP_INT | \
> +			MX25_ADCQ_CFG_IN_XP | \
> +			MX25_ADCQ_CFG_REFN_NGND2 | \
> +			MX25_ADCQ_CFG_PENIACK)
> +
> +static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv,
> +				   unsigned int settling_cnt)
> +{
> +	u32 precharge_cfg =
> +			MX25_PRECHARGE_VALUE |
> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> +	u32 touch_detect_cfg =
> +			MX25_TOUCH_DETECT_VALUE |
> +			MX25_ADCQ_CFG_NOS(1) |
> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> +
> +	regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg);
> +
> +	/* PRECHARGE */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE),
> +		     precharge_cfg);
> +
> +	/* TOUCH_DETECT */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT),
> +		     touch_detect_cfg);
> +
> +	/* X Measurement */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT),
> +		     MX25_ADCQ_CFG_YPLL_OFF |
> +		     MX25_ADCQ_CFG_XNUR_LOW |
> +		     MX25_ADCQ_CFG_XPUL_HIGH |
> +		     MX25_ADCQ_CFG_REFP_XP |
> +		     MX25_ADCQ_CFG_IN_YP |
> +		     MX25_ADCQ_CFG_REFN_XN |
> +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> +
> +	/* Y Measurement */
> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT),
> +		     MX25_ADCQ_CFG_YNLR |
> +		     MX25_ADCQ_CFG_YPLL_HIGH |
> +		     MX25_ADCQ_CFG_XNUR_OFF |
> +		     MX25_ADCQ_CFG_XPUL_OFF |
> +		     MX25_ADCQ_CFG_REFP_YP |
> +		     MX25_ADCQ_CFG_IN_XP |
> +		     MX25_ADCQ_CFG_REFN_YN |
> +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> +
> +	/* Enable the touch detection right now */
> +	regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg |
> +		     MX25_ADCQ_CFG_IGS);
> +}
> +
> +static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv,
> +				   unsigned settling_cnt, int *items)
> +{
> +	imx25_setup_queue_cfgs(priv, settling_cnt);
> +
> +	/* Setup the conversion queue */
> +	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
> +		     MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) |
> +		     MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) |
> +		     MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) |
> +		     MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) |
> +		     MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) |
> +		     MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT));
> +
> +	/*
> +	 * We measure X/Y with 'sample_count' number of samples and execute a
> +	 * touch detection twice, with 1 sample each
> +	 */
> +	priv->expected_samples = priv->sample_count * 2 + 2;
> +	*items = 6;
> +
> +	return 0;
> +}
> +
Another personal preference. I'd not bother wrapping these single line
calls up but rather just make them inline.  They don't in of
themselves add much to my mind.  Still this one is very much up to you
as far as I'm concerned.
> +static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK,
> +			   MX25_ADCQ_CR_PDMSK);
> +}
> +
> +static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0);
> +}
> +
> +static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ,
> +			   MX25_ADCQ_MR_FDRY_IRQ);
> +}
> +
> +static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0);
> +}
> +
> +static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_FQS,
> +			   MX25_ADCQ_CR_FQS);
> +}
> +
> +static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv)
> +{
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_FQS, 0);
> +}
> +
> +static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv)
> +{
> +	u32 tcqcr;
> +
> +	regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr);
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST,
> +			   MX25_ADCQ_CR_FRST);
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0);
> +	regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr);
> +}
> +
> +static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv)
> +{
> +	/* stop the queue from looping */
> +	mx25_tcq_force_queue_stop(priv);
> +
> +	/* for a clean touch detection, preload the X plane */
> +	regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE);
> +
> +	/* waste some time now to pre-load the X plate to high voltage */
> +	mx25_tcq_fifo_reset(priv);
> +
> +	/* re-enable the detection right now */
> +	regmap_write(priv->core_regs, MX25_TSC_TICR,
> +		     MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS);
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD,
> +			   MX25_ADCQ_SR_PD);
> +
> +	/* enable the pen down event to be a source for the interrupt */
> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0);
> +
> +	/* lets fire the next IRQ if someone touches the touchscreen */
> +	mx25_tcq_enable_touch_irq(priv);
> +}
> +
> +static int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,
> +					   u32 *sample_buf,
> +					   unsigned int samples)
> +{
> +	unsigned int x_pos = 0;
> +	unsigned int y_pos = 0;
> +	unsigned int touch_pre = 0;
> +	unsigned int touch_post = 0;
> +	unsigned int i;
> +	int ret = 0;
> +
> +	for (i = 0; i < samples; i++) {
> +		unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]);
> +		unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]);
> +
> +		switch (index) {
> +		case 1:
> +			touch_pre = val;
> +			break;
> +		case 2:
> +			x_pos = val;
> +			break;
> +		case 3:
> +			y_pos = val;
> +			break;
> +		case 5:
> +			touch_post = val;
> +			break;
> +		default:
> +			ret = -EINVAL;
> +			break;
> +		}
> +	}
> +
> +	if (ret == 0 && samples != 0) {
> +		/*
> +		 * only if both touch measures are below a threshold,
> +		 * the position is valid
> +		 */
> +		if (touch_pre < priv->pen_threshold &&
> +		    touch_post < priv->pen_threshold) {
> +			/* valid samples, generate a report */
> +			x_pos /= priv->sample_count;
> +			y_pos /= priv->sample_count;
> +			input_report_abs(priv->idev, ABS_X, x_pos);
> +			input_report_abs(priv->idev, ABS_Y, y_pos);
> +			input_report_key(priv->idev, BTN_TOUCH, 1);
> +			input_sync(priv->idev);
> +
> +			/* get next sample */
> +			mx25_tcq_enable_fifo_irq(priv);
> +		} else if (touch_pre >= priv->pen_threshold &&
> +			   touch_post >= priv->pen_threshold) {
> +			/*
> +			 * if both samples are invalid,
> +			 * generate a release report
> +			 */
> +			input_report_key(priv->idev, BTN_TOUCH, 0);
> +			input_sync(priv->idev);
> +			mx25_tcq_re_enable_touch_detection(priv);
> +		} else {
> +			/*
> +			 * if only one of both touch measurements are
> +			 * below the threshold, still some bouncing
> +			 * happens. Take additional samples in this
> +			 * case to be sure
> +			 */
> +			mx25_tcq_enable_fifo_irq(priv);
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
> +{
> +	struct mx25_tcq_priv *priv = dev_id;
> +	u32 sample_buf[TSC_MAX_SAMPLES];
> +	unsigned int samples = 0;
> +
> +	/* read all samples */
It's not a terribly big fifo, so I'm not convinced personally
of the merits of having the separate thread for this interrupt...

> +	while (1) {
> +		u32 stats;
> +
> +		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
> +		if (stats & MX25_ADCQ_SR_EMPT)
> +			break;
> +
> +		if (samples < TSC_MAX_SAMPLES) {
> +			regmap_read(priv->regs, MX25_ADCQ_FIFO,
> +				    &sample_buf[samples]);
> +			++samples;
> +		} else {
> +			u32 discarded;
> +			/* discard samples */
> +			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);
Comment on how this could happen would be good.

> +		}
> +	}
> +
> +	mx25_tcq_create_event_for_4wire(priv, sample_buf, samples);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mx25_tcq_irq(int irq, void *dev_id)
> +{
> +	struct mx25_tcq_priv *priv = dev_id;
> +	u32 stat;
> +	int ret = IRQ_HANDLED;
> +
> +	regmap_read(priv->regs, MX25_ADCQ_SR, &stat);
> +
> +	if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR))
> +		mx25_tcq_fifo_reset(priv);
Again, currious cross comparison with the adc driver (hardware is pretty
much the same ;)  In there you don't reset the fifo if you get one
of these.  Why not?  Now you should never get one as you only ever schedule
a single reading, but best to be consistent.

> +
> +	if (stat & MX25_ADCQ_SR_PD) {
> +		mx25_tcq_disable_touch_irq(priv);
> +		mx25_tcq_force_queue_start(priv);
> +		mx25_tcq_enable_fifo_irq(priv);
> +	}
> +
> +	if (stat & MX25_ADCQ_SR_FDRY) {
> +		mx25_tcq_disable_fifo_irq(priv);
I'm missing something I think.. In a oneshot irq the irq will be masked
anyway till the thread is done.  Why the explicit disable as well?

> +		ret = IRQ_WAKE_THREAD;
> +	}
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
> +			   MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
> +			   MX25_ADCQ_SR_PD | MX25_ADCQ_SR_EOQ,
> +			   MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR |
> +			   MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
> +			   MX25_ADCQ_SR_EOQ);
As with the adc driver you are clearing at least one bit that should
not I think ever be set... EOQ.  Why?
> +
> +	return ret;
> +}
> +
> +/* configure the statemachine for a 4-wire touchscreen */
state machine
> +static int mx25_tcq_init(struct mx25_tcq_priv *priv)
> +{
> +	u32 tgcr;
> +	unsigned int ipg_div;
> +	unsigned int adc_period;
> +	unsigned int debounce_cnt;
> +	unsigned int settling_cnt;
> +	int itemct;
> +	int ret;
> +
> +	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
> +	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
> +	adc_period = USEC_PER_SEC * ipg_div * 2 + 2;
> +	adc_period /= clk_get_rate(priv->clk) / 1000 + 1;
> +	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
> +	settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1;
> +
> +	/* Reset */
> +	regmap_write(priv->regs, MX25_ADCQ_CR,
> +		     MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST);
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0);
> +
> +	/* up to 128 * 8 ADC clocks are possible */
> +	if (debounce_cnt > 127)
> +		debounce_cnt = 127;
> +
> +	/* up to 255 * 8 ADC clocks are possible */
> +	if (settling_cnt > 255)
> +		settling_cnt = 255;
> +
> +	ret = imx25_setup_queue_4wire(priv, settling_cnt, &itemct);
> +	if (ret)
> +		return ret;
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK,
> +			   MX25_ADCQ_CR_LITEMID(itemct - 1) |
> +			   MX25_ADCQ_CR_WMRK(priv->expected_samples - 1));
> +
> +	/* setup debounce count */
> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR,
> +			   MX25_TGCR_PDBTIME_MASK,
> +			   MX25_TGCR_PDBTIME(debounce_cnt));
> +
> +	/* enable debounce */
> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN,
> +			   MX25_TGCR_PDBEN);
> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN,
> +			   MX25_TGCR_PDEN);
> +
> +	/* enable the engine on demand */
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK,
> +			   MX25_ADCQ_CR_QSM_FQS);
> +
> +	/* Enable repeat and repeat wait */
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> +			   MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK,
> +			   MX25_ADCQ_CR_RPT |
> +			   MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT));
> +
> +	mx25_tcq_re_enable_touch_detection(priv);
> +
> +	return 0;
> +}
> +
> +static int mx25_tcq_parse_dt(struct platform_device *pdev,
> +			     struct mx25_tcq_priv *priv)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	u32 wires;
> +	int ret;
> +
> +	/* Setup defaults */
> +	priv->pen_threshold = 500;
> +	priv->sample_count = 3;
> +	priv->pen_debounce = 1000000;
> +	priv->settling_time = 250000;
> +
> +	ret = of_property_read_u32(np, "fsl,wires", &wires);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
> +		return ret;
> +	}
> +
> +	if (wires == 4) {
> +		priv->mode = MX25_TS_4WIRE;
> +	} else {
> +		dev_err(&pdev->dev, "%u-wire mode not supported\n", wires);
> +		return -EINVAL;
> +	}
> +
> +	/* These are optional, we don't care about the return values */
> +	of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold);
> +	of_property_read_u32(np, "fsl,settling-time", &priv->settling_time);
> +	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
> +
> +	return 0;
> +}
> +
> +static int mx25_tcq_open(struct input_dev *idev)
> +{
> +	struct device *dev = &idev->dev;
> +	struct mx25_tcq_priv *priv = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = clk_prepare_enable(priv->clk);
> +	if (ret) {
> +		dev_err(dev, "Failed to enable ipg clock\n");
> +		return ret;
> +	}
> +
> +	ret = mx25_tcq_init(priv);
> +	if (ret) {
There is a certain missbalance in naming at least between the open
and the close.   I'd be tempted to reorganise the two so that
they obviously balance..  Either split up the init into the various
things such as enabling the interrupts and bringing the queue up
(so the oposite of the remove) or wrap the remove calls up in an
_exit which can easily be compared to the init)

This definitely comes in the category of writing the code so it
is 'obviously correct', rather than making review harder!

There is also some stuff in that init - like enabling debounce that
isn't undone in the close - to my mind that suggests it doesn't
belong in this function, but rather in device startup, but I may
well be missing some interactions in the hardware that prevent this.

> +		dev_err(dev, "Failed to init tcq\n");
> +		clk_disable_unprepare(priv->clk);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mx25_tcq_close(struct input_dev *idev)
> +{
> +	struct mx25_tcq_priv *priv = input_get_drvdata(idev);
> +
> +	mx25_tcq_force_queue_stop(priv);
> +	mx25_tcq_disable_touch_irq(priv);
> +	mx25_tcq_disable_fifo_irq(priv);
> +	clk_disable_unprepare(priv->clk);
> +}
> +
> +static int mx25_tcq_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct input_dev *idev;
> +	struct mx25_tcq_priv *priv;
> +	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
> +	struct resource *res;
> +	void __iomem *mem;
> +	int ret;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	mem = devm_ioremap_resource(dev, res);
> +	if (!mem)
> +		return -ENOMEM;
> +
> +	ret = mx25_tcq_parse_dt(pdev, priv);
> +	if (ret)
> +		return ret;
> +
> +	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig);
> +	if (IS_ERR(priv->regs)) {
> +		dev_err(dev, "Failed to initialize regmap\n");
> +		return PTR_ERR(priv->regs);
> +	}
> +
> +	priv->irq = platform_get_irq(pdev, 0);
> +	if (priv->irq <= 0) {
> +		dev_err(dev, "Failed to get IRQ\n");
> +		return priv->irq;
> +	}
> +
> +	idev = devm_input_allocate_device(dev);
> +	if (!idev) {
> +		dev_err(dev, "Failed to allocate input device\n");
> +		return -ENOMEM;
> +	}
> +
> +	idev->name = mx25_tcq_name;
> +	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> +	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
> +	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
> +	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
> +
> +	idev->id.bustype = BUS_HOST;
> +	idev->open = mx25_tcq_open;
> +	idev->close = mx25_tcq_close;
> +
> +	priv->idev = idev;
> +	input_set_drvdata(idev, priv);
> +
> +	priv->core_regs = tsadc->regs;
> +	if (!priv->core_regs)
> +		return -EINVAL;
> +
> +	priv->clk = tsadc->clk;
> +	if (!priv->clk)
> +		return -EINVAL;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	ret = input_register_device(idev);
I'm not 100% sure of whether input works the way I think it does ;)
but I'd imagine this just exposed the userspace interface.  Hence from
here on you can open the device and cause the interrupt to be enabled.
It doesn't have a handler until after the request_threaded_irq device
below...  So nasty if unlikely race condition here.

> +	if (ret) {
> +		dev_err(dev, "Failed to register input device\n");
> +		return ret;
> +	}
> +
> +	ret = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq,
> +					mx25_tcq_irq_thread, IRQF_ONESHOT,
> +					pdev->name, priv);
currious difference in approach here.  Why a devm call in this driver
and not in the adc one?  I assumed general paranoia with devm irq requests
and the various obscure ways they can go wrong so didn't mention it there...
> +	if (ret) {
> +		dev_err(dev, "Failed requesting IRQ\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct platform_driver mx25_tcq_driver = {
> +	.driver		= {
> +		.name	= "mx25-tcq",
> +		.of_match_table = mx25_tcq_ids,
> +	},
> +	.probe		= mx25_tcq_probe,
> +};
> +module_platform_driver(mx25_tcq_driver);
> +
> +MODULE_DESCRIPTION("TS input driver for Freescale mx25");
> +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
> +MODULE_LICENSE("GPL v2");
> 

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

* Re: [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
  2015-11-21 17:48       ` Jonathan Cameron
@ 2015-11-21 17:50         ` Jonathan Cameron
  -1 siblings, 0 replies; 67+ messages in thread
From: Jonathan Cameron @ 2015-11-21 17:50 UTC (permalink / raw)
  To: Markus Pargmann, Shawn Guo, Dmitry Torokhov, Lee Jones
  Cc: Denis Carikli, Eric Bénard, Sascha Hauer, devicetree,
	linux-input, linux-iio, linux-arm-kernel, Hartmut Knaack,
	Fabio Estevam, Juergen Borleis

On 21/11/15 17:48, Jonathan Cameron wrote:
> On 16/11/15 12:01, Markus Pargmann wrote:
>> This is a driver for the imx25 ADC/TSC module. It controls the
>> touchscreen conversion queue and creates a touchscreen input device.
>> The driver currently only supports 4 wire touchscreens. The driver uses
>> a simple conversion queue of precharge, touch detection, X measurement,
>> Y measurement, precharge and another touch detection.
>>
>> This driver uses the regmap from the parent to setup some touch specific
>> settings in the core driver and setup a idle configuration with touch
>> detection.
>>
>> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
>> Signed-off-by: Denis Carikli <denis@eukrea.com>
>>
>> [fix clock's period calculation]
>> [fix calculation of the 'settling' value]
>> Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
> I read this out of curiousity to see how you had handled the touchscreen
> usecase of this hardware differently from the generic version.
> 
> Anyhow, a few little bits and pieces inline from me as a result
Ah, I'd missed Dmitry's review.  Looks like he raised the same issue
on ordering in the probe so it doesn't work how I thought ;)
> 
> Jonathan
>> ---
>>
>> Notes:
>>     Changes in v7:
>>      - Moved clk_prepare_enable() and mx25_tcq_init() into mx25_tcq_open(). This
>>        was done to be able to use devm_request_threaded_irq().
>>      - Cleanup of the probe function through above change
>>      - Removed mx25_tcq_remove(), not necessary now
>>
>>  drivers/input/touchscreen/Kconfig         |   6 +
>>  drivers/input/touchscreen/Makefile        |   1 +
>>  drivers/input/touchscreen/fsl-imx25-tcq.c | 600 ++++++++++++++++++++++++++++++
>>  3 files changed, 607 insertions(+)
>>  create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
>>
>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>> index ae33da7ab51f..b44651d33080 100644
>> --- a/drivers/input/touchscreen/Kconfig
>> +++ b/drivers/input/touchscreen/Kconfig
>> @@ -811,6 +811,12 @@ config TOUCHSCREEN_USB_COMPOSITE
>>  	  To compile this driver as a module, choose M here: the
>>  	  module will be called usbtouchscreen.
>>  
>> +config TOUCHSCREEN_MX25
>> +	tristate "Freescale i.MX25 touchscreen input driver"
>> +	depends on MFD_MX25_TSADC
>> +	help
>> +	  Enable support for touchscreen connected to your i.MX25.
>> +
>>  config TOUCHSCREEN_MC13783
>>  	tristate "Freescale MC13783 touchscreen input driver"
>>  	depends on MFD_MC13XXX
>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
>> index cbaa6abb08da..77a2ac54101a 100644
>> --- a/drivers/input/touchscreen/Makefile
>> +++ b/drivers/input/touchscreen/Makefile
>> @@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
>>  obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
>>  obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
>> +obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
>>  obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
>> diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
>> new file mode 100644
>> index 000000000000..c833cd814972
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
>> @@ -0,0 +1,600 @@
>> +/*
>> + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
>> + *
>> + * This program is free software; you can redistribute it and/or modify it under
>> + * the terms of the GNU General Public License version 2 as published by the
>> + * Free Software Foundation.
>> + *
>> + * Based on driver from 2011:
>> + *   Juergen Beisert, Pengutronix <kernel@pengutronix.de>
>> + *
>> + * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
>> + * connected to the imx25 ADC.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/device.h>
>> +#include <linux/input.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/mfd/imx25-tsadc.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +
>> +static const char mx25_tcq_name[] = "mx25-tcq";
>> +
>> +enum mx25_tcq_mode {
>> +	MX25_TS_4WIRE,
>> +};
>> +
>> +struct mx25_tcq_priv {
>> +	struct regmap *regs;
>> +	struct regmap *core_regs;
>> +	struct input_dev *idev;
>> +	enum mx25_tcq_mode mode;
>> +	unsigned int pen_threshold;
>> +	unsigned int sample_count;
>> +	unsigned int expected_samples;
>> +	unsigned int pen_debounce;
>> +	unsigned int settling_time;
>> +	struct clk *clk;
>> +	int irq;
>> +};
>> +
>> +static struct regmap_config mx25_tcq_regconfig = {
>> +	.fast_io = true,
>> +	.max_register = 0x5c,
>> +	.reg_bits = 32,
>> +	.val_bits = 32,
>> +	.reg_stride = 4,
>> +};
>> +
>> +static const struct of_device_id mx25_tcq_ids[] = {
>> +	{ .compatible = "fsl,imx25-tcq", },
>> +	{ /* Sentinel */ }
>> +};
>> +
>> +#define TSC_4WIRE_PRE_INDEX 0
>> +#define TSC_4WIRE_X_INDEX 1
>> +#define TSC_4WIRE_Y_INDEX 2
>> +#define TSC_4WIRE_POST_INDEX 3
>> +#define TSC_4WIRE_LEAVE 4
>> +
>> +#define MX25_TSC_DEF_THRESHOLD 80
>> +#define TSC_MAX_SAMPLES 16
>> +
>> +#define MX25_TSC_REPEAT_WAIT 14
>> +
>> +enum mx25_adc_configurations {
>> +	MX25_CFG_PRECHARGE = 0,
>> +	MX25_CFG_TOUCH_DETECT,
>> +	MX25_CFG_X_MEASUREMENT,
>> +	MX25_CFG_Y_MEASUREMENT,
>> +};
>> +
>> +#define MX25_PRECHARGE_VALUE (\
>> +			MX25_ADCQ_CFG_YPLL_OFF | \
>> +			MX25_ADCQ_CFG_XNUR_OFF | \
>> +			MX25_ADCQ_CFG_XPUL_HIGH | \
>> +			MX25_ADCQ_CFG_REFP_INT | \
>> +			MX25_ADCQ_CFG_IN_XP | \
>> +			MX25_ADCQ_CFG_REFN_NGND2 | \
>> +			MX25_ADCQ_CFG_IGS)
>> +
>> +#define MX25_TOUCH_DETECT_VALUE (\
>> +			MX25_ADCQ_CFG_YNLR | \
>> +			MX25_ADCQ_CFG_YPLL_OFF | \
>> +			MX25_ADCQ_CFG_XNUR_OFF | \
>> +			MX25_ADCQ_CFG_XPUL_OFF | \
>> +			MX25_ADCQ_CFG_REFP_INT | \
>> +			MX25_ADCQ_CFG_IN_XP | \
>> +			MX25_ADCQ_CFG_REFN_NGND2 | \
>> +			MX25_ADCQ_CFG_PENIACK)
>> +
>> +static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv,
>> +				   unsigned int settling_cnt)
>> +{
>> +	u32 precharge_cfg =
>> +			MX25_PRECHARGE_VALUE |
>> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
>> +	u32 touch_detect_cfg =
>> +			MX25_TOUCH_DETECT_VALUE |
>> +			MX25_ADCQ_CFG_NOS(1) |
>> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
>> +
>> +	regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg);
>> +
>> +	/* PRECHARGE */
>> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE),
>> +		     precharge_cfg);
>> +
>> +	/* TOUCH_DETECT */
>> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT),
>> +		     touch_detect_cfg);
>> +
>> +	/* X Measurement */
>> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT),
>> +		     MX25_ADCQ_CFG_YPLL_OFF |
>> +		     MX25_ADCQ_CFG_XNUR_LOW |
>> +		     MX25_ADCQ_CFG_XPUL_HIGH |
>> +		     MX25_ADCQ_CFG_REFP_XP |
>> +		     MX25_ADCQ_CFG_IN_YP |
>> +		     MX25_ADCQ_CFG_REFN_XN |
>> +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
>> +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
>> +
>> +	/* Y Measurement */
>> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT),
>> +		     MX25_ADCQ_CFG_YNLR |
>> +		     MX25_ADCQ_CFG_YPLL_HIGH |
>> +		     MX25_ADCQ_CFG_XNUR_OFF |
>> +		     MX25_ADCQ_CFG_XPUL_OFF |
>> +		     MX25_ADCQ_CFG_REFP_YP |
>> +		     MX25_ADCQ_CFG_IN_XP |
>> +		     MX25_ADCQ_CFG_REFN_YN |
>> +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
>> +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
>> +
>> +	/* Enable the touch detection right now */
>> +	regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg |
>> +		     MX25_ADCQ_CFG_IGS);
>> +}
>> +
>> +static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv,
>> +				   unsigned settling_cnt, int *items)
>> +{
>> +	imx25_setup_queue_cfgs(priv, settling_cnt);
>> +
>> +	/* Setup the conversion queue */
>> +	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
>> +		     MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) |
>> +		     MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) |
>> +		     MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) |
>> +		     MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) |
>> +		     MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) |
>> +		     MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT));
>> +
>> +	/*
>> +	 * We measure X/Y with 'sample_count' number of samples and execute a
>> +	 * touch detection twice, with 1 sample each
>> +	 */
>> +	priv->expected_samples = priv->sample_count * 2 + 2;
>> +	*items = 6;
>> +
>> +	return 0;
>> +}
>> +
> Another personal preference. I'd not bother wrapping these single line
> calls up but rather just make them inline.  They don't in of
> themselves add much to my mind.  Still this one is very much up to you
> as far as I'm concerned.
>> +static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv)
>> +{
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK,
>> +			   MX25_ADCQ_CR_PDMSK);
>> +}
>> +
>> +static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv)
>> +{
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0);
>> +}
>> +
>> +static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv)
>> +{
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ,
>> +			   MX25_ADCQ_MR_FDRY_IRQ);
>> +}
>> +
>> +static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv)
>> +{
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0);
>> +}
>> +
>> +static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv)
>> +{
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
>> +			   MX25_ADCQ_CR_FQS,
>> +			   MX25_ADCQ_CR_FQS);
>> +}
>> +
>> +static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv)
>> +{
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
>> +			   MX25_ADCQ_CR_FQS, 0);
>> +}
>> +
>> +static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv)
>> +{
>> +	u32 tcqcr;
>> +
>> +	regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr);
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST,
>> +			   MX25_ADCQ_CR_FRST);
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0);
>> +	regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr);
>> +}
>> +
>> +static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv)
>> +{
>> +	/* stop the queue from looping */
>> +	mx25_tcq_force_queue_stop(priv);
>> +
>> +	/* for a clean touch detection, preload the X plane */
>> +	regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE);
>> +
>> +	/* waste some time now to pre-load the X plate to high voltage */
>> +	mx25_tcq_fifo_reset(priv);
>> +
>> +	/* re-enable the detection right now */
>> +	regmap_write(priv->core_regs, MX25_TSC_TICR,
>> +		     MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS);
>> +
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD,
>> +			   MX25_ADCQ_SR_PD);
>> +
>> +	/* enable the pen down event to be a source for the interrupt */
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0);
>> +
>> +	/* lets fire the next IRQ if someone touches the touchscreen */
>> +	mx25_tcq_enable_touch_irq(priv);
>> +}
>> +
>> +static int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,
>> +					   u32 *sample_buf,
>> +					   unsigned int samples)
>> +{
>> +	unsigned int x_pos = 0;
>> +	unsigned int y_pos = 0;
>> +	unsigned int touch_pre = 0;
>> +	unsigned int touch_post = 0;
>> +	unsigned int i;
>> +	int ret = 0;
>> +
>> +	for (i = 0; i < samples; i++) {
>> +		unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]);
>> +		unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]);
>> +
>> +		switch (index) {
>> +		case 1:
>> +			touch_pre = val;
>> +			break;
>> +		case 2:
>> +			x_pos = val;
>> +			break;
>> +		case 3:
>> +			y_pos = val;
>> +			break;
>> +		case 5:
>> +			touch_post = val;
>> +			break;
>> +		default:
>> +			ret = -EINVAL;
>> +			break;
>> +		}
>> +	}
>> +
>> +	if (ret == 0 && samples != 0) {
>> +		/*
>> +		 * only if both touch measures are below a threshold,
>> +		 * the position is valid
>> +		 */
>> +		if (touch_pre < priv->pen_threshold &&
>> +		    touch_post < priv->pen_threshold) {
>> +			/* valid samples, generate a report */
>> +			x_pos /= priv->sample_count;
>> +			y_pos /= priv->sample_count;
>> +			input_report_abs(priv->idev, ABS_X, x_pos);
>> +			input_report_abs(priv->idev, ABS_Y, y_pos);
>> +			input_report_key(priv->idev, BTN_TOUCH, 1);
>> +			input_sync(priv->idev);
>> +
>> +			/* get next sample */
>> +			mx25_tcq_enable_fifo_irq(priv);
>> +		} else if (touch_pre >= priv->pen_threshold &&
>> +			   touch_post >= priv->pen_threshold) {
>> +			/*
>> +			 * if both samples are invalid,
>> +			 * generate a release report
>> +			 */
>> +			input_report_key(priv->idev, BTN_TOUCH, 0);
>> +			input_sync(priv->idev);
>> +			mx25_tcq_re_enable_touch_detection(priv);
>> +		} else {
>> +			/*
>> +			 * if only one of both touch measurements are
>> +			 * below the threshold, still some bouncing
>> +			 * happens. Take additional samples in this
>> +			 * case to be sure
>> +			 */
>> +			mx25_tcq_enable_fifo_irq(priv);
>> +		}
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
>> +{
>> +	struct mx25_tcq_priv *priv = dev_id;
>> +	u32 sample_buf[TSC_MAX_SAMPLES];
>> +	unsigned int samples = 0;
>> +
>> +	/* read all samples */
> It's not a terribly big fifo, so I'm not convinced personally
> of the merits of having the separate thread for this interrupt...
> 
>> +	while (1) {
>> +		u32 stats;
>> +
>> +		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
>> +		if (stats & MX25_ADCQ_SR_EMPT)
>> +			break;
>> +
>> +		if (samples < TSC_MAX_SAMPLES) {
>> +			regmap_read(priv->regs, MX25_ADCQ_FIFO,
>> +				    &sample_buf[samples]);
>> +			++samples;
>> +		} else {
>> +			u32 discarded;
>> +			/* discard samples */
>> +			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);
> Comment on how this could happen would be good.
> 
>> +		}
>> +	}
>> +
>> +	mx25_tcq_create_event_for_4wire(priv, sample_buf, samples);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static irqreturn_t mx25_tcq_irq(int irq, void *dev_id)
>> +{
>> +	struct mx25_tcq_priv *priv = dev_id;
>> +	u32 stat;
>> +	int ret = IRQ_HANDLED;
>> +
>> +	regmap_read(priv->regs, MX25_ADCQ_SR, &stat);
>> +
>> +	if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR))
>> +		mx25_tcq_fifo_reset(priv);
> Again, currious cross comparison with the adc driver (hardware is pretty
> much the same ;)  In there you don't reset the fifo if you get one
> of these.  Why not?  Now you should never get one as you only ever schedule
> a single reading, but best to be consistent.
> 
>> +
>> +	if (stat & MX25_ADCQ_SR_PD) {
>> +		mx25_tcq_disable_touch_irq(priv);
>> +		mx25_tcq_force_queue_start(priv);
>> +		mx25_tcq_enable_fifo_irq(priv);
>> +	}
>> +
>> +	if (stat & MX25_ADCQ_SR_FDRY) {
>> +		mx25_tcq_disable_fifo_irq(priv);
> I'm missing something I think.. In a oneshot irq the irq will be masked
> anyway till the thread is done.  Why the explicit disable as well?
> 
>> +		ret = IRQ_WAKE_THREAD;
>> +	}
>> +
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
>> +			   MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
>> +			   MX25_ADCQ_SR_PD | MX25_ADCQ_SR_EOQ,
>> +			   MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR |
>> +			   MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
>> +			   MX25_ADCQ_SR_EOQ);
> As with the adc driver you are clearing at least one bit that should
> not I think ever be set... EOQ.  Why?
>> +
>> +	return ret;
>> +}
>> +
>> +/* configure the statemachine for a 4-wire touchscreen */
> state machine
>> +static int mx25_tcq_init(struct mx25_tcq_priv *priv)
>> +{
>> +	u32 tgcr;
>> +	unsigned int ipg_div;
>> +	unsigned int adc_period;
>> +	unsigned int debounce_cnt;
>> +	unsigned int settling_cnt;
>> +	int itemct;
>> +	int ret;
>> +
>> +	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
>> +	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
>> +	adc_period = USEC_PER_SEC * ipg_div * 2 + 2;
>> +	adc_period /= clk_get_rate(priv->clk) / 1000 + 1;
>> +	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
>> +	settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1;
>> +
>> +	/* Reset */
>> +	regmap_write(priv->regs, MX25_ADCQ_CR,
>> +		     MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST);
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
>> +			   MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0);
>> +
>> +	/* up to 128 * 8 ADC clocks are possible */
>> +	if (debounce_cnt > 127)
>> +		debounce_cnt = 127;
>> +
>> +	/* up to 255 * 8 ADC clocks are possible */
>> +	if (settling_cnt > 255)
>> +		settling_cnt = 255;
>> +
>> +	ret = imx25_setup_queue_4wire(priv, settling_cnt, &itemct);
>> +	if (ret)
>> +		return ret;
>> +
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
>> +			   MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK,
>> +			   MX25_ADCQ_CR_LITEMID(itemct - 1) |
>> +			   MX25_ADCQ_CR_WMRK(priv->expected_samples - 1));
>> +
>> +	/* setup debounce count */
>> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR,
>> +			   MX25_TGCR_PDBTIME_MASK,
>> +			   MX25_TGCR_PDBTIME(debounce_cnt));
>> +
>> +	/* enable debounce */
>> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN,
>> +			   MX25_TGCR_PDBEN);
>> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN,
>> +			   MX25_TGCR_PDEN);
>> +
>> +	/* enable the engine on demand */
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK,
>> +			   MX25_ADCQ_CR_QSM_FQS);
>> +
>> +	/* Enable repeat and repeat wait */
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
>> +			   MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK,
>> +			   MX25_ADCQ_CR_RPT |
>> +			   MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT));
>> +
>> +	mx25_tcq_re_enable_touch_detection(priv);
>> +
>> +	return 0;
>> +}
>> +
>> +static int mx25_tcq_parse_dt(struct platform_device *pdev,
>> +			     struct mx25_tcq_priv *priv)
>> +{
>> +	struct device_node *np = pdev->dev.of_node;
>> +	u32 wires;
>> +	int ret;
>> +
>> +	/* Setup defaults */
>> +	priv->pen_threshold = 500;
>> +	priv->sample_count = 3;
>> +	priv->pen_debounce = 1000000;
>> +	priv->settling_time = 250000;
>> +
>> +	ret = of_property_read_u32(np, "fsl,wires", &wires);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
>> +		return ret;
>> +	}
>> +
>> +	if (wires == 4) {
>> +		priv->mode = MX25_TS_4WIRE;
>> +	} else {
>> +		dev_err(&pdev->dev, "%u-wire mode not supported\n", wires);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* These are optional, we don't care about the return values */
>> +	of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold);
>> +	of_property_read_u32(np, "fsl,settling-time", &priv->settling_time);
>> +	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
>> +
>> +	return 0;
>> +}
>> +
>> +static int mx25_tcq_open(struct input_dev *idev)
>> +{
>> +	struct device *dev = &idev->dev;
>> +	struct mx25_tcq_priv *priv = dev_get_drvdata(dev);
>> +	int ret;
>> +
>> +	ret = clk_prepare_enable(priv->clk);
>> +	if (ret) {
>> +		dev_err(dev, "Failed to enable ipg clock\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = mx25_tcq_init(priv);
>> +	if (ret) {
> There is a certain missbalance in naming at least between the open
> and the close.   I'd be tempted to reorganise the two so that
> they obviously balance..  Either split up the init into the various
> things such as enabling the interrupts and bringing the queue up
> (so the oposite of the remove) or wrap the remove calls up in an
> _exit which can easily be compared to the init)
> 
> This definitely comes in the category of writing the code so it
> is 'obviously correct', rather than making review harder!
> 
> There is also some stuff in that init - like enabling debounce that
> isn't undone in the close - to my mind that suggests it doesn't
> belong in this function, but rather in device startup, but I may
> well be missing some interactions in the hardware that prevent this.
> 
>> +		dev_err(dev, "Failed to init tcq\n");
>> +		clk_disable_unprepare(priv->clk);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void mx25_tcq_close(struct input_dev *idev)
>> +{
>> +	struct mx25_tcq_priv *priv = input_get_drvdata(idev);
>> +
>> +	mx25_tcq_force_queue_stop(priv);
>> +	mx25_tcq_disable_touch_irq(priv);
>> +	mx25_tcq_disable_fifo_irq(priv);
>> +	clk_disable_unprepare(priv->clk);
>> +}
>> +
>> +static int mx25_tcq_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct input_dev *idev;
>> +	struct mx25_tcq_priv *priv;
>> +	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
>> +	struct resource *res;
>> +	void __iomem *mem;
>> +	int ret;
>> +
>> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> +	if (!priv)
>> +		return -ENOMEM;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	mem = devm_ioremap_resource(dev, res);
>> +	if (!mem)
>> +		return -ENOMEM;
>> +
>> +	ret = mx25_tcq_parse_dt(pdev, priv);
>> +	if (ret)
>> +		return ret;
>> +
>> +	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig);
>> +	if (IS_ERR(priv->regs)) {
>> +		dev_err(dev, "Failed to initialize regmap\n");
>> +		return PTR_ERR(priv->regs);
>> +	}
>> +
>> +	priv->irq = platform_get_irq(pdev, 0);
>> +	if (priv->irq <= 0) {
>> +		dev_err(dev, "Failed to get IRQ\n");
>> +		return priv->irq;
>> +	}
>> +
>> +	idev = devm_input_allocate_device(dev);
>> +	if (!idev) {
>> +		dev_err(dev, "Failed to allocate input device\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	idev->name = mx25_tcq_name;
>> +	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
>> +	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
>> +	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
>> +	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
>> +
>> +	idev->id.bustype = BUS_HOST;
>> +	idev->open = mx25_tcq_open;
>> +	idev->close = mx25_tcq_close;
>> +
>> +	priv->idev = idev;
>> +	input_set_drvdata(idev, priv);
>> +
>> +	priv->core_regs = tsadc->regs;
>> +	if (!priv->core_regs)
>> +		return -EINVAL;
>> +
>> +	priv->clk = tsadc->clk;
>> +	if (!priv->clk)
>> +		return -EINVAL;
>> +
>> +	platform_set_drvdata(pdev, priv);
>> +
>> +	ret = input_register_device(idev);
> I'm not 100% sure of whether input works the way I think it does ;)
> but I'd imagine this just exposed the userspace interface.  Hence from
> here on you can open the device and cause the interrupt to be enabled.
> It doesn't have a handler until after the request_threaded_irq device
> below...  So nasty if unlikely race condition here.
> 
>> +	if (ret) {
>> +		dev_err(dev, "Failed to register input device\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq,
>> +					mx25_tcq_irq_thread, IRQF_ONESHOT,
>> +					pdev->name, priv);
> currious difference in approach here.  Why a devm call in this driver
> and not in the adc one?  I assumed general paranoia with devm irq requests
> and the various obscure ways they can go wrong so didn't mention it there...
>> +	if (ret) {
>> +		dev_err(dev, "Failed requesting IRQ\n");
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver mx25_tcq_driver = {
>> +	.driver		= {
>> +		.name	= "mx25-tcq",
>> +		.of_match_table = mx25_tcq_ids,
>> +	},
>> +	.probe		= mx25_tcq_probe,
>> +};
>> +module_platform_driver(mx25_tcq_driver);
>> +
>> +MODULE_DESCRIPTION("TS input driver for Freescale mx25");
>> +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
>> +MODULE_LICENSE("GPL v2");
>>
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


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

* [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
@ 2015-11-21 17:50         ` Jonathan Cameron
  0 siblings, 0 replies; 67+ messages in thread
From: Jonathan Cameron @ 2015-11-21 17:50 UTC (permalink / raw)
  To: linux-arm-kernel

On 21/11/15 17:48, Jonathan Cameron wrote:
> On 16/11/15 12:01, Markus Pargmann wrote:
>> This is a driver for the imx25 ADC/TSC module. It controls the
>> touchscreen conversion queue and creates a touchscreen input device.
>> The driver currently only supports 4 wire touchscreens. The driver uses
>> a simple conversion queue of precharge, touch detection, X measurement,
>> Y measurement, precharge and another touch detection.
>>
>> This driver uses the regmap from the parent to setup some touch specific
>> settings in the core driver and setup a idle configuration with touch
>> detection.
>>
>> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
>> Signed-off-by: Denis Carikli <denis@eukrea.com>
>>
>> [fix clock's period calculation]
>> [fix calculation of the 'settling' value]
>> Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
> I read this out of curiousity to see how you had handled the touchscreen
> usecase of this hardware differently from the generic version.
> 
> Anyhow, a few little bits and pieces inline from me as a result
Ah, I'd missed Dmitry's review.  Looks like he raised the same issue
on ordering in the probe so it doesn't work how I thought ;)
> 
> Jonathan
>> ---
>>
>> Notes:
>>     Changes in v7:
>>      - Moved clk_prepare_enable() and mx25_tcq_init() into mx25_tcq_open(). This
>>        was done to be able to use devm_request_threaded_irq().
>>      - Cleanup of the probe function through above change
>>      - Removed mx25_tcq_remove(), not necessary now
>>
>>  drivers/input/touchscreen/Kconfig         |   6 +
>>  drivers/input/touchscreen/Makefile        |   1 +
>>  drivers/input/touchscreen/fsl-imx25-tcq.c | 600 ++++++++++++++++++++++++++++++
>>  3 files changed, 607 insertions(+)
>>  create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
>>
>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>> index ae33da7ab51f..b44651d33080 100644
>> --- a/drivers/input/touchscreen/Kconfig
>> +++ b/drivers/input/touchscreen/Kconfig
>> @@ -811,6 +811,12 @@ config TOUCHSCREEN_USB_COMPOSITE
>>  	  To compile this driver as a module, choose M here: the
>>  	  module will be called usbtouchscreen.
>>  
>> +config TOUCHSCREEN_MX25
>> +	tristate "Freescale i.MX25 touchscreen input driver"
>> +	depends on MFD_MX25_TSADC
>> +	help
>> +	  Enable support for touchscreen connected to your i.MX25.
>> +
>>  config TOUCHSCREEN_MC13783
>>  	tristate "Freescale MC13783 touchscreen input driver"
>>  	depends on MFD_MC13XXX
>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
>> index cbaa6abb08da..77a2ac54101a 100644
>> --- a/drivers/input/touchscreen/Makefile
>> +++ b/drivers/input/touchscreen/Makefile
>> @@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
>>  obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
>>  obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
>> +obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
>>  obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
>> diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
>> new file mode 100644
>> index 000000000000..c833cd814972
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
>> @@ -0,0 +1,600 @@
>> +/*
>> + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
>> + *
>> + * This program is free software; you can redistribute it and/or modify it under
>> + * the terms of the GNU General Public License version 2 as published by the
>> + * Free Software Foundation.
>> + *
>> + * Based on driver from 2011:
>> + *   Juergen Beisert, Pengutronix <kernel@pengutronix.de>
>> + *
>> + * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
>> + * connected to the imx25 ADC.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/device.h>
>> +#include <linux/input.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/mfd/imx25-tsadc.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +
>> +static const char mx25_tcq_name[] = "mx25-tcq";
>> +
>> +enum mx25_tcq_mode {
>> +	MX25_TS_4WIRE,
>> +};
>> +
>> +struct mx25_tcq_priv {
>> +	struct regmap *regs;
>> +	struct regmap *core_regs;
>> +	struct input_dev *idev;
>> +	enum mx25_tcq_mode mode;
>> +	unsigned int pen_threshold;
>> +	unsigned int sample_count;
>> +	unsigned int expected_samples;
>> +	unsigned int pen_debounce;
>> +	unsigned int settling_time;
>> +	struct clk *clk;
>> +	int irq;
>> +};
>> +
>> +static struct regmap_config mx25_tcq_regconfig = {
>> +	.fast_io = true,
>> +	.max_register = 0x5c,
>> +	.reg_bits = 32,
>> +	.val_bits = 32,
>> +	.reg_stride = 4,
>> +};
>> +
>> +static const struct of_device_id mx25_tcq_ids[] = {
>> +	{ .compatible = "fsl,imx25-tcq", },
>> +	{ /* Sentinel */ }
>> +};
>> +
>> +#define TSC_4WIRE_PRE_INDEX 0
>> +#define TSC_4WIRE_X_INDEX 1
>> +#define TSC_4WIRE_Y_INDEX 2
>> +#define TSC_4WIRE_POST_INDEX 3
>> +#define TSC_4WIRE_LEAVE 4
>> +
>> +#define MX25_TSC_DEF_THRESHOLD 80
>> +#define TSC_MAX_SAMPLES 16
>> +
>> +#define MX25_TSC_REPEAT_WAIT 14
>> +
>> +enum mx25_adc_configurations {
>> +	MX25_CFG_PRECHARGE = 0,
>> +	MX25_CFG_TOUCH_DETECT,
>> +	MX25_CFG_X_MEASUREMENT,
>> +	MX25_CFG_Y_MEASUREMENT,
>> +};
>> +
>> +#define MX25_PRECHARGE_VALUE (\
>> +			MX25_ADCQ_CFG_YPLL_OFF | \
>> +			MX25_ADCQ_CFG_XNUR_OFF | \
>> +			MX25_ADCQ_CFG_XPUL_HIGH | \
>> +			MX25_ADCQ_CFG_REFP_INT | \
>> +			MX25_ADCQ_CFG_IN_XP | \
>> +			MX25_ADCQ_CFG_REFN_NGND2 | \
>> +			MX25_ADCQ_CFG_IGS)
>> +
>> +#define MX25_TOUCH_DETECT_VALUE (\
>> +			MX25_ADCQ_CFG_YNLR | \
>> +			MX25_ADCQ_CFG_YPLL_OFF | \
>> +			MX25_ADCQ_CFG_XNUR_OFF | \
>> +			MX25_ADCQ_CFG_XPUL_OFF | \
>> +			MX25_ADCQ_CFG_REFP_INT | \
>> +			MX25_ADCQ_CFG_IN_XP | \
>> +			MX25_ADCQ_CFG_REFN_NGND2 | \
>> +			MX25_ADCQ_CFG_PENIACK)
>> +
>> +static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv,
>> +				   unsigned int settling_cnt)
>> +{
>> +	u32 precharge_cfg =
>> +			MX25_PRECHARGE_VALUE |
>> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
>> +	u32 touch_detect_cfg =
>> +			MX25_TOUCH_DETECT_VALUE |
>> +			MX25_ADCQ_CFG_NOS(1) |
>> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
>> +
>> +	regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg);
>> +
>> +	/* PRECHARGE */
>> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE),
>> +		     precharge_cfg);
>> +
>> +	/* TOUCH_DETECT */
>> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT),
>> +		     touch_detect_cfg);
>> +
>> +	/* X Measurement */
>> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT),
>> +		     MX25_ADCQ_CFG_YPLL_OFF |
>> +		     MX25_ADCQ_CFG_XNUR_LOW |
>> +		     MX25_ADCQ_CFG_XPUL_HIGH |
>> +		     MX25_ADCQ_CFG_REFP_XP |
>> +		     MX25_ADCQ_CFG_IN_YP |
>> +		     MX25_ADCQ_CFG_REFN_XN |
>> +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
>> +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
>> +
>> +	/* Y Measurement */
>> +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT),
>> +		     MX25_ADCQ_CFG_YNLR |
>> +		     MX25_ADCQ_CFG_YPLL_HIGH |
>> +		     MX25_ADCQ_CFG_XNUR_OFF |
>> +		     MX25_ADCQ_CFG_XPUL_OFF |
>> +		     MX25_ADCQ_CFG_REFP_YP |
>> +		     MX25_ADCQ_CFG_IN_XP |
>> +		     MX25_ADCQ_CFG_REFN_YN |
>> +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
>> +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
>> +
>> +	/* Enable the touch detection right now */
>> +	regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg |
>> +		     MX25_ADCQ_CFG_IGS);
>> +}
>> +
>> +static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv,
>> +				   unsigned settling_cnt, int *items)
>> +{
>> +	imx25_setup_queue_cfgs(priv, settling_cnt);
>> +
>> +	/* Setup the conversion queue */
>> +	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
>> +		     MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) |
>> +		     MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) |
>> +		     MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) |
>> +		     MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) |
>> +		     MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) |
>> +		     MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT));
>> +
>> +	/*
>> +	 * We measure X/Y with 'sample_count' number of samples and execute a
>> +	 * touch detection twice, with 1 sample each
>> +	 */
>> +	priv->expected_samples = priv->sample_count * 2 + 2;
>> +	*items = 6;
>> +
>> +	return 0;
>> +}
>> +
> Another personal preference. I'd not bother wrapping these single line
> calls up but rather just make them inline.  They don't in of
> themselves add much to my mind.  Still this one is very much up to you
> as far as I'm concerned.
>> +static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv)
>> +{
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK,
>> +			   MX25_ADCQ_CR_PDMSK);
>> +}
>> +
>> +static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv)
>> +{
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0);
>> +}
>> +
>> +static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv)
>> +{
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ,
>> +			   MX25_ADCQ_MR_FDRY_IRQ);
>> +}
>> +
>> +static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv)
>> +{
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0);
>> +}
>> +
>> +static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv)
>> +{
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
>> +			   MX25_ADCQ_CR_FQS,
>> +			   MX25_ADCQ_CR_FQS);
>> +}
>> +
>> +static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv)
>> +{
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
>> +			   MX25_ADCQ_CR_FQS, 0);
>> +}
>> +
>> +static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv)
>> +{
>> +	u32 tcqcr;
>> +
>> +	regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr);
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST,
>> +			   MX25_ADCQ_CR_FRST);
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0);
>> +	regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr);
>> +}
>> +
>> +static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv)
>> +{
>> +	/* stop the queue from looping */
>> +	mx25_tcq_force_queue_stop(priv);
>> +
>> +	/* for a clean touch detection, preload the X plane */
>> +	regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE);
>> +
>> +	/* waste some time now to pre-load the X plate to high voltage */
>> +	mx25_tcq_fifo_reset(priv);
>> +
>> +	/* re-enable the detection right now */
>> +	regmap_write(priv->core_regs, MX25_TSC_TICR,
>> +		     MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS);
>> +
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD,
>> +			   MX25_ADCQ_SR_PD);
>> +
>> +	/* enable the pen down event to be a source for the interrupt */
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0);
>> +
>> +	/* lets fire the next IRQ if someone touches the touchscreen */
>> +	mx25_tcq_enable_touch_irq(priv);
>> +}
>> +
>> +static int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,
>> +					   u32 *sample_buf,
>> +					   unsigned int samples)
>> +{
>> +	unsigned int x_pos = 0;
>> +	unsigned int y_pos = 0;
>> +	unsigned int touch_pre = 0;
>> +	unsigned int touch_post = 0;
>> +	unsigned int i;
>> +	int ret = 0;
>> +
>> +	for (i = 0; i < samples; i++) {
>> +		unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]);
>> +		unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]);
>> +
>> +		switch (index) {
>> +		case 1:
>> +			touch_pre = val;
>> +			break;
>> +		case 2:
>> +			x_pos = val;
>> +			break;
>> +		case 3:
>> +			y_pos = val;
>> +			break;
>> +		case 5:
>> +			touch_post = val;
>> +			break;
>> +		default:
>> +			ret = -EINVAL;
>> +			break;
>> +		}
>> +	}
>> +
>> +	if (ret == 0 && samples != 0) {
>> +		/*
>> +		 * only if both touch measures are below a threshold,
>> +		 * the position is valid
>> +		 */
>> +		if (touch_pre < priv->pen_threshold &&
>> +		    touch_post < priv->pen_threshold) {
>> +			/* valid samples, generate a report */
>> +			x_pos /= priv->sample_count;
>> +			y_pos /= priv->sample_count;
>> +			input_report_abs(priv->idev, ABS_X, x_pos);
>> +			input_report_abs(priv->idev, ABS_Y, y_pos);
>> +			input_report_key(priv->idev, BTN_TOUCH, 1);
>> +			input_sync(priv->idev);
>> +
>> +			/* get next sample */
>> +			mx25_tcq_enable_fifo_irq(priv);
>> +		} else if (touch_pre >= priv->pen_threshold &&
>> +			   touch_post >= priv->pen_threshold) {
>> +			/*
>> +			 * if both samples are invalid,
>> +			 * generate a release report
>> +			 */
>> +			input_report_key(priv->idev, BTN_TOUCH, 0);
>> +			input_sync(priv->idev);
>> +			mx25_tcq_re_enable_touch_detection(priv);
>> +		} else {
>> +			/*
>> +			 * if only one of both touch measurements are
>> +			 * below the threshold, still some bouncing
>> +			 * happens. Take additional samples in this
>> +			 * case to be sure
>> +			 */
>> +			mx25_tcq_enable_fifo_irq(priv);
>> +		}
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
>> +{
>> +	struct mx25_tcq_priv *priv = dev_id;
>> +	u32 sample_buf[TSC_MAX_SAMPLES];
>> +	unsigned int samples = 0;
>> +
>> +	/* read all samples */
> It's not a terribly big fifo, so I'm not convinced personally
> of the merits of having the separate thread for this interrupt...
> 
>> +	while (1) {
>> +		u32 stats;
>> +
>> +		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
>> +		if (stats & MX25_ADCQ_SR_EMPT)
>> +			break;
>> +
>> +		if (samples < TSC_MAX_SAMPLES) {
>> +			regmap_read(priv->regs, MX25_ADCQ_FIFO,
>> +				    &sample_buf[samples]);
>> +			++samples;
>> +		} else {
>> +			u32 discarded;
>> +			/* discard samples */
>> +			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);
> Comment on how this could happen would be good.
> 
>> +		}
>> +	}
>> +
>> +	mx25_tcq_create_event_for_4wire(priv, sample_buf, samples);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static irqreturn_t mx25_tcq_irq(int irq, void *dev_id)
>> +{
>> +	struct mx25_tcq_priv *priv = dev_id;
>> +	u32 stat;
>> +	int ret = IRQ_HANDLED;
>> +
>> +	regmap_read(priv->regs, MX25_ADCQ_SR, &stat);
>> +
>> +	if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR))
>> +		mx25_tcq_fifo_reset(priv);
> Again, currious cross comparison with the adc driver (hardware is pretty
> much the same ;)  In there you don't reset the fifo if you get one
> of these.  Why not?  Now you should never get one as you only ever schedule
> a single reading, but best to be consistent.
> 
>> +
>> +	if (stat & MX25_ADCQ_SR_PD) {
>> +		mx25_tcq_disable_touch_irq(priv);
>> +		mx25_tcq_force_queue_start(priv);
>> +		mx25_tcq_enable_fifo_irq(priv);
>> +	}
>> +
>> +	if (stat & MX25_ADCQ_SR_FDRY) {
>> +		mx25_tcq_disable_fifo_irq(priv);
> I'm missing something I think.. In a oneshot irq the irq will be masked
> anyway till the thread is done.  Why the explicit disable as well?
> 
>> +		ret = IRQ_WAKE_THREAD;
>> +	}
>> +
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
>> +			   MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
>> +			   MX25_ADCQ_SR_PD | MX25_ADCQ_SR_EOQ,
>> +			   MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR |
>> +			   MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
>> +			   MX25_ADCQ_SR_EOQ);
> As with the adc driver you are clearing at least one bit that should
> not I think ever be set... EOQ.  Why?
>> +
>> +	return ret;
>> +}
>> +
>> +/* configure the statemachine for a 4-wire touchscreen */
> state machine
>> +static int mx25_tcq_init(struct mx25_tcq_priv *priv)
>> +{
>> +	u32 tgcr;
>> +	unsigned int ipg_div;
>> +	unsigned int adc_period;
>> +	unsigned int debounce_cnt;
>> +	unsigned int settling_cnt;
>> +	int itemct;
>> +	int ret;
>> +
>> +	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
>> +	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
>> +	adc_period = USEC_PER_SEC * ipg_div * 2 + 2;
>> +	adc_period /= clk_get_rate(priv->clk) / 1000 + 1;
>> +	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
>> +	settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1;
>> +
>> +	/* Reset */
>> +	regmap_write(priv->regs, MX25_ADCQ_CR,
>> +		     MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST);
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
>> +			   MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0);
>> +
>> +	/* up to 128 * 8 ADC clocks are possible */
>> +	if (debounce_cnt > 127)
>> +		debounce_cnt = 127;
>> +
>> +	/* up to 255 * 8 ADC clocks are possible */
>> +	if (settling_cnt > 255)
>> +		settling_cnt = 255;
>> +
>> +	ret = imx25_setup_queue_4wire(priv, settling_cnt, &itemct);
>> +	if (ret)
>> +		return ret;
>> +
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
>> +			   MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK,
>> +			   MX25_ADCQ_CR_LITEMID(itemct - 1) |
>> +			   MX25_ADCQ_CR_WMRK(priv->expected_samples - 1));
>> +
>> +	/* setup debounce count */
>> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR,
>> +			   MX25_TGCR_PDBTIME_MASK,
>> +			   MX25_TGCR_PDBTIME(debounce_cnt));
>> +
>> +	/* enable debounce */
>> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN,
>> +			   MX25_TGCR_PDBEN);
>> +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN,
>> +			   MX25_TGCR_PDEN);
>> +
>> +	/* enable the engine on demand */
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK,
>> +			   MX25_ADCQ_CR_QSM_FQS);
>> +
>> +	/* Enable repeat and repeat wait */
>> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
>> +			   MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK,
>> +			   MX25_ADCQ_CR_RPT |
>> +			   MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT));
>> +
>> +	mx25_tcq_re_enable_touch_detection(priv);
>> +
>> +	return 0;
>> +}
>> +
>> +static int mx25_tcq_parse_dt(struct platform_device *pdev,
>> +			     struct mx25_tcq_priv *priv)
>> +{
>> +	struct device_node *np = pdev->dev.of_node;
>> +	u32 wires;
>> +	int ret;
>> +
>> +	/* Setup defaults */
>> +	priv->pen_threshold = 500;
>> +	priv->sample_count = 3;
>> +	priv->pen_debounce = 1000000;
>> +	priv->settling_time = 250000;
>> +
>> +	ret = of_property_read_u32(np, "fsl,wires", &wires);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
>> +		return ret;
>> +	}
>> +
>> +	if (wires == 4) {
>> +		priv->mode = MX25_TS_4WIRE;
>> +	} else {
>> +		dev_err(&pdev->dev, "%u-wire mode not supported\n", wires);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* These are optional, we don't care about the return values */
>> +	of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold);
>> +	of_property_read_u32(np, "fsl,settling-time", &priv->settling_time);
>> +	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
>> +
>> +	return 0;
>> +}
>> +
>> +static int mx25_tcq_open(struct input_dev *idev)
>> +{
>> +	struct device *dev = &idev->dev;
>> +	struct mx25_tcq_priv *priv = dev_get_drvdata(dev);
>> +	int ret;
>> +
>> +	ret = clk_prepare_enable(priv->clk);
>> +	if (ret) {
>> +		dev_err(dev, "Failed to enable ipg clock\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = mx25_tcq_init(priv);
>> +	if (ret) {
> There is a certain missbalance in naming at least between the open
> and the close.   I'd be tempted to reorganise the two so that
> they obviously balance..  Either split up the init into the various
> things such as enabling the interrupts and bringing the queue up
> (so the oposite of the remove) or wrap the remove calls up in an
> _exit which can easily be compared to the init)
> 
> This definitely comes in the category of writing the code so it
> is 'obviously correct', rather than making review harder!
> 
> There is also some stuff in that init - like enabling debounce that
> isn't undone in the close - to my mind that suggests it doesn't
> belong in this function, but rather in device startup, but I may
> well be missing some interactions in the hardware that prevent this.
> 
>> +		dev_err(dev, "Failed to init tcq\n");
>> +		clk_disable_unprepare(priv->clk);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void mx25_tcq_close(struct input_dev *idev)
>> +{
>> +	struct mx25_tcq_priv *priv = input_get_drvdata(idev);
>> +
>> +	mx25_tcq_force_queue_stop(priv);
>> +	mx25_tcq_disable_touch_irq(priv);
>> +	mx25_tcq_disable_fifo_irq(priv);
>> +	clk_disable_unprepare(priv->clk);
>> +}
>> +
>> +static int mx25_tcq_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct input_dev *idev;
>> +	struct mx25_tcq_priv *priv;
>> +	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
>> +	struct resource *res;
>> +	void __iomem *mem;
>> +	int ret;
>> +
>> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> +	if (!priv)
>> +		return -ENOMEM;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	mem = devm_ioremap_resource(dev, res);
>> +	if (!mem)
>> +		return -ENOMEM;
>> +
>> +	ret = mx25_tcq_parse_dt(pdev, priv);
>> +	if (ret)
>> +		return ret;
>> +
>> +	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig);
>> +	if (IS_ERR(priv->regs)) {
>> +		dev_err(dev, "Failed to initialize regmap\n");
>> +		return PTR_ERR(priv->regs);
>> +	}
>> +
>> +	priv->irq = platform_get_irq(pdev, 0);
>> +	if (priv->irq <= 0) {
>> +		dev_err(dev, "Failed to get IRQ\n");
>> +		return priv->irq;
>> +	}
>> +
>> +	idev = devm_input_allocate_device(dev);
>> +	if (!idev) {
>> +		dev_err(dev, "Failed to allocate input device\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	idev->name = mx25_tcq_name;
>> +	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
>> +	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
>> +	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
>> +	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
>> +
>> +	idev->id.bustype = BUS_HOST;
>> +	idev->open = mx25_tcq_open;
>> +	idev->close = mx25_tcq_close;
>> +
>> +	priv->idev = idev;
>> +	input_set_drvdata(idev, priv);
>> +
>> +	priv->core_regs = tsadc->regs;
>> +	if (!priv->core_regs)
>> +		return -EINVAL;
>> +
>> +	priv->clk = tsadc->clk;
>> +	if (!priv->clk)
>> +		return -EINVAL;
>> +
>> +	platform_set_drvdata(pdev, priv);
>> +
>> +	ret = input_register_device(idev);
> I'm not 100% sure of whether input works the way I think it does ;)
> but I'd imagine this just exposed the userspace interface.  Hence from
> here on you can open the device and cause the interrupt to be enabled.
> It doesn't have a handler until after the request_threaded_irq device
> below...  So nasty if unlikely race condition here.
> 
>> +	if (ret) {
>> +		dev_err(dev, "Failed to register input device\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq,
>> +					mx25_tcq_irq_thread, IRQF_ONESHOT,
>> +					pdev->name, priv);
> currious difference in approach here.  Why a devm call in this driver
> and not in the adc one?  I assumed general paranoia with devm irq requests
> and the various obscure ways they can go wrong so didn't mention it there...
>> +	if (ret) {
>> +		dev_err(dev, "Failed requesting IRQ\n");
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver mx25_tcq_driver = {
>> +	.driver		= {
>> +		.name	= "mx25-tcq",
>> +		.of_match_table = mx25_tcq_ids,
>> +	},
>> +	.probe		= mx25_tcq_probe,
>> +};
>> +module_platform_driver(mx25_tcq_driver);
>> +
>> +MODULE_DESCRIPTION("TS input driver for Freescale mx25");
>> +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
>> +MODULE_LICENSE("GPL v2");
>>
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
  2015-11-21 17:48       ` Jonathan Cameron
  (?)
@ 2015-11-23  8:21           ` Juergen Borleis
  -1 siblings, 0 replies; 67+ messages in thread
From: Juergen Borleis @ 2015-11-23  8:21 UTC (permalink / raw)
  To: kernel-bIcnvbaLZ9MEGnE8C9+IrQ
  Cc: Jonathan Cameron, Markus Pargmann, Shawn Guo, Dmitry Torokhov,
	Lee Jones, devicetree-u79uwXL29TY76Z2rM5mHXA, Eric Bc3a9nard,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Hartmut Knaack, Denis Carikli,
	linux-input-u79uwXL29TY76Z2rM5mHXA, Fabio Estevam,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Jonathan,

On Saturday 21 November 2015 18:48:10 Jonathan Cameron wrote:
> [...]
> Another personal preference. I'd not bother wrapping these single line
> calls up but rather just make them inline.  They don't in of
> themselves add much to my mind.  Still this one is very much up to you
> as far as I'm concerned.

A matter of taste. Programming bits is more or less hard to understand even if 
we use macros with useful names. So it is me to prefer self explaining 
functions by no cost of code because the compiler will optimise it away.

Regards,
Juergen

-- 
Pengutronix e.K.                              | Juergen Borleis             |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
@ 2015-11-23  8:21           ` Juergen Borleis
  0 siblings, 0 replies; 67+ messages in thread
From: Juergen Borleis @ 2015-11-23  8:21 UTC (permalink / raw)
  To: kernel
  Cc: Jonathan Cameron, Markus Pargmann, Shawn Guo, Dmitry Torokhov,
	Lee Jones, devicetree, Eric Bc3a9nard, linux-iio, Hartmut Knaack,
	Denis Carikli, linux-input, Fabio Estevam, linux-arm-kernel

Hi Jonathan,

On Saturday 21 November 2015 18:48:10 Jonathan Cameron wrote:
> [...]
> Another personal preference. I'd not bother wrapping these single line
> calls up but rather just make them inline.  They don't in of
> themselves add much to my mind.  Still this one is very much up to you
> as far as I'm concerned.

A matter of taste. Programming bits is more or less hard to understand even if 
we use macros with useful names. So it is me to prefer self explaining 
functions by no cost of code because the compiler will optimise it away.

Regards,
Juergen

-- 
Pengutronix e.K.                              | Juergen Borleis             |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |

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

* [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
@ 2015-11-23  8:21           ` Juergen Borleis
  0 siblings, 0 replies; 67+ messages in thread
From: Juergen Borleis @ 2015-11-23  8:21 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Jonathan,

On Saturday 21 November 2015 18:48:10 Jonathan Cameron wrote:
> [...]
> Another personal preference. I'd not bother wrapping these single line
> calls up but rather just make them inline.  They don't in of
> themselves add much to my mind.  Still this one is very much up to you
> as far as I'm concerned.

A matter of taste. Programming bits is more or less hard to understand even if 
we use macros with useful names. So it is me to prefer self explaining 
functions by no cost of code because the compiler will optimise it away.

Regards,
Juergen

-- 
Pengutronix e.K. ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| Juergen Borleis ? ? ? ? ? ? |
Industrial Linux Solutions ? ? ? ? ? ? ? ? ? ?| http://www.pengutronix.de/ ?|

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

* Re: [PATCH v8 4/8] mfd: fsl imx25 Touchscreen ADC driver
  2015-11-21 17:02     ` Jonathan Cameron
  (?)
@ 2015-11-23  9:17         ` Markus Pargmann
  -1 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-23  9:17 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Jonathan Cameron, Shawn Guo, Dmitry Torokhov, Lee Jones,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Eric Bénard,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Hartmut Knaack, Denis Carikli,
	Sascha Hauer, linux-input-u79uwXL29TY76Z2rM5mHXA, Fabio Estevam

[-- Attachment #1: Type: text/plain, Size: 17415 bytes --]

On Saturday 21 November 2015 17:02:34 Jonathan Cameron wrote:
> On 16/11/15 12:01, Markus Pargmann wrote:
> > This is the core driver for imx25 touchscreen/adc driver. The module
> > has one shared ADC and two different conversion queues which use the
> > ADC. The two queues are identical. Both can be used for general purpose
> > ADC but one is meant to be used for touchscreens.
> > 
> > This driver is the core which manages the central components and
> > registers of the TSC/ADC unit. It manages the IRQs and forwards them to
> > the correct components.
> > 
> > Signed-off-by: Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> > Signed-off-by: Denis Carikli <denis-fO0SIAKYzcbQT0dZR+AlfA@public.gmane.org>
> > 
> > [ensure correct ADC clock depending on the IPG clock]
> > Signed-off-by: Juergen Borleis <jbe-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> Looks good to me - one query on meaning of a comment inline.
> 
> Acked-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

Thanks, comment inline.

> 
> I'm taking the view this series wants to go through the MFD tree
> but don't mind if it goes through IIO or input instead
> if there is a good reason to do so.
> 
> Jonathan
> > ---
> > 
> > Notes:
> >     Changes in v7:
> >      - Cleanup bit defines in header files to be more readable
> >      - Fix irq check to return with an error for irq <= 0
> >      - Add COMPILE_TEST in Kconfig file
> >     
> >     Changes in v5:
> >      - Remove ifdef CONFIG_OF as this driver is only for DT usage
> >      - Remove module owner
> >      - Add Kconfig dependencies ARCH_MX25 and OF
> >     
> >     @Jonathan Cameron:
> >     I left your acked-by on the patch as these were small changes. If it should be
> >     removed, please say so. Thanks
> > 
> >  drivers/mfd/Kconfig             |   9 ++
> >  drivers/mfd/Makefile            |   2 +
> >  drivers/mfd/fsl-imx25-tsadc.c   | 204 ++++++++++++++++++++++++++++++++++++++++
> >  include/linux/mfd/imx25-tsadc.h | 140 +++++++++++++++++++++++++++
> >  4 files changed, 355 insertions(+)
> >  create mode 100644 drivers/mfd/fsl-imx25-tsadc.c
> >  create mode 100644 include/linux/mfd/imx25-tsadc.h
> > 
> > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> > index 4d92df6ef9fe..4222e202ad2b 100644
> > --- a/drivers/mfd/Kconfig
> > +++ b/drivers/mfd/Kconfig
> > @@ -271,6 +271,15 @@ config MFD_MC13XXX_I2C
> >  	help
> >  	  Select this if your MC13xxx is connected via an I2C bus.
> >  
> > +config MFD_MX25_TSADC
> > +	tristate "Freescale i.MX25 integrated Touchscreen and ADC unit"
> > +	select REGMAP_MMIO
> > +	depends on (SOC_IMX25 && OF) || COMPILE_TEST
> > +	help
> > +	  Enable support for the integrated Touchscreen and ADC unit of the
> > +	  i.MX25 processors. They consist of a conversion queue for general
> > +	  purpose ADC and a queue for Touchscreens.
> > +
> >  config MFD_HI6421_PMIC
> >  	tristate "HiSilicon Hi6421 PMU/Codec IC"
> >  	depends on OF
> > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> > index a8b76b81b467..5741be88c173 100644
> > --- a/drivers/mfd/Makefile
> > +++ b/drivers/mfd/Makefile
> > @@ -81,6 +81,8 @@ obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
> >  obj-$(CONFIG_MFD_TWL4030_AUDIO)	+= twl4030-audio.o
> >  obj-$(CONFIG_TWL6040_CORE)	+= twl6040.o
> >  
> > +obj-$(CONFIG_MFD_MX25_TSADC)	+= fsl-imx25-tsadc.o
> > +
> >  obj-$(CONFIG_MFD_MC13XXX)	+= mc13xxx-core.o
> >  obj-$(CONFIG_MFD_MC13XXX_SPI)	+= mc13xxx-spi.o
> >  obj-$(CONFIG_MFD_MC13XXX_I2C)	+= mc13xxx-i2c.o
> > diff --git a/drivers/mfd/fsl-imx25-tsadc.c b/drivers/mfd/fsl-imx25-tsadc.c
> > new file mode 100644
> > index 000000000000..e67d5ca81e10
> > --- /dev/null
> > +++ b/drivers/mfd/fsl-imx25-tsadc.c
> > @@ -0,0 +1,204 @@
> > +/*
> > + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it under
> > + * the terms of the GNU General Public License version 2 as published by the
> > + * Free Software Foundation.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/irqdesc.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/irq.h>
> > +#include <linux/mfd/imx25-tsadc.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +
> > +static struct regmap_config mx25_tsadc_regmap_config = {
> > +	.fast_io = true,
> > +	.max_register = 8,
> > +	.reg_bits = 32,
> > +	.val_bits = 32,
> > +	.reg_stride = 4,
> > +};
> > +
> > +static void mx25_tsadc_irq_handler(struct irq_desc *desc)
> > +{
> > +	struct mx25_tsadc *tsadc = irq_desc_get_handler_data(desc);
> > +	struct irq_chip *chip = irq_desc_get_chip(desc);
> > +	u32 status;
> > +
> > +	chained_irq_enter(chip, desc);
> > +
> > +	regmap_read(tsadc->regs, MX25_TSC_TGSR, &status);
> > +
> > +	if (status & MX25_TGSR_GCQ_INT)
> > +		generic_handle_irq(irq_find_mapping(tsadc->domain, 1));
> > +
> > +	if (status & MX25_TGSR_TCQ_INT)
> > +		generic_handle_irq(irq_find_mapping(tsadc->domain, 0));
> > +
> > +	chained_irq_exit(chip, desc);
> > +}
> > +
> > +static int mx25_tsadc_domain_map(struct irq_domain *d, unsigned int irq,
> > +				 irq_hw_number_t hwirq)
> > +{
> > +	struct mx25_tsadc *tsadc = d->host_data;
> > +
> > +	irq_set_chip_data(irq, tsadc);
> > +	irq_set_chip_and_handler(irq, &dummy_irq_chip,
> > +				 handle_level_irq);
> > +	irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE);
> > +
> > +	return 0;
> > +}
> > +
> > +static struct irq_domain_ops mx25_tsadc_domain_ops = {
> > +	.map = mx25_tsadc_domain_map,
> > +	.xlate = irq_domain_xlate_onecell,
> > +};
> > +
> > +static int mx25_tsadc_setup_irq(struct platform_device *pdev,
> > +				struct mx25_tsadc *tsadc)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct device_node *np = dev->of_node;
> > +	int irq;
> > +
> > +	irq = platform_get_irq(pdev, 0);
> > +	if (irq <= 0) {
> > +		dev_err(dev, "Failed to get irq\n");
> > +		return irq;
> > +	}
> > +
> > +	tsadc->domain = irq_domain_add_simple(np, 2, 0, &mx25_tsadc_domain_ops,
> > +					      tsadc);
> > +	if (!tsadc->domain) {
> > +		dev_err(dev, "Failed to add irq domain\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	irq_set_chained_handler(irq, mx25_tsadc_irq_handler);
> > +	irq_set_handler_data(irq, tsadc);
> > +
> > +	return 0;
> > +}
> > +
> > +static void mx25_tsadc_setup_clk(struct platform_device *pdev,
> > +				 struct mx25_tsadc *tsadc)
> > +{
> > +	unsigned clk_div;
> > +
> > +	/*
> > +	 * According to the datasheet the ADC clock should never
> > +	 * exceed 1,75 MHz. Base clock is the IPG and the ADC unit uses
> > +	 * a funny clock divider. To keep the time constant the ADC needs to do
> > +	 * one conversion, adapt the ADC internal clock divider to
> > +	 * the available IPG
> I'm not entirely following this explanation...
> maybe

Thanks, this is indeed a bit misleading. It should be something like this:

	To keep the ADC conversion time constant adapt the ADC internal clock
	divider to the IPG clock rate.

Will fix it.

Best Regards,

Markus

> 
> ...one conversion, then adapt the ADC...
> 
> > +	 */
> > +
> > +	dev_dbg(&pdev->dev, "Found master clock at %lu Hz\n",
> > +		clk_get_rate(tsadc->clk));
> > +
> > +	clk_div = DIV_ROUND_UP(clk_get_rate(tsadc->clk), 1750000);
> > +	dev_dbg(&pdev->dev, "Setting up ADC clock divider to %u\n", clk_div);
> > +
> > +	/* adc clock = IPG clock / (2 * div + 2) */
> > +	clk_div -= 2;
> > +	clk_div /= 2;
> > +
> > +	/*
> > +	 * the ADC clock divider changes its behaviour when values below 4
> > +	 * are used: it is fixed to "/ 10" in this case
> > +	 */
> > +	clk_div = max_t(unsigned, 4, clk_div);
> > +
> > +	dev_dbg(&pdev->dev, "Resulting ADC conversion clock at %lu Hz\n",
> > +		clk_get_rate(tsadc->clk) / (2 * clk_div + 2));
> > +
> > +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR,
> > +			   MX25_TGCR_ADCCLKCFG(0x1f),
> > +			   MX25_TGCR_ADCCLKCFG(clk_div));
> > +}
> > +
> > +static int mx25_tsadc_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct device_node *np = dev->of_node;
> > +	struct mx25_tsadc *tsadc;
> > +	struct resource *res;
> > +	int ret;
> > +	void __iomem *iomem;
> > +
> > +	tsadc = devm_kzalloc(dev, sizeof(*tsadc), GFP_KERNEL);
> > +	if (!tsadc)
> > +		return -ENOMEM;
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	iomem = devm_ioremap_resource(dev, res);
> > +	if (IS_ERR(iomem))
> > +		return PTR_ERR(iomem);
> > +
> > +	tsadc->regs = devm_regmap_init_mmio(dev, iomem,
> > +					    &mx25_tsadc_regmap_config);
> > +	if (IS_ERR(tsadc->regs)) {
> > +		dev_err(dev, "Failed to initialize regmap\n");
> > +		return PTR_ERR(tsadc->regs);
> > +	}
> > +
> > +	tsadc->clk = devm_clk_get(dev, "ipg");
> > +	if (IS_ERR(tsadc->clk)) {
> > +		dev_err(dev, "Failed to get ipg clock\n");
> > +		return PTR_ERR(tsadc->clk);
> > +	}
> > +
> > +	/* setup clock according to the datasheet */
> > +	mx25_tsadc_setup_clk(pdev, tsadc);
> > +
> > +	/* Enable clock and reset the component */
> > +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_CLK_EN,
> > +			   MX25_TGCR_CLK_EN);
> > +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_TSC_RST,
> > +			   MX25_TGCR_TSC_RST);
> > +
> > +	/* Setup powersaving mode, but enable internal reference voltage */
> > +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_POWERMODE_MASK,
> > +			   MX25_TGCR_POWERMODE_SAVE);
> > +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_INTREFEN,
> > +			   MX25_TGCR_INTREFEN);
> > +
> > +	ret = mx25_tsadc_setup_irq(pdev, tsadc);
> > +	if (ret)
> > +		return ret;
> > +
> > +	platform_set_drvdata(pdev, tsadc);
> > +
> > +	of_platform_populate(np, NULL, NULL, dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id mx25_tsadc_ids[] = {
> > +	{ .compatible = "fsl,imx25-tsadc" },
> > +	{ /* Sentinel */ }
> > +};
> > +
> > +static struct platform_driver mx25_tsadc_driver = {
> > +	.driver = {
> > +		.name = "mx25-tsadc",
> > +		.of_match_table = of_match_ptr(mx25_tsadc_ids),
> > +	},
> > +	.probe = mx25_tsadc_probe,
> > +};
> > +module_platform_driver(mx25_tsadc_driver);
> > +
> > +MODULE_DESCRIPTION("MFD for ADC/TSC for Freescale mx25");
> > +MODULE_AUTHOR("Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>");
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_ALIAS("platform:mx25-tsadc");
> > diff --git a/include/linux/mfd/imx25-tsadc.h b/include/linux/mfd/imx25-tsadc.h
> > new file mode 100644
> > index 000000000000..7fe4b8c3baac
> > --- /dev/null
> > +++ b/include/linux/mfd/imx25-tsadc.h
> > @@ -0,0 +1,140 @@
> > +#ifndef _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
> > +#define _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
> > +
> > +struct regmap;
> > +struct clk;
> > +
> > +struct mx25_tsadc {
> > +	struct regmap *regs;
> > +	struct irq_domain *domain;
> > +	struct clk *clk;
> > +};
> > +
> > +#define MX25_TSC_TGCR			0x00
> > +#define MX25_TSC_TGSR			0x04
> > +#define MX25_TSC_TICR			0x08
> > +
> > +/* The same register layout for TC and GC queue */
> > +#define MX25_ADCQ_FIFO			0x00
> > +#define MX25_ADCQ_CR			0x04
> > +#define MX25_ADCQ_SR			0x08
> > +#define MX25_ADCQ_MR			0x0c
> > +#define MX25_ADCQ_ITEM_7_0		0x20
> > +#define MX25_ADCQ_ITEM_15_8		0x24
> > +#define MX25_ADCQ_CFG(n)		(0x40 + ((n) * 0x4))
> > +
> > +#define MX25_ADCQ_MR_MASK		0xffffffff
> > +
> > +/* TGCR */
> > +#define MX25_TGCR_PDBTIME(x)		((x) << 25)
> > +#define MX25_TGCR_PDBTIME_MASK		GENMASK(31, 25)
> > +#define MX25_TGCR_PDBEN			BIT(24)
> > +#define MX25_TGCR_PDEN			BIT(23)
> > +#define MX25_TGCR_ADCCLKCFG(x)		((x) << 16)
> > +#define MX25_TGCR_GET_ADCCLK(x)		(((x) >> 16) & 0x1f)
> > +#define MX25_TGCR_INTREFEN		BIT(10)
> > +#define MX25_TGCR_POWERMODE_MASK	GENMASK(9, 8)
> > +#define MX25_TGCR_POWERMODE_SAVE	(1 << 8)
> > +#define MX25_TGCR_POWERMODE_ON		(2 << 8)
> > +#define MX25_TGCR_STLC			BIT(5)
> > +#define MX25_TGCR_SLPC			BIT(4)
> > +#define MX25_TGCR_FUNC_RST		BIT(2)
> > +#define MX25_TGCR_TSC_RST		BIT(1)
> > +#define MX25_TGCR_CLK_EN		BIT(0)
> > +
> > +/* TGSR */
> > +#define MX25_TGSR_SLP_INT		BIT(2)
> > +#define MX25_TGSR_GCQ_INT		BIT(1)
> > +#define MX25_TGSR_TCQ_INT		BIT(0)
> > +
> > +/* ADCQ_ITEM_* */
> > +#define _MX25_ADCQ_ITEM(item, x)	((x) << ((item) * 4))
> > +#define MX25_ADCQ_ITEM(item, x)		((item) >= 8 ? \
> > +		_MX25_ADCQ_ITEM((item) - 8, (x)) : _MX25_ADCQ_ITEM((item), (x)))
> > +
> > +/* ADCQ_FIFO (TCQFIFO and GCQFIFO) */
> > +#define MX25_ADCQ_FIFO_DATA(x)		(((x) >> 4) & 0xfff)
> > +#define MX25_ADCQ_FIFO_ID(x)		((x) & 0xf)
> > +
> > +/* ADCQ_CR (TCQR and GCQR) */
> > +#define MX25_ADCQ_CR_PDCFG_LEVEL	BIT(19)
> > +#define MX25_ADCQ_CR_PDMSK		BIT(18)
> > +#define MX25_ADCQ_CR_FRST		BIT(17)
> > +#define MX25_ADCQ_CR_QRST		BIT(16)
> > +#define MX25_ADCQ_CR_RWAIT_MASK		GENMASK(15, 12)
> > +#define MX25_ADCQ_CR_RWAIT(x)		((x) << 12)
> > +#define MX25_ADCQ_CR_WMRK_MASK		GENMASK(11, 8)
> > +#define MX25_ADCQ_CR_WMRK(x)		((x) << 8)
> > +#define MX25_ADCQ_CR_LITEMID_MASK	(0xf << 4)
> > +#define MX25_ADCQ_CR_LITEMID(x)		((x) << 4)
> > +#define MX25_ADCQ_CR_RPT		BIT(3)
> > +#define MX25_ADCQ_CR_FQS		BIT(2)
> > +#define MX25_ADCQ_CR_QSM_MASK		GENMASK(1, 0)
> > +#define MX25_ADCQ_CR_QSM_PD		0x1
> > +#define MX25_ADCQ_CR_QSM_FQS		0x2
> > +#define MX25_ADCQ_CR_QSM_FQS_PD		0x3
> > +
> > +/* ADCQ_SR (TCQSR and GCQSR) */
> > +#define MX25_ADCQ_SR_FDRY		BIT(15)
> > +#define MX25_ADCQ_SR_FULL		BIT(14)
> > +#define MX25_ADCQ_SR_EMPT		BIT(13)
> > +#define MX25_ADCQ_SR_FDN(x)		(((x) >> 8) & 0x1f)
> > +#define MX25_ADCQ_SR_FRR		BIT(6)
> > +#define MX25_ADCQ_SR_FUR		BIT(5)
> > +#define MX25_ADCQ_SR_FOR		BIT(4)
> > +#define MX25_ADCQ_SR_EOQ		BIT(1)
> > +#define MX25_ADCQ_SR_PD			BIT(0)
> > +
> > +/* ADCQ_MR (TCQMR and GCQMR) */
> > +#define MX25_ADCQ_MR_FDRY_DMA		BIT(31)
> > +#define MX25_ADCQ_MR_FER_DMA		BIT(22)
> > +#define MX25_ADCQ_MR_FUR_DMA		BIT(21)
> > +#define MX25_ADCQ_MR_FOR_DMA		BIT(20)
> > +#define MX25_ADCQ_MR_EOQ_DMA		BIT(17)
> > +#define MX25_ADCQ_MR_PD_DMA		BIT(16)
> > +#define MX25_ADCQ_MR_FDRY_IRQ		BIT(15)
> > +#define MX25_ADCQ_MR_FER_IRQ		BIT(6)
> > +#define MX25_ADCQ_MR_FUR_IRQ		BIT(5)
> > +#define MX25_ADCQ_MR_FOR_IRQ		BIT(4)
> > +#define MX25_ADCQ_MR_EOQ_IRQ		BIT(1)
> > +#define MX25_ADCQ_MR_PD_IRQ		BIT(0)
> > +
> > +/* ADCQ_CFG (TICR, TCC0-7,GCC0-7) */
> > +#define MX25_ADCQ_CFG_SETTLING_TIME(x)	((x) << 24)
> > +#define MX25_ADCQ_CFG_IGS		(1 << 20)
> > +#define MX25_ADCQ_CFG_NOS_MASK		GENMASK(19, 16)
> > +#define MX25_ADCQ_CFG_NOS(x)		(((x) - 1) << 16)
> > +#define MX25_ADCQ_CFG_WIPER		(1 << 15)
> > +#define MX25_ADCQ_CFG_YNLR		(1 << 14)
> > +#define MX25_ADCQ_CFG_YPLL_HIGH		(0 << 12)
> > +#define MX25_ADCQ_CFG_YPLL_OFF		(1 << 12)
> > +#define MX25_ADCQ_CFG_YPLL_LOW		(3 << 12)
> > +#define MX25_ADCQ_CFG_XNUR_HIGH		(0 << 10)
> > +#define MX25_ADCQ_CFG_XNUR_OFF		(1 << 10)
> > +#define MX25_ADCQ_CFG_XNUR_LOW		(3 << 10)
> > +#define MX25_ADCQ_CFG_XPUL_HIGH		(0 << 9)
> > +#define MX25_ADCQ_CFG_XPUL_OFF		(1 << 9)
> > +#define MX25_ADCQ_CFG_REFP(sel)		((sel) << 7)
> > +#define MX25_ADCQ_CFG_REFP_YP		MX25_ADCQ_CFG_REFP(0)
> > +#define MX25_ADCQ_CFG_REFP_XP		MX25_ADCQ_CFG_REFP(1)
> > +#define MX25_ADCQ_CFG_REFP_EXT		MX25_ADCQ_CFG_REFP(2)
> > +#define MX25_ADCQ_CFG_REFP_INT		MX25_ADCQ_CFG_REFP(3)
> > +#define MX25_ADCQ_CFG_REFP_MASK		GENMASK(8, 7)
> > +#define MX25_ADCQ_CFG_IN(sel)		((sel) << 4)
> > +#define MX25_ADCQ_CFG_IN_XP		MX25_ADCQ_CFG_IN(0)
> > +#define MX25_ADCQ_CFG_IN_YP		MX25_ADCQ_CFG_IN(1)
> > +#define MX25_ADCQ_CFG_IN_XN		MX25_ADCQ_CFG_IN(2)
> > +#define MX25_ADCQ_CFG_IN_YN		MX25_ADCQ_CFG_IN(3)
> > +#define MX25_ADCQ_CFG_IN_WIPER		MX25_ADCQ_CFG_IN(4)
> > +#define MX25_ADCQ_CFG_IN_AUX0		MX25_ADCQ_CFG_IN(5)
> > +#define MX25_ADCQ_CFG_IN_AUX1		MX25_ADCQ_CFG_IN(6)
> > +#define MX25_ADCQ_CFG_IN_AUX2		MX25_ADCQ_CFG_IN(7)
> > +#define MX25_ADCQ_CFG_REFN(sel)		((sel) << 2)
> > +#define MX25_ADCQ_CFG_REFN_XN		MX25_ADCQ_CFG_REFN(0)
> > +#define MX25_ADCQ_CFG_REFN_YN		MX25_ADCQ_CFG_REFN(1)
> > +#define MX25_ADCQ_CFG_REFN_NGND		MX25_ADCQ_CFG_REFN(2)
> > +#define MX25_ADCQ_CFG_REFN_NGND2	MX25_ADCQ_CFG_REFN(3)
> > +#define MX25_ADCQ_CFG_REFN_MASK		GENMASK(3, 2)
> > +#define MX25_ADCQ_CFG_PENIACK		(1 << 1)
> > +
> > +#endif  /* _LINUX_INCLUDE_MFD_IMX25_TSADC_H_ */
> > 
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v8 4/8] mfd: fsl imx25 Touchscreen ADC driver
@ 2015-11-23  9:17         ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-23  9:17 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Jonathan Cameron, Shawn Guo, Dmitry Torokhov, Lee Jones,
	devicetree, Eric Bénard, linux-iio, Hartmut Knaack,
	Denis Carikli, Sascha Hauer, linux-input, Fabio Estevam

[-- Attachment #1: Type: text/plain, Size: 17252 bytes --]

On Saturday 21 November 2015 17:02:34 Jonathan Cameron wrote:
> On 16/11/15 12:01, Markus Pargmann wrote:
> > This is the core driver for imx25 touchscreen/adc driver. The module
> > has one shared ADC and two different conversion queues which use the
> > ADC. The two queues are identical. Both can be used for general purpose
> > ADC but one is meant to be used for touchscreens.
> > 
> > This driver is the core which manages the central components and
> > registers of the TSC/ADC unit. It manages the IRQs and forwards them to
> > the correct components.
> > 
> > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > Signed-off-by: Denis Carikli <denis@eukrea.com>
> > 
> > [ensure correct ADC clock depending on the IPG clock]
> > Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
> Looks good to me - one query on meaning of a comment inline.
> 
> Acked-by: Jonathan Cameron <jic23@kernel.org>

Thanks, comment inline.

> 
> I'm taking the view this series wants to go through the MFD tree
> but don't mind if it goes through IIO or input instead
> if there is a good reason to do so.
> 
> Jonathan
> > ---
> > 
> > Notes:
> >     Changes in v7:
> >      - Cleanup bit defines in header files to be more readable
> >      - Fix irq check to return with an error for irq <= 0
> >      - Add COMPILE_TEST in Kconfig file
> >     
> >     Changes in v5:
> >      - Remove ifdef CONFIG_OF as this driver is only for DT usage
> >      - Remove module owner
> >      - Add Kconfig dependencies ARCH_MX25 and OF
> >     
> >     @Jonathan Cameron:
> >     I left your acked-by on the patch as these were small changes. If it should be
> >     removed, please say so. Thanks
> > 
> >  drivers/mfd/Kconfig             |   9 ++
> >  drivers/mfd/Makefile            |   2 +
> >  drivers/mfd/fsl-imx25-tsadc.c   | 204 ++++++++++++++++++++++++++++++++++++++++
> >  include/linux/mfd/imx25-tsadc.h | 140 +++++++++++++++++++++++++++
> >  4 files changed, 355 insertions(+)
> >  create mode 100644 drivers/mfd/fsl-imx25-tsadc.c
> >  create mode 100644 include/linux/mfd/imx25-tsadc.h
> > 
> > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> > index 4d92df6ef9fe..4222e202ad2b 100644
> > --- a/drivers/mfd/Kconfig
> > +++ b/drivers/mfd/Kconfig
> > @@ -271,6 +271,15 @@ config MFD_MC13XXX_I2C
> >  	help
> >  	  Select this if your MC13xxx is connected via an I2C bus.
> >  
> > +config MFD_MX25_TSADC
> > +	tristate "Freescale i.MX25 integrated Touchscreen and ADC unit"
> > +	select REGMAP_MMIO
> > +	depends on (SOC_IMX25 && OF) || COMPILE_TEST
> > +	help
> > +	  Enable support for the integrated Touchscreen and ADC unit of the
> > +	  i.MX25 processors. They consist of a conversion queue for general
> > +	  purpose ADC and a queue for Touchscreens.
> > +
> >  config MFD_HI6421_PMIC
> >  	tristate "HiSilicon Hi6421 PMU/Codec IC"
> >  	depends on OF
> > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> > index a8b76b81b467..5741be88c173 100644
> > --- a/drivers/mfd/Makefile
> > +++ b/drivers/mfd/Makefile
> > @@ -81,6 +81,8 @@ obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
> >  obj-$(CONFIG_MFD_TWL4030_AUDIO)	+= twl4030-audio.o
> >  obj-$(CONFIG_TWL6040_CORE)	+= twl6040.o
> >  
> > +obj-$(CONFIG_MFD_MX25_TSADC)	+= fsl-imx25-tsadc.o
> > +
> >  obj-$(CONFIG_MFD_MC13XXX)	+= mc13xxx-core.o
> >  obj-$(CONFIG_MFD_MC13XXX_SPI)	+= mc13xxx-spi.o
> >  obj-$(CONFIG_MFD_MC13XXX_I2C)	+= mc13xxx-i2c.o
> > diff --git a/drivers/mfd/fsl-imx25-tsadc.c b/drivers/mfd/fsl-imx25-tsadc.c
> > new file mode 100644
> > index 000000000000..e67d5ca81e10
> > --- /dev/null
> > +++ b/drivers/mfd/fsl-imx25-tsadc.c
> > @@ -0,0 +1,204 @@
> > +/*
> > + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it under
> > + * the terms of the GNU General Public License version 2 as published by the
> > + * Free Software Foundation.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/irqdesc.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/irq.h>
> > +#include <linux/mfd/imx25-tsadc.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +
> > +static struct regmap_config mx25_tsadc_regmap_config = {
> > +	.fast_io = true,
> > +	.max_register = 8,
> > +	.reg_bits = 32,
> > +	.val_bits = 32,
> > +	.reg_stride = 4,
> > +};
> > +
> > +static void mx25_tsadc_irq_handler(struct irq_desc *desc)
> > +{
> > +	struct mx25_tsadc *tsadc = irq_desc_get_handler_data(desc);
> > +	struct irq_chip *chip = irq_desc_get_chip(desc);
> > +	u32 status;
> > +
> > +	chained_irq_enter(chip, desc);
> > +
> > +	regmap_read(tsadc->regs, MX25_TSC_TGSR, &status);
> > +
> > +	if (status & MX25_TGSR_GCQ_INT)
> > +		generic_handle_irq(irq_find_mapping(tsadc->domain, 1));
> > +
> > +	if (status & MX25_TGSR_TCQ_INT)
> > +		generic_handle_irq(irq_find_mapping(tsadc->domain, 0));
> > +
> > +	chained_irq_exit(chip, desc);
> > +}
> > +
> > +static int mx25_tsadc_domain_map(struct irq_domain *d, unsigned int irq,
> > +				 irq_hw_number_t hwirq)
> > +{
> > +	struct mx25_tsadc *tsadc = d->host_data;
> > +
> > +	irq_set_chip_data(irq, tsadc);
> > +	irq_set_chip_and_handler(irq, &dummy_irq_chip,
> > +				 handle_level_irq);
> > +	irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE);
> > +
> > +	return 0;
> > +}
> > +
> > +static struct irq_domain_ops mx25_tsadc_domain_ops = {
> > +	.map = mx25_tsadc_domain_map,
> > +	.xlate = irq_domain_xlate_onecell,
> > +};
> > +
> > +static int mx25_tsadc_setup_irq(struct platform_device *pdev,
> > +				struct mx25_tsadc *tsadc)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct device_node *np = dev->of_node;
> > +	int irq;
> > +
> > +	irq = platform_get_irq(pdev, 0);
> > +	if (irq <= 0) {
> > +		dev_err(dev, "Failed to get irq\n");
> > +		return irq;
> > +	}
> > +
> > +	tsadc->domain = irq_domain_add_simple(np, 2, 0, &mx25_tsadc_domain_ops,
> > +					      tsadc);
> > +	if (!tsadc->domain) {
> > +		dev_err(dev, "Failed to add irq domain\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	irq_set_chained_handler(irq, mx25_tsadc_irq_handler);
> > +	irq_set_handler_data(irq, tsadc);
> > +
> > +	return 0;
> > +}
> > +
> > +static void mx25_tsadc_setup_clk(struct platform_device *pdev,
> > +				 struct mx25_tsadc *tsadc)
> > +{
> > +	unsigned clk_div;
> > +
> > +	/*
> > +	 * According to the datasheet the ADC clock should never
> > +	 * exceed 1,75 MHz. Base clock is the IPG and the ADC unit uses
> > +	 * a funny clock divider. To keep the time constant the ADC needs to do
> > +	 * one conversion, adapt the ADC internal clock divider to
> > +	 * the available IPG
> I'm not entirely following this explanation...
> maybe

Thanks, this is indeed a bit misleading. It should be something like this:

	To keep the ADC conversion time constant adapt the ADC internal clock
	divider to the IPG clock rate.

Will fix it.

Best Regards,

Markus

> 
> ...one conversion, then adapt the ADC...
> 
> > +	 */
> > +
> > +	dev_dbg(&pdev->dev, "Found master clock at %lu Hz\n",
> > +		clk_get_rate(tsadc->clk));
> > +
> > +	clk_div = DIV_ROUND_UP(clk_get_rate(tsadc->clk), 1750000);
> > +	dev_dbg(&pdev->dev, "Setting up ADC clock divider to %u\n", clk_div);
> > +
> > +	/* adc clock = IPG clock / (2 * div + 2) */
> > +	clk_div -= 2;
> > +	clk_div /= 2;
> > +
> > +	/*
> > +	 * the ADC clock divider changes its behaviour when values below 4
> > +	 * are used: it is fixed to "/ 10" in this case
> > +	 */
> > +	clk_div = max_t(unsigned, 4, clk_div);
> > +
> > +	dev_dbg(&pdev->dev, "Resulting ADC conversion clock at %lu Hz\n",
> > +		clk_get_rate(tsadc->clk) / (2 * clk_div + 2));
> > +
> > +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR,
> > +			   MX25_TGCR_ADCCLKCFG(0x1f),
> > +			   MX25_TGCR_ADCCLKCFG(clk_div));
> > +}
> > +
> > +static int mx25_tsadc_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct device_node *np = dev->of_node;
> > +	struct mx25_tsadc *tsadc;
> > +	struct resource *res;
> > +	int ret;
> > +	void __iomem *iomem;
> > +
> > +	tsadc = devm_kzalloc(dev, sizeof(*tsadc), GFP_KERNEL);
> > +	if (!tsadc)
> > +		return -ENOMEM;
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	iomem = devm_ioremap_resource(dev, res);
> > +	if (IS_ERR(iomem))
> > +		return PTR_ERR(iomem);
> > +
> > +	tsadc->regs = devm_regmap_init_mmio(dev, iomem,
> > +					    &mx25_tsadc_regmap_config);
> > +	if (IS_ERR(tsadc->regs)) {
> > +		dev_err(dev, "Failed to initialize regmap\n");
> > +		return PTR_ERR(tsadc->regs);
> > +	}
> > +
> > +	tsadc->clk = devm_clk_get(dev, "ipg");
> > +	if (IS_ERR(tsadc->clk)) {
> > +		dev_err(dev, "Failed to get ipg clock\n");
> > +		return PTR_ERR(tsadc->clk);
> > +	}
> > +
> > +	/* setup clock according to the datasheet */
> > +	mx25_tsadc_setup_clk(pdev, tsadc);
> > +
> > +	/* Enable clock and reset the component */
> > +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_CLK_EN,
> > +			   MX25_TGCR_CLK_EN);
> > +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_TSC_RST,
> > +			   MX25_TGCR_TSC_RST);
> > +
> > +	/* Setup powersaving mode, but enable internal reference voltage */
> > +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_POWERMODE_MASK,
> > +			   MX25_TGCR_POWERMODE_SAVE);
> > +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_INTREFEN,
> > +			   MX25_TGCR_INTREFEN);
> > +
> > +	ret = mx25_tsadc_setup_irq(pdev, tsadc);
> > +	if (ret)
> > +		return ret;
> > +
> > +	platform_set_drvdata(pdev, tsadc);
> > +
> > +	of_platform_populate(np, NULL, NULL, dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id mx25_tsadc_ids[] = {
> > +	{ .compatible = "fsl,imx25-tsadc" },
> > +	{ /* Sentinel */ }
> > +};
> > +
> > +static struct platform_driver mx25_tsadc_driver = {
> > +	.driver = {
> > +		.name = "mx25-tsadc",
> > +		.of_match_table = of_match_ptr(mx25_tsadc_ids),
> > +	},
> > +	.probe = mx25_tsadc_probe,
> > +};
> > +module_platform_driver(mx25_tsadc_driver);
> > +
> > +MODULE_DESCRIPTION("MFD for ADC/TSC for Freescale mx25");
> > +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_ALIAS("platform:mx25-tsadc");
> > diff --git a/include/linux/mfd/imx25-tsadc.h b/include/linux/mfd/imx25-tsadc.h
> > new file mode 100644
> > index 000000000000..7fe4b8c3baac
> > --- /dev/null
> > +++ b/include/linux/mfd/imx25-tsadc.h
> > @@ -0,0 +1,140 @@
> > +#ifndef _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
> > +#define _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
> > +
> > +struct regmap;
> > +struct clk;
> > +
> > +struct mx25_tsadc {
> > +	struct regmap *regs;
> > +	struct irq_domain *domain;
> > +	struct clk *clk;
> > +};
> > +
> > +#define MX25_TSC_TGCR			0x00
> > +#define MX25_TSC_TGSR			0x04
> > +#define MX25_TSC_TICR			0x08
> > +
> > +/* The same register layout for TC and GC queue */
> > +#define MX25_ADCQ_FIFO			0x00
> > +#define MX25_ADCQ_CR			0x04
> > +#define MX25_ADCQ_SR			0x08
> > +#define MX25_ADCQ_MR			0x0c
> > +#define MX25_ADCQ_ITEM_7_0		0x20
> > +#define MX25_ADCQ_ITEM_15_8		0x24
> > +#define MX25_ADCQ_CFG(n)		(0x40 + ((n) * 0x4))
> > +
> > +#define MX25_ADCQ_MR_MASK		0xffffffff
> > +
> > +/* TGCR */
> > +#define MX25_TGCR_PDBTIME(x)		((x) << 25)
> > +#define MX25_TGCR_PDBTIME_MASK		GENMASK(31, 25)
> > +#define MX25_TGCR_PDBEN			BIT(24)
> > +#define MX25_TGCR_PDEN			BIT(23)
> > +#define MX25_TGCR_ADCCLKCFG(x)		((x) << 16)
> > +#define MX25_TGCR_GET_ADCCLK(x)		(((x) >> 16) & 0x1f)
> > +#define MX25_TGCR_INTREFEN		BIT(10)
> > +#define MX25_TGCR_POWERMODE_MASK	GENMASK(9, 8)
> > +#define MX25_TGCR_POWERMODE_SAVE	(1 << 8)
> > +#define MX25_TGCR_POWERMODE_ON		(2 << 8)
> > +#define MX25_TGCR_STLC			BIT(5)
> > +#define MX25_TGCR_SLPC			BIT(4)
> > +#define MX25_TGCR_FUNC_RST		BIT(2)
> > +#define MX25_TGCR_TSC_RST		BIT(1)
> > +#define MX25_TGCR_CLK_EN		BIT(0)
> > +
> > +/* TGSR */
> > +#define MX25_TGSR_SLP_INT		BIT(2)
> > +#define MX25_TGSR_GCQ_INT		BIT(1)
> > +#define MX25_TGSR_TCQ_INT		BIT(0)
> > +
> > +/* ADCQ_ITEM_* */
> > +#define _MX25_ADCQ_ITEM(item, x)	((x) << ((item) * 4))
> > +#define MX25_ADCQ_ITEM(item, x)		((item) >= 8 ? \
> > +		_MX25_ADCQ_ITEM((item) - 8, (x)) : _MX25_ADCQ_ITEM((item), (x)))
> > +
> > +/* ADCQ_FIFO (TCQFIFO and GCQFIFO) */
> > +#define MX25_ADCQ_FIFO_DATA(x)		(((x) >> 4) & 0xfff)
> > +#define MX25_ADCQ_FIFO_ID(x)		((x) & 0xf)
> > +
> > +/* ADCQ_CR (TCQR and GCQR) */
> > +#define MX25_ADCQ_CR_PDCFG_LEVEL	BIT(19)
> > +#define MX25_ADCQ_CR_PDMSK		BIT(18)
> > +#define MX25_ADCQ_CR_FRST		BIT(17)
> > +#define MX25_ADCQ_CR_QRST		BIT(16)
> > +#define MX25_ADCQ_CR_RWAIT_MASK		GENMASK(15, 12)
> > +#define MX25_ADCQ_CR_RWAIT(x)		((x) << 12)
> > +#define MX25_ADCQ_CR_WMRK_MASK		GENMASK(11, 8)
> > +#define MX25_ADCQ_CR_WMRK(x)		((x) << 8)
> > +#define MX25_ADCQ_CR_LITEMID_MASK	(0xf << 4)
> > +#define MX25_ADCQ_CR_LITEMID(x)		((x) << 4)
> > +#define MX25_ADCQ_CR_RPT		BIT(3)
> > +#define MX25_ADCQ_CR_FQS		BIT(2)
> > +#define MX25_ADCQ_CR_QSM_MASK		GENMASK(1, 0)
> > +#define MX25_ADCQ_CR_QSM_PD		0x1
> > +#define MX25_ADCQ_CR_QSM_FQS		0x2
> > +#define MX25_ADCQ_CR_QSM_FQS_PD		0x3
> > +
> > +/* ADCQ_SR (TCQSR and GCQSR) */
> > +#define MX25_ADCQ_SR_FDRY		BIT(15)
> > +#define MX25_ADCQ_SR_FULL		BIT(14)
> > +#define MX25_ADCQ_SR_EMPT		BIT(13)
> > +#define MX25_ADCQ_SR_FDN(x)		(((x) >> 8) & 0x1f)
> > +#define MX25_ADCQ_SR_FRR		BIT(6)
> > +#define MX25_ADCQ_SR_FUR		BIT(5)
> > +#define MX25_ADCQ_SR_FOR		BIT(4)
> > +#define MX25_ADCQ_SR_EOQ		BIT(1)
> > +#define MX25_ADCQ_SR_PD			BIT(0)
> > +
> > +/* ADCQ_MR (TCQMR and GCQMR) */
> > +#define MX25_ADCQ_MR_FDRY_DMA		BIT(31)
> > +#define MX25_ADCQ_MR_FER_DMA		BIT(22)
> > +#define MX25_ADCQ_MR_FUR_DMA		BIT(21)
> > +#define MX25_ADCQ_MR_FOR_DMA		BIT(20)
> > +#define MX25_ADCQ_MR_EOQ_DMA		BIT(17)
> > +#define MX25_ADCQ_MR_PD_DMA		BIT(16)
> > +#define MX25_ADCQ_MR_FDRY_IRQ		BIT(15)
> > +#define MX25_ADCQ_MR_FER_IRQ		BIT(6)
> > +#define MX25_ADCQ_MR_FUR_IRQ		BIT(5)
> > +#define MX25_ADCQ_MR_FOR_IRQ		BIT(4)
> > +#define MX25_ADCQ_MR_EOQ_IRQ		BIT(1)
> > +#define MX25_ADCQ_MR_PD_IRQ		BIT(0)
> > +
> > +/* ADCQ_CFG (TICR, TCC0-7,GCC0-7) */
> > +#define MX25_ADCQ_CFG_SETTLING_TIME(x)	((x) << 24)
> > +#define MX25_ADCQ_CFG_IGS		(1 << 20)
> > +#define MX25_ADCQ_CFG_NOS_MASK		GENMASK(19, 16)
> > +#define MX25_ADCQ_CFG_NOS(x)		(((x) - 1) << 16)
> > +#define MX25_ADCQ_CFG_WIPER		(1 << 15)
> > +#define MX25_ADCQ_CFG_YNLR		(1 << 14)
> > +#define MX25_ADCQ_CFG_YPLL_HIGH		(0 << 12)
> > +#define MX25_ADCQ_CFG_YPLL_OFF		(1 << 12)
> > +#define MX25_ADCQ_CFG_YPLL_LOW		(3 << 12)
> > +#define MX25_ADCQ_CFG_XNUR_HIGH		(0 << 10)
> > +#define MX25_ADCQ_CFG_XNUR_OFF		(1 << 10)
> > +#define MX25_ADCQ_CFG_XNUR_LOW		(3 << 10)
> > +#define MX25_ADCQ_CFG_XPUL_HIGH		(0 << 9)
> > +#define MX25_ADCQ_CFG_XPUL_OFF		(1 << 9)
> > +#define MX25_ADCQ_CFG_REFP(sel)		((sel) << 7)
> > +#define MX25_ADCQ_CFG_REFP_YP		MX25_ADCQ_CFG_REFP(0)
> > +#define MX25_ADCQ_CFG_REFP_XP		MX25_ADCQ_CFG_REFP(1)
> > +#define MX25_ADCQ_CFG_REFP_EXT		MX25_ADCQ_CFG_REFP(2)
> > +#define MX25_ADCQ_CFG_REFP_INT		MX25_ADCQ_CFG_REFP(3)
> > +#define MX25_ADCQ_CFG_REFP_MASK		GENMASK(8, 7)
> > +#define MX25_ADCQ_CFG_IN(sel)		((sel) << 4)
> > +#define MX25_ADCQ_CFG_IN_XP		MX25_ADCQ_CFG_IN(0)
> > +#define MX25_ADCQ_CFG_IN_YP		MX25_ADCQ_CFG_IN(1)
> > +#define MX25_ADCQ_CFG_IN_XN		MX25_ADCQ_CFG_IN(2)
> > +#define MX25_ADCQ_CFG_IN_YN		MX25_ADCQ_CFG_IN(3)
> > +#define MX25_ADCQ_CFG_IN_WIPER		MX25_ADCQ_CFG_IN(4)
> > +#define MX25_ADCQ_CFG_IN_AUX0		MX25_ADCQ_CFG_IN(5)
> > +#define MX25_ADCQ_CFG_IN_AUX1		MX25_ADCQ_CFG_IN(6)
> > +#define MX25_ADCQ_CFG_IN_AUX2		MX25_ADCQ_CFG_IN(7)
> > +#define MX25_ADCQ_CFG_REFN(sel)		((sel) << 2)
> > +#define MX25_ADCQ_CFG_REFN_XN		MX25_ADCQ_CFG_REFN(0)
> > +#define MX25_ADCQ_CFG_REFN_YN		MX25_ADCQ_CFG_REFN(1)
> > +#define MX25_ADCQ_CFG_REFN_NGND		MX25_ADCQ_CFG_REFN(2)
> > +#define MX25_ADCQ_CFG_REFN_NGND2	MX25_ADCQ_CFG_REFN(3)
> > +#define MX25_ADCQ_CFG_REFN_MASK		GENMASK(3, 2)
> > +#define MX25_ADCQ_CFG_PENIACK		(1 << 1)
> > +
> > +#endif  /* _LINUX_INCLUDE_MFD_IMX25_TSADC_H_ */
> > 
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* [PATCH v8 4/8] mfd: fsl imx25 Touchscreen ADC driver
@ 2015-11-23  9:17         ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-23  9:17 UTC (permalink / raw)
  To: linux-arm-kernel

On Saturday 21 November 2015 17:02:34 Jonathan Cameron wrote:
> On 16/11/15 12:01, Markus Pargmann wrote:
> > This is the core driver for imx25 touchscreen/adc driver. The module
> > has one shared ADC and two different conversion queues which use the
> > ADC. The two queues are identical. Both can be used for general purpose
> > ADC but one is meant to be used for touchscreens.
> > 
> > This driver is the core which manages the central components and
> > registers of the TSC/ADC unit. It manages the IRQs and forwards them to
> > the correct components.
> > 
> > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > Signed-off-by: Denis Carikli <denis@eukrea.com>
> > 
> > [ensure correct ADC clock depending on the IPG clock]
> > Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
> Looks good to me - one query on meaning of a comment inline.
> 
> Acked-by: Jonathan Cameron <jic23@kernel.org>

Thanks, comment inline.

> 
> I'm taking the view this series wants to go through the MFD tree
> but don't mind if it goes through IIO or input instead
> if there is a good reason to do so.
> 
> Jonathan
> > ---
> > 
> > Notes:
> >     Changes in v7:
> >      - Cleanup bit defines in header files to be more readable
> >      - Fix irq check to return with an error for irq <= 0
> >      - Add COMPILE_TEST in Kconfig file
> >     
> >     Changes in v5:
> >      - Remove ifdef CONFIG_OF as this driver is only for DT usage
> >      - Remove module owner
> >      - Add Kconfig dependencies ARCH_MX25 and OF
> >     
> >     @Jonathan Cameron:
> >     I left your acked-by on the patch as these were small changes. If it should be
> >     removed, please say so. Thanks
> > 
> >  drivers/mfd/Kconfig             |   9 ++
> >  drivers/mfd/Makefile            |   2 +
> >  drivers/mfd/fsl-imx25-tsadc.c   | 204 ++++++++++++++++++++++++++++++++++++++++
> >  include/linux/mfd/imx25-tsadc.h | 140 +++++++++++++++++++++++++++
> >  4 files changed, 355 insertions(+)
> >  create mode 100644 drivers/mfd/fsl-imx25-tsadc.c
> >  create mode 100644 include/linux/mfd/imx25-tsadc.h
> > 
> > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> > index 4d92df6ef9fe..4222e202ad2b 100644
> > --- a/drivers/mfd/Kconfig
> > +++ b/drivers/mfd/Kconfig
> > @@ -271,6 +271,15 @@ config MFD_MC13XXX_I2C
> >  	help
> >  	  Select this if your MC13xxx is connected via an I2C bus.
> >  
> > +config MFD_MX25_TSADC
> > +	tristate "Freescale i.MX25 integrated Touchscreen and ADC unit"
> > +	select REGMAP_MMIO
> > +	depends on (SOC_IMX25 && OF) || COMPILE_TEST
> > +	help
> > +	  Enable support for the integrated Touchscreen and ADC unit of the
> > +	  i.MX25 processors. They consist of a conversion queue for general
> > +	  purpose ADC and a queue for Touchscreens.
> > +
> >  config MFD_HI6421_PMIC
> >  	tristate "HiSilicon Hi6421 PMU/Codec IC"
> >  	depends on OF
> > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> > index a8b76b81b467..5741be88c173 100644
> > --- a/drivers/mfd/Makefile
> > +++ b/drivers/mfd/Makefile
> > @@ -81,6 +81,8 @@ obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
> >  obj-$(CONFIG_MFD_TWL4030_AUDIO)	+= twl4030-audio.o
> >  obj-$(CONFIG_TWL6040_CORE)	+= twl6040.o
> >  
> > +obj-$(CONFIG_MFD_MX25_TSADC)	+= fsl-imx25-tsadc.o
> > +
> >  obj-$(CONFIG_MFD_MC13XXX)	+= mc13xxx-core.o
> >  obj-$(CONFIG_MFD_MC13XXX_SPI)	+= mc13xxx-spi.o
> >  obj-$(CONFIG_MFD_MC13XXX_I2C)	+= mc13xxx-i2c.o
> > diff --git a/drivers/mfd/fsl-imx25-tsadc.c b/drivers/mfd/fsl-imx25-tsadc.c
> > new file mode 100644
> > index 000000000000..e67d5ca81e10
> > --- /dev/null
> > +++ b/drivers/mfd/fsl-imx25-tsadc.c
> > @@ -0,0 +1,204 @@
> > +/*
> > + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it under
> > + * the terms of the GNU General Public License version 2 as published by the
> > + * Free Software Foundation.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/irqdesc.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/irq.h>
> > +#include <linux/mfd/imx25-tsadc.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +
> > +static struct regmap_config mx25_tsadc_regmap_config = {
> > +	.fast_io = true,
> > +	.max_register = 8,
> > +	.reg_bits = 32,
> > +	.val_bits = 32,
> > +	.reg_stride = 4,
> > +};
> > +
> > +static void mx25_tsadc_irq_handler(struct irq_desc *desc)
> > +{
> > +	struct mx25_tsadc *tsadc = irq_desc_get_handler_data(desc);
> > +	struct irq_chip *chip = irq_desc_get_chip(desc);
> > +	u32 status;
> > +
> > +	chained_irq_enter(chip, desc);
> > +
> > +	regmap_read(tsadc->regs, MX25_TSC_TGSR, &status);
> > +
> > +	if (status & MX25_TGSR_GCQ_INT)
> > +		generic_handle_irq(irq_find_mapping(tsadc->domain, 1));
> > +
> > +	if (status & MX25_TGSR_TCQ_INT)
> > +		generic_handle_irq(irq_find_mapping(tsadc->domain, 0));
> > +
> > +	chained_irq_exit(chip, desc);
> > +}
> > +
> > +static int mx25_tsadc_domain_map(struct irq_domain *d, unsigned int irq,
> > +				 irq_hw_number_t hwirq)
> > +{
> > +	struct mx25_tsadc *tsadc = d->host_data;
> > +
> > +	irq_set_chip_data(irq, tsadc);
> > +	irq_set_chip_and_handler(irq, &dummy_irq_chip,
> > +				 handle_level_irq);
> > +	irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE);
> > +
> > +	return 0;
> > +}
> > +
> > +static struct irq_domain_ops mx25_tsadc_domain_ops = {
> > +	.map = mx25_tsadc_domain_map,
> > +	.xlate = irq_domain_xlate_onecell,
> > +};
> > +
> > +static int mx25_tsadc_setup_irq(struct platform_device *pdev,
> > +				struct mx25_tsadc *tsadc)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct device_node *np = dev->of_node;
> > +	int irq;
> > +
> > +	irq = platform_get_irq(pdev, 0);
> > +	if (irq <= 0) {
> > +		dev_err(dev, "Failed to get irq\n");
> > +		return irq;
> > +	}
> > +
> > +	tsadc->domain = irq_domain_add_simple(np, 2, 0, &mx25_tsadc_domain_ops,
> > +					      tsadc);
> > +	if (!tsadc->domain) {
> > +		dev_err(dev, "Failed to add irq domain\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	irq_set_chained_handler(irq, mx25_tsadc_irq_handler);
> > +	irq_set_handler_data(irq, tsadc);
> > +
> > +	return 0;
> > +}
> > +
> > +static void mx25_tsadc_setup_clk(struct platform_device *pdev,
> > +				 struct mx25_tsadc *tsadc)
> > +{
> > +	unsigned clk_div;
> > +
> > +	/*
> > +	 * According to the datasheet the ADC clock should never
> > +	 * exceed 1,75 MHz. Base clock is the IPG and the ADC unit uses
> > +	 * a funny clock divider. To keep the time constant the ADC needs to do
> > +	 * one conversion, adapt the ADC internal clock divider to
> > +	 * the available IPG
> I'm not entirely following this explanation...
> maybe

Thanks, this is indeed a bit misleading. It should be something like this:

	To keep the ADC conversion time constant adapt the ADC internal clock
	divider to the IPG clock rate.

Will fix it.

Best Regards,

Markus

> 
> ...one conversion, then adapt the ADC...
> 
> > +	 */
> > +
> > +	dev_dbg(&pdev->dev, "Found master clock at %lu Hz\n",
> > +		clk_get_rate(tsadc->clk));
> > +
> > +	clk_div = DIV_ROUND_UP(clk_get_rate(tsadc->clk), 1750000);
> > +	dev_dbg(&pdev->dev, "Setting up ADC clock divider to %u\n", clk_div);
> > +
> > +	/* adc clock = IPG clock / (2 * div + 2) */
> > +	clk_div -= 2;
> > +	clk_div /= 2;
> > +
> > +	/*
> > +	 * the ADC clock divider changes its behaviour when values below 4
> > +	 * are used: it is fixed to "/ 10" in this case
> > +	 */
> > +	clk_div = max_t(unsigned, 4, clk_div);
> > +
> > +	dev_dbg(&pdev->dev, "Resulting ADC conversion clock at %lu Hz\n",
> > +		clk_get_rate(tsadc->clk) / (2 * clk_div + 2));
> > +
> > +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR,
> > +			   MX25_TGCR_ADCCLKCFG(0x1f),
> > +			   MX25_TGCR_ADCCLKCFG(clk_div));
> > +}
> > +
> > +static int mx25_tsadc_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct device_node *np = dev->of_node;
> > +	struct mx25_tsadc *tsadc;
> > +	struct resource *res;
> > +	int ret;
> > +	void __iomem *iomem;
> > +
> > +	tsadc = devm_kzalloc(dev, sizeof(*tsadc), GFP_KERNEL);
> > +	if (!tsadc)
> > +		return -ENOMEM;
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	iomem = devm_ioremap_resource(dev, res);
> > +	if (IS_ERR(iomem))
> > +		return PTR_ERR(iomem);
> > +
> > +	tsadc->regs = devm_regmap_init_mmio(dev, iomem,
> > +					    &mx25_tsadc_regmap_config);
> > +	if (IS_ERR(tsadc->regs)) {
> > +		dev_err(dev, "Failed to initialize regmap\n");
> > +		return PTR_ERR(tsadc->regs);
> > +	}
> > +
> > +	tsadc->clk = devm_clk_get(dev, "ipg");
> > +	if (IS_ERR(tsadc->clk)) {
> > +		dev_err(dev, "Failed to get ipg clock\n");
> > +		return PTR_ERR(tsadc->clk);
> > +	}
> > +
> > +	/* setup clock according to the datasheet */
> > +	mx25_tsadc_setup_clk(pdev, tsadc);
> > +
> > +	/* Enable clock and reset the component */
> > +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_CLK_EN,
> > +			   MX25_TGCR_CLK_EN);
> > +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_TSC_RST,
> > +			   MX25_TGCR_TSC_RST);
> > +
> > +	/* Setup powersaving mode, but enable internal reference voltage */
> > +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_POWERMODE_MASK,
> > +			   MX25_TGCR_POWERMODE_SAVE);
> > +	regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_INTREFEN,
> > +			   MX25_TGCR_INTREFEN);
> > +
> > +	ret = mx25_tsadc_setup_irq(pdev, tsadc);
> > +	if (ret)
> > +		return ret;
> > +
> > +	platform_set_drvdata(pdev, tsadc);
> > +
> > +	of_platform_populate(np, NULL, NULL, dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id mx25_tsadc_ids[] = {
> > +	{ .compatible = "fsl,imx25-tsadc" },
> > +	{ /* Sentinel */ }
> > +};
> > +
> > +static struct platform_driver mx25_tsadc_driver = {
> > +	.driver = {
> > +		.name = "mx25-tsadc",
> > +		.of_match_table = of_match_ptr(mx25_tsadc_ids),
> > +	},
> > +	.probe = mx25_tsadc_probe,
> > +};
> > +module_platform_driver(mx25_tsadc_driver);
> > +
> > +MODULE_DESCRIPTION("MFD for ADC/TSC for Freescale mx25");
> > +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_ALIAS("platform:mx25-tsadc");
> > diff --git a/include/linux/mfd/imx25-tsadc.h b/include/linux/mfd/imx25-tsadc.h
> > new file mode 100644
> > index 000000000000..7fe4b8c3baac
> > --- /dev/null
> > +++ b/include/linux/mfd/imx25-tsadc.h
> > @@ -0,0 +1,140 @@
> > +#ifndef _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
> > +#define _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
> > +
> > +struct regmap;
> > +struct clk;
> > +
> > +struct mx25_tsadc {
> > +	struct regmap *regs;
> > +	struct irq_domain *domain;
> > +	struct clk *clk;
> > +};
> > +
> > +#define MX25_TSC_TGCR			0x00
> > +#define MX25_TSC_TGSR			0x04
> > +#define MX25_TSC_TICR			0x08
> > +
> > +/* The same register layout for TC and GC queue */
> > +#define MX25_ADCQ_FIFO			0x00
> > +#define MX25_ADCQ_CR			0x04
> > +#define MX25_ADCQ_SR			0x08
> > +#define MX25_ADCQ_MR			0x0c
> > +#define MX25_ADCQ_ITEM_7_0		0x20
> > +#define MX25_ADCQ_ITEM_15_8		0x24
> > +#define MX25_ADCQ_CFG(n)		(0x40 + ((n) * 0x4))
> > +
> > +#define MX25_ADCQ_MR_MASK		0xffffffff
> > +
> > +/* TGCR */
> > +#define MX25_TGCR_PDBTIME(x)		((x) << 25)
> > +#define MX25_TGCR_PDBTIME_MASK		GENMASK(31, 25)
> > +#define MX25_TGCR_PDBEN			BIT(24)
> > +#define MX25_TGCR_PDEN			BIT(23)
> > +#define MX25_TGCR_ADCCLKCFG(x)		((x) << 16)
> > +#define MX25_TGCR_GET_ADCCLK(x)		(((x) >> 16) & 0x1f)
> > +#define MX25_TGCR_INTREFEN		BIT(10)
> > +#define MX25_TGCR_POWERMODE_MASK	GENMASK(9, 8)
> > +#define MX25_TGCR_POWERMODE_SAVE	(1 << 8)
> > +#define MX25_TGCR_POWERMODE_ON		(2 << 8)
> > +#define MX25_TGCR_STLC			BIT(5)
> > +#define MX25_TGCR_SLPC			BIT(4)
> > +#define MX25_TGCR_FUNC_RST		BIT(2)
> > +#define MX25_TGCR_TSC_RST		BIT(1)
> > +#define MX25_TGCR_CLK_EN		BIT(0)
> > +
> > +/* TGSR */
> > +#define MX25_TGSR_SLP_INT		BIT(2)
> > +#define MX25_TGSR_GCQ_INT		BIT(1)
> > +#define MX25_TGSR_TCQ_INT		BIT(0)
> > +
> > +/* ADCQ_ITEM_* */
> > +#define _MX25_ADCQ_ITEM(item, x)	((x) << ((item) * 4))
> > +#define MX25_ADCQ_ITEM(item, x)		((item) >= 8 ? \
> > +		_MX25_ADCQ_ITEM((item) - 8, (x)) : _MX25_ADCQ_ITEM((item), (x)))
> > +
> > +/* ADCQ_FIFO (TCQFIFO and GCQFIFO) */
> > +#define MX25_ADCQ_FIFO_DATA(x)		(((x) >> 4) & 0xfff)
> > +#define MX25_ADCQ_FIFO_ID(x)		((x) & 0xf)
> > +
> > +/* ADCQ_CR (TCQR and GCQR) */
> > +#define MX25_ADCQ_CR_PDCFG_LEVEL	BIT(19)
> > +#define MX25_ADCQ_CR_PDMSK		BIT(18)
> > +#define MX25_ADCQ_CR_FRST		BIT(17)
> > +#define MX25_ADCQ_CR_QRST		BIT(16)
> > +#define MX25_ADCQ_CR_RWAIT_MASK		GENMASK(15, 12)
> > +#define MX25_ADCQ_CR_RWAIT(x)		((x) << 12)
> > +#define MX25_ADCQ_CR_WMRK_MASK		GENMASK(11, 8)
> > +#define MX25_ADCQ_CR_WMRK(x)		((x) << 8)
> > +#define MX25_ADCQ_CR_LITEMID_MASK	(0xf << 4)
> > +#define MX25_ADCQ_CR_LITEMID(x)		((x) << 4)
> > +#define MX25_ADCQ_CR_RPT		BIT(3)
> > +#define MX25_ADCQ_CR_FQS		BIT(2)
> > +#define MX25_ADCQ_CR_QSM_MASK		GENMASK(1, 0)
> > +#define MX25_ADCQ_CR_QSM_PD		0x1
> > +#define MX25_ADCQ_CR_QSM_FQS		0x2
> > +#define MX25_ADCQ_CR_QSM_FQS_PD		0x3
> > +
> > +/* ADCQ_SR (TCQSR and GCQSR) */
> > +#define MX25_ADCQ_SR_FDRY		BIT(15)
> > +#define MX25_ADCQ_SR_FULL		BIT(14)
> > +#define MX25_ADCQ_SR_EMPT		BIT(13)
> > +#define MX25_ADCQ_SR_FDN(x)		(((x) >> 8) & 0x1f)
> > +#define MX25_ADCQ_SR_FRR		BIT(6)
> > +#define MX25_ADCQ_SR_FUR		BIT(5)
> > +#define MX25_ADCQ_SR_FOR		BIT(4)
> > +#define MX25_ADCQ_SR_EOQ		BIT(1)
> > +#define MX25_ADCQ_SR_PD			BIT(0)
> > +
> > +/* ADCQ_MR (TCQMR and GCQMR) */
> > +#define MX25_ADCQ_MR_FDRY_DMA		BIT(31)
> > +#define MX25_ADCQ_MR_FER_DMA		BIT(22)
> > +#define MX25_ADCQ_MR_FUR_DMA		BIT(21)
> > +#define MX25_ADCQ_MR_FOR_DMA		BIT(20)
> > +#define MX25_ADCQ_MR_EOQ_DMA		BIT(17)
> > +#define MX25_ADCQ_MR_PD_DMA		BIT(16)
> > +#define MX25_ADCQ_MR_FDRY_IRQ		BIT(15)
> > +#define MX25_ADCQ_MR_FER_IRQ		BIT(6)
> > +#define MX25_ADCQ_MR_FUR_IRQ		BIT(5)
> > +#define MX25_ADCQ_MR_FOR_IRQ		BIT(4)
> > +#define MX25_ADCQ_MR_EOQ_IRQ		BIT(1)
> > +#define MX25_ADCQ_MR_PD_IRQ		BIT(0)
> > +
> > +/* ADCQ_CFG (TICR, TCC0-7,GCC0-7) */
> > +#define MX25_ADCQ_CFG_SETTLING_TIME(x)	((x) << 24)
> > +#define MX25_ADCQ_CFG_IGS		(1 << 20)
> > +#define MX25_ADCQ_CFG_NOS_MASK		GENMASK(19, 16)
> > +#define MX25_ADCQ_CFG_NOS(x)		(((x) - 1) << 16)
> > +#define MX25_ADCQ_CFG_WIPER		(1 << 15)
> > +#define MX25_ADCQ_CFG_YNLR		(1 << 14)
> > +#define MX25_ADCQ_CFG_YPLL_HIGH		(0 << 12)
> > +#define MX25_ADCQ_CFG_YPLL_OFF		(1 << 12)
> > +#define MX25_ADCQ_CFG_YPLL_LOW		(3 << 12)
> > +#define MX25_ADCQ_CFG_XNUR_HIGH		(0 << 10)
> > +#define MX25_ADCQ_CFG_XNUR_OFF		(1 << 10)
> > +#define MX25_ADCQ_CFG_XNUR_LOW		(3 << 10)
> > +#define MX25_ADCQ_CFG_XPUL_HIGH		(0 << 9)
> > +#define MX25_ADCQ_CFG_XPUL_OFF		(1 << 9)
> > +#define MX25_ADCQ_CFG_REFP(sel)		((sel) << 7)
> > +#define MX25_ADCQ_CFG_REFP_YP		MX25_ADCQ_CFG_REFP(0)
> > +#define MX25_ADCQ_CFG_REFP_XP		MX25_ADCQ_CFG_REFP(1)
> > +#define MX25_ADCQ_CFG_REFP_EXT		MX25_ADCQ_CFG_REFP(2)
> > +#define MX25_ADCQ_CFG_REFP_INT		MX25_ADCQ_CFG_REFP(3)
> > +#define MX25_ADCQ_CFG_REFP_MASK		GENMASK(8, 7)
> > +#define MX25_ADCQ_CFG_IN(sel)		((sel) << 4)
> > +#define MX25_ADCQ_CFG_IN_XP		MX25_ADCQ_CFG_IN(0)
> > +#define MX25_ADCQ_CFG_IN_YP		MX25_ADCQ_CFG_IN(1)
> > +#define MX25_ADCQ_CFG_IN_XN		MX25_ADCQ_CFG_IN(2)
> > +#define MX25_ADCQ_CFG_IN_YN		MX25_ADCQ_CFG_IN(3)
> > +#define MX25_ADCQ_CFG_IN_WIPER		MX25_ADCQ_CFG_IN(4)
> > +#define MX25_ADCQ_CFG_IN_AUX0		MX25_ADCQ_CFG_IN(5)
> > +#define MX25_ADCQ_CFG_IN_AUX1		MX25_ADCQ_CFG_IN(6)
> > +#define MX25_ADCQ_CFG_IN_AUX2		MX25_ADCQ_CFG_IN(7)
> > +#define MX25_ADCQ_CFG_REFN(sel)		((sel) << 2)
> > +#define MX25_ADCQ_CFG_REFN_XN		MX25_ADCQ_CFG_REFN(0)
> > +#define MX25_ADCQ_CFG_REFN_YN		MX25_ADCQ_CFG_REFN(1)
> > +#define MX25_ADCQ_CFG_REFN_NGND		MX25_ADCQ_CFG_REFN(2)
> > +#define MX25_ADCQ_CFG_REFN_NGND2	MX25_ADCQ_CFG_REFN(3)
> > +#define MX25_ADCQ_CFG_REFN_MASK		GENMASK(3, 2)
> > +#define MX25_ADCQ_CFG_PENIACK		(1 << 1)
> > +
> > +#endif  /* _LINUX_INCLUDE_MFD_IMX25_TSADC_H_ */
> > 
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: This is a digitally signed message part.
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20151123/4791d005/attachment-0001.sig>

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

* Re: [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
  2015-11-21 17:48       ` Jonathan Cameron
@ 2015-11-23 13:43         ` Markus Pargmann
  -1 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-23 13:43 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Shawn Guo, Dmitry Torokhov, Lee Jones, Denis Carikli,
	Eric Bénard, Sascha Hauer, devicetree, linux-input,
	linux-iio, linux-arm-kernel, Hartmut Knaack, Fabio Estevam,
	Juergen Borleis

[-- Attachment #1: Type: text/plain, Size: 27551 bytes --]

On Saturday 21 November 2015 17:48:10 Jonathan Cameron wrote:
> On 16/11/15 12:01, Markus Pargmann wrote:
> > This is a driver for the imx25 ADC/TSC module. It controls the
> > touchscreen conversion queue and creates a touchscreen input device.
> > The driver currently only supports 4 wire touchscreens. The driver uses
> > a simple conversion queue of precharge, touch detection, X measurement,
> > Y measurement, precharge and another touch detection.
> > 
> > This driver uses the regmap from the parent to setup some touch specific
> > settings in the core driver and setup a idle configuration with touch
> > detection.
> > 
> > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > Signed-off-by: Denis Carikli <denis@eukrea.com>
> > 
> > [fix clock's period calculation]
> > [fix calculation of the 'settling' value]
> > Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
> I read this out of curiousity to see how you had handled the touchscreen
> usecase of this hardware differently from the generic version.
> 
> Anyhow, a few little bits and pieces inline from me as a result
> 
> Jonathan
> > ---
> > 
> > Notes:
> >     Changes in v7:
> >      - Moved clk_prepare_enable() and mx25_tcq_init() into mx25_tcq_open(). This
> >        was done to be able to use devm_request_threaded_irq().
> >      - Cleanup of the probe function through above change
> >      - Removed mx25_tcq_remove(), not necessary now
> > 
> >  drivers/input/touchscreen/Kconfig         |   6 +
> >  drivers/input/touchscreen/Makefile        |   1 +
> >  drivers/input/touchscreen/fsl-imx25-tcq.c | 600 ++++++++++++++++++++++++++++++
> >  3 files changed, 607 insertions(+)
> >  create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
> > 
> > diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> > index ae33da7ab51f..b44651d33080 100644
> > --- a/drivers/input/touchscreen/Kconfig
> > +++ b/drivers/input/touchscreen/Kconfig
> > @@ -811,6 +811,12 @@ config TOUCHSCREEN_USB_COMPOSITE
> >  	  To compile this driver as a module, choose M here: the
> >  	  module will be called usbtouchscreen.
> >  
> > +config TOUCHSCREEN_MX25
> > +	tristate "Freescale i.MX25 touchscreen input driver"
> > +	depends on MFD_MX25_TSADC
> > +	help
> > +	  Enable support for touchscreen connected to your i.MX25.
> > +
> >  config TOUCHSCREEN_MC13783
> >  	tristate "Freescale MC13783 touchscreen input driver"
> >  	depends on MFD_MC13XXX
> > diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> > index cbaa6abb08da..77a2ac54101a 100644
> > --- a/drivers/input/touchscreen/Makefile
> > +++ b/drivers/input/touchscreen/Makefile
> > @@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
> >  obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
> >  obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
> >  obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
> > +obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
> >  obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
> >  obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
> >  obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
> > diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
> > new file mode 100644
> > index 000000000000..c833cd814972
> > --- /dev/null
> > +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
> > @@ -0,0 +1,600 @@
> > +/*
> > + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it under
> > + * the terms of the GNU General Public License version 2 as published by the
> > + * Free Software Foundation.
> > + *
> > + * Based on driver from 2011:
> > + *   Juergen Beisert, Pengutronix <kernel@pengutronix.de>
> > + *
> > + * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
> > + * connected to the imx25 ADC.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/device.h>
> > +#include <linux/input.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/mfd/imx25-tsadc.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +
> > +static const char mx25_tcq_name[] = "mx25-tcq";
> > +
> > +enum mx25_tcq_mode {
> > +	MX25_TS_4WIRE,
> > +};
> > +
> > +struct mx25_tcq_priv {
> > +	struct regmap *regs;
> > +	struct regmap *core_regs;
> > +	struct input_dev *idev;
> > +	enum mx25_tcq_mode mode;
> > +	unsigned int pen_threshold;
> > +	unsigned int sample_count;
> > +	unsigned int expected_samples;
> > +	unsigned int pen_debounce;
> > +	unsigned int settling_time;
> > +	struct clk *clk;
> > +	int irq;
> > +};
> > +
> > +static struct regmap_config mx25_tcq_regconfig = {
> > +	.fast_io = true,
> > +	.max_register = 0x5c,
> > +	.reg_bits = 32,
> > +	.val_bits = 32,
> > +	.reg_stride = 4,
> > +};
> > +
> > +static const struct of_device_id mx25_tcq_ids[] = {
> > +	{ .compatible = "fsl,imx25-tcq", },
> > +	{ /* Sentinel */ }
> > +};
> > +
> > +#define TSC_4WIRE_PRE_INDEX 0
> > +#define TSC_4WIRE_X_INDEX 1
> > +#define TSC_4WIRE_Y_INDEX 2
> > +#define TSC_4WIRE_POST_INDEX 3
> > +#define TSC_4WIRE_LEAVE 4
> > +
> > +#define MX25_TSC_DEF_THRESHOLD 80
> > +#define TSC_MAX_SAMPLES 16
> > +
> > +#define MX25_TSC_REPEAT_WAIT 14
> > +
> > +enum mx25_adc_configurations {
> > +	MX25_CFG_PRECHARGE = 0,
> > +	MX25_CFG_TOUCH_DETECT,
> > +	MX25_CFG_X_MEASUREMENT,
> > +	MX25_CFG_Y_MEASUREMENT,
> > +};
> > +
> > +#define MX25_PRECHARGE_VALUE (\
> > +			MX25_ADCQ_CFG_YPLL_OFF | \
> > +			MX25_ADCQ_CFG_XNUR_OFF | \
> > +			MX25_ADCQ_CFG_XPUL_HIGH | \
> > +			MX25_ADCQ_CFG_REFP_INT | \
> > +			MX25_ADCQ_CFG_IN_XP | \
> > +			MX25_ADCQ_CFG_REFN_NGND2 | \
> > +			MX25_ADCQ_CFG_IGS)
> > +
> > +#define MX25_TOUCH_DETECT_VALUE (\
> > +			MX25_ADCQ_CFG_YNLR | \
> > +			MX25_ADCQ_CFG_YPLL_OFF | \
> > +			MX25_ADCQ_CFG_XNUR_OFF | \
> > +			MX25_ADCQ_CFG_XPUL_OFF | \
> > +			MX25_ADCQ_CFG_REFP_INT | \
> > +			MX25_ADCQ_CFG_IN_XP | \
> > +			MX25_ADCQ_CFG_REFN_NGND2 | \
> > +			MX25_ADCQ_CFG_PENIACK)
> > +
> > +static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv,
> > +				   unsigned int settling_cnt)
> > +{
> > +	u32 precharge_cfg =
> > +			MX25_PRECHARGE_VALUE |
> > +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> > +	u32 touch_detect_cfg =
> > +			MX25_TOUCH_DETECT_VALUE |
> > +			MX25_ADCQ_CFG_NOS(1) |
> > +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> > +
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg);
> > +
> > +	/* PRECHARGE */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE),
> > +		     precharge_cfg);
> > +
> > +	/* TOUCH_DETECT */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT),
> > +		     touch_detect_cfg);
> > +
> > +	/* X Measurement */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT),
> > +		     MX25_ADCQ_CFG_YPLL_OFF |
> > +		     MX25_ADCQ_CFG_XNUR_LOW |
> > +		     MX25_ADCQ_CFG_XPUL_HIGH |
> > +		     MX25_ADCQ_CFG_REFP_XP |
> > +		     MX25_ADCQ_CFG_IN_YP |
> > +		     MX25_ADCQ_CFG_REFN_XN |
> > +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> > +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> > +
> > +	/* Y Measurement */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT),
> > +		     MX25_ADCQ_CFG_YNLR |
> > +		     MX25_ADCQ_CFG_YPLL_HIGH |
> > +		     MX25_ADCQ_CFG_XNUR_OFF |
> > +		     MX25_ADCQ_CFG_XPUL_OFF |
> > +		     MX25_ADCQ_CFG_REFP_YP |
> > +		     MX25_ADCQ_CFG_IN_XP |
> > +		     MX25_ADCQ_CFG_REFN_YN |
> > +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> > +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> > +
> > +	/* Enable the touch detection right now */
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg |
> > +		     MX25_ADCQ_CFG_IGS);
> > +}
> > +
> > +static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv,
> > +				   unsigned settling_cnt, int *items)
> > +{
> > +	imx25_setup_queue_cfgs(priv, settling_cnt);
> > +
> > +	/* Setup the conversion queue */
> > +	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
> > +		     MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) |
> > +		     MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) |
> > +		     MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) |
> > +		     MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) |
> > +		     MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) |
> > +		     MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT));
> > +
> > +	/*
> > +	 * We measure X/Y with 'sample_count' number of samples and execute a
> > +	 * touch detection twice, with 1 sample each
> > +	 */
> > +	priv->expected_samples = priv->sample_count * 2 + 2;
> > +	*items = 6;
> > +
> > +	return 0;
> > +}
> > +
> Another personal preference. I'd not bother wrapping these single line
> calls up but rather just make them inline.  They don't in of
> themselves add much to my mind.  Still this one is very much up to you
> as far as I'm concerned.
> > +static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK,
> > +			   MX25_ADCQ_CR_PDMSK);
> > +}
> > +
> > +static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0);
> > +}
> > +
> > +static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ,
> > +			   MX25_ADCQ_MR_FDRY_IRQ);
> > +}
> > +
> > +static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0);
> > +}
> > +
> > +static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_FQS,
> > +			   MX25_ADCQ_CR_FQS);
> > +}
> > +
> > +static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_FQS, 0);
> > +}
> > +
> > +static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv)
> > +{
> > +	u32 tcqcr;
> > +
> > +	regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr);
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST,
> > +			   MX25_ADCQ_CR_FRST);
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0);
> > +	regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr);
> > +}
> > +
> > +static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv)
> > +{
> > +	/* stop the queue from looping */
> > +	mx25_tcq_force_queue_stop(priv);
> > +
> > +	/* for a clean touch detection, preload the X plane */
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE);
> > +
> > +	/* waste some time now to pre-load the X plate to high voltage */
> > +	mx25_tcq_fifo_reset(priv);
> > +
> > +	/* re-enable the detection right now */
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR,
> > +		     MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS);
> > +
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD,
> > +			   MX25_ADCQ_SR_PD);
> > +
> > +	/* enable the pen down event to be a source for the interrupt */
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0);
> > +
> > +	/* lets fire the next IRQ if someone touches the touchscreen */
> > +	mx25_tcq_enable_touch_irq(priv);
> > +}
> > +
> > +static int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,
> > +					   u32 *sample_buf,
> > +					   unsigned int samples)
> > +{
> > +	unsigned int x_pos = 0;
> > +	unsigned int y_pos = 0;
> > +	unsigned int touch_pre = 0;
> > +	unsigned int touch_post = 0;
> > +	unsigned int i;
> > +	int ret = 0;
> > +
> > +	for (i = 0; i < samples; i++) {
> > +		unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]);
> > +		unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]);
> > +
> > +		switch (index) {
> > +		case 1:
> > +			touch_pre = val;
> > +			break;
> > +		case 2:
> > +			x_pos = val;
> > +			break;
> > +		case 3:
> > +			y_pos = val;
> > +			break;
> > +		case 5:
> > +			touch_post = val;
> > +			break;
> > +		default:
> > +			ret = -EINVAL;
> > +			break;
> > +		}
> > +	}
> > +
> > +	if (ret == 0 && samples != 0) {
> > +		/*
> > +		 * only if both touch measures are below a threshold,
> > +		 * the position is valid
> > +		 */
> > +		if (touch_pre < priv->pen_threshold &&
> > +		    touch_post < priv->pen_threshold) {
> > +			/* valid samples, generate a report */
> > +			x_pos /= priv->sample_count;
> > +			y_pos /= priv->sample_count;
> > +			input_report_abs(priv->idev, ABS_X, x_pos);
> > +			input_report_abs(priv->idev, ABS_Y, y_pos);
> > +			input_report_key(priv->idev, BTN_TOUCH, 1);
> > +			input_sync(priv->idev);
> > +
> > +			/* get next sample */
> > +			mx25_tcq_enable_fifo_irq(priv);
> > +		} else if (touch_pre >= priv->pen_threshold &&
> > +			   touch_post >= priv->pen_threshold) {
> > +			/*
> > +			 * if both samples are invalid,
> > +			 * generate a release report
> > +			 */
> > +			input_report_key(priv->idev, BTN_TOUCH, 0);
> > +			input_sync(priv->idev);
> > +			mx25_tcq_re_enable_touch_detection(priv);
> > +		} else {
> > +			/*
> > +			 * if only one of both touch measurements are
> > +			 * below the threshold, still some bouncing
> > +			 * happens. Take additional samples in this
> > +			 * case to be sure
> > +			 */
> > +			mx25_tcq_enable_fifo_irq(priv);
> > +		}
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
> > +{
> > +	struct mx25_tcq_priv *priv = dev_id;
> > +	u32 sample_buf[TSC_MAX_SAMPLES];
> > +	unsigned int samples = 0;
> > +
> > +	/* read all samples */
> It's not a terribly big fifo, so I'm not convinced personally
> of the merits of having the separate thread for this interrupt...

Yes that is correct, but it is calling into the input framework as well.
I am not sure how much work is done there. Also the thread shouldn't be
extremely costy as it is mostly sleeping?

> 
> > +	while (1) {
> > +		u32 stats;
> > +
> > +		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
> > +		if (stats & MX25_ADCQ_SR_EMPT)
> > +			break;
> > +
> > +		if (samples < TSC_MAX_SAMPLES) {
> > +			regmap_read(priv->regs, MX25_ADCQ_FIFO,
> > +				    &sample_buf[samples]);
> > +			++samples;
> > +		} else {
> > +			u32 discarded;
> > +			/* discard samples */
> > +			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);
> Comment on how this could happen would be good.

This could happen if there are new samples while we read all samples
from the fifo.

Thanks to your comment I had a closer look on this handler again. The
problem here is that we read x samples while x may be a different number
than the number of samples generated by the conversion queue. But only
want to read samples that were produced by exactly one conversion queue
run.

So I redesigned this handler to first read the number of samples in the
fifo and then read as many as we want.

> 
> > +		}
> > +	}
> > +
> > +	mx25_tcq_create_event_for_4wire(priv, sample_buf, samples);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t mx25_tcq_irq(int irq, void *dev_id)
> > +{
> > +	struct mx25_tcq_priv *priv = dev_id;
> > +	u32 stat;
> > +	int ret = IRQ_HANDLED;
> > +
> > +	regmap_read(priv->regs, MX25_ADCQ_SR, &stat);
> > +
> > +	if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR))
> > +		mx25_tcq_fifo_reset(priv);
> Again, currious cross comparison with the adc driver (hardware is pretty
> much the same ;)  In there you don't reset the fifo if you get one
> of these.  Why not?  Now you should never get one as you only ever schedule
> a single reading, but best to be consistent.

Yes, the conversion queue in the touch driver is running continously so
that this may happen. Actually a fifo reset is not enough here. For the
touch we need to reset the conversion queue as well, thanks.
This could be added to the gcq driver but this should never happen.

> 
> > +
> > +	if (stat & MX25_ADCQ_SR_PD) {
> > +		mx25_tcq_disable_touch_irq(priv);
> > +		mx25_tcq_force_queue_start(priv);
> > +		mx25_tcq_enable_fifo_irq(priv);
> > +	}
> > +
> > +	if (stat & MX25_ADCQ_SR_FDRY) {
> > +		mx25_tcq_disable_fifo_irq(priv);
> I'm missing something I think.. In a oneshot irq the irq will be masked
> anyway till the thread is done.  Why the explicit disable as well?

The driver which receives the interrupts for both units GCQ and TCQ has
a proper interrupt status register. It uses this register to distribute
them to the different units. The problem is that it doesn't have a mask
register. The interrupts can only be masked in the TCQ or GCQ unit. So
by default the interrupt for both units will be masked.

GCQ currently doesn't use any time-critical interrupts. But waiting for
the thread shouldn't block the other unit. So I think it's better to
mask the interrupt here explicitly. This of course should not be with
IRQF_ONESHOT. That's a mistake, will fix that.

> 
> > +		ret = IRQ_WAKE_THREAD;
> > +	}
> > +
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
> > +			   MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
> > +			   MX25_ADCQ_SR_PD | MX25_ADCQ_SR_EOQ,
> > +			   MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR |
> > +			   MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
> > +			   MX25_ADCQ_SR_EOQ);
> As with the adc driver you are clearing at least one bit that should
> not I think ever be set... EOQ.  Why?

Indeed, this is always masked. Thanks, removed that.

> > +
> > +	return ret;
> > +}
> > +
> > +/* configure the statemachine for a 4-wire touchscreen */
> state machine
> > +static int mx25_tcq_init(struct mx25_tcq_priv *priv)
> > +{
> > +	u32 tgcr;
> > +	unsigned int ipg_div;
> > +	unsigned int adc_period;
> > +	unsigned int debounce_cnt;
> > +	unsigned int settling_cnt;
> > +	int itemct;
> > +	int ret;
> > +
> > +	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
> > +	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
> > +	adc_period = USEC_PER_SEC * ipg_div * 2 + 2;
> > +	adc_period /= clk_get_rate(priv->clk) / 1000 + 1;
> > +	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
> > +	settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1;
> > +
> > +	/* Reset */
> > +	regmap_write(priv->regs, MX25_ADCQ_CR,
> > +		     MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST);
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0);
> > +
> > +	/* up to 128 * 8 ADC clocks are possible */
> > +	if (debounce_cnt > 127)
> > +		debounce_cnt = 127;
> > +
> > +	/* up to 255 * 8 ADC clocks are possible */
> > +	if (settling_cnt > 255)
> > +		settling_cnt = 255;
> > +
> > +	ret = imx25_setup_queue_4wire(priv, settling_cnt, &itemct);
> > +	if (ret)
> > +		return ret;
> > +
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK,
> > +			   MX25_ADCQ_CR_LITEMID(itemct - 1) |
> > +			   MX25_ADCQ_CR_WMRK(priv->expected_samples - 1));
> > +
> > +	/* setup debounce count */
> > +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR,
> > +			   MX25_TGCR_PDBTIME_MASK,
> > +			   MX25_TGCR_PDBTIME(debounce_cnt));
> > +
> > +	/* enable debounce */
> > +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN,
> > +			   MX25_TGCR_PDBEN);
> > +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN,
> > +			   MX25_TGCR_PDEN);
> > +
> > +	/* enable the engine on demand */
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK,
> > +			   MX25_ADCQ_CR_QSM_FQS);
> > +
> > +	/* Enable repeat and repeat wait */
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK,
> > +			   MX25_ADCQ_CR_RPT |
> > +			   MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT));
> > +
> > +	mx25_tcq_re_enable_touch_detection(priv);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mx25_tcq_parse_dt(struct platform_device *pdev,
> > +			     struct mx25_tcq_priv *priv)
> > +{
> > +	struct device_node *np = pdev->dev.of_node;
> > +	u32 wires;
> > +	int ret;
> > +
> > +	/* Setup defaults */
> > +	priv->pen_threshold = 500;
> > +	priv->sample_count = 3;
> > +	priv->pen_debounce = 1000000;
> > +	priv->settling_time = 250000;
> > +
> > +	ret = of_property_read_u32(np, "fsl,wires", &wires);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
> > +		return ret;
> > +	}
> > +
> > +	if (wires == 4) {
> > +		priv->mode = MX25_TS_4WIRE;
> > +	} else {
> > +		dev_err(&pdev->dev, "%u-wire mode not supported\n", wires);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* These are optional, we don't care about the return values */
> > +	of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold);
> > +	of_property_read_u32(np, "fsl,settling-time", &priv->settling_time);
> > +	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mx25_tcq_open(struct input_dev *idev)
> > +{
> > +	struct device *dev = &idev->dev;
> > +	struct mx25_tcq_priv *priv = dev_get_drvdata(dev);
> > +	int ret;
> > +
> > +	ret = clk_prepare_enable(priv->clk);
> > +	if (ret) {
> > +		dev_err(dev, "Failed to enable ipg clock\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = mx25_tcq_init(priv);
> > +	if (ret) {
> There is a certain missbalance in naming at least between the open
> and the close.   I'd be tempted to reorganise the two so that
> they obviously balance..  Either split up the init into the various
> things such as enabling the interrupts and bringing the queue up
> (so the oposite of the remove) or wrap the remove calls up in an
> _exit which can easily be compared to the init)
> 
> This definitely comes in the category of writing the code so it
> is 'obviously correct', rather than making review harder!
> 
> There is also some stuff in that init - like enabling debounce that
> isn't undone in the close - to my mind that suggests it doesn't
> belong in this function, but rather in device startup, but I may
> well be missing some interactions in the hardware that prevent this.

Yes, init() is more of a reset_configure_and_init() function. I will try
to make this more obvious.

> 
> > +		dev_err(dev, "Failed to init tcq\n");
> > +		clk_disable_unprepare(priv->clk);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void mx25_tcq_close(struct input_dev *idev)
> > +{
> > +	struct mx25_tcq_priv *priv = input_get_drvdata(idev);
> > +
> > +	mx25_tcq_force_queue_stop(priv);
> > +	mx25_tcq_disable_touch_irq(priv);
> > +	mx25_tcq_disable_fifo_irq(priv);
> > +	clk_disable_unprepare(priv->clk);
> > +}
> > +
> > +static int mx25_tcq_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct input_dev *idev;
> > +	struct mx25_tcq_priv *priv;
> > +	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
> > +	struct resource *res;
> > +	void __iomem *mem;
> > +	int ret;
> > +
> > +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > +	if (!priv)
> > +		return -ENOMEM;
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	mem = devm_ioremap_resource(dev, res);
> > +	if (!mem)
> > +		return -ENOMEM;
> > +
> > +	ret = mx25_tcq_parse_dt(pdev, priv);
> > +	if (ret)
> > +		return ret;
> > +
> > +	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig);
> > +	if (IS_ERR(priv->regs)) {
> > +		dev_err(dev, "Failed to initialize regmap\n");
> > +		return PTR_ERR(priv->regs);
> > +	}
> > +
> > +	priv->irq = platform_get_irq(pdev, 0);
> > +	if (priv->irq <= 0) {
> > +		dev_err(dev, "Failed to get IRQ\n");
> > +		return priv->irq;
> > +	}
> > +
> > +	idev = devm_input_allocate_device(dev);
> > +	if (!idev) {
> > +		dev_err(dev, "Failed to allocate input device\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	idev->name = mx25_tcq_name;
> > +	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> > +	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
> > +	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
> > +	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
> > +
> > +	idev->id.bustype = BUS_HOST;
> > +	idev->open = mx25_tcq_open;
> > +	idev->close = mx25_tcq_close;
> > +
> > +	priv->idev = idev;
> > +	input_set_drvdata(idev, priv);
> > +
> > +	priv->core_regs = tsadc->regs;
> > +	if (!priv->core_regs)
> > +		return -EINVAL;
> > +
> > +	priv->clk = tsadc->clk;
> > +	if (!priv->clk)
> > +		return -EINVAL;
> > +
> > +	platform_set_drvdata(pdev, priv);
> > +
> > +	ret = input_register_device(idev);
> I'm not 100% sure of whether input works the way I think it does ;)
> but I'd imagine this just exposed the userspace interface.  Hence from
> here on you can open the device and cause the interrupt to be enabled.
> It doesn't have a handler until after the request_threaded_irq device
> below...  So nasty if unlikely race condition here.

Yes, fixed as Dmitry suggested.

> 
> > +	if (ret) {
> > +		dev_err(dev, "Failed to register input device\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq,
> > +					mx25_tcq_irq_thread, IRQF_ONESHOT,
> > +					pdev->name, priv);
> currious difference in approach here.  Why a devm call in this driver
> and not in the adc one?  I assumed general paranoia with devm irq requests
> and the various obscure ways they can go wrong so didn't mention it there...

For the gcq driver we have things are a bit different. There is no real
open()/close() function. We configure the unit once (including
interrupts) and want to be careful when shutting down the driver to do
it in the right order of disabling clocks, interrupts and regulators.
The interrupts there are actually never masked there.

In this tcq driver the interrupts are masked and clocks are off when the
input was closed.

Thanks,

Markus

> > +	if (ret) {
> > +		dev_err(dev, "Failed requesting IRQ\n");
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static struct platform_driver mx25_tcq_driver = {
> > +	.driver		= {
> > +		.name	= "mx25-tcq",
> > +		.of_match_table = mx25_tcq_ids,
> > +	},
> > +	.probe		= mx25_tcq_probe,
> > +};
> > +module_platform_driver(mx25_tcq_driver);
> > +
> > +MODULE_DESCRIPTION("TS input driver for Freescale mx25");
> > +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
> > +MODULE_LICENSE("GPL v2");
> > 
> 
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* [PATCH v8 6/8] input: touchscreen: imx25 tcq driver
@ 2015-11-23 13:43         ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-23 13:43 UTC (permalink / raw)
  To: linux-arm-kernel

On Saturday 21 November 2015 17:48:10 Jonathan Cameron wrote:
> On 16/11/15 12:01, Markus Pargmann wrote:
> > This is a driver for the imx25 ADC/TSC module. It controls the
> > touchscreen conversion queue and creates a touchscreen input device.
> > The driver currently only supports 4 wire touchscreens. The driver uses
> > a simple conversion queue of precharge, touch detection, X measurement,
> > Y measurement, precharge and another touch detection.
> > 
> > This driver uses the regmap from the parent to setup some touch specific
> > settings in the core driver and setup a idle configuration with touch
> > detection.
> > 
> > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > Signed-off-by: Denis Carikli <denis@eukrea.com>
> > 
> > [fix clock's period calculation]
> > [fix calculation of the 'settling' value]
> > Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
> I read this out of curiousity to see how you had handled the touchscreen
> usecase of this hardware differently from the generic version.
> 
> Anyhow, a few little bits and pieces inline from me as a result
> 
> Jonathan
> > ---
> > 
> > Notes:
> >     Changes in v7:
> >      - Moved clk_prepare_enable() and mx25_tcq_init() into mx25_tcq_open(). This
> >        was done to be able to use devm_request_threaded_irq().
> >      - Cleanup of the probe function through above change
> >      - Removed mx25_tcq_remove(), not necessary now
> > 
> >  drivers/input/touchscreen/Kconfig         |   6 +
> >  drivers/input/touchscreen/Makefile        |   1 +
> >  drivers/input/touchscreen/fsl-imx25-tcq.c | 600 ++++++++++++++++++++++++++++++
> >  3 files changed, 607 insertions(+)
> >  create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
> > 
> > diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> > index ae33da7ab51f..b44651d33080 100644
> > --- a/drivers/input/touchscreen/Kconfig
> > +++ b/drivers/input/touchscreen/Kconfig
> > @@ -811,6 +811,12 @@ config TOUCHSCREEN_USB_COMPOSITE
> >  	  To compile this driver as a module, choose M here: the
> >  	  module will be called usbtouchscreen.
> >  
> > +config TOUCHSCREEN_MX25
> > +	tristate "Freescale i.MX25 touchscreen input driver"
> > +	depends on MFD_MX25_TSADC
> > +	help
> > +	  Enable support for touchscreen connected to your i.MX25.
> > +
> >  config TOUCHSCREEN_MC13783
> >  	tristate "Freescale MC13783 touchscreen input driver"
> >  	depends on MFD_MC13XXX
> > diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> > index cbaa6abb08da..77a2ac54101a 100644
> > --- a/drivers/input/touchscreen/Makefile
> > +++ b/drivers/input/touchscreen/Makefile
> > @@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
> >  obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
> >  obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
> >  obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
> > +obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
> >  obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
> >  obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
> >  obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
> > diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
> > new file mode 100644
> > index 000000000000..c833cd814972
> > --- /dev/null
> > +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
> > @@ -0,0 +1,600 @@
> > +/*
> > + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it under
> > + * the terms of the GNU General Public License version 2 as published by the
> > + * Free Software Foundation.
> > + *
> > + * Based on driver from 2011:
> > + *   Juergen Beisert, Pengutronix <kernel@pengutronix.de>
> > + *
> > + * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
> > + * connected to the imx25 ADC.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/device.h>
> > +#include <linux/input.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/mfd/imx25-tsadc.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +
> > +static const char mx25_tcq_name[] = "mx25-tcq";
> > +
> > +enum mx25_tcq_mode {
> > +	MX25_TS_4WIRE,
> > +};
> > +
> > +struct mx25_tcq_priv {
> > +	struct regmap *regs;
> > +	struct regmap *core_regs;
> > +	struct input_dev *idev;
> > +	enum mx25_tcq_mode mode;
> > +	unsigned int pen_threshold;
> > +	unsigned int sample_count;
> > +	unsigned int expected_samples;
> > +	unsigned int pen_debounce;
> > +	unsigned int settling_time;
> > +	struct clk *clk;
> > +	int irq;
> > +};
> > +
> > +static struct regmap_config mx25_tcq_regconfig = {
> > +	.fast_io = true,
> > +	.max_register = 0x5c,
> > +	.reg_bits = 32,
> > +	.val_bits = 32,
> > +	.reg_stride = 4,
> > +};
> > +
> > +static const struct of_device_id mx25_tcq_ids[] = {
> > +	{ .compatible = "fsl,imx25-tcq", },
> > +	{ /* Sentinel */ }
> > +};
> > +
> > +#define TSC_4WIRE_PRE_INDEX 0
> > +#define TSC_4WIRE_X_INDEX 1
> > +#define TSC_4WIRE_Y_INDEX 2
> > +#define TSC_4WIRE_POST_INDEX 3
> > +#define TSC_4WIRE_LEAVE 4
> > +
> > +#define MX25_TSC_DEF_THRESHOLD 80
> > +#define TSC_MAX_SAMPLES 16
> > +
> > +#define MX25_TSC_REPEAT_WAIT 14
> > +
> > +enum mx25_adc_configurations {
> > +	MX25_CFG_PRECHARGE = 0,
> > +	MX25_CFG_TOUCH_DETECT,
> > +	MX25_CFG_X_MEASUREMENT,
> > +	MX25_CFG_Y_MEASUREMENT,
> > +};
> > +
> > +#define MX25_PRECHARGE_VALUE (\
> > +			MX25_ADCQ_CFG_YPLL_OFF | \
> > +			MX25_ADCQ_CFG_XNUR_OFF | \
> > +			MX25_ADCQ_CFG_XPUL_HIGH | \
> > +			MX25_ADCQ_CFG_REFP_INT | \
> > +			MX25_ADCQ_CFG_IN_XP | \
> > +			MX25_ADCQ_CFG_REFN_NGND2 | \
> > +			MX25_ADCQ_CFG_IGS)
> > +
> > +#define MX25_TOUCH_DETECT_VALUE (\
> > +			MX25_ADCQ_CFG_YNLR | \
> > +			MX25_ADCQ_CFG_YPLL_OFF | \
> > +			MX25_ADCQ_CFG_XNUR_OFF | \
> > +			MX25_ADCQ_CFG_XPUL_OFF | \
> > +			MX25_ADCQ_CFG_REFP_INT | \
> > +			MX25_ADCQ_CFG_IN_XP | \
> > +			MX25_ADCQ_CFG_REFN_NGND2 | \
> > +			MX25_ADCQ_CFG_PENIACK)
> > +
> > +static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv,
> > +				   unsigned int settling_cnt)
> > +{
> > +	u32 precharge_cfg =
> > +			MX25_PRECHARGE_VALUE |
> > +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> > +	u32 touch_detect_cfg =
> > +			MX25_TOUCH_DETECT_VALUE |
> > +			MX25_ADCQ_CFG_NOS(1) |
> > +			MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt);
> > +
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg);
> > +
> > +	/* PRECHARGE */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE),
> > +		     precharge_cfg);
> > +
> > +	/* TOUCH_DETECT */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT),
> > +		     touch_detect_cfg);
> > +
> > +	/* X Measurement */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT),
> > +		     MX25_ADCQ_CFG_YPLL_OFF |
> > +		     MX25_ADCQ_CFG_XNUR_LOW |
> > +		     MX25_ADCQ_CFG_XPUL_HIGH |
> > +		     MX25_ADCQ_CFG_REFP_XP |
> > +		     MX25_ADCQ_CFG_IN_YP |
> > +		     MX25_ADCQ_CFG_REFN_XN |
> > +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> > +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> > +
> > +	/* Y Measurement */
> > +	regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT),
> > +		     MX25_ADCQ_CFG_YNLR |
> > +		     MX25_ADCQ_CFG_YPLL_HIGH |
> > +		     MX25_ADCQ_CFG_XNUR_OFF |
> > +		     MX25_ADCQ_CFG_XPUL_OFF |
> > +		     MX25_ADCQ_CFG_REFP_YP |
> > +		     MX25_ADCQ_CFG_IN_XP |
> > +		     MX25_ADCQ_CFG_REFN_YN |
> > +		     MX25_ADCQ_CFG_NOS(priv->sample_count) |
> > +		     MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt));
> > +
> > +	/* Enable the touch detection right now */
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg |
> > +		     MX25_ADCQ_CFG_IGS);
> > +}
> > +
> > +static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv,
> > +				   unsigned settling_cnt, int *items)
> > +{
> > +	imx25_setup_queue_cfgs(priv, settling_cnt);
> > +
> > +	/* Setup the conversion queue */
> > +	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
> > +		     MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) |
> > +		     MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) |
> > +		     MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) |
> > +		     MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) |
> > +		     MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) |
> > +		     MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT));
> > +
> > +	/*
> > +	 * We measure X/Y with 'sample_count' number of samples and execute a
> > +	 * touch detection twice, with 1 sample each
> > +	 */
> > +	priv->expected_samples = priv->sample_count * 2 + 2;
> > +	*items = 6;
> > +
> > +	return 0;
> > +}
> > +
> Another personal preference. I'd not bother wrapping these single line
> calls up but rather just make them inline.  They don't in of
> themselves add much to my mind.  Still this one is very much up to you
> as far as I'm concerned.
> > +static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK,
> > +			   MX25_ADCQ_CR_PDMSK);
> > +}
> > +
> > +static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0);
> > +}
> > +
> > +static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ,
> > +			   MX25_ADCQ_MR_FDRY_IRQ);
> > +}
> > +
> > +static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0);
> > +}
> > +
> > +static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_FQS,
> > +			   MX25_ADCQ_CR_FQS);
> > +}
> > +
> > +static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv)
> > +{
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_FQS, 0);
> > +}
> > +
> > +static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv)
> > +{
> > +	u32 tcqcr;
> > +
> > +	regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr);
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST,
> > +			   MX25_ADCQ_CR_FRST);
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0);
> > +	regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr);
> > +}
> > +
> > +static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv)
> > +{
> > +	/* stop the queue from looping */
> > +	mx25_tcq_force_queue_stop(priv);
> > +
> > +	/* for a clean touch detection, preload the X plane */
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE);
> > +
> > +	/* waste some time now to pre-load the X plate to high voltage */
> > +	mx25_tcq_fifo_reset(priv);
> > +
> > +	/* re-enable the detection right now */
> > +	regmap_write(priv->core_regs, MX25_TSC_TICR,
> > +		     MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS);
> > +
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD,
> > +			   MX25_ADCQ_SR_PD);
> > +
> > +	/* enable the pen down event to be a source for the interrupt */
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0);
> > +
> > +	/* lets fire the next IRQ if someone touches the touchscreen */
> > +	mx25_tcq_enable_touch_irq(priv);
> > +}
> > +
> > +static int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,
> > +					   u32 *sample_buf,
> > +					   unsigned int samples)
> > +{
> > +	unsigned int x_pos = 0;
> > +	unsigned int y_pos = 0;
> > +	unsigned int touch_pre = 0;
> > +	unsigned int touch_post = 0;
> > +	unsigned int i;
> > +	int ret = 0;
> > +
> > +	for (i = 0; i < samples; i++) {
> > +		unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]);
> > +		unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]);
> > +
> > +		switch (index) {
> > +		case 1:
> > +			touch_pre = val;
> > +			break;
> > +		case 2:
> > +			x_pos = val;
> > +			break;
> > +		case 3:
> > +			y_pos = val;
> > +			break;
> > +		case 5:
> > +			touch_post = val;
> > +			break;
> > +		default:
> > +			ret = -EINVAL;
> > +			break;
> > +		}
> > +	}
> > +
> > +	if (ret == 0 && samples != 0) {
> > +		/*
> > +		 * only if both touch measures are below a threshold,
> > +		 * the position is valid
> > +		 */
> > +		if (touch_pre < priv->pen_threshold &&
> > +		    touch_post < priv->pen_threshold) {
> > +			/* valid samples, generate a report */
> > +			x_pos /= priv->sample_count;
> > +			y_pos /= priv->sample_count;
> > +			input_report_abs(priv->idev, ABS_X, x_pos);
> > +			input_report_abs(priv->idev, ABS_Y, y_pos);
> > +			input_report_key(priv->idev, BTN_TOUCH, 1);
> > +			input_sync(priv->idev);
> > +
> > +			/* get next sample */
> > +			mx25_tcq_enable_fifo_irq(priv);
> > +		} else if (touch_pre >= priv->pen_threshold &&
> > +			   touch_post >= priv->pen_threshold) {
> > +			/*
> > +			 * if both samples are invalid,
> > +			 * generate a release report
> > +			 */
> > +			input_report_key(priv->idev, BTN_TOUCH, 0);
> > +			input_sync(priv->idev);
> > +			mx25_tcq_re_enable_touch_detection(priv);
> > +		} else {
> > +			/*
> > +			 * if only one of both touch measurements are
> > +			 * below the threshold, still some bouncing
> > +			 * happens. Take additional samples in this
> > +			 * case to be sure
> > +			 */
> > +			mx25_tcq_enable_fifo_irq(priv);
> > +		}
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
> > +{
> > +	struct mx25_tcq_priv *priv = dev_id;
> > +	u32 sample_buf[TSC_MAX_SAMPLES];
> > +	unsigned int samples = 0;
> > +
> > +	/* read all samples */
> It's not a terribly big fifo, so I'm not convinced personally
> of the merits of having the separate thread for this interrupt...

Yes that is correct, but it is calling into the input framework as well.
I am not sure how much work is done there. Also the thread shouldn't be
extremely costy as it is mostly sleeping?

> 
> > +	while (1) {
> > +		u32 stats;
> > +
> > +		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
> > +		if (stats & MX25_ADCQ_SR_EMPT)
> > +			break;
> > +
> > +		if (samples < TSC_MAX_SAMPLES) {
> > +			regmap_read(priv->regs, MX25_ADCQ_FIFO,
> > +				    &sample_buf[samples]);
> > +			++samples;
> > +		} else {
> > +			u32 discarded;
> > +			/* discard samples */
> > +			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);
> Comment on how this could happen would be good.

This could happen if there are new samples while we read all samples
from the fifo.

Thanks to your comment I had a closer look on this handler again. The
problem here is that we read x samples while x may be a different number
than the number of samples generated by the conversion queue. But only
want to read samples that were produced by exactly one conversion queue
run.

So I redesigned this handler to first read the number of samples in the
fifo and then read as many as we want.

> 
> > +		}
> > +	}
> > +
> > +	mx25_tcq_create_event_for_4wire(priv, sample_buf, samples);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t mx25_tcq_irq(int irq, void *dev_id)
> > +{
> > +	struct mx25_tcq_priv *priv = dev_id;
> > +	u32 stat;
> > +	int ret = IRQ_HANDLED;
> > +
> > +	regmap_read(priv->regs, MX25_ADCQ_SR, &stat);
> > +
> > +	if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR))
> > +		mx25_tcq_fifo_reset(priv);
> Again, currious cross comparison with the adc driver (hardware is pretty
> much the same ;)  In there you don't reset the fifo if you get one
> of these.  Why not?  Now you should never get one as you only ever schedule
> a single reading, but best to be consistent.

Yes, the conversion queue in the touch driver is running continously so
that this may happen. Actually a fifo reset is not enough here. For the
touch we need to reset the conversion queue as well, thanks.
This could be added to the gcq driver but this should never happen.

> 
> > +
> > +	if (stat & MX25_ADCQ_SR_PD) {
> > +		mx25_tcq_disable_touch_irq(priv);
> > +		mx25_tcq_force_queue_start(priv);
> > +		mx25_tcq_enable_fifo_irq(priv);
> > +	}
> > +
> > +	if (stat & MX25_ADCQ_SR_FDRY) {
> > +		mx25_tcq_disable_fifo_irq(priv);
> I'm missing something I think.. In a oneshot irq the irq will be masked
> anyway till the thread is done.  Why the explicit disable as well?

The driver which receives the interrupts for both units GCQ and TCQ has
a proper interrupt status register. It uses this register to distribute
them to the different units. The problem is that it doesn't have a mask
register. The interrupts can only be masked in the TCQ or GCQ unit. So
by default the interrupt for both units will be masked.

GCQ currently doesn't use any time-critical interrupts. But waiting for
the thread shouldn't block the other unit. So I think it's better to
mask the interrupt here explicitly. This of course should not be with
IRQF_ONESHOT. That's a mistake, will fix that.

> 
> > +		ret = IRQ_WAKE_THREAD;
> > +	}
> > +
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
> > +			   MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
> > +			   MX25_ADCQ_SR_PD | MX25_ADCQ_SR_EOQ,
> > +			   MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR |
> > +			   MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
> > +			   MX25_ADCQ_SR_EOQ);
> As with the adc driver you are clearing at least one bit that should
> not I think ever be set... EOQ.  Why?

Indeed, this is always masked. Thanks, removed that.

> > +
> > +	return ret;
> > +}
> > +
> > +/* configure the statemachine for a 4-wire touchscreen */
> state machine
> > +static int mx25_tcq_init(struct mx25_tcq_priv *priv)
> > +{
> > +	u32 tgcr;
> > +	unsigned int ipg_div;
> > +	unsigned int adc_period;
> > +	unsigned int debounce_cnt;
> > +	unsigned int settling_cnt;
> > +	int itemct;
> > +	int ret;
> > +
> > +	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
> > +	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
> > +	adc_period = USEC_PER_SEC * ipg_div * 2 + 2;
> > +	adc_period /= clk_get_rate(priv->clk) / 1000 + 1;
> > +	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
> > +	settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1;
> > +
> > +	/* Reset */
> > +	regmap_write(priv->regs, MX25_ADCQ_CR,
> > +		     MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST);
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0);
> > +
> > +	/* up to 128 * 8 ADC clocks are possible */
> > +	if (debounce_cnt > 127)
> > +		debounce_cnt = 127;
> > +
> > +	/* up to 255 * 8 ADC clocks are possible */
> > +	if (settling_cnt > 255)
> > +		settling_cnt = 255;
> > +
> > +	ret = imx25_setup_queue_4wire(priv, settling_cnt, &itemct);
> > +	if (ret)
> > +		return ret;
> > +
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK,
> > +			   MX25_ADCQ_CR_LITEMID(itemct - 1) |
> > +			   MX25_ADCQ_CR_WMRK(priv->expected_samples - 1));
> > +
> > +	/* setup debounce count */
> > +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR,
> > +			   MX25_TGCR_PDBTIME_MASK,
> > +			   MX25_TGCR_PDBTIME(debounce_cnt));
> > +
> > +	/* enable debounce */
> > +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN,
> > +			   MX25_TGCR_PDBEN);
> > +	regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN,
> > +			   MX25_TGCR_PDEN);
> > +
> > +	/* enable the engine on demand */
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK,
> > +			   MX25_ADCQ_CR_QSM_FQS);
> > +
> > +	/* Enable repeat and repeat wait */
> > +	regmap_update_bits(priv->regs, MX25_ADCQ_CR,
> > +			   MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK,
> > +			   MX25_ADCQ_CR_RPT |
> > +			   MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT));
> > +
> > +	mx25_tcq_re_enable_touch_detection(priv);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mx25_tcq_parse_dt(struct platform_device *pdev,
> > +			     struct mx25_tcq_priv *priv)
> > +{
> > +	struct device_node *np = pdev->dev.of_node;
> > +	u32 wires;
> > +	int ret;
> > +
> > +	/* Setup defaults */
> > +	priv->pen_threshold = 500;
> > +	priv->sample_count = 3;
> > +	priv->pen_debounce = 1000000;
> > +	priv->settling_time = 250000;
> > +
> > +	ret = of_property_read_u32(np, "fsl,wires", &wires);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
> > +		return ret;
> > +	}
> > +
> > +	if (wires == 4) {
> > +		priv->mode = MX25_TS_4WIRE;
> > +	} else {
> > +		dev_err(&pdev->dev, "%u-wire mode not supported\n", wires);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* These are optional, we don't care about the return values */
> > +	of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold);
> > +	of_property_read_u32(np, "fsl,settling-time", &priv->settling_time);
> > +	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mx25_tcq_open(struct input_dev *idev)
> > +{
> > +	struct device *dev = &idev->dev;
> > +	struct mx25_tcq_priv *priv = dev_get_drvdata(dev);
> > +	int ret;
> > +
> > +	ret = clk_prepare_enable(priv->clk);
> > +	if (ret) {
> > +		dev_err(dev, "Failed to enable ipg clock\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = mx25_tcq_init(priv);
> > +	if (ret) {
> There is a certain missbalance in naming at least between the open
> and the close.   I'd be tempted to reorganise the two so that
> they obviously balance..  Either split up the init into the various
> things such as enabling the interrupts and bringing the queue up
> (so the oposite of the remove) or wrap the remove calls up in an
> _exit which can easily be compared to the init)
> 
> This definitely comes in the category of writing the code so it
> is 'obviously correct', rather than making review harder!
> 
> There is also some stuff in that init - like enabling debounce that
> isn't undone in the close - to my mind that suggests it doesn't
> belong in this function, but rather in device startup, but I may
> well be missing some interactions in the hardware that prevent this.

Yes, init() is more of a reset_configure_and_init() function. I will try
to make this more obvious.

> 
> > +		dev_err(dev, "Failed to init tcq\n");
> > +		clk_disable_unprepare(priv->clk);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void mx25_tcq_close(struct input_dev *idev)
> > +{
> > +	struct mx25_tcq_priv *priv = input_get_drvdata(idev);
> > +
> > +	mx25_tcq_force_queue_stop(priv);
> > +	mx25_tcq_disable_touch_irq(priv);
> > +	mx25_tcq_disable_fifo_irq(priv);
> > +	clk_disable_unprepare(priv->clk);
> > +}
> > +
> > +static int mx25_tcq_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct input_dev *idev;
> > +	struct mx25_tcq_priv *priv;
> > +	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
> > +	struct resource *res;
> > +	void __iomem *mem;
> > +	int ret;
> > +
> > +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > +	if (!priv)
> > +		return -ENOMEM;
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	mem = devm_ioremap_resource(dev, res);
> > +	if (!mem)
> > +		return -ENOMEM;
> > +
> > +	ret = mx25_tcq_parse_dt(pdev, priv);
> > +	if (ret)
> > +		return ret;
> > +
> > +	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig);
> > +	if (IS_ERR(priv->regs)) {
> > +		dev_err(dev, "Failed to initialize regmap\n");
> > +		return PTR_ERR(priv->regs);
> > +	}
> > +
> > +	priv->irq = platform_get_irq(pdev, 0);
> > +	if (priv->irq <= 0) {
> > +		dev_err(dev, "Failed to get IRQ\n");
> > +		return priv->irq;
> > +	}
> > +
> > +	idev = devm_input_allocate_device(dev);
> > +	if (!idev) {
> > +		dev_err(dev, "Failed to allocate input device\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	idev->name = mx25_tcq_name;
> > +	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> > +	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
> > +	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
> > +	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
> > +
> > +	idev->id.bustype = BUS_HOST;
> > +	idev->open = mx25_tcq_open;
> > +	idev->close = mx25_tcq_close;
> > +
> > +	priv->idev = idev;
> > +	input_set_drvdata(idev, priv);
> > +
> > +	priv->core_regs = tsadc->regs;
> > +	if (!priv->core_regs)
> > +		return -EINVAL;
> > +
> > +	priv->clk = tsadc->clk;
> > +	if (!priv->clk)
> > +		return -EINVAL;
> > +
> > +	platform_set_drvdata(pdev, priv);
> > +
> > +	ret = input_register_device(idev);
> I'm not 100% sure of whether input works the way I think it does ;)
> but I'd imagine this just exposed the userspace interface.  Hence from
> here on you can open the device and cause the interrupt to be enabled.
> It doesn't have a handler until after the request_threaded_irq device
> below...  So nasty if unlikely race condition here.

Yes, fixed as Dmitry suggested.

> 
> > +	if (ret) {
> > +		dev_err(dev, "Failed to register input device\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq,
> > +					mx25_tcq_irq_thread, IRQF_ONESHOT,
> > +					pdev->name, priv);
> currious difference in approach here.  Why a devm call in this driver
> and not in the adc one?  I assumed general paranoia with devm irq requests
> and the various obscure ways they can go wrong so didn't mention it there...

For the gcq driver we have things are a bit different. There is no real
open()/close() function. We configure the unit once (including
interrupts) and want to be careful when shutting down the driver to do
it in the right order of disabling clocks, interrupts and regulators.
The interrupts there are actually never masked there.

In this tcq driver the interrupts are masked and clocks are off when the
input was closed.

Thanks,

Markus

> > +	if (ret) {
> > +		dev_err(dev, "Failed requesting IRQ\n");
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static struct platform_driver mx25_tcq_driver = {
> > +	.driver		= {
> > +		.name	= "mx25-tcq",
> > +		.of_match_table = mx25_tcq_ids,
> > +	},
> > +	.probe		= mx25_tcq_probe,
> > +};
> > +module_platform_driver(mx25_tcq_driver);
> > +
> > +MODULE_DESCRIPTION("TS input driver for Freescale mx25");
> > +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
> > +MODULE_LICENSE("GPL v2");
> > 
> 
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: This is a digitally signed message part.
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20151123/ad5c5eaa/attachment-0001.sig>

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

* Re: [PATCH v8 1/8] ARM: dt: Binding documentation for imx25 ADC/TSC
  2015-11-16 12:01   ` Markus Pargmann
  (?)
@ 2015-11-23 14:59       ` Lee Jones
  -1 siblings, 0 replies; 67+ messages in thread
From: Lee Jones @ 2015-11-23 14:59 UTC (permalink / raw)
  To: Markus Pargmann
  Cc: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Denis Carikli,
	Eric Bénard, Sascha Hauer,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Hartmut Knaack, Fabio Estevam

On Mon, 16 Nov 2015, Markus Pargmann wrote:

> This documentation describes the devicetree bindings for the
> ADC/Touchscreen unit of the i.MX25 SoC.
> 
> Signed-off-by: Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> Acked-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> ---
> 
> Notes:
>     Changes in v6:
>      - Removed adc-ref property and replaced it with refp and refn for positive and
>        negative references. The properties are optional now as the default
>        behaviour is a positive internal reference voltage and ADC GND as negative
>        reference.
> 
>  .../devicetree/bindings/mfd/fsl-imx25-tsadc.txt    | 46 ++++++++++++++++++++++
>  1 file changed, 46 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> new file mode 100644
> index 000000000000..a857af0eb68c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> @@ -0,0 +1,46 @@
> +Freescale mx25 ADC/TSC multifunction device

Prefer "MX25" and "MultiFunction Device (MFD)"

> +This device combines two general purpose conversion queues one used for general
> +ADC and the other used for touchscreens.
> +
> +Required properties:
> + - compatible: Should be "fsl,imx25-tsadc".
> + - reg: Memory range of the device.

Not exactly.  More like start location and size.

> + - interrupts: Interrupt for this device as described in
> +   interrupts/interrupts.txt

No such file.  In any case, I'd prefer:

"(See: ../interrupt-controller/interrupts.txt)".

> + - clocks: An 'ipg' clock defined as described in clocks/clock.txt

Didn't you check any of what you've written?

"(See: ../clock/clock-bindings.txt)"

> + - interrupt-controller: This device is an interrupt controller. It controls
> +   the interrupts of both conversion queues.
> + - #interrupt-cells: Should be '<1>'.
> + - #address-cells: Should be '<1>'.
> + - #size-cells: Should be '<1>'.
> + - ranges

?

Also, don't think think that this looks better/easier to read?

> +Required properties:
> + - compatible:		Should be "fsl,imx25-tsadc".
> + - reg:			Memory range of the device.
> + - interrupts:		Interrupt for this device as described in
> +				interrupts/interrupts.txt
> + - clocks:			An 'ipg' clock defined as described in clocks/clock.txt
> + - interrupt-controller:	This device is an interrupt controller. It controls
> +   				the interrupts of both conversion queues.
> + - #interrupt-cells:		Should be '<1>'.
> + - #address-cells:		Should be '<1>'.
> + - #size-cells:		Should be '<1>'.

[...]

> +This device includes two conversion queues which can be added as subnodes.
> +The first queue is for the touchscreen, the second for general purpose ADC.
> +
> +Example:
> +	tscadc: tscadc@50030000 {
> +		compatible = "fsl,imx25-tsadc";
> +		reg = <0x50030000 0xc>;
> +		interrupts = <46>;
> +		clocks = <&clks 119>;
> +		clock-names = "ipg";

I don't think this is required, if you only have a single clock.

> +		interrupt-controller;
> +		#interrupt-cells = <1>;
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		ranges;
> +
> +		tsc: tcq@50030400 {
> +			compatible = "fsl,imx25-tcq";
> +			reg = <0x50030400 0x60>;
> +			...
> +		};
> +
> +		adc: gcq@50030800 {
> +			compatible = "fsl,imx25-gcq";
> +			reg = <0x50030800 0x60>;
> +			...
> +		};
> +	};

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v8 1/8] ARM: dt: Binding documentation for imx25 ADC/TSC
@ 2015-11-23 14:59       ` Lee Jones
  0 siblings, 0 replies; 67+ messages in thread
From: Lee Jones @ 2015-11-23 14:59 UTC (permalink / raw)
  To: Markus Pargmann
  Cc: Shawn Guo, Dmitry Torokhov, Jonathan Cameron, Denis Carikli,
	Eric Bénard, Sascha Hauer, devicetree, linux-input,
	linux-iio, linux-arm-kernel, Hartmut Knaack, Fabio Estevam

On Mon, 16 Nov 2015, Markus Pargmann wrote:

> This documentation describes the devicetree bindings for the
> ADC/Touchscreen unit of the i.MX25 SoC.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> Acked-by: Jonathan Cameron <jic23@kernel.org>
> ---
> 
> Notes:
>     Changes in v6:
>      - Removed adc-ref property and replaced it with refp and refn for positive and
>        negative references. The properties are optional now as the default
>        behaviour is a positive internal reference voltage and ADC GND as negative
>        reference.
> 
>  .../devicetree/bindings/mfd/fsl-imx25-tsadc.txt    | 46 ++++++++++++++++++++++
>  1 file changed, 46 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> new file mode 100644
> index 000000000000..a857af0eb68c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> @@ -0,0 +1,46 @@
> +Freescale mx25 ADC/TSC multifunction device

Prefer "MX25" and "MultiFunction Device (MFD)"

> +This device combines two general purpose conversion queues one used for general
> +ADC and the other used for touchscreens.
> +
> +Required properties:
> + - compatible: Should be "fsl,imx25-tsadc".
> + - reg: Memory range of the device.

Not exactly.  More like start location and size.

> + - interrupts: Interrupt for this device as described in
> +   interrupts/interrupts.txt

No such file.  In any case, I'd prefer:

"(See: ../interrupt-controller/interrupts.txt)".

> + - clocks: An 'ipg' clock defined as described in clocks/clock.txt

Didn't you check any of what you've written?

"(See: ../clock/clock-bindings.txt)"

> + - interrupt-controller: This device is an interrupt controller. It controls
> +   the interrupts of both conversion queues.
> + - #interrupt-cells: Should be '<1>'.
> + - #address-cells: Should be '<1>'.
> + - #size-cells: Should be '<1>'.
> + - ranges

?

Also, don't think think that this looks better/easier to read?

> +Required properties:
> + - compatible:		Should be "fsl,imx25-tsadc".
> + - reg:			Memory range of the device.
> + - interrupts:		Interrupt for this device as described in
> +				interrupts/interrupts.txt
> + - clocks:			An 'ipg' clock defined as described in clocks/clock.txt
> + - interrupt-controller:	This device is an interrupt controller. It controls
> +   				the interrupts of both conversion queues.
> + - #interrupt-cells:		Should be '<1>'.
> + - #address-cells:		Should be '<1>'.
> + - #size-cells:		Should be '<1>'.

[...]

> +This device includes two conversion queues which can be added as subnodes.
> +The first queue is for the touchscreen, the second for general purpose ADC.
> +
> +Example:
> +	tscadc: tscadc@50030000 {
> +		compatible = "fsl,imx25-tsadc";
> +		reg = <0x50030000 0xc>;
> +		interrupts = <46>;
> +		clocks = <&clks 119>;
> +		clock-names = "ipg";

I don't think this is required, if you only have a single clock.

> +		interrupt-controller;
> +		#interrupt-cells = <1>;
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		ranges;
> +
> +		tsc: tcq@50030400 {
> +			compatible = "fsl,imx25-tcq";
> +			reg = <0x50030400 0x60>;
> +			...
> +		};
> +
> +		adc: gcq@50030800 {
> +			compatible = "fsl,imx25-gcq";
> +			reg = <0x50030800 0x60>;
> +			...
> +		};
> +	};

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* [PATCH v8 1/8] ARM: dt: Binding documentation for imx25 ADC/TSC
@ 2015-11-23 14:59       ` Lee Jones
  0 siblings, 0 replies; 67+ messages in thread
From: Lee Jones @ 2015-11-23 14:59 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, 16 Nov 2015, Markus Pargmann wrote:

> This documentation describes the devicetree bindings for the
> ADC/Touchscreen unit of the i.MX25 SoC.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> Acked-by: Jonathan Cameron <jic23@kernel.org>
> ---
> 
> Notes:
>     Changes in v6:
>      - Removed adc-ref property and replaced it with refp and refn for positive and
>        negative references. The properties are optional now as the default
>        behaviour is a positive internal reference voltage and ADC GND as negative
>        reference.
> 
>  .../devicetree/bindings/mfd/fsl-imx25-tsadc.txt    | 46 ++++++++++++++++++++++
>  1 file changed, 46 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> new file mode 100644
> index 000000000000..a857af0eb68c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> @@ -0,0 +1,46 @@
> +Freescale mx25 ADC/TSC multifunction device

Prefer "MX25" and "MultiFunction Device (MFD)"

> +This device combines two general purpose conversion queues one used for general
> +ADC and the other used for touchscreens.
> +
> +Required properties:
> + - compatible: Should be "fsl,imx25-tsadc".
> + - reg: Memory range of the device.

Not exactly.  More like start location and size.

> + - interrupts: Interrupt for this device as described in
> +   interrupts/interrupts.txt

No such file.  In any case, I'd prefer:

"(See: ../interrupt-controller/interrupts.txt)".

> + - clocks: An 'ipg' clock defined as described in clocks/clock.txt

Didn't you check any of what you've written?

"(See: ../clock/clock-bindings.txt)"

> + - interrupt-controller: This device is an interrupt controller. It controls
> +   the interrupts of both conversion queues.
> + - #interrupt-cells: Should be '<1>'.
> + - #address-cells: Should be '<1>'.
> + - #size-cells: Should be '<1>'.
> + - ranges

?

Also, don't think think that this looks better/easier to read?

> +Required properties:
> + - compatible:		Should be "fsl,imx25-tsadc".
> + - reg:			Memory range of the device.
> + - interrupts:		Interrupt for this device as described in
> +				interrupts/interrupts.txt
> + - clocks:			An 'ipg' clock defined as described in clocks/clock.txt
> + - interrupt-controller:	This device is an interrupt controller. It controls
> +   				the interrupts of both conversion queues.
> + - #interrupt-cells:		Should be '<1>'.
> + - #address-cells:		Should be '<1>'.
> + - #size-cells:		Should be '<1>'.

[...]

> +This device includes two conversion queues which can be added as subnodes.
> +The first queue is for the touchscreen, the second for general purpose ADC.
> +
> +Example:
> +	tscadc: tscadc at 50030000 {
> +		compatible = "fsl,imx25-tsadc";
> +		reg = <0x50030000 0xc>;
> +		interrupts = <46>;
> +		clocks = <&clks 119>;
> +		clock-names = "ipg";

I don't think this is required, if you only have a single clock.

> +		interrupt-controller;
> +		#interrupt-cells = <1>;
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		ranges;
> +
> +		tsc: tcq at 50030400 {
> +			compatible = "fsl,imx25-tcq";
> +			reg = <0x50030400 0x60>;
> +			...
> +		};
> +
> +		adc: gcq at 50030800 {
> +			compatible = "fsl,imx25-gcq";
> +			reg = <0x50030800 0x60>;
> +			...
> +		};
> +	};

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v8 1/8] ARM: dt: Binding documentation for imx25 ADC/TSC
  2015-11-23 14:59       ` Lee Jones
  (?)
@ 2015-11-24 10:44         ` Markus Pargmann
  -1 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-24 10:44 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Lee Jones, devicetree-u79uwXL29TY76Z2rM5mHXA, Eric Bénard,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Fabio Estevam, Dmitry Torokhov,
	Denis Carikli, Sascha Hauer, linux-input-u79uwXL29TY76Z2rM5mHXA,
	Shawn Guo, Jonathan Cameron, Hartmut Knaack

[-- Attachment #1: Type: text/plain, Size: 4384 bytes --]

On Monday 23 November 2015 14:59:52 Lee Jones wrote:
> On Mon, 16 Nov 2015, Markus Pargmann wrote:
> 
> > This documentation describes the devicetree bindings for the
> > ADC/Touchscreen unit of the i.MX25 SoC.
> > 
> > Signed-off-by: Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> > Acked-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> > ---
> > 
> > Notes:
> >     Changes in v6:
> >      - Removed adc-ref property and replaced it with refp and refn for positive and
> >        negative references. The properties are optional now as the default
> >        behaviour is a positive internal reference voltage and ADC GND as negative
> >        reference.
> > 
> >  .../devicetree/bindings/mfd/fsl-imx25-tsadc.txt    | 46 ++++++++++++++++++++++
> >  1 file changed, 46 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> > new file mode 100644
> > index 000000000000..a857af0eb68c
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> > @@ -0,0 +1,46 @@
> > +Freescale mx25 ADC/TSC multifunction device
> 
> Prefer "MX25" and "MultiFunction Device (MFD)"

Fixed.

> 
> > +This device combines two general purpose conversion queues one used for general
> > +ADC and the other used for touchscreens.
> > +
> > +Required properties:
> > + - compatible: Should be "fsl,imx25-tsadc".
> > + - reg: Memory range of the device.
> 
> Not exactly.  More like start location and size.
> 
> > + - interrupts: Interrupt for this device as described in
> > +   interrupts/interrupts.txt
> 
> No such file.  In any case, I'd prefer:
> 
> "(See: ../interrupt-controller/interrupts.txt)".
> 
> > + - clocks: An 'ipg' clock defined as described in clocks/clock.txt
> 
> Didn't you check any of what you've written?
> 
> "(See: ../clock/clock-bindings.txt)"

Seems I didn't check these, sorry.

> 
> > + - interrupt-controller: This device is an interrupt controller. It controls
> > +   the interrupts of both conversion queues.
> > + - #interrupt-cells: Should be '<1>'.
> > + - #address-cells: Should be '<1>'.
> > + - #size-cells: Should be '<1>'.
> > + - ranges
> 
> ?
> 
> Also, don't think think that this looks better/easier to read?

Yes a bit, changed it.

> 
> > +Required properties:
> > + - compatible:		Should be "fsl,imx25-tsadc".
> > + - reg:			Memory range of the device.
> > + - interrupts:		Interrupt for this device as described in
> > +				interrupts/interrupts.txt
> > + - clocks:			An 'ipg' clock defined as described in clocks/clock.txt
> > + - interrupt-controller:	This device is an interrupt controller. It controls
> > +   				the interrupts of both conversion queues.
> > + - #interrupt-cells:		Should be '<1>'.
> > + - #address-cells:		Should be '<1>'.
> > + - #size-cells:		Should be '<1>'.
> 
> [...]
> 
> > +This device includes two conversion queues which can be added as subnodes.
> > +The first queue is for the touchscreen, the second for general purpose ADC.
> > +
> > +Example:
> > +	tscadc: tscadc@50030000 {
> > +		compatible = "fsl,imx25-tsadc";
> > +		reg = <0x50030000 0xc>;
> > +		interrupts = <46>;
> > +		clocks = <&clks 119>;
> > +		clock-names = "ipg";
> 
> I don't think this is required, if you only have a single clock.

I prefer this variation so that you immediately see what the clock is
for. The number is not terribly helpful.

Thanks,

Markus

> 
> > +		interrupt-controller;
> > +		#interrupt-cells = <1>;
> > +		#address-cells = <1>;
> > +		#size-cells = <1>;
> > +		ranges;
> > +
> > +		tsc: tcq@50030400 {
> > +			compatible = "fsl,imx25-tcq";
> > +			reg = <0x50030400 0x60>;
> > +			...
> > +		};
> > +
> > +		adc: gcq@50030800 {
> > +			compatible = "fsl,imx25-gcq";
> > +			reg = <0x50030800 0x60>;
> > +			...
> > +		};
> > +	};
> 
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v8 1/8] ARM: dt: Binding documentation for imx25 ADC/TSC
@ 2015-11-24 10:44         ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-24 10:44 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Lee Jones, devicetree, Eric Bénard, linux-iio,
	Fabio Estevam, Dmitry Torokhov, Denis Carikli, Sascha Hauer,
	linux-input, Shawn Guo, Jonathan Cameron, Hartmut Knaack

[-- Attachment #1: Type: text/plain, Size: 4330 bytes --]

On Monday 23 November 2015 14:59:52 Lee Jones wrote:
> On Mon, 16 Nov 2015, Markus Pargmann wrote:
> 
> > This documentation describes the devicetree bindings for the
> > ADC/Touchscreen unit of the i.MX25 SoC.
> > 
> > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > Acked-by: Jonathan Cameron <jic23@kernel.org>
> > ---
> > 
> > Notes:
> >     Changes in v6:
> >      - Removed adc-ref property and replaced it with refp and refn for positive and
> >        negative references. The properties are optional now as the default
> >        behaviour is a positive internal reference voltage and ADC GND as negative
> >        reference.
> > 
> >  .../devicetree/bindings/mfd/fsl-imx25-tsadc.txt    | 46 ++++++++++++++++++++++
> >  1 file changed, 46 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> > new file mode 100644
> > index 000000000000..a857af0eb68c
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> > @@ -0,0 +1,46 @@
> > +Freescale mx25 ADC/TSC multifunction device
> 
> Prefer "MX25" and "MultiFunction Device (MFD)"

Fixed.

> 
> > +This device combines two general purpose conversion queues one used for general
> > +ADC and the other used for touchscreens.
> > +
> > +Required properties:
> > + - compatible: Should be "fsl,imx25-tsadc".
> > + - reg: Memory range of the device.
> 
> Not exactly.  More like start location and size.
> 
> > + - interrupts: Interrupt for this device as described in
> > +   interrupts/interrupts.txt
> 
> No such file.  In any case, I'd prefer:
> 
> "(See: ../interrupt-controller/interrupts.txt)".
> 
> > + - clocks: An 'ipg' clock defined as described in clocks/clock.txt
> 
> Didn't you check any of what you've written?
> 
> "(See: ../clock/clock-bindings.txt)"

Seems I didn't check these, sorry.

> 
> > + - interrupt-controller: This device is an interrupt controller. It controls
> > +   the interrupts of both conversion queues.
> > + - #interrupt-cells: Should be '<1>'.
> > + - #address-cells: Should be '<1>'.
> > + - #size-cells: Should be '<1>'.
> > + - ranges
> 
> ?
> 
> Also, don't think think that this looks better/easier to read?

Yes a bit, changed it.

> 
> > +Required properties:
> > + - compatible:		Should be "fsl,imx25-tsadc".
> > + - reg:			Memory range of the device.
> > + - interrupts:		Interrupt for this device as described in
> > +				interrupts/interrupts.txt
> > + - clocks:			An 'ipg' clock defined as described in clocks/clock.txt
> > + - interrupt-controller:	This device is an interrupt controller. It controls
> > +   				the interrupts of both conversion queues.
> > + - #interrupt-cells:		Should be '<1>'.
> > + - #address-cells:		Should be '<1>'.
> > + - #size-cells:		Should be '<1>'.
> 
> [...]
> 
> > +This device includes two conversion queues which can be added as subnodes.
> > +The first queue is for the touchscreen, the second for general purpose ADC.
> > +
> > +Example:
> > +	tscadc: tscadc@50030000 {
> > +		compatible = "fsl,imx25-tsadc";
> > +		reg = <0x50030000 0xc>;
> > +		interrupts = <46>;
> > +		clocks = <&clks 119>;
> > +		clock-names = "ipg";
> 
> I don't think this is required, if you only have a single clock.

I prefer this variation so that you immediately see what the clock is
for. The number is not terribly helpful.

Thanks,

Markus

> 
> > +		interrupt-controller;
> > +		#interrupt-cells = <1>;
> > +		#address-cells = <1>;
> > +		#size-cells = <1>;
> > +		ranges;
> > +
> > +		tsc: tcq@50030400 {
> > +			compatible = "fsl,imx25-tcq";
> > +			reg = <0x50030400 0x60>;
> > +			...
> > +		};
> > +
> > +		adc: gcq@50030800 {
> > +			compatible = "fsl,imx25-gcq";
> > +			reg = <0x50030800 0x60>;
> > +			...
> > +		};
> > +	};
> 
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* [PATCH v8 1/8] ARM: dt: Binding documentation for imx25 ADC/TSC
@ 2015-11-24 10:44         ` Markus Pargmann
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Pargmann @ 2015-11-24 10:44 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 23 November 2015 14:59:52 Lee Jones wrote:
> On Mon, 16 Nov 2015, Markus Pargmann wrote:
> 
> > This documentation describes the devicetree bindings for the
> > ADC/Touchscreen unit of the i.MX25 SoC.
> > 
> > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > Acked-by: Jonathan Cameron <jic23@kernel.org>
> > ---
> > 
> > Notes:
> >     Changes in v6:
> >      - Removed adc-ref property and replaced it with refp and refn for positive and
> >        negative references. The properties are optional now as the default
> >        behaviour is a positive internal reference voltage and ADC GND as negative
> >        reference.
> > 
> >  .../devicetree/bindings/mfd/fsl-imx25-tsadc.txt    | 46 ++++++++++++++++++++++
> >  1 file changed, 46 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> > new file mode 100644
> > index 000000000000..a857af0eb68c
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt
> > @@ -0,0 +1,46 @@
> > +Freescale mx25 ADC/TSC multifunction device
> 
> Prefer "MX25" and "MultiFunction Device (MFD)"

Fixed.

> 
> > +This device combines two general purpose conversion queues one used for general
> > +ADC and the other used for touchscreens.
> > +
> > +Required properties:
> > + - compatible: Should be "fsl,imx25-tsadc".
> > + - reg: Memory range of the device.
> 
> Not exactly.  More like start location and size.
> 
> > + - interrupts: Interrupt for this device as described in
> > +   interrupts/interrupts.txt
> 
> No such file.  In any case, I'd prefer:
> 
> "(See: ../interrupt-controller/interrupts.txt)".
> 
> > + - clocks: An 'ipg' clock defined as described in clocks/clock.txt
> 
> Didn't you check any of what you've written?
> 
> "(See: ../clock/clock-bindings.txt)"

Seems I didn't check these, sorry.

> 
> > + - interrupt-controller: This device is an interrupt controller. It controls
> > +   the interrupts of both conversion queues.
> > + - #interrupt-cells: Should be '<1>'.
> > + - #address-cells: Should be '<1>'.
> > + - #size-cells: Should be '<1>'.
> > + - ranges
> 
> ?
> 
> Also, don't think think that this looks better/easier to read?

Yes a bit, changed it.

> 
> > +Required properties:
> > + - compatible:		Should be "fsl,imx25-tsadc".
> > + - reg:			Memory range of the device.
> > + - interrupts:		Interrupt for this device as described in
> > +				interrupts/interrupts.txt
> > + - clocks:			An 'ipg' clock defined as described in clocks/clock.txt
> > + - interrupt-controller:	This device is an interrupt controller. It controls
> > +   				the interrupts of both conversion queues.
> > + - #interrupt-cells:		Should be '<1>'.
> > + - #address-cells:		Should be '<1>'.
> > + - #size-cells:		Should be '<1>'.
> 
> [...]
> 
> > +This device includes two conversion queues which can be added as subnodes.
> > +The first queue is for the touchscreen, the second for general purpose ADC.
> > +
> > +Example:
> > +	tscadc: tscadc at 50030000 {
> > +		compatible = "fsl,imx25-tsadc";
> > +		reg = <0x50030000 0xc>;
> > +		interrupts = <46>;
> > +		clocks = <&clks 119>;
> > +		clock-names = "ipg";
> 
> I don't think this is required, if you only have a single clock.

I prefer this variation so that you immediately see what the clock is
for. The number is not terribly helpful.

Thanks,

Markus

> 
> > +		interrupt-controller;
> > +		#interrupt-cells = <1>;
> > +		#address-cells = <1>;
> > +		#size-cells = <1>;
> > +		ranges;
> > +
> > +		tsc: tcq at 50030400 {
> > +			compatible = "fsl,imx25-tcq";
> > +			reg = <0x50030400 0x60>;
> > +			...
> > +		};
> > +
> > +		adc: gcq at 50030800 {
> > +			compatible = "fsl,imx25-gcq";
> > +			reg = <0x50030800 0x60>;
> > +			...
> > +		};
> > +	};
> 
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: This is a digitally signed message part.
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20151124/9f3b22a0/attachment.sig>

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

end of thread, other threads:[~2015-11-24 10:45 UTC | newest]

Thread overview: 67+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-11-16 12:01 [PATCH v8 0/8] imx25 adc and touchscreen driver Markus Pargmann
2015-11-16 12:01 ` Markus Pargmann
2015-11-16 12:01 ` Markus Pargmann
2015-11-16 12:01 ` [PATCH v8 1/8] ARM: dt: Binding documentation for imx25 ADC/TSC Markus Pargmann
2015-11-16 12:01   ` Markus Pargmann
     [not found]   ` <1447675269-8831-2-git-send-email-mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2015-11-16 14:01     ` Rob Herring
2015-11-16 14:01       ` Rob Herring
2015-11-16 14:01       ` Rob Herring
2015-11-23 14:59     ` Lee Jones
2015-11-23 14:59       ` Lee Jones
2015-11-23 14:59       ` Lee Jones
2015-11-24 10:44       ` Markus Pargmann
2015-11-24 10:44         ` Markus Pargmann
2015-11-24 10:44         ` Markus Pargmann
2015-11-16 12:01 ` [PATCH v8 2/8] ARM: dt: Binding documentation for imx25 GCQ Markus Pargmann
2015-11-16 12:01   ` Markus Pargmann
2015-11-16 15:12   ` Rob Herring
2015-11-16 15:12     ` Rob Herring
2015-11-17  7:46     ` Markus Pargmann
2015-11-17  7:46       ` Markus Pargmann
2015-11-17  7:46       ` Markus Pargmann
2015-11-16 12:01 ` [PATCH v8 3/8] ARM: dt: Binding documentation for imx25 touchscreen controller Markus Pargmann
2015-11-16 12:01   ` Markus Pargmann
2015-11-16 14:16   ` Rob Herring
2015-11-16 14:16     ` Rob Herring
2015-11-17  8:54     ` Markus Pargmann
2015-11-17  8:54       ` Markus Pargmann
2015-11-17  8:54       ` Markus Pargmann
2015-11-17 18:21     ` Dmitry Torokhov
2015-11-17 18:21       ` Dmitry Torokhov
2015-11-17 18:21       ` Dmitry Torokhov
2015-11-16 12:01 ` [PATCH v8 4/8] mfd: fsl imx25 Touchscreen ADC driver Markus Pargmann
2015-11-16 12:01   ` Markus Pargmann
2015-11-21 17:02   ` Jonathan Cameron
2015-11-21 17:02     ` Jonathan Cameron
     [not found]     ` <5650A3AA.5080705-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
2015-11-21 17:26       ` Jonathan Cameron
2015-11-21 17:26         ` Jonathan Cameron
2015-11-21 17:26         ` Jonathan Cameron
2015-11-23  9:17       ` Markus Pargmann
2015-11-23  9:17         ` Markus Pargmann
2015-11-23  9:17         ` Markus Pargmann
     [not found] ` <1447675269-8831-1-git-send-email-mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2015-11-16 12:01   ` [PATCH v8 5/8] iio: adc: fsl,imx25-gcq driver Markus Pargmann
2015-11-16 12:01     ` Markus Pargmann
2015-11-16 12:01     ` Markus Pargmann
2015-11-16 12:01   ` [PATCH v8 6/8] input: touchscreen: imx25 tcq driver Markus Pargmann
2015-11-16 12:01     ` Markus Pargmann
2015-11-16 12:01     ` Markus Pargmann
     [not found]     ` <1447675269-8831-7-git-send-email-mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2015-11-17 18:17       ` Dmitry Torokhov
2015-11-17 18:17         ` Dmitry Torokhov
2015-11-17 18:17         ` Dmitry Torokhov
2015-11-20 13:33         ` Markus Pargmann
2015-11-20 13:33           ` Markus Pargmann
2015-11-20 13:33           ` Markus Pargmann
2015-11-21 17:48     ` Jonathan Cameron
2015-11-21 17:48       ` Jonathan Cameron
2015-11-21 17:50       ` Jonathan Cameron
2015-11-21 17:50         ` Jonathan Cameron
     [not found]       ` <5650AE5A.5010506-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
2015-11-23  8:21         ` Juergen Borleis
2015-11-23  8:21           ` Juergen Borleis
2015-11-23  8:21           ` Juergen Borleis
2015-11-23 13:43       ` Markus Pargmann
2015-11-23 13:43         ` Markus Pargmann
2015-11-16 12:01   ` [PATCH v8 8/8] ARM: imx_v4_v5_defconfig: Add I.MX25 Touchscreen controller and ADC support Markus Pargmann
2015-11-16 12:01     ` Markus Pargmann
2015-11-16 12:01     ` Markus Pargmann
2015-11-16 12:01 ` [PATCH v8 7/8] ARM: dts: imx25: Add TSC " Markus Pargmann
2015-11-16 12:01   ` Markus Pargmann

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.