* [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: 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 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 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
[parent not found: <1447675269-8831-2-git-send-email-mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>]
* 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
* [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 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
* 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
* [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 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
* 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
* [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
* 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 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
* 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
* [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 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 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
* 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 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
* [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 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
* 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
* [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 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 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
* 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
[parent not found: <5650A3AA.5080705-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>]
* 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
* [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 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
* 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
* [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 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
[parent not found: <1447675269-8831-1-git-send-email-mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>]
* [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", ®); + 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: 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", ®); + 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: 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", ®); + 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: 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 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
[parent not found: <1447675269-8831-7-git-send-email-mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>]
* 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
* [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 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
* 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
* [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 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
* 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
[parent not found: <5650AE5A.5010506-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>]
* 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
* [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 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
* 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
* [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: 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
* [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 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
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.