Skip to contentSkip to conversation

iio: at91: Add touch screen support in at91 adc

PATCH Superseded Josh Wu 8 participants 40 messages Jul 14, 2013 – Aug 8, 2013

Subsystems (6)

v1 of 4 Superseded Latest is v4 →

lore
Share & tools
lore

Josh Wu

author
#0 lore raw

cover iio: at91: Add touch screen support in at91 adc

This patch series will add touch screen support for at91 adc driver. To enable touch screen you need add the touch screen related dt properties.

Josh Wu (5):
iio: at91: use adc_clk_khz to make the calculation not easy to large

    than u32.

iio: at91: Use different prescal, startup mask in MR for different IP
iio: at91: ADC start-up time calculation changed since at91sam9x5
iio: at91: add an optional dt property for for adc clock hz.
iio: at91: introduce touch screen support in iio adc driver

 .../devicetree/bindings/arm/atmel-adc.txt          |   15 +
 arch/arm/mach-at91/include/mach/at91_adc.h         |   43 +-
 drivers/iio/adc/at91_adc.c                         |  492 ++++++++++++++++++--
 3 files changed, 511 insertions(+), 39 deletions(-)
Signature · 1 line
1.7.10
author
#1 lore raw

PATCH 1/5 iio: at91: use adc_clk_khz to make the calculation not easy to large than u32.

for example, if adc_clk is 20Mhz and start-up time set as larger than 215us. then the calculation "st->startup_time * adc_clk_khz" will out of u32.

In this patch, it will use khz unit for adc_clk, that avoids above problem.

---
 drivers/iio/adc/at91_adc.c |   11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)
drivers/iio/adc/at91_adc.c +6 -5
index e5b88d5..18bd54f 100644
@@ -582,7 +582,7 @@ static const struct iio_info at91_adc_info = {
 
 static int at91_adc_probe(struct platform_device *pdev)
 {
-	unsigned int prsc, mstrclk, ticks, adc_clk, shtim;
+	unsigned int prsc, mstrclk, ticks, adc_clk, adc_clk_khz, shtim;
 	int ret;
 	struct iio_dev *idev;
 	struct at91_adc_state *st;
@@ -680,6 +680,7 @@ static int at91_adc_probe(struct platform_device *pdev)
 	 */
 	mstrclk = clk_get_rate(st->clk);
 	adc_clk = clk_get_rate(st->adc_clk);
+	adc_clk_khz = adc_clk / 1000;
 	prsc = (mstrclk / (2 * adc_clk)) - 1;
 
 	if (!st->startup_time) {
@@ -693,15 +694,15 @@ static int at91_adc_probe(struct platform_device *pdev)
 	 * defined in the electrical characteristics of the board, divided by 8.
 	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
 	 */
-	ticks = round_up((st->startup_time * adc_clk /
-			  1000000) - 1, 8) / 8;
+	ticks = round_up((st->startup_time * adc_clk_khz /
+			  1000) - 1, 8) / 8;
 	/*
 	 * a minimal Sample and Hold Time is necessary for the ADC to guarantee
 	 * the best converted final value between two channels selection
 	 * The formula thus is : Sample and Hold Time = (shtim + 1) / ADCClock
 	 */
-	shtim = round_up((st->sample_hold_time * adc_clk /
-			  1000000) - 1, 1);
+	shtim = round_up((st->sample_hold_time * adc_clk_khz /
+			  1000) - 1, 1);
 
 	reg = AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL;
 	reg |= AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP;
Signature · 1 line
1.7.10
#2 lore raw

PATCH 1/5 iio: at91: use adc_clk_khz to make the calculation not easy to large than u32.

Hi Josh,

Quoted 6 lines · Josh Wu

for example, if adc_clk is 20Mhz and start-up time set as larger than 215us. then the calculation "st->startup_time * adc_clk_khz" will out of u32.

In this patch, it will use khz unit for adc_clk, that avoids above problem.

Maybe we can have a slightly better commit log here, like:

------8<-----------
iio: at91: Fix adc_clk overflow

The adc_clk variable is currently defined using a 32-bits unsigned integer, which will overflow under some very valid range of operations.

Such overflow will occur if, for example, the parent clock is set to a 20MHz frequency and the ADC startup time is larger than 215ns.

To fix this, introduce an intermediate variable holding the clock rate in kHz.

--------8<---------------

Or something like that.

For the patch itself, I'm happy about it. You can add my Acked-by.

Thanks!
Maxime

Signature · 10 lines
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130715/b37339f0/attachment.sig>
author
#3 lore raw

PATCH 1/5 iio: at91: use adc_clk_khz to make the calculation not easy to large than u32.

Hi, Maxime

Quoted 20 lines · Maxime Ripard

Hi Josh,

Quoted 6 lines · Josh Wu

for example, if adc_clk is 20Mhz and start-up time set as larger than 215us. then the calculation "st->startup_time * adc_clk_khz" will out of u32.

In this patch, it will use khz unit for adc_clk, that avoids above problem.

Maybe we can have a slightly better commit log here, like:

------8<-----------
iio: at91: Fix adc_clk overflow

The adc_clk variable is currently defined using a 32-bits unsigned integer, which will overflow under some very valid range of operations.

Such overflow will occur if, for example, the parent clock is set to a 20MHz frequency and the ADC startup time is larger than 215ns.

Quoted 8 lines

To fix this, introduce an intermediate variable holding the clock rate in kHz.

--------8<---------------

Or something like that.

For the patch itself, I'm happy about it. You can add my Acked-by.

Thank you. Your commit log is better. I will fix the commit log base on your commit in next version.

Quoted 4 lines

Thanks!
Maxime

Best Regards,
Josh Wu

author
#4 lore raw

PATCH 2/5 iio: at91: Use different prescal, startup mask in MR for different IP

In reply to Josh Wu

For at91 boards, there are different IPs for adc. Different IPs has different STARTUP & PRESCAL mask in ADC_MR.

This patch can change the masks according to the different IP version.

---
 arch/arm/mach-at91/include/mach/at91_adc.h |    9 ++++--
 drivers/iio/adc/at91_adc.c                 |   48 ++++++++++++++++++++++++++--
 2 files changed, 53 insertions(+), 4 deletions(-)
arch/arm/mach-at91/include/mach/at91_adc.h +7 -2
index 8e7ed5c..ab273ee 100644
@@ -28,9 +28,12 @@
 #define			AT91_ADC_TRGSEL_EXTERNAL	(6 << 1)
 #define		AT91_ADC_LOWRES		(1 << 4)	/* Low Resolution */
 #define		AT91_ADC_SLEEP		(1 << 5)	/* Sleep Mode */
-#define		AT91_ADC_PRESCAL	(0x3f << 8)	/* Prescalar Rate Selection */
+#define		AT91_ADC_PRESCAL	(0xff << 8)	/* Prescalar Rate Selection */
+#define		AT91_ADC_PRESCAL_9260	(0x3f << 8)
 #define			AT91_ADC_PRESCAL_(x)	((x) << 8)
-#define		AT91_ADC_STARTUP	(0x1f << 16)	/* Startup Up Time */
+#define		AT91_ADC_STARTUP	(0xf << 16)	/* Startup Up Time */
+#define		AT91_ADC_STARTUP_9260	(0x1f << 16)
+#define		AT91_ADC_STARTUP_9G45	(0x7f << 16)
 #define			AT91_ADC_STARTUP_(x)	((x) << 16)
 #define		AT91_ADC_SHTIM		(0xf  << 24)	/* Sample & Hold Time */
 #define			AT91_ADC_SHTIM_(x)	((x) << 24)
@@ -58,4 +61,6 @@
 #define AT91_ADC_CHR(n)		(0x30 + ((n) * 4))	/* Channel Data Register N */
 #define		AT91_ADC_DATA		(0x3ff)
 
+#define AT91_ADC_VERSION	0xFC
+
 #endif
drivers/iio/adc/at91_adc.c +46 -2

78 lines not on this page · raw message

Signature · 1 line
1.7.10
#5 lore raw

PATCH 2/5 iio: at91: Use different prescal, startup mask in MR for different IP

Hi Josh,

Quoted 90 lines · Josh Wu

For at91 boards, there are different IPs for adc. Different IPs has different STARTUP & PRESCAL mask in ADC_MR.

This patch can change the masks according to the different IP version.

---
 arch/arm/mach-at91/include/mach/at91_adc.h |    9 ++++--
 drivers/iio/adc/at91_adc.c                 |   48 ++++++++++++++++++++++++++--
 2 files changed, 53 insertions(+), 4 deletions(-)
arch/arm/mach-at91/include/mach/at91_adc.h +7 -2
index 8e7ed5c..ab273ee 100644
@@ -28,9 +28,12 @@
 #define			AT91_ADC_TRGSEL_EXTERNAL	(6 << 1)
 #define		AT91_ADC_LOWRES		(1 << 4)	/* Low Resolution */
 #define		AT91_ADC_SLEEP		(1 << 5)	/* Sleep Mode */
-#define		AT91_ADC_PRESCAL	(0x3f << 8)	/* Prescalar Rate Selection */
+#define		AT91_ADC_PRESCAL	(0xff << 8)	/* Prescalar Rate Selection */
+#define		AT91_ADC_PRESCAL_9260	(0x3f << 8)
 #define			AT91_ADC_PRESCAL_(x)	((x) << 8)
-#define		AT91_ADC_STARTUP	(0x1f << 16)	/* Startup Up Time */
+#define		AT91_ADC_STARTUP	(0xf << 16)	/* Startup Up Time */
+#define		AT91_ADC_STARTUP_9260	(0x1f << 16)
+#define		AT91_ADC_STARTUP_9G45	(0x7f << 16)
 #define			AT91_ADC_STARTUP_(x)	((x) << 16)
 #define		AT91_ADC_SHTIM		(0xf  << 24)	/* Sample & Hold Time */
 #define			AT91_ADC_SHTIM_(x)	((x) << 24)
@@ -58,4 +61,6 @@
 #define AT91_ADC_CHR(n)		(0x30 + ((n) * 4))	/* Channel Data Register N */
 #define		AT91_ADC_DATA		(0x3ff)
 
+#define AT91_ADC_VERSION	0xFC
+
 #endif
drivers/iio/adc/at91_adc.c +31 -0
index 18bd54f..14e99ba 100644
@@ -39,6 +39,12 @@
 #define at91_adc_writel(st, reg, val) \
 	(writel_relaxed(val, st->reg_base + reg))
 
+struct at91_adc_caps {
+	bool	has_tsmr;	/* only at91sam9x5, sama5d3 have TSMR reg */
+	u32	mr_prescal_mask;
+	u32	mr_startup_mask;
+};
+
 struct at91_adc_state {
 	struct clk		*adc_clk;
 	u16			*buffer;
@@ -62,6 +68,7 @@ struct at91_adc_state {
 	u32			res;		/* resolution used for convertions */
 	bool			low_res;	/* the resolution corresponds to the lowest one */
 	wait_queue_head_t	wq_data_avail;
+	struct at91_adc_caps	caps;
 };
 
 static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
@@ -580,6 +587,41 @@ static const struct iio_info at91_adc_info = {
 	.read_raw = &at91_adc_read_raw,
 };
 
+/*
+ * Since atmel adc support different ip for touchscreen mode. Through the
+ * IP check, we will know the touchscreen capbilities.
+ */
+static void atmel_adc_get_cap(struct at91_adc_state *st)
+{
+	unsigned int version;
+	struct iio_dev *idev = iio_priv_to_dev(st);
+
+	version = at91_adc_readl(st, AT91_ADC_VERSION);
+	dev_dbg(&idev->dev, "version: 0x%x\n", version);
+
+	st->caps.mr_prescal_mask = AT91_ADC_PRESCAL_9260;
+	st->caps.mr_startup_mask = AT91_ADC_STARTUP_9260;
+
+	/* keep only major version number */
+	switch (version & 0xf00) {
+	case 0x500:	/* SAMA5D3 */
+	case 0x400:	/* AT91SAM9X5/9N12 */
+		st->caps.has_tsmr = 1;
+		st->caps.mr_startup_mask = AT91_ADC_STARTUP;
+	case 0x200:	/* AT91SAM9M10/9G45 */
+		st->caps.mr_prescal_mask = AT91_ADC_PRESCAL;
+

drivers/iio/adc/at91_adc.c static const struct iio_info at91_adc_info = { · line 623

Quoted 10 lines
drivers/iio/adc/at91_adc.c +10 -0
+		if ((version & 0xf00) == 0x200)
+			st->caps.mr_startup_mask = AT91_ADC_STARTUP_9G45;
+	case 0x100:	/* AT91SAM9260/9G20 */
+		break;
+	default:
+		dev_warn(&idev->dev,
+				"Unmanaged adc version, use minimal capabilities\n");
+		break;
+	};
+}

Why don't you use different compatible names and derive your capabilities from which compatible is declared.

It seems safer.

Maxime

Signature · 10 lines
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130715/896a845b/attachment.sig>
author
#6 lore raw

PATCH 2/5 iio: at91: Use different prescal, startup mask in MR for different IP

Hi, Maxime

Quoted 106 lines · Maxime Ripard

Hi Josh,

Quoted 90 lines · Josh Wu

For at91 boards, there are different IPs for adc. Different IPs has different STARTUP & PRESCAL mask in ADC_MR.

This patch can change the masks according to the different IP version.

---
  arch/arm/mach-at91/include/mach/at91_adc.h |    9 ++++--
  drivers/iio/adc/at91_adc.c                 |   48 ++++++++++++++++++++++++++--
  2 files changed, 53 insertions(+), 4 deletions(-)
arch/arm/mach-at91/include/mach/at91_adc.h +7 -2
index 8e7ed5c..ab273ee 100644
@@ -28,9 +28,12 @@
  #define			AT91_ADC_TRGSEL_EXTERNAL	(6 << 1)
  #define		AT91_ADC_LOWRES		(1 << 4)	/* Low Resolution */
  #define		AT91_ADC_SLEEP		(1 << 5)	/* Sleep Mode */
-#define		AT91_ADC_PRESCAL	(0x3f << 8)	/* Prescalar Rate Selection */
+#define		AT91_ADC_PRESCAL	(0xff << 8)	/* Prescalar Rate Selection */
+#define		AT91_ADC_PRESCAL_9260	(0x3f << 8)
  #define			AT91_ADC_PRESCAL_(x)	((x) << 8)
-#define		AT91_ADC_STARTUP	(0x1f << 16)	/* Startup Up Time */
+#define		AT91_ADC_STARTUP	(0xf << 16)	/* Startup Up Time */
+#define		AT91_ADC_STARTUP_9260	(0x1f << 16)
+#define		AT91_ADC_STARTUP_9G45	(0x7f << 16)
  #define			AT91_ADC_STARTUP_(x)	((x) << 16)
  #define		AT91_ADC_SHTIM		(0xf  << 24)	/* Sample & Hold Time */
  #define			AT91_ADC_SHTIM_(x)	((x) << 24)
@@ -58,4 +61,6 @@
  #define AT91_ADC_CHR(n)		(0x30 + ((n) * 4))	/* Channel Data Register N */
  #define		AT91_ADC_DATA		(0x3ff)
 
+#define AT91_ADC_VERSION	0xFC
+
  #endif
drivers/iio/adc/at91_adc.c +31 -0
index 18bd54f..14e99ba 100644
@@ -39,6 +39,12 @@
  #define at91_adc_writel(st, reg, val) \
  	(writel_relaxed(val, st->reg_base + reg))
 
+struct at91_adc_caps {
+	bool	has_tsmr;	/* only at91sam9x5, sama5d3 have TSMR reg */
+	u32	mr_prescal_mask;
+	u32	mr_startup_mask;
+};
+
  struct at91_adc_state {
  	struct clk		*adc_clk;
  	u16			*buffer;
@@ -62,6 +68,7 @@ struct at91_adc_state {
  	u32			res;		/* resolution used for convertions */
  	bool			low_res;	/* the resolution corresponds to the lowest one */
  	wait_queue_head_t	wq_data_avail;
+	struct at91_adc_caps	caps;
  };
 
  static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
@@ -580,6 +587,41 @@ static const struct iio_info at91_adc_info = {
  	.read_raw = &at91_adc_read_raw,
  };
 
+/*
+ * Since atmel adc support different ip for touchscreen mode. Through the
+ * IP check, we will know the touchscreen capbilities.
+ */
+static void atmel_adc_get_cap(struct at91_adc_state *st)
+{
+	unsigned int version;
+	struct iio_dev *idev = iio_priv_to_dev(st);
+
+	version = at91_adc_readl(st, AT91_ADC_VERSION);
+	dev_dbg(&idev->dev, "version: 0x%x\n", version);
+
+	st->caps.mr_prescal_mask = AT91_ADC_PRESCAL_9260;
+	st->caps.mr_startup_mask = AT91_ADC_STARTUP_9260;
+
+	/* keep only major version number */
+	switch (version & 0xf00) {
+	case 0x500:	/* SAMA5D3 */
+	case 0x400:	/* AT91SAM9X5/9N12 */
+		st->caps.has_tsmr = 1;
+		st->caps.mr_startup_mask = AT91_ADC_STARTUP;
+	case 0x200:	/* AT91SAM9M10/9G45 */
+		st->caps.mr_prescal_mask = AT91_ADC_PRESCAL;
+

drivers/iio/adc/at91_adc.c static const struct iio_info at91_adc_info = { · line 623

Quoted 10 lines
drivers/iio/adc/at91_adc.c +10 -0
+		if ((version & 0xf00) == 0x200)
+			st->caps.mr_startup_mask = AT91_ADC_STARTUP_9G45;
+	case 0x100:	/* AT91SAM9260/9G20 */
+		break;
+	default:
+		dev_warn(&idev->dev,
+				"Unmanaged adc version, use minimal capabilities\n");
+		break;
+	};
+}

Why don't you use different compatible names and derive your capabilities from which compatible is declared.

Quoted 1 line

It seems safer.

Ok, that make sense. I will use compatible names for the capabilities in next version. Thanks.

Quoted 3 lines

Maxime

Best Regards,
Josh Wu

#7 lore raw

PATCH 2/5 iio: at91: Use different prescal, startup mask in MR for different IP

On 16/07/2013 10:35, Josh Wu :

Quoted 116 lines

Hi, Maxime

Quoted 113 lines · Maxime Ripard

Hi Josh,

Quoted 96 lines · Josh Wu

For at91 boards, there are different IPs for adc. Different IPs has different
STARTUP & PRESCAL mask in ADC_MR.

This patch can change the masks according to the different IP version.

---
  arch/arm/mach-at91/include/mach/at91_adc.h |    9 ++++--
  drivers/iio/adc/at91_adc.c                 |   48
++++++++++++++++++++++++++--
  2 files changed, 53 insertions(+), 4 deletions(-)
arch/arm/mach-at91/include/mach/at91_adc.h +0 -1
index 8e7ed5c..ab273ee 100644
@@ -28,9 +28,12 @@
  #define            AT91_ADC_TRGSEL_EXTERNAL    (6 << 1)
  #define        AT91_ADC_LOWRES        (1 << 4)    /* Low Resolution */
  #define        AT91_ADC_SLEEP        (1 << 5)    /* Sleep Mode */
-#define        AT91_ADC_PRESCAL    (0x3f << 8)    /* Prescalar Rate

Selection */
+#define AT91_ADC_PRESCAL (0xff << 8) /* Prescalar Rate
Selection */

arch/arm/mach-at91/include/mach/at91_adc.h +1 -1
+#define        AT91_ADC_PRESCAL_9260    (0x3f << 8)
  #define            AT91_ADC_PRESCAL_(x)    ((x) << 8)
-#define        AT91_ADC_STARTUP    (0x1f << 16)    /* Startup Up

Time */

arch/arm/mach-at91/include/mach/at91_adc.h +3 -0
+#define        AT91_ADC_STARTUP    (0xf << 16)    /* Startup Up Time */
+#define        AT91_ADC_STARTUP_9260    (0x1f << 16)
+#define        AT91_ADC_STARTUP_9G45    (0x7f << 16)
  #define            AT91_ADC_STARTUP_(x)    ((x) << 16)
  #define        AT91_ADC_SHTIM        (0xf  << 24)    /* Sample &

Hold Time */
#define AT91_ADC_SHTIM_(x) ((x) << 24)

arch/arm/mach-at91/include/mach/at91_adc.h +0 -0
@@ -58,4 +61,6 @@
  #define AT91_ADC_CHR(n)        (0x30 + ((n) * 4))    /* Channel

Data Register N */
#define AT91_ADC_DATA (0x3ff)

arch/arm/mach-at91/include/mach/at91_adc.h +2 -0
+#define AT91_ADC_VERSION    0xFC
+
  #endif
drivers/iio/adc/at91_adc.c +6 -0
index 18bd54f..14e99ba 100644
@@ -39,6 +39,12 @@
  #define at91_adc_writel(st, reg, val) \
      (writel_relaxed(val, st->reg_base + reg))
+struct at91_adc_caps {
+    bool    has_tsmr;    /* only at91sam9x5, sama5d3 have TSMR reg */
+    u32    mr_prescal_mask;
+    u32    mr_startup_mask;
+};
+
  struct at91_adc_state {
      struct clk        *adc_clk;
      u16            *buffer;
@@ -62,6 +68,7 @@ struct at91_adc_state {
      u32            res;        /* resolution used for convertions */
      bool            low_res;    /* the resolution corresponds to

the lowest one */

      wait_queue_head_t    wq_data_avail;
drivers/iio/adc/at91_adc.c +3 -0
+    struct at91_adc_caps    caps;
  };
  static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
@@ -580,6 +587,41 @@ static const struct iio_info at91_adc_info = {
      .read_raw = &at91_adc_read_raw,
  };
+/*
+ * Since atmel adc support different ip for touchscreen mode.

Through the
+ * IP check, we will know the touchscreen capbilities.
+ */

drivers/iio/adc/at91_adc.c +20 -0
+static void atmel_adc_get_cap(struct at91_adc_state *st)
+{
+    unsigned int version;
+    struct iio_dev *idev = iio_priv_to_dev(st);
+
+    version = at91_adc_readl(st, AT91_ADC_VERSION);
+    dev_dbg(&idev->dev, "version: 0x%x\n", version);
+
+    st->caps.mr_prescal_mask = AT91_ADC_PRESCAL_9260;
+    st->caps.mr_startup_mask = AT91_ADC_STARTUP_9260;
+
+    /* keep only major version number */
+    switch (version & 0xf00) {
+    case 0x500:    /* SAMA5D3 */
+    case 0x400:    /* AT91SAM9X5/9N12 */
+        st->caps.has_tsmr = 1;
+        st->caps.mr_startup_mask = AT91_ADC_STARTUP;
+    case 0x200:    /* AT91SAM9M10/9G45 */
+        st->caps.mr_prescal_mask = AT91_ADC_PRESCAL;
+

drivers/iio/adc/at91_adc.c static const struct iio_info at91_adc_info = { · line 623

Quoted 10 lines
drivers/iio/adc/at91_adc.c +10 -0
+        if ((version & 0xf00) == 0x200)
+            st->caps.mr_startup_mask = AT91_ADC_STARTUP_9G45;
+    case 0x100:    /* AT91SAM9260/9G20 */
+        break;
+    default:
+        dev_warn(&idev->dev,
+                "Unmanaged adc version, use minimal capabilities\n");
+        break;
+    };
+}

Why don't you use different compatible names and derive your capabilities from which compatible is declared.

It seems safer.

I see it as handier in the sense that a different IP version can be compatible with an older IP version: so we do not need to modify the driver just to use another SoC.

On your side Maxime, what makes you say that it is "safer"?

Quoted 2 lines

Ok, that make sense. I will use compatible names for the capabilities in next version. Thanks.

Hold on a little bit Josh, I know that Jean-Christophe is not in favor of the use of multiple compatible strings. So, as the code is already there, let's wait and see if we find another argument...

Bye,

Signature · 1 line
Nicolas Ferre
#8 lore raw

PATCH 2/5 iio: at91: Use different prescal, startup mask in MR for different IP

Quoted 9 lines · Nicolas Ferre
Quoted 3 lines
Quoted 3 lines

capabilities from which compatible is declared.

It seems safer.

I see it as handier in the sense that a different IP version can be compatible with an older IP version: so we do not need to modify the driver just to use another SoC.

On your side Maxime, what makes you say that it is "safer"?

Well, the register holding the IP version seem to be not programmed in some cases (or, at least, the driver handles this case).

So, what would happen if one SoC was in such case? You wouldn't be able to use the ADC/touchscreen, even though the IP in itself might very well work, which doesn't sound very nice, while the DT will always be there, and will always have a compatible property.

Quoted 6 lines
Quoted 2 lines

Ok, that make sense. I will use compatible names for the capabilities in next version. Thanks.

Hold on a little bit Josh, I know that Jean-Christophe is not in favor of the use of multiple compatible strings. So, as the code is already there, let's wait and see if we find another argument...

And you know my feeling about this for quite some time already ;)

Maxime

Signature · 10 lines
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130716/17f0b0cb/attachment-0001.sig>
#9 lore raw

PATCH 2/5 iio: at91: Use different prescal, startup mask in MR for different IP

In reply to Nicolas Ferre

Dear Nicolas Ferre,

Quoted 6 lines · Nicolas Ferre
Quoted 2 lines

Ok, that make sense. I will use compatible names for the capabilities in next version. Thanks.

Hold on a little bit Josh, I know that Jean-Christophe is not in favor of the use of multiple compatible strings. So, as the code is already there, let's wait and see if we find another argument...

I've asked exactly this question last week at Linaro Connect during the ARM SoC consolidation panel/discussion, where Grant Likely, Arnd Bergmann, Olof and others were answering Device Tree related questions.

My question, which precisely had the at91-adc DT binding in mind was precisely whether we should use different compatible properties to identify different revisions of an IP block and let the driver handle those differences, or whether the DT binding should provide sufficient properties (register offsets, bit numbers, etc.) to make the driver independent of the IP revisions. And clearly, the answer was that different compatible properties should be used to identify the different versions of the IP block, and the driver should abstract out the differences. I.e, was has been done for at91-adc is completely the opposite of the best practices for Device Tree on ARM.

See
http://www.youtube.com/watch?v=zF_AXLgkFy4&feature=player_detailpage#t=1581s
where I ask exactly this question, and get answers from Olof Johansson and Grant Likely. They clearly say that the solution of having separate compatible properties and a driver that handles the differences is the way to go. So the way at91-adc (and possibly other at91 drivers) is using the Device Tree is wrong, there should have been multiple compatible properties. It's a shame because this is something we did say when we submitted at91-adc and during the reviews, but the maintainer wasn't listening to our comments...

Best regards,

Thomas

Signature · 4 lines
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
maintainer
#10 lore raw

PATCH 2/5 iio: at91: Use different prescal, startup mask in MR for different IP

Quoted 26 lines · Thomas Petazzoni

Dear Nicolas Ferre,

Quoted 6 lines · Nicolas Ferre
Quoted 2 lines

Ok, that make sense. I will use compatible names for the capabilities in next version. Thanks.

Hold on a little bit Josh, I know that Jean-Christophe is not in favor of the use of multiple compatible strings. So, as the code is already there, let's wait and see if we find another argument...

I've asked exactly this question last week at Linaro Connect during the ARM SoC consolidation panel/discussion, where Grant Likely, Arnd Bergmann, Olof and others were answering Device Tree related questions.

My question, which precisely had the at91-adc DT binding in mind was precisely whether we should use different compatible properties to identify different revisions of an IP block and let the driver handle those differences, or whether the DT binding should provide sufficient properties (register offsets, bit numbers, etc.) to make the driver independent of the IP revisions. And clearly, the answer was that different compatible properties should be used to identify the different versions of the IP block, and the driver should abstract out the differences. I.e, was has been done for at91-adc is completely the opposite of the best practices for Device Tree on ARM.

Quoted 11 lines

See
http://www.youtube.com/watch?v=zF_AXLgkFy4&feature=player_detailpage#t=1581s
where I ask exactly this question, and get answers from Olof Johansson and Grant Likely. They clearly say that the solution of having separate compatible properties and a driver that handles the differences is the way to go. So the way at91-adc (and possibly other at91 drivers) is using the Device Tree is wrong, there should have been multiple compatible properties. It's a shame because this is something we did say when we submitted at91-adc and during the reviews, but the maintainer wasn't listening to our comments...

Thanks for getting some clarity on this Thomas. So I'll ask the somewhat obvious question - how do we unwind from where we are to where we want to be wrt to the bindings?

#11 lore raw

PATCH 2/5 iio: at91: Use different prescal, startup mask in MR for different IP

Dear Jonathan Cameron,

Quoted 28 lines · Jonathan Cameron
Quoted 15 lines · Thomas Petazzoni

I've asked exactly this question last week at Linaro Connect during the ARM SoC consolidation panel/discussion, where Grant Likely, Arnd Bergmann, Olof and others were answering Device Tree related questions.

My question, which precisely had the at91-adc DT binding in mind was precisely whether we should use different compatible properties to identify different revisions of an IP block and let the driver handle those differences, or whether the DT binding should provide sufficient properties (register offsets, bit numbers, etc.) to make the driver independent of the IP revisions. And clearly, the answer was that different compatible properties should be used to identify the different versions of the IP block, and the driver should abstract out the differences. I.e, was has been done for at91-adc is completely the opposite of the best practices for Device Tree on ARM.

Quoted 11 lines

See
http://www.youtube.com/watch?v=zF_AXLgkFy4&feature=player_detailpage#t=1581s
where I ask exactly this question, and get answers from Olof Johansson and Grant Likely. They clearly say that the solution of having separate compatible properties and a driver that handles the differences is the way to go. So the way at91-adc (and possibly other at91 drivers) is using the Device Tree is wrong, there should have been multiple compatible properties. It's a shame because this is something we did say when we submitted at91-adc and during the reviews, but the maintainer wasn't listening to our comments...

Quoted 3 lines

Thanks for getting some clarity on this Thomas. So I'll ask the somewhat obvious question - how do we unwind from where we are to where we want to be wrt to the bindings?

During Linaro Connect last week, there was some discussion about marking DT bindings as unstable for a little while, once they get reviewed by a group of DT "experts" that mark them as stable. Until they are stable, the kernel does not offer any ABI guarantees, and we are free to change the DT bindings as needed.

Now, since this unstable/stable thing is not in place at the moment, deciding whether to break or not existing bindings is something to be decided by the maintainer of this platform, judging what is the best option depending on whether there are already many users of the DT for this platform or not, for example.

Best regards,

Thomas

Signature · 4 lines
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
#12 lore raw

PATCH 2/5 iio: at91: Use different prescal, startup mask in MR for different IP

On 16/07/2013 21:17, Thomas Petazzoni :

Quoted 42 lines

Dear Jonathan Cameron,

Quoted 28 lines · Jonathan Cameron
Quoted 15 lines · Thomas Petazzoni

I've asked exactly this question last week at Linaro Connect during the ARM SoC consolidation panel/discussion, where Grant Likely, Arnd Bergmann, Olof and others were answering Device Tree related questions.

My question, which precisely had the at91-adc DT binding in mind was precisely whether we should use different compatible properties to identify different revisions of an IP block and let the driver handle those differences, or whether the DT binding should provide sufficient properties (register offsets, bit numbers, etc.) to make the driver independent of the IP revisions. And clearly, the answer was that different compatible properties should be used to identify the different versions of the IP block, and the driver should abstract out the differences. I.e, was has been done for at91-adc is completely the opposite of the best practices for Device Tree on ARM.

Quoted 11 lines

See
http://www.youtube.com/watch?v=zF_AXLgkFy4&feature=player_detailpage#t=1581s
where I ask exactly this question, and get answers from Olof Johansson and Grant Likely. They clearly say that the solution of having separate compatible properties and a driver that handles the differences is the way to go. So the way at91-adc (and possibly other at91 drivers) is using the Device Tree is wrong, there should have been multiple compatible properties. It's a shame because this is something we did say when we submitted at91-adc and during the reviews, but the maintainer wasn't listening to our comments...

Quoted 3 lines

Thanks for getting some clarity on this Thomas. So I'll ask the somewhat obvious question - how do we unwind from where we are to where we want to be wrt to the bindings?

During Linaro Connect last week, there was some discussion about marking DT bindings as unstable for a little while, once they get reviewed by a group of DT "experts" that mark them as stable. Until they are stable, the kernel does not offer any ABI guarantees, and we are free to change the DT bindings as needed.

Quoted 5 lines

Now, since this unstable/stable thing is not in place at the moment, deciding whether to break or not existing bindings is something to be decided by the maintainer of this platform, judging what is the best option depending on whether there are already many users of the DT for this platform or not, for example.

I didn't had in mind that the current discussion about the addition of some properties could cast doubt on the entire at91-adc binding!

The binding itself has several drawbacks and is kind of over engineered, I agree with that. Some register offsets in particular have nothing to do in a DT binding.
On the other hand, some values are highly dependent on the SoC process itself and can't be stored in the driver because it would require to change the driver for each new SoC, depending on the electrical characteristics.
In conclusion, we have to be cautious with this binding and make sure that we don't throw the baby out with the bath water.

Moreover, at the time we are just beginning to be comfortable with DT on AT91 and beginning to overcome the difficulty of converting our platforms, I see this new step on the path to "mainline + DT stable" as another slowdown.

Bye,

Signature · 1 line
Nicolas Ferre
#13 lore raw

PATCH 2/5 iio: at91: Use different prescal, startup mask in MR for different IP

In reply to Maxime Ripard

On 15/07/2013 14:58, Maxime Ripard :

Quoted 98 lines

Hi Josh,

Quoted 95 lines · Josh Wu

For at91 boards, there are different IPs for adc. Different IPs has different STARTUP & PRESCAL mask in ADC_MR.

This patch can change the masks according to the different IP version.

---
  arch/arm/mach-at91/include/mach/at91_adc.h |    9 ++++--
  drivers/iio/adc/at91_adc.c                 |   48 ++++++++++++++++++++++++++--
  2 files changed, 53 insertions(+), 4 deletions(-)
arch/arm/mach-at91/include/mach/at91_adc.h +7 -2
index 8e7ed5c..ab273ee 100644
@@ -28,9 +28,12 @@
  #define			AT91_ADC_TRGSEL_EXTERNAL	(6 << 1)
  #define		AT91_ADC_LOWRES		(1 << 4)	/* Low Resolution */
  #define		AT91_ADC_SLEEP		(1 << 5)	/* Sleep Mode */
-#define		AT91_ADC_PRESCAL	(0x3f << 8)	/* Prescalar Rate Selection */
+#define		AT91_ADC_PRESCAL	(0xff << 8)	/* Prescalar Rate Selection */
+#define		AT91_ADC_PRESCAL_9260	(0x3f << 8)
  #define			AT91_ADC_PRESCAL_(x)	((x) << 8)
-#define		AT91_ADC_STARTUP	(0x1f << 16)	/* Startup Up Time */
+#define		AT91_ADC_STARTUP	(0xf << 16)	/* Startup Up Time */
+#define		AT91_ADC_STARTUP_9260	(0x1f << 16)
+#define		AT91_ADC_STARTUP_9G45	(0x7f << 16)
  #define			AT91_ADC_STARTUP_(x)	((x) << 16)
  #define		AT91_ADC_SHTIM		(0xf  << 24)	/* Sample & Hold Time */
  #define			AT91_ADC_SHTIM_(x)	((x) << 24)
@@ -58,4 +61,6 @@
  #define AT91_ADC_CHR(n)		(0x30 + ((n) * 4))	/* Channel Data Register N */
  #define		AT91_ADC_DATA		(0x3ff)
 
+#define AT91_ADC_VERSION	0xFC
+
  #endif
drivers/iio/adc/at91_adc.c +36 -0
index 18bd54f..14e99ba 100644
@@ -39,6 +39,12 @@
  #define at91_adc_writel(st, reg, val) \
  	(writel_relaxed(val, st->reg_base + reg))
 
+struct at91_adc_caps {
+	bool	has_tsmr;	/* only at91sam9x5, sama5d3 have TSMR reg */
+	u32	mr_prescal_mask;
+	u32	mr_startup_mask;
+};
+
  struct at91_adc_state {
  	struct clk		*adc_clk;
  	u16			*buffer;
@@ -62,6 +68,7 @@ struct at91_adc_state {
  	u32			res;		/* resolution used for convertions */
  	bool			low_res;	/* the resolution corresponds to the lowest one */
  	wait_queue_head_t	wq_data_avail;
+	struct at91_adc_caps	caps;
  };
 
  static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
@@ -580,6 +587,41 @@ static const struct iio_info at91_adc_info = {
  	.read_raw = &at91_adc_read_raw,
  };
 
+/*
+ * Since atmel adc support different ip for touchscreen mode. Through the
+ * IP check, we will know the touchscreen capbilities.
+ */
+static void atmel_adc_get_cap(struct at91_adc_state *st)
+{
+	unsigned int version;
+	struct iio_dev *idev = iio_priv_to_dev(st);
+
+	version = at91_adc_readl(st, AT91_ADC_VERSION);
+	dev_dbg(&idev->dev, "version: 0x%x\n", version);
+
+	st->caps.mr_prescal_mask = AT91_ADC_PRESCAL_9260;
+	st->caps.mr_startup_mask = AT91_ADC_STARTUP_9260;
+
+	/* keep only major version number */
+	switch (version & 0xf00) {
+	case 0x500:	/* SAMA5D3 */
+	case 0x400:	/* AT91SAM9X5/9N12 */
+		st->caps.has_tsmr = 1;
+		st->caps.mr_startup_mask = AT91_ADC_STARTUP;
+	case 0x200:	/* AT91SAM9M10/9G45 */
+		st->caps.mr_prescal_mask = AT91_ADC_PRESCAL;
+
+		if ((version & 0xf00) == 0x200)
+			st->caps.mr_startup_mask = AT91_ADC_STARTUP_9G45;
+	case 0x100:	/* AT91SAM9260/9G20 */
+		break;
+	default:
Quoted 8 lines

drivers/iio/adc/at91_adc.c static const struct iio_info at91_adc_info = { · line 623

Quoted 5 lines
drivers/iio/adc/at91_adc.c +5 -0
+		dev_warn(&idev->dev,
+				"Unmanaged adc version, use minimal capabilities\n");
+		break;
+	};
+}

Why don't you use different compatible names and derive your capabilities from which compatible is declared.

Do not forget that we still have a handful of platforms that do not support DT (and never will).

Josh, do you think that we can deal with this with different compatible string instead of checking the IP revision?

Bye,

Signature · 1 line
Nicolas Ferre
author
#14 lore raw

PATCH 2/5 iio: at91: Use different prescal, startup mask in MR for different IP

Hi, Nicolas

Quoted 120 lines · Nicolas Ferre

On 15/07/2013 14:58, Maxime Ripard :

Quoted 110 lines

Hi Josh,

Quoted 107 lines · Josh Wu

For at91 boards, there are different IPs for adc. Different IPs has different
STARTUP & PRESCAL mask in ADC_MR.

This patch can change the masks according to the different IP version.

---
  arch/arm/mach-at91/include/mach/at91_adc.h |    9 ++++--
  drivers/iio/adc/at91_adc.c                 |   48 
++++++++++++++++++++++++++--
  2 files changed, 53 insertions(+), 4 deletions(-)
arch/arm/mach-at91/include/mach/at91_adc.h +0 -0
index 8e7ed5c..ab273ee 100644
@@ -28,9 +28,12 @@
  #define            AT91_ADC_TRGSEL_EXTERNAL    (6 << 1)
  #define        AT91_ADC_LOWRES        (1 << 4)    /* Low 

Resolution */
#define AT91_ADC_SLEEP (1 << 5) /* Sleep Mode */
-#define AT91_ADC_PRESCAL (0x3f << 8) /* Prescalar Rate
Selection */
+#define AT91_ADC_PRESCAL (0xff << 8) /* Prescalar Rate
Selection */

arch/arm/mach-at91/include/mach/at91_adc.h +1 -1
+#define        AT91_ADC_PRESCAL_9260    (0x3f << 8)
  #define            AT91_ADC_PRESCAL_(x)    ((x) << 8)
-#define        AT91_ADC_STARTUP    (0x1f << 16)    /* Startup Up 

Time */
+#define AT91_ADC_STARTUP (0xf << 16) /* Startup Up
Time */

arch/arm/mach-at91/include/mach/at91_adc.h +2 -0
+#define        AT91_ADC_STARTUP_9260    (0x1f << 16)
+#define        AT91_ADC_STARTUP_9G45    (0x7f << 16)
  #define            AT91_ADC_STARTUP_(x)    ((x) << 16)
  #define        AT91_ADC_SHTIM        (0xf  << 24) /* Sample & Hold 

Time */
#define AT91_ADC_SHTIM_(x) ((x) << 24)

arch/arm/mach-at91/include/mach/at91_adc.h +0 -0
@@ -58,4 +61,6 @@
  #define AT91_ADC_CHR(n)        (0x30 + ((n) * 4))    /* Channel 

Data Register N */
#define AT91_ADC_DATA (0x3ff)

arch/arm/mach-at91/include/mach/at91_adc.h +2 -0
+#define AT91_ADC_VERSION    0xFC
+
  #endif
drivers/iio/adc/at91_adc.c +6 -0
index 18bd54f..14e99ba 100644
@@ -39,6 +39,12 @@
  #define at91_adc_writel(st, reg, val) \
      (writel_relaxed(val, st->reg_base + reg))
 
+struct at91_adc_caps {
+    bool    has_tsmr;    /* only at91sam9x5, sama5d3 have TSMR reg */
+    u32    mr_prescal_mask;
+    u32    mr_startup_mask;
+};
+
  struct at91_adc_state {
      struct clk        *adc_clk;
      u16            *buffer;
@@ -62,6 +68,7 @@ struct at91_adc_state {
      u32            res;        /* resolution used for convertions */
      bool            low_res;    /* the resolution corresponds to 

the lowest one */

      wait_queue_head_t    wq_data_avail;
drivers/iio/adc/at91_adc.c +3 -0
+    struct at91_adc_caps    caps;
  };
 
  static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
@@ -580,6 +587,41 @@ static const struct iio_info at91_adc_info = {
      .read_raw = &at91_adc_read_raw,
  };
 
+/*
+ * Since atmel adc support different ip for touchscreen mode. 

Through the
+ * IP check, we will know the touchscreen capbilities.
+ */

drivers/iio/adc/at91_adc.c +25 -0
+static void atmel_adc_get_cap(struct at91_adc_state *st)
+{
+    unsigned int version;
+    struct iio_dev *idev = iio_priv_to_dev(st);
+
+    version = at91_adc_readl(st, AT91_ADC_VERSION);
+    dev_dbg(&idev->dev, "version: 0x%x\n", version);
+
+    st->caps.mr_prescal_mask = AT91_ADC_PRESCAL_9260;
+    st->caps.mr_startup_mask = AT91_ADC_STARTUP_9260;
+
+    /* keep only major version number */
+    switch (version & 0xf00) {
+    case 0x500:    /* SAMA5D3 */
+    case 0x400:    /* AT91SAM9X5/9N12 */
+        st->caps.has_tsmr = 1;
+        st->caps.mr_startup_mask = AT91_ADC_STARTUP;
+    case 0x200:    /* AT91SAM9M10/9G45 */
+        st->caps.mr_prescal_mask = AT91_ADC_PRESCAL;
+
+        if ((version & 0xf00) == 0x200)
+            st->caps.mr_startup_mask = AT91_ADC_STARTUP_9G45;
+    case 0x100:    /* AT91SAM9260/9G20 */
+        break;
+    default:
Quoted 8 lines

drivers/iio/adc/at91_adc.c static const struct iio_info at91_adc_info = { · line 623

Quoted 5 lines
drivers/iio/adc/at91_adc.c +5 -0
+        dev_warn(&idev->dev,
+                "Unmanaged adc version, use minimal capabilities\n");
+        break;
+    };
+}

Why don't you use different compatible names and derive your capabilities from which compatible is declared.

Quoted 5 lines

Do not forget that we still have a handful of platforms that do not support DT (and never will).

Josh, do you think that we can deal with this with different compatible string instead of checking the IP revision?

yes, the simplest way is define additional adc_cap structure for different compatible string.
like following:

static const struct of_device_id at91_adc_dt_ids[] = {
     { .compatible = "atmel,at91sam9260-adc", .data 
=&at91sam9260-adc-caps },
     { .compatible = "atmel,at91sam9x5-adc", .data =&at91sam9x5-adc-caps },
     {},
};

Then we can define the capability for different chip very easy.

Quoted 2 lines

Bye,

Best Regards,
Josh Wu

#15 lore raw

PATCH 2/5 iio: at91: Use different prescal, startup mask in MR for different IP

In reply to Nicolas Ferre

Hi Nicolas,

Quoted 40 lines · Nicolas Ferre

On 15/07/2013 14:58, Maxime Ripard :

Quoted 30 lines · Josh Wu
Quoted 29 lines
drivers/iio/adc/at91_adc.c +29 -0
+/*
+ * Since atmel adc support different ip for touchscreen mode. Through the
+ * IP check, we will know the touchscreen capbilities.
+ */
+static void atmel_adc_get_cap(struct at91_adc_state *st)
+{
+	unsigned int version;
+	struct iio_dev *idev = iio_priv_to_dev(st);
+
+	version = at91_adc_readl(st, AT91_ADC_VERSION);
+	dev_dbg(&idev->dev, "version: 0x%x\n", version);
+
+	st->caps.mr_prescal_mask = AT91_ADC_PRESCAL_9260;
+	st->caps.mr_startup_mask = AT91_ADC_STARTUP_9260;
+
+	/* keep only major version number */
+	switch (version & 0xf00) {
+	case 0x500:	/* SAMA5D3 */
+	case 0x400:	/* AT91SAM9X5/9N12 */
+		st->caps.has_tsmr = 1;
+		st->caps.mr_startup_mask = AT91_ADC_STARTUP;
+	case 0x200:	/* AT91SAM9M10/9G45 */
+		st->caps.mr_prescal_mask = AT91_ADC_PRESCAL;
+
+		if ((version & 0xf00) == 0x200)
+			st->caps.mr_startup_mask = AT91_ADC_STARTUP_9G45;
+	case 0x100:	/* AT91SAM9260/9G20 */
+		break;
+	default:
Quoted 8 lines

drivers/iio/adc/at91_adc.c static const struct iio_info at91_adc_info = { · line 623

Quoted 5 lines
drivers/iio/adc/at91_adc.c +5 -0
+		dev_warn(&idev->dev,
+				"Unmanaged adc version, use minimal capabilities\n");
+		break;
+	};
+}

Why don't you use different compatible names and derive your capabilities from which compatible is declared.

Quoted 2 lines

Do not forget that we still have a handful of platforms that do not support DT (and never will).

You can do pretty much the same thing with board-file-probed device drivers, using the id_table field of the platform_driver structure and use the driver_data field of the associated platform_device_id array to store the needed values.

(see for example drivers/gpio/gpio-mxs.c)

Maxime

Signature · 10 lines
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130717/71912aca/attachment-0001.sig>
#16 lore raw

PATCH 2/5 iio: at91: Use different prescal, startup mask in MR for different IP

In reply to Josh Wu

On 14/07/2013 10:04, Josh Wu :

Quoted 16 lines

For at91 boards, there are different IPs for adc. Different IPs has different STARTUP & PRESCAL mask in ADC_MR.

This patch can change the masks according to the different IP version.

---
  arch/arm/mach-at91/include/mach/at91_adc.h |    9 ++++--
  drivers/iio/adc/at91_adc.c                 |   48 ++++++++++++++++++++++++++--
  2 files changed, 53 insertions(+), 4 deletions(-)
arch/arm/mach-at91/include/mach/at91_adc.h +0 -0
index 8e7ed5c..ab273ee 100644

arch/arm/mach-at91/include/mach/at91_adc.h line 34

Quoted 9 lines
arch/arm/mach-at91/include/mach/at91_adc.h +3 -2
@@ -28,9 +28,12 @@
  #define			AT91_ADC_TRGSEL_EXTERNAL	(6 << 1)
  #define		AT91_ADC_LOWRES		(1 << 4)	/* Low Resolution */
  #define		AT91_ADC_SLEEP		(1 << 5)	/* Sleep Mode */
-#define		AT91_ADC_PRESCAL	(0x3f << 8)	/* Prescalar Rate Selection */
+#define		AT91_ADC_PRESCAL	(0xff << 8)	/* Prescalar Rate Selection */
+#define		AT91_ADC_PRESCAL_9260	(0x3f << 8)
  #define			AT91_ADC_PRESCAL_(x)	((x) << 8)
-#define		AT91_ADC_STARTUP	(0x1f << 16)	/* Startup Up Time */
+#define		AT91_ADC_STARTUP	(0xf << 16)	/* Startup Up Time */

Which product has this kind of startup mask: 0xf? Compared with 9260 and 9g45 values, it seems strange to me: maybe a little comment should be added.

Quoted 17 lines
arch/arm/mach-at91/include/mach/at91_adc.h +4 -0
+#define		AT91_ADC_STARTUP_9260	(0x1f << 16)
+#define		AT91_ADC_STARTUP_9G45	(0x7f << 16)
  #define			AT91_ADC_STARTUP_(x)	((x) << 16)
  #define		AT91_ADC_SHTIM		(0xf  << 24)	/* Sample & Hold Time */
  #define			AT91_ADC_SHTIM_(x)	((x) << 24)
@@ -58,4 +61,6 @@
  #define AT91_ADC_CHR(n)		(0x30 + ((n) * 4))	/* Channel Data Register N */
  #define		AT91_ADC_DATA		(0x3ff)
 
+#define AT91_ADC_VERSION	0xFC
+
  #endif
drivers/iio/adc/at91_adc.c +0 -0
index 18bd54f..14e99ba 100644

drivers/iio/adc/at91_adc.c line 47

Quoted 9 lines
drivers/iio/adc/at91_adc.c +6 -0
@@ -39,6 +39,12 @@
  #define at91_adc_writel(st, reg, val) \
  	(writel_relaxed(val, st->reg_base + reg))
 
+struct at91_adc_caps {
+	bool	has_tsmr;	/* only at91sam9x5, sama5d3 have TSMR reg */
+	u32	mr_prescal_mask;
+	u32	mr_startup_mask;
+};
+

If we move to DT and compatible string for these values, maybe a separate structure won't be needed....

Quoted 26 lines
drivers/iio/adc/at91_adc.c +12 -0
  struct at91_adc_state {
  	struct clk		*adc_clk;
  	u16			*buffer;
@@ -62,6 +68,7 @@ struct at91_adc_state {
  	u32			res;		/* resolution used for convertions */
  	bool			low_res;	/* the resolution corresponds to the lowest one */
  	wait_queue_head_t	wq_data_avail;
+	struct at91_adc_caps	caps;
  };
 
  static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
@@ -580,6 +587,41 @@ static const struct iio_info at91_adc_info = {
  	.read_raw = &at91_adc_read_raw,
  };
 
+/*
+ * Since atmel adc support different ip for touchscreen mode. Through the
+ * IP check, we will know the touchscreen capbilities.
+ */
+static void atmel_adc_get_cap(struct at91_adc_state *st)
+{
+	unsigned int version;
+	struct iio_dev *idev = iio_priv_to_dev(st);
+
+	version = at91_adc_readl(st, AT91_ADC_VERSION);
+	dev_dbg(&idev->dev, "version: 0x%x\n", version);

drivers/iio/adc/at91_adc.c static const struct iio_info at91_adc_info = { · line 610

Quoted 10 lines
drivers/iio/adc/at91_adc.c +10 -0
+
+	st->caps.mr_prescal_mask = AT91_ADC_PRESCAL_9260;
+	st->caps.mr_startup_mask = AT91_ADC_STARTUP_9260;
+
+	/* keep only major version number */
+	switch (version & 0xf00) {
+	case 0x500:	/* SAMA5D3 */
+	case 0x400:	/* AT91SAM9X5/9N12 */
+		st->caps.has_tsmr = 1;
+		st->caps.mr_startup_mask = AT91_ADC_STARTUP;

Here is where AT91_ADC_STARTUP may need another name: what about AT91_ADC_STARTUP_9x5 to match the "compatibility" of this value?

drivers/iio/adc/at91_adc.c static const struct iio_info at91_adc_info = { · line 614

Quoted 4 lines
drivers/iio/adc/at91_adc.c +4 -0
+	case 0x200:	/* AT91SAM9M10/9G45 */
+		st->caps.mr_prescal_mask = AT91_ADC_PRESCAL;
+
+		if ((version & 0xf00) == 0x200)

Sorry but I do not understand this test in a switch/case entry where it should always be true...

Quoted 34 lines
drivers/iio/adc/at91_adc.c +14 -2
+			st->caps.mr_startup_mask = AT91_ADC_STARTUP_9G45;
+	case 0x100:	/* AT91SAM9260/9G20 */
+		break;
+	default:
+		dev_warn(&idev->dev,
+				"Unmanaged adc version, use minimal capabilities\n");
+		break;
+	};
+}
+
  static int at91_adc_probe(struct platform_device *pdev)
  {
  	unsigned int prsc, mstrclk, ticks, adc_clk, adc_clk_khz, shtim;
@@ -645,6 +687,8 @@ static int at91_adc_probe(struct platform_device *pdev)
  		goto error_free_device;
  	}
 
+	atmel_adc_get_cap(st);
+
  	st->clk = devm_clk_get(&pdev->dev, "adc_clk");
  	if (IS_ERR(st->clk)) {
  		dev_err(&pdev->dev, "Failed to get the clock.\n");
@@ -704,8 +748,8 @@ static int at91_adc_probe(struct platform_device *pdev)
  	shtim = round_up((st->sample_hold_time * adc_clk_khz /
  			  1000) - 1, 1);
 
-	reg = AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL;
-	reg |= AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP;
+	reg = AT91_ADC_PRESCAL_(prsc) & st->caps.mr_prescal_mask;
+	reg |= AT91_ADC_STARTUP_(ticks) & st->caps.mr_startup_mask;
  	if (st->low_res)
  		reg |= AT91_ADC_LOWRES;
  	if (st->sleep_mode)
Signature · 1 line
Nicolas Ferre
author
#17 lore raw

PATCH 2/5 iio: at91: Use different prescal, startup mask in MR for different IP

Hi, Nicolas

Thanks for your review.

Quoted 28 lines · Nicolas Ferre

On 14/07/2013 10:04, Josh Wu :

Quoted 27 lines

For at91 boards, there are different IPs for adc. Different IPs has different
STARTUP & PRESCAL mask in ADC_MR.

This patch can change the masks according to the different IP version.

---
  arch/arm/mach-at91/include/mach/at91_adc.h |    9 ++++--
  drivers/iio/adc/at91_adc.c                 |   48 
++++++++++++++++++++++++++--
  2 files changed, 53 insertions(+), 4 deletions(-)
arch/arm/mach-at91/include/mach/at91_adc.h +0 -1
index 8e7ed5c..ab273ee 100644
@@ -28,9 +28,12 @@
  #define            AT91_ADC_TRGSEL_EXTERNAL    (6 << 1)
  #define        AT91_ADC_LOWRES        (1 << 4)    /* Low Resolution */
  #define        AT91_ADC_SLEEP        (1 << 5)    /* Sleep Mode */
-#define        AT91_ADC_PRESCAL    (0x3f << 8)    /* Prescalar Rate 

Selection */
+#define AT91_ADC_PRESCAL (0xff << 8) /* Prescalar Rate
Selection */
+#define AT91_ADC_PRESCAL_9260 (0x3f << 8)

Quoted 8 lines
Quoted 4 lines

#define AT91_ADC_PRESCAL_(x) ((x) << 8)
-#define AT91_ADC_STARTUP (0x1f << 16) /* Startup Up
Time */
+#define AT91_ADC_STARTUP (0xf << 16) /* Startup Up Time */

Which product has this kind of startup mask: 0xf? Compared with 9260 and 9g45 values, it seems strange to me: maybe a little comment should be added.

From the datasheet, In at91sam9x5 and sama5d3x, the startup time mask
is become 0xf. In meanwhile, the startup time calculation method is also changed.

Before at91sam9x5, Startup Time = (STARTUP+1) * 8/ADCCLK

Since at91sam9x5, Startup Time = <find the following lookup table>
0 SUT0 0 periods of ADCClock
1 SUT8 8 periods of ADCClock
2 SUT16 16 periods of ADCClock
3 SUT24 24 periods of ADCClock
4 SUT64 64 periods of ADCClock
5 SUT80 80 periods of ADCClock
... ...
14 SUT896 896 periods of ADCClock
15 SUT960 960 periods of ADCClock

so only 4bits, it still can define < 960's ADC clock.

In my 3/5 patch, it implement this calculation method.

Quoted 24 lines
Quoted 24 lines
arch/arm/mach-at91/include/mach/at91_adc.h +2 -0
+#define        AT91_ADC_STARTUP_9260 (0x1f << 16)
+#define        AT91_ADC_STARTUP_9G45    (0x7f << 16)
  #define            AT91_ADC_STARTUP_(x)    ((x) << 16)
  #define        AT91_ADC_SHTIM        (0xf  << 24)    /* Sample & 

Hold Time */
#define AT91_ADC_SHTIM_(x) ((x) << 24)

arch/arm/mach-at91/include/mach/at91_adc.h +0 -0
@@ -58,4 +61,6 @@
  #define AT91_ADC_CHR(n)        (0x30 + ((n) * 4))    /* Channel 

Data Register N */
#define AT91_ADC_DATA (0x3ff)

arch/arm/mach-at91/include/mach/at91_adc.h +2 -0
+#define AT91_ADC_VERSION    0xFC
+
  #endif
drivers/iio/adc/at91_adc.c +0 -0
index 18bd54f..14e99ba 100644
@@ -39,6 +39,12 @@
  #define at91_adc_writel(st, reg, val) \
      (writel_relaxed(val, st->reg_base + reg))
 
Quoted 9 lines

drivers/iio/adc/at91_adc.c line 47

Quoted 6 lines
drivers/iio/adc/at91_adc.c +6 -0
+struct at91_adc_caps {
+    bool    has_tsmr;    /* only at91sam9x5, sama5d3 have TSMR reg */
+    u32    mr_prescal_mask;
+    u32    mr_startup_mask;
+};
+

If we move to DT and compatible string for these values, maybe a separate structure won't be needed....

yes, if we move the mask as DT property.
But another way is define above structure in driver. Driver will load the correct mask by checking the compatible string. In this way, we don't exposure all IP details out to DT.
I prefer the second way in the moment.

By answer this question, I just had a second thought of the ADC compatible implementation:
For touch ADC, there should have 3 catalogs:
9260/9g20: not support touch.
9g45: support touch, but no average, no TSMR. Need a software filter 9x5, sama5: support touch, with average, TSMR. Sample data function
changed a lot from 9g45.

Quoted 35 lines
Quoted 35 lines
drivers/iio/adc/at91_adc.c +0 -0
  struct at91_adc_state {
      struct clk        *adc_clk;
      u16            *buffer;
@@ -62,6 +68,7 @@ struct at91_adc_state {
      u32            res;        /* resolution used for convertions */
      bool            low_res;    /* the resolution corresponds to 

the lowest one */

      wait_queue_head_t    wq_data_avail;
drivers/iio/adc/at91_adc.c +3 -0
+    struct at91_adc_caps    caps;
  };
 
  static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
@@ -580,6 +587,41 @@ static const struct iio_info at91_adc_info = {
      .read_raw = &at91_adc_read_raw,
  };
 
+/*
+ * Since atmel adc support different ip for touchscreen mode. 

Through the
+ * IP check, we will know the touchscreen capbilities.
+ */

drivers/iio/adc/at91_adc.c +12 -0
+static void atmel_adc_get_cap(struct at91_adc_state *st)
+{
+    unsigned int version;
+    struct iio_dev *idev = iio_priv_to_dev(st);
+
+    version = at91_adc_readl(st, AT91_ADC_VERSION);
+    dev_dbg(&idev->dev, "version: 0x%x\n", version);
+
+    st->caps.mr_prescal_mask = AT91_ADC_PRESCAL_9260;
+    st->caps.mr_startup_mask = AT91_ADC_STARTUP_9260;
+
+    /* keep only major version number */
Quoted 8 lines

drivers/iio/adc/at91_adc.c static const struct iio_info at91_adc_info = { · line 610

Quoted 5 lines
drivers/iio/adc/at91_adc.c +5 -0
+    switch (version & 0xf00) {
+    case 0x500:    /* SAMA5D3 */
+    case 0x400:    /* AT91SAM9X5/9N12 */
+        st->caps.has_tsmr = 1;
+        st->caps.mr_startup_mask = AT91_ADC_STARTUP;

Here is where AT91_ADC_STARTUP may need another name: what about AT91_ADC_STARTUP_9x5 to match the "compatibility" of this value?

ok.

Quoted 9 lines

drivers/iio/adc/at91_adc.c static const struct iio_info at91_adc_info = { · line 614

Quoted 6 lines
drivers/iio/adc/at91_adc.c +4 -0
+    case 0x200:    /* AT91SAM9M10/9G45 */
+        st->caps.mr_prescal_mask = AT91_ADC_PRESCAL;
+
+        if ((version & 0xf00) == 0x200)

Sorry but I do not understand this test in a switch/case entry where it should always be true...

Since the switch case is fall through, that means the higher IP (at91sam9x5, sama5) will also running this check, but this line only for 9G45 chip.

a little bit hard to read :)

Quoted 30 lines
Quoted 30 lines
drivers/iio/adc/at91_adc.c +12 -0
+            st->caps.mr_startup_mask = AT91_ADC_STARTUP_9G45;
+    case 0x100:    /* AT91SAM9260/9G20 */
+        break;
+    default:
+        dev_warn(&idev->dev,
+                "Unmanaged adc version, use minimal capabilities\n");
+        break;
+    };
+}
+
  static int at91_adc_probe(struct platform_device *pdev)
  {
      unsigned int prsc, mstrclk, ticks, adc_clk, adc_clk_khz, shtim;
@@ -645,6 +687,8 @@ static int at91_adc_probe(struct platform_device 
 *pdev)
          goto error_free_device;
      }
 
+    atmel_adc_get_cap(st);
+
      st->clk = devm_clk_get(&pdev->dev, "adc_clk");
      if (IS_ERR(st->clk)) {
          dev_err(&pdev->dev, "Failed to get the clock.\n");
drivers/iio/adc/at91_adc.c +0 -0
@@ -704,8 +748,8 @@ static int at91_adc_probe(struct platform_device 
 *pdev)
      shtim = round_up((st->sample_hold_time * adc_clk_khz /
                1000) - 1, 1);
 
Quoted 10 lines
Quoted 8 lines
drivers/iio/adc/at91_adc.c +2 -2
-    reg = AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL;
-    reg |= AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP;
+    reg = AT91_ADC_PRESCAL_(prsc) & st->caps.mr_prescal_mask;
+    reg |= AT91_ADC_STARTUP_(ticks) & st->caps.mr_startup_mask;
      if (st->low_res)
          reg |= AT91_ADC_LOWRES;
      if (st->sleep_mode)

Thanks again.
Best Regards,
Josh Wu

maintainer
#18 lore raw

PATCH 2/5 iio: at91: Use different prescal, startup mask in MR for different IP

Quoted 148 lines · Josh Wu

Hi, Nicolas

Thanks for your review.

Quoted 21 lines · Nicolas Ferre

On 14/07/2013 10:04, Josh Wu :

Quoted 20 lines

For at91 boards, there are different IPs for adc. Different IPs has different STARTUP & PRESCAL mask in ADC_MR.

This patch can change the masks according to the different IP version.

---
  arch/arm/mach-at91/include/mach/at91_adc.h |    9 ++++--
  drivers/iio/adc/at91_adc.c                 |   48 ++++++++++++++++++++++++++--
  2 files changed, 53 insertions(+), 4 deletions(-)
arch/arm/mach-at91/include/mach/at91_adc.h +0 -1
index 8e7ed5c..ab273ee 100644
@@ -28,9 +28,12 @@
  #define            AT91_ADC_TRGSEL_EXTERNAL    (6 << 1)
  #define        AT91_ADC_LOWRES        (1 << 4)    /* Low Resolution */
  #define        AT91_ADC_SLEEP        (1 << 5)    /* Sleep Mode */
-#define        AT91_ADC_PRESCAL    (0x3f << 8)    /* Prescalar Rate Selection */
Quoted 8 lines

arch/arm/mach-at91/include/mach/at91_adc.h line 34

Quoted 5 lines
arch/arm/mach-at91/include/mach/at91_adc.h +3 -1
+#define        AT91_ADC_PRESCAL    (0xff << 8)    /* Prescalar Rate Selection */
+#define        AT91_ADC_PRESCAL_9260    (0x3f << 8)
  #define            AT91_ADC_PRESCAL_(x)    ((x) << 8)
-#define        AT91_ADC_STARTUP    (0x1f << 16)    /* Startup Up Time */
+#define        AT91_ADC_STARTUP    (0xf << 16)    /* Startup Up Time */

Which product has this kind of startup mask: 0xf? Compared with 9260 and 9g45 values, it seems strange to me: maybe a little comment should be added.

From the datasheet, In at91sam9x5 and sama5d3x, the startup time mask is become 0xf. In meanwhile, the startup time calculation method is also changed.

Before at91sam9x5, Startup Time = (STARTUP+1) * 8/ADCCLK

Since at91sam9x5, Startup Time = <find the following lookup table>
0 SUT0 0 periods of ADCClock
1 SUT8 8 periods of ADCClock
2 SUT16 16 periods of ADCClock
3 SUT24 24 periods of ADCClock
4 SUT64 64 periods of ADCClock
5 SUT80 80 periods of ADCClock
... ...
14 SUT896 896 periods of ADCClock
15 SUT960 960 periods of ADCClock

so only 4bits, it still can define < 960's ADC clock.

In my 3/5 patch, it implement this calculation method.

Quoted 22 lines
Quoted 22 lines
arch/arm/mach-at91/include/mach/at91_adc.h +4 -0
+#define        AT91_ADC_STARTUP_9260 (0x1f << 16)
+#define        AT91_ADC_STARTUP_9G45    (0x7f << 16)
  #define            AT91_ADC_STARTUP_(x)    ((x) << 16)
  #define        AT91_ADC_SHTIM        (0xf  << 24)    /* Sample & Hold Time */
  #define            AT91_ADC_SHTIM_(x)    ((x) << 24)
@@ -58,4 +61,6 @@
  #define AT91_ADC_CHR(n)        (0x30 + ((n) * 4))    /* Channel Data Register N */
  #define        AT91_ADC_DATA        (0x3ff)
 
+#define AT91_ADC_VERSION    0xFC
+
  #endif
drivers/iio/adc/at91_adc.c +0 -0
index 18bd54f..14e99ba 100644
@@ -39,6 +39,12 @@
  #define at91_adc_writel(st, reg, val) \
      (writel_relaxed(val, st->reg_base + reg))
 
Quoted 8 lines

drivers/iio/adc/at91_adc.c line 47

Quoted 6 lines
drivers/iio/adc/at91_adc.c +6 -0
+struct at91_adc_caps {
+    bool    has_tsmr;    /* only at91sam9x5, sama5d3 have TSMR reg */
+    u32    mr_prescal_mask;
+    u32    mr_startup_mask;
+};
+

If we move to DT and compatible string for these values, maybe a separate structure won't be needed....

yes, if we move the mask as DT property.
But another way is define above structure in driver. Driver will load the correct mask by checking the compatible string. In this way, we don't exposure all IP details out to DT.
I prefer the second way in the moment.

By answer this question, I just had a second thought of the ADC compatible implementation:
For touch ADC, there should have 3 catalogs:
9260/9g20: not support touch.
9g45: support touch, but no average, no TSMR. Need a software filter
9x5, sama5: support touch, with average, TSMR. Sample data function changed a lot from 9g45.

Quoted 33 lines
Quoted 33 lines
drivers/iio/adc/at91_adc.c +17 -0
  struct at91_adc_state {
      struct clk        *adc_clk;
      u16            *buffer;
@@ -62,6 +68,7 @@ struct at91_adc_state {
      u32            res;        /* resolution used for convertions */
      bool            low_res;    /* the resolution corresponds to the lowest one */
      wait_queue_head_t    wq_data_avail;
+    struct at91_adc_caps    caps;
  };
 
  static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
@@ -580,6 +587,41 @@ static const struct iio_info at91_adc_info = {
      .read_raw = &at91_adc_read_raw,
  };
 
+/*
+ * Since atmel adc support different ip for touchscreen mode. Through the
+ * IP check, we will know the touchscreen capbilities.
+ */
+static void atmel_adc_get_cap(struct at91_adc_state *st)
+{
+    unsigned int version;
+    struct iio_dev *idev = iio_priv_to_dev(st);
+
+    version = at91_adc_readl(st, AT91_ADC_VERSION);
+    dev_dbg(&idev->dev, "version: 0x%x\n", version);
+
+    st->caps.mr_prescal_mask = AT91_ADC_PRESCAL_9260;
+    st->caps.mr_startup_mask = AT91_ADC_STARTUP_9260;
+
+    /* keep only major version number */
Quoted 8 lines

drivers/iio/adc/at91_adc.c static const struct iio_info at91_adc_info = { · line 610

Quoted 5 lines
drivers/iio/adc/at91_adc.c +5 -0
+    switch (version & 0xf00) {
+    case 0x500:    /* SAMA5D3 */
+    case 0x400:    /* AT91SAM9X5/9N12 */
+        st->caps.has_tsmr = 1;
+        st->caps.mr_startup_mask = AT91_ADC_STARTUP;

Here is where AT91_ADC_STARTUP may need another name: what about AT91_ADC_STARTUP_9x5 to match the "compatibility" of this value?

ok.

Quoted 7 lines
Quoted 6 lines
drivers/iio/adc/at91_adc.c +4 -0
+    case 0x200:    /* AT91SAM9M10/9G45 */
+        st->caps.mr_prescal_mask = AT91_ADC_PRESCAL;
+
+        if ((version & 0xf00) == 0x200)
Quoted 6 lines
Quoted 1 line

Sorry but I do not understand this test in a switch/case entry where it should always be true...

Since the switch case is fall through, that means the higher IP (at91sam9x5, sama5) will also running this check, but this line only for 9G45 chip.

a little bit hard to read :)

Indeed. Write the case statements out long hand without the full throughs. It'll take a couple more lines but be much easer to read.

Quoted 47 lines
Quoted 29 lines
Quoted 29 lines
drivers/iio/adc/at91_adc.c +12 -0
+            st->caps.mr_startup_mask = AT91_ADC_STARTUP_9G45;
+    case 0x100:    /* AT91SAM9260/9G20 */
+        break;
+    default:
+        dev_warn(&idev->dev,
+                "Unmanaged adc version, use minimal capabilities\n");
+        break;
+    };
+}
+
  static int at91_adc_probe(struct platform_device *pdev)
  {
      unsigned int prsc, mstrclk, ticks, adc_clk, adc_clk_khz, shtim;
@@ -645,6 +687,8 @@ static int at91_adc_probe(struct platform_device *pdev)
          goto error_free_device;
      }
 
+    atmel_adc_get_cap(st);
+
      st->clk = devm_clk_get(&pdev->dev, "adc_clk");
      if (IS_ERR(st->clk)) {
          dev_err(&pdev->dev, "Failed to get the clock.\n");
@@ -704,8 +748,8 @@ static int at91_adc_probe(struct platform_device *pdev)
      shtim = round_up((st->sample_hold_time * adc_clk_khz /
                1000) - 1, 1);
 
Quoted 10 lines

drivers/iio/adc/at91_adc.c static int at91_adc_probe(struct platform_device *pdev) · line 755

Quoted 8 lines
drivers/iio/adc/at91_adc.c +2 -2
-    reg = AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL;
-    reg |= AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP;
+    reg = AT91_ADC_PRESCAL_(prsc) & st->caps.mr_prescal_mask;
+    reg |= AT91_ADC_STARTUP_(ticks) & st->caps.mr_startup_mask;
      if (st->low_res)
          reg |= AT91_ADC_LOWRES;
      if (st->sleep_mode)

Thanks again.
Best Regards,
Josh Wu

Signature · 3 lines
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
author
#19 lore raw

PATCH 3/5 iio: at91: ADC start-up time calculation changed since at91sam9x5

In reply to Josh Wu

Since in at91sam9x5, sama5d3x chip. the start up time calucation is changed. And these two chips also have TSMR, so we check whether ADC has TSMR, then choose different start up time calculation formula.

---
 drivers/iio/adc/at91_adc.c |   40 +++++++++++++++++++++++++++++++++-------
 1 file changed, 33 insertions(+), 7 deletions(-)
drivers/iio/adc/at91_adc.c +33 -7
index 14e99ba..e93a075 100644
@@ -733,13 +733,39 @@ static int at91_adc_probe(struct platform_device *pdev)
 		goto error_disable_adc_clk;
 	}
 
-	/*
-	 * Number of ticks needed to cover the startup time of the ADC as
-	 * defined in the electrical characteristics of the board, divided by 8.
-	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
-	 */
-	ticks = round_up((st->startup_time * adc_clk_khz /
-			  1000) - 1, 8) / 8;
+	if (!st->caps.has_tsmr) {
+		/*
+		 * Number of ticks needed to cover the startup time of the ADC
+		 * as defined in the electrical characteristics of the board,
+		 * divided by 8. The formula thus is :
+		 *   Startup Time = (ticks + 1) * 8 / ADC Clock
+		 */
+		ticks = round_up((st->startup_time * adc_clk_khz /
+				  1000) - 1, 8) / 8;
+	} else {
+		/*
+		 * For sama5d3x and at91sam9x5, the formula changes to:
+		 * Startup Time = <lookup_table_value> / ADC Clock
+		 */
+		const int startup_lookup[] = {
+			0  , 8  , 16 , 24 ,
+			64 , 80 , 96 , 112,
+			512, 576, 640, 704,
+			768, 832, 896, 960
+			};
+
+		int i, size = ARRAY_SIZE(startup_lookup);
+		ticks = st->startup_time * adc_clk_khz / 1000;
+		for (i = 0; i < size; i++)
+			if (ticks < startup_lookup[i])
+				break;
+
+		ticks = i;
+		if (ticks == size)
+			/* Reach the end of lookup table */
+			ticks = size - 1;
+	}
+
 	/*
 	 * a minimal Sample and Hold Time is necessary for the ADC to guarantee
 	 * the best converted final value between two channels selection
Signature · 1 line
1.7.10
maintainer
#20 lore raw

PATCH 3/5 iio: at91: ADC start-up time calculation changed since at91sam9x5

Quoted 3 lines · Josh Wu

Since in at91sam9x5, sama5d3x chip. the start up time calucation is changed. And these two chips also have TSMR, so we check whether ADC has TSMR, then choose different start up time calculation formula.

If this doesn't have anything directly to do with the TSMR and this is just coincidence, then do this separately with it's own flag as it will be easier to maintain in the long run.

Jonathan

Quoted 58 lines
---
 drivers/iio/adc/at91_adc.c |   40 +++++++++++++++++++++++++++++++++-------
 1 file changed, 33 insertions(+), 7 deletions(-)
drivers/iio/adc/at91_adc.c +33 -7
index 14e99ba..e93a075 100644
@@ -733,13 +733,39 @@ static int at91_adc_probe(struct platform_device *pdev)
 		goto error_disable_adc_clk;
 	}
 
-	/*
-	 * Number of ticks needed to cover the startup time of the ADC as
-	 * defined in the electrical characteristics of the board, divided by 8.
-	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
-	 */
-	ticks = round_up((st->startup_time * adc_clk_khz /
-			  1000) - 1, 8) / 8;
+	if (!st->caps.has_tsmr) {
+		/*
+		 * Number of ticks needed to cover the startup time of the ADC
+		 * as defined in the electrical characteristics of the board,
+		 * divided by 8. The formula thus is :
+		 *   Startup Time = (ticks + 1) * 8 / ADC Clock
+		 */
+		ticks = round_up((st->startup_time * adc_clk_khz /
+				  1000) - 1, 8) / 8;
+	} else {
+		/*
+		 * For sama5d3x and at91sam9x5, the formula changes to:
+		 * Startup Time = <lookup_table_value> / ADC Clock
+		 */
+		const int startup_lookup[] = {
+			0  , 8  , 16 , 24 ,
+			64 , 80 , 96 , 112,
+			512, 576, 640, 704,
+			768, 832, 896, 960
+			};
+
+		int i, size = ARRAY_SIZE(startup_lookup);
+		ticks = st->startup_time * adc_clk_khz / 1000;
+		for (i = 0; i < size; i++)
+			if (ticks < startup_lookup[i])
+				break;
+
+		ticks = i;
+		if (ticks == size)
+			/* Reach the end of lookup table */
+			ticks = size - 1;
+	}
+
 	/*
 	 * a minimal Sample and Hold Time is necessary for the ADC to guarantee
 	 * the best converted final value between two channels selection
author
#21 lore raw

PATCH 3/5 iio: at91: ADC start-up time calculation changed since at91sam9x5

Quoted 7 lines · Jonathan Cameron
Quoted 3 lines · Josh Wu

Since in at91sam9x5, sama5d3x chip. the start up time calucation is changed. And these two chips also have TSMR, so we check whether ADC has TSMR, then choose different start up time calculation formula.

If this doesn't have anything directly to do with the TSMR and this is just coincidence, then do this separately with it's own flag as it will be easier to maintain in the long run.

Agreed. I will fix it in next version.

Best Regards,
Josh Wu

Quoted 59 lines

Jonathan

Quoted 57 lines
---
  drivers/iio/adc/at91_adc.c |   40 +++++++++++++++++++++++++++++++++-------
  1 file changed, 33 insertions(+), 7 deletions(-)
drivers/iio/adc/at91_adc.c +33 -7
index 14e99ba..e93a075 100644
@@ -733,13 +733,39 @@ static int at91_adc_probe(struct platform_device *pdev)
  		goto error_disable_adc_clk;
  	}
 
-	/*
-	 * Number of ticks needed to cover the startup time of the ADC as
-	 * defined in the electrical characteristics of the board, divided by 8.
-	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
-	 */
-	ticks = round_up((st->startup_time * adc_clk_khz /
-			  1000) - 1, 8) / 8;
+	if (!st->caps.has_tsmr) {
+		/*
+		 * Number of ticks needed to cover the startup time of the ADC
+		 * as defined in the electrical characteristics of the board,
+		 * divided by 8. The formula thus is :
+		 *   Startup Time = (ticks + 1) * 8 / ADC Clock
+		 */
+		ticks = round_up((st->startup_time * adc_clk_khz /
+				  1000) - 1, 8) / 8;
+	} else {
+		/*
+		 * For sama5d3x and at91sam9x5, the formula changes to:
+		 * Startup Time = <lookup_table_value> / ADC Clock
+		 */
+		const int startup_lookup[] = {
+			0  , 8  , 16 , 24 ,
+			64 , 80 , 96 , 112,
+			512, 576, 640, 704,
+			768, 832, 896, 960
+			};
+
+		int i, size = ARRAY_SIZE(startup_lookup);
+		ticks = st->startup_time * adc_clk_khz / 1000;
+		for (i = 0; i < size; i++)
+			if (ticks < startup_lookup[i])
+				break;
+
+		ticks = i;
+		if (ticks == size)
+			/* Reach the end of lookup table */
+			ticks = size - 1;
+	}
+
  	/*
  	 * a minimal Sample and Hold Time is necessary for the ADC to guarantee
  	 * the best converted final value between two channels selection
author
#22 lore raw

PATCH 4/5 iio: at91: add an optional dt property for for adc clock hz.

In reply to Josh Wu
---
 Documentation/devicetree/bindings/arm/atmel-adc.txt |    2 ++
 drivers/iio/adc/at91_adc.c                          |    8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
index 16769d9..0db2945 100644
@@ -27,6 +27,8 @@ Optional properties:
 		       resolution will be used.
   - atmel,adc-sleep-mode: Boolean to enable sleep mode when no conversion
   - atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
+  - atmel,adc-clock-rate: ADC clock rate. If not specified, use the default
+			  adc_op_clk.
  
 Optional trigger Nodes:
   - Required properties:
drivers/iio/adc/at91_adc.c +7 -1
index e93a075..8f1386f 100644
@@ -47,6 +47,7 @@ struct at91_adc_caps {
 
 struct at91_adc_state {
 	struct clk		*adc_clk;
+	u32			adc_clk_rate;
 	u16			*buffer;
 	unsigned long		channels_mask;
 	struct clk		*clk;
@@ -448,6 +449,10 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
 	if (!node)
 		return -EINVAL;
 
+	prop = 0;
+	of_property_read_u32(node, "atmel,adc-clock-rate", &prop);
+	st->adc_clk_rate = prop;
+
 	st->use_external = of_property_read_bool(node, "atmel,adc-use-external-triggers");
 
 	if (of_property_read_u32(node, "atmel,adc-channels-used", &prop)) {
@@ -723,7 +728,8 @@ static int at91_adc_probe(struct platform_device *pdev)
 	 * specified by the electrical characteristics of the board.
 	 */
 	mstrclk = clk_get_rate(st->clk);
-	adc_clk = clk_get_rate(st->adc_clk);
+	adc_clk = st->adc_clk_rate ?
+		st->adc_clk_rate : clk_get_rate(st->adc_clk);
 	adc_clk_khz = adc_clk / 1000;
 	prsc = (mstrclk / (2 * adc_clk)) - 1;
 
Signature · 1 line
1.7.10
#23 lore raw

PATCH 4/5 iio: at91: add an optional dt property for for adc clock hz.

Hi Josh,

Quoted 43 lines · Josh Wu
---
 Documentation/devicetree/bindings/arm/atmel-adc.txt |    2 ++
 drivers/iio/adc/at91_adc.c                          |    8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
index 16769d9..0db2945 100644
@@ -27,6 +27,8 @@ Optional properties:
 		       resolution will be used.
   - atmel,adc-sleep-mode: Boolean to enable sleep mode when no conversion
   - atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
+  - atmel,adc-clock-rate: ADC clock rate. If not specified, use the default
+			  adc_op_clk.
 
 Optional trigger Nodes:
   - Required properties:
drivers/iio/adc/at91_adc.c +5 -0
index e93a075..8f1386f 100644
@@ -47,6 +47,7 @@ struct at91_adc_caps {
 
 struct at91_adc_state {
 	struct clk		*adc_clk;
+	u32			adc_clk_rate;
 	u16			*buffer;
 	unsigned long		channels_mask;
 	struct clk		*clk;
@@ -448,6 +449,10 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
 	if (!node)
 		return -EINVAL;
 
+	prop = 0;
+	of_property_read_u32(node, "atmel,adc-clock-rate", &prop);
+	st->adc_clk_rate = prop;
+
 	st->use_external = of_property_read_bool(node, "atmel,adc-use-external-triggers");
 
 	if (of_property_read_u32(node, "atmel,adc-channels-used", &prop)) {

drivers/iio/adc/at91_adc.c static int at91_adc_probe(struct platform_device *pdev) · line 732

Quoted 6 lines
drivers/iio/adc/at91_adc.c +2 -1
@@ -723,7 +728,8 @@ static int at91_adc_probe(struct platform_device *pdev)
 	 * specified by the electrical characteristics of the board.
 	 */
 	mstrclk = clk_get_rate(st->clk);
-	adc_clk = clk_get_rate(st->adc_clk);
+	adc_clk = st->adc_clk_rate ?
+		st->adc_clk_rate : clk_get_rate(st->adc_clk);

Why is that needed? Isn't it completely redundant with the clocks property?

Maxime

Signature · 10 lines
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130715/bc33e590/attachment-0001.sig>
author
#24 lore raw

PATCH 4/5 iio: at91: add an optional dt property for for adc clock hz.

Hi, Maxime

Quoted 44 lines · Maxime Ripard

Hi Josh,

Quoted 41 lines · Josh Wu
---
  Documentation/devicetree/bindings/arm/atmel-adc.txt |    2 ++
  drivers/iio/adc/at91_adc.c                          |    8 +++++++-
  2 files changed, 9 insertions(+), 1 deletion(-)
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
index 16769d9..0db2945 100644
@@ -27,6 +27,8 @@ Optional properties:
  		       resolution will be used.
    - atmel,adc-sleep-mode: Boolean to enable sleep mode when no conversion
    - atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
+  - atmel,adc-clock-rate: ADC clock rate. If not specified, use the default
+			  adc_op_clk.
 
  Optional trigger Nodes:
    - Required properties:
drivers/iio/adc/at91_adc.c +5 -0
index e93a075..8f1386f 100644
@@ -47,6 +47,7 @@ struct at91_adc_caps {
 
  struct at91_adc_state {
  	struct clk		*adc_clk;
+	u32			adc_clk_rate;
  	u16			*buffer;
  	unsigned long		channels_mask;
  	struct clk		*clk;
@@ -448,6 +449,10 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
  	if (!node)
  		return -EINVAL;
 
+	prop = 0;
+	of_property_read_u32(node, "atmel,adc-clock-rate", &prop);
+	st->adc_clk_rate = prop;
+
  	st->use_external = of_property_read_bool(node, "atmel,adc-use-external-triggers");
 
Quoted 10 lines

drivers/iio/adc/at91_adc.c static int at91_adc_probe(struct platform_device *pdev) · line 732

Quoted 8 lines
drivers/iio/adc/at91_adc.c +2 -1
  	if (of_property_read_u32(node, "atmel,adc-channels-used", &prop)) {
@@ -723,7 +728,8 @@ static int at91_adc_probe(struct platform_device *pdev)
  	 * specified by the electrical characteristics of the board.
  	 */
  	mstrclk = clk_get_rate(st->clk);
-	adc_clk = clk_get_rate(st->adc_clk);
+	adc_clk = st->adc_clk_rate ?
+		st->adc_clk_rate : clk_get_rate(st->adc_clk);

Why is that needed? Isn't it completely redundant with the clocks property?

As st->adc_clk rate is specified in arch/arm/mach-at91/sama5d3.c (take sama5d3 for example), changing the clock rate should recompile the kernel binary.
Use dt parameter will let us easily specify the clock rate instead of recompile the code.

And yes, it is redundant that we can define the adc_op_clk rate in two places (clock property in .c and adc-clock-rate in dts). But this can be compatible with the non-dt platform.

After a further thinking of this, maybe remove the adc_op_clk is better since it is a fake clock, and only used to specify the clock rate. To specify the clock rate use a dt property or platform data parameter is better.

Quoted 4 lines

Maxime

Best Regards,
Josh Wu

#25 lore raw

PATCH 4/5 iio: at91: add an optional dt property for for adc clock hz.

Quoted 37 lines · Josh Wu
Quoted 22 lines · Josh Wu
Quoted 21 lines
drivers/iio/adc/at91_adc.c +5 -0
index e93a075..8f1386f 100644
@@ -47,6 +47,7 @@ struct at91_adc_caps {
 struct at91_adc_state {
 	struct clk		*adc_clk;
+	u32			adc_clk_rate;
 	u16			*buffer;
 	unsigned long		channels_mask;
 	struct clk		*clk;
@@ -448,6 +449,10 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
 	if (!node)
 		return -EINVAL;
+	prop = 0;
+	of_property_read_u32(node, "atmel,adc-clock-rate", &prop);
+	st->adc_clk_rate = prop;
+
 	st->use_external = of_property_read_bool(node, "atmel,adc-use-external-triggers");
 	if (of_property_read_u32(node, "atmel,adc-channels-used", &prop)) {
@@ -723,7 +728,8 @@ static int at91_adc_probe(struct platform_device *pdev)
Quoted 8 lines

drivers/iio/adc/at91_adc.c static int at91_adc_probe(struct platform_device *pdev) · line 732

Quoted 6 lines
drivers/iio/adc/at91_adc.c +2 -1
 	 * specified by the electrical characteristics of the board.
 	 */
 	mstrclk = clk_get_rate(st->clk);
-	adc_clk = clk_get_rate(st->adc_clk);
+	adc_clk = st->adc_clk_rate ?
+		st->adc_clk_rate : clk_get_rate(st->adc_clk);

Why is that needed? Isn't it completely redundant with the clocks property?

As st->adc_clk rate is specified in arch/arm/mach-at91/sama5d3.c (take sama5d3 for example), changing the clock rate should recompile the kernel binary.
Use dt parameter will let us easily specify the clock rate instead of recompile the code.

Quoted 3 lines

And yes, it is redundant that we can define the adc_op_clk rate in two places (clock property in .c and adc-clock-rate in dts). But this can be compatible with the non-dt platform.

Yet, it's not, while, like you pointed at, the common clock framework actually *is* usable for both DT and non-DT platforms at no cost.

The fact that AT91 isn't using the DT to retrieve its clock tree yet is another story (but I believe that it's a work in progress).

Quoted 5 lines

After a further thinking of this, maybe remove the adc_op_clk is better since it is a fake clock, and only used to specify the clock rate.
To specify the clock rate use a dt property or platform data parameter is better.

No, to specify *any* clock, the common clock framework is the better solution.

Maxime

Signature · 10 lines
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130716/4fd79342/attachment-0001.sig>
#26 lore raw

PATCH 4/5 iio: at91: add an optional dt property for for adc clock hz.

On 07/16/2013 12:30 PM, Maxime Ripard wrote:
[...]

Quoted 8 lines
Quoted 5 lines

After a further thinking of this, maybe remove the adc_op_clk is better since it is a fake clock, and only used to specify the clock rate.
To specify the clock rate use a dt property or platform data parameter is better.

No, to specify *any* clock, the common clock framework is the better solution.

Yep, this patch is not the right approach. It's trying to work around the limitations of the platforms clock API implementation. Please fix the at91 clock implementation instead (e.g. by switching to the common clock framework).

- Lars

author
#27 lore raw

PATCH 4/5 iio: at91: add an optional dt property for for adc clock hz.

Quoted 7 lines · Lars-Peter Clausen

On 07/16/2013 12:30 PM, Maxime Ripard wrote:
[...]

Quoted 5 lines
Quoted 5 lines

After a further thinking of this, maybe remove the adc_op_clk is better since it is a fake clock, and only used to specify the clock rate.
To specify the clock rate use a dt property or platform data parameter is better.

Quoted 5 lines
Quoted 2 lines

No, to specify *any* clock, the common clock framework is the better solution.

Yep, this patch is not the right approach. It's trying to work around the limitations of the platforms clock API implementation. Please fix the at91 clock implementation instead (e.g. by switching to the common clock framework).

Thank you and Maxime for the clarify. I will drop this patch in next version.
Just FYI. The at91 clock common framework is in implementation by Boris Brezillion.

Quoted 3 lines

- Lars

Best Regards,
Josh Wu

#28 lore raw

PATCH 4/5 iio: at91: add an optional dt property for for adc clock hz.

In reply to Josh Wu

Hi Josh,

Quoted 44 lines · Josh Wu
---
  Documentation/devicetree/bindings/arm/atmel-adc.txt |    2 ++
  drivers/iio/adc/at91_adc.c                          |    8 +++++++-
  2 files changed, 9 insertions(+), 1 deletion(-)
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
index 16769d9..0db2945 100644
@@ -27,6 +27,8 @@ Optional properties:
  		       resolution will be used.
    - atmel,adc-sleep-mode: Boolean to enable sleep mode when no conversion
    - atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
+  - atmel,adc-clock-rate: ADC clock rate. If not specified, use the default
+			  adc_op_clk.
 
  Optional trigger Nodes:
    - Required properties:
drivers/iio/adc/at91_adc.c +5 -0
index e93a075..8f1386f 100644
@@ -47,6 +47,7 @@ struct at91_adc_caps {
 
  struct at91_adc_state {
  	struct clk		*adc_clk;
+	u32			adc_clk_rate;
  	u16			*buffer;
  	unsigned long		channels_mask;
  	struct clk		*clk;
@@ -448,6 +449,10 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
  	if (!node)
  		return -EINVAL;
 
+	prop = 0;
+	of_property_read_u32(node, "atmel,adc-clock-rate", &prop);
+	st->adc_clk_rate = prop;
+
  	st->use_external = of_property_read_bool(node, "atmel,adc-use-external-triggers");
 
  	if (of_property_read_u32(node, "atmel,adc-channels-used", &prop)) {

drivers/iio/adc/at91_adc.c static int at91_adc_probe(struct platform_device *pdev) · line 735

Quoted 9 lines
drivers/iio/adc/at91_adc.c +2 -1
@@ -723,7 +728,8 @@ static int at91_adc_probe(struct platform_device *pdev)
  	 * specified by the electrical characteristics of the board.
  	 */
  	mstrclk = clk_get_rate(st->clk);
-	adc_clk = clk_get_rate(st->adc_clk);
+	adc_clk = st->adc_clk_rate ?
+		st->adc_clk_rate : clk_get_rate(st->adc_clk);
  	adc_clk_khz = adc_clk / 1000;
  	prsc = (mstrclk / (2 * adc_clk)) - 1;
 

As said by Maxime and Lars-Peter, I think this should be handled by a proper clock implementation (adc_clock ?) using common clock framework.

IMO the fake adc_op_clk should not exist. Instead the adc clock binding should define properties describing the adc clock characteristics (see pll implementation in at91 common clk series).
These characteristics can be found in 'ADC characteristics' chapter in atmel's datasheets.

The adc clock binding would look like this (based on sama5 datasheet):

adc_clk {
	compatible = "sama5d3-adc-clk";
	output = <1000000 20000000>; /* output clock frequency range */
};

It is up to the clk user (adc driver) to choose the appropriate frequency to use (maximum rate ?) and configure it with clk_set_rate function.
The adc clk implem will take care of PRESCAL field config in ADC_MR according to parent rate (mck rate) and requested rate. It will also check if the requested rate is in the output clk range.

These are just thought, feel free to comment it.

Best Regards,

Boris

#29 lore raw

PATCH 4/5 iio: at91: add an optional dt property for for adc clock hz.

Quoted 72 lines · boris brezillon

Hi Josh,

Quoted 50 lines · Josh Wu
---
  Documentation/devicetree/bindings/arm/atmel-adc.txt |    2 ++
  drivers/iio/adc/at91_adc.c                          |    8 +++++++-
  2 files changed, 9 insertions(+), 1 deletion(-)
Documentation/devicetree/bindings/arm/atmel-adc.txt +0 -0
index 16769d9..0db2945 100644
@@ -27,6 +27,8 @@ Optional properties:
                 resolution will be used.
    - atmel,adc-sleep-mode: Boolean to enable sleep mode when no

conversion
- atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
+ - atmel,adc-clock-rate: ADC clock rate. If not specified, use the default

Documentation/devicetree/bindings/arm/atmel-adc.txt +1 -0
+              adc_op_clk.
 
  Optional trigger Nodes:
    - Required properties:
drivers/iio/adc/at91_adc.c +5 -0
index e93a075..8f1386f 100644
@@ -47,6 +47,7 @@ struct at91_adc_caps {
 
  struct at91_adc_state {
      struct clk        *adc_clk;
+    u32            adc_clk_rate;
      u16            *buffer;
      unsigned long        channels_mask;
      struct clk        *clk;
@@ -448,6 +449,10 @@ static int at91_adc_probe_dt(struct
 at91_adc_state *st,
      if (!node)
          return -EINVAL;
 
+    prop = 0;
+    of_property_read_u32(node, "atmel,adc-clock-rate", &prop);
+    st->adc_clk_rate = prop;
+
      st->use_external = of_property_read_bool(node,
 "atmel,adc-use-external-triggers");
      if (of_property_read_u32(node, "atmel,adc-channels-used", &prop)) {

drivers/iio/adc/at91_adc.c static int at91_adc_probe(struct platform_device · line 735

Quoted 9 lines
drivers/iio/adc/at91_adc.c +2 -1
@@ -723,7 +728,8 @@ static int at91_adc_probe(struct platform_device
 *pdev)
       * specified by the electrical characteristics of the board.
       */
      mstrclk = clk_get_rate(st->clk);
-    adc_clk = clk_get_rate(st->adc_clk);
+    adc_clk = st->adc_clk_rate ?
+        st->adc_clk_rate : clk_get_rate(st->adc_clk);
      adc_clk_khz = adc_clk / 1000;
      prsc = (mstrclk / (2 * adc_clk)) - 1;

As said by Maxime and Lars-Peter, I think this should be handled by a proper clock implementation (adc_clock ?) using common clock framework.

IMO the fake adc_op_clk should not exist. Instead the adc clock binding should define properties describing the adc clock characteristics (see pll implementation in at91 common clk series).
These characteristics can be found in 'ADC characteristics' chapter in atmel's datasheets.

Quoted 6 lines

The adc clock binding would look like this (based on sama5 datasheet):

adc_clk {
     compatible = "sama5d3-adc-clk";
     output = <1000000 20000000>; /* output clock frequency range */
};

or more like this:

adc_clk {
	compatible = "xxx-adc-clk";
	#clock-cells = <0>;
	clocks = <&mck>;
	output = <1000000 20000000>; /* output clock frequency range */
};
Quoted 18 lines

It is up to the clk user (adc driver) to choose the appropriate frequency to use (maximum rate ?) and configure it with clk_set_rate function.
The adc clk implem will take care of PRESCAL field config in ADC_MR according to parent rate (mck rate) and requested rate. It will also check if the requested rate is in the output clk range.

These are just thought, feel free to comment it.

Best Regards,

Boris

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

author
#30 lore raw

PATCH 5/5 iio: at91: introduce touch screen support in iio adc driver

In reply to Josh Wu

AT91 ADC hardware integrate touch screen support. So this patch add touch screen support for at91 adc iio driver.
To enable touch screen support in adc, you need to add the dt parameters:
which type of touch are used? (4 or 5 wires), sample period time, pen detect debounce time, average samples and pen detect resistor.

In the meantime, since touch screen will use a interal period trigger of adc, so it is conflict to other hardware triggers. Driver will disable the hardware trigger support if touch screen is enabled.

This driver has been tested in AT91SAM9X5-EK and SAMA5D3x-EK.

Cc Dmitry Torokhov <[email protected]>
---
 .../devicetree/bindings/arm/atmel-adc.txt          |   13 +
 arch/arm/mach-at91/include/mach/at91_adc.h         |   34 ++
 drivers/iio/adc/at91_adc.c                         |  389 ++++++++++++++++++--
 3 files changed, 412 insertions(+), 24 deletions(-)
Documentation/devicetree/bindings/arm/atmel-adc.txt +13 -0
index 0db2945..925d656 100644
@@ -29,6 +29,19 @@ Optional properties:
   - atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
   - atmel,adc-clock-rate: ADC clock rate. If not specified, use the default
 			  adc_op_clk.
+  - atmel,adc-touchscreen-wires: Number of touch screen wires. Only support
+				 4 and 5 wires touch screen.
+    NOTE: when adc touch screen enabled, the adc hardware trigger will be
+          disabled. Since touch screen will occupied the trigger register.
+  - atmel,adc-ts-pendet-debounce: Debounce time in microsecond for touch pen
+				  detect.
+  - atmel,adc-ts-sample-period-time: Sample Period Time in microsecond for
+				     touch screen
+  - atmel,adc-ts-filter-average: Numbers of sampling data will be averaged.
+    0 means no average. 1 means average two samples. 2 means average four
+    samples. 3 means average eight samples.
+  - atmel,adc-ts-pendet-sensitivity: Pen Detection input pull-up resistor.
+    It can be 0, 1, 2, 3.
  
 Optional trigger Nodes:
   - Required properties:
arch/arm/mach-at91/include/mach/at91_adc.h +34 -0
index ab273ee..6d6cc14 100644
@@ -57,10 +57,44 @@
 #define AT91_ADC_IER		0x24		/* Interrupt Enable Register */
 #define AT91_ADC_IDR		0x28		/* Interrupt Disable Register */
 #define AT91_ADC_IMR		0x2C		/* Interrupt Mask Register */
+#define		AT91_ADC_IER_PEN	(1 << 29)
+#define		AT91_ADC_IER_NOPEN	(1 << 30)
+#define		AT91_ADC_IER_XRDY	(1 << 20)
+#define		AT91_ADC_IER_YRDY	(1 << 21)
+#define		AT91_ADC_IER_PRDY	(1 << 22)
+#define		AT91_ADC_ISR_PENS	(1 << 31)
 
 #define AT91_ADC_CHR(n)		(0x30 + ((n) * 4))	/* Channel Data Register N */
 #define		AT91_ADC_DATA		(0x3ff)
 
+#define AT91_ADC_ACR		0x94	/* Analog Control Register */
+#define		AT91_ADC_ACR_PENDETSENS	(0x3 << 0)	/* pull-up resistor */
+
+#define AT91_ADC_TSMR		0xB0
+#define		AT91_ADC_TSMR_TSMODE	(3 << 0)	/* Touch Screen Mode */
+#define			AT91_ADC_TSMR_TSMODE_NONE		(0 << 0)
+#define			AT91_ADC_TSMR_TSMODE_4WIRE_NO_PRESS	(1 << 0)
+#define			AT91_ADC_TSMR_TSMODE_4WIRE_PRESS	(2 << 0)
+#define			AT91_ADC_TSMR_TSMODE_5WIRE		(3 << 0)
+#define		AT91_ADC_TSMR_TSAV	(3 << 4)	/* Averages samples */
+#define			AT91_ADC_TSMR_TSAV_(x)		((x) << 4)
+#define		AT91_ADC_TSMR_SCTIM	(0x0f << 16)	/* Switch closure time */
+#define		AT91_ADC_TSMR_PENDBC	(0x0f << 28)	/* Pen Debounce time */
+#define			AT91_ADC_TSMR_PENDBC_(x)	((x) << 28)
+#define		AT91_ADC_TSMR_NOTSDMA	(1 << 22)	/* No Touchscreen DMA */
+#define		AT91_ADC_TSMR_PENDET_DIS	(0 << 24)	/* Pen contact detection disable */
+#define		AT91_ADC_TSMR_PENDET_ENA	(1 << 24)	/* Pen contact detection enable */
+
+#define AT91_ADC_TSXPOSR	0xB4
+#define AT91_ADC_TSYPOSR	0xB8
+#define AT91_ADC_TSPRESSR	0xBC
+
 #define AT91_ADC_VERSION	0xFC
 
+/* Trigger Register bit field */
+#define		AT91_ADC_TRGR_TRGPER	(0xffff << 16)
+#define			AT91_ADC_TRGR_TRGPER_(x)	((x) << 16)
+#define		AT91_ADC_TRGR_TRGMOD	(0x7 << 0)
+#define			AT91_ADC_TRGR_MOD_PERIOD_TRIG	(5 << 0)
+
 #endif
drivers/iio/adc/at91_adc.c +365 -24

491 lines not on this page · raw message

Signature · 1 line
1.7.10
#31 lore raw

PATCH 5/5 iio: at91: introduce touch screen support in iio adc driver

Hi Josh,

Quoted 31 lines · Josh Wu

AT91 ADC hardware integrate touch screen support. So this patch add touch screen support for at91 adc iio driver.
To enable touch screen support in adc, you need to add the dt parameters:
which type of touch are used? (4 or 5 wires), sample period time, pen detect debounce time, average samples and pen detect resistor.

In the meantime, since touch screen will use a interal period trigger of adc, so it is conflict to other hardware triggers. Driver will disable the hardware trigger support if touch screen is enabled.

This driver has been tested in AT91SAM9X5-EK and SAMA5D3x-EK.

Cc Dmitry Torokhov <[email protected]>
---
 .../devicetree/bindings/arm/atmel-adc.txt          |   13 +
 arch/arm/mach-at91/include/mach/at91_adc.h         |   34 ++
 drivers/iio/adc/at91_adc.c                         |  389 ++++++++++++++++++--
 3 files changed, 412 insertions(+), 24 deletions(-)
Documentation/devicetree/bindings/arm/atmel-adc.txt +3 -0
index 0db2945..925d656 100644
@@ -29,6 +29,19 @@ Optional properties:
   - atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
   - atmel,adc-clock-rate: ADC clock rate. If not specified, use the default
 			  adc_op_clk.
+  - atmel,adc-touchscreen-wires: Number of touch screen wires. Only support
+				 4 and 5 wires touch screen.
+    NOTE: when adc touch screen enabled, the adc hardware trigger will be

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 44

Quoted 10 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +10 -0
+          disabled. Since touch screen will occupied the trigger register.
+  - atmel,adc-ts-pendet-debounce: Debounce time in microsecond for touch pen
+				  detect.
+  - atmel,adc-ts-sample-period-time: Sample Period Time in microsecond for
+				     touch screen
+  - atmel,adc-ts-filter-average: Numbers of sampling data will be averaged.
+    0 means no average. 1 means average two samples. 2 means average four
+    samples. 3 means average eight samples.
+  - atmel,adc-ts-pendet-sensitivity: Pen Detection input pull-up resistor.
+    It can be 0, 1, 2, 3.

Could you expand a bit on what are these properties for? Are they board-specific? IP-specific?

[...]

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 45

Quoted 1 line
Documentation/devicetree/bindings/arm/atmel-adc.txt +1 -0
+#define DRIVER_NAME		"at91_adc"

This looks like this part doesn't belong to another cleanup patch.

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties:

Quoted 6 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +5 -0
+#define MAX_POS_BITS		12
+
+#define ZTHRESHOLD		9000
+
 struct at91_adc_caps {
+	bool	has_12bits_xy;	/* true means use 12 bits. Otherwise 10 bits */

Isn't that redundant with the low_res boolean?

Quoted 48 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +29 -0
 	bool	has_tsmr;	/* only at91sam9x5, sama5d3 have TSMR reg */
 	u32	mr_prescal_mask;
 	u32	mr_startup_mask;
 };
 
+enum atmel_adc_ts_type {
+	ATMEL_ADC_TOUCHSCREEN_NONE = 0,
+	ATMEL_ADC_TOUCHSCREEN_4WIRE,
+	ATMEL_ADC_TOUCHSCREEN_5WIRE,
+};
+
 struct at91_adc_state {
 	struct clk		*adc_clk;
 	u32			adc_clk_rate;
@@ -70,6 +83,29 @@ struct at91_adc_state {
 	bool			low_res;	/* the resolution corresponds to the lowest one */
 	wait_queue_head_t	wq_data_avail;
 	struct at91_adc_caps	caps;
+
+	/*
+	 * Following ADC channels are shared by touchscreen:
+	 *
+	 * CH0 -- Touch screen XP/UL
+	 * CH1 -- Touch screen XM/UR
+	 * CH2 -- Touch screen YP/LL
+	 * CH3 -- Touch screen YM/Sense
+	 * CH4 -- Touch screen LR(5-wire only)
+	 *
+	 * The bitfields below represents the reserved channel in the
+	 * touchscreen mode.
+	 */
+#define CHAN_MASK_TOUCHSCREEN_4WIRE	(0xf << 0)
+#define CHAN_MASK_TOUCHSCREEN_5WIRE	(0x1f << 0)
+	enum atmel_adc_ts_type	touchscreen_type;
+	struct input_dev		*ts_input;
+
+	u8			ts_filter_average;
+	u16			ts_pen_detect_debounce;
+	u8			ts_pen_detect_sensitivity;
+	u16			ts_sample_period_time;
+	u16			ts_sample_period_val;
 };
 
 static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
@@ -104,14 +140,10 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
 	return IRQ_HANDLED;
 }
 

Documentation/devicetree/bindings/arm/atmel-adc.txt static irqreturn_t at91_adc_trigger_handler(int irq, void *p) · line 114

Quoted 10 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -6
-static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
+/* Handler for classic adc channel eoc trigger */
+void handle_adc_eoc_trigger(int irq, struct iio_dev *idev)
 {
-	struct iio_dev *idev = private;
 	struct at91_adc_state *st = iio_priv(idev);
-	u32 status = at91_adc_readl(st, st->registers->status_register);
-
-	if (!(status & st->registers->drdy_mask))
-		return IRQ_HANDLED;

Why are you changing the prototype and remove most of the useful part out of the handler?

Maxime

Signature · 10 lines
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130715/14495b41/attachment.sig>
author
#32 lore raw

PATCH 5/5 iio: at91: introduce touch screen support in iio adc driver

Hi, Maxime

Quoted 38 lines · Maxime Ripard

Hi Josh,

Quoted 35 lines · Josh Wu

AT91 ADC hardware integrate touch screen support. So this patch add touch screen support for at91 adc iio driver.
To enable touch screen support in adc, you need to add the dt parameters:
which type of touch are used? (4 or 5 wires), sample period time, pen detect debounce time, average samples and pen detect resistor.

In the meantime, since touch screen will use a interal period trigger of adc, so it is conflict to other hardware triggers. Driver will disable the hardware trigger support if touch screen is enabled.

This driver has been tested in AT91SAM9X5-EK and SAMA5D3x-EK.

Cc Dmitry Torokhov <[email protected]>
---
  .../devicetree/bindings/arm/atmel-adc.txt          |   13 +
  arch/arm/mach-at91/include/mach/at91_adc.h         |   34 ++
  drivers/iio/adc/at91_adc.c                         |  389 ++++++++++++++++++--
  3 files changed, 412 insertions(+), 24 deletions(-)
Documentation/devicetree/bindings/arm/atmel-adc.txt +7 -0
index 0db2945..925d656 100644
@@ -29,6 +29,19 @@ Optional properties:
    - atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
    - atmel,adc-clock-rate: ADC clock rate. If not specified, use the default
  			  adc_op_clk.
+  - atmel,adc-touchscreen-wires: Number of touch screen wires. Only support
+				 4 and 5 wires touch screen.
+    NOTE: when adc touch screen enabled, the adc hardware trigger will be
+          disabled. Since touch screen will occupied the trigger register.
+  - atmel,adc-ts-pendet-debounce: Debounce time in microsecond for touch pen
+				  detect.
+  - atmel,adc-ts-sample-period-time: Sample Period Time in microsecond for
Quoted 8 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 44

Quoted 6 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +6 -0
+				     touch screen
+  - atmel,adc-ts-filter-average: Numbers of sampling data will be averaged.
+    0 means no average. 1 means average two samples. 2 means average four
+    samples. 3 means average eight samples.
+  - atmel,adc-ts-pendet-sensitivity: Pen Detection input pull-up resistor.
+    It can be 0, 1, 2, 3.

Could you expand a bit on what are these properties for? Are they board-specific? IP-specific?

+ - atmel,adc-touchscreen-wires: Number of touch screen wires. Only support
+ 4 and 5 wires touch screen.
+ NOTE: when adc touch screen enabled, the adc hardware trigger will be
+ disabled. Since touch screen will occupied the trigger register.

It is board specific. Currently in AT91SAM9M10G45EK, AT91SAM9X5-EK, SAMA5D3x-EK all use 4 wire touch.
Now the driver not support 5 wire yet.

+ - atmel,adc-ts-pendet-debounce: Debounce time in microsecond for touch pen
+ detect.

de-glitch time for pen detect. Board specific.

+ - atmel,adc-ts-sample-period-time: Sample Period Time in microsecond for
+ touch screen

The period to sample a touch data after pen is touched. Board specific.

+ - atmel,adc-ts-filter-average: Numbers of sampling data will be averaged.
+ 0 means no average. 1 means average two samples. 2 means average four
+ samples. 3 means average eight samples.
+ - atmel,adc-ts-pendet-sensitivity: Pen Detection input pull-up resistor.
+ It can be 0, 1, 2, 3.

Above two properties only supported in SAM9X5, SAMA5D3 IP.

Quoted 5 lines

[...]

Quoted 1 line

+#define DRIVER_NAME "at91_adc"

This looks like this part doesn't belong to another cleanup patch.

it is part of this patch. As it's a little cleanup, I integrate it as add touch feature.

Quoted 8 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt

Quoted 7 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +5 -0
+#define MAX_POS_BITS		12
+
+#define ZTHRESHOLD		9000
+
  struct at91_adc_caps {
+	bool	has_12bits_xy;	/* true means use 12 bits. Otherwise 10 bits */

Isn't that redundant with the low_res boolean?

yes, right. I will remove it.

Quoted 49 lines
Quoted 49 lines
  	bool	has_tsmr;	/* only at91sam9x5, sama5d3 have TSMR reg */
  	u32	mr_prescal_mask;
  	u32	mr_startup_mask;
  };
Documentation/devicetree/bindings/arm/atmel-adc.txt +29 -0
+enum atmel_adc_ts_type {
+	ATMEL_ADC_TOUCHSCREEN_NONE = 0,
+	ATMEL_ADC_TOUCHSCREEN_4WIRE,
+	ATMEL_ADC_TOUCHSCREEN_5WIRE,
+};
+
  struct at91_adc_state {
  	struct clk		*adc_clk;
  	u32			adc_clk_rate;
@@ -70,6 +83,29 @@ struct at91_adc_state {
  	bool			low_res;	/* the resolution corresponds to the lowest one */
  	wait_queue_head_t	wq_data_avail;
  	struct at91_adc_caps	caps;
+
+	/*
+	 * Following ADC channels are shared by touchscreen:
+	 *
+	 * CH0 -- Touch screen XP/UL
+	 * CH1 -- Touch screen XM/UR
+	 * CH2 -- Touch screen YP/LL
+	 * CH3 -- Touch screen YM/Sense
+	 * CH4 -- Touch screen LR(5-wire only)
+	 *
+	 * The bitfields below represents the reserved channel in the
+	 * touchscreen mode.
+	 */
+#define CHAN_MASK_TOUCHSCREEN_4WIRE	(0xf << 0)
+#define CHAN_MASK_TOUCHSCREEN_5WIRE	(0x1f << 0)
+	enum atmel_adc_ts_type	touchscreen_type;
+	struct input_dev		*ts_input;
+
+	u8			ts_filter_average;
+	u16			ts_pen_detect_debounce;
+	u8			ts_pen_detect_sensitivity;
+	u16			ts_sample_period_time;
+	u16			ts_sample_period_val;
  };
 
  static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
@@ -104,14 +140,10 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
  	return IRQ_HANDLED;
  }
 
Quoted 12 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt static irqreturn_t at91_adc_trigger_handler(int irq, void *p) · line 114

Quoted 10 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -6
-static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
+/* Handler for classic adc channel eoc trigger */
+void handle_adc_eoc_trigger(int irq, struct iio_dev *idev)
  {
-	struct iio_dev *idev = private;
  	struct at91_adc_state *st = iio_priv(idev);
-	u32 status = at91_adc_readl(st, st->registers->status_register);
-
-	if (!(status & st->registers->drdy_mask))
-		return IRQ_HANDLED;

Why are you changing the prototype and remove most of the useful part out of the handler?

I change the protype because I make it as a sub interrupt handler. Since I just add a new interrupt handler which will check the interrupt type, if DRDY interrupt is coming, it will call your original handler. Otherwise use touch screen interrupt handler code.

Following is the new interrupt handler code in this patch:

+static irqreturn_t at91_adc_interrupt(int irq, void *private) +{
+ struct iio_dev *idev = private;
+ struct at91_adc_state *st = iio_priv(idev);
+ u32 status = at91_adc_readl(st, st->registers->status_register);
+ const uint32_t ts_data_irq_mask =
+ AT91_ADC_IER_XRDY |
+ AT91_ADC_IER_YRDY |
+ AT91_ADC_IER_PRDY;
+
+ if (status & st->registers->drdy_mask)
+ handle_adc_eoc_trigger(irq, idev);

                              ^
                              |

here we call original trigger handler.

+
+ if (status & AT91_ADC_IER_PEN) {
+ at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
+ at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_NOPEN |
+ ts_data_irq_mask);
+ /* Set up period trigger for sampling */
+ at91_adc_writel(st, st->registers->trigger_register,
+ AT91_ADC_TRGR_MOD_PERIOD_TRIG |
+ AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val));
+ } else if (status & AT91_ADC_IER_NOPEN) {
+ at91_adc_writel(st, st->registers->trigger_register, 0);
+ at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_NOPEN |
+ ts_data_irq_mask);
+ at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
+
+ input_report_key(st->ts_input, BTN_TOUCH, 0);
+ input_sync(st->ts_input);
+ } else if ((status & ts_data_irq_mask) == ts_data_irq_mask) {

Those code will handle for the Touch screen interrupt.

Quoted 3 lines

Maxime

Thanks.
Best Regards,
Josh Wu

#33 lore raw

PATCH 5/5 iio: at91: introduce touch screen support in iio adc driver

Hi Josh,

Quoted 53 lines · Josh Wu
Quoted 46 lines · Maxime Ripard

Hi Josh,

Quoted 31 lines · Josh Wu

AT91 ADC hardware integrate touch screen support. So this patch add touch screen support for at91 adc iio driver.
To enable touch screen support in adc, you need to add the dt parameters:
which type of touch are used? (4 or 5 wires), sample period time, pen detect debounce time, average samples and pen detect resistor.

In the meantime, since touch screen will use a interal period trigger of adc, so it is conflict to other hardware triggers. Driver will disable the hardware trigger support if touch screen is enabled.

This driver has been tested in AT91SAM9X5-EK and SAMA5D3x-EK.

Cc Dmitry Torokhov <[email protected]>
---
 .../devicetree/bindings/arm/atmel-adc.txt          |   13 +
 arch/arm/mach-at91/include/mach/at91_adc.h         |   34 ++
 drivers/iio/adc/at91_adc.c                         |  389 ++++++++++++++++++--
 3 files changed, 412 insertions(+), 24 deletions(-)
Documentation/devicetree/bindings/arm/atmel-adc.txt +3 -0
index 0db2945..925d656 100644
@@ -29,6 +29,19 @@ Optional properties:
   - atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
   - atmel,adc-clock-rate: ADC clock rate. If not specified, use the default
 			  adc_op_clk.
+  - atmel,adc-touchscreen-wires: Number of touch screen wires. Only support
+				 4 and 5 wires touch screen.
+    NOTE: when adc touch screen enabled, the adc hardware trigger will be

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 44

Quoted 10 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +10 -0
+          disabled. Since touch screen will occupied the trigger register.
+  - atmel,adc-ts-pendet-debounce: Debounce time in microsecond for touch pen
+				  detect.
+  - atmel,adc-ts-sample-period-time: Sample Period Time in microsecond for
+				     touch screen
+  - atmel,adc-ts-filter-average: Numbers of sampling data will be averaged.
+    0 means no average. 1 means average two samples. 2 means average four
+    samples. 3 means average eight samples.
+  - atmel,adc-ts-pendet-sensitivity: Pen Detection input pull-up resistor.
+    It can be 0, 1, 2, 3.

Could you expand a bit on what are these properties for? Are they board-specific? IP-specific?

Documentation/devicetree/bindings/arm/atmel-adc.txt +4 -0
+  - atmel,adc-touchscreen-wires: Number of touch screen wires. Only support
+                 4 and 5 wires touch screen.
+    NOTE: when adc touch screen enabled, the adc hardware trigger will be
+          disabled. Since touch screen will occupied the trigger register.
Quoted 3 lines

It is board specific. Currently in AT91SAM9M10G45EK, AT91SAM9X5-EK, SAMA5D3x-EK all use 4 wire touch.
Now the driver not support 5 wire yet.

I see, maybe you should add this in the documentation then

Quoted 17 lines

+ - atmel,adc-ts-pendet-debounce: Debounce time in microsecond for touch pen
+ detect.

de-glitch time for pen detect. Board specific.

Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
+  - atmel,adc-ts-sample-period-time: Sample Period Time in microsecond for
+                     touch screen

The period to sample a touch data after pen is touched. Board specific.

Documentation/devicetree/bindings/arm/atmel-adc.txt +5 -0
+  - atmel,adc-ts-filter-average: Numbers of sampling data will be averaged.
+    0 means no average. 1 means average two samples. 2 means average four
+    samples. 3 means average eight samples.
+  - atmel,adc-ts-pendet-sensitivity: Pen Detection input pull-up resistor.
+    It can be 0, 1, 2, 3.
Quoted 1 line

Above two properties only supported in SAM9X5, SAMA5D3 IP.

Ok, that should go to the documentation as well. What do the values of atmel,adc-ts-pendet-sensitivity correspond to?

Quoted 60 lines
Quoted 7 lines
Quoted 7 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -1
@@ -104,14 +140,10 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
 	return IRQ_HANDLED;
 }
-static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
+/* Handler for classic adc channel eoc trigger */
+void handle_adc_eoc_trigger(int irq, struct iio_dev *idev)
 {
Quoted 8 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt static irqreturn_t at91_adc_trigger_handler(int irq, void *p) · line 113

Quoted 6 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +0 -5
-	struct iio_dev *idev = private;
 	struct at91_adc_state *st = iio_priv(idev);
-	u32 status = at91_adc_readl(st, st->registers->status_register);
-
-	if (!(status & st->registers->drdy_mask))
-		return IRQ_HANDLED;

Why are you changing the prototype and remove most of the useful part out of the handler?

I change the protype because I make it as a sub interrupt handler. Since I just add a new interrupt handler which will check the interrupt type, if DRDY interrupt is coming, it will call your original handler.
Otherwise use touch screen interrupt handler code.

Following is the new interrupt handler code in this patch:

drivers/iio/adc/at91_adc.c +12 -0
+static irqreturn_t at91_adc_interrupt(int irq, void *private)
+{
+	struct iio_dev *idev = private;
+	struct at91_adc_state *st = iio_priv(idev);
+	u32 status = at91_adc_readl(st, st->registers->status_register);
+	const uint32_t ts_data_irq_mask =
+		AT91_ADC_IER_XRDY |
+		AT91_ADC_IER_YRDY |
+		AT91_ADC_IER_PRDY;
+
+	if (status & st->registers->drdy_mask)
+		handle_adc_eoc_trigger(irq, idev);
 
                             ^
                             |

here we call original trigger handler.

drivers/iio/adc/at91_adc.c +18 -0
+
+	if (status & AT91_ADC_IER_PEN) {
+		at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
+		at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_NOPEN |
+			ts_data_irq_mask);
+		/* Set up period trigger for sampling */
+		at91_adc_writel(st, st->registers->trigger_register,
+			AT91_ADC_TRGR_MOD_PERIOD_TRIG |
+			AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val));
+	} else if (status & AT91_ADC_IER_NOPEN) {
+		at91_adc_writel(st, st->registers->trigger_register, 0);
+		at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_NOPEN |
+			ts_data_irq_mask);
+		at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
+
+		input_report_key(st->ts_input, BTN_TOUCH, 0);
+		input_sync(st->ts_input);
+	} else if ((status & ts_data_irq_mask) == ts_data_irq_mask) {
Quoted 1 line

Those code will handle for the Touch screen interrupt.

Ah, right.

Thanks!
Maxime

Signature · 10 lines
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130716/ccb648f8/attachment.sig>
maintainer
#34 lore raw

PATCH 5/5 iio: at91: introduce touch screen support in iio adc driver

In reply to Josh Wu
Quoted 11 lines · Josh Wu

AT91 ADC hardware integrate touch screen support. So this patch add touch screen support for at91 adc iio driver.
To enable touch screen support in adc, you need to add the dt parameters:
which type of touch are used? (4 or 5 wires), sample period time, pen detect debounce time, average samples and pen detect resistor.

In the meantime, since touch screen will use a interal period trigger of adc, so it is conflict to other hardware triggers. Driver will disable the hardware trigger support if touch screen is enabled.

This driver has been tested in AT91SAM9X5-EK and SAMA5D3x-EK.

Firstly, this is adding input subsystem stuff inside an iio driver. Generally we are very anti this sort of combination, but I agree that right now the support for doing it any other way is not really present.

The next bit of text is actually largely irrelevant here as I actually read the driver after I wrote it and discovered the hardware handles all the nastiness of touch screen processing here. I'll leave it here for reference of anyone working on parts where it is relevant ;)

When I had more time to work on IIO I was working on an iio to input
bridge driver (similar to iio-hwmon, but using the buffered interface hooks instead). It was fundamentally working, but I never quite got around to cleaning it up for submission. I'd appreciate any comments on what is missing or does not work for this case.

http://marc.info/?l=linux-iio&m=135167944813803&w=2

Ulitimately what I would love to see is a generic touch screen consumer driver for IIO adc's but appreciate that is a big and complex job given all the different varieties of touch screen and trying to abstract the control of the various channels.

So back to this part. My immediate thought is that this really ought to have an mfd underneath separate IIO and input drivers.

Dmitry, what do you think?

Quoted 592 lines
Cc Dmitry Torokhov <[email protected]>
---
 .../devicetree/bindings/arm/atmel-adc.txt          |   13 +
 arch/arm/mach-at91/include/mach/at91_adc.h         |   34 ++
 drivers/iio/adc/at91_adc.c                         |  389 ++++++++++++++++++--
 3 files changed, 412 insertions(+), 24 deletions(-)
Documentation/devicetree/bindings/arm/atmel-adc.txt +13 -0
index 0db2945..925d656 100644
@@ -29,6 +29,19 @@ Optional properties:
   - atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
   - atmel,adc-clock-rate: ADC clock rate. If not specified, use the default
 			  adc_op_clk.
+  - atmel,adc-touchscreen-wires: Number of touch screen wires. Only support
+				 4 and 5 wires touch screen.
+    NOTE: when adc touch screen enabled, the adc hardware trigger will be
+          disabled. Since touch screen will occupied the trigger register.
+  - atmel,adc-ts-pendet-debounce: Debounce time in microsecond for touch pen
+				  detect.
+  - atmel,adc-ts-sample-period-time: Sample Period Time in microsecond for
+				     touch screen
+  - atmel,adc-ts-filter-average: Numbers of sampling data will be averaged.
+    0 means no average. 1 means average two samples. 2 means average four
+    samples. 3 means average eight samples.
+  - atmel,adc-ts-pendet-sensitivity: Pen Detection input pull-up resistor.
+    It can be 0, 1, 2, 3.
 
 Optional trigger Nodes:
   - Required properties:
arch/arm/mach-at91/include/mach/at91_adc.h +34 -0
index ab273ee..6d6cc14 100644
@@ -57,10 +57,44 @@
 #define AT91_ADC_IER		0x24		/* Interrupt Enable Register */
 #define AT91_ADC_IDR		0x28		/* Interrupt Disable Register */
 #define AT91_ADC_IMR		0x2C		/* Interrupt Mask Register */
+#define		AT91_ADC_IER_PEN	(1 << 29)
+#define		AT91_ADC_IER_NOPEN	(1 << 30)
+#define		AT91_ADC_IER_XRDY	(1 << 20)
+#define		AT91_ADC_IER_YRDY	(1 << 21)
+#define		AT91_ADC_IER_PRDY	(1 << 22)
+#define		AT91_ADC_ISR_PENS	(1 << 31)
 
 #define AT91_ADC_CHR(n)		(0x30 + ((n) * 4))	/* Channel Data Register N */
 #define		AT91_ADC_DATA		(0x3ff)
 
+#define AT91_ADC_ACR		0x94	/* Analog Control Register */
+#define		AT91_ADC_ACR_PENDETSENS	(0x3 << 0)	/* pull-up resistor */
+
+#define AT91_ADC_TSMR		0xB0
+#define		AT91_ADC_TSMR_TSMODE	(3 << 0)	/* Touch Screen Mode */
+#define			AT91_ADC_TSMR_TSMODE_NONE		(0 << 0)
+#define			AT91_ADC_TSMR_TSMODE_4WIRE_NO_PRESS	(1 << 0)
+#define			AT91_ADC_TSMR_TSMODE_4WIRE_PRESS	(2 << 0)
+#define			AT91_ADC_TSMR_TSMODE_5WIRE		(3 << 0)
+#define		AT91_ADC_TSMR_TSAV	(3 << 4)	/* Averages samples */
+#define			AT91_ADC_TSMR_TSAV_(x)		((x) << 4)
+#define		AT91_ADC_TSMR_SCTIM	(0x0f << 16)	/* Switch closure time */
+#define		AT91_ADC_TSMR_PENDBC	(0x0f << 28)	/* Pen Debounce time */
+#define			AT91_ADC_TSMR_PENDBC_(x)	((x) << 28)
+#define		AT91_ADC_TSMR_NOTSDMA	(1 << 22)	/* No Touchscreen DMA */
+#define		AT91_ADC_TSMR_PENDET_DIS	(0 << 24)	/* Pen contact detection disable */
+#define		AT91_ADC_TSMR_PENDET_ENA	(1 << 24)	/* Pen contact detection enable */
+
+#define AT91_ADC_TSXPOSR	0xB4
+#define AT91_ADC_TSYPOSR	0xB8
+#define AT91_ADC_TSPRESSR	0xBC
+
 #define AT91_ADC_VERSION	0xFC
 
+/* Trigger Register bit field */
+#define		AT91_ADC_TRGR_TRGPER	(0xffff << 16)
+#define			AT91_ADC_TRGR_TRGPER_(x)	((x) << 16)
+#define		AT91_ADC_TRGR_TRGMOD	(0x7 << 0)
+#define			AT91_ADC_TRGR_MOD_PERIOD_TRIG	(5 << 0)
+
 #endif
drivers/iio/adc/at91_adc.c +365 -24
index 8f1386f..ffc0e42 100644
@@ -11,6 +11,7 @@
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/io.h>
+#include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
@@ -39,12 +40,24 @@
 #define at91_adc_writel(st, reg, val) \
 	(writel_relaxed(val, st->reg_base + reg))
 
+#define DRIVER_NAME		"at91_adc"
+#define MAX_POS_BITS		12
+
+#define ZTHRESHOLD		9000
+
 struct at91_adc_caps {
+	bool	has_12bits_xy;	/* true means use 12 bits. Otherwise 10 bits */
 	bool	has_tsmr;	/* only at91sam9x5, sama5d3 have TSMR reg */
 	u32	mr_prescal_mask;
 	u32	mr_startup_mask;
 };
 
+enum atmel_adc_ts_type {
+	ATMEL_ADC_TOUCHSCREEN_NONE = 0,
+	ATMEL_ADC_TOUCHSCREEN_4WIRE,
+	ATMEL_ADC_TOUCHSCREEN_5WIRE,
+};
+
 struct at91_adc_state {
 	struct clk		*adc_clk;
 	u32			adc_clk_rate;
@@ -70,6 +83,29 @@ struct at91_adc_state {
 	bool			low_res;	/* the resolution corresponds to the lowest one */
 	wait_queue_head_t	wq_data_avail;
 	struct at91_adc_caps	caps;
+
+	/*
+	 * Following ADC channels are shared by touchscreen:
+	 *
+	 * CH0 -- Touch screen XP/UL
+	 * CH1 -- Touch screen XM/UR
+	 * CH2 -- Touch screen YP/LL
+	 * CH3 -- Touch screen YM/Sense
+	 * CH4 -- Touch screen LR(5-wire only)
+	 *
+	 * The bitfields below represents the reserved channel in the
+	 * touchscreen mode.
+	 */
+#define CHAN_MASK_TOUCHSCREEN_4WIRE	(0xf << 0)
+#define CHAN_MASK_TOUCHSCREEN_5WIRE	(0x1f << 0)
+	enum atmel_adc_ts_type	touchscreen_type;
+	struct input_dev		*ts_input;
+
+	u8			ts_filter_average;
+	u16			ts_pen_detect_debounce;
+	u8			ts_pen_detect_sensitivity;
+	u16			ts_sample_period_time;
+	u16			ts_sample_period_val;
 };
 
 static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
@@ -104,14 +140,10 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
 	return IRQ_HANDLED;
 }
 
-static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
+/* Handler for classic adc channel eoc trigger */
+void handle_adc_eoc_trigger(int irq, struct iio_dev *idev)
 {
-	struct iio_dev *idev = private;
 	struct at91_adc_state *st = iio_priv(idev);
-	u32 status = at91_adc_readl(st, st->registers->status_register);
-
-	if (!(status & st->registers->drdy_mask))
-		return IRQ_HANDLED;
 
 	if (iio_buffer_enabled(idev)) {
 		disable_irq_nosync(irq);
@@ -121,6 +153,115 @@ static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
 		st->done = true;
 		wake_up_interruptible(&st->wq_data_avail);
 	}
+}
+
+static int at91_ts_sample(struct at91_adc_state *st)
+{
+	unsigned int xscale, yscale, reg, z1, z2;
+	unsigned int x, y, pres, xpos, ypos;
+	unsigned int rxp = 1;
+	unsigned int factor = 1000;
+	struct iio_dev *idev = iio_priv_to_dev(st);
+
+	unsigned int xyz_mask_bits = st->caps.has_12bits_xy ? 12 : 10;
+	unsigned int xyz_mask = (1 << xyz_mask_bits) - 1;
+
+	/* calculate position */
+	/* x position = (x / xscale) * max, max = 2^MAX_POS_BITS - 1 */
+	reg = at91_adc_readl(st, AT91_ADC_TSXPOSR);
+	xpos = reg & xyz_mask;
+	x = (xpos << MAX_POS_BITS) - xpos;
+	xscale = (reg >> 16) & xyz_mask;
+	if (xscale != 0)
+		x /= xscale;
+	else
+		dev_err(&idev->dev, "xscale == 0!!!\n");
+
+	/* y position = (y / yscale) * max, max = 2^MAX_POS_BITS - 1 */
+	reg = at91_adc_readl(st, AT91_ADC_TSYPOSR);
+	ypos = reg & xyz_mask;
+	y = (ypos << MAX_POS_BITS) - ypos;
+	yscale = (reg >> 16) & xyz_mask;
+	if (yscale != 0)
+		y /= yscale;
+	else
+		dev_err(&idev->dev, "yscale == 0!!!\n");
+
+	/* calculate the pressure */
+	reg = at91_adc_readl(st, AT91_ADC_TSPRESSR);
+	z1 = reg & xyz_mask;
+	z2 = (reg >> 16) & xyz_mask;
+
+	if (z1 != 0)
+		pres = rxp * (x * factor / 1024) * (z2 * factor / z1 - factor)
+			/ factor;
+	else
+		pres = ZTHRESHOLD;	/* no pen contacted */
+
+	dev_dbg(&idev->dev, "xpos = %d, xscale = %d, ypos = %d, yscale = %d, z1 = %d, z2 = %d, press = %d\n",
+				xpos, xscale, ypos, yscale, z1, z2, pres);
+
+	if (pres < ZTHRESHOLD) {
+		dev_dbg(&idev->dev, "x = %d, y = %d, pressure = %d\n",
+					x, y, pres / factor);
+		input_report_abs(st->ts_input, ABS_X, x);
+		input_report_abs(st->ts_input, ABS_Y, y);
+		input_report_abs(st->ts_input, ABS_PRESSURE, pres);
+		input_report_key(st->ts_input, BTN_TOUCH, 1);
+		input_sync(st->ts_input);
+	} else {
+		dev_dbg(&idev->dev,
+				"pressure too low: not reporting\n");
+	}
+
+	return 0;
+}
+
+
+static irqreturn_t at91_adc_interrupt(int irq, void *private)
+{
+	struct iio_dev *idev = private;
+	struct at91_adc_state *st = iio_priv(idev);
+	u32 status = at91_adc_readl(st, st->registers->status_register);
+	const uint32_t ts_data_irq_mask =
+		AT91_ADC_IER_XRDY |
+		AT91_ADC_IER_YRDY |
+		AT91_ADC_IER_PRDY;
+
+	if (status & st->registers->drdy_mask)
+		handle_adc_eoc_trigger(irq, idev);
+
+	if (status & AT91_ADC_IER_PEN) {
+		at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
+		at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_NOPEN |
+			ts_data_irq_mask);
+		/* Set up period trigger for sampling */
+		at91_adc_writel(st, st->registers->trigger_register,
+			AT91_ADC_TRGR_MOD_PERIOD_TRIG |
+			AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val));
+	} else if (status & AT91_ADC_IER_NOPEN) {
+		at91_adc_writel(st, st->registers->trigger_register, 0);
+		at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_NOPEN |
+			ts_data_irq_mask);
+		at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
+
+		input_report_key(st->ts_input, BTN_TOUCH, 0);
+		input_sync(st->ts_input);
+	} else if ((status & ts_data_irq_mask) == ts_data_irq_mask) {
+		/* Now all touchscreen data is ready */
+
+		if (status & AT91_ADC_ISR_PENS) {
+			/* validate data by pen contact */
+			at91_ts_sample(st);
+		} else {
+			/* triggered by event that is no pen contact, just read
+			 * them to clean the interrupt and discard all.
+			 */
+			at91_adc_readl(st, AT91_ADC_TSXPOSR);
+			at91_adc_readl(st, AT91_ADC_TSYPOSR);
+			at91_adc_readl(st, AT91_ADC_TSPRESSR);
+		}
+	}
 
 	return IRQ_HANDLED;
 }
@@ -130,6 +271,16 @@ static int at91_adc_channel_init(struct iio_dev *idev)
 	struct at91_adc_state *st = iio_priv(idev);
 	struct iio_chan_spec *chan_array, *timestamp;
 	int bit, idx = 0;
+	unsigned long rsvd_mask = 0;
+
+	/* If touchscreen is enable, then reserve the adc channels */
+	if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
+		rsvd_mask = CHAN_MASK_TOUCHSCREEN_4WIRE;
+	else if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_5WIRE)
+		rsvd_mask = CHAN_MASK_TOUCHSCREEN_5WIRE;
+
+	/* set up the channel mask to reserve touchscreen channels */
+	st->channels_mask &= ~rsvd_mask;
 
 	idev->num_channels = bitmap_weight(&st->channels_mask,
 					   st->num_channels) + 1;
@@ -561,6 +712,57 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
 		i++;
 	}
 
+	/* Check if touchscreen is enabled in DT. */
+	ret = of_property_read_u32(node, "atmel,adc-touchscreen-wires", &prop);
+	if (ret)
+		dev_info(&idev->dev, "Touchscreen not enabled.\n");
+	else if (prop == 4)
+		st->touchscreen_type = ATMEL_ADC_TOUCHSCREEN_4WIRE;
+	else if (prop == 5)
+		st->touchscreen_type = ATMEL_ADC_TOUCHSCREEN_5WIRE;
+	else
+		dev_warn(&idev->dev, "Unsupported number of touchscreen wires (%d)\n",
+				prop);
+
+	if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_NONE)
+		return 0;
+
+	/* Touch screen is enabled, so check touch screen dt parameters */
+	if (of_property_read_u32(node, "atmel,adc-ts-filter-average", &prop)) {
+		dev_err(&idev->dev, "Missing atmel,adc-ts-filtering-average property in the DT.\n");
+		ret = -EINVAL;
+		goto error_ret;
+	}
+	st->ts_filter_average = prop;
+	if (st->ts_filter_average > 3) {
+		dev_err(&idev->dev, "Invalid atmel,adc-ts-filtering-average property in the DT.\n");
+		ret = -EINVAL;
+		goto error_ret;
+	}
+
+	prop = 0;
+	of_property_read_u32(node, "atmel,adc-ts-pendet-debounce", &prop);
+	st->ts_pen_detect_debounce = prop;
+
+	/* default sample period is 2ms. The real touch sample period should be
+	 * this period * TSFREQ.
+	 */
+	prop = 2000;
+	of_property_read_u32(node, "atmel,adc-ts-sample-period-time", &prop);
+	st->ts_sample_period_time = prop;
+
+	if (of_property_read_u32(node, "atmel,adc-ts-pendet-sensitivity", &prop)) {
+		dev_err(&idev->dev, "Missing atmel,adc-ts-pendet-sensitivity property in the DT.\n");
+		ret = -EINVAL;
+		goto error_ret;
+	}
+	st->ts_pen_detect_sensitivity = prop;
+	if (st->ts_pen_detect_sensitivity > 3) {
+		dev_err(&idev->dev, "Invalid atmel,adc-ts-pendet-sensitivity property in the DT.\n");
+		ret = -EINVAL;
+		goto error_ret;
+	}
+
 	return 0;
 
 error_ret:
@@ -592,6 +794,114 @@ static const struct iio_info at91_adc_info = {
 	.read_raw = &at91_adc_read_raw,
 };
 
+/* Touchscreen related functions */
+static int atmel_ts_open(struct input_dev *dev)
+{
+	struct at91_adc_state *st = input_get_drvdata(dev);
+
+	at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
+	return 0;
+}
+
+static void atmel_ts_close(struct input_dev *dev)
+{
+	struct at91_adc_state *st = input_get_drvdata(dev);
+
+	at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
+}
+
+static int at91_ts_hw_init(struct at91_adc_state *st, u32 adc_clk_khz)
+{
+	u32 reg = 0, pendbc;
+	int i = 0;
+
+	if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
+		reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS;
+	else
+		reg = AT91_ADC_TSMR_TSMODE_5WIRE;
+
+	/* a Pen Detect Debounce Time is necessary for the ADC Touch to avoid
+	 * pen detect noise.
+	 * The formula is : Pen Detect Debounce Time = (2 ^ pendbc) / ADCClock
+	 */
+	pendbc = round_up(st->ts_pen_detect_debounce * adc_clk_khz / 1000, 1);
+
+	while (pendbc >> ++i)
+		;	/* Empty! Find the shift offset */
+	if (abs(pendbc - (1 << i)) < abs(pendbc - (1 << (i - 1))))
+		pendbc = i;
+	else
+		pendbc = i - 1;
+
+	if (st->caps.has_tsmr) {
+		reg |= AT91_ADC_TSMR_TSAV_(st->ts_filter_average)
+				& AT91_ADC_TSMR_TSAV;
+		reg |= AT91_ADC_TSMR_PENDBC_(pendbc) & AT91_ADC_TSMR_PENDBC;
+		reg |= AT91_ADC_TSMR_NOTSDMA;
+		reg |= AT91_ADC_TSMR_PENDET_ENA;
+		reg |= 0x03 << 8;	/* TSFREQ, need bigger than TSAV */
+
+		at91_adc_writel(st, AT91_ADC_TSMR, reg);
+	} else {
+		/* TODO: for 9g45 which has no TSMR */
+	}
+
+	/* Change adc internal resistor value for better pen detection,
+	 * default value is 100 kOhm.
+	 * 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm
+	 * option only available on ES2 and higher
+	 */
+	at91_adc_writel(st, AT91_ADC_ACR, st->ts_pen_detect_sensitivity
+			& AT91_ADC_ACR_PENDETSENS);
+
+	/* Sample Peroid Time = (TRGPER + 1) / ADCClock */
+	st->ts_sample_period_val = round_up((st->ts_sample_period_time *
+			adc_clk_khz / 1000) - 1, 1);
+
+	return 0;
+}
+
+static int at91_ts_register(struct at91_adc_state *st)
+{
+	struct input_dev *input;
+	struct iio_dev *idev = iio_priv_to_dev(st);
+	int ret;
+
+	input = input_allocate_device();
+	if (!input) {
+		dev_err(&idev->dev, "Failed to allocate TS device!\n");
+		return -ENOMEM;
+	}
+
+	input->name = DRIVER_NAME;
+	input->id.bustype = BUS_HOST;
+	input->dev.parent = idev->dev.parent;
+	input->open = atmel_ts_open;
+	input->close = atmel_ts_close;
+
+	__set_bit(EV_ABS, input->evbit);
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(BTN_TOUCH, input->keybit);
+	input_set_abs_params(input, ABS_X, 0, (1 << MAX_POS_BITS) - 1, 0, 0);
+	input_set_abs_params(input, ABS_Y, 0, (1 << MAX_POS_BITS) - 1, 0, 0);
+	input_set_abs_params(input, ABS_PRESSURE, 0, 0xffffff, 0, 0);
+
+	st->ts_input = input;
+	input_set_drvdata(input, st);
+
+	ret = input_register_device(input);
+	if (ret)
+		input_free_device(st->ts_input);
+
+	return ret;
+}
+
+static void at91_ts_unregister(struct at91_adc_state *st)
+{
+	input_unregister_device(st->ts_input);
+	input_free_device(st->ts_input);
+}
+
 /*
  * Since atmel adc support different ip for touchscreen mode. Through the
  * IP check, we will know the touchscreen capbilities.
@@ -610,6 +920,7 @@ static void atmel_adc_get_cap(struct at91_adc_state *st)
 	/* keep only major version number */
 	switch (version & 0xf00) {
 	case 0x500:	/* SAMA5D3 */
+		st->caps.has_12bits_xy = 1;
 	case 0x400:	/* AT91SAM9X5/9N12 */
 		st->caps.has_tsmr = 1;
 		st->caps.mr_startup_mask = AT91_ADC_STARTUP;
@@ -683,7 +994,7 @@ static int at91_adc_probe(struct platform_device *pdev)
 	at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_SWRST);
 	at91_adc_writel(st, AT91_ADC_IDR, 0xFFFFFFFF);
 	ret = request_irq(st->irq,
-			  at91_adc_eoc_trigger,
+			  at91_adc_interrupt,
 			  0,
 			  pdev->dev.driver->name,
 			  idev);
@@ -731,6 +1042,10 @@ static int at91_adc_probe(struct platform_device *pdev)
 	adc_clk = st->adc_clk_rate ?
 		st->adc_clk_rate : clk_get_rate(st->adc_clk);
 	adc_clk_khz = adc_clk / 1000;
+
+	dev_dbg(&pdev->dev, "Master clock is set as: %d Hz, adc_clk should set as: %d Hz\n",
+		mstrclk, adc_clk);
+
 	prsc = (mstrclk / (2 * adc_clk)) - 1;
 
 	if (!st->startup_time) {
@@ -799,30 +1114,52 @@ static int at91_adc_probe(struct platform_device *pdev)
 	init_waitqueue_head(&st->wq_data_avail);
 	mutex_init(&st->lock);
 
-	ret = at91_adc_buffer_init(idev);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
-		goto error_disable_adc_clk;
-	}
+	/*
+	 * Since touch screen will set trigger register as period trigger. So
+	 * when touch screen is enabled, then we have to disable hardware
+	 * trigger for classic adc.
+	 */
+	if (!st->touchscreen_type) {
+		ret = at91_adc_buffer_init(idev);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
+			goto error_disable_adc_clk;
+		}
 
-	ret = at91_adc_trigger_init(idev);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
-		goto error_unregister_buffer;
+		ret = at91_adc_trigger_init(idev);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
+			at91_adc_buffer_remove(idev);
+			goto error_disable_adc_clk;
+		}
+	} else {
+		if (!st->caps.has_tsmr) {
+			dev_err(&pdev->dev, "We don't support non-TSMR adc\n");
+			goto error_disable_adc_clk;
+		}
+
+		ret = at91_ts_register(st);
+		if (ret)
+			goto error_disable_adc_clk;
+
+		at91_ts_hw_init(st, adc_clk_khz);
 	}
 
 	ret = iio_device_register(idev);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Couldn't register the device.\n");
-		goto error_remove_triggers;
+		goto error_iio_device_register;
 	}
 
 	return 0;
 
-error_remove_triggers:
-	at91_adc_trigger_remove(idev);
-error_unregister_buffer:
-	at91_adc_buffer_remove(idev);
+error_iio_device_register:
+	if (!st->touchscreen_type) {
+		at91_adc_trigger_remove(idev);
+		at91_adc_buffer_remove(idev);
+	} else {
+		at91_ts_unregister(st);
+	}
 error_disable_adc_clk:
 	clk_disable_unprepare(st->adc_clk);
 error_disable_clk:
@@ -841,8 +1178,12 @@ static int at91_adc_remove(struct platform_device *pdev)
 	struct at91_adc_state *st = iio_priv(idev);
 
 	iio_device_unregister(idev);
-	at91_adc_trigger_remove(idev);
-	at91_adc_buffer_remove(idev);
+	if (!st->touchscreen_type) {
+		at91_adc_trigger_remove(idev);
+		at91_adc_buffer_remove(idev);
+	} else {
+		at91_ts_unregister(st);
+	}
 	clk_disable_unprepare(st->adc_clk);
 	clk_disable_unprepare(st->clk);
 	free_irq(st->irq, idev);
@@ -861,7 +1202,7 @@ static struct platform_driver at91_adc_driver = {
 	.probe = at91_adc_probe,
 	.remove = at91_adc_remove,
 	.driver = {
-		   .name = "at91_adc",
+		   .name = DRIVER_NAME,
 		   .of_match_table = of_match_ptr(at91_adc_dt_ids),
 	},
 };
#35 lore raw

PATCH 5/5 iio: at91: introduce touch screen support in iio adc driver

In reply to Josh Wu
Quoted 25 lines · Josh Wu

AT91 ADC hardware integrate touch screen support. So this patch add touch screen support for at91 adc iio driver.
To enable touch screen support in adc, you need to add the dt parameters:
which type of touch are used? (4 or 5 wires), sample period time, pen detect debounce time, average samples and pen detect resistor.

In the meantime, since touch screen will use a interal period trigger of adc, so it is conflict to other hardware triggers. Driver will disable the hardware trigger support if touch screen is enabled.

This driver has been tested in AT91SAM9X5-EK and SAMA5D3x-EK.

Cc Dmitry Torokhov <[email protected]>
---
 .../devicetree/bindings/arm/atmel-adc.txt          |   13 +
 arch/arm/mach-at91/include/mach/at91_adc.h         |   34 ++
 drivers/iio/adc/at91_adc.c                         |  389 ++++++++++++++++++--
 3 files changed, 412 insertions(+), 24 deletions(-)
Documentation/devicetree/bindings/arm/atmel-adc.txt +0 -0
index 0db2945..925d656 100644

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 33

Quoted 5 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
@@ -29,6 +29,19 @@ Optional properties:
   - atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
   - atmel,adc-clock-rate: ADC clock rate. If not specified, use the default
                          adc_op_clk.
+  - atmel,adc-touchscreen-wires: Number of touch screen wires. Only support
+                                4 and 5 wires touch screen.

Are 4 and 5 wire configurations that can exist, or the only ones supported by the driver?

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 37

Quoted 4 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +4 -0
+    NOTE: when adc touch screen enabled, the adc hardware trigger will be
+          disabled. Since touch screen will occupied the trigger register.
+  - atmel,adc-ts-pendet-debounce: Debounce time in microsecond for touch pen
+                                 detect.

For consistency with the adc-touchscreen-wires property, and other properties with timing information, how about atmel,adc-touchscreen-debounce-delay-us ?

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 39

Quoted 2 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
+  - atmel,adc-ts-sample-period-time: Sample Period Time in microsecond for
+                                    touch screen

Again, please be consistent with ts or touchscreen. It may also be good to have -us to make the units explicit (though it does leave the property name being quite a mothful):

atmel,adc-touchscreen-sample-period-us ?

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 42

Quoted 3 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +3 -0
+  - atmel,adc-ts-filter-average: Numbers of sampling data will be averaged.
+    0 means no average. 1 means average two samples. 2 means average four
+    samples. 3 means average eight samples.

Is this averaging done in the hardware, or the kernel driver?

If it's the latter, this can be left for the kernel to decide.

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 44

Quoted 2 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
+  - atmel,adc-ts-pendet-sensitivity: Pen Detection input pull-up resistor.
+    It can be 0, 1, 2, 3.

I think "pendet" is a bit opaque. "pen-detect" may be better. What physical property does this represent (are these discrete values, or an enumeration)?

Thanks,
Mark.

Quoted 570 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +0 -0
 Optional trigger Nodes:
   - Required properties:
arch/arm/mach-at91/include/mach/at91_adc.h +34 -0
index ab273ee..6d6cc14 100644
@@ -57,10 +57,44 @@
 #define AT91_ADC_IER           0x24            /* Interrupt Enable Register */
 #define AT91_ADC_IDR           0x28            /* Interrupt Disable Register */
 #define AT91_ADC_IMR           0x2C            /* Interrupt Mask Register */
+#define                AT91_ADC_IER_PEN        (1 << 29)
+#define                AT91_ADC_IER_NOPEN      (1 << 30)
+#define                AT91_ADC_IER_XRDY       (1 << 20)
+#define                AT91_ADC_IER_YRDY       (1 << 21)
+#define                AT91_ADC_IER_PRDY       (1 << 22)
+#define                AT91_ADC_ISR_PENS       (1 << 31)
 
 #define AT91_ADC_CHR(n)                (0x30 + ((n) * 4))      /* Channel Data Register N */
 #define                AT91_ADC_DATA           (0x3ff)
 
+#define AT91_ADC_ACR           0x94    /* Analog Control Register */
+#define                AT91_ADC_ACR_PENDETSENS (0x3 << 0)      /* pull-up resistor */
+
+#define AT91_ADC_TSMR          0xB0
+#define                AT91_ADC_TSMR_TSMODE    (3 << 0)        /* Touch Screen Mode */
+#define                        AT91_ADC_TSMR_TSMODE_NONE               (0 << 0)
+#define                        AT91_ADC_TSMR_TSMODE_4WIRE_NO_PRESS     (1 << 0)
+#define                        AT91_ADC_TSMR_TSMODE_4WIRE_PRESS        (2 << 0)
+#define                        AT91_ADC_TSMR_TSMODE_5WIRE              (3 << 0)
+#define                AT91_ADC_TSMR_TSAV      (3 << 4)        /* Averages samples */
+#define                        AT91_ADC_TSMR_TSAV_(x)          ((x) << 4)
+#define                AT91_ADC_TSMR_SCTIM     (0x0f << 16)    /* Switch closure time */
+#define                AT91_ADC_TSMR_PENDBC    (0x0f << 28)    /* Pen Debounce time */
+#define                        AT91_ADC_TSMR_PENDBC_(x)        ((x) << 28)
+#define                AT91_ADC_TSMR_NOTSDMA   (1 << 22)       /* No Touchscreen DMA */
+#define                AT91_ADC_TSMR_PENDET_DIS        (0 << 24)       /* Pen contact detection disable */
+#define                AT91_ADC_TSMR_PENDET_ENA        (1 << 24)       /* Pen contact detection enable */
+
+#define AT91_ADC_TSXPOSR       0xB4
+#define AT91_ADC_TSYPOSR       0xB8
+#define AT91_ADC_TSPRESSR      0xBC
+
 #define AT91_ADC_VERSION       0xFC
 
+/* Trigger Register bit field */
+#define                AT91_ADC_TRGR_TRGPER    (0xffff << 16)
+#define                        AT91_ADC_TRGR_TRGPER_(x)        ((x) << 16)
+#define                AT91_ADC_TRGR_TRGMOD    (0x7 << 0)
+#define                        AT91_ADC_TRGR_MOD_PERIOD_TRIG   (5 << 0)
+
 #endif
drivers/iio/adc/at91_adc.c +365 -25
index 8f1386f..ffc0e42 100644
@@ -11,6 +11,7 @@
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/io.h>
+#include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
@@ -39,12 +40,24 @@
 #define at91_adc_writel(st, reg, val) \
        (writel_relaxed(val, st->reg_base + reg))
 
+#define DRIVER_NAME            "at91_adc"
+#define MAX_POS_BITS           12
+
+#define ZTHRESHOLD             9000
+
 struct at91_adc_caps {
+       bool    has_12bits_xy;  /* true means use 12 bits. Otherwise 10 bits */
        bool    has_tsmr;       /* only at91sam9x5, sama5d3 have TSMR reg */
        u32     mr_prescal_mask;
        u32     mr_startup_mask;
 };
 
+enum atmel_adc_ts_type {
+       ATMEL_ADC_TOUCHSCREEN_NONE = 0,
+       ATMEL_ADC_TOUCHSCREEN_4WIRE,
+       ATMEL_ADC_TOUCHSCREEN_5WIRE,
+};
+
 struct at91_adc_state {
        struct clk              *adc_clk;
        u32                     adc_clk_rate;
@@ -70,6 +83,29 @@ struct at91_adc_state {
        bool                    low_res;        /* the resolution corresponds to the lowest one */
        wait_queue_head_t       wq_data_avail;
        struct at91_adc_caps    caps;
+
+       /*
+        * Following ADC channels are shared by touchscreen:
+        *
+        * CH0 -- Touch screen XP/UL
+        * CH1 -- Touch screen XM/UR
+        * CH2 -- Touch screen YP/LL
+        * CH3 -- Touch screen YM/Sense
+        * CH4 -- Touch screen LR(5-wire only)
+        *
+        * The bitfields below represents the reserved channel in the
+        * touchscreen mode.
+        */
+#define CHAN_MASK_TOUCHSCREEN_4WIRE    (0xf << 0)
+#define CHAN_MASK_TOUCHSCREEN_5WIRE    (0x1f << 0)
+       enum atmel_adc_ts_type  touchscreen_type;
+       struct input_dev                *ts_input;
+
+       u8                      ts_filter_average;
+       u16                     ts_pen_detect_debounce;
+       u8                      ts_pen_detect_sensitivity;
+       u16                     ts_sample_period_time;
+       u16                     ts_sample_period_val;
 };
 
 static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
@@ -104,14 +140,10 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
        return IRQ_HANDLED;
 }
 
-static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
+/* Handler for classic adc channel eoc trigger */
+void handle_adc_eoc_trigger(int irq, struct iio_dev *idev)
 {
-       struct iio_dev *idev = private;
        struct at91_adc_state *st = iio_priv(idev);
-       u32 status = at91_adc_readl(st, st->registers->status_register);
-
-       if (!(status & st->registers->drdy_mask))
-               return IRQ_HANDLED;
 
        if (iio_buffer_enabled(idev)) {
                disable_irq_nosync(irq);
@@ -121,6 +153,115 @@ static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
                st->done = true;
                wake_up_interruptible(&st->wq_data_avail);
        }
+}
+
+static int at91_ts_sample(struct at91_adc_state *st)
+{
+       unsigned int xscale, yscale, reg, z1, z2;
+       unsigned int x, y, pres, xpos, ypos;
+       unsigned int rxp = 1;
+       unsigned int factor = 1000;
+       struct iio_dev *idev = iio_priv_to_dev(st);
+
+       unsigned int xyz_mask_bits = st->caps.has_12bits_xy ? 12 : 10;
+       unsigned int xyz_mask = (1 << xyz_mask_bits) - 1;
+
+       /* calculate position */
+       /* x position = (x / xscale) * max, max = 2^MAX_POS_BITS - 1 */
+       reg = at91_adc_readl(st, AT91_ADC_TSXPOSR);
+       xpos = reg & xyz_mask;
+       x = (xpos << MAX_POS_BITS) - xpos;
+       xscale = (reg >> 16) & xyz_mask;
+       if (xscale != 0)
+               x /= xscale;
+       else
+               dev_err(&idev->dev, "xscale == 0!!!\n");
+
+       /* y position = (y / yscale) * max, max = 2^MAX_POS_BITS - 1 */
+       reg = at91_adc_readl(st, AT91_ADC_TSYPOSR);
+       ypos = reg & xyz_mask;
+       y = (ypos << MAX_POS_BITS) - ypos;
+       yscale = (reg >> 16) & xyz_mask;
+       if (yscale != 0)
+               y /= yscale;
+       else
+               dev_err(&idev->dev, "yscale == 0!!!\n");
+
+       /* calculate the pressure */
+       reg = at91_adc_readl(st, AT91_ADC_TSPRESSR);
+       z1 = reg & xyz_mask;
+       z2 = (reg >> 16) & xyz_mask;
+
+       if (z1 != 0)
+               pres = rxp * (x * factor / 1024) * (z2 * factor / z1 - factor)
+                       / factor;
+       else
+               pres = ZTHRESHOLD;      /* no pen contacted */
+
+       dev_dbg(&idev->dev, "xpos = %d, xscale = %d, ypos = %d, yscale = %d, z1 = %d, z2 = %d, press = %d\n",
+                               xpos, xscale, ypos, yscale, z1, z2, pres);
+
+       if (pres < ZTHRESHOLD) {
+               dev_dbg(&idev->dev, "x = %d, y = %d, pressure = %d\n",
+                                       x, y, pres / factor);
+               input_report_abs(st->ts_input, ABS_X, x);
+               input_report_abs(st->ts_input, ABS_Y, y);
+               input_report_abs(st->ts_input, ABS_PRESSURE, pres);
+               input_report_key(st->ts_input, BTN_TOUCH, 1);
+               input_sync(st->ts_input);
+       } else {
+               dev_dbg(&idev->dev,
+                               "pressure too low: not reporting\n");
+       }
+
+       return 0;
+}
+
+
+static irqreturn_t at91_adc_interrupt(int irq, void *private)
+{
+       struct iio_dev *idev = private;
+       struct at91_adc_state *st = iio_priv(idev);
+       u32 status = at91_adc_readl(st, st->registers->status_register);
+       const uint32_t ts_data_irq_mask =
+               AT91_ADC_IER_XRDY |
+               AT91_ADC_IER_YRDY |
+               AT91_ADC_IER_PRDY;
+
+       if (status & st->registers->drdy_mask)
+               handle_adc_eoc_trigger(irq, idev);
+
+       if (status & AT91_ADC_IER_PEN) {
+               at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
+               at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_NOPEN |
+                       ts_data_irq_mask);
+               /* Set up period trigger for sampling */
+               at91_adc_writel(st, st->registers->trigger_register,
+                       AT91_ADC_TRGR_MOD_PERIOD_TRIG |
+                       AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val));
+       } else if (status & AT91_ADC_IER_NOPEN) {
+               at91_adc_writel(st, st->registers->trigger_register, 0);
+               at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_NOPEN |
+                       ts_data_irq_mask);
+               at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
+
+               input_report_key(st->ts_input, BTN_TOUCH, 0);
+               input_sync(st->ts_input);
+       } else if ((status & ts_data_irq_mask) == ts_data_irq_mask) {
+               /* Now all touchscreen data is ready */
+
+               if (status & AT91_ADC_ISR_PENS) {
+                       /* validate data by pen contact */
+                       at91_ts_sample(st);
+               } else {
+                       /* triggered by event that is no pen contact, just read
+                        * them to clean the interrupt and discard all.
+                        */
+                       at91_adc_readl(st, AT91_ADC_TSXPOSR);
+                       at91_adc_readl(st, AT91_ADC_TSYPOSR);
+                       at91_adc_readl(st, AT91_ADC_TSPRESSR);
+               }
+       }
 
        return IRQ_HANDLED;
 }
@@ -130,6 +271,16 @@ static int at91_adc_channel_init(struct iio_dev *idev)
        struct at91_adc_state *st = iio_priv(idev);
        struct iio_chan_spec *chan_array, *timestamp;
        int bit, idx = 0;
+       unsigned long rsvd_mask = 0;
+
+       /* If touchscreen is enable, then reserve the adc channels */
+       if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
+               rsvd_mask = CHAN_MASK_TOUCHSCREEN_4WIRE;
+       else if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_5WIRE)
+               rsvd_mask = CHAN_MASK_TOUCHSCREEN_5WIRE;
+
+       /* set up the channel mask to reserve touchscreen channels */
+       st->channels_mask &= ~rsvd_mask;
 
        idev->num_channels = bitmap_weight(&st->channels_mask,
                                           st->num_channels) + 1;
@@ -561,6 +712,57 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
                i++;
        }
 
+       /* Check if touchscreen is enabled in DT. */
+       ret = of_property_read_u32(node, "atmel,adc-touchscreen-wires", &prop);
+       if (ret)
+               dev_info(&idev->dev, "Touchscreen not enabled.\n");
+       else if (prop == 4)
+               st->touchscreen_type = ATMEL_ADC_TOUCHSCREEN_4WIRE;
+       else if (prop == 5)
+               st->touchscreen_type = ATMEL_ADC_TOUCHSCREEN_5WIRE;
+       else
+               dev_warn(&idev->dev, "Unsupported number of touchscreen wires (%d)\n",
+                               prop);
+
+       if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_NONE)
+               return 0;
+
+       /* Touch screen is enabled, so check touch screen dt parameters */
+       if (of_property_read_u32(node, "atmel,adc-ts-filter-average", &prop)) {
+               dev_err(&idev->dev, "Missing atmel,adc-ts-filtering-average property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       st->ts_filter_average = prop;
+       if (st->ts_filter_average > 3) {
+               dev_err(&idev->dev, "Invalid atmel,adc-ts-filtering-average property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+
+       prop = 0;
+       of_property_read_u32(node, "atmel,adc-ts-pendet-debounce", &prop);
+       st->ts_pen_detect_debounce = prop;
+
+       /* default sample period is 2ms. The real touch sample period should be
+        * this period * TSFREQ.
+        */
+       prop = 2000;
+       of_property_read_u32(node, "atmel,adc-ts-sample-period-time", &prop);
+       st->ts_sample_period_time = prop;
+
+       if (of_property_read_u32(node, "atmel,adc-ts-pendet-sensitivity", &prop)) {
+               dev_err(&idev->dev, "Missing atmel,adc-ts-pendet-sensitivity property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       st->ts_pen_detect_sensitivity = prop;
+       if (st->ts_pen_detect_sensitivity > 3) {
+               dev_err(&idev->dev, "Invalid atmel,adc-ts-pendet-sensitivity property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+
        return 0;
 
 error_ret:
@@ -592,6 +794,114 @@ static const struct iio_info at91_adc_info = {
        .read_raw = &at91_adc_read_raw,
 };
 
+/* Touchscreen related functions */
+static int atmel_ts_open(struct input_dev *dev)
+{
+       struct at91_adc_state *st = input_get_drvdata(dev);
+
+       at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
+       return 0;
+}
+
+static void atmel_ts_close(struct input_dev *dev)
+{
+       struct at91_adc_state *st = input_get_drvdata(dev);
+
+       at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
+}
+
+static int at91_ts_hw_init(struct at91_adc_state *st, u32 adc_clk_khz)
+{
+       u32 reg = 0, pendbc;
+       int i = 0;
+
+       if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
+               reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS;
+       else
+               reg = AT91_ADC_TSMR_TSMODE_5WIRE;
+
+       /* a Pen Detect Debounce Time is necessary for the ADC Touch to avoid
+        * pen detect noise.
+        * The formula is : Pen Detect Debounce Time = (2 ^ pendbc) / ADCClock
+        */
+       pendbc = round_up(st->ts_pen_detect_debounce * adc_clk_khz / 1000, 1);
+
+       while (pendbc >> ++i)
+               ;       /* Empty! Find the shift offset */
+       if (abs(pendbc - (1 << i)) < abs(pendbc - (1 << (i - 1))))
+               pendbc = i;
+       else
+               pendbc = i - 1;
+
+       if (st->caps.has_tsmr) {
+               reg |= AT91_ADC_TSMR_TSAV_(st->ts_filter_average)
+                               & AT91_ADC_TSMR_TSAV;
+               reg |= AT91_ADC_TSMR_PENDBC_(pendbc) & AT91_ADC_TSMR_PENDBC;
+               reg |= AT91_ADC_TSMR_NOTSDMA;
+               reg |= AT91_ADC_TSMR_PENDET_ENA;
+               reg |= 0x03 << 8;       /* TSFREQ, need bigger than TSAV */
+
+               at91_adc_writel(st, AT91_ADC_TSMR, reg);
+       } else {
+               /* TODO: for 9g45 which has no TSMR */
+       }
+
+       /* Change adc internal resistor value for better pen detection,
+        * default value is 100 kOhm.
+        * 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm
+        * option only available on ES2 and higher
+        */
+       at91_adc_writel(st, AT91_ADC_ACR, st->ts_pen_detect_sensitivity
+                       & AT91_ADC_ACR_PENDETSENS);
+
+       /* Sample Peroid Time = (TRGPER + 1) / ADCClock */
+       st->ts_sample_period_val = round_up((st->ts_sample_period_time *
+                       adc_clk_khz / 1000) - 1, 1);
+
+       return 0;
+}
+
+static int at91_ts_register(struct at91_adc_state *st)
+{
+       struct input_dev *input;
+       struct iio_dev *idev = iio_priv_to_dev(st);
+       int ret;
+
+       input = input_allocate_device();
+       if (!input) {
+               dev_err(&idev->dev, "Failed to allocate TS device!\n");
+               return -ENOMEM;
+       }
+
+       input->name = DRIVER_NAME;
+       input->id.bustype = BUS_HOST;
+       input->dev.parent = idev->dev.parent;
+       input->open = atmel_ts_open;
+       input->close = atmel_ts_close;
+
+       __set_bit(EV_ABS, input->evbit);
+       __set_bit(EV_KEY, input->evbit);
+       __set_bit(BTN_TOUCH, input->keybit);
+       input_set_abs_params(input, ABS_X, 0, (1 << MAX_POS_BITS) - 1, 0, 0);
+       input_set_abs_params(input, ABS_Y, 0, (1 << MAX_POS_BITS) - 1, 0, 0);
+       input_set_abs_params(input, ABS_PRESSURE, 0, 0xffffff, 0, 0);
+
+       st->ts_input = input;
+       input_set_drvdata(input, st);
+
+       ret = input_register_device(input);
+       if (ret)
+               input_free_device(st->ts_input);
+
+       return ret;
+}
+
+static void at91_ts_unregister(struct at91_adc_state *st)
+{
+       input_unregister_device(st->ts_input);
+       input_free_device(st->ts_input);
+}
+
 /*
  * Since atmel adc support different ip for touchscreen mode. Through the
  * IP check, we will know the touchscreen capbilities.
@@ -610,6 +920,7 @@ static void atmel_adc_get_cap(struct at91_adc_state *st)
        /* keep only major version number */
        switch (version & 0xf00) {
        case 0x500:     /* SAMA5D3 */
+               st->caps.has_12bits_xy = 1;
        case 0x400:     /* AT91SAM9X5/9N12 */
                st->caps.has_tsmr = 1;
                st->caps.mr_startup_mask = AT91_ADC_STARTUP;
@@ -683,7 +994,7 @@ static int at91_adc_probe(struct platform_device *pdev)
        at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_SWRST);
        at91_adc_writel(st, AT91_ADC_IDR, 0xFFFFFFFF);
        ret = request_irq(st->irq,
-                         at91_adc_eoc_trigger,
+                         at91_adc_interrupt,
                          0,
                          pdev->dev.driver->name,
                          idev);
@@ -731,6 +1042,10 @@ static int at91_adc_probe(struct platform_device *pdev)
        adc_clk = st->adc_clk_rate ?
                st->adc_clk_rate : clk_get_rate(st->adc_clk);
        adc_clk_khz = adc_clk / 1000;
+
+       dev_dbg(&pdev->dev, "Master clock is set as: %d Hz, adc_clk should set as: %d Hz\n",
+               mstrclk, adc_clk);
+
        prsc = (mstrclk / (2 * adc_clk)) - 1;
 
        if (!st->startup_time) {
@@ -799,30 +1114,52 @@ static int at91_adc_probe(struct platform_device *pdev)
        init_waitqueue_head(&st->wq_data_avail);
        mutex_init(&st->lock);
 
-       ret = at91_adc_buffer_init(idev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
-               goto error_disable_adc_clk;
-       }
+       /*
+        * Since touch screen will set trigger register as period trigger. So
+        * when touch screen is enabled, then we have to disable hardware
+        * trigger for classic adc.
+        */
+       if (!st->touchscreen_type) {
+               ret = at91_adc_buffer_init(idev);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
+                       goto error_disable_adc_clk;
+               }
 
-       ret = at91_adc_trigger_init(idev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
-               goto error_unregister_buffer;
+               ret = at91_adc_trigger_init(idev);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
+                       at91_adc_buffer_remove(idev);
+                       goto error_disable_adc_clk;
+               }
+       } else {
+               if (!st->caps.has_tsmr) {
+                       dev_err(&pdev->dev, "We don't support non-TSMR adc\n");
+                       goto error_disable_adc_clk;
+               }
+
+               ret = at91_ts_register(st);
+               if (ret)
+                       goto error_disable_adc_clk;
+
+               at91_ts_hw_init(st, adc_clk_khz);
        }
 
        ret = iio_device_register(idev);
        if (ret < 0) {
                dev_err(&pdev->dev, "Couldn't register the device.\n");
-               goto error_remove_triggers;
+               goto error_iio_device_register;
        }
 
        return 0;
 
-error_remove_triggers:
-       at91_adc_trigger_remove(idev);
-error_unregister_buffer:
-       at91_adc_buffer_remove(idev);
+error_iio_device_register:
+       if (!st->touchscreen_type) {
+               at91_adc_trigger_remove(idev);
+               at91_adc_buffer_remove(idev);
+       } else {
+               at91_ts_unregister(st);
+       }
 error_disable_adc_clk:
        clk_disable_unprepare(st->adc_clk);
 error_disable_clk:
@@ -841,8 +1178,12 @@ static int at91_adc_remove(struct platform_device *pdev)
        struct at91_adc_state *st = iio_priv(idev);
 
        iio_device_unregister(idev);
-       at91_adc_trigger_remove(idev);
-       at91_adc_buffer_remove(idev);
+       if (!st->touchscreen_type) {
+               at91_adc_trigger_remove(idev);
+               at91_adc_buffer_remove(idev);
+       } else {
+               at91_ts_unregister(st);
+       }
        clk_disable_unprepare(st->adc_clk);
        clk_disable_unprepare(st->clk);
        free_irq(st->irq, idev);
@@ -861,7 +1202,7 @@ static struct platform_driver at91_adc_driver = {
        .probe = at91_adc_probe,
        .remove = at91_adc_remove,
        .driver = {
-                  .name = "at91_adc",
+                  .name = DRIVER_NAME,
                   .of_match_table = of_match_ptr(at91_adc_dt_ids),
        },
 };
--

1.7.10

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

author
#36 lore raw

PATCH 5/5 iio: at91: introduce touch screen support in iio adc driver

Hi, Dear Mark

Quoted 21 lines · Mark Rutland
Quoted 20 lines · Josh Wu

AT91 ADC hardware integrate touch screen support. So this patch add touch screen support for at91 adc iio driver.
To enable touch screen support in adc, you need to add the dt parameters:
which type of touch are used? (4 or 5 wires), sample period time, pen detect debounce time, average samples and pen detect resistor.

In the meantime, since touch screen will use a interal period trigger of adc, so it is conflict to other hardware triggers. Driver will disable the hardware trigger support if touch screen is enabled.

This driver has been tested in AT91SAM9X5-EK and SAMA5D3x-EK.

Cc Dmitry Torokhov <[email protected]>
---
  .../devicetree/bindings/arm/atmel-adc.txt          |   13 +
  arch/arm/mach-at91/include/mach/at91_adc.h         |   34 ++
  drivers/iio/adc/at91_adc.c                         |  389 ++++++++++++++++++--
  3 files changed, 412 insertions(+), 24 deletions(-)
Quoted 12 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 33

Quoted 10 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
index 0db2945..925d656 100644
@@ -29,6 +29,19 @@ Optional properties:
    - atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
    - atmel,adc-clock-rate: ADC clock rate. If not specified, use the default
                           adc_op_clk.
+  - atmel,adc-touchscreen-wires: Number of touch screen wires. Only support
+                                4 and 5 wires touch screen.

Are 4 and 5 wire configurations that can exist, or the only ones supported by the driver?

It can be set:

atmel,adc-touchscreen-wires = <4>;
or
atmel,adc-touchscreen-wires = <5>;

according to your touch screen.

Quoted 8 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 37

Quoted 5 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +4 -0
+    NOTE: when adc touch screen enabled, the adc hardware trigger will be
+          disabled. Since touch screen will occupied the trigger register.
+  - atmel,adc-ts-pendet-debounce: Debounce time in microsecond for touch pen
+                                 detect.

For consistency with the adc-touchscreen-wires property, and other properties with timing information, how about atmel,adc-touchscreen-debounce-delay-us ?

sound nice to me.

Quoted 8 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 39

Quoted 3 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
+  - atmel,adc-ts-sample-period-time: Sample Period Time in microsecond for
+                                    touch screen

Again, please be consistent with ts or touchscreen. It may also be good to have -us to make the units explicit (though it does leave the property name being quite a mothful):

atmel,adc-touchscreen-sample-period-us ?

nice. I will use this one.

Quoted 5 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 42

Quoted 4 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +3 -0
+  - atmel,adc-ts-filter-average: Numbers of sampling data will be averaged.
+    0 means no average. 1 means average two samples. 2 means average four
+    samples. 3 means average eight samples.

Is this averaging done in the hardware, or the kernel driver?

It is done in the hardware.
But for some soc, like AT91SAM9G45, hardware doesn't support hardware average.
So I am wondering use it as for both hardware and softer average.

BTW, you mentioned the kernel driver, do you mean a filter algorithm is already implemented in kernel library?

Quoted 6 lines

If it's the latter, this can be left for the kernel to decide.

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 44

Quoted 2 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
+  - atmel,adc-ts-pendet-sensitivity: Pen Detection input pull-up resistor.
+    It can be 0, 1, 2, 3.

I think "pendet" is a bit opaque. "pen-detect" may be better.

yes. I'll change this.

Quoted 3 lines

What
physical property does this represent (are these discrete values, or an enumeration)?

This property is supported by hardware, it can change the adc internal resistor value for better pen detection,
* 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm
In general, we just use default value 2 for 100kOhm.

I think I need add more detail infomation in the dt document.

Thanks again,
Best Regards,
Josh Wu

Quoted 573 lines

Thanks,
Mark.

Quoted 569 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +0 -0
  Optional trigger Nodes:
    - Required properties:
arch/arm/mach-at91/include/mach/at91_adc.h +34 -0
index ab273ee..6d6cc14 100644
@@ -57,10 +57,44 @@
  #define AT91_ADC_IER           0x24            /* Interrupt Enable Register */
  #define AT91_ADC_IDR           0x28            /* Interrupt Disable Register */
  #define AT91_ADC_IMR           0x2C            /* Interrupt Mask Register */
+#define                AT91_ADC_IER_PEN        (1 << 29)
+#define                AT91_ADC_IER_NOPEN      (1 << 30)
+#define                AT91_ADC_IER_XRDY       (1 << 20)
+#define                AT91_ADC_IER_YRDY       (1 << 21)
+#define                AT91_ADC_IER_PRDY       (1 << 22)
+#define                AT91_ADC_ISR_PENS       (1 << 31)
 
  #define AT91_ADC_CHR(n)                (0x30 + ((n) * 4))      /* Channel Data Register N */
  #define                AT91_ADC_DATA           (0x3ff)
 
+#define AT91_ADC_ACR           0x94    /* Analog Control Register */
+#define                AT91_ADC_ACR_PENDETSENS (0x3 << 0)      /* pull-up resistor */
+
+#define AT91_ADC_TSMR          0xB0
+#define                AT91_ADC_TSMR_TSMODE    (3 << 0)        /* Touch Screen Mode */
+#define                        AT91_ADC_TSMR_TSMODE_NONE               (0 << 0)
+#define                        AT91_ADC_TSMR_TSMODE_4WIRE_NO_PRESS     (1 << 0)
+#define                        AT91_ADC_TSMR_TSMODE_4WIRE_PRESS        (2 << 0)
+#define                        AT91_ADC_TSMR_TSMODE_5WIRE              (3 << 0)
+#define                AT91_ADC_TSMR_TSAV      (3 << 4)        /* Averages samples */
+#define                        AT91_ADC_TSMR_TSAV_(x)          ((x) << 4)
+#define                AT91_ADC_TSMR_SCTIM     (0x0f << 16)    /* Switch closure time */
+#define                AT91_ADC_TSMR_PENDBC    (0x0f << 28)    /* Pen Debounce time */
+#define                        AT91_ADC_TSMR_PENDBC_(x)        ((x) << 28)
+#define                AT91_ADC_TSMR_NOTSDMA   (1 << 22)       /* No Touchscreen DMA */
+#define                AT91_ADC_TSMR_PENDET_DIS        (0 << 24)       /* Pen contact detection disable */
+#define                AT91_ADC_TSMR_PENDET_ENA        (1 << 24)       /* Pen contact detection enable */
+
+#define AT91_ADC_TSXPOSR       0xB4
+#define AT91_ADC_TSYPOSR       0xB8
+#define AT91_ADC_TSPRESSR      0xBC
+
  #define AT91_ADC_VERSION       0xFC
 
+/* Trigger Register bit field */
+#define                AT91_ADC_TRGR_TRGPER    (0xffff << 16)
+#define                        AT91_ADC_TRGR_TRGPER_(x)        ((x) << 16)
+#define                AT91_ADC_TRGR_TRGMOD    (0x7 << 0)
+#define                        AT91_ADC_TRGR_MOD_PERIOD_TRIG   (5 << 0)
+
  #endif
drivers/iio/adc/at91_adc.c +365 -25
index 8f1386f..ffc0e42 100644
@@ -11,6 +11,7 @@
  #include <linux/clk.h>
  #include <linux/err.h>
  #include <linux/io.h>
+#include <linux/input.h>
  #include <linux/interrupt.h>
  #include <linux/jiffies.h>
  #include <linux/kernel.h>
@@ -39,12 +40,24 @@
  #define at91_adc_writel(st, reg, val) \
         (writel_relaxed(val, st->reg_base + reg))
 
+#define DRIVER_NAME            "at91_adc"
+#define MAX_POS_BITS           12
+
+#define ZTHRESHOLD             9000
+
  struct at91_adc_caps {
+       bool    has_12bits_xy;  /* true means use 12 bits. Otherwise 10 bits */
         bool    has_tsmr;       /* only at91sam9x5, sama5d3 have TSMR reg */
         u32     mr_prescal_mask;
         u32     mr_startup_mask;
  };
 
+enum atmel_adc_ts_type {
+       ATMEL_ADC_TOUCHSCREEN_NONE = 0,
+       ATMEL_ADC_TOUCHSCREEN_4WIRE,
+       ATMEL_ADC_TOUCHSCREEN_5WIRE,
+};
+
  struct at91_adc_state {
         struct clk              *adc_clk;
         u32                     adc_clk_rate;
@@ -70,6 +83,29 @@ struct at91_adc_state {
         bool                    low_res;        /* the resolution corresponds to the lowest one */
         wait_queue_head_t       wq_data_avail;
         struct at91_adc_caps    caps;
+
+       /*
+        * Following ADC channels are shared by touchscreen:
+        *
+        * CH0 -- Touch screen XP/UL
+        * CH1 -- Touch screen XM/UR
+        * CH2 -- Touch screen YP/LL
+        * CH3 -- Touch screen YM/Sense
+        * CH4 -- Touch screen LR(5-wire only)
+        *
+        * The bitfields below represents the reserved channel in the
+        * touchscreen mode.
+        */
+#define CHAN_MASK_TOUCHSCREEN_4WIRE    (0xf << 0)
+#define CHAN_MASK_TOUCHSCREEN_5WIRE    (0x1f << 0)
+       enum atmel_adc_ts_type  touchscreen_type;
+       struct input_dev                *ts_input;
+
+       u8                      ts_filter_average;
+       u16                     ts_pen_detect_debounce;
+       u8                      ts_pen_detect_sensitivity;
+       u16                     ts_sample_period_time;
+       u16                     ts_sample_period_val;
  };
 
  static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
@@ -104,14 +140,10 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
         return IRQ_HANDLED;
  }
 
-static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
+/* Handler for classic adc channel eoc trigger */
+void handle_adc_eoc_trigger(int irq, struct iio_dev *idev)
  {
-       struct iio_dev *idev = private;
         struct at91_adc_state *st = iio_priv(idev);
-       u32 status = at91_adc_readl(st, st->registers->status_register);
-
-       if (!(status & st->registers->drdy_mask))
-               return IRQ_HANDLED;
 
         if (iio_buffer_enabled(idev)) {
                 disable_irq_nosync(irq);
@@ -121,6 +153,115 @@ static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
                 st->done = true;
                 wake_up_interruptible(&st->wq_data_avail);
         }
+}
+
+static int at91_ts_sample(struct at91_adc_state *st)
+{
+       unsigned int xscale, yscale, reg, z1, z2;
+       unsigned int x, y, pres, xpos, ypos;
+       unsigned int rxp = 1;
+       unsigned int factor = 1000;
+       struct iio_dev *idev = iio_priv_to_dev(st);
+
+       unsigned int xyz_mask_bits = st->caps.has_12bits_xy ? 12 : 10;
+       unsigned int xyz_mask = (1 << xyz_mask_bits) - 1;
+
+       /* calculate position */
+       /* x position = (x / xscale) * max, max = 2^MAX_POS_BITS - 1 */
+       reg = at91_adc_readl(st, AT91_ADC_TSXPOSR);
+       xpos = reg & xyz_mask;
+       x = (xpos << MAX_POS_BITS) - xpos;
+       xscale = (reg >> 16) & xyz_mask;
+       if (xscale != 0)
+               x /= xscale;
+       else
+               dev_err(&idev->dev, "xscale == 0!!!\n");
+
+       /* y position = (y / yscale) * max, max = 2^MAX_POS_BITS - 1 */
+       reg = at91_adc_readl(st, AT91_ADC_TSYPOSR);
+       ypos = reg & xyz_mask;
+       y = (ypos << MAX_POS_BITS) - ypos;
+       yscale = (reg >> 16) & xyz_mask;
+       if (yscale != 0)
+               y /= yscale;
+       else
+               dev_err(&idev->dev, "yscale == 0!!!\n");
+
+       /* calculate the pressure */
+       reg = at91_adc_readl(st, AT91_ADC_TSPRESSR);
+       z1 = reg & xyz_mask;
+       z2 = (reg >> 16) & xyz_mask;
+
+       if (z1 != 0)
+               pres = rxp * (x * factor / 1024) * (z2 * factor / z1 - factor)
+                       / factor;
+       else
+               pres = ZTHRESHOLD;      /* no pen contacted */
+
+       dev_dbg(&idev->dev, "xpos = %d, xscale = %d, ypos = %d, yscale = %d, z1 = %d, z2 = %d, press = %d\n",
+                               xpos, xscale, ypos, yscale, z1, z2, pres);
+
+       if (pres < ZTHRESHOLD) {
+               dev_dbg(&idev->dev, "x = %d, y = %d, pressure = %d\n",
+                                       x, y, pres / factor);
+               input_report_abs(st->ts_input, ABS_X, x);
+               input_report_abs(st->ts_input, ABS_Y, y);
+               input_report_abs(st->ts_input, ABS_PRESSURE, pres);
+               input_report_key(st->ts_input, BTN_TOUCH, 1);
+               input_sync(st->ts_input);
+       } else {
+               dev_dbg(&idev->dev,
+                               "pressure too low: not reporting\n");
+       }
+
+       return 0;
+}
+
+
+static irqreturn_t at91_adc_interrupt(int irq, void *private)
+{
+       struct iio_dev *idev = private;
+       struct at91_adc_state *st = iio_priv(idev);
+       u32 status = at91_adc_readl(st, st->registers->status_register);
+       const uint32_t ts_data_irq_mask =
+               AT91_ADC_IER_XRDY |
+               AT91_ADC_IER_YRDY |
+               AT91_ADC_IER_PRDY;
+
+       if (status & st->registers->drdy_mask)
+               handle_adc_eoc_trigger(irq, idev);
+
+       if (status & AT91_ADC_IER_PEN) {
+               at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
+               at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_NOPEN |
+                       ts_data_irq_mask);
+               /* Set up period trigger for sampling */
+               at91_adc_writel(st, st->registers->trigger_register,
+                       AT91_ADC_TRGR_MOD_PERIOD_TRIG |
+                       AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val));
+       } else if (status & AT91_ADC_IER_NOPEN) {
+               at91_adc_writel(st, st->registers->trigger_register, 0);
+               at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_NOPEN |
+                       ts_data_irq_mask);
+               at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
+
+               input_report_key(st->ts_input, BTN_TOUCH, 0);
+               input_sync(st->ts_input);
+       } else if ((status & ts_data_irq_mask) == ts_data_irq_mask) {
+               /* Now all touchscreen data is ready */
+
+               if (status & AT91_ADC_ISR_PENS) {
+                       /* validate data by pen contact */
+                       at91_ts_sample(st);
+               } else {
+                       /* triggered by event that is no pen contact, just read
+                        * them to clean the interrupt and discard all.
+                        */
+                       at91_adc_readl(st, AT91_ADC_TSXPOSR);
+                       at91_adc_readl(st, AT91_ADC_TSYPOSR);
+                       at91_adc_readl(st, AT91_ADC_TSPRESSR);
+               }
+       }
 
         return IRQ_HANDLED;
  }
@@ -130,6 +271,16 @@ static int at91_adc_channel_init(struct iio_dev *idev)
         struct at91_adc_state *st = iio_priv(idev);
         struct iio_chan_spec *chan_array, *timestamp;
         int bit, idx = 0;
+       unsigned long rsvd_mask = 0;
+
+       /* If touchscreen is enable, then reserve the adc channels */
+       if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
+               rsvd_mask = CHAN_MASK_TOUCHSCREEN_4WIRE;
+       else if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_5WIRE)
+               rsvd_mask = CHAN_MASK_TOUCHSCREEN_5WIRE;
+
+       /* set up the channel mask to reserve touchscreen channels */
+       st->channels_mask &= ~rsvd_mask;
 
         idev->num_channels = bitmap_weight(&st->channels_mask,
                                            st->num_channels) + 1;
@@ -561,6 +712,57 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
                 i++;
         }
 
+       /* Check if touchscreen is enabled in DT. */
+       ret = of_property_read_u32(node, "atmel,adc-touchscreen-wires", &prop);
+       if (ret)
+               dev_info(&idev->dev, "Touchscreen not enabled.\n");
+       else if (prop == 4)
+               st->touchscreen_type = ATMEL_ADC_TOUCHSCREEN_4WIRE;
+       else if (prop == 5)
+               st->touchscreen_type = ATMEL_ADC_TOUCHSCREEN_5WIRE;
+       else
+               dev_warn(&idev->dev, "Unsupported number of touchscreen wires (%d)\n",
+                               prop);
+
+       if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_NONE)
+               return 0;
+
+       /* Touch screen is enabled, so check touch screen dt parameters */
+       if (of_property_read_u32(node, "atmel,adc-ts-filter-average", &prop)) {
+               dev_err(&idev->dev, "Missing atmel,adc-ts-filtering-average property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       st->ts_filter_average = prop;
+       if (st->ts_filter_average > 3) {
+               dev_err(&idev->dev, "Invalid atmel,adc-ts-filtering-average property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+
+       prop = 0;
+       of_property_read_u32(node, "atmel,adc-ts-pendet-debounce", &prop);
+       st->ts_pen_detect_debounce = prop;
+
+       /* default sample period is 2ms. The real touch sample period should be
+        * this period * TSFREQ.
+        */
+       prop = 2000;
+       of_property_read_u32(node, "atmel,adc-ts-sample-period-time", &prop);
+       st->ts_sample_period_time = prop;
+
+       if (of_property_read_u32(node, "atmel,adc-ts-pendet-sensitivity", &prop)) {
+               dev_err(&idev->dev, "Missing atmel,adc-ts-pendet-sensitivity property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       st->ts_pen_detect_sensitivity = prop;
+       if (st->ts_pen_detect_sensitivity > 3) {
+               dev_err(&idev->dev, "Invalid atmel,adc-ts-pendet-sensitivity property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+
         return 0;
 
  error_ret:
@@ -592,6 +794,114 @@ static const struct iio_info at91_adc_info = {
         .read_raw = &at91_adc_read_raw,
  };
 
+/* Touchscreen related functions */
+static int atmel_ts_open(struct input_dev *dev)
+{
+       struct at91_adc_state *st = input_get_drvdata(dev);
+
+       at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
+       return 0;
+}
+
+static void atmel_ts_close(struct input_dev *dev)
+{
+       struct at91_adc_state *st = input_get_drvdata(dev);
+
+       at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
+}
+
+static int at91_ts_hw_init(struct at91_adc_state *st, u32 adc_clk_khz)
+{
+       u32 reg = 0, pendbc;
+       int i = 0;
+
+       if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
+               reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS;
+       else
+               reg = AT91_ADC_TSMR_TSMODE_5WIRE;
+
+       /* a Pen Detect Debounce Time is necessary for the ADC Touch to avoid
+        * pen detect noise.
+        * The formula is : Pen Detect Debounce Time = (2 ^ pendbc) / ADCClock
+        */
+       pendbc = round_up(st->ts_pen_detect_debounce * adc_clk_khz / 1000, 1);
+
+       while (pendbc >> ++i)
+               ;       /* Empty! Find the shift offset */
+       if (abs(pendbc - (1 << i)) < abs(pendbc - (1 << (i - 1))))
+               pendbc = i;
+       else
+               pendbc = i - 1;
+
+       if (st->caps.has_tsmr) {
+               reg |= AT91_ADC_TSMR_TSAV_(st->ts_filter_average)
+                               & AT91_ADC_TSMR_TSAV;
+               reg |= AT91_ADC_TSMR_PENDBC_(pendbc) & AT91_ADC_TSMR_PENDBC;
+               reg |= AT91_ADC_TSMR_NOTSDMA;
+               reg |= AT91_ADC_TSMR_PENDET_ENA;
+               reg |= 0x03 << 8;       /* TSFREQ, need bigger than TSAV */
+
+               at91_adc_writel(st, AT91_ADC_TSMR, reg);
+       } else {
+               /* TODO: for 9g45 which has no TSMR */
+       }
+
+       /* Change adc internal resistor value for better pen detection,
+        * default value is 100 kOhm.
+        * 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm
+        * option only available on ES2 and higher
+        */
+       at91_adc_writel(st, AT91_ADC_ACR, st->ts_pen_detect_sensitivity
+                       & AT91_ADC_ACR_PENDETSENS);
+
+       /* Sample Peroid Time = (TRGPER + 1) / ADCClock */
+       st->ts_sample_period_val = round_up((st->ts_sample_period_time *
+                       adc_clk_khz / 1000) - 1, 1);
+
+       return 0;
+}
+
+static int at91_ts_register(struct at91_adc_state *st)
+{
+       struct input_dev *input;
+       struct iio_dev *idev = iio_priv_to_dev(st);
+       int ret;
+
+       input = input_allocate_device();
+       if (!input) {
+               dev_err(&idev->dev, "Failed to allocate TS device!\n");
+               return -ENOMEM;
+       }
+
+       input->name = DRIVER_NAME;
+       input->id.bustype = BUS_HOST;
+       input->dev.parent = idev->dev.parent;
+       input->open = atmel_ts_open;
+       input->close = atmel_ts_close;
+
+       __set_bit(EV_ABS, input->evbit);
+       __set_bit(EV_KEY, input->evbit);
+       __set_bit(BTN_TOUCH, input->keybit);
+       input_set_abs_params(input, ABS_X, 0, (1 << MAX_POS_BITS) - 1, 0, 0);
+       input_set_abs_params(input, ABS_Y, 0, (1 << MAX_POS_BITS) - 1, 0, 0);
+       input_set_abs_params(input, ABS_PRESSURE, 0, 0xffffff, 0, 0);
+
+       st->ts_input = input;
+       input_set_drvdata(input, st);
+
+       ret = input_register_device(input);
+       if (ret)
+               input_free_device(st->ts_input);
+
+       return ret;
+}
+
+static void at91_ts_unregister(struct at91_adc_state *st)
+{
+       input_unregister_device(st->ts_input);
+       input_free_device(st->ts_input);
+}
+
  /*
   * Since atmel adc support different ip for touchscreen mode. Through the
   * IP check, we will know the touchscreen capbilities.
@@ -610,6 +920,7 @@ static void atmel_adc_get_cap(struct at91_adc_state *st)
         /* keep only major version number */
         switch (version & 0xf00) {
         case 0x500:     /* SAMA5D3 */
+               st->caps.has_12bits_xy = 1;
         case 0x400:     /* AT91SAM9X5/9N12 */
                 st->caps.has_tsmr = 1;
                 st->caps.mr_startup_mask = AT91_ADC_STARTUP;
@@ -683,7 +994,7 @@ static int at91_adc_probe(struct platform_device *pdev)
         at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_SWRST);
         at91_adc_writel(st, AT91_ADC_IDR, 0xFFFFFFFF);
         ret = request_irq(st->irq,
-                         at91_adc_eoc_trigger,
+                         at91_adc_interrupt,
                           0,
                           pdev->dev.driver->name,
                           idev);
@@ -731,6 +1042,10 @@ static int at91_adc_probe(struct platform_device *pdev)
         adc_clk = st->adc_clk_rate ?
                 st->adc_clk_rate : clk_get_rate(st->adc_clk);
         adc_clk_khz = adc_clk / 1000;
+
+       dev_dbg(&pdev->dev, "Master clock is set as: %d Hz, adc_clk should set as: %d Hz\n",
+               mstrclk, adc_clk);
+
         prsc = (mstrclk / (2 * adc_clk)) - 1;
 
         if (!st->startup_time) {
@@ -799,30 +1114,52 @@ static int at91_adc_probe(struct platform_device *pdev)
         init_waitqueue_head(&st->wq_data_avail);
         mutex_init(&st->lock);
 
-       ret = at91_adc_buffer_init(idev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
-               goto error_disable_adc_clk;
-       }
+       /*
+        * Since touch screen will set trigger register as period trigger. So
+        * when touch screen is enabled, then we have to disable hardware
+        * trigger for classic adc.
+        */
+       if (!st->touchscreen_type) {
+               ret = at91_adc_buffer_init(idev);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
+                       goto error_disable_adc_clk;
+               }
 
-       ret = at91_adc_trigger_init(idev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
-               goto error_unregister_buffer;
+               ret = at91_adc_trigger_init(idev);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
+                       at91_adc_buffer_remove(idev);
+                       goto error_disable_adc_clk;
+               }
+       } else {
+               if (!st->caps.has_tsmr) {
+                       dev_err(&pdev->dev, "We don't support non-TSMR adc\n");
+                       goto error_disable_adc_clk;
+               }
+
+               ret = at91_ts_register(st);
+               if (ret)
+                       goto error_disable_adc_clk;
+
+               at91_ts_hw_init(st, adc_clk_khz);
         }
 
         ret = iio_device_register(idev);
         if (ret < 0) {
                 dev_err(&pdev->dev, "Couldn't register the device.\n");
-               goto error_remove_triggers;
+               goto error_iio_device_register;
         }
 
         return 0;
 
-error_remove_triggers:
-       at91_adc_trigger_remove(idev);
-error_unregister_buffer:
-       at91_adc_buffer_remove(idev);
+error_iio_device_register:
+       if (!st->touchscreen_type) {
+               at91_adc_trigger_remove(idev);
+               at91_adc_buffer_remove(idev);
+       } else {
+               at91_ts_unregister(st);
+       }
  error_disable_adc_clk:
         clk_disable_unprepare(st->adc_clk);
  error_disable_clk:
@@ -841,8 +1178,12 @@ static int at91_adc_remove(struct platform_device *pdev)
         struct at91_adc_state *st = iio_priv(idev);
 
         iio_device_unregister(idev);
-       at91_adc_trigger_remove(idev);
-       at91_adc_buffer_remove(idev);
+       if (!st->touchscreen_type) {
+               at91_adc_trigger_remove(idev);
+               at91_adc_buffer_remove(idev);
+       } else {
+               at91_ts_unregister(st);
+       }
         clk_disable_unprepare(st->adc_clk);
         clk_disable_unprepare(st->clk);
         free_irq(st->irq, idev);
@@ -861,7 +1202,7 @@ static struct platform_driver at91_adc_driver = {
         .probe = at91_adc_probe,
         .remove = at91_adc_remove,
         .driver = {
-                  .name = "at91_adc",
+                  .name = DRIVER_NAME,
                    .of_match_table = of_match_ptr(at91_adc_dt_ids),
         },
  };
--

1.7.10

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

#37 lore raw

PATCH 5/5 iio: at91: introduce touch screen support in iio adc driver

Quoted 37 lines · Josh Wu

Hi, Dear Mark

Quoted 21 lines · Mark Rutland
Quoted 20 lines · Josh Wu

AT91 ADC hardware integrate touch screen support. So this patch add touch screen support for at91 adc iio driver.
To enable touch screen support in adc, you need to add the dt parameters:
which type of touch are used? (4 or 5 wires), sample period time, pen detect debounce time, average samples and pen detect resistor.

In the meantime, since touch screen will use a interal period trigger of adc, so it is conflict to other hardware triggers. Driver will disable the hardware trigger support if touch screen is enabled.

This driver has been tested in AT91SAM9X5-EK and SAMA5D3x-EK.

Cc Dmitry Torokhov <[email protected]>
---
  .../devicetree/bindings/arm/atmel-adc.txt          |   13 +
  arch/arm/mach-at91/include/mach/at91_adc.h         |   34 ++
  drivers/iio/adc/at91_adc.c                         |  389 ++++++++++++++++++--
  3 files changed, 412 insertions(+), 24 deletions(-)
Quoted 12 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 33

Quoted 10 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
index 0db2945..925d656 100644
@@ -29,6 +29,19 @@ Optional properties:
    - atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
    - atmel,adc-clock-rate: ADC clock rate. If not specified, use the default
                           adc_op_clk.
+  - atmel,adc-touchscreen-wires: Number of touch screen wires. Only support
+                                4 and 5 wires touch screen.

Are 4 and 5 wire configurations that can exist, or the only ones supported by the driver?

Quoted 7 lines

It can be set:

atmel,adc-touchscreen-wires = <4>;
or
atmel,adc-touchscreen-wires = <5>;

according to your touch screen.

That doesn't answer my question.

Is it possible that 3 or 6 wire configurations might exist, for example, even if not supported by this driver? Or does the design of the adc prevent this?

Is there any documentation that might make this clearer?

Quoted 11 lines
Quoted 9 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 37

Quoted 6 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +4 -0
+    NOTE: when adc touch screen enabled, the adc hardware trigger will be
+          disabled. Since touch screen will occupied the trigger register.
+  - atmel,adc-ts-pendet-debounce: Debounce time in microsecond for touch pen
+                                 detect.

For consistency with the adc-touchscreen-wires property, and other properties with timing information, how about atmel,adc-touchscreen-debounce-delay-us ?

sound nice to me.

Additionally, is this likely to vary from board to board? This feels like configuration that could be done based on the compatible string...

Quoted 11 lines
Quoted 9 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 39

Quoted 4 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
+  - atmel,adc-ts-sample-period-time: Sample Period Time in microsecond for
+                                    touch screen

Again, please be consistent with ts or touchscreen. It may also be good to have -us to make the units explicit (though it does leave the property name being quite a mothful):

atmel,adc-touchscreen-sample-period-us ?

nice. I will use this one.

Looking again at the driver code, it looks like a value derived from this eventually gets written to the hardware. Is this a fixed value at integration time, or is this a configuration value? If it's the latter, can the driver not derive a good value for this itself?

Quoted 8 lines
Quoted 6 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 42

Quoted 5 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +3 -0
+  - atmel,adc-ts-filter-average: Numbers of sampling data will be averaged.
+    0 means no average. 1 means average two samples. 2 means average four
+    samples. 3 means average eight samples.

Is this averaging done in the hardware, or the kernel driver?

It is done in the hardware.

Similarly, this seems to eventually get written to hardware, and thus seems more like configuration than hardware description. Why does this need to be in the DT?

As an aside, in general it's nicer to describe a property as a logical value rather than the raw value that gets programmed into hardware (e.g. this property could by 2, 4, or 8 rather than 1, 2, or 4).

Quoted 6 lines

But for some soc, like AT91SAM9G45, hardware doesn't support hardware average.
So I am wondering use it as for both hardware and softer average.

BTW, you mentioned the kernel driver, do you mean a filter algorithm is already implemented in kernel library?

I do not know of any such filter algorithm in the kernel, though there may be one. I was simply confused as to what this was used for.

Quoted 10 lines
Quoted 7 lines

If it's the latter, this can be left for the kernel to decide.

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 44

Quoted 2 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
+  - atmel,adc-ts-pendet-sensitivity: Pen Detection input pull-up resistor.
+    It can be 0, 1, 2, 3.

I think "pendet" is a bit opaque. "pen-detect" may be better.

yes. I'll change this.

Quoted 8 lines
Quoted 3 lines

What
physical property does this represent (are these discrete values, or an enumeration)?

This property is supported by hardware, it can change the adc internal resistor value for better pen detection,
* 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm
In general, we just use default value 2 for 100kOhm.

This value eventually gets written to hardware, and seems more like configuration than hardware description. Why does this need to be in the DT?

Thanks,
Mark.

Quoted 586 lines

I think I need add more detail infomation in the dt document.

Thanks again,
Best Regards,
Josh Wu

Quoted 568 lines

Thanks,
Mark.

Quoted 564 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +0 -0
  Optional trigger Nodes:
    - Required properties:
arch/arm/mach-at91/include/mach/at91_adc.h +34 -0
index ab273ee..6d6cc14 100644
@@ -57,10 +57,44 @@
  #define AT91_ADC_IER           0x24            /* Interrupt Enable Register */
  #define AT91_ADC_IDR           0x28            /* Interrupt Disable Register */
  #define AT91_ADC_IMR           0x2C            /* Interrupt Mask Register */
+#define                AT91_ADC_IER_PEN        (1 << 29)
+#define                AT91_ADC_IER_NOPEN      (1 << 30)
+#define                AT91_ADC_IER_XRDY       (1 << 20)
+#define                AT91_ADC_IER_YRDY       (1 << 21)
+#define                AT91_ADC_IER_PRDY       (1 << 22)
+#define                AT91_ADC_ISR_PENS       (1 << 31)
 
  #define AT91_ADC_CHR(n)                (0x30 + ((n) * 4))      /* Channel Data Register N */
  #define                AT91_ADC_DATA           (0x3ff)
 
+#define AT91_ADC_ACR           0x94    /* Analog Control Register */
+#define                AT91_ADC_ACR_PENDETSENS (0x3 << 0)      /* pull-up resistor */
+
+#define AT91_ADC_TSMR          0xB0
+#define                AT91_ADC_TSMR_TSMODE    (3 << 0)        /* Touch Screen Mode */
+#define                        AT91_ADC_TSMR_TSMODE_NONE               (0 << 0)
+#define                        AT91_ADC_TSMR_TSMODE_4WIRE_NO_PRESS     (1 << 0)
+#define                        AT91_ADC_TSMR_TSMODE_4WIRE_PRESS        (2 << 0)
+#define                        AT91_ADC_TSMR_TSMODE_5WIRE              (3 << 0)
+#define                AT91_ADC_TSMR_TSAV      (3 << 4)        /* Averages samples */
+#define                        AT91_ADC_TSMR_TSAV_(x)          ((x) << 4)
+#define                AT91_ADC_TSMR_SCTIM     (0x0f << 16)    /* Switch closure time */
+#define                AT91_ADC_TSMR_PENDBC    (0x0f << 28)    /* Pen Debounce time */
+#define                        AT91_ADC_TSMR_PENDBC_(x)        ((x) << 28)
+#define                AT91_ADC_TSMR_NOTSDMA   (1 << 22)       /* No Touchscreen DMA */
+#define                AT91_ADC_TSMR_PENDET_DIS        (0 << 24)       /* Pen contact detection disable */
+#define                AT91_ADC_TSMR_PENDET_ENA        (1 << 24)       /* Pen contact detection enable */
+
+#define AT91_ADC_TSXPOSR       0xB4
+#define AT91_ADC_TSYPOSR       0xB8
+#define AT91_ADC_TSPRESSR      0xBC
+
  #define AT91_ADC_VERSION       0xFC
 
+/* Trigger Register bit field */
+#define                AT91_ADC_TRGR_TRGPER    (0xffff << 16)
+#define                        AT91_ADC_TRGR_TRGPER_(x)        ((x) << 16)
+#define                AT91_ADC_TRGR_TRGMOD    (0x7 << 0)
+#define                        AT91_ADC_TRGR_MOD_PERIOD_TRIG   (5 << 0)
+
  #endif
drivers/iio/adc/at91_adc.c +365 -25
index 8f1386f..ffc0e42 100644
@@ -11,6 +11,7 @@
  #include <linux/clk.h>
  #include <linux/err.h>
  #include <linux/io.h>
+#include <linux/input.h>
  #include <linux/interrupt.h>
  #include <linux/jiffies.h>
  #include <linux/kernel.h>
@@ -39,12 +40,24 @@
  #define at91_adc_writel(st, reg, val) \
         (writel_relaxed(val, st->reg_base + reg))
 
+#define DRIVER_NAME            "at91_adc"
+#define MAX_POS_BITS           12
+
+#define ZTHRESHOLD             9000
+
  struct at91_adc_caps {
+       bool    has_12bits_xy;  /* true means use 12 bits. Otherwise 10 bits */
         bool    has_tsmr;       /* only at91sam9x5, sama5d3 have TSMR reg */
         u32     mr_prescal_mask;
         u32     mr_startup_mask;
  };
 
+enum atmel_adc_ts_type {
+       ATMEL_ADC_TOUCHSCREEN_NONE = 0,
+       ATMEL_ADC_TOUCHSCREEN_4WIRE,
+       ATMEL_ADC_TOUCHSCREEN_5WIRE,
+};
+
  struct at91_adc_state {
         struct clk              *adc_clk;
         u32                     adc_clk_rate;
@@ -70,6 +83,29 @@ struct at91_adc_state {
         bool                    low_res;        /* the resolution corresponds to the lowest one */
         wait_queue_head_t       wq_data_avail;
         struct at91_adc_caps    caps;
+
+       /*
+        * Following ADC channels are shared by touchscreen:
+        *
+        * CH0 -- Touch screen XP/UL
+        * CH1 -- Touch screen XM/UR
+        * CH2 -- Touch screen YP/LL
+        * CH3 -- Touch screen YM/Sense
+        * CH4 -- Touch screen LR(5-wire only)
+        *
+        * The bitfields below represents the reserved channel in the
+        * touchscreen mode.
+        */
+#define CHAN_MASK_TOUCHSCREEN_4WIRE    (0xf << 0)
+#define CHAN_MASK_TOUCHSCREEN_5WIRE    (0x1f << 0)
+       enum atmel_adc_ts_type  touchscreen_type;
+       struct input_dev                *ts_input;
+
+       u8                      ts_filter_average;
+       u16                     ts_pen_detect_debounce;
+       u8                      ts_pen_detect_sensitivity;
+       u16                     ts_sample_period_time;
+       u16                     ts_sample_period_val;
  };
 
  static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
@@ -104,14 +140,10 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
         return IRQ_HANDLED;
  }
 
-static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
+/* Handler for classic adc channel eoc trigger */
+void handle_adc_eoc_trigger(int irq, struct iio_dev *idev)
  {
-       struct iio_dev *idev = private;
         struct at91_adc_state *st = iio_priv(idev);
-       u32 status = at91_adc_readl(st, st->registers->status_register);
-
-       if (!(status & st->registers->drdy_mask))
-               return IRQ_HANDLED;
 
         if (iio_buffer_enabled(idev)) {
                 disable_irq_nosync(irq);
@@ -121,6 +153,115 @@ static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
                 st->done = true;
                 wake_up_interruptible(&st->wq_data_avail);
         }
+}
+
+static int at91_ts_sample(struct at91_adc_state *st)
+{
+       unsigned int xscale, yscale, reg, z1, z2;
+       unsigned int x, y, pres, xpos, ypos;
+       unsigned int rxp = 1;
+       unsigned int factor = 1000;
+       struct iio_dev *idev = iio_priv_to_dev(st);
+
+       unsigned int xyz_mask_bits = st->caps.has_12bits_xy ? 12 : 10;
+       unsigned int xyz_mask = (1 << xyz_mask_bits) - 1;
+
+       /* calculate position */
+       /* x position = (x / xscale) * max, max = 2^MAX_POS_BITS - 1 */
+       reg = at91_adc_readl(st, AT91_ADC_TSXPOSR);
+       xpos = reg & xyz_mask;
+       x = (xpos << MAX_POS_BITS) - xpos;
+       xscale = (reg >> 16) & xyz_mask;
+       if (xscale != 0)
+               x /= xscale;
+       else
+               dev_err(&idev->dev, "xscale == 0!!!\n");
+
+       /* y position = (y / yscale) * max, max = 2^MAX_POS_BITS - 1 */
+       reg = at91_adc_readl(st, AT91_ADC_TSYPOSR);
+       ypos = reg & xyz_mask;
+       y = (ypos << MAX_POS_BITS) - ypos;
+       yscale = (reg >> 16) & xyz_mask;
+       if (yscale != 0)
+               y /= yscale;
+       else
+               dev_err(&idev->dev, "yscale == 0!!!\n");
+
+       /* calculate the pressure */
+       reg = at91_adc_readl(st, AT91_ADC_TSPRESSR);
+       z1 = reg & xyz_mask;
+       z2 = (reg >> 16) & xyz_mask;
+
+       if (z1 != 0)
+               pres = rxp * (x * factor / 1024) * (z2 * factor / z1 - factor)
+                       / factor;
+       else
+               pres = ZTHRESHOLD;      /* no pen contacted */
+
+       dev_dbg(&idev->dev, "xpos = %d, xscale = %d, ypos = %d, yscale = %d, z1 = %d, z2 = %d, press = %d\n",
+                               xpos, xscale, ypos, yscale, z1, z2, pres);
+
+       if (pres < ZTHRESHOLD) {
+               dev_dbg(&idev->dev, "x = %d, y = %d, pressure = %d\n",
+                                       x, y, pres / factor);
+               input_report_abs(st->ts_input, ABS_X, x);
+               input_report_abs(st->ts_input, ABS_Y, y);
+               input_report_abs(st->ts_input, ABS_PRESSURE, pres);
+               input_report_key(st->ts_input, BTN_TOUCH, 1);
+               input_sync(st->ts_input);
+       } else {
+               dev_dbg(&idev->dev,
+                               "pressure too low: not reporting\n");
+       }
+
+       return 0;
+}
+
+
+static irqreturn_t at91_adc_interrupt(int irq, void *private)
+{
+       struct iio_dev *idev = private;
+       struct at91_adc_state *st = iio_priv(idev);
+       u32 status = at91_adc_readl(st, st->registers->status_register);
+       const uint32_t ts_data_irq_mask =
+               AT91_ADC_IER_XRDY |
+               AT91_ADC_IER_YRDY |
+               AT91_ADC_IER_PRDY;
+
+       if (status & st->registers->drdy_mask)
+               handle_adc_eoc_trigger(irq, idev);
+
+       if (status & AT91_ADC_IER_PEN) {
+               at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
+               at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_NOPEN |
+                       ts_data_irq_mask);
+               /* Set up period trigger for sampling */
+               at91_adc_writel(st, st->registers->trigger_register,
+                       AT91_ADC_TRGR_MOD_PERIOD_TRIG |
+                       AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val));
+       } else if (status & AT91_ADC_IER_NOPEN) {
+               at91_adc_writel(st, st->registers->trigger_register, 0);
+               at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_NOPEN |
+                       ts_data_irq_mask);
+               at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
+
+               input_report_key(st->ts_input, BTN_TOUCH, 0);
+               input_sync(st->ts_input);
+       } else if ((status & ts_data_irq_mask) == ts_data_irq_mask) {
+               /* Now all touchscreen data is ready */
+
+               if (status & AT91_ADC_ISR_PENS) {
+                       /* validate data by pen contact */
+                       at91_ts_sample(st);
+               } else {
+                       /* triggered by event that is no pen contact, just read
+                        * them to clean the interrupt and discard all.
+                        */
+                       at91_adc_readl(st, AT91_ADC_TSXPOSR);
+                       at91_adc_readl(st, AT91_ADC_TSYPOSR);
+                       at91_adc_readl(st, AT91_ADC_TSPRESSR);
+               }
+       }
 
         return IRQ_HANDLED;
  }
@@ -130,6 +271,16 @@ static int at91_adc_channel_init(struct iio_dev *idev)
         struct at91_adc_state *st = iio_priv(idev);
         struct iio_chan_spec *chan_array, *timestamp;
         int bit, idx = 0;
+       unsigned long rsvd_mask = 0;
+
+       /* If touchscreen is enable, then reserve the adc channels */
+       if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
+               rsvd_mask = CHAN_MASK_TOUCHSCREEN_4WIRE;
+       else if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_5WIRE)
+               rsvd_mask = CHAN_MASK_TOUCHSCREEN_5WIRE;
+
+       /* set up the channel mask to reserve touchscreen channels */
+       st->channels_mask &= ~rsvd_mask;
 
         idev->num_channels = bitmap_weight(&st->channels_mask,
                                            st->num_channels) + 1;
@@ -561,6 +712,57 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
                 i++;
         }
 
+       /* Check if touchscreen is enabled in DT. */
+       ret = of_property_read_u32(node, "atmel,adc-touchscreen-wires", &prop);
+       if (ret)
+               dev_info(&idev->dev, "Touchscreen not enabled.\n");
+       else if (prop == 4)
+               st->touchscreen_type = ATMEL_ADC_TOUCHSCREEN_4WIRE;
+       else if (prop == 5)
+               st->touchscreen_type = ATMEL_ADC_TOUCHSCREEN_5WIRE;
+       else
+               dev_warn(&idev->dev, "Unsupported number of touchscreen wires (%d)\n",
+                               prop);
+
+       if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_NONE)
+               return 0;
+
+       /* Touch screen is enabled, so check touch screen dt parameters */
+       if (of_property_read_u32(node, "atmel,adc-ts-filter-average", &prop)) {
+               dev_err(&idev->dev, "Missing atmel,adc-ts-filtering-average property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       st->ts_filter_average = prop;
+       if (st->ts_filter_average > 3) {
+               dev_err(&idev->dev, "Invalid atmel,adc-ts-filtering-average property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+
+       prop = 0;
+       of_property_read_u32(node, "atmel,adc-ts-pendet-debounce", &prop);
+       st->ts_pen_detect_debounce = prop;
+
+       /* default sample period is 2ms. The real touch sample period should be
+        * this period * TSFREQ.
+        */
+       prop = 2000;
+       of_property_read_u32(node, "atmel,adc-ts-sample-period-time", &prop);
+       st->ts_sample_period_time = prop;
+
+       if (of_property_read_u32(node, "atmel,adc-ts-pendet-sensitivity", &prop)) {
+               dev_err(&idev->dev, "Missing atmel,adc-ts-pendet-sensitivity property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       st->ts_pen_detect_sensitivity = prop;
+       if (st->ts_pen_detect_sensitivity > 3) {
+               dev_err(&idev->dev, "Invalid atmel,adc-ts-pendet-sensitivity property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+
         return 0;
 
  error_ret:
@@ -592,6 +794,114 @@ static const struct iio_info at91_adc_info = {
         .read_raw = &at91_adc_read_raw,
  };
 
+/* Touchscreen related functions */
+static int atmel_ts_open(struct input_dev *dev)
+{
+       struct at91_adc_state *st = input_get_drvdata(dev);
+
+       at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
+       return 0;
+}
+
+static void atmel_ts_close(struct input_dev *dev)
+{
+       struct at91_adc_state *st = input_get_drvdata(dev);
+
+       at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
+}
+
+static int at91_ts_hw_init(struct at91_adc_state *st, u32 adc_clk_khz)
+{
+       u32 reg = 0, pendbc;
+       int i = 0;
+
+       if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
+               reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS;
+       else
+               reg = AT91_ADC_TSMR_TSMODE_5WIRE;
+
+       /* a Pen Detect Debounce Time is necessary for the ADC Touch to avoid
+        * pen detect noise.
+        * The formula is : Pen Detect Debounce Time = (2 ^ pendbc) / ADCClock
+        */
+       pendbc = round_up(st->ts_pen_detect_debounce * adc_clk_khz / 1000, 1);
+
+       while (pendbc >> ++i)
+               ;       /* Empty! Find the shift offset */
+       if (abs(pendbc - (1 << i)) < abs(pendbc - (1 << (i - 1))))
+               pendbc = i;
+       else
+               pendbc = i - 1;
+
+       if (st->caps.has_tsmr) {
+               reg |= AT91_ADC_TSMR_TSAV_(st->ts_filter_average)
+                               & AT91_ADC_TSMR_TSAV;
+               reg |= AT91_ADC_TSMR_PENDBC_(pendbc) & AT91_ADC_TSMR_PENDBC;
+               reg |= AT91_ADC_TSMR_NOTSDMA;
+               reg |= AT91_ADC_TSMR_PENDET_ENA;
+               reg |= 0x03 << 8;       /* TSFREQ, need bigger than TSAV */
+
+               at91_adc_writel(st, AT91_ADC_TSMR, reg);
+       } else {
+               /* TODO: for 9g45 which has no TSMR */
+       }
+
+       /* Change adc internal resistor value for better pen detection,
+        * default value is 100 kOhm.
+        * 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm
+        * option only available on ES2 and higher
+        */
+       at91_adc_writel(st, AT91_ADC_ACR, st->ts_pen_detect_sensitivity
+                       & AT91_ADC_ACR_PENDETSENS);
+
+       /* Sample Peroid Time = (TRGPER + 1) / ADCClock */
+       st->ts_sample_period_val = round_up((st->ts_sample_period_time *
+                       adc_clk_khz / 1000) - 1, 1);
+
+       return 0;
+}
+
+static int at91_ts_register(struct at91_adc_state *st)
+{
+       struct input_dev *input;
+       struct iio_dev *idev = iio_priv_to_dev(st);
+       int ret;
+
+       input = input_allocate_device();
+       if (!input) {
+               dev_err(&idev->dev, "Failed to allocate TS device!\n");
+               return -ENOMEM;
+       }
+
+       input->name = DRIVER_NAME;
+       input->id.bustype = BUS_HOST;
+       input->dev.parent = idev->dev.parent;
+       input->open = atmel_ts_open;
+       input->close = atmel_ts_close;
+
+       __set_bit(EV_ABS, input->evbit);
+       __set_bit(EV_KEY, input->evbit);
+       __set_bit(BTN_TOUCH, input->keybit);
+       input_set_abs_params(input, ABS_X, 0, (1 << MAX_POS_BITS) - 1, 0, 0);
+       input_set_abs_params(input, ABS_Y, 0, (1 << MAX_POS_BITS) - 1, 0, 0);
+       input_set_abs_params(input, ABS_PRESSURE, 0, 0xffffff, 0, 0);
+
+       st->ts_input = input;
+       input_set_drvdata(input, st);
+
+       ret = input_register_device(input);
+       if (ret)
+               input_free_device(st->ts_input);
+
+       return ret;
+}
+
+static void at91_ts_unregister(struct at91_adc_state *st)
+{
+       input_unregister_device(st->ts_input);
+       input_free_device(st->ts_input);
+}
+
  /*
   * Since atmel adc support different ip for touchscreen mode. Through the
   * IP check, we will know the touchscreen capbilities.
@@ -610,6 +920,7 @@ static void atmel_adc_get_cap(struct at91_adc_state *st)
         /* keep only major version number */
         switch (version & 0xf00) {
         case 0x500:     /* SAMA5D3 */
+               st->caps.has_12bits_xy = 1;
         case 0x400:     /* AT91SAM9X5/9N12 */
                 st->caps.has_tsmr = 1;
                 st->caps.mr_startup_mask = AT91_ADC_STARTUP;
@@ -683,7 +994,7 @@ static int at91_adc_probe(struct platform_device *pdev)
         at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_SWRST);
         at91_adc_writel(st, AT91_ADC_IDR, 0xFFFFFFFF);
         ret = request_irq(st->irq,
-                         at91_adc_eoc_trigger,
+                         at91_adc_interrupt,
                           0,
                           pdev->dev.driver->name,
                           idev);
@@ -731,6 +1042,10 @@ static int at91_adc_probe(struct platform_device *pdev)
         adc_clk = st->adc_clk_rate ?
                 st->adc_clk_rate : clk_get_rate(st->adc_clk);
         adc_clk_khz = adc_clk / 1000;
+
+       dev_dbg(&pdev->dev, "Master clock is set as: %d Hz, adc_clk should set as: %d Hz\n",
+               mstrclk, adc_clk);
+
         prsc = (mstrclk / (2 * adc_clk)) - 1;
 
         if (!st->startup_time) {
@@ -799,30 +1114,52 @@ static int at91_adc_probe(struct platform_device *pdev)
         init_waitqueue_head(&st->wq_data_avail);
         mutex_init(&st->lock);
 
-       ret = at91_adc_buffer_init(idev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
-               goto error_disable_adc_clk;
-       }
+       /*
+        * Since touch screen will set trigger register as period trigger. So
+        * when touch screen is enabled, then we have to disable hardware
+        * trigger for classic adc.
+        */
+       if (!st->touchscreen_type) {
+               ret = at91_adc_buffer_init(idev);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
+                       goto error_disable_adc_clk;
+               }
 
-       ret = at91_adc_trigger_init(idev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
-               goto error_unregister_buffer;
+               ret = at91_adc_trigger_init(idev);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
+                       at91_adc_buffer_remove(idev);
+                       goto error_disable_adc_clk;
+               }
+       } else {
+               if (!st->caps.has_tsmr) {
+                       dev_err(&pdev->dev, "We don't support non-TSMR adc\n");
+                       goto error_disable_adc_clk;
+               }
+
+               ret = at91_ts_register(st);
+               if (ret)
+                       goto error_disable_adc_clk;
+
+               at91_ts_hw_init(st, adc_clk_khz);
         }
 
         ret = iio_device_register(idev);
         if (ret < 0) {
                 dev_err(&pdev->dev, "Couldn't register the device.\n");
-               goto error_remove_triggers;
+               goto error_iio_device_register;
         }
 
         return 0;
 
-error_remove_triggers:
-       at91_adc_trigger_remove(idev);
-error_unregister_buffer:
-       at91_adc_buffer_remove(idev);
+error_iio_device_register:
+       if (!st->touchscreen_type) {
+               at91_adc_trigger_remove(idev);
+               at91_adc_buffer_remove(idev);
+       } else {
+               at91_ts_unregister(st);
+       }
  error_disable_adc_clk:
         clk_disable_unprepare(st->adc_clk);
  error_disable_clk:
@@ -841,8 +1178,12 @@ static int at91_adc_remove(struct platform_device *pdev)
         struct at91_adc_state *st = iio_priv(idev);
 
         iio_device_unregister(idev);
-       at91_adc_trigger_remove(idev);
-       at91_adc_buffer_remove(idev);
+       if (!st->touchscreen_type) {
+               at91_adc_trigger_remove(idev);
+               at91_adc_buffer_remove(idev);
+       } else {
+               at91_ts_unregister(st);
+       }
         clk_disable_unprepare(st->adc_clk);
         clk_disable_unprepare(st->clk);
         free_irq(st->irq, idev);
@@ -861,7 +1202,7 @@ static struct platform_driver at91_adc_driver = {
         .probe = at91_adc_probe,
         .remove = at91_adc_remove,
         .driver = {
-                  .name = "at91_adc",
+                  .name = DRIVER_NAME,
                    .of_match_table = of_match_ptr(at91_adc_dt_ids),
         },
  };
--

1.7.10

Quoted 5 lines
Quoted 5 lines

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

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

author
#38 lore raw

PATCH 5/5 iio: at91: introduce touch screen support in iio adc driver

Dear Mark

Thanks for the detailed comment. Check mine in below:

Quoted 43 lines · Mark Rutland
Quoted 42 lines · Josh Wu

Hi, Dear Mark

Quoted 21 lines · Mark Rutland
Quoted 20 lines · Josh Wu

AT91 ADC hardware integrate touch screen support. So this patch add touch screen support for at91 adc iio driver.
To enable touch screen support in adc, you need to add the dt parameters:
which type of touch are used? (4 or 5 wires), sample period time, pen detect debounce time, average samples and pen detect resistor.

In the meantime, since touch screen will use a interal period trigger of adc, so it is conflict to other hardware triggers. Driver will disable the hardware trigger support if touch screen is enabled.

This driver has been tested in AT91SAM9X5-EK and SAMA5D3x-EK.

Cc Dmitry Torokhov <[email protected]>
---
   .../devicetree/bindings/arm/atmel-adc.txt          |   13 +
   arch/arm/mach-at91/include/mach/at91_adc.h         |   34 ++
   drivers/iio/adc/at91_adc.c                         |  389 ++++++++++++++++++--
   3 files changed, 412 insertions(+), 24 deletions(-)
Quoted 12 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 33

Quoted 10 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
index 0db2945..925d656 100644
@@ -29,6 +29,19 @@ Optional properties:
     - atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
     - atmel,adc-clock-rate: ADC clock rate. If not specified, use the default
                            adc_op_clk.
+  - atmel,adc-touchscreen-wires: Number of touch screen wires. Only support
+                                4 and 5 wires touch screen.

Are 4 and 5 wire configurations that can exist, or the only ones supported by the driver?

It can be set:

atmel,adc-touchscreen-wires = <4>;
or
atmel,adc-touchscreen-wires = <5>;

Quoted 6 lines
Quoted 1 line

according to your touch screen.

That doesn't answer my question.

Is it possible that 3 or 6 wire configurations might exist, for example, even if not supported by this driver? Or does the design of the adc prevent this?

No, only 4-wire or 5-wire touch screen support in market so far.

Quoted 20 lines

Is there any documentation that might make this clearer?

Quoted 8 lines
Quoted 7 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 37

Quoted 4 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +4 -0
+    NOTE: when adc touch screen enabled, the adc hardware trigger will be
+          disabled. Since touch screen will occupied the trigger register.
+  - atmel,adc-ts-pendet-debounce: Debounce time in microsecond for touch pen
+                                 detect.

For consistency with the adc-touchscreen-wires property, and other properties with timing information, how about atmel,adc-touchscreen-debounce-delay-us ?

sound nice to me.

Additionally, is this likely to vary from board to board? This feels like configuration that could be done based on the compatible string...

Quoted 6 lines
Quoted 6 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 39

Quoted 2 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
+  - atmel,adc-ts-sample-period-time: Sample Period Time in microsecond for
+                                    touch screen

Again, please be consistent with ts or touchscreen. It may also be good to have -us to make the units explicit (though it does leave the property name being quite a mothful):

Quoted 6 lines
Quoted 2 lines
Quoted 1 line

atmel,adc-touchscreen-sample-period-us ?

nice. I will use this one.

Looking again at the driver code, it looks like a value derived from this eventually gets written to the hardware. Is this a fixed value at integration time, or is this a configuration value? If it's the latter, can the driver not derive a good value for this itself?

After a further thinking, I think it is better to remove this sample period us & pen detect debouce us.
Since it is a fixed value and no need to change it so far (in at91sam9x5ek, sama5d3xek).

Quoted 33 lines
Quoted 6 lines
Quoted 5 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 42

Quoted 4 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +3 -0
+  - atmel,adc-ts-filter-average: Numbers of sampling data will be averaged.
+    0 means no average. 1 means average two samples. 2 means average four
+    samples. 3 means average eight samples.

Is this averaging done in the hardware, or the kernel driver?

It is done in the hardware.

Similarly, this seems to eventually get written to hardware, and thus seems more like configuration than hardware description. Why does this need to be in the DT?

As an aside, in general it's nicer to describe a property as a logical value rather than the raw value that gets programmed into hardware (e.g. this property could by 2, 4, or 8 rather than 1, 2, or 4).

Quoted 6 lines

But for some soc, like AT91SAM9G45, hardware doesn't support hardware average.
So I am wondering use it as for both hardware and softer average.

BTW, you mentioned the kernel driver, do you mean a filter algorithm is already implemented in kernel library?

I do not know of any such filter algorithm in the kernel, though there may be one. I was simply confused as to what this was used for.

Quoted 10 lines
Quoted 5 lines

If it's the latter, this can be left for the kernel to decide.

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 44

Quoted 2 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
+  - atmel,adc-ts-pendet-sensitivity: Pen Detection input pull-up resistor.
+    It can be 0, 1, 2, 3.

I think "pendet" is a bit opaque. "pen-detect" may be better.

yes. I'll change this.

Quoted 3 lines

What
physical property does this represent (are these discrete values, or an enumeration)?

Quoted 7 lines
Quoted 4 lines

This property is supported by hardware, it can change the adc internal resistor value for better pen detection,
* 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm
In general, we just use default value 2 for 100kOhm.

This value eventually gets written to hardware, and seems more like configuration than hardware description. Why does this need to be in the DT?

I am a little bit confused by the hardware configuration and hardware description.
It seems I prefer to put all the hardware configurable value to DT. Since I think this can be changed in DT.
But as you mentioned above, only the hardware description can be in DT. Could you give me a more detail about the difference between hardware configuration and hardware description?

Thanks in advance.

Quoted 3 lines

Thanks,
Mark.

[... ...]

Best Regards,
Josh Wu

#39 lore raw

PATCH 5/5 iio: at91: introduce touch screen support in iio adc driver

Quoted 48 lines · Josh Wu

Dear Mark

Thanks for the detailed comment. Check mine in below:

Quoted 43 lines · Mark Rutland
Quoted 42 lines · Josh Wu

Hi, Dear Mark

Quoted 21 lines · Mark Rutland
Quoted 20 lines · Josh Wu

AT91 ADC hardware integrate touch screen support. So this patch add touch screen support for at91 adc iio driver.
To enable touch screen support in adc, you need to add the dt parameters:
which type of touch are used? (4 or 5 wires), sample period time, pen detect debounce time, average samples and pen detect resistor.

In the meantime, since touch screen will use a interal period trigger of adc, so it is conflict to other hardware triggers. Driver will disable the hardware trigger support if touch screen is enabled.

This driver has been tested in AT91SAM9X5-EK and SAMA5D3x-EK.

Cc Dmitry Torokhov <[email protected]>
---
   .../devicetree/bindings/arm/atmel-adc.txt          |   13 +
   arch/arm/mach-at91/include/mach/at91_adc.h         |   34 ++
   drivers/iio/adc/at91_adc.c                         |  389 ++++++++++++++++++--
   3 files changed, 412 insertions(+), 24 deletions(-)
Quoted 12 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 33

Quoted 10 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
index 0db2945..925d656 100644
@@ -29,6 +29,19 @@ Optional properties:
     - atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
     - atmel,adc-clock-rate: ADC clock rate. If not specified, use the default
                            adc_op_clk.
+  - atmel,adc-touchscreen-wires: Number of touch screen wires. Only support
+                                4 and 5 wires touch screen.

Are 4 and 5 wire configurations that can exist, or the only ones supported by the driver?

It can be set:

atmel,adc-touchscreen-wires = <4>;
or
atmel,adc-touchscreen-wires = <5>;

Quoted 8 lines
Quoted 6 lines
Quoted 1 line

according to your touch screen.

That doesn't answer my question.

Is it possible that 3 or 6 wire configurations might exist, for example, even if not supported by this driver? Or does the design of the adc prevent this?

No, only 4-wire or 5-wire touch screen support in market so far.

Ok. The point that I was trying to get at was that "Only support 4 and 5 wires touch screen" seems to be a description of the driver rather than a physical limitation that should limit what we can describe in dt.

Quoted 28 lines
Quoted 21 lines

Is there any documentation that might make this clearer?

Quoted 8 lines
Quoted 7 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 37

Quoted 4 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +4 -0
+    NOTE: when adc touch screen enabled, the adc hardware trigger will be
+          disabled. Since touch screen will occupied the trigger register.
+  - atmel,adc-ts-pendet-debounce: Debounce time in microsecond for touch pen
+                                 detect.

For consistency with the adc-touchscreen-wires property, and other properties with timing information, how about atmel,adc-touchscreen-debounce-delay-us ?

sound nice to me.

Additionally, is this likely to vary from board to board? This feels like configuration that could be done based on the compatible string...

Quoted 6 lines
Quoted 6 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 39

Quoted 2 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
+  - atmel,adc-ts-sample-period-time: Sample Period Time in microsecond for
+                                    touch screen

Again, please be consistent with ts or touchscreen. It may also be good to have -us to make the units explicit (though it does leave the property name being quite a mothful):

Quoted 6 lines
Quoted 2 lines
Quoted 1 line

atmel,adc-touchscreen-sample-period-us ?

nice. I will use this one.

Looking again at the driver code, it looks like a value derived from this eventually gets written to the hardware. Is this a fixed value at integration time, or is this a configuration value? If it's the latter, can the driver not derive a good value for this itself?

Quoted 4 lines

After a further thinking, I think it is better to remove this sample period us & pen detect debouce us.
Since it is a fixed value and no need to change it so far (in at91sam9x5ek, sama5d3xek).

Ok.

Quoted 42 lines
Quoted 34 lines
Quoted 7 lines
Quoted 6 lines

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 42

Quoted 5 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +3 -0
+  - atmel,adc-ts-filter-average: Numbers of sampling data will be averaged.
+    0 means no average. 1 means average two samples. 2 means average four
+    samples. 3 means average eight samples.

Is this averaging done in the hardware, or the kernel driver?

It is done in the hardware.

Similarly, this seems to eventually get written to hardware, and thus seems more like configuration than hardware description. Why does this need to be in the DT?

As an aside, in general it's nicer to describe a property as a logical value rather than the raw value that gets programmed into hardware (e.g. this property could by 2, 4, or 8 rather than 1, 2, or 4).

Quoted 6 lines

But for some soc, like AT91SAM9G45, hardware doesn't support hardware average.
So I am wondering use it as for both hardware and softer average.

BTW, you mentioned the kernel driver, do you mean a filter algorithm is already implemented in kernel library?

I do not know of any such filter algorithm in the kernel, though there may be one. I was simply confused as to what this was used for.

Quoted 10 lines
Quoted 5 lines

If it's the latter, this can be left for the kernel to decide.

Documentation/devicetree/bindings/arm/atmel-adc.txt Optional properties: · line 44

Quoted 2 lines
Documentation/devicetree/bindings/arm/atmel-adc.txt +2 -0
+  - atmel,adc-ts-pendet-sensitivity: Pen Detection input pull-up resistor.
+    It can be 0, 1, 2, 3.

I think "pendet" is a bit opaque. "pen-detect" may be better.

yes. I'll change this.

Quoted 3 lines

What
physical property does this represent (are these discrete values, or an enumeration)?

Quoted 7 lines
Quoted 4 lines

This property is supported by hardware, it can change the adc internal resistor value for better pen detection,
* 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm
In general, we just use default value 2 for 100kOhm.

This value eventually gets written to hardware, and seems more like configuration than hardware description. Why does this need to be in the DT?

Quoted 7 lines

I am a little bit confused by the hardware configuration and hardware description.
It seems I prefer to put all the hardware configurable value to DT. Since I think this can be changed in DT.
But as you mentioned above, only the hardware description can be in DT. Could you give me a more detail about the difference between hardware configuration and hardware description?

It's admittedly a gray area, but the basic idea would be that anything we might feasibly want to change the value of later shouldn't go into the DT, while properties of the hardware that might influence those decisions should be in the DT. It seems to me that the pen detect resistor value is something we might want to change later, if people found a particular touchscreen were too sensitive, for instance. However, maybe not.

Thanks,
Mark.