All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] mxs-lradc: Split driver into MFD
@ 2016-04-29 11:46 ` Ksenija Stanojevic
  0 siblings, 0 replies; 40+ messages in thread
From: Ksenija Stanojevic @ 2016-04-29 11:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: lee.jones, dmitry.torokhov, linux-input, jic23, knaack.h, lars,
	pmeerw, marex, linux-iio, harald, Ksenija Stanojevic

Split existing driver mxs-lradc into MFD with touchscreen and
IIO part.

Tested on I.MX28.

Ksenija Stanojevic (3):
  mfd: mxs-lradc: Add support for mxs-lradc MFD
  iio: adc: mxs-lradc: Add support for adc driver
  input: touchscreen: mxs-lradc: Add support for touchscreen

 drivers/iio/adc/Kconfig                  |  37 +-
 drivers/iio/adc/Makefile                 |   1 +
 drivers/iio/adc/mxs-lradc-adc.c          | 832 +++++++++++++++++++++++++++++++
 drivers/input/touchscreen/Kconfig        |  14 +-
 drivers/input/touchscreen/Makefile       |   1 +
 drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++
 drivers/mfd/Kconfig                      |  33 +-
 drivers/mfd/Makefile                     |   1 +
 drivers/mfd/mxs-lradc.c                  | 213 ++++++++
 include/linux/mfd/mxs-lradc.h            | 210 ++++++++
 10 files changed, 2049 insertions(+), 22 deletions(-)
 create mode 100644 drivers/iio/adc/mxs-lradc-adc.c
 create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
 create mode 100644 drivers/mfd/mxs-lradc.c
 create mode 100644 include/linux/mfd/mxs-lradc.h

-- 
1.9.1

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

* [PATCH 0/3] mxs-lradc: Split driver into MFD
@ 2016-04-29 11:46 ` Ksenija Stanojevic
  0 siblings, 0 replies; 40+ messages in thread
From: Ksenija Stanojevic @ 2016-04-29 11:46 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	linux-input-u79uwXL29TY76Z2rM5mHXA, jic23-DgEjT+Ai2ygdnm+yROfE0A,
	knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, marex-ynQEQJNshbs,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, harald-95f8Dae0BrPYtjvyW6yDsg,
	Ksenija Stanojevic

Split existing driver mxs-lradc into MFD with touchscreen and
IIO part.

Tested on I.MX28.

Ksenija Stanojevic (3):
  mfd: mxs-lradc: Add support for mxs-lradc MFD
  iio: adc: mxs-lradc: Add support for adc driver
  input: touchscreen: mxs-lradc: Add support for touchscreen

 drivers/iio/adc/Kconfig                  |  37 +-
 drivers/iio/adc/Makefile                 |   1 +
 drivers/iio/adc/mxs-lradc-adc.c          | 832 +++++++++++++++++++++++++++++++
 drivers/input/touchscreen/Kconfig        |  14 +-
 drivers/input/touchscreen/Makefile       |   1 +
 drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++
 drivers/mfd/Kconfig                      |  33 +-
 drivers/mfd/Makefile                     |   1 +
 drivers/mfd/mxs-lradc.c                  | 213 ++++++++
 include/linux/mfd/mxs-lradc.h            | 210 ++++++++
 10 files changed, 2049 insertions(+), 22 deletions(-)
 create mode 100644 drivers/iio/adc/mxs-lradc-adc.c
 create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
 create mode 100644 drivers/mfd/mxs-lradc.c
 create mode 100644 include/linux/mfd/mxs-lradc.h

-- 
1.9.1

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

* [PATCH 1/3] mfd: mxs-lradc: Add support for mxs-lradc MFD
@ 2016-04-29 11:47   ` Ksenija Stanojevic
  0 siblings, 0 replies; 40+ messages in thread
From: Ksenija Stanojevic @ 2016-04-29 11:47 UTC (permalink / raw)
  To: linux-kernel
  Cc: lee.jones, dmitry.torokhov, linux-input, jic23, knaack.h, lars,
	pmeerw, marex, linux-iio, harald, Ksenija Stanojevic

Add core files for mxs-lradc MFD driver.

Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
---
 drivers/mfd/Kconfig           |  33 +++++--
 drivers/mfd/Makefile          |   1 +
 drivers/mfd/mxs-lradc.c       | 213 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/mxs-lradc.h | 210 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 449 insertions(+), 8 deletions(-)
 create mode 100644 drivers/mfd/mxs-lradc.c
 create mode 100644 include/linux/mfd/mxs-lradc.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index eea61e3..fff44d6 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -16,7 +16,7 @@ config MFD_CS5535
 	depends on PCI && (X86_32 || (X86 && COMPILE_TEST))
 	---help---
 	  This is the core driver for CS5535/CS5536 MFD functions.  This is
-          necessary for using the board's GPIO and MFGPT functionality.
+	  necessary for using the board's GPIO and MFGPT functionality.
 
 config MFD_ACT8945A
 	tristate "Active-semi ACT8945A"
@@ -319,6 +319,23 @@ config MFD_HI6421_PMIC
 	  menus in order to enable them.
 	  We communicate with the Hi6421 via memory-mapped I/O.
 
+config MFD_MXS_LRADC
+	tristate "Freescale i.MX23/i.MX28 LRADC"
+	depends on ARCH_MXS || COMPILE_TEST
+	select MFD_CORE
+	select STMP_DEVICE
+	help
+	  Say yes here to build support for the low-resolution
+	  analog-to-digital converter (LRADC) found on the i.MX23 and i.MX28
+	  processors. This driver provides common support for accessing the
+	  device, additional drivers must be enabled in order to use the
+	  functionality of the device:
+		mxs-lradc-adc for ADC readings
+		mxs-lradc-ts  for touchscreen support
+
+	  This driver can also be built as a module. If so, the module will be
+	  called mxs-lradc.
+
 config HTC_EGPIO
 	bool "HTC EGPIO support"
 	depends on GPIOLIB && ARM
@@ -650,7 +667,7 @@ config EZX_PCAP
 	  needed for MMC, TouchScreen, Sound, USB, etc..
 
 config MFD_VIPERBOARD
-        tristate "Nano River Technologies Viperboard"
+	tristate "Nano River Technologies Viperboard"
 	select MFD_CORE
 	depends on USB
 	default n
@@ -898,11 +915,11 @@ config MFD_SMSC
        select MFD_CORE
        select REGMAP_I2C
        help
-        If you say yes here you get support for the
-        ece1099 chips from SMSC.
+	If you say yes here you get support for the
+	ece1099 chips from SMSC.
 
-        To compile this driver as a module, choose M here: the
-        module will be called smsc.
+	To compile this driver as a module, choose M here: the
+	module will be called smsc.
 
 config ABX500_CORE
 	bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
@@ -956,8 +973,8 @@ config AB8500_DEBUG
        depends on AB8500_GPADC && DEBUG_FS
        default y if DEBUG_FS
        help
-         Select this option if you want debug information using the debug
-         filesystem, debugfs.
+	 Select this option if you want debug information using the debug
+	 filesystem, debugfs.
 
 config AB8500_GPADC
 	bool "ST-Ericsson AB8500 GPADC driver"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 5eaa6465d..236b831 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -203,3 +203,4 @@ intel-soc-pmic-objs		:= intel_soc_pmic_core.o intel_soc_pmic_crc.o
 intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC)	+= intel_soc_pmic_bxtwc.o
 obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
 obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
+obj-$(CONFIG_MFD_MXS_LRADC)	+= mxs-lradc.o
diff --git a/drivers/mfd/mxs-lradc.c b/drivers/mfd/mxs-lradc.c
new file mode 100644
index 0000000..e1c8f9e
--- /dev/null
+++ b/drivers/mfd/mxs-lradc.c
@@ -0,0 +1,213 @@
+/*
+ * Freescale MXS LRADC driver
+ *
+ * Copyright (c) 2012 DENX Software Engineering, GmbH.
+ * Marek Vasut <marex@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/mxs-lradc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+static struct mfd_cell lradc_adc_dev = {
+	.name = DRIVER_NAME_ADC,
+};
+
+static struct mfd_cell lradc_ts_dev = {
+	.name = DRIVER_NAME_TS,
+};
+
+static const char * const mx23_lradc_irq_names[] = {
+	"mxs-lradc-touchscreen",
+	"mxs-lradc-channel0",
+	"mxs-lradc-channel1",
+	"mxs-lradc-channel2",
+	"mxs-lradc-channel3",
+	"mxs-lradc-channel4",
+	"mxs-lradc-channel5",
+	"mxs-lradc-channel6",
+	"mxs-lradc-channel7",
+};
+
+static const char * const mx28_lradc_irq_names[] = {
+	"mxs-lradc-touchscreen",
+	"mxs-lradc-thresh0",
+	"mxs-lradc-thresh1",
+	"mxs-lradc-channel0",
+	"mxs-lradc-channel1",
+	"mxs-lradc-channel2",
+	"mxs-lradc-channel3",
+	"mxs-lradc-channel4",
+	"mxs-lradc-channel5",
+	"mxs-lradc-channel6",
+	"mxs-lradc-channel7",
+	"mxs-lradc-button0",
+	"mxs-lradc-button1",
+};
+
+struct mxs_lradc_of_config {
+	const int		irq_count;
+	const char * const	*irq_name;
+};
+
+static const struct mxs_lradc_of_config mxs_lradc_of_config[] = {
+	[IMX23_LRADC] = {
+		.irq_count	= ARRAY_SIZE(mx23_lradc_irq_names),
+		.irq_name	= mx23_lradc_irq_names,
+	},
+	[IMX28_LRADC] = {
+		.irq_count	= ARRAY_SIZE(mx28_lradc_irq_names),
+		.irq_name	= mx28_lradc_irq_names,
+	},
+};
+
+static const struct of_device_id mxs_lradc_dt_ids[] = {
+	{ .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, },
+	{ .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
+
+static int mxs_lradc_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *of_id =
+		of_match_device(mxs_lradc_dt_ids, &pdev->dev);
+	const struct mxs_lradc_of_config *of_cfg =
+		&mxs_lradc_of_config[(enum mxs_lradc_id)of_id->data];
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct mxs_lradc *lradc;
+	struct resource *iores;
+	int ret = 0, touch_ret, i;
+	u32 ts_wires = 0;
+
+	lradc = devm_kzalloc(&pdev->dev, sizeof(*lradc), GFP_KERNEL);
+	if (!lradc)
+		return -ENOMEM;
+	lradc->soc = (enum mxs_lradc_id)of_id->data;
+
+	/* Grab the memory area */
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	lradc->base = devm_ioremap_resource(dev, iores);
+	if (IS_ERR(lradc->base))
+		return PTR_ERR(lradc->base);
+
+	lradc->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(lradc->clk)) {
+		dev_err(dev, "Failed to get the delay unit clock\n");
+		return PTR_ERR(lradc->clk);
+	}
+	ret = clk_prepare_enable(lradc->clk);
+	if (ret != 0) {
+		dev_err(dev, "Failed to enable the delay unit clock\n");
+		return ret;
+	}
+
+	touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
+					 &ts_wires);
+
+	if (touch_ret == 0)
+		lradc->buffer_vchans = BUFFER_VCHANS_LIMITED;
+	else
+		lradc->buffer_vchans = BUFFER_VCHANS_ALL;
+
+	lradc->irq_count = of_cfg->irq_count;
+	lradc->irq_name = of_cfg->irq_name;
+	for (i = 0; i < lradc->irq_count; i++) {
+		lradc->irq[i] = platform_get_irq(pdev, i);
+		if (lradc->irq[i] < 0) {
+			ret = lradc->irq[i];
+			goto err_clk;
+		}
+	}
+
+	platform_set_drvdata(pdev, lradc);
+
+	ret = stmp_reset_block(lradc->base);
+
+	if (ret)
+		return ret;
+
+	lradc_adc_dev.platform_data = lradc;
+	lradc_adc_dev.pdata_size = sizeof(*lradc);
+
+	ret = mfd_add_devices(&pdev->dev, -1, &lradc_adc_dev, 1, NULL, 0, NULL);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to add the ADC subdevice\n");
+		return ret;
+	}
+
+	lradc_ts_dev.platform_data = lradc;
+	lradc_ts_dev.pdata_size = sizeof(*lradc);
+
+	switch (ts_wires) {
+	case 4:
+		lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE;
+		break;
+	case 5:
+		if (lradc->soc == IMX28_LRADC) {
+			lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE;
+			break;
+		}
+		/* fall through an error message for i.MX23 */
+	default:
+		dev_err(&pdev->dev,
+			"Unsupported number of touchscreen wires (%d)\n",
+			ts_wires);
+		return -EINVAL;
+	}
+
+	ret = mfd_add_devices(&pdev->dev, -1, &lradc_ts_dev, 1, NULL, 0, NULL);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Failed to add the touchscreen subdevice\n");
+		goto err_remove_adc;
+	}
+
+	return 0;
+
+err_remove_adc:
+	mfd_remove_devices(&pdev->dev);
+err_clk:
+	clk_disable_unprepare(lradc->clk);
+	return ret;
+}
+
+static int mxs_lradc_remove(struct platform_device *pdev)
+{
+	struct mxs_lradc *lradc = platform_get_drvdata(pdev);
+
+	mfd_remove_devices(&pdev->dev);
+	clk_disable_unprepare(lradc->clk);
+	return 0;
+}
+
+static struct platform_driver mxs_lradc_driver = {
+	.driver = {
+		.name = "mxs-lradc",
+		.of_match_table = mxs_lradc_dt_ids,
+	},
+	.probe = mxs_lradc_probe,
+	.remove = mxs_lradc_remove,
+};
+
+module_platform_driver(mxs_lradc_driver);
+
+MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mxs-lradc");
diff --git a/include/linux/mfd/mxs-lradc.h b/include/linux/mfd/mxs-lradc.h
new file mode 100644
index 0000000..c062969
--- /dev/null
+++ b/include/linux/mfd/mxs-lradc.h
@@ -0,0 +1,210 @@
+/*
+ * Freescale MXS LRADC driver
+ *
+ * Copyright (c) 2012 DENX Software Engineering, GmbH.
+ * Marek Vasut <marex@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MXS_LRADC_H
+#define __MXS_LRADC_H
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/stmp_device.h>
+#include <linux/sysfs.h>
+
+#define DRIVER_NAME_ADC "mxs-lradc-adc"
+#define DRIVER_NAME_TS "mxs-lradc-ts"
+
+#define LRADC_MAX_DELAY_CHANS	4
+#define LRADC_MAX_MAPPED_CHANS	8
+#define LRADC_MAX_TOTAL_CHANS	16
+
+#define LRADC_DELAY_TIMER_HZ	2000
+
+#define LRADC_CTRL0				0x00
+# define LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE	BIT(23)
+# define LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE	BIT(22)
+# define LRADC_CTRL0_MX28_YNNSW /* YM */	BIT(21)
+# define LRADC_CTRL0_MX28_YPNSW /* YP */	BIT(20)
+# define LRADC_CTRL0_MX28_YPPSW /* YP */	BIT(19)
+# define LRADC_CTRL0_MX28_XNNSW /* XM */	BIT(18)
+# define LRADC_CTRL0_MX28_XNPSW /* XM */	BIT(17)
+# define LRADC_CTRL0_MX28_XPPSW /* XP */	BIT(16)
+
+# define LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE	BIT(20)
+# define LRADC_CTRL0_MX23_YM			BIT(19)
+# define LRADC_CTRL0_MX23_XM			BIT(18)
+# define LRADC_CTRL0_MX23_YP			BIT(17)
+# define LRADC_CTRL0_MX23_XP			BIT(16)
+
+# define LRADC_CTRL0_MX28_PLATE_MASK \
+		(LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE | \
+		LRADC_CTRL0_MX28_YNNSW | LRADC_CTRL0_MX28_YPNSW | \
+		LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW | \
+		LRADC_CTRL0_MX28_XNPSW | LRADC_CTRL0_MX28_XPPSW)
+
+# define LRADC_CTRL0_MX23_PLATE_MASK \
+		(LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE | \
+		LRADC_CTRL0_MX23_YM | LRADC_CTRL0_MX23_XM | \
+		LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XP)
+
+#define LRADC_CTRL1				0x10
+#define LRADC_CTRL1_TOUCH_DETECT_IRQ_EN		BIT(24)
+#define LRADC_CTRL1_LRADC_IRQ_EN(n)		(1 << ((n) + 16))
+#define LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK	(0x1fff << 16)
+#define LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK	(0x01ff << 16)
+#define LRADC_CTRL1_LRADC_IRQ_EN_OFFSET		16
+#define LRADC_CTRL1_TOUCH_DETECT_IRQ		BIT(8)
+#define LRADC_CTRL1_LRADC_IRQ(n)		(1 << (n))
+#define LRADC_CTRL1_MX28_LRADC_IRQ_MASK		0x1fff
+#define LRADC_CTRL1_MX23_LRADC_IRQ_MASK		0x01ff
+#define LRADC_CTRL1_LRADC_IRQ_OFFSET		0
+
+#define LRADC_CTRL2				0x20
+#define LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET	24
+#define LRADC_CTRL2_TEMPSENSE_PWD		BIT(15)
+
+#define LRADC_STATUS				0x40
+#define LRADC_STATUS_TOUCH_DETECT_RAW		BIT(0)
+
+#define LRADC_CH(n)				(0x50 + (0x10 * (n)))
+#define LRADC_CH_ACCUMULATE			BIT(29)
+#define LRADC_CH_NUM_SAMPLES_MASK		(0x1f << 24)
+#define LRADC_CH_NUM_SAMPLES_OFFSET		24
+#define LRADC_CH_NUM_SAMPLES(x) \
+				((x) << LRADC_CH_NUM_SAMPLES_OFFSET)
+#define LRADC_CH_VALUE_MASK			0x3ffff
+#define LRADC_CH_VALUE_OFFSET			0
+
+#define LRADC_DELAY(n)				(0xd0 + (0x10 * (n)))
+#define LRADC_DELAY_TRIGGER_LRADCS_MASK		(0xffUL << 24)
+#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET	24
+#define LRADC_DELAY_TRIGGER(x) \
+				(((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \
+				LRADC_DELAY_TRIGGER_LRADCS_MASK)
+#define LRADC_DELAY_KICK			BIT(20)
+#define LRADC_DELAY_TRIGGER_DELAYS_MASK		(0xf << 16)
+#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET	16
+#define LRADC_DELAY_TRIGGER_DELAYS(x) \
+				(((x) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) & \
+				LRADC_DELAY_TRIGGER_DELAYS_MASK)
+#define LRADC_DELAY_LOOP_COUNT_MASK		(0x1f << 11)
+#define LRADC_DELAY_LOOP_COUNT_OFFSET		11
+#define LRADC_DELAY_LOOP(x) \
+				(((x) << LRADC_DELAY_LOOP_COUNT_OFFSET) & \
+				LRADC_DELAY_LOOP_COUNT_MASK)
+#define LRADC_DELAY_DELAY_MASK			0x7ff
+#define LRADC_DELAY_DELAY_OFFSET		0
+#define LRADC_DELAY_DELAY(x) \
+				(((x) << LRADC_DELAY_DELAY_OFFSET) & \
+				LRADC_DELAY_DELAY_MASK)
+
+#define LRADC_CTRL4				0x140
+#define LRADC_CTRL4_LRADCSELECT_MASK(n)		(0xf << ((n) * 4))
+#define LRADC_CTRL4_LRADCSELECT_OFFSET(n)	((n) * 4)
+#define LRADC_CTRL4_LRADCSELECT(n, x) \
+				(((x) << LRADC_CTRL4_LRADCSELECT_OFFSET(n)) & \
+				LRADC_CTRL4_LRADCSELECT_MASK(n))
+
+#define LRADC_RESOLUTION			12
+#define LRADC_SINGLE_SAMPLE_MASK		((1 << LRADC_RESOLUTION) - 1)
+
+enum mxs_lradc_id {
+	IMX23_LRADC,
+	IMX28_LRADC,
+};
+
+enum mxs_lradc_ts_wires {
+	MXS_LRADC_TOUCHSCREEN_NONE = 0,
+	MXS_LRADC_TOUCHSCREEN_4WIRE,
+	MXS_LRADC_TOUCHSCREEN_5WIRE,
+};
+
+struct mxs_lradc {
+	enum mxs_lradc_id	soc;
+
+	void __iomem		*base;
+	struct clk		*clk;
+
+	int			irq[13];
+	const char * const	*irq_name;
+	int			irq_count;
+
+#define BUFFER_VCHANS_LIMITED		0x3f
+#define BUFFER_VCHANS_ALL		0xff
+	u8			buffer_vchans;
+
+	/*
+	 * Certain LRADC channels are shared between touchscreen
+	 * and/or touch-buttons and generic LRADC block. Therefore when using
+	 * either of these, these channels are not available for the regular
+	 * sampling. The shared channels are as follows:
+	 *
+	 * CH0 -- Touch button #0
+	 * CH1 -- Touch button #1
+	 * CH2 -- Touch screen XPUL
+	 * CH3 -- Touch screen YPLL
+	 * CH4 -- Touch screen XNUL
+	 * CH5 -- Touch screen YNLR
+	 * CH6 -- Touch screen WIPER (5-wire only)
+	 *
+	 * The bit fields below represents which parts of the LRADC block are
+	 * switched into special mode of operation. These channels can not
+	 * be sampled as regular LRADC channels. The driver will refuse any
+	 * attempt to sample these channels.
+	 */
+#define CHAN_MASK_TOUCHBUTTON		(BIT(1) | BIT(0))
+#define CHAN_MASK_TOUCHSCREEN_4WIRE	(0xf << 2)
+#define CHAN_MASK_TOUCHSCREEN_5WIRE	(0x1f << 2)
+	enum mxs_lradc_ts_wires	use_touchscreen;
+	bool			use_touchbutton;
+};
+
+static inline void mxs_lradc_reg_set(struct mxs_lradc *lradc, u32 val, u32 reg)
+{
+	writel(val, lradc->base + reg + STMP_OFFSET_REG_SET);
+}
+
+static inline void mxs_lradc_reg_clear(struct mxs_lradc *lradc, u32 val,
+				       u32 reg)
+{
+	writel(val, lradc->base + reg + STMP_OFFSET_REG_CLR);
+}
+
+static inline void mxs_lradc_reg_wrt(struct mxs_lradc *lradc, u32 val, u32 reg)
+{
+	writel(val, lradc->base + reg);
+}
+
+static inline u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc)
+{
+	if (lradc->soc == IMX23_LRADC)
+		return LRADC_CTRL1_MX23_LRADC_IRQ_MASK;
+	return LRADC_CTRL1_MX28_LRADC_IRQ_MASK;
+}
+
+#endif /* __MXS_LRADC_H */
-- 
1.9.1

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

* [PATCH 1/3] mfd: mxs-lradc: Add support for mxs-lradc MFD
@ 2016-04-29 11:47   ` Ksenija Stanojevic
  0 siblings, 0 replies; 40+ messages in thread
From: Ksenija Stanojevic @ 2016-04-29 11:47 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	linux-input-u79uwXL29TY76Z2rM5mHXA, jic23-DgEjT+Ai2ygdnm+yROfE0A,
	knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, marex-ynQEQJNshbs,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, harald-95f8Dae0BrPYtjvyW6yDsg,
	Ksenija Stanojevic

Add core files for mxs-lradc MFD driver.

Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
 drivers/mfd/Kconfig           |  33 +++++--
 drivers/mfd/Makefile          |   1 +
 drivers/mfd/mxs-lradc.c       | 213 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/mxs-lradc.h | 210 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 449 insertions(+), 8 deletions(-)
 create mode 100644 drivers/mfd/mxs-lradc.c
 create mode 100644 include/linux/mfd/mxs-lradc.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index eea61e3..fff44d6 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -16,7 +16,7 @@ config MFD_CS5535
 	depends on PCI && (X86_32 || (X86 && COMPILE_TEST))
 	---help---
 	  This is the core driver for CS5535/CS5536 MFD functions.  This is
-          necessary for using the board's GPIO and MFGPT functionality.
+	  necessary for using the board's GPIO and MFGPT functionality.
 
 config MFD_ACT8945A
 	tristate "Active-semi ACT8945A"
@@ -319,6 +319,23 @@ config MFD_HI6421_PMIC
 	  menus in order to enable them.
 	  We communicate with the Hi6421 via memory-mapped I/O.
 
+config MFD_MXS_LRADC
+	tristate "Freescale i.MX23/i.MX28 LRADC"
+	depends on ARCH_MXS || COMPILE_TEST
+	select MFD_CORE
+	select STMP_DEVICE
+	help
+	  Say yes here to build support for the low-resolution
+	  analog-to-digital converter (LRADC) found on the i.MX23 and i.MX28
+	  processors. This driver provides common support for accessing the
+	  device, additional drivers must be enabled in order to use the
+	  functionality of the device:
+		mxs-lradc-adc for ADC readings
+		mxs-lradc-ts  for touchscreen support
+
+	  This driver can also be built as a module. If so, the module will be
+	  called mxs-lradc.
+
 config HTC_EGPIO
 	bool "HTC EGPIO support"
 	depends on GPIOLIB && ARM
@@ -650,7 +667,7 @@ config EZX_PCAP
 	  needed for MMC, TouchScreen, Sound, USB, etc..
 
 config MFD_VIPERBOARD
-        tristate "Nano River Technologies Viperboard"
+	tristate "Nano River Technologies Viperboard"
 	select MFD_CORE
 	depends on USB
 	default n
@@ -898,11 +915,11 @@ config MFD_SMSC
        select MFD_CORE
        select REGMAP_I2C
        help
-        If you say yes here you get support for the
-        ece1099 chips from SMSC.
+	If you say yes here you get support for the
+	ece1099 chips from SMSC.
 
-        To compile this driver as a module, choose M here: the
-        module will be called smsc.
+	To compile this driver as a module, choose M here: the
+	module will be called smsc.
 
 config ABX500_CORE
 	bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
@@ -956,8 +973,8 @@ config AB8500_DEBUG
        depends on AB8500_GPADC && DEBUG_FS
        default y if DEBUG_FS
        help
-         Select this option if you want debug information using the debug
-         filesystem, debugfs.
+	 Select this option if you want debug information using the debug
+	 filesystem, debugfs.
 
 config AB8500_GPADC
 	bool "ST-Ericsson AB8500 GPADC driver"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 5eaa6465d..236b831 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -203,3 +203,4 @@ intel-soc-pmic-objs		:= intel_soc_pmic_core.o intel_soc_pmic_crc.o
 intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC)	+= intel_soc_pmic_bxtwc.o
 obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
 obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
+obj-$(CONFIG_MFD_MXS_LRADC)	+= mxs-lradc.o
diff --git a/drivers/mfd/mxs-lradc.c b/drivers/mfd/mxs-lradc.c
new file mode 100644
index 0000000..e1c8f9e
--- /dev/null
+++ b/drivers/mfd/mxs-lradc.c
@@ -0,0 +1,213 @@
+/*
+ * Freescale MXS LRADC driver
+ *
+ * Copyright (c) 2012 DENX Software Engineering, GmbH.
+ * Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/mxs-lradc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+static struct mfd_cell lradc_adc_dev = {
+	.name = DRIVER_NAME_ADC,
+};
+
+static struct mfd_cell lradc_ts_dev = {
+	.name = DRIVER_NAME_TS,
+};
+
+static const char * const mx23_lradc_irq_names[] = {
+	"mxs-lradc-touchscreen",
+	"mxs-lradc-channel0",
+	"mxs-lradc-channel1",
+	"mxs-lradc-channel2",
+	"mxs-lradc-channel3",
+	"mxs-lradc-channel4",
+	"mxs-lradc-channel5",
+	"mxs-lradc-channel6",
+	"mxs-lradc-channel7",
+};
+
+static const char * const mx28_lradc_irq_names[] = {
+	"mxs-lradc-touchscreen",
+	"mxs-lradc-thresh0",
+	"mxs-lradc-thresh1",
+	"mxs-lradc-channel0",
+	"mxs-lradc-channel1",
+	"mxs-lradc-channel2",
+	"mxs-lradc-channel3",
+	"mxs-lradc-channel4",
+	"mxs-lradc-channel5",
+	"mxs-lradc-channel6",
+	"mxs-lradc-channel7",
+	"mxs-lradc-button0",
+	"mxs-lradc-button1",
+};
+
+struct mxs_lradc_of_config {
+	const int		irq_count;
+	const char * const	*irq_name;
+};
+
+static const struct mxs_lradc_of_config mxs_lradc_of_config[] = {
+	[IMX23_LRADC] = {
+		.irq_count	= ARRAY_SIZE(mx23_lradc_irq_names),
+		.irq_name	= mx23_lradc_irq_names,
+	},
+	[IMX28_LRADC] = {
+		.irq_count	= ARRAY_SIZE(mx28_lradc_irq_names),
+		.irq_name	= mx28_lradc_irq_names,
+	},
+};
+
+static const struct of_device_id mxs_lradc_dt_ids[] = {
+	{ .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, },
+	{ .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
+
+static int mxs_lradc_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *of_id =
+		of_match_device(mxs_lradc_dt_ids, &pdev->dev);
+	const struct mxs_lradc_of_config *of_cfg =
+		&mxs_lradc_of_config[(enum mxs_lradc_id)of_id->data];
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct mxs_lradc *lradc;
+	struct resource *iores;
+	int ret = 0, touch_ret, i;
+	u32 ts_wires = 0;
+
+	lradc = devm_kzalloc(&pdev->dev, sizeof(*lradc), GFP_KERNEL);
+	if (!lradc)
+		return -ENOMEM;
+	lradc->soc = (enum mxs_lradc_id)of_id->data;
+
+	/* Grab the memory area */
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	lradc->base = devm_ioremap_resource(dev, iores);
+	if (IS_ERR(lradc->base))
+		return PTR_ERR(lradc->base);
+
+	lradc->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(lradc->clk)) {
+		dev_err(dev, "Failed to get the delay unit clock\n");
+		return PTR_ERR(lradc->clk);
+	}
+	ret = clk_prepare_enable(lradc->clk);
+	if (ret != 0) {
+		dev_err(dev, "Failed to enable the delay unit clock\n");
+		return ret;
+	}
+
+	touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
+					 &ts_wires);
+
+	if (touch_ret == 0)
+		lradc->buffer_vchans = BUFFER_VCHANS_LIMITED;
+	else
+		lradc->buffer_vchans = BUFFER_VCHANS_ALL;
+
+	lradc->irq_count = of_cfg->irq_count;
+	lradc->irq_name = of_cfg->irq_name;
+	for (i = 0; i < lradc->irq_count; i++) {
+		lradc->irq[i] = platform_get_irq(pdev, i);
+		if (lradc->irq[i] < 0) {
+			ret = lradc->irq[i];
+			goto err_clk;
+		}
+	}
+
+	platform_set_drvdata(pdev, lradc);
+
+	ret = stmp_reset_block(lradc->base);
+
+	if (ret)
+		return ret;
+
+	lradc_adc_dev.platform_data = lradc;
+	lradc_adc_dev.pdata_size = sizeof(*lradc);
+
+	ret = mfd_add_devices(&pdev->dev, -1, &lradc_adc_dev, 1, NULL, 0, NULL);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to add the ADC subdevice\n");
+		return ret;
+	}
+
+	lradc_ts_dev.platform_data = lradc;
+	lradc_ts_dev.pdata_size = sizeof(*lradc);
+
+	switch (ts_wires) {
+	case 4:
+		lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE;
+		break;
+	case 5:
+		if (lradc->soc == IMX28_LRADC) {
+			lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE;
+			break;
+		}
+		/* fall through an error message for i.MX23 */
+	default:
+		dev_err(&pdev->dev,
+			"Unsupported number of touchscreen wires (%d)\n",
+			ts_wires);
+		return -EINVAL;
+	}
+
+	ret = mfd_add_devices(&pdev->dev, -1, &lradc_ts_dev, 1, NULL, 0, NULL);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Failed to add the touchscreen subdevice\n");
+		goto err_remove_adc;
+	}
+
+	return 0;
+
+err_remove_adc:
+	mfd_remove_devices(&pdev->dev);
+err_clk:
+	clk_disable_unprepare(lradc->clk);
+	return ret;
+}
+
+static int mxs_lradc_remove(struct platform_device *pdev)
+{
+	struct mxs_lradc *lradc = platform_get_drvdata(pdev);
+
+	mfd_remove_devices(&pdev->dev);
+	clk_disable_unprepare(lradc->clk);
+	return 0;
+}
+
+static struct platform_driver mxs_lradc_driver = {
+	.driver = {
+		.name = "mxs-lradc",
+		.of_match_table = mxs_lradc_dt_ids,
+	},
+	.probe = mxs_lradc_probe,
+	.remove = mxs_lradc_remove,
+};
+
+module_platform_driver(mxs_lradc_driver);
+
+MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mxs-lradc");
diff --git a/include/linux/mfd/mxs-lradc.h b/include/linux/mfd/mxs-lradc.h
new file mode 100644
index 0000000..c062969
--- /dev/null
+++ b/include/linux/mfd/mxs-lradc.h
@@ -0,0 +1,210 @@
+/*
+ * Freescale MXS LRADC driver
+ *
+ * Copyright (c) 2012 DENX Software Engineering, GmbH.
+ * Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MXS_LRADC_H
+#define __MXS_LRADC_H
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/stmp_device.h>
+#include <linux/sysfs.h>
+
+#define DRIVER_NAME_ADC "mxs-lradc-adc"
+#define DRIVER_NAME_TS "mxs-lradc-ts"
+
+#define LRADC_MAX_DELAY_CHANS	4
+#define LRADC_MAX_MAPPED_CHANS	8
+#define LRADC_MAX_TOTAL_CHANS	16
+
+#define LRADC_DELAY_TIMER_HZ	2000
+
+#define LRADC_CTRL0				0x00
+# define LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE	BIT(23)
+# define LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE	BIT(22)
+# define LRADC_CTRL0_MX28_YNNSW /* YM */	BIT(21)
+# define LRADC_CTRL0_MX28_YPNSW /* YP */	BIT(20)
+# define LRADC_CTRL0_MX28_YPPSW /* YP */	BIT(19)
+# define LRADC_CTRL0_MX28_XNNSW /* XM */	BIT(18)
+# define LRADC_CTRL0_MX28_XNPSW /* XM */	BIT(17)
+# define LRADC_CTRL0_MX28_XPPSW /* XP */	BIT(16)
+
+# define LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE	BIT(20)
+# define LRADC_CTRL0_MX23_YM			BIT(19)
+# define LRADC_CTRL0_MX23_XM			BIT(18)
+# define LRADC_CTRL0_MX23_YP			BIT(17)
+# define LRADC_CTRL0_MX23_XP			BIT(16)
+
+# define LRADC_CTRL0_MX28_PLATE_MASK \
+		(LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE | \
+		LRADC_CTRL0_MX28_YNNSW | LRADC_CTRL0_MX28_YPNSW | \
+		LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW | \
+		LRADC_CTRL0_MX28_XNPSW | LRADC_CTRL0_MX28_XPPSW)
+
+# define LRADC_CTRL0_MX23_PLATE_MASK \
+		(LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE | \
+		LRADC_CTRL0_MX23_YM | LRADC_CTRL0_MX23_XM | \
+		LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XP)
+
+#define LRADC_CTRL1				0x10
+#define LRADC_CTRL1_TOUCH_DETECT_IRQ_EN		BIT(24)
+#define LRADC_CTRL1_LRADC_IRQ_EN(n)		(1 << ((n) + 16))
+#define LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK	(0x1fff << 16)
+#define LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK	(0x01ff << 16)
+#define LRADC_CTRL1_LRADC_IRQ_EN_OFFSET		16
+#define LRADC_CTRL1_TOUCH_DETECT_IRQ		BIT(8)
+#define LRADC_CTRL1_LRADC_IRQ(n)		(1 << (n))
+#define LRADC_CTRL1_MX28_LRADC_IRQ_MASK		0x1fff
+#define LRADC_CTRL1_MX23_LRADC_IRQ_MASK		0x01ff
+#define LRADC_CTRL1_LRADC_IRQ_OFFSET		0
+
+#define LRADC_CTRL2				0x20
+#define LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET	24
+#define LRADC_CTRL2_TEMPSENSE_PWD		BIT(15)
+
+#define LRADC_STATUS				0x40
+#define LRADC_STATUS_TOUCH_DETECT_RAW		BIT(0)
+
+#define LRADC_CH(n)				(0x50 + (0x10 * (n)))
+#define LRADC_CH_ACCUMULATE			BIT(29)
+#define LRADC_CH_NUM_SAMPLES_MASK		(0x1f << 24)
+#define LRADC_CH_NUM_SAMPLES_OFFSET		24
+#define LRADC_CH_NUM_SAMPLES(x) \
+				((x) << LRADC_CH_NUM_SAMPLES_OFFSET)
+#define LRADC_CH_VALUE_MASK			0x3ffff
+#define LRADC_CH_VALUE_OFFSET			0
+
+#define LRADC_DELAY(n)				(0xd0 + (0x10 * (n)))
+#define LRADC_DELAY_TRIGGER_LRADCS_MASK		(0xffUL << 24)
+#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET	24
+#define LRADC_DELAY_TRIGGER(x) \
+				(((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \
+				LRADC_DELAY_TRIGGER_LRADCS_MASK)
+#define LRADC_DELAY_KICK			BIT(20)
+#define LRADC_DELAY_TRIGGER_DELAYS_MASK		(0xf << 16)
+#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET	16
+#define LRADC_DELAY_TRIGGER_DELAYS(x) \
+				(((x) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) & \
+				LRADC_DELAY_TRIGGER_DELAYS_MASK)
+#define LRADC_DELAY_LOOP_COUNT_MASK		(0x1f << 11)
+#define LRADC_DELAY_LOOP_COUNT_OFFSET		11
+#define LRADC_DELAY_LOOP(x) \
+				(((x) << LRADC_DELAY_LOOP_COUNT_OFFSET) & \
+				LRADC_DELAY_LOOP_COUNT_MASK)
+#define LRADC_DELAY_DELAY_MASK			0x7ff
+#define LRADC_DELAY_DELAY_OFFSET		0
+#define LRADC_DELAY_DELAY(x) \
+				(((x) << LRADC_DELAY_DELAY_OFFSET) & \
+				LRADC_DELAY_DELAY_MASK)
+
+#define LRADC_CTRL4				0x140
+#define LRADC_CTRL4_LRADCSELECT_MASK(n)		(0xf << ((n) * 4))
+#define LRADC_CTRL4_LRADCSELECT_OFFSET(n)	((n) * 4)
+#define LRADC_CTRL4_LRADCSELECT(n, x) \
+				(((x) << LRADC_CTRL4_LRADCSELECT_OFFSET(n)) & \
+				LRADC_CTRL4_LRADCSELECT_MASK(n))
+
+#define LRADC_RESOLUTION			12
+#define LRADC_SINGLE_SAMPLE_MASK		((1 << LRADC_RESOLUTION) - 1)
+
+enum mxs_lradc_id {
+	IMX23_LRADC,
+	IMX28_LRADC,
+};
+
+enum mxs_lradc_ts_wires {
+	MXS_LRADC_TOUCHSCREEN_NONE = 0,
+	MXS_LRADC_TOUCHSCREEN_4WIRE,
+	MXS_LRADC_TOUCHSCREEN_5WIRE,
+};
+
+struct mxs_lradc {
+	enum mxs_lradc_id	soc;
+
+	void __iomem		*base;
+	struct clk		*clk;
+
+	int			irq[13];
+	const char * const	*irq_name;
+	int			irq_count;
+
+#define BUFFER_VCHANS_LIMITED		0x3f
+#define BUFFER_VCHANS_ALL		0xff
+	u8			buffer_vchans;
+
+	/*
+	 * Certain LRADC channels are shared between touchscreen
+	 * and/or touch-buttons and generic LRADC block. Therefore when using
+	 * either of these, these channels are not available for the regular
+	 * sampling. The shared channels are as follows:
+	 *
+	 * CH0 -- Touch button #0
+	 * CH1 -- Touch button #1
+	 * CH2 -- Touch screen XPUL
+	 * CH3 -- Touch screen YPLL
+	 * CH4 -- Touch screen XNUL
+	 * CH5 -- Touch screen YNLR
+	 * CH6 -- Touch screen WIPER (5-wire only)
+	 *
+	 * The bit fields below represents which parts of the LRADC block are
+	 * switched into special mode of operation. These channels can not
+	 * be sampled as regular LRADC channels. The driver will refuse any
+	 * attempt to sample these channels.
+	 */
+#define CHAN_MASK_TOUCHBUTTON		(BIT(1) | BIT(0))
+#define CHAN_MASK_TOUCHSCREEN_4WIRE	(0xf << 2)
+#define CHAN_MASK_TOUCHSCREEN_5WIRE	(0x1f << 2)
+	enum mxs_lradc_ts_wires	use_touchscreen;
+	bool			use_touchbutton;
+};
+
+static inline void mxs_lradc_reg_set(struct mxs_lradc *lradc, u32 val, u32 reg)
+{
+	writel(val, lradc->base + reg + STMP_OFFSET_REG_SET);
+}
+
+static inline void mxs_lradc_reg_clear(struct mxs_lradc *lradc, u32 val,
+				       u32 reg)
+{
+	writel(val, lradc->base + reg + STMP_OFFSET_REG_CLR);
+}
+
+static inline void mxs_lradc_reg_wrt(struct mxs_lradc *lradc, u32 val, u32 reg)
+{
+	writel(val, lradc->base + reg);
+}
+
+static inline u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc)
+{
+	if (lradc->soc == IMX23_LRADC)
+		return LRADC_CTRL1_MX23_LRADC_IRQ_MASK;
+	return LRADC_CTRL1_MX28_LRADC_IRQ_MASK;
+}
+
+#endif /* __MXS_LRADC_H */
-- 
1.9.1

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

* [PATCH 2/3] iio: adc: mxs-lradc: Add support for adc driver
  2016-04-29 11:46 ` Ksenija Stanojevic
  (?)
  (?)
@ 2016-04-29 11:48 ` Ksenija Stanojevic
  2016-04-29 13:21   ` Marek Vasut
  2016-05-01 16:38     ` Jonathan Cameron
  -1 siblings, 2 replies; 40+ messages in thread
From: Ksenija Stanojevic @ 2016-04-29 11:48 UTC (permalink / raw)
  To: linux-kernel
  Cc: lee.jones, dmitry.torokhov, linux-input, jic23, knaack.h, lars,
	pmeerw, marex, linux-iio, harald, Ksenija Stanojevic

Add mxs-lradc adc driver.

Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
---
 drivers/iio/adc/Kconfig         |  37 +-
 drivers/iio/adc/Makefile        |   1 +
 drivers/iio/adc/mxs-lradc-adc.c | 832 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 858 insertions(+), 12 deletions(-)
 create mode 100644 drivers/iio/adc/mxs-lradc-adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 82c718c..50847b8 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -233,6 +233,19 @@ config IMX7D_ADC
 	  This driver can also be built as a module. If so, the module will be
 	  called imx7d_adc.
 
+config MXS_LRADC_ADC
+	tristate "Freescale i.MX23/i.MX28 LRADC ADC"
+	depends on MFD_MXS_LRADC
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Say yes here to build support for the ADC functions of the
+	  i.MX23/i.MX28 LRADC. This includes general-purpose ADC readings,
+	  battery voltage measurement, and die temperature measurement.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called mxs-lradc-adc.
+
 config LP8788_ADC
 	tristate "LP8788 ADC driver"
 	depends on MFD_LP8788
@@ -306,18 +319,18 @@ config MEN_Z188_ADC
 	  called men_z188_adc.
 
 config MXS_LRADC
-        tristate "Freescale i.MX23/i.MX28 LRADC"
-        depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
-        depends on INPUT
-        select STMP_DEVICE
-        select IIO_BUFFER
-        select IIO_TRIGGERED_BUFFER
-        help
-          Say yes here to build support for i.MX23/i.MX28 LRADC convertor
-          built into these chips.
-
-          To compile this driver as a module, choose M here: the
-          module will be called mxs-lradc.
+	tristate "Freescale i.MX23/i.MX28 LRADC"
+	depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
+	depends on INPUT
+	select STMP_DEVICE
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Say yes here to build support for i.MX23/i.MX28 LRADC convertor
+	  built into these chips.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mxs-lradc.
 
 config NAU7802
 	tristate "Nuvoton NAU7802 ADC driver"
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 0cb7921..ca7d6aa 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
 obj-$(CONFIG_MCP3422) += mcp3422.o
 obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
 obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
+obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
 obj-$(CONFIG_NAU7802) += nau7802.o
 obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
 obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c
new file mode 100644
index 0000000..793c369
--- /dev/null
+++ b/drivers/iio/adc/mxs-lradc-adc.c
@@ -0,0 +1,832 @@
+/*
+ * Freescale MXS LRADC ADC driver
+ *
+ * Copyright (c) 2012 DENX Software Engineering, GmbH.
+ * Marek Vasut <marex@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mfd/mxs-lradc.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/sysfs.h>
+
+/*
+ * Make this runtime configurable if necessary. Currently, if the buffered mode
+ * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before
+ * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000)
+ * seconds. The result is that the samples arrive every 500mS.
+ */
+#define LRADC_DELAY_TIMER_PER	200
+#define LRADC_DELAY_TIMER_LOOP	5
+
+#define VREF_MV_BASE 1850
+
+static const u32 mxs_lradc_adc_vref_mv[][LRADC_MAX_TOTAL_CHANS] = {
+	[IMX23_LRADC] = {
+		VREF_MV_BASE,		/* CH0 */
+		VREF_MV_BASE,		/* CH1 */
+		VREF_MV_BASE,		/* CH2 */
+		VREF_MV_BASE,		/* CH3 */
+		VREF_MV_BASE,		/* CH4 */
+		VREF_MV_BASE,		/* CH5 */
+		VREF_MV_BASE * 2,	/* CH6 VDDIO */
+		VREF_MV_BASE * 4,	/* CH7 VBATT */
+		VREF_MV_BASE,		/* CH8 Temp sense 0 */
+		VREF_MV_BASE,		/* CH9 Temp sense 1 */
+		VREF_MV_BASE,		/* CH10 */
+		VREF_MV_BASE,		/* CH11 */
+		VREF_MV_BASE,		/* CH12 USB_DP */
+		VREF_MV_BASE,		/* CH13 USB_DN */
+		VREF_MV_BASE,		/* CH14 VBG */
+		VREF_MV_BASE * 4,	/* CH15 VDD5V */
+	},
+	[IMX28_LRADC] = {
+		VREF_MV_BASE,		/* CH0 */
+		VREF_MV_BASE,		/* CH1 */
+		VREF_MV_BASE,		/* CH2 */
+		VREF_MV_BASE,		/* CH3 */
+		VREF_MV_BASE,		/* CH4 */
+		VREF_MV_BASE,		/* CH5 */
+		VREF_MV_BASE,		/* CH6 */
+		VREF_MV_BASE * 4,	/* CH7 VBATT */
+		VREF_MV_BASE,		/* CH8 Temp sense 0 */
+		VREF_MV_BASE,		/* CH9 Temp sense 1 */
+		VREF_MV_BASE * 2,	/* CH10 VDDIO */
+		VREF_MV_BASE,		/* CH11 VTH */
+		VREF_MV_BASE * 2,	/* CH12 VDDA */
+		VREF_MV_BASE,		/* CH13 VDDD */
+		VREF_MV_BASE,		/* CH14 VBG */
+		VREF_MV_BASE * 4,	/* CH15 VDD5V */
+	},
+};
+
+enum mxs_lradc_divbytwo {
+	MXS_LRADC_DIV_DISABLED = 0,
+	MXS_LRADC_DIV_ENABLED,
+};
+
+struct mxs_lradc_scale {
+	unsigned int		integer;
+	unsigned int		nano;
+};
+
+struct mxs_lradc_adc {
+	struct mxs_lradc	*lradc;
+	struct device		*dev;
+
+	u32			*buffer;
+	struct iio_trigger	*trig;
+	struct mutex		lock;
+	struct completion	completion;
+
+	const u32		*vref_mv;
+	struct mxs_lradc_scale	scale_avail[LRADC_MAX_TOTAL_CHANS][2];
+	unsigned long		is_divided;
+};
+
+/*
+ * Raw I/O operations
+ */
+static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan,
+				     int *val)
+{
+	struct mxs_lradc_adc *adc = iio_priv(iio_dev);
+	struct mxs_lradc *lradc = adc->lradc;
+	int ret;
+
+	/*
+	 * See if there is no buffered operation in progress. If there is simply
+	 * bail out. This can be improved to support both buffered and raw IO at
+	 * the same time, yet the code becomes horribly complicated. Therefore I
+	 * applied KISS principle here.
+	 */
+	ret = mutex_trylock(&adc->lock);
+	if (!ret)
+		return -EBUSY;
+
+	reinit_completion(&adc->completion);
+
+	/*
+	 * No buffered operation in progress, map the channel and trigger it.
+	 * Virtual channel 0 is always used here as the others are always not
+	 * used if doing raw sampling.
+	 */
+	if (lradc->soc == IMX28_LRADC)
+		mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0),
+				    LRADC_CTRL1);
+	mxs_lradc_reg_clear(lradc, 0x1, LRADC_CTRL0);
+
+	/* Enable / disable the divider per requirement */
+	if (test_bit(chan, &adc->is_divided))
+		mxs_lradc_reg_set(lradc,
+				  1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
+				  LRADC_CTRL2);
+	else
+		mxs_lradc_reg_clear(lradc,
+				    1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
+				    LRADC_CTRL2);
+
+	/* Clean the slot's previous content, then set new one. */
+	mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(0),
+			    LRADC_CTRL4);
+	mxs_lradc_reg_set(lradc, chan, LRADC_CTRL4);
+
+	mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(0));
+
+	/* Enable the IRQ and start sampling the channel. */
+	mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
+	mxs_lradc_reg_set(lradc, BIT(0), LRADC_CTRL0);
+
+	/* Wait for completion on the channel, 1 second max. */
+	ret = wait_for_completion_killable_timeout(&adc->completion, HZ);
+	if (!ret)
+		ret = -ETIMEDOUT;
+	if (ret < 0)
+		goto err;
+
+	/* Read the data. */
+	*val = readl(lradc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
+	ret = IIO_VAL_INT;
+
+err:
+	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
+
+	mutex_unlock(&adc->lock);
+
+	return ret;
+}
+
+static int mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val)
+{
+	int ret, min, max;
+
+	ret = mxs_lradc_adc_read_single(iio_dev, 8, &min);
+	if (ret != IIO_VAL_INT)
+		return ret;
+
+	ret = mxs_lradc_adc_read_single(iio_dev, 9, &max);
+	if (ret != IIO_VAL_INT)
+		return ret;
+
+	*val = max - min;
+
+	return IIO_VAL_INT;
+}
+
+static int mxs_lradc_adc_read_raw(struct iio_dev *iio_dev,
+			      const struct iio_chan_spec *chan,
+			      int *val, int *val2, long m)
+{
+	struct mxs_lradc_adc *adc = iio_priv(iio_dev);
+
+	switch (m) {
+	case IIO_CHAN_INFO_RAW:
+		if (chan->type == IIO_TEMP)
+			return mxs_lradc_adc_read_temp(iio_dev, val);
+
+		return mxs_lradc_adc_read_single(iio_dev, chan->channel, val);
+
+	case IIO_CHAN_INFO_SCALE:
+		if (chan->type == IIO_TEMP) {
+			/* From the datasheet, we have to multiply by 1.012 and
+			 * divide by 4
+			 */
+			*val = 0;
+			*val2 = 253000;
+			return IIO_VAL_INT_PLUS_MICRO;
+		}
+
+		*val = adc->vref_mv[chan->channel];
+		*val2 = chan->scan_type.realbits -
+			test_bit(chan->channel, &adc->is_divided);
+		return IIO_VAL_FRACTIONAL_LOG2;
+
+	case IIO_CHAN_INFO_OFFSET:
+		if (chan->type == IIO_TEMP) {
+			/* The calculated value from the ADC is in Kelvin, we
+			 * want Celsius for hwmon so the offset is -273.15
+			 * The offset is applied before scaling so it is
+			 * actually -213.15 * 4 / 1.012 = -1079.644268
+			 */
+			*val = -1079;
+			*val2 = 644268;
+
+			return IIO_VAL_INT_PLUS_MICRO;
+		}
+
+		return -EINVAL;
+
+	default:
+		break;
+	}
+
+	return -EINVAL;
+}
+
+static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev,
+				   const struct iio_chan_spec *chan,
+				   int val, int val2, long m)
+{
+	struct mxs_lradc_adc *adc = iio_priv(iio_dev);
+	struct mxs_lradc_scale *scale_avail =
+			adc->scale_avail[chan->channel];
+	int ret;
+
+	ret = mutex_trylock(&adc->lock);
+	if (!ret)
+		return -EBUSY;
+
+	switch (m) {
+	case IIO_CHAN_INFO_SCALE:
+		ret = -EINVAL;
+		if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
+		    val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) {
+			/* divider by two disabled */
+			clear_bit(chan->channel, &adc->is_divided);
+			ret = 0;
+		} else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
+			   val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) {
+			/* divider by two enabled */
+			set_bit(chan->channel, &adc->is_divided);
+			ret = 0;
+		}
+
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	mutex_unlock(&adc->lock);
+
+	return ret;
+}
+
+static int mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev,
+					   const struct iio_chan_spec *chan,
+					   long m)
+{
+	return IIO_VAL_INT_PLUS_NANO;
+}
+
+static ssize_t mxs_lradc_adc_show_scale_avail_ch(struct device *dev,
+						 struct device_attribute *attr,
+						 char *buf,
+						 int ch)
+{
+	struct iio_dev *iio = dev_to_iio_dev(dev);
+	struct mxs_lradc_adc *adc = iio_priv(iio);
+	int i, len = 0;
+
+	for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++)
+		len += sprintf(buf + len, "%u.%09u ",
+			       adc->scale_avail[ch][i].integer,
+			       adc->scale_avail[ch][i].nano);
+
+	len += sprintf(buf + len, "\n");
+
+	return len;
+}
+
+static ssize_t mxs_lradc_adc_show_scale_avail(struct device *dev,
+					      struct device_attribute *attr,
+					      char *buf)
+{
+	struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
+
+	return mxs_lradc_adc_show_scale_avail_ch(dev, attr, buf,
+						 iio_attr->address);
+}
+
+#define SHOW_SCALE_AVAILABLE_ATTR(ch)					\
+static IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, S_IRUGO,	\
+		       mxs_lradc_adc_show_scale_avail, NULL, ch)
+
+SHOW_SCALE_AVAILABLE_ATTR(0);
+SHOW_SCALE_AVAILABLE_ATTR(1);
+SHOW_SCALE_AVAILABLE_ATTR(2);
+SHOW_SCALE_AVAILABLE_ATTR(3);
+SHOW_SCALE_AVAILABLE_ATTR(4);
+SHOW_SCALE_AVAILABLE_ATTR(5);
+SHOW_SCALE_AVAILABLE_ATTR(6);
+SHOW_SCALE_AVAILABLE_ATTR(7);
+SHOW_SCALE_AVAILABLE_ATTR(10);
+SHOW_SCALE_AVAILABLE_ATTR(11);
+SHOW_SCALE_AVAILABLE_ATTR(12);
+SHOW_SCALE_AVAILABLE_ATTR(13);
+SHOW_SCALE_AVAILABLE_ATTR(14);
+SHOW_SCALE_AVAILABLE_ATTR(15);
+
+static struct attribute *mxs_lradc_adc_attributes[] = {
+	&iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
+	&iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
+	&iio_dev_attr_in_voltage2_scale_available.dev_attr.attr,
+	&iio_dev_attr_in_voltage3_scale_available.dev_attr.attr,
+	&iio_dev_attr_in_voltage4_scale_available.dev_attr.attr,
+	&iio_dev_attr_in_voltage5_scale_available.dev_attr.attr,
+	&iio_dev_attr_in_voltage6_scale_available.dev_attr.attr,
+	&iio_dev_attr_in_voltage7_scale_available.dev_attr.attr,
+	&iio_dev_attr_in_voltage10_scale_available.dev_attr.attr,
+	&iio_dev_attr_in_voltage11_scale_available.dev_attr.attr,
+	&iio_dev_attr_in_voltage12_scale_available.dev_attr.attr,
+	&iio_dev_attr_in_voltage13_scale_available.dev_attr.attr,
+	&iio_dev_attr_in_voltage14_scale_available.dev_attr.attr,
+	&iio_dev_attr_in_voltage15_scale_available.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group mxs_lradc_adc_attribute_group = {
+	.attrs = mxs_lradc_adc_attributes,
+};
+
+static const struct iio_info mxs_lradc_adc_iio_info = {
+	.driver_module		= THIS_MODULE,
+	.read_raw		= mxs_lradc_adc_read_raw,
+	.write_raw		= mxs_lradc_adc_write_raw,
+	.write_raw_get_fmt	= mxs_lradc_adc_write_raw_get_fmt,
+	.attrs			= &mxs_lradc_adc_attribute_group,
+};
+
+/*
+ * IRQ Handling
+ */
+static irqreturn_t mxs_lradc_adc_handle_irq(int irq, void *data)
+{
+	struct iio_dev *iio = data;
+	struct mxs_lradc_adc *adc = iio_priv(iio);
+	struct mxs_lradc *lradc = adc->lradc;
+	unsigned long reg = readl(lradc->base + LRADC_CTRL1);
+
+	if (!(reg & mxs_lradc_irq_mask(lradc)))
+		return IRQ_NONE;
+
+	if (iio_buffer_enabled(iio)) {
+		if (reg & lradc->buffer_vchans)
+			iio_trigger_poll(iio->trig);
+	} else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) {
+		complete(&adc->completion);
+	}
+
+	mxs_lradc_reg_clear(lradc, reg & mxs_lradc_irq_mask(lradc),
+			    LRADC_CTRL1);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Trigger handling
+ */
+static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *iio = pf->indio_dev;
+	struct mxs_lradc_adc *adc = iio_priv(iio);
+	const u32 chan_value = LRADC_CH_ACCUMULATE |
+		((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
+	unsigned int i, j = 0;
+
+	for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
+		adc->buffer[j] = readl(adc->lradc->base + LRADC_CH(j));
+		mxs_lradc_reg_wrt(adc->lradc, chan_value, LRADC_CH(j));
+		adc->buffer[j] &= LRADC_CH_VALUE_MASK;
+		adc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
+		j++;
+	}
+
+	iio_push_to_buffers_with_timestamp(iio, adc->buffer, pf->timestamp);
+
+	iio_trigger_notify_done(iio->trig);
+
+	return IRQ_HANDLED;
+}
+
+static int mxs_lradc_adc_configure_trigger(struct iio_trigger *trig, bool state)
+{
+	struct iio_dev *iio = iio_trigger_get_drvdata(trig);
+	struct mxs_lradc_adc *adc = iio_priv(iio);
+	const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
+
+	mxs_lradc_reg_wrt(adc->lradc, LRADC_DELAY_KICK, LRADC_DELAY(0) + st);
+
+	return 0;
+}
+
+static const struct iio_trigger_ops mxs_lradc_adc_trigger_ops = {
+	.owner = THIS_MODULE,
+	.set_trigger_state = &mxs_lradc_adc_configure_trigger,
+};
+
+static int mxs_lradc_adc_trigger_init(struct iio_dev *iio)
+{
+	int ret;
+	struct iio_trigger *trig;
+	struct mxs_lradc_adc *adc = iio_priv(iio);
+
+	trig = iio_trigger_alloc("%s-dev%i", iio->name, iio->id);
+	if (!trig)
+		return -ENOMEM;
+
+	trig->dev.parent = adc->dev;
+	iio_trigger_set_drvdata(trig, iio);
+	trig->ops = &mxs_lradc_adc_trigger_ops;
+
+	ret = iio_trigger_register(trig);
+	if (ret) {
+		iio_trigger_free(trig);
+		return ret;
+	}
+
+	adc->trig = trig;
+
+	return 0;
+}
+
+static void mxs_lradc_adc_trigger_remove(struct iio_dev *iio)
+{
+	struct mxs_lradc_adc *adc = iio_priv(iio);
+
+	iio_trigger_unregister(adc->trig);
+	iio_trigger_free(adc->trig);
+}
+
+static int mxs_lradc_adc_buffer_preenable(struct iio_dev *iio)
+{
+	struct mxs_lradc_adc *adc = iio_priv(iio);
+	struct mxs_lradc *lradc = adc->lradc;
+	int ret = 0, chan, ofs = 0;
+	unsigned long enable = 0;
+	u32 ctrl4_set = 0;
+	u32 ctrl4_clr = 0;
+	u32 ctrl1_irq = 0;
+	const u32 chan_value = LRADC_CH_ACCUMULATE |
+		((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
+	const int len = bitmap_weight(iio->active_scan_mask,
+			LRADC_MAX_TOTAL_CHANS);
+
+	if (!len)
+		return -EINVAL;
+
+	/*
+	 * Lock the driver so raw access can not be done during buffered
+	 * operation. This simplifies the code a lot.
+	 */
+	ret = mutex_trylock(&adc->lock);
+	if (!ret)
+		return -EBUSY;
+
+	adc->buffer = kmalloc_array(len, sizeof(*adc->buffer), GFP_KERNEL);
+	if (!adc->buffer) {
+		ret = -ENOMEM;
+		goto err_mem;
+	}
+
+	if (lradc->soc == IMX28_LRADC)
+		mxs_lradc_reg_clear(
+			lradc,
+			lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
+			LRADC_CTRL1);
+	mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
+
+	for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
+		ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
+		ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
+		ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
+		mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(ofs));
+		bitmap_set(&enable, ofs, 1);
+		ofs++;
+	}
+
+	mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
+			    LRADC_DELAY_KICK, LRADC_DELAY(0));
+	mxs_lradc_reg_clear(lradc, ctrl4_clr, LRADC_CTRL4);
+	mxs_lradc_reg_set(lradc, ctrl4_set, LRADC_CTRL4);
+	mxs_lradc_reg_set(lradc, ctrl1_irq, LRADC_CTRL1);
+	mxs_lradc_reg_set(lradc, enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
+			  LRADC_DELAY(0));
+
+	return 0;
+
+err_mem:
+	mutex_unlock(&adc->lock);
+	return ret;
+}
+
+static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio)
+{
+	struct mxs_lradc_adc *adc = iio_priv(iio);
+	struct mxs_lradc *lradc = adc->lradc;
+
+	mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
+			    LRADC_DELAY_KICK, LRADC_DELAY(0));
+
+	mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
+	if (lradc->soc == IMX28_LRADC)
+		mxs_lradc_reg_clear(
+			lradc,
+			lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
+			LRADC_CTRL1);
+
+	kfree(adc->buffer);
+	mutex_unlock(&adc->lock);
+
+	return 0;
+}
+
+static bool mxs_lradc_adc_validate_scan_mask(struct iio_dev *iio,
+					     const unsigned long *mask)
+{
+	struct mxs_lradc_adc *adc = iio_priv(iio);
+	struct mxs_lradc *lradc = adc->lradc;
+	const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS);
+	int rsvd_chans = 0;
+	unsigned long rsvd_mask = 0;
+
+	if (lradc->use_touchbutton)
+		rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
+	if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_4WIRE)
+		rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
+	if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
+		rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
+
+	if (lradc->use_touchbutton)
+		rsvd_chans++;
+	if (lradc->use_touchscreen)
+		rsvd_chans += 2;
+
+	/* Test for attempts to map channels with special mode of operation. */
+	if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
+		return false;
+
+	/* Test for attempts to map more channels then available slots. */
+	if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
+		return false;
+
+	return true;
+}
+
+static const struct iio_buffer_setup_ops mxs_lradc_adc_buffer_ops = {
+	.preenable = &mxs_lradc_adc_buffer_preenable,
+	.postenable = &iio_triggered_buffer_postenable,
+	.predisable = &iio_triggered_buffer_predisable,
+	.postdisable = &mxs_lradc_adc_buffer_postdisable,
+	.validate_scan_mask = &mxs_lradc_adc_validate_scan_mask,
+};
+
+/*
+ * Driver initialization
+ */
+
+#define MXS_ADC_CHAN(idx, chan_type, name) {			\
+	.type = (chan_type),					\
+	.indexed = 1,						\
+	.scan_index = (idx),					\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |		\
+			      BIT(IIO_CHAN_INFO_SCALE),		\
+	.channel = (idx),					\
+	.address = (idx),					\
+	.scan_type = {						\
+		.sign = 'u',					\
+		.realbits = LRADC_RESOLUTION,			\
+		.storagebits = 32,				\
+	},							\
+	.datasheet_name = (name),				\
+}
+
+static const struct iio_chan_spec mx23_lradc_chan_spec[] = {
+	MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
+	MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
+	MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
+	MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
+	MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
+	MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
+	MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"),
+	MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
+	/* Combined Temperature sensors */
+	{
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.scan_index = 8,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_OFFSET) |
+				      BIT(IIO_CHAN_INFO_SCALE),
+		.channel = 8,
+		.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
+		.datasheet_name = "TEMP_DIE",
+	},
+	/* Hidden channel to keep indexes */
+	{
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.scan_index = -1,
+		.channel = 9,
+	},
+	MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL),
+	MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL),
+	MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"),
+	MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"),
+	MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
+	MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
+};
+
+static const struct iio_chan_spec mx28_lradc_chan_spec[] = {
+	MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
+	MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
+	MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
+	MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
+	MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
+	MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
+	MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"),
+	MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
+	/* Combined Temperature sensors */
+	{
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.scan_index = 8,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_OFFSET) |
+				      BIT(IIO_CHAN_INFO_SCALE),
+		.channel = 8,
+		.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
+		.datasheet_name = "TEMP_DIE",
+	},
+	/* Hidden channel to keep indexes */
+	{
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.scan_index = -1,
+		.channel = 9,
+	},
+	MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"),
+	MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"),
+	MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"),
+	MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"),
+	MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
+	MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
+};
+
+static void mxs_lradc_adc_hw_init(struct mxs_lradc_adc *adc)
+{
+	struct mxs_lradc *lradc = adc->lradc;
+
+	/* The ADC always uses DELAY CHANNEL 0. */
+	const u32 adc_cfg =
+		(1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
+		(LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
+
+	/* Configure DELAY CHANNEL 0 for generic ADC sampling. */
+	mxs_lradc_reg_wrt(lradc, adc_cfg, LRADC_DELAY(0));
+
+	/* Start internal temperature sensing. */
+	mxs_lradc_reg_wrt(lradc, 0, LRADC_CTRL2);
+}
+
+static void mxs_lradc_adc_hw_stop(struct mxs_lradc_adc *adc)
+{
+	mxs_lradc_reg_wrt(adc->lradc, 0, LRADC_DELAY(0));
+}
+
+static int mxs_lradc_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mxs_lradc *lradc = dev_get_platdata(dev);
+	struct mxs_lradc_adc *adc;
+	struct iio_dev *iio;
+	int ret, i, s;
+	u64 scale_uv;
+
+	/* Allocate the IIO device. */
+	iio = devm_iio_device_alloc(dev, sizeof(*adc));
+	if (!iio) {
+		dev_err(dev, "Failed to allocate IIO device\n");
+		return -ENOMEM;
+	}
+
+	adc = iio_priv(iio);
+	adc->lradc = lradc;
+	adc->dev = dev;
+
+	init_completion(&adc->completion);
+	mutex_init(&adc->lock);
+
+	for (i = 0; i < lradc->irq_count; i++) {
+		ret = devm_request_irq(dev, lradc->irq[i],
+				       mxs_lradc_adc_handle_irq,
+				       IRQF_SHARED, lradc->irq_name[i], iio);
+		if (ret)
+			return ret;
+	}
+
+	platform_set_drvdata(pdev, iio);
+
+	iio->name = pdev->name;
+	iio->dev.parent = dev;
+	iio->dev.of_node = dev->parent->of_node;
+	iio->info = &mxs_lradc_adc_iio_info;
+	iio->modes = INDIO_DIRECT_MODE;
+	iio->masklength = LRADC_MAX_TOTAL_CHANS;
+
+	if (lradc->soc == IMX23_LRADC) {
+		iio->channels = mx23_lradc_chan_spec;
+		iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec);
+	} else {
+		iio->channels = mx28_lradc_chan_spec;
+		iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec);
+	}
+
+	ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
+					 &mxs_lradc_adc_trigger_handler,
+					 &mxs_lradc_adc_buffer_ops);
+	if (ret)
+		return ret;
+
+	ret = mxs_lradc_adc_trigger_init(iio);
+	if (ret)
+		goto err_trig;
+
+	adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc];
+
+	/* Populate available ADC input ranges */
+	for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) {
+		for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) {
+			/*
+			 * [s=0] = optional divider by two disabled (default)
+			 * [s=1] = optional divider by two enabled
+			 *
+			 * The scale is calculated by doing:
+			 *   Vref >> (realbits - s)
+			 * which multiplies by two on the second component
+			 * of the array.
+			 */
+			scale_uv = ((u64)adc->vref_mv[i] * 100000000) >>
+				   (LRADC_RESOLUTION - s);
+			adc->scale_avail[i][s].nano =
+					do_div(scale_uv, 100000000) * 10;
+			adc->scale_avail[i][s].integer = scale_uv;
+		}
+	}
+
+	/* Configure the hardware. */
+	mxs_lradc_adc_hw_init(adc);
+
+	/* Register IIO device. */
+	ret = iio_device_register(iio);
+	if (ret) {
+		dev_err(dev, "Failed to register IIO device\n");
+		goto err_dev;
+	}
+
+	return 0;
+
+err_dev:
+	mxs_lradc_adc_hw_stop(adc);
+	mxs_lradc_adc_trigger_remove(iio);
+err_trig:
+	iio_triggered_buffer_cleanup(iio);
+	return ret;
+}
+
+static int mxs_lradc_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *iio = platform_get_drvdata(pdev);
+	struct mxs_lradc_adc *adc = iio_priv(iio);
+
+	iio_device_unregister(iio);
+	mxs_lradc_adc_hw_stop(adc);
+	mxs_lradc_adc_trigger_remove(iio);
+	iio_triggered_buffer_cleanup(iio);
+
+	return 0;
+}
+
+static struct platform_driver mxs_lradc_adc_driver = {
+	.driver = {
+		.name	= DRIVER_NAME_ADC,
+	},
+	.probe	= mxs_lradc_adc_probe,
+	.remove = mxs_lradc_adc_remove,
+};
+
+module_platform_driver(mxs_lradc_adc_driver);
+
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_DESCRIPTION("Freescale MXS LRADC driver general purpose ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME_ADC);
+
+
-- 
1.9.1

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

* [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
  2016-04-29 11:46 ` Ksenija Stanojevic
                   ` (2 preceding siblings ...)
  (?)
@ 2016-04-29 11:49 ` Ksenija Stanojevic
  2016-04-29 13:22   ` Marek Vasut
                     ` (2 more replies)
  -1 siblings, 3 replies; 40+ messages in thread
From: Ksenija Stanojevic @ 2016-04-29 11:49 UTC (permalink / raw)
  To: linux-kernel
  Cc: lee.jones, dmitry.torokhov, linux-input, jic23, knaack.h, lars,
	pmeerw, marex, linux-iio, harald, Ksenija Stanojevic

Add mxs-lradc touchscreen driver.

Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
---
 drivers/input/touchscreen/Kconfig        |  14 +-
 drivers/input/touchscreen/Makefile       |   1 +
 drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
 3 files changed, 742 insertions(+), 2 deletions(-)
 create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 8ecdc38..d614d248 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
 	depends on SH_HP6XX && SH_ADC
 	help
 	  Say Y here if you have a HP Jornada 620/660/680/690 and want to
-          support the built-in touchscreen.
+	  support the built-in touchscreen.
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called hp680_ts_input.
@@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
 	  This enables support for the Philips UCB1400 touchscreen interface.
 	  The UCB1400 is an AC97 audio codec.  The touchscreen interface
 	  will be initialized only after the ALSA subsystem has been
-	  brought up and the UCB1400 detected.  You therefore have to
+	  brought up and the UCB1400 detected.	You therefore have to
 	  configure ALSA support as well (either built-in or modular,
 	  independently of whether this driver is itself built-in or
 	  modular) for this driver to work.
@@ -842,6 +842,16 @@ config TOUCHSCREEN_MX25
 	  To compile this driver as a module, choose M here: the
 	  module will be called fsl-imx25-tcq.
 
+config TOUCHSCREEN_MXS_LRADC
+	tristate "Freescale i.MX23/i.MX28 LRADC touchscreen"
+	depends on MFD_MXS_LRADC
+	help
+	  Say Y here if you have a touchscreen connected to the low-resolution
+	  analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called mxs-lradc-ts.
+
 config TOUCHSCREEN_MC13783
 	tristate "Freescale MC13783 touchscreen input driver"
 	depends on MFD_MC13XXX
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index f42975e..513a6ff 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MMS114)	+= mms114.o
 obj-$(CONFIG_TOUCHSCREEN_MTOUCH)	+= mtouch.o
 obj-$(CONFIG_TOUCHSCREEN_MK712)		+= mk712.o
+obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC)	+= mxs-lradc-ts.o
 obj-$(CONFIG_TOUCHSCREEN_HP600)		+= hp680_ts_input.o
 obj-$(CONFIG_TOUCHSCREEN_HP7XX)		+= jornada720_ts.o
 obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO)	+= ipaq-micro-ts.o
diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c
new file mode 100644
index 0000000..27abb8e
--- /dev/null
+++ b/drivers/input/touchscreen/mxs-lradc-ts.c
@@ -0,0 +1,729 @@
+/*
+ * Freescale MXS LRADC driver
+ *
+ * Copyright (c) 2012 DENX Software Engineering, GmbH.
+ * Marek Vasut <marex@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/mfd/mxs-lradc.h>
+#include <linux/platform_device.h>
+
+/*
+ * Touchscreen handling
+ */
+enum mxs_lradc_ts_plate {
+	LRADC_TOUCH = 0,
+	LRADC_SAMPLE_X,
+	LRADC_SAMPLE_Y,
+	LRADC_SAMPLE_PRESSURE,
+	LRADC_SAMPLE_VALID,
+};
+
+struct mxs_lradc_ts {
+	struct mxs_lradc	*lradc;
+	struct device		*dev;
+	/*
+	 * When the touchscreen is enabled, we give it two private virtual
+	 * channels: #6 and #7. This means that only 6 virtual channels (instead
+	 * of 8) will be available for buffered capture.
+	 */
+#define TOUCHSCREEN_VCHANNEL1		7
+#define TOUCHSCREEN_VCHANNEL2		6
+
+	struct input_dev	*ts_input;
+
+	enum mxs_lradc_ts_plate	cur_plate; /* state machine */
+	bool			ts_valid;
+	unsigned		ts_x_pos;
+	unsigned		ts_y_pos;
+	unsigned		ts_pressure;
+
+	/* handle touchscreen's physical behaviour */
+	/* samples per coordinate */
+	unsigned		over_sample_cnt;
+	/* time clocks between samples */
+	unsigned		over_sample_delay;
+	/* time in clocks to wait after the plates where switched */
+	unsigned		settling_delay;
+};
+
+static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
+{
+	if (lradc->soc == IMX23_LRADC)
+		return LRADC_CTRL0_MX23_PLATE_MASK;
+	return LRADC_CTRL0_MX28_PLATE_MASK;
+}
+
+static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
+{
+	if (lradc->soc == IMX23_LRADC)
+		return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
+	return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE;
+}
+
+static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc)
+{
+	if (lradc->soc == IMX23_LRADC)
+		return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM;
+	return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW;
+}
+
+static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc)
+{
+	if (lradc->soc == IMX23_LRADC)
+		return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM;
+	return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW;
+}
+
+static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc)
+{
+	if (lradc->soc == IMX23_LRADC)
+		return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM;
+	return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW;
+}
+
+static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
+{
+	return !!(readl(lradc->base + LRADC_STATUS) &
+					LRADC_STATUS_TOUCH_DETECT_RAW);
+}
+
+static void mxs_lradc_map_ts_channel(struct mxs_lradc *lradc, unsigned vch,
+				     unsigned ch)
+{
+	mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
+			    LRADC_CTRL4);
+	mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4);
+}
+
+static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned ch)
+{
+	struct mxs_lradc *lradc = ts->lradc;
+
+	/*
+	 * prepare for oversampling conversion
+	 *
+	 * from the datasheet:
+	 * "The ACCUMULATE bit in the appropriate channel register
+	 * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
+	 * otherwise, the IRQs will not fire."
+	 */
+	mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE |
+			  LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1),
+			  LRADC_CH(ch));
+
+	/* from the datasheet:
+	 * "Software must clear this register in preparation for a
+	 * multi-cycle accumulation.
+	 */
+	mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch));
+
+	/*
+	 * prepare the delay/loop unit according to the oversampling count
+	 *
+	 * from the datasheet:
+	 * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
+	 * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
+	 * the LRADC will not trigger the delay group."
+	 */
+	mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
+			  LRADC_DELAY_TRIGGER_DELAYS(0) |
+			  LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
+			  LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
+			  LRADC_DELAY(3));
+
+	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1);
+
+	/*
+	 * after changing the touchscreen plates setting
+	 * the signals need some initial time to settle. Start the
+	 * SoC's delay unit and start the conversion later
+	 * and automatically.
+	 */
+	mxs_lradc_reg_wrt(
+		lradc,
+		LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
+		LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
+		LRADC_DELAY_KICK |
+		LRADC_DELAY_DELAY(ts->settling_delay),
+		LRADC_DELAY(2));
+}
+
+/*
+ * Pressure detection is special:
+ * We want to do both required measurements for the pressure detection in
+ * one turn. Use the hardware features to chain both conversions and let the
+ * hardware report one interrupt if both conversions are done
+ */
+static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts, unsigned ch1,
+					unsigned ch2)
+{
+	struct mxs_lradc *lradc = ts->lradc;
+	u32 reg;
+
+	/*
+	 * prepare for oversampling conversion
+	 *
+	 * from the datasheet:
+	 * "The ACCUMULATE bit in the appropriate channel register
+	 * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
+	 * otherwise, the IRQs will not fire."
+	 */
+	reg = LRADC_CH_ACCUMULATE |
+		LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1);
+	mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
+	mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
+
+	/* from the datasheet:
+	 * "Software must clear this register in preparation for a
+	 * multi-cycle accumulation.
+	 */
+	mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1));
+	mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2));
+
+	/* prepare the delay/loop unit according to the oversampling count */
+	mxs_lradc_reg_wrt(
+		    lradc,
+		    LRADC_DELAY_TRIGGER(1 << ch1) |
+		    LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */
+		    LRADC_DELAY_TRIGGER_DELAYS(0) |
+		    LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
+		    LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
+		    LRADC_DELAY(3));
+
+	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1);
+
+	/*
+	 * after changing the touchscreen plates setting
+	 * the signals need some initial time to settle. Start the
+	 * SoC's delay unit and start the conversion later
+	 * and automatically.
+	 */
+	mxs_lradc_reg_wrt(
+		lradc,
+		LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
+		LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
+		LRADC_DELAY_KICK |
+		LRADC_DELAY_DELAY(ts->settling_delay), LRADC_DELAY(2));
+}
+
+static unsigned mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts,
+					      unsigned channel)
+{
+	u32 reg;
+	unsigned num_samples, val;
+	struct mxs_lradc *lradc = ts->lradc;
+
+	reg = readl(lradc->base + LRADC_CH(channel));
+	if (reg & LRADC_CH_ACCUMULATE)
+		num_samples = ts->over_sample_cnt;
+	else
+		num_samples = 1;
+
+	val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
+	return val / num_samples;
+}
+
+static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts,
+					   unsigned ch1, unsigned ch2)
+{
+	u32 reg, mask;
+	unsigned pressure, m1, m2;
+	struct mxs_lradc *lradc = ts->lradc;
+
+	mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
+	reg = readl(lradc->base + LRADC_CTRL1) & mask;
+
+	while (reg != mask) {
+		reg = readl(lradc->base + LRADC_CTRL1) & mask;
+		dev_dbg(ts->dev, "One channel is still busy: %X\n", reg);
+	}
+
+	m1 = mxs_lradc_ts_read_raw_channel(ts, ch1);
+	m2 = mxs_lradc_ts_read_raw_channel(ts, ch2);
+
+	if (m2 == 0) {
+		dev_warn(ts->dev, "Cannot calculate pressure\n");
+		return 1 << (LRADC_RESOLUTION - 1);
+	}
+
+	/* simply scale the value from 0 ... max ADC resolution */
+	pressure = m1;
+	pressure *= (1 << LRADC_RESOLUTION);
+	pressure /= m2;
+
+	dev_dbg(ts->dev, "Pressure = %u\n", pressure);
+	return pressure;
+}
+
+#define TS_CH_XP 2
+#define TS_CH_YP 3
+#define TS_CH_XM 4
+#define TS_CH_YM 5
+
+/*
+ * YP(open)--+-------------+
+ *	     |		   |--+
+ *	     |		   |  |
+ *    YM(-)--+-------------+  |
+ *	       +--------------+
+ *	       |	      |
+ *	   XP(weak+)	    XM(open)
+ *
+ * "weak+" means 200k Ohm VDDIO
+ * (-) means GND
+ */
+static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts)
+{
+	struct mxs_lradc *lradc = ts->lradc;
+
+	/*
+	 * In order to detect a touch event the 'touch detect enable' bit
+	 * enables:
+	 *  - a weak pullup to the X+ connector
+	 *  - a strong ground at the Y- connector
+	 */
+	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
+	mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc),
+			  LRADC_CTRL0);
+}
+
+/*
+ * YP(meas)--+-------------+
+ *	     |		   |--+
+ *	     |		   |  |
+ * YM(open)--+-------------+  |
+ *	       +--------------+
+ *	       |	      |
+ *	     XP(+)	    XM(-)
+ *
+ * (+) means here 1.85 V
+ * (-) means here GND
+ */
+static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts)
+{
+	struct mxs_lradc *lradc = ts->lradc;
+
+	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
+	mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
+
+	ts->cur_plate = LRADC_SAMPLE_X;
+	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
+	mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
+}
+
+/*
+ *   YP(+)--+-------------+
+ *	    |		  |--+
+ *	    |		  |  |
+ *   YM(-)--+-------------+  |
+ *	      +--------------+
+ *	      |		     |
+ *	   XP(open)	   XM(meas)
+ *
+ * (+) means here 1.85 V
+ * (-) means here GND
+ */
+static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts)
+{
+	struct mxs_lradc *lradc = ts->lradc;
+
+	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
+	mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
+
+	ts->cur_plate = LRADC_SAMPLE_Y;
+	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
+	mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
+}
+
+/*
+ *    YP(+)--+-------------+
+ *	     |		   |--+
+ *	     |		   |  |
+ * YM(meas)--+-------------+  |
+ *	       +--------------+
+ *	       |	      |
+ *	    XP(meas)	    XM(-)
+ *
+ * (+) means here 1.85 V
+ * (-) means here GND
+ */
+static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts)
+{
+	struct mxs_lradc *lradc = ts->lradc;
+
+	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
+	mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
+
+	ts->cur_plate = LRADC_SAMPLE_PRESSURE;
+	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
+	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
+	mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2,
+				    TOUCHSCREEN_VCHANNEL1);
+}
+
+static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts)
+{
+	mxs_lradc_setup_touch_detection(ts);
+
+	ts->cur_plate = LRADC_TOUCH;
+	mxs_lradc_reg_clear(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
+			    LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
+	mxs_lradc_reg_set(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+			  LRADC_CTRL1);
+}
+
+static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts)
+{
+	mxs_lradc_reg_clear(ts->lradc,
+			    LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+			    LRADC_CTRL1);
+	mxs_lradc_reg_set(ts->lradc,
+			  LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1),
+			  LRADC_CTRL1);
+	/*
+	 * start with the Y-pos, because it uses nearly the same plate
+	 * settings like the touch detection
+	 */
+	mxs_lradc_prepare_y_pos(ts);
+}
+
+static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts)
+{
+	input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos);
+	input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos);
+	input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure);
+	input_report_key(ts->ts_input, BTN_TOUCH, 1);
+	input_sync(ts->ts_input);
+}
+
+static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts)
+{
+	struct mxs_lradc *lradc = ts->lradc;
+
+	mxs_lradc_setup_touch_detection(ts);
+	ts->cur_plate = LRADC_SAMPLE_VALID;
+	/*
+	 * start a dummy conversion to burn time to settle the signals
+	 * note: we are not interested in the conversion's value
+	 */
+	mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
+	mxs_lradc_reg_clear(lradc,
+			    LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
+			    LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2),
+			    LRADC_CTRL1);
+	mxs_lradc_reg_wrt(
+		    lradc,
+		    LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
+		    LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
+		    LRADC_DELAY(2));
+}
+
+/*
+ * in order to avoid false measurements, report only samples where
+ * the surface is still touched after the position measurement
+ */
+static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid)
+{
+	struct mxs_lradc *lradc = ts->lradc;
+
+	/* if it is still touched, report the sample */
+	if (valid && mxs_lradc_check_touch_event(lradc)) {
+		ts->ts_valid = true;
+		mxs_lradc_report_ts_event(ts);
+	}
+
+	/* if it is even still touched, continue with the next measurement */
+	if (mxs_lradc_check_touch_event(lradc)) {
+		mxs_lradc_prepare_y_pos(ts);
+		return;
+	}
+
+	if (ts->ts_valid) {
+		/* signal the release */
+		ts->ts_valid = false;
+		input_report_key(ts->ts_input, BTN_TOUCH, 0);
+		input_sync(ts->ts_input);
+	}
+
+	/* if it is released, wait for the next touch via IRQ */
+	ts->cur_plate = LRADC_TOUCH;
+	mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
+	mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
+	mxs_lradc_reg_clear(lradc,
+			    LRADC_CTRL1_TOUCH_DETECT_IRQ |
+			    LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
+			    LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1),
+			    LRADC_CTRL1);
+	mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
+}
+
+/* touchscreen's state machine */
+static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts)
+{
+	struct mxs_lradc *lradc = ts->lradc;
+
+	switch (ts->cur_plate) {
+	case LRADC_TOUCH:
+		if (mxs_lradc_check_touch_event(lradc))
+			mxs_lradc_start_touch_event(ts);
+		mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
+				    LRADC_CTRL1);
+		return;
+
+	case LRADC_SAMPLE_Y:
+		ts->ts_y_pos =
+		    mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
+		mxs_lradc_prepare_x_pos(ts);
+		return;
+
+	case LRADC_SAMPLE_X:
+		ts->ts_x_pos =
+		    mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
+		mxs_lradc_prepare_pressure(ts);
+		return;
+
+	case LRADC_SAMPLE_PRESSURE:
+		ts->ts_pressure =
+		    mxs_lradc_read_ts_pressure(ts,
+					       TOUCHSCREEN_VCHANNEL2,
+					       TOUCHSCREEN_VCHANNEL1);
+		mxs_lradc_complete_touch_event(ts);
+		return;
+
+	case LRADC_SAMPLE_VALID:
+		mxs_lradc_finish_touch_event(ts, 1);
+		break;
+	}
+}
+
+/*
+ * IRQ Handling
+ */
+static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data)
+{
+	struct mxs_lradc_ts *ts = data;
+	struct mxs_lradc *lradc = ts->lradc;
+	unsigned long reg = readl(lradc->base + LRADC_CTRL1);
+	u32 clr_irq = mxs_lradc_irq_mask(lradc);
+	const u32 ts_irq_mask =
+		LRADC_CTRL1_TOUCH_DETECT_IRQ |
+		LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
+		LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
+
+	if (!(reg & mxs_lradc_irq_mask(lradc)))
+		return IRQ_NONE;
+
+	if (lradc->use_touchscreen && (reg & ts_irq_mask)) {
+		mxs_lradc_handle_touch(ts);
+
+		/* Make sure we don't clear the next conversion's interrupt. */
+		clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
+				LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
+		mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int mxs_lradc_ts_open(struct input_dev *dev)
+{
+	struct mxs_lradc_ts *ts = input_get_drvdata(dev);
+
+	/* Enable the touch-detect circuitry. */
+	mxs_lradc_enable_touch_detection(ts);
+
+	return 0;
+}
+
+static void mxs_lradc_disable_ts(struct mxs_lradc_ts *ts)
+{
+	struct mxs_lradc *lradc = ts->lradc;
+
+	/* stop all interrupts from firing */
+	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
+		LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
+		LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
+
+	/* Power-down touchscreen touch-detect circuitry. */
+	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
+}
+
+static void mxs_lradc_ts_close(struct input_dev *dev)
+{
+	struct mxs_lradc_ts *ts = input_get_drvdata(dev);
+
+	mxs_lradc_disable_ts(ts);
+}
+
+static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts)
+{
+	struct mxs_lradc *lradc = ts->lradc;
+
+	/* Configure the touchscreen type */
+	if (lradc->soc == IMX28_LRADC) {
+		mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
+				    LRADC_CTRL0);
+
+		if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
+			mxs_lradc_reg_set(lradc,
+					  LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
+					  LRADC_CTRL0);
+	}
+}
+
+static void mxs_lradc_ts_hw_stop(struct mxs_lradc_ts *ts)
+{
+	int i;
+	struct mxs_lradc *lradc = ts->lradc;
+
+	mxs_lradc_reg_clear(lradc,
+			lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
+			LRADC_CTRL1);
+
+	for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++)
+		mxs_lradc_reg_wrt(ts->lradc, 0, LRADC_DELAY(i));
+}
+
+static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts)
+{
+	struct input_dev *input = ts->ts_input;
+	struct device *dev = ts->dev;
+	struct mxs_lradc *lradc = ts->lradc;
+
+	if (!lradc->use_touchscreen)
+		return 0;
+
+	input->name = DRIVER_NAME_TS;
+	input->id.bustype = BUS_HOST;
+	input->dev.parent = dev;
+	input->open = mxs_lradc_ts_open;
+	input->close = mxs_lradc_ts_close;
+
+	__set_bit(EV_ABS, input->evbit);
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(BTN_TOUCH, input->keybit);
+	__set_bit(INPUT_PROP_DIRECT, input->propbit);
+	input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
+	input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
+	input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
+			     0, 0);
+
+	ts->ts_input = input;
+	input_set_drvdata(input, ts);
+
+	return input_register_device(input);
+}
+
+static int mxs_lradc_ts_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->parent->of_node;
+	struct mxs_lradc *lradc = dev_get_platdata(dev);
+	struct mxs_lradc_ts *ts;
+	struct input_dev *input;
+	int touch_ret, ret, i;
+	u32 ts_wires = 0, adapt;
+
+	ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
+	input = devm_input_allocate_device(dev);
+	if (!ts || !input)
+		return -ENOMEM;
+
+	ts->lradc = lradc;
+	ts->dev = dev;
+	ts->ts_input = input;
+	platform_set_drvdata(pdev, ts);
+	input_set_drvdata(input, ts);
+
+	touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
+					 &ts_wires);
+
+	if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) {
+		ts->over_sample_cnt = 4;
+	} else {
+		if (adapt < 1 || adapt > 32) {
+			dev_err(ts->dev, "Invalid sample count (%u)\n",
+				adapt);
+			touch_ret = -EINVAL;
+		} else {
+			ts->over_sample_cnt = adapt;
+		}
+	}
+
+	if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) {
+		ts->over_sample_delay = 2;
+	} else {
+		if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) {
+			dev_err(ts->dev, "Invalid sample delay (%u)\n",
+				adapt);
+			touch_ret = -EINVAL;
+		} else {
+			ts->over_sample_delay = adapt;
+		}
+	}
+
+	if (of_property_read_u32(node, "fsl,settling", &adapt)) {
+		ts->settling_delay = 10;
+	} else {
+		if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) {
+			dev_err(ts->dev, "Invalid settling delay (%u)\n",
+				adapt);
+			touch_ret = -EINVAL;
+		} else {
+			ts->settling_delay = adapt;
+		}
+	}
+
+	mxs_lradc_ts_hw_init(ts);
+	for (i = 0; i < lradc->irq_count; i++) {
+		ret = devm_request_irq(dev, lradc->irq[i],
+				       mxs_lradc_ts_handle_irq,
+				       IRQF_SHARED, lradc->irq_name[i], ts);
+		if (ret)
+			return ret;
+	}
+
+	if (!touch_ret) {
+		ret = mxs_lradc_ts_register(ts);
+		if (!ret)
+			goto err_ts_register;
+	}
+
+	return 0;
+
+err_ts_register:
+	mxs_lradc_ts_hw_stop(ts);
+	return ret;
+}
+
+static int mxs_lradc_ts_remove(struct platform_device *pdev)
+{
+	struct mxs_lradc_ts *ts = platform_get_drvdata(pdev);
+
+	mxs_lradc_ts_hw_stop(ts);
+
+	return 0;
+}
+
+static struct platform_driver mxs_lradc_ts_driver = {
+	.driver	= {
+		.name = DRIVER_NAME_TS,
+	},
+	.probe	= mxs_lradc_ts_probe,
+	.remove	= mxs_lradc_ts_remove,
+};
+module_platform_driver(mxs_lradc_ts_driver);
+
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

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

* Re: [PATCH 1/3] mfd: mxs-lradc: Add support for mxs-lradc MFD
@ 2016-04-29 13:15     ` Marek Vasut
  0 siblings, 0 replies; 40+ messages in thread
From: Marek Vasut @ 2016-04-29 13:15 UTC (permalink / raw)
  To: Ksenija Stanojevic, linux-kernel
  Cc: lee.jones, dmitry.torokhov, linux-input, jic23, knaack.h, lars,
	pmeerw, linux-iio, harald

On 04/29/2016 01:47 PM, Ksenija Stanojevic wrote:
> Add core files for mxs-lradc MFD driver.
> 
> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
> ---
>  drivers/mfd/Kconfig           |  33 +++++--
>  drivers/mfd/Makefile          |   1 +
>  drivers/mfd/mxs-lradc.c       | 213 ++++++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/mxs-lradc.h | 210 +++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 449 insertions(+), 8 deletions(-)
>  create mode 100644 drivers/mfd/mxs-lradc.c
>  create mode 100644 include/linux/mfd/mxs-lradc.h

Is there any chance you can also remove the same code from lradc ?

> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index eea61e3..fff44d6 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -16,7 +16,7 @@ config MFD_CS5535
>  	depends on PCI && (X86_32 || (X86 && COMPILE_TEST))
>  	---help---
>  	  This is the core driver for CS5535/CS5536 MFD functions.  This is
> -          necessary for using the board's GPIO and MFGPT functionality.
> +	  necessary for using the board's GPIO and MFGPT functionality.

Probably shouldn't be part of the patch ?

>  config MFD_ACT8945A
>  	tristate "Active-semi ACT8945A"
> @@ -319,6 +319,23 @@ config MFD_HI6421_PMIC
>  	  menus in order to enable them.
>  	  We communicate with the Hi6421 via memory-mapped I/O.
>  
> +config MFD_MXS_LRADC
> +	tristate "Freescale i.MX23/i.MX28 LRADC"
> +	depends on ARCH_MXS || COMPILE_TEST
> +	select MFD_CORE
> +	select STMP_DEVICE
> +	help
> +	  Say yes here to build support for the low-resolution
> +	  analog-to-digital converter (LRADC) found on the i.MX23 and i.MX28
> +	  processors. This driver provides common support for accessing the
> +	  device, additional drivers must be enabled in order to use the
> +	  functionality of the device:
> +		mxs-lradc-adc for ADC readings
> +		mxs-lradc-ts  for touchscreen support
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called mxs-lradc.
> +
>  config HTC_EGPIO
>  	bool "HTC EGPIO support"
>  	depends on GPIOLIB && ARM
> @@ -650,7 +667,7 @@ config EZX_PCAP
>  	  needed for MMC, TouchScreen, Sound, USB, etc..
>  
>  config MFD_VIPERBOARD
> -        tristate "Nano River Technologies Viperboard"
> +	tristate "Nano River Technologies Viperboard"

Shouldn't be part of this patch

>  	select MFD_CORE
>  	depends on USB
>  	default n
> @@ -898,11 +915,11 @@ config MFD_SMSC
>         select MFD_CORE
>         select REGMAP_I2C
>         help
> -        If you say yes here you get support for the
> -        ece1099 chips from SMSC.
> +	If you say yes here you get support for the
> +	ece1099 chips from SMSC.

DTTO

> -        To compile this driver as a module, choose M here: the
> -        module will be called smsc.
> +	To compile this driver as a module, choose M here: the
> +	module will be called smsc.
>  
>  config ABX500_CORE
>  	bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
> @@ -956,8 +973,8 @@ config AB8500_DEBUG
>         depends on AB8500_GPADC && DEBUG_FS
>         default y if DEBUG_FS
>         help
> -         Select this option if you want debug information using the debug
> -         filesystem, debugfs.
> +	 Select this option if you want debug information using the debug
> +	 filesystem, debugfs.

Same here

>  config AB8500_GPADC
>  	bool "ST-Ericsson AB8500 GPADC driver"
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 5eaa6465d..236b831 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -203,3 +203,4 @@ intel-soc-pmic-objs		:= intel_soc_pmic_core.o intel_soc_pmic_crc.o
>  intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC)	+= intel_soc_pmic_bxtwc.o
>  obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
>  obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
> +obj-$(CONFIG_MFD_MXS_LRADC)	+= mxs-lradc.o
> diff --git a/drivers/mfd/mxs-lradc.c b/drivers/mfd/mxs-lradc.c
> new file mode 100644
> index 0000000..e1c8f9e
> --- /dev/null
> +++ b/drivers/mfd/mxs-lradc.c
> @@ -0,0 +1,213 @@
> +/*
> + * Freescale MXS LRADC driver
> + *
> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
> + * Marek Vasut <marex@denx.de>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/mxs-lradc.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +static struct mfd_cell lradc_adc_dev = {
> +	.name = DRIVER_NAME_ADC,
> +};
> +
> +static struct mfd_cell lradc_ts_dev = {
> +	.name = DRIVER_NAME_TS,
> +};
> +
> +static const char * const mx23_lradc_irq_names[] = {
> +	"mxs-lradc-touchscreen",
> +	"mxs-lradc-channel0",
> +	"mxs-lradc-channel1",
> +	"mxs-lradc-channel2",
> +	"mxs-lradc-channel3",
> +	"mxs-lradc-channel4",
> +	"mxs-lradc-channel5",
> +	"mxs-lradc-channel6",
> +	"mxs-lradc-channel7",
> +};
> +
> +static const char * const mx28_lradc_irq_names[] = {
> +	"mxs-lradc-touchscreen",
> +	"mxs-lradc-thresh0",
> +	"mxs-lradc-thresh1",
> +	"mxs-lradc-channel0",
> +	"mxs-lradc-channel1",
> +	"mxs-lradc-channel2",
> +	"mxs-lradc-channel3",
> +	"mxs-lradc-channel4",
> +	"mxs-lradc-channel5",
> +	"mxs-lradc-channel6",
> +	"mxs-lradc-channel7",
> +	"mxs-lradc-button0",
> +	"mxs-lradc-button1",
> +};
> +
> +struct mxs_lradc_of_config {
> +	const int		irq_count;
> +	const char * const	*irq_name;
> +};
> +
> +static const struct mxs_lradc_of_config mxs_lradc_of_config[] = {
> +	[IMX23_LRADC] = {
> +		.irq_count	= ARRAY_SIZE(mx23_lradc_irq_names),
> +		.irq_name	= mx23_lradc_irq_names,
> +	},
> +	[IMX28_LRADC] = {
> +		.irq_count	= ARRAY_SIZE(mx28_lradc_irq_names),
> +		.irq_name	= mx28_lradc_irq_names,
> +	},
> +};
> +
> +static const struct of_device_id mxs_lradc_dt_ids[] = {
> +	{ .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, },
> +	{ .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
> +
> +static int mxs_lradc_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *of_id =
> +		of_match_device(mxs_lradc_dt_ids, &pdev->dev);
> +	const struct mxs_lradc_of_config *of_cfg =
> +		&mxs_lradc_of_config[(enum mxs_lradc_id)of_id->data];
> +	struct device *dev = &pdev->dev;
> +	struct device_node *node = dev->of_node;
> +	struct mxs_lradc *lradc;
> +	struct resource *iores;
> +	int ret = 0, touch_ret, i;
> +	u32 ts_wires = 0;
> +
> +	lradc = devm_kzalloc(&pdev->dev, sizeof(*lradc), GFP_KERNEL);
> +	if (!lradc)
> +		return -ENOMEM;
> +	lradc->soc = (enum mxs_lradc_id)of_id->data;
> +
> +	/* Grab the memory area */
> +	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	lradc->base = devm_ioremap_resource(dev, iores);
> +	if (IS_ERR(lradc->base))
> +		return PTR_ERR(lradc->base);
> +
> +	lradc->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(lradc->clk)) {
> +		dev_err(dev, "Failed to get the delay unit clock\n");
> +		return PTR_ERR(lradc->clk);
> +	}
> +	ret = clk_prepare_enable(lradc->clk);
> +	if (ret != 0) {
> +		dev_err(dev, "Failed to enable the delay unit clock\n");
> +		return ret;
> +	}
> +
> +	touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
> +					 &ts_wires);
> +
> +	if (touch_ret == 0)
> +		lradc->buffer_vchans = BUFFER_VCHANS_LIMITED;
> +	else
> +		lradc->buffer_vchans = BUFFER_VCHANS_ALL;
> +
> +	lradc->irq_count = of_cfg->irq_count;
> +	lradc->irq_name = of_cfg->irq_name;
> +	for (i = 0; i < lradc->irq_count; i++) {
> +		lradc->irq[i] = platform_get_irq(pdev, i);
> +		if (lradc->irq[i] < 0) {
> +			ret = lradc->irq[i];
> +			goto err_clk;
> +		}
> +	}
> +
> +	platform_set_drvdata(pdev, lradc);
> +
> +	ret = stmp_reset_block(lradc->base);
> +

Drop this newline here

> +	if (ret)
> +		return ret;
> +
> +	lradc_adc_dev.platform_data = lradc;
> +	lradc_adc_dev.pdata_size = sizeof(*lradc);
> +
> +	ret = mfd_add_devices(&pdev->dev, -1, &lradc_adc_dev, 1, NULL, 0, NULL);

devm_mfd_add_devices()?

> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to add the ADC subdevice\n");
> +		return ret;
> +	}
> +
> +	lradc_ts_dev.platform_data = lradc;
> +	lradc_ts_dev.pdata_size = sizeof(*lradc);
> +
> +	switch (ts_wires) {
> +	case 4:
> +		lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE;
> +		break;
> +	case 5:
> +		if (lradc->soc == IMX28_LRADC) {
> +			lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE;
> +			break;
> +		}
> +		/* fall through an error message for i.MX23 */
> +	default:
> +		dev_err(&pdev->dev,
> +			"Unsupported number of touchscreen wires (%d)\n",
> +			ts_wires);
> +		return -EINVAL;
> +	}
> +
> +	ret = mfd_add_devices(&pdev->dev, -1, &lradc_ts_dev, 1, NULL, 0, NULL);

devm_mfd_add_devices()?

You might want to split registration of each MFD subdev into separate
function.

> +	if (ret) {
> +		dev_err(&pdev->dev,
> +			"Failed to add the touchscreen subdevice\n");
> +		goto err_remove_adc;
> +	}
> +
> +	return 0;
> +
> +err_remove_adc:
> +	mfd_remove_devices(&pdev->dev);
> +err_clk:
> +	clk_disable_unprepare(lradc->clk);
> +	return ret;
> +}
> +
> +static int mxs_lradc_remove(struct platform_device *pdev)
> +{
> +	struct mxs_lradc *lradc = platform_get_drvdata(pdev);
> +
> +	mfd_remove_devices(&pdev->dev);
> +	clk_disable_unprepare(lradc->clk);
> +	return 0;
> +}
> +
> +static struct platform_driver mxs_lradc_driver = {
> +	.driver = {
> +		.name = "mxs-lradc",
> +		.of_match_table = mxs_lradc_dt_ids,
> +	},
> +	.probe = mxs_lradc_probe,
> +	.remove = mxs_lradc_remove,
> +};
> +
> +module_platform_driver(mxs_lradc_driver);
> +
> +MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:mxs-lradc");

[...]


-- 
Best regards,
Marek Vasut

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

* Re: [PATCH 1/3] mfd: mxs-lradc: Add support for mxs-lradc MFD
@ 2016-04-29 13:15     ` Marek Vasut
  0 siblings, 0 replies; 40+ messages in thread
From: Marek Vasut @ 2016-04-29 13:15 UTC (permalink / raw)
  To: Ksenija Stanojevic, linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	linux-input-u79uwXL29TY76Z2rM5mHXA, jic23-DgEjT+Ai2ygdnm+yROfE0A,
	knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	harald-95f8Dae0BrPYtjvyW6yDsg

On 04/29/2016 01:47 PM, Ksenija Stanojevic wrote:
> Add core files for mxs-lradc MFD driver.
> 
> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
>  drivers/mfd/Kconfig           |  33 +++++--
>  drivers/mfd/Makefile          |   1 +
>  drivers/mfd/mxs-lradc.c       | 213 ++++++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/mxs-lradc.h | 210 +++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 449 insertions(+), 8 deletions(-)
>  create mode 100644 drivers/mfd/mxs-lradc.c
>  create mode 100644 include/linux/mfd/mxs-lradc.h

Is there any chance you can also remove the same code from lradc ?

> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index eea61e3..fff44d6 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -16,7 +16,7 @@ config MFD_CS5535
>  	depends on PCI && (X86_32 || (X86 && COMPILE_TEST))
>  	---help---
>  	  This is the core driver for CS5535/CS5536 MFD functions.  This is
> -          necessary for using the board's GPIO and MFGPT functionality.
> +	  necessary for using the board's GPIO and MFGPT functionality.

Probably shouldn't be part of the patch ?

>  config MFD_ACT8945A
>  	tristate "Active-semi ACT8945A"
> @@ -319,6 +319,23 @@ config MFD_HI6421_PMIC
>  	  menus in order to enable them.
>  	  We communicate with the Hi6421 via memory-mapped I/O.
>  
> +config MFD_MXS_LRADC
> +	tristate "Freescale i.MX23/i.MX28 LRADC"
> +	depends on ARCH_MXS || COMPILE_TEST
> +	select MFD_CORE
> +	select STMP_DEVICE
> +	help
> +	  Say yes here to build support for the low-resolution
> +	  analog-to-digital converter (LRADC) found on the i.MX23 and i.MX28
> +	  processors. This driver provides common support for accessing the
> +	  device, additional drivers must be enabled in order to use the
> +	  functionality of the device:
> +		mxs-lradc-adc for ADC readings
> +		mxs-lradc-ts  for touchscreen support
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called mxs-lradc.
> +
>  config HTC_EGPIO
>  	bool "HTC EGPIO support"
>  	depends on GPIOLIB && ARM
> @@ -650,7 +667,7 @@ config EZX_PCAP
>  	  needed for MMC, TouchScreen, Sound, USB, etc..
>  
>  config MFD_VIPERBOARD
> -        tristate "Nano River Technologies Viperboard"
> +	tristate "Nano River Technologies Viperboard"

Shouldn't be part of this patch

>  	select MFD_CORE
>  	depends on USB
>  	default n
> @@ -898,11 +915,11 @@ config MFD_SMSC
>         select MFD_CORE
>         select REGMAP_I2C
>         help
> -        If you say yes here you get support for the
> -        ece1099 chips from SMSC.
> +	If you say yes here you get support for the
> +	ece1099 chips from SMSC.

DTTO

> -        To compile this driver as a module, choose M here: the
> -        module will be called smsc.
> +	To compile this driver as a module, choose M here: the
> +	module will be called smsc.
>  
>  config ABX500_CORE
>  	bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
> @@ -956,8 +973,8 @@ config AB8500_DEBUG
>         depends on AB8500_GPADC && DEBUG_FS
>         default y if DEBUG_FS
>         help
> -         Select this option if you want debug information using the debug
> -         filesystem, debugfs.
> +	 Select this option if you want debug information using the debug
> +	 filesystem, debugfs.

Same here

>  config AB8500_GPADC
>  	bool "ST-Ericsson AB8500 GPADC driver"
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 5eaa6465d..236b831 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -203,3 +203,4 @@ intel-soc-pmic-objs		:= intel_soc_pmic_core.o intel_soc_pmic_crc.o
>  intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC)	+= intel_soc_pmic_bxtwc.o
>  obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
>  obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
> +obj-$(CONFIG_MFD_MXS_LRADC)	+= mxs-lradc.o
> diff --git a/drivers/mfd/mxs-lradc.c b/drivers/mfd/mxs-lradc.c
> new file mode 100644
> index 0000000..e1c8f9e
> --- /dev/null
> +++ b/drivers/mfd/mxs-lradc.c
> @@ -0,0 +1,213 @@
> +/*
> + * Freescale MXS LRADC driver
> + *
> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
> + * Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/mxs-lradc.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +static struct mfd_cell lradc_adc_dev = {
> +	.name = DRIVER_NAME_ADC,
> +};
> +
> +static struct mfd_cell lradc_ts_dev = {
> +	.name = DRIVER_NAME_TS,
> +};
> +
> +static const char * const mx23_lradc_irq_names[] = {
> +	"mxs-lradc-touchscreen",
> +	"mxs-lradc-channel0",
> +	"mxs-lradc-channel1",
> +	"mxs-lradc-channel2",
> +	"mxs-lradc-channel3",
> +	"mxs-lradc-channel4",
> +	"mxs-lradc-channel5",
> +	"mxs-lradc-channel6",
> +	"mxs-lradc-channel7",
> +};
> +
> +static const char * const mx28_lradc_irq_names[] = {
> +	"mxs-lradc-touchscreen",
> +	"mxs-lradc-thresh0",
> +	"mxs-lradc-thresh1",
> +	"mxs-lradc-channel0",
> +	"mxs-lradc-channel1",
> +	"mxs-lradc-channel2",
> +	"mxs-lradc-channel3",
> +	"mxs-lradc-channel4",
> +	"mxs-lradc-channel5",
> +	"mxs-lradc-channel6",
> +	"mxs-lradc-channel7",
> +	"mxs-lradc-button0",
> +	"mxs-lradc-button1",
> +};
> +
> +struct mxs_lradc_of_config {
> +	const int		irq_count;
> +	const char * const	*irq_name;
> +};
> +
> +static const struct mxs_lradc_of_config mxs_lradc_of_config[] = {
> +	[IMX23_LRADC] = {
> +		.irq_count	= ARRAY_SIZE(mx23_lradc_irq_names),
> +		.irq_name	= mx23_lradc_irq_names,
> +	},
> +	[IMX28_LRADC] = {
> +		.irq_count	= ARRAY_SIZE(mx28_lradc_irq_names),
> +		.irq_name	= mx28_lradc_irq_names,
> +	},
> +};
> +
> +static const struct of_device_id mxs_lradc_dt_ids[] = {
> +	{ .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, },
> +	{ .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
> +
> +static int mxs_lradc_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *of_id =
> +		of_match_device(mxs_lradc_dt_ids, &pdev->dev);
> +	const struct mxs_lradc_of_config *of_cfg =
> +		&mxs_lradc_of_config[(enum mxs_lradc_id)of_id->data];
> +	struct device *dev = &pdev->dev;
> +	struct device_node *node = dev->of_node;
> +	struct mxs_lradc *lradc;
> +	struct resource *iores;
> +	int ret = 0, touch_ret, i;
> +	u32 ts_wires = 0;
> +
> +	lradc = devm_kzalloc(&pdev->dev, sizeof(*lradc), GFP_KERNEL);
> +	if (!lradc)
> +		return -ENOMEM;
> +	lradc->soc = (enum mxs_lradc_id)of_id->data;
> +
> +	/* Grab the memory area */
> +	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	lradc->base = devm_ioremap_resource(dev, iores);
> +	if (IS_ERR(lradc->base))
> +		return PTR_ERR(lradc->base);
> +
> +	lradc->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(lradc->clk)) {
> +		dev_err(dev, "Failed to get the delay unit clock\n");
> +		return PTR_ERR(lradc->clk);
> +	}
> +	ret = clk_prepare_enable(lradc->clk);
> +	if (ret != 0) {
> +		dev_err(dev, "Failed to enable the delay unit clock\n");
> +		return ret;
> +	}
> +
> +	touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
> +					 &ts_wires);
> +
> +	if (touch_ret == 0)
> +		lradc->buffer_vchans = BUFFER_VCHANS_LIMITED;
> +	else
> +		lradc->buffer_vchans = BUFFER_VCHANS_ALL;
> +
> +	lradc->irq_count = of_cfg->irq_count;
> +	lradc->irq_name = of_cfg->irq_name;
> +	for (i = 0; i < lradc->irq_count; i++) {
> +		lradc->irq[i] = platform_get_irq(pdev, i);
> +		if (lradc->irq[i] < 0) {
> +			ret = lradc->irq[i];
> +			goto err_clk;
> +		}
> +	}
> +
> +	platform_set_drvdata(pdev, lradc);
> +
> +	ret = stmp_reset_block(lradc->base);
> +

Drop this newline here

> +	if (ret)
> +		return ret;
> +
> +	lradc_adc_dev.platform_data = lradc;
> +	lradc_adc_dev.pdata_size = sizeof(*lradc);
> +
> +	ret = mfd_add_devices(&pdev->dev, -1, &lradc_adc_dev, 1, NULL, 0, NULL);

devm_mfd_add_devices()?

> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to add the ADC subdevice\n");
> +		return ret;
> +	}
> +
> +	lradc_ts_dev.platform_data = lradc;
> +	lradc_ts_dev.pdata_size = sizeof(*lradc);
> +
> +	switch (ts_wires) {
> +	case 4:
> +		lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE;
> +		break;
> +	case 5:
> +		if (lradc->soc == IMX28_LRADC) {
> +			lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE;
> +			break;
> +		}
> +		/* fall through an error message for i.MX23 */
> +	default:
> +		dev_err(&pdev->dev,
> +			"Unsupported number of touchscreen wires (%d)\n",
> +			ts_wires);
> +		return -EINVAL;
> +	}
> +
> +	ret = mfd_add_devices(&pdev->dev, -1, &lradc_ts_dev, 1, NULL, 0, NULL);

devm_mfd_add_devices()?

You might want to split registration of each MFD subdev into separate
function.

> +	if (ret) {
> +		dev_err(&pdev->dev,
> +			"Failed to add the touchscreen subdevice\n");
> +		goto err_remove_adc;
> +	}
> +
> +	return 0;
> +
> +err_remove_adc:
> +	mfd_remove_devices(&pdev->dev);
> +err_clk:
> +	clk_disable_unprepare(lradc->clk);
> +	return ret;
> +}
> +
> +static int mxs_lradc_remove(struct platform_device *pdev)
> +{
> +	struct mxs_lradc *lradc = platform_get_drvdata(pdev);
> +
> +	mfd_remove_devices(&pdev->dev);
> +	clk_disable_unprepare(lradc->clk);
> +	return 0;
> +}
> +
> +static struct platform_driver mxs_lradc_driver = {
> +	.driver = {
> +		.name = "mxs-lradc",
> +		.of_match_table = mxs_lradc_dt_ids,
> +	},
> +	.probe = mxs_lradc_probe,
> +	.remove = mxs_lradc_remove,
> +};
> +
> +module_platform_driver(mxs_lradc_driver);
> +
> +MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:mxs-lradc");

[...]


-- 
Best regards,
Marek Vasut

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

* Re: [PATCH 2/3] iio: adc: mxs-lradc: Add support for adc driver
  2016-04-29 11:48 ` [PATCH 2/3] iio: adc: mxs-lradc: Add support for adc driver Ksenija Stanojevic
@ 2016-04-29 13:21   ` Marek Vasut
  2016-05-01 16:38     ` Jonathan Cameron
  1 sibling, 0 replies; 40+ messages in thread
From: Marek Vasut @ 2016-04-29 13:21 UTC (permalink / raw)
  To: Ksenija Stanojevic, linux-kernel
  Cc: lee.jones, dmitry.torokhov, linux-input, jic23, knaack.h, lars,
	pmeerw, linux-iio, harald

On 04/29/2016 01:48 PM, Ksenija Stanojevic wrote:
> Add mxs-lradc adc driver.

Commit message could use some improvement ;-)

> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
> ---
>  drivers/iio/adc/Kconfig         |  37 +-
>  drivers/iio/adc/Makefile        |   1 +
>  drivers/iio/adc/mxs-lradc-adc.c | 832 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 858 insertions(+), 12 deletions(-)
>  create mode 100644 drivers/iio/adc/mxs-lradc-adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 82c718c..50847b8 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -233,6 +233,19 @@ config IMX7D_ADC
>  	  This driver can also be built as a module. If so, the module will be
>  	  called imx7d_adc.
>  
> +config MXS_LRADC_ADC
> +	tristate "Freescale i.MX23/i.MX28 LRADC ADC"
> +	depends on MFD_MXS_LRADC
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	  Say yes here to build support for the ADC functions of the
> +	  i.MX23/i.MX28 LRADC. This includes general-purpose ADC readings,
> +	  battery voltage measurement, and die temperature measurement.
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called mxs-lradc-adc.
> +
>  config LP8788_ADC
>  	tristate "LP8788 ADC driver"
>  	depends on MFD_LP8788
> @@ -306,18 +319,18 @@ config MEN_Z188_ADC
>  	  called men_z188_adc.
>  
>  config MXS_LRADC
> -        tristate "Freescale i.MX23/i.MX28 LRADC"
> -        depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
> -        depends on INPUT
> -        select STMP_DEVICE
> -        select IIO_BUFFER
> -        select IIO_TRIGGERED_BUFFER
> -        help
> -          Say yes here to build support for i.MX23/i.MX28 LRADC convertor
> -          built into these chips.
> -
> -          To compile this driver as a module, choose M here: the
> -          module will be called mxs-lradc.
> +	tristate "Freescale i.MX23/i.MX28 LRADC"
> +	depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
> +	depends on INPUT
> +	select STMP_DEVICE
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	  Say yes here to build support for i.MX23/i.MX28 LRADC convertor
> +	  built into these chips.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called mxs-lradc.
>  
>  config NAU7802
>  	tristate "Nuvoton NAU7802 ADC driver"
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 0cb7921..ca7d6aa 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -31,6 +31,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
>  obj-$(CONFIG_MCP3422) += mcp3422.o
>  obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
>  obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
> +obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
>  obj-$(CONFIG_NAU7802) += nau7802.o
>  obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
>  obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
> diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c
> new file mode 100644
> index 0000000..793c369
> --- /dev/null
> +++ b/drivers/iio/adc/mxs-lradc-adc.c

[...]

> +	if (lradc->soc == IMX28_LRADC)
> +		mxs_lradc_reg_clear(
> +			lradc,
> +			lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
> +			LRADC_CTRL1);

Can you tweak the formatting here ?

if (lradc->soc == IMX28_LRADC) {
	mxs_lradc_reg_clear(lradc,
		lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
		LRADC_CTRL1);
}

might look at least a bit less odd.

> +	mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
> +
> +	for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
> +		ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
> +		ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
> +		ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
> +		mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(ofs));
> +		bitmap_set(&enable, ofs, 1);
> +		ofs++;
> +	}
> +
> +	mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
> +			    LRADC_DELAY_KICK, LRADC_DELAY(0));
> +	mxs_lradc_reg_clear(lradc, ctrl4_clr, LRADC_CTRL4);
> +	mxs_lradc_reg_set(lradc, ctrl4_set, LRADC_CTRL4);
> +	mxs_lradc_reg_set(lradc, ctrl1_irq, LRADC_CTRL1);
> +	mxs_lradc_reg_set(lradc, enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
> +			  LRADC_DELAY(0));
> +
> +	return 0;
> +
> +err_mem:
> +	mutex_unlock(&adc->lock);
> +	return ret;
> +}

[...]

> +
> +static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio)
> +{
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +	struct mxs_lradc *lradc = adc->lradc;
> +
> +	mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
> +			    LRADC_DELAY_KICK, LRADC_DELAY(0));
> +
> +	mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
> +	if (lradc->soc == IMX28_LRADC)
> +		mxs_lradc_reg_clear(
> +			lradc,
> +			lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
> +			LRADC_CTRL1);

Same here, this is horrible :)

> +	kfree(adc->buffer);
> +	mutex_unlock(&adc->lock);
> +
> +	return 0;
> +}
[...]

Looks good otherwise :)


-- 
Best regards,
Marek Vasut

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
  2016-04-29 11:49 ` [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen Ksenija Stanojevic
@ 2016-04-29 13:22   ` Marek Vasut
  2016-04-29 23:36     ` Dmitry Torokhov
  2016-05-01 16:47   ` Jonathan Cameron
  2 siblings, 0 replies; 40+ messages in thread
From: Marek Vasut @ 2016-04-29 13:22 UTC (permalink / raw)
  To: Ksenija Stanojevic, linux-kernel
  Cc: lee.jones, dmitry.torokhov, linux-input, jic23, knaack.h, lars,
	pmeerw, linux-iio, harald

On 04/29/2016 01:49 PM, Ksenija Stanojevic wrote:
> Add mxs-lradc touchscreen driver.
> 
> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
> ---
>  drivers/input/touchscreen/Kconfig        |  14 +-
>  drivers/input/touchscreen/Makefile       |   1 +
>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
>  3 files changed, 742 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 8ecdc38..d614d248 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
>  	depends on SH_HP6XX && SH_ADC
>  	help
>  	  Say Y here if you have a HP Jornada 620/660/680/690 and want to
> -          support the built-in touchscreen.
> +	  support the built-in touchscreen.

Grumble.

This patch looks good otherwise, thanks!

-- 
Best regards,
Marek Vasut

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

* Re: [PATCH 1/3] mfd: mxs-lradc: Add support for mxs-lradc MFD
  2016-04-29 13:15     ` Marek Vasut
  (?)
@ 2016-04-29 13:43     ` Ksenija Stanojević
  2016-04-29 14:12         ` Marek Vasut
  -1 siblings, 1 reply; 40+ messages in thread
From: Ksenija Stanojević @ 2016-04-29 13:43 UTC (permalink / raw)
  To: Marek Vasut
  Cc: linux-kernel, lee.jones, Dmitry Torokhov, linux-input,
	Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, linux-iio, Harald Geyer

On Fri, Apr 29, 2016 at 3:15 PM, Marek Vasut <marex@denx.de> wrote:
> On 04/29/2016 01:47 PM, Ksenija Stanojevic wrote:
>> Add core files for mxs-lradc MFD driver.
>>
>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
>> ---
>>  drivers/mfd/Kconfig           |  33 +++++--
>>  drivers/mfd/Makefile          |   1 +
>>  drivers/mfd/mxs-lradc.c       | 213 ++++++++++++++++++++++++++++++++++++++++++
>>  include/linux/mfd/mxs-lradc.h | 210 +++++++++++++++++++++++++++++++++++++++++
>>  4 files changed, 449 insertions(+), 8 deletions(-)
>>  create mode 100644 drivers/mfd/mxs-lradc.c
>>  create mode 100644 include/linux/mfd/mxs-lradc.h
>
> Is there any chance you can also remove the same code from lradc ?

You mean drivers/iio/adc/mxs-lradc.c. I thought to remove it once this
patch set was accepted,
but I can include that in patch set.

>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>> index eea61e3..fff44d6 100644
>> --- a/drivers/mfd/Kconfig
>> +++ b/drivers/mfd/Kconfig
>> @@ -16,7 +16,7 @@ config MFD_CS5535
>>       depends on PCI && (X86_32 || (X86 && COMPILE_TEST))
>>       ---help---
>>         This is the core driver for CS5535/CS5536 MFD functions.  This is
>> -          necessary for using the board's GPIO and MFGPT functionality.
>> +       necessary for using the board's GPIO and MFGPT functionality.
>
> Probably shouldn't be part of the patch ?

Yeah, sorry about that.

>>  config MFD_ACT8945A
>>       tristate "Active-semi ACT8945A"
>> @@ -319,6 +319,23 @@ config MFD_HI6421_PMIC
>>         menus in order to enable them.
>>         We communicate with the Hi6421 via memory-mapped I/O.
>>
>> +config MFD_MXS_LRADC
>> +     tristate "Freescale i.MX23/i.MX28 LRADC"
>> +     depends on ARCH_MXS || COMPILE_TEST
>> +     select MFD_CORE
>> +     select STMP_DEVICE
>> +     help
>> +       Say yes here to build support for the low-resolution
>> +       analog-to-digital converter (LRADC) found on the i.MX23 and i.MX28
>> +       processors. This driver provides common support for accessing the
>> +       device, additional drivers must be enabled in order to use the
>> +       functionality of the device:
>> +             mxs-lradc-adc for ADC readings
>> +             mxs-lradc-ts  for touchscreen support
>> +
>> +       This driver can also be built as a module. If so, the module will be
>> +       called mxs-lradc.
>> +
>>  config HTC_EGPIO
>>       bool "HTC EGPIO support"
>>       depends on GPIOLIB && ARM
>> @@ -650,7 +667,7 @@ config EZX_PCAP
>>         needed for MMC, TouchScreen, Sound, USB, etc..
>>
>>  config MFD_VIPERBOARD
>> -        tristate "Nano River Technologies Viperboard"
>> +     tristate "Nano River Technologies Viperboard"
>
> Shouldn't be part of this patch

No, I should have check it twice.

>>       select MFD_CORE
>>       depends on USB
>>       default n
>> @@ -898,11 +915,11 @@ config MFD_SMSC
>>         select MFD_CORE
>>         select REGMAP_I2C
>>         help
>> -        If you say yes here you get support for the
>> -        ece1099 chips from SMSC.
>> +     If you say yes here you get support for the
>> +     ece1099 chips from SMSC.
>
> DTTO
>
>> -        To compile this driver as a module, choose M here: the
>> -        module will be called smsc.
>> +     To compile this driver as a module, choose M here: the
>> +     module will be called smsc.
>>
>>  config ABX500_CORE
>>       bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
>> @@ -956,8 +973,8 @@ config AB8500_DEBUG
>>         depends on AB8500_GPADC && DEBUG_FS
>>         default y if DEBUG_FS
>>         help
>> -         Select this option if you want debug information using the debug
>> -         filesystem, debugfs.
>> +      Select this option if you want debug information using the debug
>> +      filesystem, debugfs.
>
> Same here
>
>>  config AB8500_GPADC
>>       bool "ST-Ericsson AB8500 GPADC driver"
>> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
>> index 5eaa6465d..236b831 100644
>> --- a/drivers/mfd/Makefile
>> +++ b/drivers/mfd/Makefile
>> @@ -203,3 +203,4 @@ intel-soc-pmic-objs               := intel_soc_pmic_core.o intel_soc_pmic_crc.o
>>  intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC)       += intel_soc_pmic_bxtwc.o
>>  obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
>>  obj-$(CONFIG_MFD_MT6397)     += mt6397-core.o
>> +obj-$(CONFIG_MFD_MXS_LRADC)  += mxs-lradc.o
>> diff --git a/drivers/mfd/mxs-lradc.c b/drivers/mfd/mxs-lradc.c
>> new file mode 100644
>> index 0000000..e1c8f9e
>> --- /dev/null
>> +++ b/drivers/mfd/mxs-lradc.c
>> @@ -0,0 +1,213 @@
>> +/*
>> + * Freescale MXS LRADC driver
>> + *
>> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
>> + * Marek Vasut <marex@denx.de>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/device.h>
>> +#include <linux/mfd/core.h>
>> +#include <linux/mfd/mxs-lradc.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +
>> +static struct mfd_cell lradc_adc_dev = {
>> +     .name = DRIVER_NAME_ADC,
>> +};
>> +
>> +static struct mfd_cell lradc_ts_dev = {
>> +     .name = DRIVER_NAME_TS,
>> +};
>> +
>> +static const char * const mx23_lradc_irq_names[] = {
>> +     "mxs-lradc-touchscreen",
>> +     "mxs-lradc-channel0",
>> +     "mxs-lradc-channel1",
>> +     "mxs-lradc-channel2",
>> +     "mxs-lradc-channel3",
>> +     "mxs-lradc-channel4",
>> +     "mxs-lradc-channel5",
>> +     "mxs-lradc-channel6",
>> +     "mxs-lradc-channel7",
>> +};
>> +
>> +static const char * const mx28_lradc_irq_names[] = {
>> +     "mxs-lradc-touchscreen",
>> +     "mxs-lradc-thresh0",
>> +     "mxs-lradc-thresh1",
>> +     "mxs-lradc-channel0",
>> +     "mxs-lradc-channel1",
>> +     "mxs-lradc-channel2",
>> +     "mxs-lradc-channel3",
>> +     "mxs-lradc-channel4",
>> +     "mxs-lradc-channel5",
>> +     "mxs-lradc-channel6",
>> +     "mxs-lradc-channel7",
>> +     "mxs-lradc-button0",
>> +     "mxs-lradc-button1",
>> +};
>> +
>> +struct mxs_lradc_of_config {
>> +     const int               irq_count;
>> +     const char * const      *irq_name;
>> +};
>> +
>> +static const struct mxs_lradc_of_config mxs_lradc_of_config[] = {
>> +     [IMX23_LRADC] = {
>> +             .irq_count      = ARRAY_SIZE(mx23_lradc_irq_names),
>> +             .irq_name       = mx23_lradc_irq_names,
>> +     },
>> +     [IMX28_LRADC] = {
>> +             .irq_count      = ARRAY_SIZE(mx28_lradc_irq_names),
>> +             .irq_name       = mx28_lradc_irq_names,
>> +     },
>> +};
>> +
>> +static const struct of_device_id mxs_lradc_dt_ids[] = {
>> +     { .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, },
>> +     { .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, },
>> +     { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
>> +
>> +static int mxs_lradc_probe(struct platform_device *pdev)
>> +{
>> +     const struct of_device_id *of_id =
>> +             of_match_device(mxs_lradc_dt_ids, &pdev->dev);
>> +     const struct mxs_lradc_of_config *of_cfg =
>> +             &mxs_lradc_of_config[(enum mxs_lradc_id)of_id->data];
>> +     struct device *dev = &pdev->dev;
>> +     struct device_node *node = dev->of_node;
>> +     struct mxs_lradc *lradc;
>> +     struct resource *iores;
>> +     int ret = 0, touch_ret, i;
>> +     u32 ts_wires = 0;
>> +
>> +     lradc = devm_kzalloc(&pdev->dev, sizeof(*lradc), GFP_KERNEL);
>> +     if (!lradc)
>> +             return -ENOMEM;
>> +     lradc->soc = (enum mxs_lradc_id)of_id->data;
>> +
>> +     /* Grab the memory area */
>> +     iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +     lradc->base = devm_ioremap_resource(dev, iores);
>> +     if (IS_ERR(lradc->base))
>> +             return PTR_ERR(lradc->base);
>> +
>> +     lradc->clk = devm_clk_get(&pdev->dev, NULL);
>> +     if (IS_ERR(lradc->clk)) {
>> +             dev_err(dev, "Failed to get the delay unit clock\n");
>> +             return PTR_ERR(lradc->clk);
>> +     }
>> +     ret = clk_prepare_enable(lradc->clk);
>> +     if (ret != 0) {
>> +             dev_err(dev, "Failed to enable the delay unit clock\n");
>> +             return ret;
>> +     }
>> +
>> +     touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
>> +                                      &ts_wires);
>> +
>> +     if (touch_ret == 0)
>> +             lradc->buffer_vchans = BUFFER_VCHANS_LIMITED;
>> +     else
>> +             lradc->buffer_vchans = BUFFER_VCHANS_ALL;
>> +
>> +     lradc->irq_count = of_cfg->irq_count;
>> +     lradc->irq_name = of_cfg->irq_name;
>> +     for (i = 0; i < lradc->irq_count; i++) {
>> +             lradc->irq[i] = platform_get_irq(pdev, i);
>> +             if (lradc->irq[i] < 0) {
>> +                     ret = lradc->irq[i];
>> +                     goto err_clk;
>> +             }
>> +     }
>> +
>> +     platform_set_drvdata(pdev, lradc);
>> +
>> +     ret = stmp_reset_block(lradc->base);
>> +
>
> Drop this newline here
>
>> +     if (ret)
>> +             return ret;
>> +
>> +     lradc_adc_dev.platform_data = lradc;
>> +     lradc_adc_dev.pdata_size = sizeof(*lradc);
>> +
>> +     ret = mfd_add_devices(&pdev->dev, -1, &lradc_adc_dev, 1, NULL, 0, NULL);
>
> devm_mfd_add_devices()?
>
>> +     if (ret) {
>> +             dev_err(&pdev->dev, "Failed to add the ADC subdevice\n");
>> +             return ret;
>> +     }
>> +
>> +     lradc_ts_dev.platform_data = lradc;
>> +     lradc_ts_dev.pdata_size = sizeof(*lradc);
>> +
>> +     switch (ts_wires) {
>> +     case 4:
>> +             lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE;
>> +             break;
>> +     case 5:
>> +             if (lradc->soc == IMX28_LRADC) {
>> +                     lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE;
>> +                     break;
>> +             }
>> +             /* fall through an error message for i.MX23 */
>> +     default:
>> +             dev_err(&pdev->dev,
>> +                     "Unsupported number of touchscreen wires (%d)\n",
>> +                     ts_wires);
>> +             return -EINVAL;
>> +     }
>> +
>> +     ret = mfd_add_devices(&pdev->dev, -1, &lradc_ts_dev, 1, NULL, 0, NULL);
>
> devm_mfd_add_devices()?
>
> You might want to split registration of each MFD subdev into separate
> function.

Ok, I will fix it in v2

>> +     if (ret) {
>> +             dev_err(&pdev->dev,
>> +                     "Failed to add the touchscreen subdevice\n");
>> +             goto err_remove_adc;
>> +     }
>> +
>> +     return 0;
>> +
>> +err_remove_adc:
>> +     mfd_remove_devices(&pdev->dev);
>> +err_clk:
>> +     clk_disable_unprepare(lradc->clk);
>> +     return ret;
>> +}
>> +
>> +static int mxs_lradc_remove(struct platform_device *pdev)
>> +{
>> +     struct mxs_lradc *lradc = platform_get_drvdata(pdev);
>> +
>> +     mfd_remove_devices(&pdev->dev);
>> +     clk_disable_unprepare(lradc->clk);
>> +     return 0;
>> +}
>> +
>> +static struct platform_driver mxs_lradc_driver = {
>> +     .driver = {
>> +             .name = "mxs-lradc",
>> +             .of_match_table = mxs_lradc_dt_ids,
>> +     },
>> +     .probe = mxs_lradc_probe,
>> +     .remove = mxs_lradc_remove,
>> +};
>> +
>> +module_platform_driver(mxs_lradc_driver);
>> +
>> +MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:mxs-lradc");
>
> [...]
>
>
> --
> Best regards,
> Marek Vasut

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

* Re: [PATCH 1/3] mfd: mxs-lradc: Add support for mxs-lradc MFD
  2016-04-29 13:43     ` Ksenija Stanojević
@ 2016-04-29 14:12         ` Marek Vasut
  0 siblings, 0 replies; 40+ messages in thread
From: Marek Vasut @ 2016-04-29 14:12 UTC (permalink / raw)
  To: Ksenija Stanojević
  Cc: linux-kernel, lee.jones, Dmitry Torokhov, linux-input,
	Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, linux-iio, Harald Geyer

On 04/29/2016 03:43 PM, Ksenija Stanojević wrote:
> On Fri, Apr 29, 2016 at 3:15 PM, Marek Vasut <marex@denx.de> wrote:
>> On 04/29/2016 01:47 PM, Ksenija Stanojevic wrote:
>>> Add core files for mxs-lradc MFD driver.
>>>
>>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
>>> ---
>>>  drivers/mfd/Kconfig           |  33 +++++--
>>>  drivers/mfd/Makefile          |   1 +
>>>  drivers/mfd/mxs-lradc.c       | 213 ++++++++++++++++++++++++++++++++++++++++++
>>>  include/linux/mfd/mxs-lradc.h | 210 +++++++++++++++++++++++++++++++++++++++++
>>>  4 files changed, 449 insertions(+), 8 deletions(-)
>>>  create mode 100644 drivers/mfd/mxs-lradc.c
>>>  create mode 100644 include/linux/mfd/mxs-lradc.h
>>
>> Is there any chance you can also remove the same code from lradc ?
> 
> You mean drivers/iio/adc/mxs-lradc.c. I thought to remove it once this
> patch set was accepted,
> but I can include that in patch set.

I'd much rather see one driver mutate into another than having two
drivers in the tree. If that's possible without crazy amount of effort
that is.

>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>> index eea61e3..fff44d6 100644
>>> --- a/drivers/mfd/Kconfig
>>> +++ b/drivers/mfd/Kconfig
>>> @@ -16,7 +16,7 @@ config MFD_CS5535
>>>       depends on PCI && (X86_32 || (X86 && COMPILE_TEST))
>>>       ---help---
>>>         This is the core driver for CS5535/CS5536 MFD functions.  This is
>>> -          necessary for using the board's GPIO and MFGPT functionality.
>>> +       necessary for using the board's GPIO and MFGPT functionality.
>>
>> Probably shouldn't be part of the patch ?
> 
> Yeah, sorry about that.

:)

>>>  config MFD_ACT8945A
>>>       tristate "Active-semi ACT8945A"
>>> @@ -319,6 +319,23 @@ config MFD_HI6421_PMIC
>>>         menus in order to enable them.
>>>         We communicate with the Hi6421 via memory-mapped I/O.
>>>
>>> +config MFD_MXS_LRADC
>>> +     tristate "Freescale i.MX23/i.MX28 LRADC"
>>> +     depends on ARCH_MXS || COMPILE_TEST
>>> +     select MFD_CORE
>>> +     select STMP_DEVICE
>>> +     help
>>> +       Say yes here to build support for the low-resolution
>>> +       analog-to-digital converter (LRADC) found on the i.MX23 and i.MX28
>>> +       processors. This driver provides common support for accessing the
>>> +       device, additional drivers must be enabled in order to use the
>>> +       functionality of the device:
>>> +             mxs-lradc-adc for ADC readings
>>> +             mxs-lradc-ts  for touchscreen support
>>> +
>>> +       This driver can also be built as a module. If so, the module will be
>>> +       called mxs-lradc.
>>> +
>>>  config HTC_EGPIO
>>>       bool "HTC EGPIO support"
>>>       depends on GPIOLIB && ARM
>>> @@ -650,7 +667,7 @@ config EZX_PCAP
>>>         needed for MMC, TouchScreen, Sound, USB, etc..
>>>
>>>  config MFD_VIPERBOARD
>>> -        tristate "Nano River Technologies Viperboard"
>>> +     tristate "Nano River Technologies Viperboard"
>>
>> Shouldn't be part of this patch
> 
> No, I should have check it twice.

Yeah, but that's what the patch review process is for.

[...]

>>> +     ret = mfd_add_devices(&pdev->dev, -1, &lradc_ts_dev, 1, NULL, 0, NULL);
>>
>> devm_mfd_add_devices()?
>>
>> You might want to split registration of each MFD subdev into separate
>> function.
> 
> Ok, I will fix it in v2

Just be careful not to introduce a race with enabling/disabling the
clock. That's something I am not sure about and something which might
bite you.

>>> +     if (ret) {
>>> +             dev_err(&pdev->dev,
>>> +                     "Failed to add the touchscreen subdevice\n");
>>> +             goto err_remove_adc;
>>> +     }
>>> +
>>> +     return 0;
>>> +
>>> +err_remove_adc:
>>> +     mfd_remove_devices(&pdev->dev);
>>> +err_clk:
>>> +     clk_disable_unprepare(lradc->clk);
>>> +     return ret;
>>> +}
>>> +
>>> +static int mxs_lradc_remove(struct platform_device *pdev)
>>> +{
>>> +     struct mxs_lradc *lradc = platform_get_drvdata(pdev);
>>> +
>>> +     mfd_remove_devices(&pdev->dev);
>>> +     clk_disable_unprepare(lradc->clk);
>>> +     return 0;
>>> +}
>>> +
>>> +static struct platform_driver mxs_lradc_driver = {
>>> +     .driver = {
>>> +             .name = "mxs-lradc",
>>> +             .of_match_table = mxs_lradc_dt_ids,
>>> +     },
>>> +     .probe = mxs_lradc_probe,
>>> +     .remove = mxs_lradc_remove,
>>> +};
>>> +
>>> +module_platform_driver(mxs_lradc_driver);
>>> +
>>> +MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver");
>>> +MODULE_LICENSE("GPL v2");
>>> +MODULE_ALIAS("platform:mxs-lradc");
>>
>> [...]
>>
>>
>> --
>> Best regards,
>> Marek Vasut


-- 
Best regards,
Marek Vasut

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

* Re: [PATCH 1/3] mfd: mxs-lradc: Add support for mxs-lradc MFD
@ 2016-04-29 14:12         ` Marek Vasut
  0 siblings, 0 replies; 40+ messages in thread
From: Marek Vasut @ 2016-04-29 14:12 UTC (permalink / raw)
  To: Ksenija Stanojević
  Cc: linux-kernel, lee.jones, Dmitry Torokhov, linux-input,
	Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, linux-iio, Harald Geyer

On 04/29/2016 03:43 PM, Ksenija Stanojević wrote:
> On Fri, Apr 29, 2016 at 3:15 PM, Marek Vasut <marex@denx.de> wrote:
>> On 04/29/2016 01:47 PM, Ksenija Stanojevic wrote:
>>> Add core files for mxs-lradc MFD driver.
>>>
>>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
>>> ---
>>>  drivers/mfd/Kconfig           |  33 +++++--
>>>  drivers/mfd/Makefile          |   1 +
>>>  drivers/mfd/mxs-lradc.c       | 213 ++++++++++++++++++++++++++++++++++++++++++
>>>  include/linux/mfd/mxs-lradc.h | 210 +++++++++++++++++++++++++++++++++++++++++
>>>  4 files changed, 449 insertions(+), 8 deletions(-)
>>>  create mode 100644 drivers/mfd/mxs-lradc.c
>>>  create mode 100644 include/linux/mfd/mxs-lradc.h
>>
>> Is there any chance you can also remove the same code from lradc ?
> 
> You mean drivers/iio/adc/mxs-lradc.c. I thought to remove it once this
> patch set was accepted,
> but I can include that in patch set.

I'd much rather see one driver mutate into another than having two
drivers in the tree. If that's possible without crazy amount of effort
that is.

>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>> index eea61e3..fff44d6 100644
>>> --- a/drivers/mfd/Kconfig
>>> +++ b/drivers/mfd/Kconfig
>>> @@ -16,7 +16,7 @@ config MFD_CS5535
>>>       depends on PCI && (X86_32 || (X86 && COMPILE_TEST))
>>>       ---help---
>>>         This is the core driver for CS5535/CS5536 MFD functions.  This is
>>> -          necessary for using the board's GPIO and MFGPT functionality.
>>> +       necessary for using the board's GPIO and MFGPT functionality.
>>
>> Probably shouldn't be part of the patch ?
> 
> Yeah, sorry about that.

:)

>>>  config MFD_ACT8945A
>>>       tristate "Active-semi ACT8945A"
>>> @@ -319,6 +319,23 @@ config MFD_HI6421_PMIC
>>>         menus in order to enable them.
>>>         We communicate with the Hi6421 via memory-mapped I/O.
>>>
>>> +config MFD_MXS_LRADC
>>> +     tristate "Freescale i.MX23/i.MX28 LRADC"
>>> +     depends on ARCH_MXS || COMPILE_TEST
>>> +     select MFD_CORE
>>> +     select STMP_DEVICE
>>> +     help
>>> +       Say yes here to build support for the low-resolution
>>> +       analog-to-digital converter (LRADC) found on the i.MX23 and i.MX28
>>> +       processors. This driver provides common support for accessing the
>>> +       device, additional drivers must be enabled in order to use the
>>> +       functionality of the device:
>>> +             mxs-lradc-adc for ADC readings
>>> +             mxs-lradc-ts  for touchscreen support
>>> +
>>> +       This driver can also be built as a module. If so, the module will be
>>> +       called mxs-lradc.
>>> +
>>>  config HTC_EGPIO
>>>       bool "HTC EGPIO support"
>>>       depends on GPIOLIB && ARM
>>> @@ -650,7 +667,7 @@ config EZX_PCAP
>>>         needed for MMC, TouchScreen, Sound, USB, etc..
>>>
>>>  config MFD_VIPERBOARD
>>> -        tristate "Nano River Technologies Viperboard"
>>> +     tristate "Nano River Technologies Viperboard"
>>
>> Shouldn't be part of this patch
> 
> No, I should have check it twice.

Yeah, but that's what the patch review process is for.

[...]

>>> +     ret = mfd_add_devices(&pdev->dev, -1, &lradc_ts_dev, 1, NULL, 0, NULL);
>>
>> devm_mfd_add_devices()?
>>
>> You might want to split registration of each MFD subdev into separate
>> function.
> 
> Ok, I will fix it in v2

Just be careful not to introduce a race with enabling/disabling the
clock. That's something I am not sure about and something which might
bite you.

>>> +     if (ret) {
>>> +             dev_err(&pdev->dev,
>>> +                     "Failed to add the touchscreen subdevice\n");
>>> +             goto err_remove_adc;
>>> +     }
>>> +
>>> +     return 0;
>>> +
>>> +err_remove_adc:
>>> +     mfd_remove_devices(&pdev->dev);
>>> +err_clk:
>>> +     clk_disable_unprepare(lradc->clk);
>>> +     return ret;
>>> +}
>>> +
>>> +static int mxs_lradc_remove(struct platform_device *pdev)
>>> +{
>>> +     struct mxs_lradc *lradc = platform_get_drvdata(pdev);
>>> +
>>> +     mfd_remove_devices(&pdev->dev);
>>> +     clk_disable_unprepare(lradc->clk);
>>> +     return 0;
>>> +}
>>> +
>>> +static struct platform_driver mxs_lradc_driver = {
>>> +     .driver = {
>>> +             .name = "mxs-lradc",
>>> +             .of_match_table = mxs_lradc_dt_ids,
>>> +     },
>>> +     .probe = mxs_lradc_probe,
>>> +     .remove = mxs_lradc_remove,
>>> +};
>>> +
>>> +module_platform_driver(mxs_lradc_driver);
>>> +
>>> +MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver");
>>> +MODULE_LICENSE("GPL v2");
>>> +MODULE_ALIAS("platform:mxs-lradc");
>>
>> [...]
>>
>>
>> --
>> Best regards,
>> Marek Vasut


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

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

* Re: [PATCH 1/3] mfd: mxs-lradc: Add support for mxs-lradc MFD
@ 2016-04-29 17:19     ` Harald Geyer
  0 siblings, 0 replies; 40+ messages in thread
From: Harald Geyer @ 2016-04-29 17:19 UTC (permalink / raw)
  To: Ksenija Stanojevic
  Cc: linux-kernel, lee.jones, dmitry.torokhov, linux-input, jic23,
	knaack.h, lars, pmeerw, marex, linux-iio

Hi Ksenija,

thanks for working on this.

A bit of nit picking inline.

On 29.04.2016 13:47, Ksenija Stanojevic wrote:
> Add core files for mxs-lradc MFD driver.
>
> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
> ---
>  drivers/mfd/Kconfig           |  33 +++++--
>  drivers/mfd/Makefile          |   1 +
>  drivers/mfd/mxs-lradc.c       | 213
> ++++++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/mxs-lradc.h | 210
> +++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 449 insertions(+), 8 deletions(-)
>  create mode 100644 drivers/mfd/mxs-lradc.c
>  create mode 100644 include/linux/mfd/mxs-lradc.h
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index eea61e3..fff44d6 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -16,7 +16,7 @@ config MFD_CS5535
>  	depends on PCI && (X86_32 || (X86 && COMPILE_TEST))
>  	---help---
>  	  This is the core driver for CS5535/CS5536 MFD functions.  This is
> -          necessary for using the board's GPIO and MFGPT 
> functionality.
> +	  necessary for using the board's GPIO and MFGPT functionality.
>
>  config MFD_ACT8945A
>  	tristate "Active-semi ACT8945A"
> @@ -319,6 +319,23 @@ config MFD_HI6421_PMIC
>  	  menus in order to enable them.
>  	  We communicate with the Hi6421 via memory-mapped I/O.
>
> +config MFD_MXS_LRADC
> +	tristate "Freescale i.MX23/i.MX28 LRADC"
> +	depends on ARCH_MXS || COMPILE_TEST
> +	select MFD_CORE
> +	select STMP_DEVICE
> +	help
> +	  Say yes here to build support for the low-resolution
> +	  analog-to-digital converter (LRADC) found on the i.MX23 and 
> i.MX28
> +	  processors. This driver provides common support for accessing the
> +	  device, additional drivers must be enabled in order to use the
> +	  functionality of the device:
> +		mxs-lradc-adc for ADC readings
> +		mxs-lradc-ts  for touchscreen support
> +
> +	  This driver can also be built as a module. If so, the module will 
> be
> +	  called mxs-lradc.
> +
>  config HTC_EGPIO
>  	bool "HTC EGPIO support"
>  	depends on GPIOLIB && ARM
> @@ -650,7 +667,7 @@ config EZX_PCAP
>  	  needed for MMC, TouchScreen, Sound, USB, etc..
>
>  config MFD_VIPERBOARD
> -        tristate "Nano River Technologies Viperboard"
> +	tristate "Nano River Technologies Viperboard"
>  	select MFD_CORE
>  	depends on USB
>  	default n
> @@ -898,11 +915,11 @@ config MFD_SMSC
>         select MFD_CORE
>         select REGMAP_I2C
>         help
> -        If you say yes here you get support for the
> -        ece1099 chips from SMSC.
> +	If you say yes here you get support for the
> +	ece1099 chips from SMSC.
>
> -        To compile this driver as a module, choose M here: the
> -        module will be called smsc.
> +	To compile this driver as a module, choose M here: the
> +	module will be called smsc.
>
>  config ABX500_CORE
>  	bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
> @@ -956,8 +973,8 @@ config AB8500_DEBUG
>         depends on AB8500_GPADC && DEBUG_FS
>         default y if DEBUG_FS
>         help
> -         Select this option if you want debug information using the 
> debug
> -         filesystem, debugfs.
> +	 Select this option if you want debug information using the debug
> +	 filesystem, debugfs.
>
>  config AB8500_GPADC
>  	bool "ST-Ericsson AB8500 GPADC driver"
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 5eaa6465d..236b831 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -203,3 +203,4 @@ intel-soc-pmic-objs		:= intel_soc_pmic_core.o
> intel_soc_pmic_crc.o
>  intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC)	+= intel_soc_pmic_bxtwc.o
>  obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
>  obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
> +obj-$(CONFIG_MFD_MXS_LRADC)	+= mxs-lradc.o
> diff --git a/drivers/mfd/mxs-lradc.c b/drivers/mfd/mxs-lradc.c
> new file mode 100644
> index 0000000..e1c8f9e
> --- /dev/null
> +++ b/drivers/mfd/mxs-lradc.c
> @@ -0,0 +1,213 @@
> +/*
> + * Freescale MXS LRADC driver
> + *
> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
> + * Marek Vasut <marex@denx.de>
> + *
> + * This program is free software; you can redistribute it and/or 
> modify
> + * it under the terms of the GNU General Public License as published 
> by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/mxs-lradc.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>

IIRC all necessary header files should be included directly from
the source file instead of being pulled in by other header files.

> +static struct mfd_cell lradc_adc_dev = {
> +	.name = DRIVER_NAME_ADC,
> +};
> +
> +static struct mfd_cell lradc_ts_dev = {
> +	.name = DRIVER_NAME_TS,
> +};

All chips using this driver have only one instance of this, so
the above static structs are fine. However below
struct mxs-lradc is allocated dynamically (to allow for
multiple lradc blocks in theory I suppose). I don't have
strong feelings either way, but I think you should make up
your mind one way or the other.

I'd make these structs either members of mxs-lradc or even
allocate them dynamically.

Thanks,
Harald

> +static const char * const mx23_lradc_irq_names[] = {
> +	"mxs-lradc-touchscreen",
> +	"mxs-lradc-channel0",
> +	"mxs-lradc-channel1",
> +	"mxs-lradc-channel2",
> +	"mxs-lradc-channel3",
> +	"mxs-lradc-channel4",
> +	"mxs-lradc-channel5",
> +	"mxs-lradc-channel6",
> +	"mxs-lradc-channel7",
> +};
> +
> +static const char * const mx28_lradc_irq_names[] = {
> +	"mxs-lradc-touchscreen",
> +	"mxs-lradc-thresh0",
> +	"mxs-lradc-thresh1",
> +	"mxs-lradc-channel0",
> +	"mxs-lradc-channel1",
> +	"mxs-lradc-channel2",
> +	"mxs-lradc-channel3",
> +	"mxs-lradc-channel4",
> +	"mxs-lradc-channel5",
> +	"mxs-lradc-channel6",
> +	"mxs-lradc-channel7",
> +	"mxs-lradc-button0",
> +	"mxs-lradc-button1",
> +};
> +
> +struct mxs_lradc_of_config {
> +	const int		irq_count;
> +	const char * const	*irq_name;
> +};
> +
> +static const struct mxs_lradc_of_config mxs_lradc_of_config[] = {
> +	[IMX23_LRADC] = {
> +		.irq_count	= ARRAY_SIZE(mx23_lradc_irq_names),
> +		.irq_name	= mx23_lradc_irq_names,
> +	},
> +	[IMX28_LRADC] = {
> +		.irq_count	= ARRAY_SIZE(mx28_lradc_irq_names),
> +		.irq_name	= mx28_lradc_irq_names,
> +	},
> +};
> +
> +static const struct of_device_id mxs_lradc_dt_ids[] = {
> +	{ .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, },
> +	{ .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
> +
> +static int mxs_lradc_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *of_id =
> +		of_match_device(mxs_lradc_dt_ids, &pdev->dev);
> +	const struct mxs_lradc_of_config *of_cfg =
> +		&mxs_lradc_of_config[(enum mxs_lradc_id)of_id->data];
> +	struct device *dev = &pdev->dev;
> +	struct device_node *node = dev->of_node;
> +	struct mxs_lradc *lradc;
> +	struct resource *iores;
> +	int ret = 0, touch_ret, i;
> +	u32 ts_wires = 0;
> +
> +	lradc = devm_kzalloc(&pdev->dev, sizeof(*lradc), GFP_KERNEL);
> +	if (!lradc)
> +		return -ENOMEM;
> +	lradc->soc = (enum mxs_lradc_id)of_id->data;
> +
> +	/* Grab the memory area */
> +	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	lradc->base = devm_ioremap_resource(dev, iores);
> +	if (IS_ERR(lradc->base))
> +		return PTR_ERR(lradc->base);
> +
> +	lradc->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(lradc->clk)) {
> +		dev_err(dev, "Failed to get the delay unit clock\n");
> +		return PTR_ERR(lradc->clk);
> +	}
> +	ret = clk_prepare_enable(lradc->clk);
> +	if (ret != 0) {
> +		dev_err(dev, "Failed to enable the delay unit clock\n");
> +		return ret;
> +	}
> +
> +	touch_ret = of_property_read_u32(node, 
> "fsl,lradc-touchscreen-wires",
> +					 &ts_wires);
> +
> +	if (touch_ret == 0)
> +		lradc->buffer_vchans = BUFFER_VCHANS_LIMITED;
> +	else
> +		lradc->buffer_vchans = BUFFER_VCHANS_ALL;
> +
> +	lradc->irq_count = of_cfg->irq_count;
> +	lradc->irq_name = of_cfg->irq_name;
> +	for (i = 0; i < lradc->irq_count; i++) {
> +		lradc->irq[i] = platform_get_irq(pdev, i);
> +		if (lradc->irq[i] < 0) {
> +			ret = lradc->irq[i];
> +			goto err_clk;
> +		}
> +	}
> +
> +	platform_set_drvdata(pdev, lradc);
> +
> +	ret = stmp_reset_block(lradc->base);
> +
> +	if (ret)
> +		return ret;
> +
> +	lradc_adc_dev.platform_data = lradc;
> +	lradc_adc_dev.pdata_size = sizeof(*lradc);
> +
> +	ret = mfd_add_devices(&pdev->dev, -1, &lradc_adc_dev, 1, NULL, 0, 
> NULL);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to add the ADC subdevice\n");
> +		return ret;
> +	}
> +
> +	lradc_ts_dev.platform_data = lradc;
> +	lradc_ts_dev.pdata_size = sizeof(*lradc);
> +
> +	switch (ts_wires) {
> +	case 4:
> +		lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE;
> +		break;
> +	case 5:
> +		if (lradc->soc == IMX28_LRADC) {
> +			lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE;
> +			break;
> +		}
> +		/* fall through an error message for i.MX23 */
> +	default:
> +		dev_err(&pdev->dev,
> +			"Unsupported number of touchscreen wires (%d)\n",
> +			ts_wires);
> +		return -EINVAL;
> +	}
> +
> +	ret = mfd_add_devices(&pdev->dev, -1, &lradc_ts_dev, 1, NULL, 0, 
> NULL);
> +	if (ret) {
> +		dev_err(&pdev->dev,
> +			"Failed to add the touchscreen subdevice\n");
> +		goto err_remove_adc;
> +	}
> +
> +	return 0;
> +
> +err_remove_adc:
> +	mfd_remove_devices(&pdev->dev);
> +err_clk:
> +	clk_disable_unprepare(lradc->clk);
> +	return ret;
> +}
> +
> +static int mxs_lradc_remove(struct platform_device *pdev)
> +{
> +	struct mxs_lradc *lradc = platform_get_drvdata(pdev);
> +
> +	mfd_remove_devices(&pdev->dev);
> +	clk_disable_unprepare(lradc->clk);
> +	return 0;
> +}
> +
> +static struct platform_driver mxs_lradc_driver = {
> +	.driver = {
> +		.name = "mxs-lradc",
> +		.of_match_table = mxs_lradc_dt_ids,
> +	},
> +	.probe = mxs_lradc_probe,
> +	.remove = mxs_lradc_remove,
> +};
> +
> +module_platform_driver(mxs_lradc_driver);
> +
> +MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:mxs-lradc");
> diff --git a/include/linux/mfd/mxs-lradc.h 
> b/include/linux/mfd/mxs-lradc.h
> new file mode 100644
> index 0000000..c062969
> --- /dev/null
> +++ b/include/linux/mfd/mxs-lradc.h
> @@ -0,0 +1,210 @@
> +/*
> + * Freescale MXS LRADC driver
> + *
> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
> + * Marek Vasut <marex@denx.de>
> + *
> + * This program is free software; you can redistribute it and/or 
> modify
> + * it under the terms of the GNU General Public License as published 
> by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __MXS_LRADC_H
> +#define __MXS_LRADC_H
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/stmp_device.h>
> +#include <linux/sysfs.h>
> +
> +#define DRIVER_NAME_ADC "mxs-lradc-adc"
> +#define DRIVER_NAME_TS "mxs-lradc-ts"
> +
> +#define LRADC_MAX_DELAY_CHANS	4
> +#define LRADC_MAX_MAPPED_CHANS	8
> +#define LRADC_MAX_TOTAL_CHANS	16
> +
> +#define LRADC_DELAY_TIMER_HZ	2000
> +
> +#define LRADC_CTRL0				0x00
> +# define LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE	BIT(23)
> +# define LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE	BIT(22)
> +# define LRADC_CTRL0_MX28_YNNSW /* YM */	BIT(21)
> +# define LRADC_CTRL0_MX28_YPNSW /* YP */	BIT(20)
> +# define LRADC_CTRL0_MX28_YPPSW /* YP */	BIT(19)
> +# define LRADC_CTRL0_MX28_XNNSW /* XM */	BIT(18)
> +# define LRADC_CTRL0_MX28_XNPSW /* XM */	BIT(17)
> +# define LRADC_CTRL0_MX28_XPPSW /* XP */	BIT(16)
> +
> +# define LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE	BIT(20)
> +# define LRADC_CTRL0_MX23_YM			BIT(19)
> +# define LRADC_CTRL0_MX23_XM			BIT(18)
> +# define LRADC_CTRL0_MX23_YP			BIT(17)
> +# define LRADC_CTRL0_MX23_XP			BIT(16)
> +
> +# define LRADC_CTRL0_MX28_PLATE_MASK \
> +		(LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE | \
> +		LRADC_CTRL0_MX28_YNNSW | LRADC_CTRL0_MX28_YPNSW | \
> +		LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW | \
> +		LRADC_CTRL0_MX28_XNPSW | LRADC_CTRL0_MX28_XPPSW)
> +
> +# define LRADC_CTRL0_MX23_PLATE_MASK \
> +		(LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE | \
> +		LRADC_CTRL0_MX23_YM | LRADC_CTRL0_MX23_XM | \
> +		LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XP)
> +
> +#define LRADC_CTRL1				0x10
> +#define LRADC_CTRL1_TOUCH_DETECT_IRQ_EN		BIT(24)
> +#define LRADC_CTRL1_LRADC_IRQ_EN(n)		(1 << ((n) + 16))
> +#define LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK	(0x1fff << 16)
> +#define LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK	(0x01ff << 16)
> +#define LRADC_CTRL1_LRADC_IRQ_EN_OFFSET		16
> +#define LRADC_CTRL1_TOUCH_DETECT_IRQ		BIT(8)
> +#define LRADC_CTRL1_LRADC_IRQ(n)		(1 << (n))
> +#define LRADC_CTRL1_MX28_LRADC_IRQ_MASK		0x1fff
> +#define LRADC_CTRL1_MX23_LRADC_IRQ_MASK		0x01ff
> +#define LRADC_CTRL1_LRADC_IRQ_OFFSET		0
> +
> +#define LRADC_CTRL2				0x20
> +#define LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET	24
> +#define LRADC_CTRL2_TEMPSENSE_PWD		BIT(15)
> +
> +#define LRADC_STATUS				0x40
> +#define LRADC_STATUS_TOUCH_DETECT_RAW		BIT(0)
> +
> +#define LRADC_CH(n)				(0x50 + (0x10 * (n)))
> +#define LRADC_CH_ACCUMULATE			BIT(29)
> +#define LRADC_CH_NUM_SAMPLES_MASK		(0x1f << 24)
> +#define LRADC_CH_NUM_SAMPLES_OFFSET		24
> +#define LRADC_CH_NUM_SAMPLES(x) \
> +				((x) << LRADC_CH_NUM_SAMPLES_OFFSET)
> +#define LRADC_CH_VALUE_MASK			0x3ffff
> +#define LRADC_CH_VALUE_OFFSET			0
> +
> +#define LRADC_DELAY(n)				(0xd0 + (0x10 * (n)))
> +#define LRADC_DELAY_TRIGGER_LRADCS_MASK		(0xffUL << 24)
> +#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET	24
> +#define LRADC_DELAY_TRIGGER(x) \
> +				(((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \
> +				LRADC_DELAY_TRIGGER_LRADCS_MASK)
> +#define LRADC_DELAY_KICK			BIT(20)
> +#define LRADC_DELAY_TRIGGER_DELAYS_MASK		(0xf << 16)
> +#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET	16
> +#define LRADC_DELAY_TRIGGER_DELAYS(x) \
> +				(((x) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) & \
> +				LRADC_DELAY_TRIGGER_DELAYS_MASK)
> +#define LRADC_DELAY_LOOP_COUNT_MASK		(0x1f << 11)
> +#define LRADC_DELAY_LOOP_COUNT_OFFSET		11
> +#define LRADC_DELAY_LOOP(x) \
> +				(((x) << LRADC_DELAY_LOOP_COUNT_OFFSET) & \
> +				LRADC_DELAY_LOOP_COUNT_MASK)
> +#define LRADC_DELAY_DELAY_MASK			0x7ff
> +#define LRADC_DELAY_DELAY_OFFSET		0
> +#define LRADC_DELAY_DELAY(x) \
> +				(((x) << LRADC_DELAY_DELAY_OFFSET) & \
> +				LRADC_DELAY_DELAY_MASK)
> +
> +#define LRADC_CTRL4				0x140
> +#define LRADC_CTRL4_LRADCSELECT_MASK(n)		(0xf << ((n) * 4))
> +#define LRADC_CTRL4_LRADCSELECT_OFFSET(n)	((n) * 4)
> +#define LRADC_CTRL4_LRADCSELECT(n, x) \
> +				(((x) << LRADC_CTRL4_LRADCSELECT_OFFSET(n)) & \
> +				LRADC_CTRL4_LRADCSELECT_MASK(n))
> +
> +#define LRADC_RESOLUTION			12
> +#define LRADC_SINGLE_SAMPLE_MASK		((1 << LRADC_RESOLUTION) - 1)
> +
> +enum mxs_lradc_id {
> +	IMX23_LRADC,
> +	IMX28_LRADC,
> +};
> +
> +enum mxs_lradc_ts_wires {
> +	MXS_LRADC_TOUCHSCREEN_NONE = 0,
> +	MXS_LRADC_TOUCHSCREEN_4WIRE,
> +	MXS_LRADC_TOUCHSCREEN_5WIRE,
> +};
> +
> +struct mxs_lradc {
> +	enum mxs_lradc_id	soc;
> +
> +	void __iomem		*base;
> +	struct clk		*clk;
> +
> +	int			irq[13];
> +	const char * const	*irq_name;
> +	int			irq_count;
> +
> +#define BUFFER_VCHANS_LIMITED		0x3f
> +#define BUFFER_VCHANS_ALL		0xff
> +	u8			buffer_vchans;
> +
> +	/*
> +	 * Certain LRADC channels are shared between touchscreen
> +	 * and/or touch-buttons and generic LRADC block. Therefore when 
> using
> +	 * either of these, these channels are not available for the 
> regular
> +	 * sampling. The shared channels are as follows:
> +	 *
> +	 * CH0 -- Touch button #0
> +	 * CH1 -- Touch button #1
> +	 * CH2 -- Touch screen XPUL
> +	 * CH3 -- Touch screen YPLL
> +	 * CH4 -- Touch screen XNUL
> +	 * CH5 -- Touch screen YNLR
> +	 * CH6 -- Touch screen WIPER (5-wire only)
> +	 *
> +	 * The bit fields below represents which parts of the LRADC block 
> are
> +	 * switched into special mode of operation. These channels can not
> +	 * be sampled as regular LRADC channels. The driver will refuse any
> +	 * attempt to sample these channels.
> +	 */
> +#define CHAN_MASK_TOUCHBUTTON		(BIT(1) | BIT(0))
> +#define CHAN_MASK_TOUCHSCREEN_4WIRE	(0xf << 2)
> +#define CHAN_MASK_TOUCHSCREEN_5WIRE	(0x1f << 2)
> +	enum mxs_lradc_ts_wires	use_touchscreen;
> +	bool			use_touchbutton;
> +};
> +
> +static inline void mxs_lradc_reg_set(struct mxs_lradc *lradc, u32
> val, u32 reg)
> +{
> +	writel(val, lradc->base + reg + STMP_OFFSET_REG_SET);
> +}
> +
> +static inline void mxs_lradc_reg_clear(struct mxs_lradc *lradc, u32 
> val,
> +				       u32 reg)
> +{
> +	writel(val, lradc->base + reg + STMP_OFFSET_REG_CLR);
> +}
> +
> +static inline void mxs_lradc_reg_wrt(struct mxs_lradc *lradc, u32
> val, u32 reg)
> +{
> +	writel(val, lradc->base + reg);
> +}
> +
> +static inline u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL1_MX23_LRADC_IRQ_MASK;
> +	return LRADC_CTRL1_MX28_LRADC_IRQ_MASK;
> +}
> +
> +#endif /* __MXS_LRADC_H */

-- 
If you want to support my work:
see http://friends.ccbib.org/harald/supporting/
or donate via peercoin to P98LRdhit3gZbHDBe7ta5jtXrMJUms4p7w
or bitcoin 1FUtd8T9jRN1rFz63vZz7s2fDtB6d6A7aS

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

* Re: [PATCH 1/3] mfd: mxs-lradc: Add support for mxs-lradc MFD
@ 2016-04-29 17:19     ` Harald Geyer
  0 siblings, 0 replies; 40+ messages in thread
From: Harald Geyer @ 2016-04-29 17:19 UTC (permalink / raw)
  To: Ksenija Stanojevic
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	linux-input-u79uwXL29TY76Z2rM5mHXA, jic23-DgEjT+Ai2ygdnm+yROfE0A,
	knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, marex-ynQEQJNshbs,
	linux-iio-u79uwXL29TY76Z2rM5mHXA

Hi Ksenija,

thanks for working on this.

A bit of nit picking inline.

On 29.04.2016 13:47, Ksenija Stanojevic wrote:
> Add core files for mxs-lradc MFD driver.
>
> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
>  drivers/mfd/Kconfig           |  33 +++++--
>  drivers/mfd/Makefile          |   1 +
>  drivers/mfd/mxs-lradc.c       | 213
> ++++++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/mxs-lradc.h | 210
> +++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 449 insertions(+), 8 deletions(-)
>  create mode 100644 drivers/mfd/mxs-lradc.c
>  create mode 100644 include/linux/mfd/mxs-lradc.h
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index eea61e3..fff44d6 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -16,7 +16,7 @@ config MFD_CS5535
>  	depends on PCI && (X86_32 || (X86 && COMPILE_TEST))
>  	---help---
>  	  This is the core driver for CS5535/CS5536 MFD functions.  This is
> -          necessary for using the board's GPIO and MFGPT 
> functionality.
> +	  necessary for using the board's GPIO and MFGPT functionality.
>
>  config MFD_ACT8945A
>  	tristate "Active-semi ACT8945A"
> @@ -319,6 +319,23 @@ config MFD_HI6421_PMIC
>  	  menus in order to enable them.
>  	  We communicate with the Hi6421 via memory-mapped I/O.
>
> +config MFD_MXS_LRADC
> +	tristate "Freescale i.MX23/i.MX28 LRADC"
> +	depends on ARCH_MXS || COMPILE_TEST
> +	select MFD_CORE
> +	select STMP_DEVICE
> +	help
> +	  Say yes here to build support for the low-resolution
> +	  analog-to-digital converter (LRADC) found on the i.MX23 and 
> i.MX28
> +	  processors. This driver provides common support for accessing the
> +	  device, additional drivers must be enabled in order to use the
> +	  functionality of the device:
> +		mxs-lradc-adc for ADC readings
> +		mxs-lradc-ts  for touchscreen support
> +
> +	  This driver can also be built as a module. If so, the module will 
> be
> +	  called mxs-lradc.
> +
>  config HTC_EGPIO
>  	bool "HTC EGPIO support"
>  	depends on GPIOLIB && ARM
> @@ -650,7 +667,7 @@ config EZX_PCAP
>  	  needed for MMC, TouchScreen, Sound, USB, etc..
>
>  config MFD_VIPERBOARD
> -        tristate "Nano River Technologies Viperboard"
> +	tristate "Nano River Technologies Viperboard"
>  	select MFD_CORE
>  	depends on USB
>  	default n
> @@ -898,11 +915,11 @@ config MFD_SMSC
>         select MFD_CORE
>         select REGMAP_I2C
>         help
> -        If you say yes here you get support for the
> -        ece1099 chips from SMSC.
> +	If you say yes here you get support for the
> +	ece1099 chips from SMSC.
>
> -        To compile this driver as a module, choose M here: the
> -        module will be called smsc.
> +	To compile this driver as a module, choose M here: the
> +	module will be called smsc.
>
>  config ABX500_CORE
>  	bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
> @@ -956,8 +973,8 @@ config AB8500_DEBUG
>         depends on AB8500_GPADC && DEBUG_FS
>         default y if DEBUG_FS
>         help
> -         Select this option if you want debug information using the 
> debug
> -         filesystem, debugfs.
> +	 Select this option if you want debug information using the debug
> +	 filesystem, debugfs.
>
>  config AB8500_GPADC
>  	bool "ST-Ericsson AB8500 GPADC driver"
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 5eaa6465d..236b831 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -203,3 +203,4 @@ intel-soc-pmic-objs		:= intel_soc_pmic_core.o
> intel_soc_pmic_crc.o
>  intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC)	+= intel_soc_pmic_bxtwc.o
>  obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
>  obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
> +obj-$(CONFIG_MFD_MXS_LRADC)	+= mxs-lradc.o
> diff --git a/drivers/mfd/mxs-lradc.c b/drivers/mfd/mxs-lradc.c
> new file mode 100644
> index 0000000..e1c8f9e
> --- /dev/null
> +++ b/drivers/mfd/mxs-lradc.c
> @@ -0,0 +1,213 @@
> +/*
> + * Freescale MXS LRADC driver
> + *
> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
> + * Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or 
> modify
> + * it under the terms of the GNU General Public License as published 
> by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/mxs-lradc.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>

IIRC all necessary header files should be included directly from
the source file instead of being pulled in by other header files.

> +static struct mfd_cell lradc_adc_dev = {
> +	.name = DRIVER_NAME_ADC,
> +};
> +
> +static struct mfd_cell lradc_ts_dev = {
> +	.name = DRIVER_NAME_TS,
> +};

All chips using this driver have only one instance of this, so
the above static structs are fine. However below
struct mxs-lradc is allocated dynamically (to allow for
multiple lradc blocks in theory I suppose). I don't have
strong feelings either way, but I think you should make up
your mind one way or the other.

I'd make these structs either members of mxs-lradc or even
allocate them dynamically.

Thanks,
Harald

> +static const char * const mx23_lradc_irq_names[] = {
> +	"mxs-lradc-touchscreen",
> +	"mxs-lradc-channel0",
> +	"mxs-lradc-channel1",
> +	"mxs-lradc-channel2",
> +	"mxs-lradc-channel3",
> +	"mxs-lradc-channel4",
> +	"mxs-lradc-channel5",
> +	"mxs-lradc-channel6",
> +	"mxs-lradc-channel7",
> +};
> +
> +static const char * const mx28_lradc_irq_names[] = {
> +	"mxs-lradc-touchscreen",
> +	"mxs-lradc-thresh0",
> +	"mxs-lradc-thresh1",
> +	"mxs-lradc-channel0",
> +	"mxs-lradc-channel1",
> +	"mxs-lradc-channel2",
> +	"mxs-lradc-channel3",
> +	"mxs-lradc-channel4",
> +	"mxs-lradc-channel5",
> +	"mxs-lradc-channel6",
> +	"mxs-lradc-channel7",
> +	"mxs-lradc-button0",
> +	"mxs-lradc-button1",
> +};
> +
> +struct mxs_lradc_of_config {
> +	const int		irq_count;
> +	const char * const	*irq_name;
> +};
> +
> +static const struct mxs_lradc_of_config mxs_lradc_of_config[] = {
> +	[IMX23_LRADC] = {
> +		.irq_count	= ARRAY_SIZE(mx23_lradc_irq_names),
> +		.irq_name	= mx23_lradc_irq_names,
> +	},
> +	[IMX28_LRADC] = {
> +		.irq_count	= ARRAY_SIZE(mx28_lradc_irq_names),
> +		.irq_name	= mx28_lradc_irq_names,
> +	},
> +};
> +
> +static const struct of_device_id mxs_lradc_dt_ids[] = {
> +	{ .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, },
> +	{ .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
> +
> +static int mxs_lradc_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *of_id =
> +		of_match_device(mxs_lradc_dt_ids, &pdev->dev);
> +	const struct mxs_lradc_of_config *of_cfg =
> +		&mxs_lradc_of_config[(enum mxs_lradc_id)of_id->data];
> +	struct device *dev = &pdev->dev;
> +	struct device_node *node = dev->of_node;
> +	struct mxs_lradc *lradc;
> +	struct resource *iores;
> +	int ret = 0, touch_ret, i;
> +	u32 ts_wires = 0;
> +
> +	lradc = devm_kzalloc(&pdev->dev, sizeof(*lradc), GFP_KERNEL);
> +	if (!lradc)
> +		return -ENOMEM;
> +	lradc->soc = (enum mxs_lradc_id)of_id->data;
> +
> +	/* Grab the memory area */
> +	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	lradc->base = devm_ioremap_resource(dev, iores);
> +	if (IS_ERR(lradc->base))
> +		return PTR_ERR(lradc->base);
> +
> +	lradc->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(lradc->clk)) {
> +		dev_err(dev, "Failed to get the delay unit clock\n");
> +		return PTR_ERR(lradc->clk);
> +	}
> +	ret = clk_prepare_enable(lradc->clk);
> +	if (ret != 0) {
> +		dev_err(dev, "Failed to enable the delay unit clock\n");
> +		return ret;
> +	}
> +
> +	touch_ret = of_property_read_u32(node, 
> "fsl,lradc-touchscreen-wires",
> +					 &ts_wires);
> +
> +	if (touch_ret == 0)
> +		lradc->buffer_vchans = BUFFER_VCHANS_LIMITED;
> +	else
> +		lradc->buffer_vchans = BUFFER_VCHANS_ALL;
> +
> +	lradc->irq_count = of_cfg->irq_count;
> +	lradc->irq_name = of_cfg->irq_name;
> +	for (i = 0; i < lradc->irq_count; i++) {
> +		lradc->irq[i] = platform_get_irq(pdev, i);
> +		if (lradc->irq[i] < 0) {
> +			ret = lradc->irq[i];
> +			goto err_clk;
> +		}
> +	}
> +
> +	platform_set_drvdata(pdev, lradc);
> +
> +	ret = stmp_reset_block(lradc->base);
> +
> +	if (ret)
> +		return ret;
> +
> +	lradc_adc_dev.platform_data = lradc;
> +	lradc_adc_dev.pdata_size = sizeof(*lradc);
> +
> +	ret = mfd_add_devices(&pdev->dev, -1, &lradc_adc_dev, 1, NULL, 0, 
> NULL);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to add the ADC subdevice\n");
> +		return ret;
> +	}
> +
> +	lradc_ts_dev.platform_data = lradc;
> +	lradc_ts_dev.pdata_size = sizeof(*lradc);
> +
> +	switch (ts_wires) {
> +	case 4:
> +		lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE;
> +		break;
> +	case 5:
> +		if (lradc->soc == IMX28_LRADC) {
> +			lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE;
> +			break;
> +		}
> +		/* fall through an error message for i.MX23 */
> +	default:
> +		dev_err(&pdev->dev,
> +			"Unsupported number of touchscreen wires (%d)\n",
> +			ts_wires);
> +		return -EINVAL;
> +	}
> +
> +	ret = mfd_add_devices(&pdev->dev, -1, &lradc_ts_dev, 1, NULL, 0, 
> NULL);
> +	if (ret) {
> +		dev_err(&pdev->dev,
> +			"Failed to add the touchscreen subdevice\n");
> +		goto err_remove_adc;
> +	}
> +
> +	return 0;
> +
> +err_remove_adc:
> +	mfd_remove_devices(&pdev->dev);
> +err_clk:
> +	clk_disable_unprepare(lradc->clk);
> +	return ret;
> +}
> +
> +static int mxs_lradc_remove(struct platform_device *pdev)
> +{
> +	struct mxs_lradc *lradc = platform_get_drvdata(pdev);
> +
> +	mfd_remove_devices(&pdev->dev);
> +	clk_disable_unprepare(lradc->clk);
> +	return 0;
> +}
> +
> +static struct platform_driver mxs_lradc_driver = {
> +	.driver = {
> +		.name = "mxs-lradc",
> +		.of_match_table = mxs_lradc_dt_ids,
> +	},
> +	.probe = mxs_lradc_probe,
> +	.remove = mxs_lradc_remove,
> +};
> +
> +module_platform_driver(mxs_lradc_driver);
> +
> +MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:mxs-lradc");
> diff --git a/include/linux/mfd/mxs-lradc.h 
> b/include/linux/mfd/mxs-lradc.h
> new file mode 100644
> index 0000000..c062969
> --- /dev/null
> +++ b/include/linux/mfd/mxs-lradc.h
> @@ -0,0 +1,210 @@
> +/*
> + * Freescale MXS LRADC driver
> + *
> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
> + * Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or 
> modify
> + * it under the terms of the GNU General Public License as published 
> by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __MXS_LRADC_H
> +#define __MXS_LRADC_H
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/stmp_device.h>
> +#include <linux/sysfs.h>
> +
> +#define DRIVER_NAME_ADC "mxs-lradc-adc"
> +#define DRIVER_NAME_TS "mxs-lradc-ts"
> +
> +#define LRADC_MAX_DELAY_CHANS	4
> +#define LRADC_MAX_MAPPED_CHANS	8
> +#define LRADC_MAX_TOTAL_CHANS	16
> +
> +#define LRADC_DELAY_TIMER_HZ	2000
> +
> +#define LRADC_CTRL0				0x00
> +# define LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE	BIT(23)
> +# define LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE	BIT(22)
> +# define LRADC_CTRL0_MX28_YNNSW /* YM */	BIT(21)
> +# define LRADC_CTRL0_MX28_YPNSW /* YP */	BIT(20)
> +# define LRADC_CTRL0_MX28_YPPSW /* YP */	BIT(19)
> +# define LRADC_CTRL0_MX28_XNNSW /* XM */	BIT(18)
> +# define LRADC_CTRL0_MX28_XNPSW /* XM */	BIT(17)
> +# define LRADC_CTRL0_MX28_XPPSW /* XP */	BIT(16)
> +
> +# define LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE	BIT(20)
> +# define LRADC_CTRL0_MX23_YM			BIT(19)
> +# define LRADC_CTRL0_MX23_XM			BIT(18)
> +# define LRADC_CTRL0_MX23_YP			BIT(17)
> +# define LRADC_CTRL0_MX23_XP			BIT(16)
> +
> +# define LRADC_CTRL0_MX28_PLATE_MASK \
> +		(LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE | \
> +		LRADC_CTRL0_MX28_YNNSW | LRADC_CTRL0_MX28_YPNSW | \
> +		LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW | \
> +		LRADC_CTRL0_MX28_XNPSW | LRADC_CTRL0_MX28_XPPSW)
> +
> +# define LRADC_CTRL0_MX23_PLATE_MASK \
> +		(LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE | \
> +		LRADC_CTRL0_MX23_YM | LRADC_CTRL0_MX23_XM | \
> +		LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XP)
> +
> +#define LRADC_CTRL1				0x10
> +#define LRADC_CTRL1_TOUCH_DETECT_IRQ_EN		BIT(24)
> +#define LRADC_CTRL1_LRADC_IRQ_EN(n)		(1 << ((n) + 16))
> +#define LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK	(0x1fff << 16)
> +#define LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK	(0x01ff << 16)
> +#define LRADC_CTRL1_LRADC_IRQ_EN_OFFSET		16
> +#define LRADC_CTRL1_TOUCH_DETECT_IRQ		BIT(8)
> +#define LRADC_CTRL1_LRADC_IRQ(n)		(1 << (n))
> +#define LRADC_CTRL1_MX28_LRADC_IRQ_MASK		0x1fff
> +#define LRADC_CTRL1_MX23_LRADC_IRQ_MASK		0x01ff
> +#define LRADC_CTRL1_LRADC_IRQ_OFFSET		0
> +
> +#define LRADC_CTRL2				0x20
> +#define LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET	24
> +#define LRADC_CTRL2_TEMPSENSE_PWD		BIT(15)
> +
> +#define LRADC_STATUS				0x40
> +#define LRADC_STATUS_TOUCH_DETECT_RAW		BIT(0)
> +
> +#define LRADC_CH(n)				(0x50 + (0x10 * (n)))
> +#define LRADC_CH_ACCUMULATE			BIT(29)
> +#define LRADC_CH_NUM_SAMPLES_MASK		(0x1f << 24)
> +#define LRADC_CH_NUM_SAMPLES_OFFSET		24
> +#define LRADC_CH_NUM_SAMPLES(x) \
> +				((x) << LRADC_CH_NUM_SAMPLES_OFFSET)
> +#define LRADC_CH_VALUE_MASK			0x3ffff
> +#define LRADC_CH_VALUE_OFFSET			0
> +
> +#define LRADC_DELAY(n)				(0xd0 + (0x10 * (n)))
> +#define LRADC_DELAY_TRIGGER_LRADCS_MASK		(0xffUL << 24)
> +#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET	24
> +#define LRADC_DELAY_TRIGGER(x) \
> +				(((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \
> +				LRADC_DELAY_TRIGGER_LRADCS_MASK)
> +#define LRADC_DELAY_KICK			BIT(20)
> +#define LRADC_DELAY_TRIGGER_DELAYS_MASK		(0xf << 16)
> +#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET	16
> +#define LRADC_DELAY_TRIGGER_DELAYS(x) \
> +				(((x) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) & \
> +				LRADC_DELAY_TRIGGER_DELAYS_MASK)
> +#define LRADC_DELAY_LOOP_COUNT_MASK		(0x1f << 11)
> +#define LRADC_DELAY_LOOP_COUNT_OFFSET		11
> +#define LRADC_DELAY_LOOP(x) \
> +				(((x) << LRADC_DELAY_LOOP_COUNT_OFFSET) & \
> +				LRADC_DELAY_LOOP_COUNT_MASK)
> +#define LRADC_DELAY_DELAY_MASK			0x7ff
> +#define LRADC_DELAY_DELAY_OFFSET		0
> +#define LRADC_DELAY_DELAY(x) \
> +				(((x) << LRADC_DELAY_DELAY_OFFSET) & \
> +				LRADC_DELAY_DELAY_MASK)
> +
> +#define LRADC_CTRL4				0x140
> +#define LRADC_CTRL4_LRADCSELECT_MASK(n)		(0xf << ((n) * 4))
> +#define LRADC_CTRL4_LRADCSELECT_OFFSET(n)	((n) * 4)
> +#define LRADC_CTRL4_LRADCSELECT(n, x) \
> +				(((x) << LRADC_CTRL4_LRADCSELECT_OFFSET(n)) & \
> +				LRADC_CTRL4_LRADCSELECT_MASK(n))
> +
> +#define LRADC_RESOLUTION			12
> +#define LRADC_SINGLE_SAMPLE_MASK		((1 << LRADC_RESOLUTION) - 1)
> +
> +enum mxs_lradc_id {
> +	IMX23_LRADC,
> +	IMX28_LRADC,
> +};
> +
> +enum mxs_lradc_ts_wires {
> +	MXS_LRADC_TOUCHSCREEN_NONE = 0,
> +	MXS_LRADC_TOUCHSCREEN_4WIRE,
> +	MXS_LRADC_TOUCHSCREEN_5WIRE,
> +};
> +
> +struct mxs_lradc {
> +	enum mxs_lradc_id	soc;
> +
> +	void __iomem		*base;
> +	struct clk		*clk;
> +
> +	int			irq[13];
> +	const char * const	*irq_name;
> +	int			irq_count;
> +
> +#define BUFFER_VCHANS_LIMITED		0x3f
> +#define BUFFER_VCHANS_ALL		0xff
> +	u8			buffer_vchans;
> +
> +	/*
> +	 * Certain LRADC channels are shared between touchscreen
> +	 * and/or touch-buttons and generic LRADC block. Therefore when 
> using
> +	 * either of these, these channels are not available for the 
> regular
> +	 * sampling. The shared channels are as follows:
> +	 *
> +	 * CH0 -- Touch button #0
> +	 * CH1 -- Touch button #1
> +	 * CH2 -- Touch screen XPUL
> +	 * CH3 -- Touch screen YPLL
> +	 * CH4 -- Touch screen XNUL
> +	 * CH5 -- Touch screen YNLR
> +	 * CH6 -- Touch screen WIPER (5-wire only)
> +	 *
> +	 * The bit fields below represents which parts of the LRADC block 
> are
> +	 * switched into special mode of operation. These channels can not
> +	 * be sampled as regular LRADC channels. The driver will refuse any
> +	 * attempt to sample these channels.
> +	 */
> +#define CHAN_MASK_TOUCHBUTTON		(BIT(1) | BIT(0))
> +#define CHAN_MASK_TOUCHSCREEN_4WIRE	(0xf << 2)
> +#define CHAN_MASK_TOUCHSCREEN_5WIRE	(0x1f << 2)
> +	enum mxs_lradc_ts_wires	use_touchscreen;
> +	bool			use_touchbutton;
> +};
> +
> +static inline void mxs_lradc_reg_set(struct mxs_lradc *lradc, u32
> val, u32 reg)
> +{
> +	writel(val, lradc->base + reg + STMP_OFFSET_REG_SET);
> +}
> +
> +static inline void mxs_lradc_reg_clear(struct mxs_lradc *lradc, u32 
> val,
> +				       u32 reg)
> +{
> +	writel(val, lradc->base + reg + STMP_OFFSET_REG_CLR);
> +}
> +
> +static inline void mxs_lradc_reg_wrt(struct mxs_lradc *lradc, u32
> val, u32 reg)
> +{
> +	writel(val, lradc->base + reg);
> +}
> +
> +static inline u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL1_MX23_LRADC_IRQ_MASK;
> +	return LRADC_CTRL1_MX28_LRADC_IRQ_MASK;
> +}
> +
> +#endif /* __MXS_LRADC_H */

-- 
If you want to support my work:
see http://friends.ccbib.org/harald/supporting/
or donate via peercoin to P98LRdhit3gZbHDBe7ta5jtXrMJUms4p7w
or bitcoin 1FUtd8T9jRN1rFz63vZz7s2fDtB6d6A7aS

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
@ 2016-04-29 23:36     ` Dmitry Torokhov
  0 siblings, 0 replies; 40+ messages in thread
From: Dmitry Torokhov @ 2016-04-29 23:36 UTC (permalink / raw)
  To: Ksenija Stanojevic
  Cc: linux-kernel, lee.jones, linux-input, jic23, knaack.h, lars,
	pmeerw, marex, linux-iio, harald

Hi Ksenija,

On Fri, Apr 29, 2016 at 01:49:11PM +0200, Ksenija Stanojevic wrote:
> Add mxs-lradc touchscreen driver.
> 
> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
> ---
>  drivers/input/touchscreen/Kconfig        |  14 +-
>  drivers/input/touchscreen/Makefile       |   1 +
>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
>  3 files changed, 742 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 8ecdc38..d614d248 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
>  	depends on SH_HP6XX && SH_ADC
>  	help
>  	  Say Y here if you have a HP Jornada 620/660/680/690 and want to
> -          support the built-in touchscreen.
> +	  support the built-in touchscreen.
>  
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called hp680_ts_input.
> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
>  	  This enables support for the Philips UCB1400 touchscreen interface.
>  	  The UCB1400 is an AC97 audio codec.  The touchscreen interface
>  	  will be initialized only after the ALSA subsystem has been
> -	  brought up and the UCB1400 detected.  You therefore have to
> +	  brought up and the UCB1400 detected.	You therefore have to

Why do we have the tab in the middle of the text?

>  	  configure ALSA support as well (either built-in or modular,
>  	  independently of whether this driver is itself built-in or
>  	  modular) for this driver to work.
> @@ -842,6 +842,16 @@ config TOUCHSCREEN_MX25
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called fsl-imx25-tcq.
>  
> +config TOUCHSCREEN_MXS_LRADC
> +	tristate "Freescale i.MX23/i.MX28 LRADC touchscreen"
> +	depends on MFD_MXS_LRADC
> +	help
> +	  Say Y here if you have a touchscreen connected to the low-resolution
> +	  analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor.
> +
> +	  To compile this driver as a module, choose M here: the module will be
> +	  called mxs-lradc-ts.
> +
>  config TOUCHSCREEN_MC13783
>  	tristate "Freescale MC13783 touchscreen input driver"
>  	depends on MFD_MC13XXX
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index f42975e..513a6ff 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MMS114)	+= mms114.o
>  obj-$(CONFIG_TOUCHSCREEN_MTOUCH)	+= mtouch.o
>  obj-$(CONFIG_TOUCHSCREEN_MK712)		+= mk712.o
> +obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC)	+= mxs-lradc-ts.o
>  obj-$(CONFIG_TOUCHSCREEN_HP600)		+= hp680_ts_input.o
>  obj-$(CONFIG_TOUCHSCREEN_HP7XX)		+= jornada720_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO)	+= ipaq-micro-ts.o
> diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c
> new file mode 100644
> index 0000000..27abb8e
> --- /dev/null
> +++ b/drivers/input/touchscreen/mxs-lradc-ts.c
> @@ -0,0 +1,729 @@
> +/*
> + * Freescale MXS LRADC driver
> + *
> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
> + * Marek Vasut <marex@denx.de>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/mfd/mxs-lradc.h>
> +#include <linux/platform_device.h>
> +
> +/*
> + * Touchscreen handling
> + */
> +enum mxs_lradc_ts_plate {
> +	LRADC_TOUCH = 0,
> +	LRADC_SAMPLE_X,
> +	LRADC_SAMPLE_Y,
> +	LRADC_SAMPLE_PRESSURE,
> +	LRADC_SAMPLE_VALID,
> +};
> +
> +struct mxs_lradc_ts {
> +	struct mxs_lradc	*lradc;
> +	struct device		*dev;
> +	/*
> +	 * When the touchscreen is enabled, we give it two private virtual
> +	 * channels: #6 and #7. This means that only 6 virtual channels (instead
> +	 * of 8) will be available for buffered capture.
> +	 */
> +#define TOUCHSCREEN_VCHANNEL1		7
> +#define TOUCHSCREEN_VCHANNEL2		6
> +
> +	struct input_dev	*ts_input;
> +
> +	enum mxs_lradc_ts_plate	cur_plate; /* state machine */
> +	bool			ts_valid;
> +	unsigned		ts_x_pos;
> +	unsigned		ts_y_pos;
> +	unsigned		ts_pressure;
> +
> +	/* handle touchscreen's physical behaviour */
> +	/* samples per coordinate */
> +	unsigned		over_sample_cnt;
> +	/* time clocks between samples */
> +	unsigned		over_sample_delay;
> +	/* time in clocks to wait after the plates where switched */
> +	unsigned		settling_delay;
> +};
> +
> +static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL0_MX23_PLATE_MASK;
> +	return LRADC_CTRL0_MX28_PLATE_MASK;
> +}
> +
> +static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
> +	return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE;
> +}
> +
> +static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM;
> +	return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW;
> +}
> +
> +static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM;
> +	return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW;
> +}
> +
> +static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM;
> +	return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW;
> +}
> +
> +static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
> +{
> +	return !!(readl(lradc->base + LRADC_STATUS) &
> +					LRADC_STATUS_TOUCH_DETECT_RAW);
> +}
> +
> +static void mxs_lradc_map_ts_channel(struct mxs_lradc *lradc, unsigned vch,
> +				     unsigned ch)
> +{
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
> +			    LRADC_CTRL4);
> +	mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4);
> +}
> +
> +static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned ch)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	/*
> +	 * prepare for oversampling conversion
> +	 *
> +	 * from the datasheet:
> +	 * "The ACCUMULATE bit in the appropriate channel register
> +	 * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
> +	 * otherwise, the IRQs will not fire."
> +	 */
> +	mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE |
> +			  LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1),
> +			  LRADC_CH(ch));
> +
> +	/* from the datasheet:
> +	 * "Software must clear this register in preparation for a
> +	 * multi-cycle accumulation.
> +	 */
> +	mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch));
> +
> +	/*
> +	 * prepare the delay/loop unit according to the oversampling count
> +	 *
> +	 * from the datasheet:
> +	 * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
> +	 * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
> +	 * the LRADC will not trigger the delay group."
> +	 */
> +	mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
> +			  LRADC_DELAY_TRIGGER_DELAYS(0) |
> +			  LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
> +			  LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
> +			  LRADC_DELAY(3));
> +
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1);
> +
> +	/*
> +	 * after changing the touchscreen plates setting
> +	 * the signals need some initial time to settle. Start the
> +	 * SoC's delay unit and start the conversion later
> +	 * and automatically.
> +	 */
> +	mxs_lradc_reg_wrt(
> +		lradc,
> +		LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
> +		LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
> +		LRADC_DELAY_KICK |
> +		LRADC_DELAY_DELAY(ts->settling_delay),
> +		LRADC_DELAY(2));
> +}
> +
> +/*
> + * Pressure detection is special:
> + * We want to do both required measurements for the pressure detection in
> + * one turn. Use the hardware features to chain both conversions and let the
> + * hardware report one interrupt if both conversions are done
> + */
> +static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts, unsigned ch1,
> +					unsigned ch2)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +	u32 reg;
> +
> +	/*
> +	 * prepare for oversampling conversion
> +	 *
> +	 * from the datasheet:
> +	 * "The ACCUMULATE bit in the appropriate channel register
> +	 * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
> +	 * otherwise, the IRQs will not fire."
> +	 */
> +	reg = LRADC_CH_ACCUMULATE |
> +		LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1);
> +	mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
> +	mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
> +
> +	/* from the datasheet:
> +	 * "Software must clear this register in preparation for a
> +	 * multi-cycle accumulation.
> +	 */
> +	mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1));
> +	mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2));
> +
> +	/* prepare the delay/loop unit according to the oversampling count */
> +	mxs_lradc_reg_wrt(
> +		    lradc,
> +		    LRADC_DELAY_TRIGGER(1 << ch1) |
> +		    LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */
> +		    LRADC_DELAY_TRIGGER_DELAYS(0) |
> +		    LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
> +		    LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
> +		    LRADC_DELAY(3));
> +
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1);
> +
> +	/*
> +	 * after changing the touchscreen plates setting
> +	 * the signals need some initial time to settle. Start the
> +	 * SoC's delay unit and start the conversion later
> +	 * and automatically.
> +	 */
> +	mxs_lradc_reg_wrt(
> +		lradc,
> +		LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
> +		LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
> +		LRADC_DELAY_KICK |
> +		LRADC_DELAY_DELAY(ts->settling_delay), LRADC_DELAY(2));
> +}
> +
> +static unsigned mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts,
> +					      unsigned channel)
> +{
> +	u32 reg;
> +	unsigned num_samples, val;
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	reg = readl(lradc->base + LRADC_CH(channel));
> +	if (reg & LRADC_CH_ACCUMULATE)
> +		num_samples = ts->over_sample_cnt;
> +	else
> +		num_samples = 1;
> +
> +	val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
> +	return val / num_samples;
> +}
> +
> +static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts,
> +					   unsigned ch1, unsigned ch2)
> +{
> +	u32 reg, mask;
> +	unsigned pressure, m1, m2;
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
> +	reg = readl(lradc->base + LRADC_CTRL1) & mask;
> +
> +	while (reg != mask) {
> +		reg = readl(lradc->base + LRADC_CTRL1) & mask;
> +		dev_dbg(ts->dev, "One channel is still busy: %X\n", reg);
> +	}
> +
> +	m1 = mxs_lradc_ts_read_raw_channel(ts, ch1);
> +	m2 = mxs_lradc_ts_read_raw_channel(ts, ch2);
> +
> +	if (m2 == 0) {
> +		dev_warn(ts->dev, "Cannot calculate pressure\n");
> +		return 1 << (LRADC_RESOLUTION - 1);
> +	}
> +
> +	/* simply scale the value from 0 ... max ADC resolution */
> +	pressure = m1;
> +	pressure *= (1 << LRADC_RESOLUTION);
> +	pressure /= m2;
> +
> +	dev_dbg(ts->dev, "Pressure = %u\n", pressure);
> +	return pressure;
> +}
> +
> +#define TS_CH_XP 2
> +#define TS_CH_YP 3
> +#define TS_CH_XM 4
> +#define TS_CH_YM 5
> +
> +/*
> + * YP(open)--+-------------+
> + *	     |		   |--+
> + *	     |		   |  |
> + *    YM(-)--+-------------+  |
> + *	       +--------------+
> + *	       |	      |
> + *	   XP(weak+)	    XM(open)
> + *
> + * "weak+" means 200k Ohm VDDIO
> + * (-) means GND
> + */
> +static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	/*
> +	 * In order to detect a touch event the 'touch detect enable' bit
> +	 * enables:
> +	 *  - a weak pullup to the X+ connector
> +	 *  - a strong ground at the Y- connector
> +	 */
> +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
> +	mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc),
> +			  LRADC_CTRL0);
> +}
> +
> +/*
> + * YP(meas)--+-------------+
> + *	     |		   |--+
> + *	     |		   |  |
> + * YM(open)--+-------------+  |
> + *	       +--------------+
> + *	       |	      |
> + *	     XP(+)	    XM(-)
> + *
> + * (+) means here 1.85 V
> + * (-) means here GND
> + */
> +static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
> +	mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
> +
> +	ts->cur_plate = LRADC_SAMPLE_X;
> +	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
> +	mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
> +}
> +
> +/*
> + *   YP(+)--+-------------+
> + *	    |		  |--+
> + *	    |		  |  |
> + *   YM(-)--+-------------+  |
> + *	      +--------------+
> + *	      |		     |
> + *	   XP(open)	   XM(meas)
> + *
> + * (+) means here 1.85 V
> + * (-) means here GND
> + */
> +static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
> +	mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
> +
> +	ts->cur_plate = LRADC_SAMPLE_Y;
> +	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
> +	mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
> +}
> +
> +/*
> + *    YP(+)--+-------------+
> + *	     |		   |--+
> + *	     |		   |  |
> + * YM(meas)--+-------------+  |
> + *	       +--------------+
> + *	       |	      |
> + *	    XP(meas)	    XM(-)
> + *
> + * (+) means here 1.85 V
> + * (-) means here GND
> + */
> +static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
> +	mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
> +
> +	ts->cur_plate = LRADC_SAMPLE_PRESSURE;
> +	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
> +	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
> +	mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2,
> +				    TOUCHSCREEN_VCHANNEL1);
> +}
> +
> +static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts)
> +{
> +	mxs_lradc_setup_touch_detection(ts);
> +
> +	ts->cur_plate = LRADC_TOUCH;
> +	mxs_lradc_reg_clear(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
> +			    LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
> +	mxs_lradc_reg_set(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
> +			  LRADC_CTRL1);
> +}
> +
> +static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts)
> +{
> +	mxs_lradc_reg_clear(ts->lradc,
> +			    LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
> +			    LRADC_CTRL1);
> +	mxs_lradc_reg_set(ts->lradc,
> +			  LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1),
> +			  LRADC_CTRL1);
> +	/*
> +	 * start with the Y-pos, because it uses nearly the same plate
> +	 * settings like the touch detection
> +	 */
> +	mxs_lradc_prepare_y_pos(ts);
> +}
> +
> +static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts)
> +{
> +	input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos);
> +	input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos);
> +	input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure);
> +	input_report_key(ts->ts_input, BTN_TOUCH, 1);
> +	input_sync(ts->ts_input);
> +}
> +
> +static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mxs_lradc_setup_touch_detection(ts);
> +	ts->cur_plate = LRADC_SAMPLE_VALID;
> +	/*
> +	 * start a dummy conversion to burn time to settle the signals
> +	 * note: we are not interested in the conversion's value
> +	 */
> +	mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
> +	mxs_lradc_reg_clear(lradc,
> +			    LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
> +			    LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2),
> +			    LRADC_CTRL1);
> +	mxs_lradc_reg_wrt(
> +		    lradc,
> +		    LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
> +		    LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
> +		    LRADC_DELAY(2));
> +}
> +
> +/*
> + * in order to avoid false measurements, report only samples where
> + * the surface is still touched after the position measurement
> + */
> +static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	/* if it is still touched, report the sample */
> +	if (valid && mxs_lradc_check_touch_event(lradc)) {
> +		ts->ts_valid = true;
> +		mxs_lradc_report_ts_event(ts);
> +	}
> +
> +	/* if it is even still touched, continue with the next measurement */
> +	if (mxs_lradc_check_touch_event(lradc)) {
> +		mxs_lradc_prepare_y_pos(ts);
> +		return;
> +	}
> +
> +	if (ts->ts_valid) {
> +		/* signal the release */
> +		ts->ts_valid = false;
> +		input_report_key(ts->ts_input, BTN_TOUCH, 0);
> +		input_sync(ts->ts_input);
> +	}
> +
> +	/* if it is released, wait for the next touch via IRQ */
> +	ts->cur_plate = LRADC_TOUCH;
> +	mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
> +	mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
> +	mxs_lradc_reg_clear(lradc,
> +			    LRADC_CTRL1_TOUCH_DETECT_IRQ |
> +			    LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
> +			    LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1),
> +			    LRADC_CTRL1);
> +	mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
> +}
> +
> +/* touchscreen's state machine */
> +static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	switch (ts->cur_plate) {
> +	case LRADC_TOUCH:
> +		if (mxs_lradc_check_touch_event(lradc))
> +			mxs_lradc_start_touch_event(ts);
> +		mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
> +				    LRADC_CTRL1);
> +		return;
> +
> +	case LRADC_SAMPLE_Y:
> +		ts->ts_y_pos =
> +		    mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
> +		mxs_lradc_prepare_x_pos(ts);
> +		return;
> +
> +	case LRADC_SAMPLE_X:
> +		ts->ts_x_pos =
> +		    mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
> +		mxs_lradc_prepare_pressure(ts);
> +		return;
> +
> +	case LRADC_SAMPLE_PRESSURE:
> +		ts->ts_pressure =
> +		    mxs_lradc_read_ts_pressure(ts,
> +					       TOUCHSCREEN_VCHANNEL2,
> +					       TOUCHSCREEN_VCHANNEL1);
> +		mxs_lradc_complete_touch_event(ts);
> +		return;
> +
> +	case LRADC_SAMPLE_VALID:
> +		mxs_lradc_finish_touch_event(ts, 1);
> +		break;
> +	}
> +}
> +
> +/*
> + * IRQ Handling
> + */
> +static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data)
> +{
> +	struct mxs_lradc_ts *ts = data;
> +	struct mxs_lradc *lradc = ts->lradc;
> +	unsigned long reg = readl(lradc->base + LRADC_CTRL1);
> +	u32 clr_irq = mxs_lradc_irq_mask(lradc);
> +	const u32 ts_irq_mask =
> +		LRADC_CTRL1_TOUCH_DETECT_IRQ |
> +		LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
> +		LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
> +
> +	if (!(reg & mxs_lradc_irq_mask(lradc)))
> +		return IRQ_NONE;
> +
> +	if (lradc->use_touchscreen && (reg & ts_irq_mask)) {

Why do we have this flag instead of not creating the platform device by
MFD parent when touchscreen is not in use?

> +		mxs_lradc_handle_touch(ts);
> +
> +		/* Make sure we don't clear the next conversion's interrupt. */
> +		clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
> +				LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
> +		mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int mxs_lradc_ts_open(struct input_dev *dev)
> +{
> +	struct mxs_lradc_ts *ts = input_get_drvdata(dev);
> +
> +	/* Enable the touch-detect circuitry. */
> +	mxs_lradc_enable_touch_detection(ts);
> +
> +	return 0;
> +}
> +
> +static void mxs_lradc_disable_ts(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	/* stop all interrupts from firing */
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
> +		LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
> +		LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
> +
> +	/* Power-down touchscreen touch-detect circuitry. */
> +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
> +}
> +
> +static void mxs_lradc_ts_close(struct input_dev *dev)
> +{
> +	struct mxs_lradc_ts *ts = input_get_drvdata(dev);
> +
> +	mxs_lradc_disable_ts(ts);
> +}
> +
> +static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	/* Configure the touchscreen type */
> +	if (lradc->soc == IMX28_LRADC) {
> +		mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
> +				    LRADC_CTRL0);
> +
> +		if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
> +			mxs_lradc_reg_set(lradc,
> +					  LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
> +					  LRADC_CTRL0);
> +	}
> +}
> +
> +static void mxs_lradc_ts_hw_stop(struct mxs_lradc_ts *ts)
> +{
> +	int i;
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mxs_lradc_reg_clear(lradc,
> +			lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
> +			LRADC_CTRL1);
> +
> +	for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++)
> +		mxs_lradc_reg_wrt(ts->lradc, 0, LRADC_DELAY(i));
> +}
> +
> +static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts)
> +{
> +	struct input_dev *input = ts->ts_input;
> +	struct device *dev = ts->dev;
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	if (!lradc->use_touchscreen)
> +		return 0;
> +
> +	input->name = DRIVER_NAME_TS;
> +	input->id.bustype = BUS_HOST;
> +	input->dev.parent = dev;

Not needed for devm-managed input devices.

> +	input->open = mxs_lradc_ts_open;
> +	input->close = mxs_lradc_ts_close;
> +
> +	__set_bit(EV_ABS, input->evbit);
> +	__set_bit(EV_KEY, input->evbit);
> +	__set_bit(BTN_TOUCH, input->keybit);
> +	__set_bit(INPUT_PROP_DIRECT, input->propbit);
> +	input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
> +	input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
> +	input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
> +			     0, 0);
> +
> +	ts->ts_input = input;
> +	input_set_drvdata(input, ts);
> +
> +	return input_register_device(input);
> +}
> +
> +static int mxs_lradc_ts_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *node = dev->parent->of_node;
> +	struct mxs_lradc *lradc = dev_get_platdata(dev);
> +	struct mxs_lradc_ts *ts;
> +	struct input_dev *input;
> +	int touch_ret, ret, i;
> +	u32 ts_wires = 0, adapt;
> +
> +	ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
> +	input = devm_input_allocate_device(dev);

I'd expect input allocation be done in mxs_lradc_ts_register().

> +	if (!ts || !input)
> +		return -ENOMEM;
> +
> +	ts->lradc = lradc;
> +	ts->dev = dev;
> +	ts->ts_input = input;
> +	platform_set_drvdata(pdev, ts);
> +	input_set_drvdata(input, ts);
> +
> +	touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
> +					 &ts_wires);
> +
> +	if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) {
> +		ts->over_sample_cnt = 4;
> +	} else {
> +		if (adapt < 1 || adapt > 32) {
> +			dev_err(ts->dev, "Invalid sample count (%u)\n",
> +				adapt);
> +			touch_ret = -EINVAL;
> +		} else {
> +			ts->over_sample_cnt = adapt;
> +		}
> +	}
> +
> +	if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) {
> +		ts->over_sample_delay = 2;
> +	} else {
> +		if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) {
> +			dev_err(ts->dev, "Invalid sample delay (%u)\n",
> +				adapt);
> +			touch_ret = -EINVAL;
> +		} else {
> +			ts->over_sample_delay = adapt;
> +		}
> +	}
> +
> +	if (of_property_read_u32(node, "fsl,settling", &adapt)) {
> +		ts->settling_delay = 10;
> +	} else {
> +		if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) {
> +			dev_err(ts->dev, "Invalid settling delay (%u)\n",
> +				adapt);
> +			touch_ret = -EINVAL;

Why are we not aborting here?

> +		} else {
> +			ts->settling_delay = adapt;
> +		}
> +	}
> +
> +	mxs_lradc_ts_hw_init(ts);
> +	for (i = 0; i < lradc->irq_count; i++) {
> +		ret = devm_request_irq(dev, lradc->irq[i],
> +				       mxs_lradc_ts_handle_irq,

Hmm, if you have several interrupts handled by the same interrupt
handler you'd need some locking there.

> +				       IRQF_SHARED, lradc->irq_name[i], ts);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (!touch_ret) {
> +		ret = mxs_lradc_ts_register(ts);
> +		if (!ret)
> +			goto err_ts_register;
> +	}
> +
> +	return 0;
> +
> +err_ts_register:
> +	mxs_lradc_ts_hw_stop(ts);
> +	return ret;

Would prefer calling this variable "error".

> +}
> +
> +static int mxs_lradc_ts_remove(struct platform_device *pdev)
> +{
> +	struct mxs_lradc_ts *ts = platform_get_drvdata(pdev);
> +
> +	mxs_lradc_ts_hw_stop(ts);

I wonder if mxs_lradc_ts_hw_stop() can be combined with
mxs_lradc_disable_ts() which is called automatically if input device is
opened? This way we'd be able to get rid of mxs_lradc_ts_remove(). 

> +
> +	return 0;
> +}
> +
> +static struct platform_driver mxs_lradc_ts_driver = {
> +	.driver	= {
> +		.name = DRIVER_NAME_TS,
> +	},
> +	.probe	= mxs_lradc_ts_probe,
> +	.remove	= mxs_lradc_ts_remove,
> +};
> +module_platform_driver(mxs_lradc_ts_driver);
> +
> +MODULE_LICENSE("GPL v2");

"GPL" since you are not limiting to v2 only.

Thanks.

-- 
Dmitry

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
@ 2016-04-29 23:36     ` Dmitry Torokhov
  0 siblings, 0 replies; 40+ messages in thread
From: Dmitry Torokhov @ 2016-04-29 23:36 UTC (permalink / raw)
  To: Ksenija Stanojevic
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	linux-input-u79uwXL29TY76Z2rM5mHXA, jic23-DgEjT+Ai2ygdnm+yROfE0A,
	knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, marex-ynQEQJNshbs,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, harald-95f8Dae0BrPYtjvyW6yDsg

Hi Ksenija,

On Fri, Apr 29, 2016 at 01:49:11PM +0200, Ksenija Stanojevic wrote:
> Add mxs-lradc touchscreen driver.
> 
> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
>  drivers/input/touchscreen/Kconfig        |  14 +-
>  drivers/input/touchscreen/Makefile       |   1 +
>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
>  3 files changed, 742 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 8ecdc38..d614d248 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
>  	depends on SH_HP6XX && SH_ADC
>  	help
>  	  Say Y here if you have a HP Jornada 620/660/680/690 and want to
> -          support the built-in touchscreen.
> +	  support the built-in touchscreen.
>  
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called hp680_ts_input.
> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
>  	  This enables support for the Philips UCB1400 touchscreen interface.
>  	  The UCB1400 is an AC97 audio codec.  The touchscreen interface
>  	  will be initialized only after the ALSA subsystem has been
> -	  brought up and the UCB1400 detected.  You therefore have to
> +	  brought up and the UCB1400 detected.	You therefore have to

Why do we have the tab in the middle of the text?

>  	  configure ALSA support as well (either built-in or modular,
>  	  independently of whether this driver is itself built-in or
>  	  modular) for this driver to work.
> @@ -842,6 +842,16 @@ config TOUCHSCREEN_MX25
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called fsl-imx25-tcq.
>  
> +config TOUCHSCREEN_MXS_LRADC
> +	tristate "Freescale i.MX23/i.MX28 LRADC touchscreen"
> +	depends on MFD_MXS_LRADC
> +	help
> +	  Say Y here if you have a touchscreen connected to the low-resolution
> +	  analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor.
> +
> +	  To compile this driver as a module, choose M here: the module will be
> +	  called mxs-lradc-ts.
> +
>  config TOUCHSCREEN_MC13783
>  	tristate "Freescale MC13783 touchscreen input driver"
>  	depends on MFD_MC13XXX
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index f42975e..513a6ff 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MMS114)	+= mms114.o
>  obj-$(CONFIG_TOUCHSCREEN_MTOUCH)	+= mtouch.o
>  obj-$(CONFIG_TOUCHSCREEN_MK712)		+= mk712.o
> +obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC)	+= mxs-lradc-ts.o
>  obj-$(CONFIG_TOUCHSCREEN_HP600)		+= hp680_ts_input.o
>  obj-$(CONFIG_TOUCHSCREEN_HP7XX)		+= jornada720_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO)	+= ipaq-micro-ts.o
> diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c
> new file mode 100644
> index 0000000..27abb8e
> --- /dev/null
> +++ b/drivers/input/touchscreen/mxs-lradc-ts.c
> @@ -0,0 +1,729 @@
> +/*
> + * Freescale MXS LRADC driver
> + *
> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
> + * Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/mfd/mxs-lradc.h>
> +#include <linux/platform_device.h>
> +
> +/*
> + * Touchscreen handling
> + */
> +enum mxs_lradc_ts_plate {
> +	LRADC_TOUCH = 0,
> +	LRADC_SAMPLE_X,
> +	LRADC_SAMPLE_Y,
> +	LRADC_SAMPLE_PRESSURE,
> +	LRADC_SAMPLE_VALID,
> +};
> +
> +struct mxs_lradc_ts {
> +	struct mxs_lradc	*lradc;
> +	struct device		*dev;
> +	/*
> +	 * When the touchscreen is enabled, we give it two private virtual
> +	 * channels: #6 and #7. This means that only 6 virtual channels (instead
> +	 * of 8) will be available for buffered capture.
> +	 */
> +#define TOUCHSCREEN_VCHANNEL1		7
> +#define TOUCHSCREEN_VCHANNEL2		6
> +
> +	struct input_dev	*ts_input;
> +
> +	enum mxs_lradc_ts_plate	cur_plate; /* state machine */
> +	bool			ts_valid;
> +	unsigned		ts_x_pos;
> +	unsigned		ts_y_pos;
> +	unsigned		ts_pressure;
> +
> +	/* handle touchscreen's physical behaviour */
> +	/* samples per coordinate */
> +	unsigned		over_sample_cnt;
> +	/* time clocks between samples */
> +	unsigned		over_sample_delay;
> +	/* time in clocks to wait after the plates where switched */
> +	unsigned		settling_delay;
> +};
> +
> +static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL0_MX23_PLATE_MASK;
> +	return LRADC_CTRL0_MX28_PLATE_MASK;
> +}
> +
> +static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
> +	return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE;
> +}
> +
> +static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM;
> +	return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW;
> +}
> +
> +static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM;
> +	return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW;
> +}
> +
> +static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM;
> +	return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW;
> +}
> +
> +static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
> +{
> +	return !!(readl(lradc->base + LRADC_STATUS) &
> +					LRADC_STATUS_TOUCH_DETECT_RAW);
> +}
> +
> +static void mxs_lradc_map_ts_channel(struct mxs_lradc *lradc, unsigned vch,
> +				     unsigned ch)
> +{
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
> +			    LRADC_CTRL4);
> +	mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4);
> +}
> +
> +static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned ch)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	/*
> +	 * prepare for oversampling conversion
> +	 *
> +	 * from the datasheet:
> +	 * "The ACCUMULATE bit in the appropriate channel register
> +	 * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
> +	 * otherwise, the IRQs will not fire."
> +	 */
> +	mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE |
> +			  LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1),
> +			  LRADC_CH(ch));
> +
> +	/* from the datasheet:
> +	 * "Software must clear this register in preparation for a
> +	 * multi-cycle accumulation.
> +	 */
> +	mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch));
> +
> +	/*
> +	 * prepare the delay/loop unit according to the oversampling count
> +	 *
> +	 * from the datasheet:
> +	 * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
> +	 * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
> +	 * the LRADC will not trigger the delay group."
> +	 */
> +	mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
> +			  LRADC_DELAY_TRIGGER_DELAYS(0) |
> +			  LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
> +			  LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
> +			  LRADC_DELAY(3));
> +
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1);
> +
> +	/*
> +	 * after changing the touchscreen plates setting
> +	 * the signals need some initial time to settle. Start the
> +	 * SoC's delay unit and start the conversion later
> +	 * and automatically.
> +	 */
> +	mxs_lradc_reg_wrt(
> +		lradc,
> +		LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
> +		LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
> +		LRADC_DELAY_KICK |
> +		LRADC_DELAY_DELAY(ts->settling_delay),
> +		LRADC_DELAY(2));
> +}
> +
> +/*
> + * Pressure detection is special:
> + * We want to do both required measurements for the pressure detection in
> + * one turn. Use the hardware features to chain both conversions and let the
> + * hardware report one interrupt if both conversions are done
> + */
> +static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts, unsigned ch1,
> +					unsigned ch2)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +	u32 reg;
> +
> +	/*
> +	 * prepare for oversampling conversion
> +	 *
> +	 * from the datasheet:
> +	 * "The ACCUMULATE bit in the appropriate channel register
> +	 * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
> +	 * otherwise, the IRQs will not fire."
> +	 */
> +	reg = LRADC_CH_ACCUMULATE |
> +		LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1);
> +	mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
> +	mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
> +
> +	/* from the datasheet:
> +	 * "Software must clear this register in preparation for a
> +	 * multi-cycle accumulation.
> +	 */
> +	mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1));
> +	mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2));
> +
> +	/* prepare the delay/loop unit according to the oversampling count */
> +	mxs_lradc_reg_wrt(
> +		    lradc,
> +		    LRADC_DELAY_TRIGGER(1 << ch1) |
> +		    LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */
> +		    LRADC_DELAY_TRIGGER_DELAYS(0) |
> +		    LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
> +		    LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
> +		    LRADC_DELAY(3));
> +
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1);
> +
> +	/*
> +	 * after changing the touchscreen plates setting
> +	 * the signals need some initial time to settle. Start the
> +	 * SoC's delay unit and start the conversion later
> +	 * and automatically.
> +	 */
> +	mxs_lradc_reg_wrt(
> +		lradc,
> +		LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
> +		LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
> +		LRADC_DELAY_KICK |
> +		LRADC_DELAY_DELAY(ts->settling_delay), LRADC_DELAY(2));
> +}
> +
> +static unsigned mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts,
> +					      unsigned channel)
> +{
> +	u32 reg;
> +	unsigned num_samples, val;
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	reg = readl(lradc->base + LRADC_CH(channel));
> +	if (reg & LRADC_CH_ACCUMULATE)
> +		num_samples = ts->over_sample_cnt;
> +	else
> +		num_samples = 1;
> +
> +	val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
> +	return val / num_samples;
> +}
> +
> +static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts,
> +					   unsigned ch1, unsigned ch2)
> +{
> +	u32 reg, mask;
> +	unsigned pressure, m1, m2;
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
> +	reg = readl(lradc->base + LRADC_CTRL1) & mask;
> +
> +	while (reg != mask) {
> +		reg = readl(lradc->base + LRADC_CTRL1) & mask;
> +		dev_dbg(ts->dev, "One channel is still busy: %X\n", reg);
> +	}
> +
> +	m1 = mxs_lradc_ts_read_raw_channel(ts, ch1);
> +	m2 = mxs_lradc_ts_read_raw_channel(ts, ch2);
> +
> +	if (m2 == 0) {
> +		dev_warn(ts->dev, "Cannot calculate pressure\n");
> +		return 1 << (LRADC_RESOLUTION - 1);
> +	}
> +
> +	/* simply scale the value from 0 ... max ADC resolution */
> +	pressure = m1;
> +	pressure *= (1 << LRADC_RESOLUTION);
> +	pressure /= m2;
> +
> +	dev_dbg(ts->dev, "Pressure = %u\n", pressure);
> +	return pressure;
> +}
> +
> +#define TS_CH_XP 2
> +#define TS_CH_YP 3
> +#define TS_CH_XM 4
> +#define TS_CH_YM 5
> +
> +/*
> + * YP(open)--+-------------+
> + *	     |		   |--+
> + *	     |		   |  |
> + *    YM(-)--+-------------+  |
> + *	       +--------------+
> + *	       |	      |
> + *	   XP(weak+)	    XM(open)
> + *
> + * "weak+" means 200k Ohm VDDIO
> + * (-) means GND
> + */
> +static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	/*
> +	 * In order to detect a touch event the 'touch detect enable' bit
> +	 * enables:
> +	 *  - a weak pullup to the X+ connector
> +	 *  - a strong ground at the Y- connector
> +	 */
> +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
> +	mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc),
> +			  LRADC_CTRL0);
> +}
> +
> +/*
> + * YP(meas)--+-------------+
> + *	     |		   |--+
> + *	     |		   |  |
> + * YM(open)--+-------------+  |
> + *	       +--------------+
> + *	       |	      |
> + *	     XP(+)	    XM(-)
> + *
> + * (+) means here 1.85 V
> + * (-) means here GND
> + */
> +static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
> +	mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
> +
> +	ts->cur_plate = LRADC_SAMPLE_X;
> +	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
> +	mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
> +}
> +
> +/*
> + *   YP(+)--+-------------+
> + *	    |		  |--+
> + *	    |		  |  |
> + *   YM(-)--+-------------+  |
> + *	      +--------------+
> + *	      |		     |
> + *	   XP(open)	   XM(meas)
> + *
> + * (+) means here 1.85 V
> + * (-) means here GND
> + */
> +static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
> +	mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
> +
> +	ts->cur_plate = LRADC_SAMPLE_Y;
> +	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
> +	mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
> +}
> +
> +/*
> + *    YP(+)--+-------------+
> + *	     |		   |--+
> + *	     |		   |  |
> + * YM(meas)--+-------------+  |
> + *	       +--------------+
> + *	       |	      |
> + *	    XP(meas)	    XM(-)
> + *
> + * (+) means here 1.85 V
> + * (-) means here GND
> + */
> +static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
> +	mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
> +
> +	ts->cur_plate = LRADC_SAMPLE_PRESSURE;
> +	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
> +	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
> +	mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2,
> +				    TOUCHSCREEN_VCHANNEL1);
> +}
> +
> +static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts)
> +{
> +	mxs_lradc_setup_touch_detection(ts);
> +
> +	ts->cur_plate = LRADC_TOUCH;
> +	mxs_lradc_reg_clear(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
> +			    LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
> +	mxs_lradc_reg_set(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
> +			  LRADC_CTRL1);
> +}
> +
> +static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts)
> +{
> +	mxs_lradc_reg_clear(ts->lradc,
> +			    LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
> +			    LRADC_CTRL1);
> +	mxs_lradc_reg_set(ts->lradc,
> +			  LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1),
> +			  LRADC_CTRL1);
> +	/*
> +	 * start with the Y-pos, because it uses nearly the same plate
> +	 * settings like the touch detection
> +	 */
> +	mxs_lradc_prepare_y_pos(ts);
> +}
> +
> +static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts)
> +{
> +	input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos);
> +	input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos);
> +	input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure);
> +	input_report_key(ts->ts_input, BTN_TOUCH, 1);
> +	input_sync(ts->ts_input);
> +}
> +
> +static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mxs_lradc_setup_touch_detection(ts);
> +	ts->cur_plate = LRADC_SAMPLE_VALID;
> +	/*
> +	 * start a dummy conversion to burn time to settle the signals
> +	 * note: we are not interested in the conversion's value
> +	 */
> +	mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
> +	mxs_lradc_reg_clear(lradc,
> +			    LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
> +			    LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2),
> +			    LRADC_CTRL1);
> +	mxs_lradc_reg_wrt(
> +		    lradc,
> +		    LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
> +		    LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
> +		    LRADC_DELAY(2));
> +}
> +
> +/*
> + * in order to avoid false measurements, report only samples where
> + * the surface is still touched after the position measurement
> + */
> +static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	/* if it is still touched, report the sample */
> +	if (valid && mxs_lradc_check_touch_event(lradc)) {
> +		ts->ts_valid = true;
> +		mxs_lradc_report_ts_event(ts);
> +	}
> +
> +	/* if it is even still touched, continue with the next measurement */
> +	if (mxs_lradc_check_touch_event(lradc)) {
> +		mxs_lradc_prepare_y_pos(ts);
> +		return;
> +	}
> +
> +	if (ts->ts_valid) {
> +		/* signal the release */
> +		ts->ts_valid = false;
> +		input_report_key(ts->ts_input, BTN_TOUCH, 0);
> +		input_sync(ts->ts_input);
> +	}
> +
> +	/* if it is released, wait for the next touch via IRQ */
> +	ts->cur_plate = LRADC_TOUCH;
> +	mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
> +	mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
> +	mxs_lradc_reg_clear(lradc,
> +			    LRADC_CTRL1_TOUCH_DETECT_IRQ |
> +			    LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
> +			    LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1),
> +			    LRADC_CTRL1);
> +	mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
> +}
> +
> +/* touchscreen's state machine */
> +static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	switch (ts->cur_plate) {
> +	case LRADC_TOUCH:
> +		if (mxs_lradc_check_touch_event(lradc))
> +			mxs_lradc_start_touch_event(ts);
> +		mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
> +				    LRADC_CTRL1);
> +		return;
> +
> +	case LRADC_SAMPLE_Y:
> +		ts->ts_y_pos =
> +		    mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
> +		mxs_lradc_prepare_x_pos(ts);
> +		return;
> +
> +	case LRADC_SAMPLE_X:
> +		ts->ts_x_pos =
> +		    mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
> +		mxs_lradc_prepare_pressure(ts);
> +		return;
> +
> +	case LRADC_SAMPLE_PRESSURE:
> +		ts->ts_pressure =
> +		    mxs_lradc_read_ts_pressure(ts,
> +					       TOUCHSCREEN_VCHANNEL2,
> +					       TOUCHSCREEN_VCHANNEL1);
> +		mxs_lradc_complete_touch_event(ts);
> +		return;
> +
> +	case LRADC_SAMPLE_VALID:
> +		mxs_lradc_finish_touch_event(ts, 1);
> +		break;
> +	}
> +}
> +
> +/*
> + * IRQ Handling
> + */
> +static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data)
> +{
> +	struct mxs_lradc_ts *ts = data;
> +	struct mxs_lradc *lradc = ts->lradc;
> +	unsigned long reg = readl(lradc->base + LRADC_CTRL1);
> +	u32 clr_irq = mxs_lradc_irq_mask(lradc);
> +	const u32 ts_irq_mask =
> +		LRADC_CTRL1_TOUCH_DETECT_IRQ |
> +		LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
> +		LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
> +
> +	if (!(reg & mxs_lradc_irq_mask(lradc)))
> +		return IRQ_NONE;
> +
> +	if (lradc->use_touchscreen && (reg & ts_irq_mask)) {

Why do we have this flag instead of not creating the platform device by
MFD parent when touchscreen is not in use?

> +		mxs_lradc_handle_touch(ts);
> +
> +		/* Make sure we don't clear the next conversion's interrupt. */
> +		clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
> +				LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
> +		mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int mxs_lradc_ts_open(struct input_dev *dev)
> +{
> +	struct mxs_lradc_ts *ts = input_get_drvdata(dev);
> +
> +	/* Enable the touch-detect circuitry. */
> +	mxs_lradc_enable_touch_detection(ts);
> +
> +	return 0;
> +}
> +
> +static void mxs_lradc_disable_ts(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	/* stop all interrupts from firing */
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
> +		LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
> +		LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
> +
> +	/* Power-down touchscreen touch-detect circuitry. */
> +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
> +}
> +
> +static void mxs_lradc_ts_close(struct input_dev *dev)
> +{
> +	struct mxs_lradc_ts *ts = input_get_drvdata(dev);
> +
> +	mxs_lradc_disable_ts(ts);
> +}
> +
> +static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	/* Configure the touchscreen type */
> +	if (lradc->soc == IMX28_LRADC) {
> +		mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
> +				    LRADC_CTRL0);
> +
> +		if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
> +			mxs_lradc_reg_set(lradc,
> +					  LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
> +					  LRADC_CTRL0);
> +	}
> +}
> +
> +static void mxs_lradc_ts_hw_stop(struct mxs_lradc_ts *ts)
> +{
> +	int i;
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mxs_lradc_reg_clear(lradc,
> +			lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
> +			LRADC_CTRL1);
> +
> +	for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++)
> +		mxs_lradc_reg_wrt(ts->lradc, 0, LRADC_DELAY(i));
> +}
> +
> +static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts)
> +{
> +	struct input_dev *input = ts->ts_input;
> +	struct device *dev = ts->dev;
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	if (!lradc->use_touchscreen)
> +		return 0;
> +
> +	input->name = DRIVER_NAME_TS;
> +	input->id.bustype = BUS_HOST;
> +	input->dev.parent = dev;

Not needed for devm-managed input devices.

> +	input->open = mxs_lradc_ts_open;
> +	input->close = mxs_lradc_ts_close;
> +
> +	__set_bit(EV_ABS, input->evbit);
> +	__set_bit(EV_KEY, input->evbit);
> +	__set_bit(BTN_TOUCH, input->keybit);
> +	__set_bit(INPUT_PROP_DIRECT, input->propbit);
> +	input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
> +	input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
> +	input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
> +			     0, 0);
> +
> +	ts->ts_input = input;
> +	input_set_drvdata(input, ts);
> +
> +	return input_register_device(input);
> +}
> +
> +static int mxs_lradc_ts_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *node = dev->parent->of_node;
> +	struct mxs_lradc *lradc = dev_get_platdata(dev);
> +	struct mxs_lradc_ts *ts;
> +	struct input_dev *input;
> +	int touch_ret, ret, i;
> +	u32 ts_wires = 0, adapt;
> +
> +	ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
> +	input = devm_input_allocate_device(dev);

I'd expect input allocation be done in mxs_lradc_ts_register().

> +	if (!ts || !input)
> +		return -ENOMEM;
> +
> +	ts->lradc = lradc;
> +	ts->dev = dev;
> +	ts->ts_input = input;
> +	platform_set_drvdata(pdev, ts);
> +	input_set_drvdata(input, ts);
> +
> +	touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
> +					 &ts_wires);
> +
> +	if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) {
> +		ts->over_sample_cnt = 4;
> +	} else {
> +		if (adapt < 1 || adapt > 32) {
> +			dev_err(ts->dev, "Invalid sample count (%u)\n",
> +				adapt);
> +			touch_ret = -EINVAL;
> +		} else {
> +			ts->over_sample_cnt = adapt;
> +		}
> +	}
> +
> +	if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) {
> +		ts->over_sample_delay = 2;
> +	} else {
> +		if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) {
> +			dev_err(ts->dev, "Invalid sample delay (%u)\n",
> +				adapt);
> +			touch_ret = -EINVAL;
> +		} else {
> +			ts->over_sample_delay = adapt;
> +		}
> +	}
> +
> +	if (of_property_read_u32(node, "fsl,settling", &adapt)) {
> +		ts->settling_delay = 10;
> +	} else {
> +		if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) {
> +			dev_err(ts->dev, "Invalid settling delay (%u)\n",
> +				adapt);
> +			touch_ret = -EINVAL;

Why are we not aborting here?

> +		} else {
> +			ts->settling_delay = adapt;
> +		}
> +	}
> +
> +	mxs_lradc_ts_hw_init(ts);
> +	for (i = 0; i < lradc->irq_count; i++) {
> +		ret = devm_request_irq(dev, lradc->irq[i],
> +				       mxs_lradc_ts_handle_irq,

Hmm, if you have several interrupts handled by the same interrupt
handler you'd need some locking there.

> +				       IRQF_SHARED, lradc->irq_name[i], ts);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (!touch_ret) {
> +		ret = mxs_lradc_ts_register(ts);
> +		if (!ret)
> +			goto err_ts_register;
> +	}
> +
> +	return 0;
> +
> +err_ts_register:
> +	mxs_lradc_ts_hw_stop(ts);
> +	return ret;

Would prefer calling this variable "error".

> +}
> +
> +static int mxs_lradc_ts_remove(struct platform_device *pdev)
> +{
> +	struct mxs_lradc_ts *ts = platform_get_drvdata(pdev);
> +
> +	mxs_lradc_ts_hw_stop(ts);

I wonder if mxs_lradc_ts_hw_stop() can be combined with
mxs_lradc_disable_ts() which is called automatically if input device is
opened? This way we'd be able to get rid of mxs_lradc_ts_remove(). 

> +
> +	return 0;
> +}
> +
> +static struct platform_driver mxs_lradc_ts_driver = {
> +	.driver	= {
> +		.name = DRIVER_NAME_TS,
> +	},
> +	.probe	= mxs_lradc_ts_probe,
> +	.remove	= mxs_lradc_ts_remove,
> +};
> +module_platform_driver(mxs_lradc_ts_driver);
> +
> +MODULE_LICENSE("GPL v2");

"GPL" since you are not limiting to v2 only.

Thanks.

-- 
Dmitry

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
  2016-04-29 23:36     ` Dmitry Torokhov
@ 2016-04-29 23:57       ` Marek Vasut
  -1 siblings, 0 replies; 40+ messages in thread
From: Marek Vasut @ 2016-04-29 23:57 UTC (permalink / raw)
  To: Dmitry Torokhov, Ksenija Stanojevic
  Cc: linux-kernel, lee.jones, linux-input, jic23, knaack.h, lars,
	pmeerw, linux-iio, harald

On 04/30/2016 01:36 AM, Dmitry Torokhov wrote:
> Hi Ksenija,

Hi all,

> On Fri, Apr 29, 2016 at 01:49:11PM +0200, Ksenija Stanojevic wrote:
>> Add mxs-lradc touchscreen driver.
>>
>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
>> ---
>>  drivers/input/touchscreen/Kconfig        |  14 +-
>>  drivers/input/touchscreen/Makefile       |   1 +
>>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
>>  3 files changed, 742 insertions(+), 2 deletions(-)
>>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
>>
>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>> index 8ecdc38..d614d248 100644
>> --- a/drivers/input/touchscreen/Kconfig
>> +++ b/drivers/input/touchscreen/Kconfig
>> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
>>  	depends on SH_HP6XX && SH_ADC
>>  	help
>>  	  Say Y here if you have a HP Jornada 620/660/680/690 and want to
>> -          support the built-in touchscreen.
>> +	  support the built-in touchscreen.
>>  
>>  	  To compile this driver as a module, choose M here: the
>>  	  module will be called hp680_ts_input.
>> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
>>  	  This enables support for the Philips UCB1400 touchscreen interface.
>>  	  The UCB1400 is an AC97 audio codec.  The touchscreen interface
>>  	  will be initialized only after the ALSA subsystem has been
>> -	  brought up and the UCB1400 detected.  You therefore have to
>> +	  brought up and the UCB1400 detected.	You therefore have to
> 
> Why do we have the tab in the middle of the text?

This shouldn't be a part of the patch.

>>  	  configure ALSA support as well (either built-in or modular,
>>  	  independently of whether this driver is itself built-in or
>>  	  modular) for this driver to work.

[...]

>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver mxs_lradc_ts_driver = {
>> +	.driver	= {
>> +		.name = DRIVER_NAME_TS,
>> +	},
>> +	.probe	= mxs_lradc_ts_probe,
>> +	.remove	= mxs_lradc_ts_remove,
>> +};
>> +module_platform_driver(mxs_lradc_ts_driver);
>> +
>> +MODULE_LICENSE("GPL v2");
> 
> "GPL" since you are not limiting to v2 only.

The original driver ( drivers/iio/adc/mxs-lradc.c ) is GPLv2 , but
unless the license gets changed to BSD or somesuch, I don't think anyone
will really complain if it's changed to a more fitting version(s) of
GPL. I'm fine with any GPL version here.

Thanks for the thorough review, some points were new to me.

> Thanks.
> 


-- 
Best regards,
Marek Vasut

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
@ 2016-04-29 23:57       ` Marek Vasut
  0 siblings, 0 replies; 40+ messages in thread
From: Marek Vasut @ 2016-04-29 23:57 UTC (permalink / raw)
  To: Dmitry Torokhov, Ksenija Stanojevic
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	linux-input-u79uwXL29TY76Z2rM5mHXA, jic23-DgEjT+Ai2ygdnm+yROfE0A,
	knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	harald-95f8Dae0BrPYtjvyW6yDsg

On 04/30/2016 01:36 AM, Dmitry Torokhov wrote:
> Hi Ksenija,

Hi all,

> On Fri, Apr 29, 2016 at 01:49:11PM +0200, Ksenija Stanojevic wrote:
>> Add mxs-lradc touchscreen driver.
>>
>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> ---
>>  drivers/input/touchscreen/Kconfig        |  14 +-
>>  drivers/input/touchscreen/Makefile       |   1 +
>>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
>>  3 files changed, 742 insertions(+), 2 deletions(-)
>>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
>>
>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>> index 8ecdc38..d614d248 100644
>> --- a/drivers/input/touchscreen/Kconfig
>> +++ b/drivers/input/touchscreen/Kconfig
>> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
>>  	depends on SH_HP6XX && SH_ADC
>>  	help
>>  	  Say Y here if you have a HP Jornada 620/660/680/690 and want to
>> -          support the built-in touchscreen.
>> +	  support the built-in touchscreen.
>>  
>>  	  To compile this driver as a module, choose M here: the
>>  	  module will be called hp680_ts_input.
>> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
>>  	  This enables support for the Philips UCB1400 touchscreen interface.
>>  	  The UCB1400 is an AC97 audio codec.  The touchscreen interface
>>  	  will be initialized only after the ALSA subsystem has been
>> -	  brought up and the UCB1400 detected.  You therefore have to
>> +	  brought up and the UCB1400 detected.	You therefore have to
> 
> Why do we have the tab in the middle of the text?

This shouldn't be a part of the patch.

>>  	  configure ALSA support as well (either built-in or modular,
>>  	  independently of whether this driver is itself built-in or
>>  	  modular) for this driver to work.

[...]

>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver mxs_lradc_ts_driver = {
>> +	.driver	= {
>> +		.name = DRIVER_NAME_TS,
>> +	},
>> +	.probe	= mxs_lradc_ts_probe,
>> +	.remove	= mxs_lradc_ts_remove,
>> +};
>> +module_platform_driver(mxs_lradc_ts_driver);
>> +
>> +MODULE_LICENSE("GPL v2");
> 
> "GPL" since you are not limiting to v2 only.

The original driver ( drivers/iio/adc/mxs-lradc.c ) is GPLv2 , but
unless the license gets changed to BSD or somesuch, I don't think anyone
will really complain if it's changed to a more fitting version(s) of
GPL. I'm fine with any GPL version here.

Thanks for the thorough review, some points were new to me.

> Thanks.
> 


-- 
Best regards,
Marek Vasut

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

* Re: [PATCH 1/3] mfd: mxs-lradc: Add support for mxs-lradc MFD
  2016-04-29 11:47   ` Ksenija Stanojevic
                     ` (2 preceding siblings ...)
  (?)
@ 2016-05-01 13:06   ` Jonathan Cameron
  -1 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2016-05-01 13:06 UTC (permalink / raw)
  To: Ksenija Stanojevic, linux-kernel
  Cc: lee.jones, dmitry.torokhov, linux-input, knaack.h, lars, pmeerw,
	marex, linux-iio, harald

On 29/04/16 12:47, Ksenija Stanojevic wrote:
> Add core files for mxs-lradc MFD driver.
> 
> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
Only a few trivial additions to others comments from me.

Looking pretty good.

Jonathan
> ---
>  drivers/mfd/Kconfig           |  33 +++++--
>  drivers/mfd/Makefile          |   1 +
>  drivers/mfd/mxs-lradc.c       | 213 ++++++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/mxs-lradc.h | 210 +++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 449 insertions(+), 8 deletions(-)
>  create mode 100644 drivers/mfd/mxs-lradc.c
>  create mode 100644 include/linux/mfd/mxs-lradc.h
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index eea61e3..fff44d6 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -16,7 +16,7 @@ config MFD_CS5535
>  	depends on PCI && (X86_32 || (X86 && COMPILE_TEST))
>  	---help---
>  	  This is the core driver for CS5535/CS5536 MFD functions.  This is
> -          necessary for using the board's GPIO and MFGPT functionality.
> +	  necessary for using the board's GPIO and MFGPT functionality.
>  
>  config MFD_ACT8945A
>  	tristate "Active-semi ACT8945A"
> @@ -319,6 +319,23 @@ config MFD_HI6421_PMIC
>  	  menus in order to enable them.
>  	  We communicate with the Hi6421 via memory-mapped I/O.
>  
> +config MFD_MXS_LRADC
> +	tristate "Freescale i.MX23/i.MX28 LRADC"
> +	depends on ARCH_MXS || COMPILE_TEST
> +	select MFD_CORE
> +	select STMP_DEVICE
> +	help
> +	  Say yes here to build support for the low-resolution
> +	  analog-to-digital converter (LRADC) found on the i.MX23 and i.MX28
> +	  processors. This driver provides common support for accessing the
> +	  device, additional drivers must be enabled in order to use the
> +	  functionality of the device:
> +		mxs-lradc-adc for ADC readings
> +		mxs-lradc-ts  for touchscreen support
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called mxs-lradc.
> +
>  config HTC_EGPIO
>  	bool "HTC EGPIO support"
>  	depends on GPIOLIB && ARM
> @@ -650,7 +667,7 @@ config EZX_PCAP
>  	  needed for MMC, TouchScreen, Sound, USB, etc..
>  
>  config MFD_VIPERBOARD
> -        tristate "Nano River Technologies Viperboard"
> +	tristate "Nano River Technologies Viperboard"
>  	select MFD_CORE
>  	depends on USB
>  	default n
> @@ -898,11 +915,11 @@ config MFD_SMSC
>         select MFD_CORE
>         select REGMAP_I2C
>         help
> -        If you say yes here you get support for the
> -        ece1099 chips from SMSC.
> +	If you say yes here you get support for the
> +	ece1099 chips from SMSC.
>  
> -        To compile this driver as a module, choose M here: the
> -        module will be called smsc.
> +	To compile this driver as a module, choose M here: the
> +	module will be called smsc.
>  
>  config ABX500_CORE
>  	bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
> @@ -956,8 +973,8 @@ config AB8500_DEBUG
>         depends on AB8500_GPADC && DEBUG_FS
>         default y if DEBUG_FS
>         help
> -         Select this option if you want debug information using the debug
> -         filesystem, debugfs.
> +	 Select this option if you want debug information using the debug
> +	 filesystem, debugfs.
>  
>  config AB8500_GPADC
>  	bool "ST-Ericsson AB8500 GPADC driver"
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 5eaa6465d..236b831 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -203,3 +203,4 @@ intel-soc-pmic-objs		:= intel_soc_pmic_core.o intel_soc_pmic_crc.o
>  intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC)	+= intel_soc_pmic_bxtwc.o
>  obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
>  obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
> +obj-$(CONFIG_MFD_MXS_LRADC)	+= mxs-lradc.o
> diff --git a/drivers/mfd/mxs-lradc.c b/drivers/mfd/mxs-lradc.c
> new file mode 100644
> index 0000000..e1c8f9e
> --- /dev/null
> +++ b/drivers/mfd/mxs-lradc.c
> @@ -0,0 +1,213 @@
> +/*
> + * Freescale MXS LRADC driver
> + *
> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
> + * Marek Vasut <marex@denx.de>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/mxs-lradc.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +static struct mfd_cell lradc_adc_dev = {
> +	.name = DRIVER_NAME_ADC,
> +};
> +
> +static struct mfd_cell lradc_ts_dev = {
> +	.name = DRIVER_NAME_TS,
> +};
> +
> +static const char * const mx23_lradc_irq_names[] = {
> +	"mxs-lradc-touchscreen",
> +	"mxs-lradc-channel0",
> +	"mxs-lradc-channel1",
> +	"mxs-lradc-channel2",
> +	"mxs-lradc-channel3",
> +	"mxs-lradc-channel4",
> +	"mxs-lradc-channel5",
> +	"mxs-lradc-channel6",
> +	"mxs-lradc-channel7",
> +};
> +
> +static const char * const mx28_lradc_irq_names[] = {
> +	"mxs-lradc-touchscreen",
> +	"mxs-lradc-thresh0",
> +	"mxs-lradc-thresh1",
> +	"mxs-lradc-channel0",
> +	"mxs-lradc-channel1",
> +	"mxs-lradc-channel2",
> +	"mxs-lradc-channel3",
> +	"mxs-lradc-channel4",
> +	"mxs-lradc-channel5",
> +	"mxs-lradc-channel6",
> +	"mxs-lradc-channel7",
> +	"mxs-lradc-button0",
> +	"mxs-lradc-button1",
> +};
> +
> +struct mxs_lradc_of_config {
> +	const int		irq_count;
> +	const char * const	*irq_name;
> +};
> +
> +static const struct mxs_lradc_of_config mxs_lradc_of_config[] = {
> +	[IMX23_LRADC] = {
> +		.irq_count	= ARRAY_SIZE(mx23_lradc_irq_names),
> +		.irq_name	= mx23_lradc_irq_names,
> +	},
> +	[IMX28_LRADC] = {
> +		.irq_count	= ARRAY_SIZE(mx28_lradc_irq_names),
> +		.irq_name	= mx28_lradc_irq_names,
> +	},
> +};
> +
> +static const struct of_device_id mxs_lradc_dt_ids[] = {
> +	{ .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, },
> +	{ .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
> +
> +static int mxs_lradc_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *of_id =
> +		of_match_device(mxs_lradc_dt_ids, &pdev->dev);
> +	const struct mxs_lradc_of_config *of_cfg =
> +		&mxs_lradc_of_config[(enum mxs_lradc_id)of_id->data];
> +	struct device *dev = &pdev->dev;
> +	struct device_node *node = dev->of_node;
> +	struct mxs_lradc *lradc;
> +	struct resource *iores;
> +	int ret = 0, touch_ret, i;
> +	u32 ts_wires = 0;
> +
> +	lradc = devm_kzalloc(&pdev->dev, sizeof(*lradc), GFP_KERNEL);
> +	if (!lradc)
> +		return -ENOMEM;
> +	lradc->soc = (enum mxs_lradc_id)of_id->data;
> +
> +	/* Grab the memory area */
> +	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	lradc->base = devm_ioremap_resource(dev, iores);
> +	if (IS_ERR(lradc->base))
> +		return PTR_ERR(lradc->base);
> +
> +	lradc->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(lradc->clk)) {
> +		dev_err(dev, "Failed to get the delay unit clock\n");
> +		return PTR_ERR(lradc->clk);
> +	}
> +	ret = clk_prepare_enable(lradc->clk);
> +	if (ret != 0) {
> +		dev_err(dev, "Failed to enable the delay unit clock\n");
> +		return ret;
> +	}
> +
> +	touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
> +					 &ts_wires);
> +
> +	if (touch_ret == 0)
> +		lradc->buffer_vchans = BUFFER_VCHANS_LIMITED;
> +	else
> +		lradc->buffer_vchans = BUFFER_VCHANS_ALL;
> +
> +	lradc->irq_count = of_cfg->irq_count;
> +	lradc->irq_name = of_cfg->irq_name;
> +	for (i = 0; i < lradc->irq_count; i++) {
> +		lradc->irq[i] = platform_get_irq(pdev, i);
> +		if (lradc->irq[i] < 0) {
> +			ret = lradc->irq[i];
> +			goto err_clk;
> +		}
> +	}
> +
> +	platform_set_drvdata(pdev, lradc);
> +
> +	ret = stmp_reset_block(lradc->base);
> +
> +	if (ret)
> +		return ret;
> +
> +	lradc_adc_dev.platform_data = lradc;
> +	lradc_adc_dev.pdata_size = sizeof(*lradc);
> +
> +	ret = mfd_add_devices(&pdev->dev, -1, &lradc_adc_dev, 1, NULL, 0, NULL);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to add the ADC subdevice\n");
> +		return ret;
> +	}
> +
> +	lradc_ts_dev.platform_data = lradc;
> +	lradc_ts_dev.pdata_size = sizeof(*lradc);
> +
> +	switch (ts_wires) {
> +	case 4:
> +		lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE;
> +		break;
> +	case 5:
> +		if (lradc->soc == IMX28_LRADC) {
> +			lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE;
> +			break;
> +		}
> +		/* fall through an error message for i.MX23 */
> +	default:
> +		dev_err(&pdev->dev,
> +			"Unsupported number of touchscreen wires (%d)\n",
> +			ts_wires);
> +		return -EINVAL;
> +	}
> +
> +	ret = mfd_add_devices(&pdev->dev, -1, &lradc_ts_dev, 1, NULL, 0, NULL);
> +	if (ret) {
> +		dev_err(&pdev->dev,
> +			"Failed to add the touchscreen subdevice\n");
> +		goto err_remove_adc;
> +	}
> +
> +	return 0;
> +
> +err_remove_adc:
> +	mfd_remove_devices(&pdev->dev);
> +err_clk:
> +	clk_disable_unprepare(lradc->clk);
> +	return ret;
> +}
> +
> +static int mxs_lradc_remove(struct platform_device *pdev)
> +{
> +	struct mxs_lradc *lradc = platform_get_drvdata(pdev);
> +
> +	mfd_remove_devices(&pdev->dev)
> +	clk_disable_unprepare(lradc->clk);
blank line here ideally...
> +	return 0;
> +}
> +
> +static struct platform_driver mxs_lradc_driver = {
> +	.driver = {
> +		.name = "mxs-lradc",
> +		.of_match_table = mxs_lradc_dt_ids,
> +	},
> +	.probe = mxs_lradc_probe,
> +	.remove = mxs_lradc_remove,
> +};
> +
> +module_platform_driver(mxs_lradc_driver);
> +
> +MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:mxs-lradc");
> diff --git a/include/linux/mfd/mxs-lradc.h b/include/linux/mfd/mxs-lradc.h
> new file mode 100644
> index 0000000..c062969
> --- /dev/null
> +++ b/include/linux/mfd/mxs-lradc.h
> @@ -0,0 +1,210 @@
> +/*
> + * Freescale MXS LRADC driver
> + *
> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
> + * Marek Vasut <marex@denx.de>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __MXS_LRADC_H
> +#define __MXS_LRADC_H
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/stmp_device.h>
> +#include <linux/sysfs.h>
> +
> +#define DRIVER_NAME_ADC "mxs-lradc-adc"
> +#define DRIVER_NAME_TS "mxs-lradc-ts"
> +
> +#define LRADC_MAX_DELAY_CHANS	4
> +#define LRADC_MAX_MAPPED_CHANS	8
> +#define LRADC_MAX_TOTAL_CHANS	16
> +
> +#define LRADC_DELAY_TIMER_HZ	2000
> +
> +#define LRADC_CTRL0				0x00
> +# define LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE	BIT(23)
> +# define LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE	BIT(22)
> +# define LRADC_CTRL0_MX28_YNNSW /* YM */	BIT(21)
> +# define LRADC_CTRL0_MX28_YPNSW /* YP */	BIT(20)
> +# define LRADC_CTRL0_MX28_YPPSW /* YP */	BIT(19)
> +# define LRADC_CTRL0_MX28_XNNSW /* XM */	BIT(18)
> +# define LRADC_CTRL0_MX28_XNPSW /* XM */	BIT(17)
> +# define LRADC_CTRL0_MX28_XPPSW /* XP */	BIT(16)
> +
> +# define LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE	BIT(20)
> +# define LRADC_CTRL0_MX23_YM			BIT(19)
> +# define LRADC_CTRL0_MX23_XM			BIT(18)
> +# define LRADC_CTRL0_MX23_YP			BIT(17)
> +# define LRADC_CTRL0_MX23_XP			BIT(16)
> +
> +# define LRADC_CTRL0_MX28_PLATE_MASK \
> +		(LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE | \
> +		LRADC_CTRL0_MX28_YNNSW | LRADC_CTRL0_MX28_YPNSW | \
> +		LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW | \
> +		LRADC_CTRL0_MX28_XNPSW | LRADC_CTRL0_MX28_XPPSW)
> +
> +# define LRADC_CTRL0_MX23_PLATE_MASK \
> +		(LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE | \
> +		LRADC_CTRL0_MX23_YM | LRADC_CTRL0_MX23_XM | \
> +		LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XP)
> +
> +#define LRADC_CTRL1				0x10
> +#define LRADC_CTRL1_TOUCH_DETECT_IRQ_EN		BIT(24)
> +#define LRADC_CTRL1_LRADC_IRQ_EN(n)		(1 << ((n) + 16))
> +#define LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK	(0x1fff << 16)
> +#define LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK	(0x01ff << 16)
> +#define LRADC_CTRL1_LRADC_IRQ_EN_OFFSET		16
> +#define LRADC_CTRL1_TOUCH_DETECT_IRQ		BIT(8)
> +#define LRADC_CTRL1_LRADC_IRQ(n)		(1 << (n))
> +#define LRADC_CTRL1_MX28_LRADC_IRQ_MASK		0x1fff
> +#define LRADC_CTRL1_MX23_LRADC_IRQ_MASK		0x01ff
> +#define LRADC_CTRL1_LRADC_IRQ_OFFSET		0
> +
> +#define LRADC_CTRL2				0x20
> +#define LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET	24
> +#define LRADC_CTRL2_TEMPSENSE_PWD		BIT(15)
> +
> +#define LRADC_STATUS				0x40
> +#define LRADC_STATUS_TOUCH_DETECT_RAW		BIT(0)
> +
> +#define LRADC_CH(n)				(0x50 + (0x10 * (n)))
> +#define LRADC_CH_ACCUMULATE			BIT(29)
> +#define LRADC_CH_NUM_SAMPLES_MASK		(0x1f << 24)
> +#define LRADC_CH_NUM_SAMPLES_OFFSET		24
> +#define LRADC_CH_NUM_SAMPLES(x) \
> +				((x) << LRADC_CH_NUM_SAMPLES_OFFSET)
> +#define LRADC_CH_VALUE_MASK			0x3ffff
> +#define LRADC_CH_VALUE_OFFSET			0
> +
> +#define LRADC_DELAY(n)				(0xd0 + (0x10 * (n)))
> +#define LRADC_DELAY_TRIGGER_LRADCS_MASK		(0xffUL << 24)
> +#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET	24
> +#define LRADC_DELAY_TRIGGER(x) \
> +				(((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \
> +				LRADC_DELAY_TRIGGER_LRADCS_MASK)
> +#define LRADC_DELAY_KICK			BIT(20)
> +#define LRADC_DELAY_TRIGGER_DELAYS_MASK		(0xf << 16)
> +#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET	16
> +#define LRADC_DELAY_TRIGGER_DELAYS(x) \
> +				(((x) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) & \
> +				LRADC_DELAY_TRIGGER_DELAYS_MASK)
> +#define LRADC_DELAY_LOOP_COUNT_MASK		(0x1f << 11)
> +#define LRADC_DELAY_LOOP_COUNT_OFFSET		11
> +#define LRADC_DELAY_LOOP(x) \
> +				(((x) << LRADC_DELAY_LOOP_COUNT_OFFSET) & \
> +				LRADC_DELAY_LOOP_COUNT_MASK)
> +#define LRADC_DELAY_DELAY_MASK			0x7ff
> +#define LRADC_DELAY_DELAY_OFFSET		0
> +#define LRADC_DELAY_DELAY(x) \
> +				(((x) << LRADC_DELAY_DELAY_OFFSET) & \
> +				LRADC_DELAY_DELAY_MASK)
> +
> +#define LRADC_CTRL4				0x140
> +#define LRADC_CTRL4_LRADCSELECT_MASK(n)		(0xf << ((n) * 4))
> +#define LRADC_CTRL4_LRADCSELECT_OFFSET(n)	((n) * 4)
> +#define LRADC_CTRL4_LRADCSELECT(n, x) \
> +				(((x) << LRADC_CTRL4_LRADCSELECT_OFFSET(n)) & \
> +				LRADC_CTRL4_LRADCSELECT_MASK(n))
> +
> +#define LRADC_RESOLUTION			12
> +#define LRADC_SINGLE_SAMPLE_MASK		((1 << LRADC_RESOLUTION) - 1)
> +
> +enum mxs_lradc_id {
> +	IMX23_LRADC,
> +	IMX28_LRADC,
> +};
> +
> +enum mxs_lradc_ts_wires {
> +	MXS_LRADC_TOUCHSCREEN_NONE = 0,
> +	MXS_LRADC_TOUCHSCREEN_4WIRE,
> +	MXS_LRADC_TOUCHSCREEN_5WIRE,
> +};
> +
> +struct mxs_lradc {
> +	enum mxs_lradc_id	soc;
> +
> +	void __iomem		*base;
> +	struct clk		*clk;
> +
> +	int			irq[13];
> +	const char * const	*irq_name;
> +	int			irq_count;
> +
> +#define BUFFER_VCHANS_LIMITED		0x3f
> +#define BUFFER_VCHANS_ALL		0xff
> +	u8			buffer_vchans;
> +
> +	/*
> +	 * Certain LRADC channels are shared between touchscreen
> +	 * and/or touch-buttons and generic LRADC block. Therefore when using
> +	 * either of these, these channels are not available for the regular
> +	 * sampling. The shared channels are as follows:
> +	 *
> +	 * CH0 -- Touch button #0
> +	 * CH1 -- Touch button #1
> +	 * CH2 -- Touch screen XPUL
> +	 * CH3 -- Touch screen YPLL
> +	 * CH4 -- Touch screen XNUL
> +	 * CH5 -- Touch screen YNLR
> +	 * CH6 -- Touch screen WIPER (5-wire only)
> +	 *
> +	 * The bit fields below represents which parts of the LRADC block are
> +	 * switched into special mode of operation. These channels can not
> +	 * be sampled as regular LRADC channels. The driver will refuse any
> +	 * attempt to sample these channels.
> +	 */
> +#define CHAN_MASK_TOUCHBUTTON		(BIT(1) | BIT(0))
> +#define CHAN_MASK_TOUCHSCREEN_4WIRE	(0xf << 2)
> +#define CHAN_MASK_TOUCHSCREEN_5WIRE	(0x1f << 2)
> +	enum mxs_lradc_ts_wires	use_touchscreen;
> +	bool			use_touchbutton;
> +};
> +
> +static inline void mxs_lradc_reg_set(struct mxs_lradc *lradc, u32 val, u32 reg)
> +{
> +	writel(val, lradc->base + reg + STMP_OFFSET_REG_SET);
> +}
> +
> +static inline void mxs_lradc_reg_clear(struct mxs_lradc *lradc, u32 val,
> +				       u32 reg)
> +{
> +	writel(val, lradc->base + reg + STMP_OFFSET_REG_CLR);
> +}
> +
> +static inline void mxs_lradc_reg_wrt(struct mxs_lradc *lradc, u32 val, u32 reg)
> +{
> +	writel(val, lradc->base + reg);
> +}
> +
> +static inline u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc)
> +{
As we are changing the driver here I would ever so slightly prefer
this to be a switch statement rather than an if. Takes a couple of lines more
but makes adding new parts in the future easier!

> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL1_MX23_LRADC_IRQ_MASK;
> +	return LRADC_CTRL1_MX28_LRADC_IRQ_MASK;
> +}
> +
> +#endif /* __MXS_LRADC_H */
> 

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

* Re: [PATCH 2/3] iio: adc: mxs-lradc: Add support for adc driver
@ 2016-05-01 16:38     ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2016-05-01 16:38 UTC (permalink / raw)
  To: Ksenija Stanojevic, linux-kernel
  Cc: lee.jones, dmitry.torokhov, linux-input, knaack.h, lars, pmeerw,
	marex, linux-iio, harald

On 29/04/16 12:48, Ksenija Stanojevic wrote:
> Add mxs-lradc adc driver.
> 
> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
Mostly looking good.  A few nitpicks and suggestions inline.

Jonathan
> ---
>  drivers/iio/adc/Kconfig         |  37 +-
>  drivers/iio/adc/Makefile        |   1 +
>  drivers/iio/adc/mxs-lradc-adc.c | 832 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 858 insertions(+), 12 deletions(-)
>  create mode 100644 drivers/iio/adc/mxs-lradc-adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 82c718c..50847b8 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -233,6 +233,19 @@ config IMX7D_ADC
>  	  This driver can also be built as a module. If so, the module will be
>  	  called imx7d_adc.
>  
> +config MXS_LRADC_ADC
> +	tristate "Freescale i.MX23/i.MX28 LRADC ADC"
> +	depends on MFD_MXS_LRADC
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	  Say yes here to build support for the ADC functions of the
> +	  i.MX23/i.MX28 LRADC. This includes general-purpose ADC readings,
> +	  battery voltage measurement, and die temperature measurement.
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called mxs-lradc-adc.
> +
>  config LP8788_ADC
>  	tristate "LP8788 ADC driver"
>  	depends on MFD_LP8788
> @@ -306,18 +319,18 @@ config MEN_Z188_ADC
>  	  called men_z188_adc.
>  
>  config MXS_LRADC
> -        tristate "Freescale i.MX23/i.MX28 LRADC"
> -        depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
> -        depends on INPUT
> -        select STMP_DEVICE
> -        select IIO_BUFFER
> -        select IIO_TRIGGERED_BUFFER
> -        help
> -          Say yes here to build support for i.MX23/i.MX28 LRADC convertor
> -          built into these chips.
> -
> -          To compile this driver as a module, choose M here: the
> -          module will be called mxs-lradc.
> +	tristate "Freescale i.MX23/i.MX28 LRADC"
> +	depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
> +	depends on INPUT
> +	select STMP_DEVICE
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	  Say yes here to build support for i.MX23/i.MX28 LRADC convertor
> +	  built into these chips.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called mxs-lradc.
>  
>  config NAU7802
>  	tristate "Nuvoton NAU7802 ADC driver"
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 0cb7921..ca7d6aa 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -31,6 +31,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
>  obj-$(CONFIG_MCP3422) += mcp3422.o
>  obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
>  obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
> +obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
>  obj-$(CONFIG_NAU7802) += nau7802.o
>  obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
>  obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
> diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c
> new file mode 100644
> index 0000000..793c369
> --- /dev/null
> +++ b/drivers/iio/adc/mxs-lradc-adc.c
> @@ -0,0 +1,832 @@
> +/*
> + * Freescale MXS LRADC ADC driver
> + *
> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
> + * Marek Vasut <marex@denx.de>
You should probably add your own copyright as you are making substantial
improvements!

> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/mfd/mxs-lradc.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/sysfs.h>
> +
> +/*
> + * Make this runtime configurable if necessary. Currently, if the buffered mode
> + * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before
> + * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000)
> + * seconds. The result is that the samples arrive every 500mS.
> + */
> +#define LRADC_DELAY_TIMER_PER	200
> +#define LRADC_DELAY_TIMER_LOOP	5
> +
> +#define VREF_MV_BASE 1850
> +
> +static const u32 mxs_lradc_adc_vref_mv[][LRADC_MAX_TOTAL_CHANS] = {
> +	[IMX23_LRADC] = {
> +		VREF_MV_BASE,		/* CH0 */
> +		VREF_MV_BASE,		/* CH1 */
> +		VREF_MV_BASE,		/* CH2 */
> +		VREF_MV_BASE,		/* CH3 */
> +		VREF_MV_BASE,		/* CH4 */
> +		VREF_MV_BASE,		/* CH5 */
> +		VREF_MV_BASE * 2,	/* CH6 VDDIO */
> +		VREF_MV_BASE * 4,	/* CH7 VBATT */
> +		VREF_MV_BASE,		/* CH8 Temp sense 0 */
> +		VREF_MV_BASE,		/* CH9 Temp sense 1 */
> +		VREF_MV_BASE,		/* CH10 */
> +		VREF_MV_BASE,		/* CH11 */
> +		VREF_MV_BASE,		/* CH12 USB_DP */
> +		VREF_MV_BASE,		/* CH13 USB_DN */
> +		VREF_MV_BASE,		/* CH14 VBG */
> +		VREF_MV_BASE * 4,	/* CH15 VDD5V */
> +	},
> +	[IMX28_LRADC] = {
> +		VREF_MV_BASE,		/* CH0 */
> +		VREF_MV_BASE,		/* CH1 */
> +		VREF_MV_BASE,		/* CH2 */
> +		VREF_MV_BASE,		/* CH3 */
> +		VREF_MV_BASE,		/* CH4 */
> +		VREF_MV_BASE,		/* CH5 */
> +		VREF_MV_BASE,		/* CH6 */
> +		VREF_MV_BASE * 4,	/* CH7 VBATT */
> +		VREF_MV_BASE,		/* CH8 Temp sense 0 */
> +		VREF_MV_BASE,		/* CH9 Temp sense 1 */
> +		VREF_MV_BASE * 2,	/* CH10 VDDIO */
> +		VREF_MV_BASE,		/* CH11 VTH */
> +		VREF_MV_BASE * 2,	/* CH12 VDDA */
> +		VREF_MV_BASE,		/* CH13 VDDD */
> +		VREF_MV_BASE,		/* CH14 VBG */
> +		VREF_MV_BASE * 4,	/* CH15 VDD5V */
> +	},
> +};
> +
> +enum mxs_lradc_divbytwo {
> +	MXS_LRADC_DIV_DISABLED = 0,
> +	MXS_LRADC_DIV_ENABLED,
> +};
> +
> +struct mxs_lradc_scale {
> +	unsigned int		integer;
> +	unsigned int		nano;
> +};
> +
> +struct mxs_lradc_adc {
> +	struct mxs_lradc	*lradc;
> +	struct device		*dev;
> +
> +	u32			*buffer;
> +	struct iio_trigger	*trig;
> +	struct mutex		lock;
> +	struct completion	completion;
> +
> +	const u32		*vref_mv;
> +	struct mxs_lradc_scale	scale_avail[LRADC_MAX_TOTAL_CHANS][2];
> +	unsigned long		is_divided;
> +};
> +
> +/*
> + * Raw I/O operations
> + */
> +static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan,
> +				     int *val)
> +{
> +	struct mxs_lradc_adc *adc = iio_priv(iio_dev);
> +	struct mxs_lradc *lradc = adc->lradc;
> +	int ret;
> +
> +	/*
> +	 * See if there is no buffered operation in progress. If there is simply
> +	 * bail out. This can be improved to support both buffered and raw IO at
> +	 * the same time, yet the code becomes horribly complicated. Therefore I
> +	 * applied KISS principle here.
This is true in lots of devices!  Hence we often have a similar check.
> +	 */
> +	ret = mutex_trylock(&adc->lock);
> +	if (!ret)
> +		return -EBUSY;
We have standard core infrastructure for this now that should do the trick.
iio_device_claim_direct_mode() etc.
> +
> +	reinit_completion(&adc->completion);
> +
> +	/*
> +	 * No buffered operation in progress, map the channel and trigger it.
> +	 * Virtual channel 0 is always used here as the others are always not
> +	 * used if doing raw sampling.
> +	 */
> +	if (lradc->soc == IMX28_LRADC)
> +		mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0),
> +				    LRADC_CTRL1);
> +	mxs_lradc_reg_clear(lradc, 0x1, LRADC_CTRL0);
> +
> +	/* Enable / disable the divider per requirement */
> +	if (test_bit(chan, &adc->is_divided))
> +		mxs_lradc_reg_set(lradc,
> +				  1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
> +				  LRADC_CTRL2);
> +	else
> +		mxs_lradc_reg_clear(lradc,
> +				    1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
> +				    LRADC_CTRL2);
> +
> +	/* Clean the slot's previous content, then set new one. */
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(0),
> +			    LRADC_CTRL4);
> +	mxs_lradc_reg_set(lradc, chan, LRADC_CTRL4);
> +
> +	mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(0));
> +
> +	/* Enable the IRQ and start sampling the channel. */
> +	mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
> +	mxs_lradc_reg_set(lradc, BIT(0), LRADC_CTRL0);
> +
> +	/* Wait for completion on the channel, 1 second max. */
> +	ret = wait_for_completion_killable_timeout(&adc->completion, HZ);
> +	if (!ret)
> +		ret = -ETIMEDOUT;
> +	if (ret < 0)
> +		goto err;
> +
> +	/* Read the data. */
> +	*val = readl(lradc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
> +	ret = IIO_VAL_INT;
> +
> +err:
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
> +
> +	mutex_unlock(&adc->lock);
> +
> +	return ret;
> +}
> +
> +static int mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val)
> +{
> +	int ret, min, max;
> +
> +	ret = mxs_lradc_adc_read_single(iio_dev, 8, &min);
> +	if (ret != IIO_VAL_INT)
> +		return ret;
> +
> +	ret = mxs_lradc_adc_read_single(iio_dev, 9, &max);
> +	if (ret != IIO_VAL_INT)
> +		return ret;
> +
> +	*val = max - min;
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static int mxs_lradc_adc_read_raw(struct iio_dev *iio_dev,
> +			      const struct iio_chan_spec *chan,
> +			      int *val, int *val2, long m)
> +{
> +	struct mxs_lradc_adc *adc = iio_priv(iio_dev);
> +
> +	switch (m) {
> +	case IIO_CHAN_INFO_RAW:
> +		if (chan->type == IIO_TEMP)
> +			return mxs_lradc_adc_read_temp(iio_dev, val);
> +
> +		return mxs_lradc_adc_read_single(iio_dev, chan->channel, val);
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		if (chan->type == IIO_TEMP) {
> +			/* From the datasheet, we have to multiply by 1.012 and
> +			 * divide by 4
> +			 */
> +			*val = 0;
> +			*val2 = 253000;
> +			return IIO_VAL_INT_PLUS_MICRO;
> +		}
> +
> +		*val = adc->vref_mv[chan->channel];
> +		*val2 = chan->scan_type.realbits -
> +			test_bit(chan->channel, &adc->is_divided);
blank line here as you have done elsewhere...
> +		return IIO_VAL_FRACTIONAL_LOG2;
> +
> +	case IIO_CHAN_INFO_OFFSET:
> +		if (chan->type == IIO_TEMP) {
standard multiline comment syntax please.  Please fix this throughout.

/*
 * The ...
> +			/* The calculated value from the ADC is in Kelvin, we
> +			 * want Celsius for hwmon so the offset is -273.15
> +			 * The offset is applied before scaling so it is
> +			 * actually -213.15 * 4 / 1.012 = -1079.644268
> +			 */
> +			*val = -1079;
> +			*val2 = 644268;
> +
> +			return IIO_VAL_INT_PLUS_MICRO;
> +		}
> +
> +		return -EINVAL;
> +
> +	default:
> +		break;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev,
> +				   const struct iio_chan_spec *chan,
> +				   int val, int val2, long m)
> +{
> +	struct mxs_lradc_adc *adc = iio_priv(iio_dev);
> +	struct mxs_lradc_scale *scale_avail =
> +			adc->scale_avail[chan->channel];
> +	int ret;
> +
> +	ret = mutex_trylock(&adc->lock);
> +	if (!ret)
> +		return -EBUSY;
> +
> +	switch (m) {
> +	case IIO_CHAN_INFO_SCALE:
> +		ret = -EINVAL;
> +		if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
> +		    val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) {
> +			/* divider by two disabled */
> +			clear_bit(chan->channel, &adc->is_divided);
> +			ret = 0;
> +		} else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
> +			   val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) {
> +			/* divider by two enabled */
> +			set_bit(chan->channel, &adc->is_divided);
> +			ret = 0;
> +		}
> +
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	mutex_unlock(&adc->lock);
> +
> +	return ret;
> +}
> +
> +static int mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev,
> +					   const struct iio_chan_spec *chan,
> +					   long m)
> +{
> +	return IIO_VAL_INT_PLUS_NANO;
> +}
> +
> +static ssize_t mxs_lradc_adc_show_scale_avail_ch(struct device *dev,
> +						 struct device_attribute *attr,
> +						 char *buf,
> +						 int ch)
> +{
> +	struct iio_dev *iio = dev_to_iio_dev(dev);
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +	int i, len = 0;
> +
> +	for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++)
> +		len += sprintf(buf + len, "%u.%09u ",
> +			       adc->scale_avail[ch][i].integer,
> +			       adc->scale_avail[ch][i].nano);
> +
> +	len += sprintf(buf + len, "\n");
> +
> +	return len;
> +}
> +
I'm unconvinced this wrapper adds anything...
> +static ssize_t mxs_lradc_adc_show_scale_avail(struct device *dev,
> +					      struct device_attribute *attr,
> +					      char *buf)
> +{
> +	struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
> +
> +	return mxs_lradc_adc_show_scale_avail_ch(dev, attr, buf,
> +						 iio_attr->address);
> +}
> +
> +#define SHOW_SCALE_AVAILABLE_ATTR(ch)					\
> +static IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, S_IRUGO,	\
> +		       mxs_lradc_adc_show_scale_avail, NULL, ch)
> +
> +SHOW_SCALE_AVAILABLE_ATTR(0);
> +SHOW_SCALE_AVAILABLE_ATTR(1);
> +SHOW_SCALE_AVAILABLE_ATTR(2);
> +SHOW_SCALE_AVAILABLE_ATTR(3);
> +SHOW_SCALE_AVAILABLE_ATTR(4);
> +SHOW_SCALE_AVAILABLE_ATTR(5);
> +SHOW_SCALE_AVAILABLE_ATTR(6);
> +SHOW_SCALE_AVAILABLE_ATTR(7);
> +SHOW_SCALE_AVAILABLE_ATTR(10);
> +SHOW_SCALE_AVAILABLE_ATTR(11);
> +SHOW_SCALE_AVAILABLE_ATTR(12);
> +SHOW_SCALE_AVAILABLE_ATTR(13);
> +SHOW_SCALE_AVAILABLE_ATTR(14);
> +SHOW_SCALE_AVAILABLE_ATTR(15);
> +
> +static struct attribute *mxs_lradc_adc_attributes[] = {
> +	&iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage2_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage3_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage4_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage5_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage6_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage7_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage10_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage11_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage12_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage13_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage14_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage15_scale_available.dev_attr.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group mxs_lradc_adc_attribute_group = {
> +	.attrs = mxs_lradc_adc_attributes,
> +};
> +
> +static const struct iio_info mxs_lradc_adc_iio_info = {
> +	.driver_module		= THIS_MODULE,
> +	.read_raw		= mxs_lradc_adc_read_raw,
> +	.write_raw		= mxs_lradc_adc_write_raw,
> +	.write_raw_get_fmt	= mxs_lradc_adc_write_raw_get_fmt,
> +	.attrs			= &mxs_lradc_adc_attribute_group,
> +};
> +
> +/*
> + * IRQ Handling
> + */
Single line comment syntax preferred.
> +static irqreturn_t mxs_lradc_adc_handle_irq(int irq, void *data)
> +{
> +	struct iio_dev *iio = data;
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +	struct mxs_lradc *lradc = adc->lradc;
> +	unsigned long reg = readl(lradc->base + LRADC_CTRL1);
> +
> +	if (!(reg & mxs_lradc_irq_mask(lradc)))
> +		return IRQ_NONE;
> +
> +	if (iio_buffer_enabled(iio)) {
> +		if (reg & lradc->buffer_vchans)
> +			iio_trigger_poll(iio->trig);
> +	} else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) {
> +		complete(&adc->completion);
> +	}
> +
> +	mxs_lradc_reg_clear(lradc, reg & mxs_lradc_irq_mask(lradc),
> +			    LRADC_CTRL1);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * Trigger handling
Single line comment - Clearly it's kind of more of a heading but I'd rather
not deal with the inevitable patch converting it to the standard single line
format!
> + */
> +static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *iio = pf->indio_dev;
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +	const u32 chan_value = LRADC_CH_ACCUMULATE |
> +		((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
> +	unsigned int i, j = 0;
> +
> +	for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
> +		adc->buffer[j] = readl(adc->lradc->base + LRADC_CH(j));
> +		mxs_lradc_reg_wrt(adc->lradc, chan_value, LRADC_CH(j));
> +		adc->buffer[j] &= LRADC_CH_VALUE_MASK;
> +		adc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
> +		j++;
> +	}
> +
> +	iio_push_to_buffers_with_timestamp(iio, adc->buffer, pf->timestamp);
> +
> +	iio_trigger_notify_done(iio->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int mxs_lradc_adc_configure_trigger(struct iio_trigger *trig, bool state)
> +{
> +	struct iio_dev *iio = iio_trigger_get_drvdata(trig);
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +	const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
> +
> +	mxs_lradc_reg_wrt(adc->lradc, LRADC_DELAY_KICK, LRADC_DELAY(0) + st);
> +
> +	return 0;
> +}
> +
> +static const struct iio_trigger_ops mxs_lradc_adc_trigger_ops = {
> +	.owner = THIS_MODULE,
> +	.set_trigger_state = &mxs_lradc_adc_configure_trigger,
> +};
> +
> +static int mxs_lradc_adc_trigger_init(struct iio_dev *iio)
> +{
> +	int ret;
> +	struct iio_trigger *trig;
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +
Could simplify things ever so slightly by using devm_iio_trigger_alloc if you
were to reorder the trigger init to be before the buffer setup (which I think
should be fine).  Perhaps it is not worth the hassle...
> +	trig = iio_trigger_alloc("%s-dev%i", iio->name, iio->id);
> +	if (!trig)
> +		return -ENOMEM;
> +
> +	trig->dev.parent = adc->dev;
> +	iio_trigger_set_drvdata(trig, iio);
> +	trig->ops = &mxs_lradc_adc_trigger_ops;
> +
> +	ret = iio_trigger_register(trig);
> +	if (ret) {
> +		iio_trigger_free(trig);
> +		return ret;
> +	}
> +
> +	adc->trig = trig;
> +
> +	return 0;
> +}
> +
> +static void mxs_lradc_adc_trigger_remove(struct iio_dev *iio)
> +{
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +
> +	iio_trigger_unregister(adc->trig);
> +	iio_trigger_free(adc->trig);
> +}
> +
> +static int mxs_lradc_adc_buffer_preenable(struct iio_dev *iio)
> +{
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +	struct mxs_lradc *lradc = adc->lradc;
> +	int ret = 0, chan, ofs = 0;
> +	unsigned long enable = 0;
> +	u32 ctrl4_set = 0;
> +	u32 ctrl4_clr = 0;
> +	u32 ctrl1_irq = 0;
> +	const u32 chan_value = LRADC_CH_ACCUMULATE |
> +		((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
> +	const int len = bitmap_weight(iio->active_scan_mask,
> +			LRADC_MAX_TOTAL_CHANS);
> +
> +	if (!len)
> +		return -EINVAL;
> +
> +	/*
> +	 * Lock the driver so raw access can not be done during buffered
> +	 * operation. This simplifies the code a lot.
> +	 */
> +	ret = mutex_trylock(&adc->lock);
> +	if (!ret)
> +		return -EBUSY;
Hmm. could this use iio_dev->mlock?  That is what that particular lock is
for and it has nice wrapper functions to make it clear.
> +
> +	adc->buffer = kmalloc_array(len, sizeof(*adc->buffer), GFP_KERNEL);
> +	if (!adc->buffer) {
> +		ret = -ENOMEM;
> +		goto err_mem;
> +	}
I wonder if it's worth the hassel of doing this dynamically.  Maybe just have a
buffer that is big enough to take all possibilities as part of the adc
struct?
> +
> +	if (lradc->soc == IMX28_LRADC)
> +		mxs_lradc_reg_clear(
> +			lradc,
> +			lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
> +			LRADC_CTRL1);
> +	mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
> +
> +	for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
> +		ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
> +		ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
> +		ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
> +		mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(ofs));
> +		bitmap_set(&enable, ofs, 1);
> +		ofs++;
> +	}
> +
> +	mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
> +			    LRADC_DELAY_KICK, LRADC_DELAY(0));
> +	mxs_lradc_reg_clear(lradc, ctrl4_clr, LRADC_CTRL4);
> +	mxs_lradc_reg_set(lradc, ctrl4_set, LRADC_CTRL4);
> +	mxs_lradc_reg_set(lradc, ctrl1_irq, LRADC_CTRL1);
> +	mxs_lradc_reg_set(lradc, enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
> +			  LRADC_DELAY(0));
> +
> +	return 0;
> +
> +err_mem:
> +	mutex_unlock(&adc->lock);
> +	return ret;
> +}
> +
> +static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio)
> +{
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +	struct mxs_lradc *lradc = adc->lradc;
> +
> +	mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
> +			    LRADC_DELAY_KICK, LRADC_DELAY(0));
> +
> +	mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
> +	if (lradc->soc == IMX28_LRADC)
> +		mxs_lradc_reg_clear(
> +			lradc,
> +			lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
> +			LRADC_CTRL1);
> +
> +	kfree(adc->buffer);
> +	mutex_unlock(&adc->lock);
> +
> +	return 0;
> +}
> +
> +static bool mxs_lradc_adc_validate_scan_mask(struct iio_dev *iio,
> +					     const unsigned long *mask)
> +{
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +	struct mxs_lradc *lradc = adc->lradc;
> +	const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS);
> +	int rsvd_chans = 0;
> +	unsigned long rsvd_mask = 0;
> +
> +	if (lradc->use_touchbutton)
> +		rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
> +	if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_4WIRE)
> +		rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
> +	if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
> +		rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
> +
> +	if (lradc->use_touchbutton)
> +		rsvd_chans++;
> +	if (lradc->use_touchscreen)
> +		rsvd_chans += 2;
> +
> +	/* Test for attempts to map channels with special mode of operation. */
> +	if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
> +		return false;
> +
> +	/* Test for attempts to map more channels then available slots. */
> +	if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
> +		return false;
> +
> +	return true;
> +}
> +
> +static const struct iio_buffer_setup_ops mxs_lradc_adc_buffer_ops = {
> +	.preenable = &mxs_lradc_adc_buffer_preenable,
> +	.postenable = &iio_triggered_buffer_postenable,
> +	.predisable = &iio_triggered_buffer_predisable,
> +	.postdisable = &mxs_lradc_adc_buffer_postdisable,
> +	.validate_scan_mask = &mxs_lradc_adc_validate_scan_mask,
> +};
> +
> +/*
> + * Driver initialization
> + */
> +
> +#define MXS_ADC_CHAN(idx, chan_type, name) {			\
> +	.type = (chan_type),					\
> +	.indexed = 1,						\
> +	.scan_index = (idx),					\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |		\
> +			      BIT(IIO_CHAN_INFO_SCALE),		\
> +	.channel = (idx),					\
> +	.address = (idx),					\
> +	.scan_type = {						\
> +		.sign = 'u',					\
> +		.realbits = LRADC_RESOLUTION,			\
> +		.storagebits = 32,				\
> +	},							\
> +	.datasheet_name = (name),				\
> +}
> +
> +static const struct iio_chan_spec mx23_lradc_chan_spec[] = {
> +	MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
> +	MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
> +	MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
> +	MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
> +	MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
> +	MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
> +	MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"),
> +	MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
> +	/* Combined Temperature sensors */
> +	{
> +		.type = IIO_TEMP,
> +		.indexed = 1,
> +		.scan_index = 8,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				      BIT(IIO_CHAN_INFO_OFFSET) |
> +				      BIT(IIO_CHAN_INFO_SCALE),
> +		.channel = 8,
> +		.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
> +		.datasheet_name = "TEMP_DIE",
> +	},
> +	/* Hidden channel to keep indexes */
> +	{
> +		.type = IIO_TEMP,
> +		.indexed = 1,
> +		.scan_index = -1,
> +		.channel = 9,
> +	},
> +	MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL),
> +	MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL),
> +	MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"),
> +	MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"),
> +	MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
> +	MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
> +};
> +
> +static const struct iio_chan_spec mx28_lradc_chan_spec[] = {
> +	MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
> +	MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
> +	MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
> +	MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
> +	MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
> +	MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
> +	MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"),
> +	MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
> +	/* Combined Temperature sensors */
> +	{
> +		.type = IIO_TEMP,
> +		.indexed = 1,
> +		.scan_index = 8,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				      BIT(IIO_CHAN_INFO_OFFSET) |
> +				      BIT(IIO_CHAN_INFO_SCALE),
> +		.channel = 8,
> +		.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
> +		.datasheet_name = "TEMP_DIE",
> +	},
> +	/* Hidden channel to keep indexes */
> +	{
> +		.type = IIO_TEMP,
> +		.indexed = 1,
> +		.scan_index = -1,
> +		.channel = 9,
> +	},
> +	MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"),
> +	MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"),
> +	MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"),
> +	MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"),
> +	MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
> +	MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
> +};
> +
> +static void mxs_lradc_adc_hw_init(struct mxs_lradc_adc *adc)
> +{
> +	struct mxs_lradc *lradc = adc->lradc;
> +
> +	/* The ADC always uses DELAY CHANNEL 0. */
> +	const u32 adc_cfg =
> +		(1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
> +		(LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
> +
> +	/* Configure DELAY CHANNEL 0 for generic ADC sampling. */
> +	mxs_lradc_reg_wrt(lradc, adc_cfg, LRADC_DELAY(0));
> +
> +	/* Start internal temperature sensing. */
We start internal temperature sensing, do we ever want to stop it?
> +	mxs_lradc_reg_wrt(lradc, 0, LRADC_CTRL2);
> +}
> +
> +static void mxs_lradc_adc_hw_stop(struct mxs_lradc_adc *adc)
> +{
> +	mxs_lradc_reg_wrt(adc->lradc, 0, LRADC_DELAY(0));
> +}
> +
> +static int mxs_lradc_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mxs_lradc *lradc = dev_get_platdata(dev);
> +	struct mxs_lradc_adc *adc;
> +	struct iio_dev *iio;
> +	int ret, i, s;
> +	u64 scale_uv;
> +
> +	/* Allocate the IIO device. */
> +	iio = devm_iio_device_alloc(dev, sizeof(*adc));
> +	if (!iio) {
> +		dev_err(dev, "Failed to allocate IIO device\n");
> +		return -ENOMEM;
> +	}
> +
> +	adc = iio_priv(iio);
> +	adc->lradc = lradc;
> +	adc->dev = dev;
> +
> +	init_completion(&adc->completion);
> +	mutex_init(&adc->lock);
> +
> +	for (i = 0; i < lradc->irq_count; i++) {
> +		ret = devm_request_irq(dev, lradc->irq[i],
> +				       mxs_lradc_adc_handle_irq,
> +				       IRQF_SHARED, lradc->irq_name[i], iio);
> +		if (ret)
> +			return ret;
This seems to be getting a whole load of irq's whether or not they are actually
going to be used by the ADC?  Aren't some of these going to be typically used by
the touchscreen?
> +	}
> +
> +	platform_set_drvdata(pdev, iio);
> +
> +	iio->name = pdev->name;
> +	iio->dev.parent = dev;
> +	iio->dev.of_node = dev->parent->of_node;
> +	iio->info = &mxs_lradc_adc_iio_info;
> +	iio->modes = INDIO_DIRECT_MODE;
> +	iio->masklength = LRADC_MAX_TOTAL_CHANS;
> +
> +	if (lradc->soc == IMX23_LRADC) {
> +		iio->channels = mx23_lradc_chan_spec;
> +		iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec);
> +	} else {
> +		iio->channels = mx28_lradc_chan_spec;
> +		iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec);
> +	}
> +
> +	ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
> +					 &mxs_lradc_adc_trigger_handler,
> +					 &mxs_lradc_adc_buffer_ops);
> +	if (ret)
> +		return ret;
> +
> +	ret = mxs_lradc_adc_trigger_init(iio);
> +	if (ret)
> +		goto err_trig;
> +
> +	adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc];
> +
> +	/* Populate available ADC input ranges */
> +	for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) {
> +		for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) {
> +			/*
> +			 * [s=0] = optional divider by two disabled (default)
> +			 * [s=1] = optional divider by two enabled
> +			 *
> +			 * The scale is calculated by doing:
> +			 *   Vref >> (realbits - s)
> +			 * which multiplies by two on the second component
> +			 * of the array.
> +			 */
> +			scale_uv = ((u64)adc->vref_mv[i] * 100000000) >>
> +				   (LRADC_RESOLUTION - s);
> +			adc->scale_avail[i][s].nano =
> +					do_div(scale_uv, 100000000) * 10;
> +			adc->scale_avail[i][s].integer = scale_uv;
> +		}
> +	}
> +
> +	/* Configure the hardware. */
> +	mxs_lradc_adc_hw_init(adc);
> +
> +	/* Register IIO device. */
> +	ret = iio_device_register(iio);
> +	if (ret) {
> +		dev_err(dev, "Failed to register IIO device\n");
> +		goto err_dev;
> +	}
> +
> +	return 0;
> +
> +err_dev:
> +	mxs_lradc_adc_hw_stop(adc);
> +	mxs_lradc_adc_trigger_remove(iio);
> +err_trig:
> +	iio_triggered_buffer_cleanup(iio);
> +	return ret;
> +}
> +
> +static int mxs_lradc_adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *iio = platform_get_drvdata(pdev);
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +
> +	iio_device_unregister(iio);
> +	mxs_lradc_adc_hw_stop(adc);
> +	mxs_lradc_adc_trigger_remove(iio);
> +	iio_triggered_buffer_cleanup(iio);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver mxs_lradc_adc_driver = {
> +	.driver = {
> +		.name	= DRIVER_NAME_ADC,
> +	},
> +	.probe	= mxs_lradc_adc_probe,
> +	.remove = mxs_lradc_adc_remove,
> +};
> +
> +module_platform_driver(mxs_lradc_adc_driver);
> +
> +MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
> +MODULE_DESCRIPTION("Freescale MXS LRADC driver general purpose ADC driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME_ADC);
> +
Bonus blank line at end of file...
> +
> 

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

* Re: [PATCH 2/3] iio: adc: mxs-lradc: Add support for adc driver
@ 2016-05-01 16:38     ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2016-05-01 16:38 UTC (permalink / raw)
  To: Ksenija Stanojevic, linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	linux-input-u79uwXL29TY76Z2rM5mHXA, knaack.h-Mmb7MZpHnFY,
	lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
	marex-ynQEQJNshbs, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	harald-95f8Dae0BrPYtjvyW6yDsg

On 29/04/16 12:48, Ksenija Stanojevic wrote:
> Add mxs-lradc adc driver.
> 
> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Mostly looking good.  A few nitpicks and suggestions inline.

Jonathan
> ---
>  drivers/iio/adc/Kconfig         |  37 +-
>  drivers/iio/adc/Makefile        |   1 +
>  drivers/iio/adc/mxs-lradc-adc.c | 832 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 858 insertions(+), 12 deletions(-)
>  create mode 100644 drivers/iio/adc/mxs-lradc-adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 82c718c..50847b8 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -233,6 +233,19 @@ config IMX7D_ADC
>  	  This driver can also be built as a module. If so, the module will be
>  	  called imx7d_adc.
>  
> +config MXS_LRADC_ADC
> +	tristate "Freescale i.MX23/i.MX28 LRADC ADC"
> +	depends on MFD_MXS_LRADC
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	  Say yes here to build support for the ADC functions of the
> +	  i.MX23/i.MX28 LRADC. This includes general-purpose ADC readings,
> +	  battery voltage measurement, and die temperature measurement.
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called mxs-lradc-adc.
> +
>  config LP8788_ADC
>  	tristate "LP8788 ADC driver"
>  	depends on MFD_LP8788
> @@ -306,18 +319,18 @@ config MEN_Z188_ADC
>  	  called men_z188_adc.
>  
>  config MXS_LRADC
> -        tristate "Freescale i.MX23/i.MX28 LRADC"
> -        depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
> -        depends on INPUT
> -        select STMP_DEVICE
> -        select IIO_BUFFER
> -        select IIO_TRIGGERED_BUFFER
> -        help
> -          Say yes here to build support for i.MX23/i.MX28 LRADC convertor
> -          built into these chips.
> -
> -          To compile this driver as a module, choose M here: the
> -          module will be called mxs-lradc.
> +	tristate "Freescale i.MX23/i.MX28 LRADC"
> +	depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
> +	depends on INPUT
> +	select STMP_DEVICE
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	  Say yes here to build support for i.MX23/i.MX28 LRADC convertor
> +	  built into these chips.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called mxs-lradc.
>  
>  config NAU7802
>  	tristate "Nuvoton NAU7802 ADC driver"
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 0cb7921..ca7d6aa 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -31,6 +31,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
>  obj-$(CONFIG_MCP3422) += mcp3422.o
>  obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
>  obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
> +obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
>  obj-$(CONFIG_NAU7802) += nau7802.o
>  obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
>  obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
> diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c
> new file mode 100644
> index 0000000..793c369
> --- /dev/null
> +++ b/drivers/iio/adc/mxs-lradc-adc.c
> @@ -0,0 +1,832 @@
> +/*
> + * Freescale MXS LRADC ADC driver
> + *
> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
> + * Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>
You should probably add your own copyright as you are making substantial
improvements!

> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/mfd/mxs-lradc.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/sysfs.h>
> +
> +/*
> + * Make this runtime configurable if necessary. Currently, if the buffered mode
> + * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before
> + * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000)
> + * seconds. The result is that the samples arrive every 500mS.
> + */
> +#define LRADC_DELAY_TIMER_PER	200
> +#define LRADC_DELAY_TIMER_LOOP	5
> +
> +#define VREF_MV_BASE 1850
> +
> +static const u32 mxs_lradc_adc_vref_mv[][LRADC_MAX_TOTAL_CHANS] = {
> +	[IMX23_LRADC] = {
> +		VREF_MV_BASE,		/* CH0 */
> +		VREF_MV_BASE,		/* CH1 */
> +		VREF_MV_BASE,		/* CH2 */
> +		VREF_MV_BASE,		/* CH3 */
> +		VREF_MV_BASE,		/* CH4 */
> +		VREF_MV_BASE,		/* CH5 */
> +		VREF_MV_BASE * 2,	/* CH6 VDDIO */
> +		VREF_MV_BASE * 4,	/* CH7 VBATT */
> +		VREF_MV_BASE,		/* CH8 Temp sense 0 */
> +		VREF_MV_BASE,		/* CH9 Temp sense 1 */
> +		VREF_MV_BASE,		/* CH10 */
> +		VREF_MV_BASE,		/* CH11 */
> +		VREF_MV_BASE,		/* CH12 USB_DP */
> +		VREF_MV_BASE,		/* CH13 USB_DN */
> +		VREF_MV_BASE,		/* CH14 VBG */
> +		VREF_MV_BASE * 4,	/* CH15 VDD5V */
> +	},
> +	[IMX28_LRADC] = {
> +		VREF_MV_BASE,		/* CH0 */
> +		VREF_MV_BASE,		/* CH1 */
> +		VREF_MV_BASE,		/* CH2 */
> +		VREF_MV_BASE,		/* CH3 */
> +		VREF_MV_BASE,		/* CH4 */
> +		VREF_MV_BASE,		/* CH5 */
> +		VREF_MV_BASE,		/* CH6 */
> +		VREF_MV_BASE * 4,	/* CH7 VBATT */
> +		VREF_MV_BASE,		/* CH8 Temp sense 0 */
> +		VREF_MV_BASE,		/* CH9 Temp sense 1 */
> +		VREF_MV_BASE * 2,	/* CH10 VDDIO */
> +		VREF_MV_BASE,		/* CH11 VTH */
> +		VREF_MV_BASE * 2,	/* CH12 VDDA */
> +		VREF_MV_BASE,		/* CH13 VDDD */
> +		VREF_MV_BASE,		/* CH14 VBG */
> +		VREF_MV_BASE * 4,	/* CH15 VDD5V */
> +	},
> +};
> +
> +enum mxs_lradc_divbytwo {
> +	MXS_LRADC_DIV_DISABLED = 0,
> +	MXS_LRADC_DIV_ENABLED,
> +};
> +
> +struct mxs_lradc_scale {
> +	unsigned int		integer;
> +	unsigned int		nano;
> +};
> +
> +struct mxs_lradc_adc {
> +	struct mxs_lradc	*lradc;
> +	struct device		*dev;
> +
> +	u32			*buffer;
> +	struct iio_trigger	*trig;
> +	struct mutex		lock;
> +	struct completion	completion;
> +
> +	const u32		*vref_mv;
> +	struct mxs_lradc_scale	scale_avail[LRADC_MAX_TOTAL_CHANS][2];
> +	unsigned long		is_divided;
> +};
> +
> +/*
> + * Raw I/O operations
> + */
> +static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan,
> +				     int *val)
> +{
> +	struct mxs_lradc_adc *adc = iio_priv(iio_dev);
> +	struct mxs_lradc *lradc = adc->lradc;
> +	int ret;
> +
> +	/*
> +	 * See if there is no buffered operation in progress. If there is simply
> +	 * bail out. This can be improved to support both buffered and raw IO at
> +	 * the same time, yet the code becomes horribly complicated. Therefore I
> +	 * applied KISS principle here.
This is true in lots of devices!  Hence we often have a similar check.
> +	 */
> +	ret = mutex_trylock(&adc->lock);
> +	if (!ret)
> +		return -EBUSY;
We have standard core infrastructure for this now that should do the trick.
iio_device_claim_direct_mode() etc.
> +
> +	reinit_completion(&adc->completion);
> +
> +	/*
> +	 * No buffered operation in progress, map the channel and trigger it.
> +	 * Virtual channel 0 is always used here as the others are always not
> +	 * used if doing raw sampling.
> +	 */
> +	if (lradc->soc == IMX28_LRADC)
> +		mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0),
> +				    LRADC_CTRL1);
> +	mxs_lradc_reg_clear(lradc, 0x1, LRADC_CTRL0);
> +
> +	/* Enable / disable the divider per requirement */
> +	if (test_bit(chan, &adc->is_divided))
> +		mxs_lradc_reg_set(lradc,
> +				  1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
> +				  LRADC_CTRL2);
> +	else
> +		mxs_lradc_reg_clear(lradc,
> +				    1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
> +				    LRADC_CTRL2);
> +
> +	/* Clean the slot's previous content, then set new one. */
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(0),
> +			    LRADC_CTRL4);
> +	mxs_lradc_reg_set(lradc, chan, LRADC_CTRL4);
> +
> +	mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(0));
> +
> +	/* Enable the IRQ and start sampling the channel. */
> +	mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
> +	mxs_lradc_reg_set(lradc, BIT(0), LRADC_CTRL0);
> +
> +	/* Wait for completion on the channel, 1 second max. */
> +	ret = wait_for_completion_killable_timeout(&adc->completion, HZ);
> +	if (!ret)
> +		ret = -ETIMEDOUT;
> +	if (ret < 0)
> +		goto err;
> +
> +	/* Read the data. */
> +	*val = readl(lradc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
> +	ret = IIO_VAL_INT;
> +
> +err:
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
> +
> +	mutex_unlock(&adc->lock);
> +
> +	return ret;
> +}
> +
> +static int mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val)
> +{
> +	int ret, min, max;
> +
> +	ret = mxs_lradc_adc_read_single(iio_dev, 8, &min);
> +	if (ret != IIO_VAL_INT)
> +		return ret;
> +
> +	ret = mxs_lradc_adc_read_single(iio_dev, 9, &max);
> +	if (ret != IIO_VAL_INT)
> +		return ret;
> +
> +	*val = max - min;
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static int mxs_lradc_adc_read_raw(struct iio_dev *iio_dev,
> +			      const struct iio_chan_spec *chan,
> +			      int *val, int *val2, long m)
> +{
> +	struct mxs_lradc_adc *adc = iio_priv(iio_dev);
> +
> +	switch (m) {
> +	case IIO_CHAN_INFO_RAW:
> +		if (chan->type == IIO_TEMP)
> +			return mxs_lradc_adc_read_temp(iio_dev, val);
> +
> +		return mxs_lradc_adc_read_single(iio_dev, chan->channel, val);
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		if (chan->type == IIO_TEMP) {
> +			/* From the datasheet, we have to multiply by 1.012 and
> +			 * divide by 4
> +			 */
> +			*val = 0;
> +			*val2 = 253000;
> +			return IIO_VAL_INT_PLUS_MICRO;
> +		}
> +
> +		*val = adc->vref_mv[chan->channel];
> +		*val2 = chan->scan_type.realbits -
> +			test_bit(chan->channel, &adc->is_divided);
blank line here as you have done elsewhere...
> +		return IIO_VAL_FRACTIONAL_LOG2;
> +
> +	case IIO_CHAN_INFO_OFFSET:
> +		if (chan->type == IIO_TEMP) {
standard multiline comment syntax please.  Please fix this throughout.

/*
 * The ...
> +			/* The calculated value from the ADC is in Kelvin, we
> +			 * want Celsius for hwmon so the offset is -273.15
> +			 * The offset is applied before scaling so it is
> +			 * actually -213.15 * 4 / 1.012 = -1079.644268
> +			 */
> +			*val = -1079;
> +			*val2 = 644268;
> +
> +			return IIO_VAL_INT_PLUS_MICRO;
> +		}
> +
> +		return -EINVAL;
> +
> +	default:
> +		break;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev,
> +				   const struct iio_chan_spec *chan,
> +				   int val, int val2, long m)
> +{
> +	struct mxs_lradc_adc *adc = iio_priv(iio_dev);
> +	struct mxs_lradc_scale *scale_avail =
> +			adc->scale_avail[chan->channel];
> +	int ret;
> +
> +	ret = mutex_trylock(&adc->lock);
> +	if (!ret)
> +		return -EBUSY;
> +
> +	switch (m) {
> +	case IIO_CHAN_INFO_SCALE:
> +		ret = -EINVAL;
> +		if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
> +		    val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) {
> +			/* divider by two disabled */
> +			clear_bit(chan->channel, &adc->is_divided);
> +			ret = 0;
> +		} else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
> +			   val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) {
> +			/* divider by two enabled */
> +			set_bit(chan->channel, &adc->is_divided);
> +			ret = 0;
> +		}
> +
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	mutex_unlock(&adc->lock);
> +
> +	return ret;
> +}
> +
> +static int mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev,
> +					   const struct iio_chan_spec *chan,
> +					   long m)
> +{
> +	return IIO_VAL_INT_PLUS_NANO;
> +}
> +
> +static ssize_t mxs_lradc_adc_show_scale_avail_ch(struct device *dev,
> +						 struct device_attribute *attr,
> +						 char *buf,
> +						 int ch)
> +{
> +	struct iio_dev *iio = dev_to_iio_dev(dev);
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +	int i, len = 0;
> +
> +	for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++)
> +		len += sprintf(buf + len, "%u.%09u ",
> +			       adc->scale_avail[ch][i].integer,
> +			       adc->scale_avail[ch][i].nano);
> +
> +	len += sprintf(buf + len, "\n");
> +
> +	return len;
> +}
> +
I'm unconvinced this wrapper adds anything...
> +static ssize_t mxs_lradc_adc_show_scale_avail(struct device *dev,
> +					      struct device_attribute *attr,
> +					      char *buf)
> +{
> +	struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
> +
> +	return mxs_lradc_adc_show_scale_avail_ch(dev, attr, buf,
> +						 iio_attr->address);
> +}
> +
> +#define SHOW_SCALE_AVAILABLE_ATTR(ch)					\
> +static IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, S_IRUGO,	\
> +		       mxs_lradc_adc_show_scale_avail, NULL, ch)
> +
> +SHOW_SCALE_AVAILABLE_ATTR(0);
> +SHOW_SCALE_AVAILABLE_ATTR(1);
> +SHOW_SCALE_AVAILABLE_ATTR(2);
> +SHOW_SCALE_AVAILABLE_ATTR(3);
> +SHOW_SCALE_AVAILABLE_ATTR(4);
> +SHOW_SCALE_AVAILABLE_ATTR(5);
> +SHOW_SCALE_AVAILABLE_ATTR(6);
> +SHOW_SCALE_AVAILABLE_ATTR(7);
> +SHOW_SCALE_AVAILABLE_ATTR(10);
> +SHOW_SCALE_AVAILABLE_ATTR(11);
> +SHOW_SCALE_AVAILABLE_ATTR(12);
> +SHOW_SCALE_AVAILABLE_ATTR(13);
> +SHOW_SCALE_AVAILABLE_ATTR(14);
> +SHOW_SCALE_AVAILABLE_ATTR(15);
> +
> +static struct attribute *mxs_lradc_adc_attributes[] = {
> +	&iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage2_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage3_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage4_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage5_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage6_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage7_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage10_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage11_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage12_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage13_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage14_scale_available.dev_attr.attr,
> +	&iio_dev_attr_in_voltage15_scale_available.dev_attr.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group mxs_lradc_adc_attribute_group = {
> +	.attrs = mxs_lradc_adc_attributes,
> +};
> +
> +static const struct iio_info mxs_lradc_adc_iio_info = {
> +	.driver_module		= THIS_MODULE,
> +	.read_raw		= mxs_lradc_adc_read_raw,
> +	.write_raw		= mxs_lradc_adc_write_raw,
> +	.write_raw_get_fmt	= mxs_lradc_adc_write_raw_get_fmt,
> +	.attrs			= &mxs_lradc_adc_attribute_group,
> +};
> +
> +/*
> + * IRQ Handling
> + */
Single line comment syntax preferred.
> +static irqreturn_t mxs_lradc_adc_handle_irq(int irq, void *data)
> +{
> +	struct iio_dev *iio = data;
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +	struct mxs_lradc *lradc = adc->lradc;
> +	unsigned long reg = readl(lradc->base + LRADC_CTRL1);
> +
> +	if (!(reg & mxs_lradc_irq_mask(lradc)))
> +		return IRQ_NONE;
> +
> +	if (iio_buffer_enabled(iio)) {
> +		if (reg & lradc->buffer_vchans)
> +			iio_trigger_poll(iio->trig);
> +	} else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) {
> +		complete(&adc->completion);
> +	}
> +
> +	mxs_lradc_reg_clear(lradc, reg & mxs_lradc_irq_mask(lradc),
> +			    LRADC_CTRL1);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * Trigger handling
Single line comment - Clearly it's kind of more of a heading but I'd rather
not deal with the inevitable patch converting it to the standard single line
format!
> + */
> +static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *iio = pf->indio_dev;
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +	const u32 chan_value = LRADC_CH_ACCUMULATE |
> +		((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
> +	unsigned int i, j = 0;
> +
> +	for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
> +		adc->buffer[j] = readl(adc->lradc->base + LRADC_CH(j));
> +		mxs_lradc_reg_wrt(adc->lradc, chan_value, LRADC_CH(j));
> +		adc->buffer[j] &= LRADC_CH_VALUE_MASK;
> +		adc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
> +		j++;
> +	}
> +
> +	iio_push_to_buffers_with_timestamp(iio, adc->buffer, pf->timestamp);
> +
> +	iio_trigger_notify_done(iio->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int mxs_lradc_adc_configure_trigger(struct iio_trigger *trig, bool state)
> +{
> +	struct iio_dev *iio = iio_trigger_get_drvdata(trig);
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +	const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
> +
> +	mxs_lradc_reg_wrt(adc->lradc, LRADC_DELAY_KICK, LRADC_DELAY(0) + st);
> +
> +	return 0;
> +}
> +
> +static const struct iio_trigger_ops mxs_lradc_adc_trigger_ops = {
> +	.owner = THIS_MODULE,
> +	.set_trigger_state = &mxs_lradc_adc_configure_trigger,
> +};
> +
> +static int mxs_lradc_adc_trigger_init(struct iio_dev *iio)
> +{
> +	int ret;
> +	struct iio_trigger *trig;
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +
Could simplify things ever so slightly by using devm_iio_trigger_alloc if you
were to reorder the trigger init to be before the buffer setup (which I think
should be fine).  Perhaps it is not worth the hassle...
> +	trig = iio_trigger_alloc("%s-dev%i", iio->name, iio->id);
> +	if (!trig)
> +		return -ENOMEM;
> +
> +	trig->dev.parent = adc->dev;
> +	iio_trigger_set_drvdata(trig, iio);
> +	trig->ops = &mxs_lradc_adc_trigger_ops;
> +
> +	ret = iio_trigger_register(trig);
> +	if (ret) {
> +		iio_trigger_free(trig);
> +		return ret;
> +	}
> +
> +	adc->trig = trig;
> +
> +	return 0;
> +}
> +
> +static void mxs_lradc_adc_trigger_remove(struct iio_dev *iio)
> +{
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +
> +	iio_trigger_unregister(adc->trig);
> +	iio_trigger_free(adc->trig);
> +}
> +
> +static int mxs_lradc_adc_buffer_preenable(struct iio_dev *iio)
> +{
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +	struct mxs_lradc *lradc = adc->lradc;
> +	int ret = 0, chan, ofs = 0;
> +	unsigned long enable = 0;
> +	u32 ctrl4_set = 0;
> +	u32 ctrl4_clr = 0;
> +	u32 ctrl1_irq = 0;
> +	const u32 chan_value = LRADC_CH_ACCUMULATE |
> +		((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
> +	const int len = bitmap_weight(iio->active_scan_mask,
> +			LRADC_MAX_TOTAL_CHANS);
> +
> +	if (!len)
> +		return -EINVAL;
> +
> +	/*
> +	 * Lock the driver so raw access can not be done during buffered
> +	 * operation. This simplifies the code a lot.
> +	 */
> +	ret = mutex_trylock(&adc->lock);
> +	if (!ret)
> +		return -EBUSY;
Hmm. could this use iio_dev->mlock?  That is what that particular lock is
for and it has nice wrapper functions to make it clear.
> +
> +	adc->buffer = kmalloc_array(len, sizeof(*adc->buffer), GFP_KERNEL);
> +	if (!adc->buffer) {
> +		ret = -ENOMEM;
> +		goto err_mem;
> +	}
I wonder if it's worth the hassel of doing this dynamically.  Maybe just have a
buffer that is big enough to take all possibilities as part of the adc
struct?
> +
> +	if (lradc->soc == IMX28_LRADC)
> +		mxs_lradc_reg_clear(
> +			lradc,
> +			lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
> +			LRADC_CTRL1);
> +	mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
> +
> +	for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
> +		ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
> +		ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
> +		ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
> +		mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(ofs));
> +		bitmap_set(&enable, ofs, 1);
> +		ofs++;
> +	}
> +
> +	mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
> +			    LRADC_DELAY_KICK, LRADC_DELAY(0));
> +	mxs_lradc_reg_clear(lradc, ctrl4_clr, LRADC_CTRL4);
> +	mxs_lradc_reg_set(lradc, ctrl4_set, LRADC_CTRL4);
> +	mxs_lradc_reg_set(lradc, ctrl1_irq, LRADC_CTRL1);
> +	mxs_lradc_reg_set(lradc, enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
> +			  LRADC_DELAY(0));
> +
> +	return 0;
> +
> +err_mem:
> +	mutex_unlock(&adc->lock);
> +	return ret;
> +}
> +
> +static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio)
> +{
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +	struct mxs_lradc *lradc = adc->lradc;
> +
> +	mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
> +			    LRADC_DELAY_KICK, LRADC_DELAY(0));
> +
> +	mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
> +	if (lradc->soc == IMX28_LRADC)
> +		mxs_lradc_reg_clear(
> +			lradc,
> +			lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
> +			LRADC_CTRL1);
> +
> +	kfree(adc->buffer);
> +	mutex_unlock(&adc->lock);
> +
> +	return 0;
> +}
> +
> +static bool mxs_lradc_adc_validate_scan_mask(struct iio_dev *iio,
> +					     const unsigned long *mask)
> +{
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +	struct mxs_lradc *lradc = adc->lradc;
> +	const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS);
> +	int rsvd_chans = 0;
> +	unsigned long rsvd_mask = 0;
> +
> +	if (lradc->use_touchbutton)
> +		rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
> +	if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_4WIRE)
> +		rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
> +	if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
> +		rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
> +
> +	if (lradc->use_touchbutton)
> +		rsvd_chans++;
> +	if (lradc->use_touchscreen)
> +		rsvd_chans += 2;
> +
> +	/* Test for attempts to map channels with special mode of operation. */
> +	if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
> +		return false;
> +
> +	/* Test for attempts to map more channels then available slots. */
> +	if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
> +		return false;
> +
> +	return true;
> +}
> +
> +static const struct iio_buffer_setup_ops mxs_lradc_adc_buffer_ops = {
> +	.preenable = &mxs_lradc_adc_buffer_preenable,
> +	.postenable = &iio_triggered_buffer_postenable,
> +	.predisable = &iio_triggered_buffer_predisable,
> +	.postdisable = &mxs_lradc_adc_buffer_postdisable,
> +	.validate_scan_mask = &mxs_lradc_adc_validate_scan_mask,
> +};
> +
> +/*
> + * Driver initialization
> + */
> +
> +#define MXS_ADC_CHAN(idx, chan_type, name) {			\
> +	.type = (chan_type),					\
> +	.indexed = 1,						\
> +	.scan_index = (idx),					\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |		\
> +			      BIT(IIO_CHAN_INFO_SCALE),		\
> +	.channel = (idx),					\
> +	.address = (idx),					\
> +	.scan_type = {						\
> +		.sign = 'u',					\
> +		.realbits = LRADC_RESOLUTION,			\
> +		.storagebits = 32,				\
> +	},							\
> +	.datasheet_name = (name),				\
> +}
> +
> +static const struct iio_chan_spec mx23_lradc_chan_spec[] = {
> +	MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
> +	MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
> +	MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
> +	MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
> +	MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
> +	MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
> +	MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"),
> +	MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
> +	/* Combined Temperature sensors */
> +	{
> +		.type = IIO_TEMP,
> +		.indexed = 1,
> +		.scan_index = 8,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				      BIT(IIO_CHAN_INFO_OFFSET) |
> +				      BIT(IIO_CHAN_INFO_SCALE),
> +		.channel = 8,
> +		.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
> +		.datasheet_name = "TEMP_DIE",
> +	},
> +	/* Hidden channel to keep indexes */
> +	{
> +		.type = IIO_TEMP,
> +		.indexed = 1,
> +		.scan_index = -1,
> +		.channel = 9,
> +	},
> +	MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL),
> +	MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL),
> +	MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"),
> +	MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"),
> +	MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
> +	MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
> +};
> +
> +static const struct iio_chan_spec mx28_lradc_chan_spec[] = {
> +	MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
> +	MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
> +	MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
> +	MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
> +	MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
> +	MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
> +	MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"),
> +	MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
> +	/* Combined Temperature sensors */
> +	{
> +		.type = IIO_TEMP,
> +		.indexed = 1,
> +		.scan_index = 8,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				      BIT(IIO_CHAN_INFO_OFFSET) |
> +				      BIT(IIO_CHAN_INFO_SCALE),
> +		.channel = 8,
> +		.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
> +		.datasheet_name = "TEMP_DIE",
> +	},
> +	/* Hidden channel to keep indexes */
> +	{
> +		.type = IIO_TEMP,
> +		.indexed = 1,
> +		.scan_index = -1,
> +		.channel = 9,
> +	},
> +	MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"),
> +	MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"),
> +	MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"),
> +	MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"),
> +	MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
> +	MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
> +};
> +
> +static void mxs_lradc_adc_hw_init(struct mxs_lradc_adc *adc)
> +{
> +	struct mxs_lradc *lradc = adc->lradc;
> +
> +	/* The ADC always uses DELAY CHANNEL 0. */
> +	const u32 adc_cfg =
> +		(1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
> +		(LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
> +
> +	/* Configure DELAY CHANNEL 0 for generic ADC sampling. */
> +	mxs_lradc_reg_wrt(lradc, adc_cfg, LRADC_DELAY(0));
> +
> +	/* Start internal temperature sensing. */
We start internal temperature sensing, do we ever want to stop it?
> +	mxs_lradc_reg_wrt(lradc, 0, LRADC_CTRL2);
> +}
> +
> +static void mxs_lradc_adc_hw_stop(struct mxs_lradc_adc *adc)
> +{
> +	mxs_lradc_reg_wrt(adc->lradc, 0, LRADC_DELAY(0));
> +}
> +
> +static int mxs_lradc_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mxs_lradc *lradc = dev_get_platdata(dev);
> +	struct mxs_lradc_adc *adc;
> +	struct iio_dev *iio;
> +	int ret, i, s;
> +	u64 scale_uv;
> +
> +	/* Allocate the IIO device. */
> +	iio = devm_iio_device_alloc(dev, sizeof(*adc));
> +	if (!iio) {
> +		dev_err(dev, "Failed to allocate IIO device\n");
> +		return -ENOMEM;
> +	}
> +
> +	adc = iio_priv(iio);
> +	adc->lradc = lradc;
> +	adc->dev = dev;
> +
> +	init_completion(&adc->completion);
> +	mutex_init(&adc->lock);
> +
> +	for (i = 0; i < lradc->irq_count; i++) {
> +		ret = devm_request_irq(dev, lradc->irq[i],
> +				       mxs_lradc_adc_handle_irq,
> +				       IRQF_SHARED, lradc->irq_name[i], iio);
> +		if (ret)
> +			return ret;
This seems to be getting a whole load of irq's whether or not they are actually
going to be used by the ADC?  Aren't some of these going to be typically used by
the touchscreen?
> +	}
> +
> +	platform_set_drvdata(pdev, iio);
> +
> +	iio->name = pdev->name;
> +	iio->dev.parent = dev;
> +	iio->dev.of_node = dev->parent->of_node;
> +	iio->info = &mxs_lradc_adc_iio_info;
> +	iio->modes = INDIO_DIRECT_MODE;
> +	iio->masklength = LRADC_MAX_TOTAL_CHANS;
> +
> +	if (lradc->soc == IMX23_LRADC) {
> +		iio->channels = mx23_lradc_chan_spec;
> +		iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec);
> +	} else {
> +		iio->channels = mx28_lradc_chan_spec;
> +		iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec);
> +	}
> +
> +	ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
> +					 &mxs_lradc_adc_trigger_handler,
> +					 &mxs_lradc_adc_buffer_ops);
> +	if (ret)
> +		return ret;
> +
> +	ret = mxs_lradc_adc_trigger_init(iio);
> +	if (ret)
> +		goto err_trig;
> +
> +	adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc];
> +
> +	/* Populate available ADC input ranges */
> +	for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) {
> +		for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) {
> +			/*
> +			 * [s=0] = optional divider by two disabled (default)
> +			 * [s=1] = optional divider by two enabled
> +			 *
> +			 * The scale is calculated by doing:
> +			 *   Vref >> (realbits - s)
> +			 * which multiplies by two on the second component
> +			 * of the array.
> +			 */
> +			scale_uv = ((u64)adc->vref_mv[i] * 100000000) >>
> +				   (LRADC_RESOLUTION - s);
> +			adc->scale_avail[i][s].nano =
> +					do_div(scale_uv, 100000000) * 10;
> +			adc->scale_avail[i][s].integer = scale_uv;
> +		}
> +	}
> +
> +	/* Configure the hardware. */
> +	mxs_lradc_adc_hw_init(adc);
> +
> +	/* Register IIO device. */
> +	ret = iio_device_register(iio);
> +	if (ret) {
> +		dev_err(dev, "Failed to register IIO device\n");
> +		goto err_dev;
> +	}
> +
> +	return 0;
> +
> +err_dev:
> +	mxs_lradc_adc_hw_stop(adc);
> +	mxs_lradc_adc_trigger_remove(iio);
> +err_trig:
> +	iio_triggered_buffer_cleanup(iio);
> +	return ret;
> +}
> +
> +static int mxs_lradc_adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *iio = platform_get_drvdata(pdev);
> +	struct mxs_lradc_adc *adc = iio_priv(iio);
> +
> +	iio_device_unregister(iio);
> +	mxs_lradc_adc_hw_stop(adc);
> +	mxs_lradc_adc_trigger_remove(iio);
> +	iio_triggered_buffer_cleanup(iio);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver mxs_lradc_adc_driver = {
> +	.driver = {
> +		.name	= DRIVER_NAME_ADC,
> +	},
> +	.probe	= mxs_lradc_adc_probe,
> +	.remove = mxs_lradc_adc_remove,
> +};
> +
> +module_platform_driver(mxs_lradc_adc_driver);
> +
> +MODULE_AUTHOR("Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>");
> +MODULE_DESCRIPTION("Freescale MXS LRADC driver general purpose ADC driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME_ADC);
> +
Bonus blank line at end of file...
> +
> 

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
  2016-04-29 11:49 ` [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen Ksenija Stanojevic
  2016-04-29 13:22   ` Marek Vasut
  2016-04-29 23:36     ` Dmitry Torokhov
@ 2016-05-01 16:47   ` Jonathan Cameron
  2016-05-28 17:45       ` Ksenija Stanojević
  2 siblings, 1 reply; 40+ messages in thread
From: Jonathan Cameron @ 2016-05-01 16:47 UTC (permalink / raw)
  To: Ksenija Stanojevic, linux-kernel
  Cc: lee.jones, dmitry.torokhov, linux-input, knaack.h, lars, pmeerw,
	marex, linux-iio, harald

On 29/04/16 12:49, Ksenija Stanojevic wrote:
> Add mxs-lradc touchscreen driver.
> 
> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
The only real thing this raises for me is why we are grabbing IRQs that I
don't think this driver even cares about...

Jonathan
> ---
>  drivers/input/touchscreen/Kconfig        |  14 +-
>  drivers/input/touchscreen/Makefile       |   1 +
>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
>  3 files changed, 742 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 8ecdc38..d614d248 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
>  	depends on SH_HP6XX && SH_ADC
>  	help
>  	  Say Y here if you have a HP Jornada 620/660/680/690 and want to
> -          support the built-in touchscreen.
> +	  support the built-in touchscreen.
>  
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called hp680_ts_input.
> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
>  	  This enables support for the Philips UCB1400 touchscreen interface.
>  	  The UCB1400 is an AC97 audio codec.  The touchscreen interface
>  	  will be initialized only after the ALSA subsystem has been
> -	  brought up and the UCB1400 detected.  You therefore have to
> +	  brought up and the UCB1400 detected.	You therefore have to
>  	  configure ALSA support as well (either built-in or modular,
>  	  independently of whether this driver is itself built-in or
>  	  modular) for this driver to work.
> @@ -842,6 +842,16 @@ config TOUCHSCREEN_MX25
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called fsl-imx25-tcq.
>  
> +config TOUCHSCREEN_MXS_LRADC
> +	tristate "Freescale i.MX23/i.MX28 LRADC touchscreen"
> +	depends on MFD_MXS_LRADC
> +	help
> +	  Say Y here if you have a touchscreen connected to the low-resolution
> +	  analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor.
> +
> +	  To compile this driver as a module, choose M here: the module will be
> +	  called mxs-lradc-ts.
> +
>  config TOUCHSCREEN_MC13783
>  	tristate "Freescale MC13783 touchscreen input driver"
>  	depends on MFD_MC13XXX
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index f42975e..513a6ff 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MMS114)	+= mms114.o
>  obj-$(CONFIG_TOUCHSCREEN_MTOUCH)	+= mtouch.o
>  obj-$(CONFIG_TOUCHSCREEN_MK712)		+= mk712.o
> +obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC)	+= mxs-lradc-ts.o
>  obj-$(CONFIG_TOUCHSCREEN_HP600)		+= hp680_ts_input.o
>  obj-$(CONFIG_TOUCHSCREEN_HP7XX)		+= jornada720_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO)	+= ipaq-micro-ts.o
> diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c
> new file mode 100644
> index 0000000..27abb8e
> --- /dev/null
> +++ b/drivers/input/touchscreen/mxs-lradc-ts.c
> @@ -0,0 +1,729 @@
> +/*
> + * Freescale MXS LRADC driver
> + *
> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
> + * Marek Vasut <marex@denx.de>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/mfd/mxs-lradc.h>
> +#include <linux/platform_device.h>
> +
> +/*
> + * Touchscreen handling
> + */
> +enum mxs_lradc_ts_plate {
> +	LRADC_TOUCH = 0,
> +	LRADC_SAMPLE_X,
> +	LRADC_SAMPLE_Y,
> +	LRADC_SAMPLE_PRESSURE,
> +	LRADC_SAMPLE_VALID,
> +};
> +
> +struct mxs_lradc_ts {
> +	struct mxs_lradc	*lradc;
> +	struct device		*dev;
> +	/*
> +	 * When the touchscreen is enabled, we give it two private virtual
> +	 * channels: #6 and #7. This means that only 6 virtual channels (instead
> +	 * of 8) will be available for buffered capture.
> +	 */
> +#define TOUCHSCREEN_VCHANNEL1		7
> +#define TOUCHSCREEN_VCHANNEL2		6
> +
> +	struct input_dev	*ts_input;
> +
> +	enum mxs_lradc_ts_plate	cur_plate; /* state machine */
> +	bool			ts_valid;
> +	unsigned		ts_x_pos;
> +	unsigned		ts_y_pos;
> +	unsigned		ts_pressure;
> +
> +	/* handle touchscreen's physical behaviour */
> +	/* samples per coordinate */
> +	unsigned		over_sample_cnt;
> +	/* time clocks between samples */
> +	unsigned		over_sample_delay;
> +	/* time in clocks to wait after the plates where switched */
> +	unsigned		settling_delay;
> +};
> +
> +static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL0_MX23_PLATE_MASK;
> +	return LRADC_CTRL0_MX28_PLATE_MASK;
> +}
> +
> +static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
> +	return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE;
> +}
> +
> +static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM;
> +	return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW;
> +}
> +
> +static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM;
> +	return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW;
> +}
> +
> +static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc)
> +{
> +	if (lradc->soc == IMX23_LRADC)
> +		return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM;
> +	return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW;
> +}
> +
> +static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
> +{
> +	return !!(readl(lradc->base + LRADC_STATUS) &
> +					LRADC_STATUS_TOUCH_DETECT_RAW);
> +}
> +
> +static void mxs_lradc_map_ts_channel(struct mxs_lradc *lradc, unsigned vch,
> +				     unsigned ch)
> +{
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
> +			    LRADC_CTRL4);
> +	mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4);
> +}
> +
> +static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned ch)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	/*
> +	 * prepare for oversampling conversion
> +	 *
> +	 * from the datasheet:
> +	 * "The ACCUMULATE bit in the appropriate channel register
> +	 * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
> +	 * otherwise, the IRQs will not fire."
> +	 */
> +	mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE |
> +			  LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1),
> +			  LRADC_CH(ch));
> +
> +	/* from the datasheet:
> +	 * "Software must clear this register in preparation for a
> +	 * multi-cycle accumulation.
> +	 */
> +	mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch));
> +
> +	/*
> +	 * prepare the delay/loop unit according to the oversampling count
> +	 *
> +	 * from the datasheet:
> +	 * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
> +	 * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
> +	 * the LRADC will not trigger the delay group."
> +	 */
> +	mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
> +			  LRADC_DELAY_TRIGGER_DELAYS(0) |
> +			  LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
> +			  LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
> +			  LRADC_DELAY(3));
> +
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1);
> +
> +	/*
> +	 * after changing the touchscreen plates setting
> +	 * the signals need some initial time to settle. Start the
> +	 * SoC's delay unit and start the conversion later
> +	 * and automatically.
> +	 */
> +	mxs_lradc_reg_wrt(
> +		lradc,
> +		LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
> +		LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
> +		LRADC_DELAY_KICK |
> +		LRADC_DELAY_DELAY(ts->settling_delay),
> +		LRADC_DELAY(2));
> +}
> +
> +/*
> + * Pressure detection is special:
> + * We want to do both required measurements for the pressure detection in
> + * one turn. Use the hardware features to chain both conversions and let the
> + * hardware report one interrupt if both conversions are done
> + */
> +static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts, unsigned ch1,
> +					unsigned ch2)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +	u32 reg;
> +
> +	/*
> +	 * prepare for oversampling conversion
> +	 *
> +	 * from the datasheet:
> +	 * "The ACCUMULATE bit in the appropriate channel register
> +	 * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
> +	 * otherwise, the IRQs will not fire."
> +	 */
> +	reg = LRADC_CH_ACCUMULATE |
> +		LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1);
> +	mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
> +	mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
> +
> +	/* from the datasheet:
> +	 * "Software must clear this register in preparation for a
> +	 * multi-cycle accumulation.
> +	 */
> +	mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1));
> +	mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2));
> +
> +	/* prepare the delay/loop unit according to the oversampling count */
> +	mxs_lradc_reg_wrt(
> +		    lradc,
> +		    LRADC_DELAY_TRIGGER(1 << ch1) |
> +		    LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */
> +		    LRADC_DELAY_TRIGGER_DELAYS(0) |
> +		    LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
> +		    LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
> +		    LRADC_DELAY(3));
> +
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1);
> +
> +	/*
> +	 * after changing the touchscreen plates setting
> +	 * the signals need some initial time to settle. Start the
> +	 * SoC's delay unit and start the conversion later
> +	 * and automatically.
> +	 */
> +	mxs_lradc_reg_wrt(
> +		lradc,
> +		LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
> +		LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
> +		LRADC_DELAY_KICK |
> +		LRADC_DELAY_DELAY(ts->settling_delay), LRADC_DELAY(2));
> +}
> +
> +static unsigned mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts,
> +					      unsigned channel)
> +{
> +	u32 reg;
> +	unsigned num_samples, val;
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	reg = readl(lradc->base + LRADC_CH(channel));
> +	if (reg & LRADC_CH_ACCUMULATE)
> +		num_samples = ts->over_sample_cnt;
> +	else
> +		num_samples = 1;
> +
> +	val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
> +	return val / num_samples;
> +}
> +
> +static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts,
> +					   unsigned ch1, unsigned ch2)
> +{
> +	u32 reg, mask;
> +	unsigned pressure, m1, m2;
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
> +	reg = readl(lradc->base + LRADC_CTRL1) & mask;
> +
> +	while (reg != mask) {
> +		reg = readl(lradc->base + LRADC_CTRL1) & mask;
> +		dev_dbg(ts->dev, "One channel is still busy: %X\n", reg);
> +	}
> +
> +	m1 = mxs_lradc_ts_read_raw_channel(ts, ch1);
> +	m2 = mxs_lradc_ts_read_raw_channel(ts, ch2);
> +
> +	if (m2 == 0) {
> +		dev_warn(ts->dev, "Cannot calculate pressure\n");
> +		return 1 << (LRADC_RESOLUTION - 1);
> +	}
> +
> +	/* simply scale the value from 0 ... max ADC resolution */
> +	pressure = m1;
> +	pressure *= (1 << LRADC_RESOLUTION);
> +	pressure /= m2;
> +
> +	dev_dbg(ts->dev, "Pressure = %u\n", pressure);
> +	return pressure;
> +}
> +
> +#define TS_CH_XP 2
> +#define TS_CH_YP 3
> +#define TS_CH_XM 4
> +#define TS_CH_YM 5
> +
> +/*
> + * YP(open)--+-------------+
> + *	     |		   |--+
> + *	     |		   |  |
> + *    YM(-)--+-------------+  |
> + *	       +--------------+
> + *	       |	      |
> + *	   XP(weak+)	    XM(open)
> + *
> + * "weak+" means 200k Ohm VDDIO
> + * (-) means GND
> + */
> +static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	/*
> +	 * In order to detect a touch event the 'touch detect enable' bit
> +	 * enables:
> +	 *  - a weak pullup to the X+ connector
> +	 *  - a strong ground at the Y- connector
> +	 */
> +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
> +	mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc),
> +			  LRADC_CTRL0);
> +}
> +
> +/*
> + * YP(meas)--+-------------+
> + *	     |		   |--+
> + *	     |		   |  |
> + * YM(open)--+-------------+  |
> + *	       +--------------+
> + *	       |	      |
> + *	     XP(+)	    XM(-)
> + *
> + * (+) means here 1.85 V
> + * (-) means here GND
> + */
> +static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
> +	mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
> +
> +	ts->cur_plate = LRADC_SAMPLE_X;
> +	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
> +	mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
> +}
> +
> +/*
> + *   YP(+)--+-------------+
> + *	    |		  |--+
> + *	    |		  |  |
> + *   YM(-)--+-------------+  |
> + *	      +--------------+
> + *	      |		     |
> + *	   XP(open)	   XM(meas)
> + *
> + * (+) means here 1.85 V
> + * (-) means here GND
> + */
> +static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
> +	mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
> +
> +	ts->cur_plate = LRADC_SAMPLE_Y;
> +	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
> +	mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
> +}
> +
> +/*
> + *    YP(+)--+-------------+
> + *	     |		   |--+
> + *	     |		   |  |
> + * YM(meas)--+-------------+  |
> + *	       +--------------+
> + *	       |	      |
> + *	    XP(meas)	    XM(-)
> + *
> + * (+) means here 1.85 V
> + * (-) means here GND
> + */
> +static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
> +	mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
> +
> +	ts->cur_plate = LRADC_SAMPLE_PRESSURE;
> +	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
> +	mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
> +	mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2,
> +				    TOUCHSCREEN_VCHANNEL1);
> +}
> +
> +static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts)
> +{
> +	mxs_lradc_setup_touch_detection(ts);
> +
> +	ts->cur_plate = LRADC_TOUCH;
> +	mxs_lradc_reg_clear(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
> +			    LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
> +	mxs_lradc_reg_set(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
> +			  LRADC_CTRL1);
> +}
> +
> +static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts)
> +{
> +	mxs_lradc_reg_clear(ts->lradc,
> +			    LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
> +			    LRADC_CTRL1);
> +	mxs_lradc_reg_set(ts->lradc,
> +			  LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1),
> +			  LRADC_CTRL1);
> +	/*
> +	 * start with the Y-pos, because it uses nearly the same plate
> +	 * settings like the touch detection
> +	 */
> +	mxs_lradc_prepare_y_pos(ts);
> +}
> +
> +static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts)
> +{
> +	input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos);
> +	input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos);
> +	input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure);
> +	input_report_key(ts->ts_input, BTN_TOUCH, 1);
> +	input_sync(ts->ts_input);
> +}
> +
> +static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mxs_lradc_setup_touch_detection(ts);
> +	ts->cur_plate = LRADC_SAMPLE_VALID;
> +	/*
> +	 * start a dummy conversion to burn time to settle the signals
> +	 * note: we are not interested in the conversion's value
> +	 */
> +	mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
> +	mxs_lradc_reg_clear(lradc,
> +			    LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
> +			    LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2),
> +			    LRADC_CTRL1);
> +	mxs_lradc_reg_wrt(
> +		    lradc,
> +		    LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
> +		    LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
> +		    LRADC_DELAY(2));
> +}
> +
> +/*
> + * in order to avoid false measurements, report only samples where
> + * the surface is still touched after the position measurement
> + */
> +static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	/* if it is still touched, report the sample */
> +	if (valid && mxs_lradc_check_touch_event(lradc)) {
> +		ts->ts_valid = true;
> +		mxs_lradc_report_ts_event(ts);
> +	}
> +
> +	/* if it is even still touched, continue with the next measurement */
> +	if (mxs_lradc_check_touch_event(lradc)) {
> +		mxs_lradc_prepare_y_pos(ts);
> +		return;
> +	}
> +
> +	if (ts->ts_valid) {
> +		/* signal the release */
> +		ts->ts_valid = false;
> +		input_report_key(ts->ts_input, BTN_TOUCH, 0);
> +		input_sync(ts->ts_input);
> +	}
> +
> +	/* if it is released, wait for the next touch via IRQ */
> +	ts->cur_plate = LRADC_TOUCH;
> +	mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
> +	mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
> +	mxs_lradc_reg_clear(lradc,
> +			    LRADC_CTRL1_TOUCH_DETECT_IRQ |
> +			    LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
> +			    LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1),
> +			    LRADC_CTRL1);
> +	mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
> +}
> +
> +/* touchscreen's state machine */
> +static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	switch (ts->cur_plate) {
> +	case LRADC_TOUCH:
> +		if (mxs_lradc_check_touch_event(lradc))
> +			mxs_lradc_start_touch_event(ts);
> +		mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
> +				    LRADC_CTRL1);
> +		return;
> +
> +	case LRADC_SAMPLE_Y:
> +		ts->ts_y_pos =
> +		    mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
> +		mxs_lradc_prepare_x_pos(ts);
> +		return;
> +
> +	case LRADC_SAMPLE_X:
> +		ts->ts_x_pos =
> +		    mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
> +		mxs_lradc_prepare_pressure(ts);
> +		return;
> +
> +	case LRADC_SAMPLE_PRESSURE:
> +		ts->ts_pressure =
> +		    mxs_lradc_read_ts_pressure(ts,
> +					       TOUCHSCREEN_VCHANNEL2,
> +					       TOUCHSCREEN_VCHANNEL1);
> +		mxs_lradc_complete_touch_event(ts);
> +		return;
> +
> +	case LRADC_SAMPLE_VALID:
> +		mxs_lradc_finish_touch_event(ts, 1);
> +		break;
> +	}
> +}
> +
> +/*
> + * IRQ Handling
> + */
> +static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data)
> +{
> +	struct mxs_lradc_ts *ts = data;
> +	struct mxs_lradc *lradc = ts->lradc;
> +	unsigned long reg = readl(lradc->base + LRADC_CTRL1);
> +	u32 clr_irq = mxs_lradc_irq_mask(lradc);
> +	const u32 ts_irq_mask =
> +		LRADC_CTRL1_TOUCH_DETECT_IRQ |
> +		LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
> +		LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
> +
> +	if (!(reg & mxs_lradc_irq_mask(lradc)))
> +		return IRQ_NONE;
> +
> +	if (lradc->use_touchscreen && (reg & ts_irq_mask)) {
> +		mxs_lradc_handle_touch(ts);
> +
> +		/* Make sure we don't clear the next conversion's interrupt. */
> +		clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
> +				LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
> +		mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int mxs_lradc_ts_open(struct input_dev *dev)
> +{
> +	struct mxs_lradc_ts *ts = input_get_drvdata(dev);
> +
> +	/* Enable the touch-detect circuitry. */
> +	mxs_lradc_enable_touch_detection(ts);
> +
> +	return 0;
> +}
> +
> +static void mxs_lradc_disable_ts(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	/* stop all interrupts from firing */
> +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
> +		LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
> +		LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
> +
> +	/* Power-down touchscreen touch-detect circuitry. */
> +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
> +}
> +
> +static void mxs_lradc_ts_close(struct input_dev *dev)
> +{
> +	struct mxs_lradc_ts *ts = input_get_drvdata(dev);
> +
> +	mxs_lradc_disable_ts(ts);
> +}
> +
> +static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts)
> +{
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	/* Configure the touchscreen type */
> +	if (lradc->soc == IMX28_LRADC) {
> +		mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
> +				    LRADC_CTRL0);
> +
> +		if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
> +			mxs_lradc_reg_set(lradc,
> +					  LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
> +					  LRADC_CTRL0);
> +	}
> +}
> +
> +static void mxs_lradc_ts_hw_stop(struct mxs_lradc_ts *ts)
> +{
> +	int i;
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	mxs_lradc_reg_clear(lradc,
> +			lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
> +			LRADC_CTRL1);
> +
> +	for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++)
> +		mxs_lradc_reg_wrt(ts->lradc, 0, LRADC_DELAY(i));
> +}
> +
> +static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts)
> +{
> +	struct input_dev *input = ts->ts_input;
> +	struct device *dev = ts->dev;
> +	struct mxs_lradc *lradc = ts->lradc;
> +
> +	if (!lradc->use_touchscreen)
> +		return 0;
> +
> +	input->name = DRIVER_NAME_TS;
> +	input->id.bustype = BUS_HOST;
> +	input->dev.parent = dev;
> +	input->open = mxs_lradc_ts_open;
> +	input->close = mxs_lradc_ts_close;
> +
> +	__set_bit(EV_ABS, input->evbit);
> +	__set_bit(EV_KEY, input->evbit);
> +	__set_bit(BTN_TOUCH, input->keybit);
> +	__set_bit(INPUT_PROP_DIRECT, input->propbit);
> +	input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
> +	input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
> +	input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
> +			     0, 0);
> +
> +	ts->ts_input = input;
> +	input_set_drvdata(input, ts);
> +
> +	return input_register_device(input);
> +}
> +
> +static int mxs_lradc_ts_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *node = dev->parent->of_node;
> +	struct mxs_lradc *lradc = dev_get_platdata(dev);
> +	struct mxs_lradc_ts *ts;
> +	struct input_dev *input;
> +	int touch_ret, ret, i;
> +	u32 ts_wires = 0, adapt;
> +
> +	ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
> +	input = devm_input_allocate_device(dev);
> +	if (!ts || !input)
> +		return -ENOMEM;
> +
> +	ts->lradc = lradc;
> +	ts->dev = dev;
> +	ts->ts_input = input;
> +	platform_set_drvdata(pdev, ts);
> +	input_set_drvdata(input, ts);
> +
> +	touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
> +					 &ts_wires);
> +
> +	if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) {
> +		ts->over_sample_cnt = 4;
> +	} else {
> +		if (adapt < 1 || adapt > 32) {
> +			dev_err(ts->dev, "Invalid sample count (%u)\n",
> +				adapt);
> +			touch_ret = -EINVAL;
> +		} else {
> +			ts->over_sample_cnt = adapt;
> +		}
> +	}
> +
> +	if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) {
> +		ts->over_sample_delay = 2;
> +	} else {
> +		if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) {
> +			dev_err(ts->dev, "Invalid sample delay (%u)\n",
> +				adapt);
> +			touch_ret = -EINVAL;
> +		} else {
> +			ts->over_sample_delay = adapt;
> +		}
> +	}
> +
> +	if (of_property_read_u32(node, "fsl,settling", &adapt)) {
> +		ts->settling_delay = 10;
> +	} else {
> +		if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) {
> +			dev_err(ts->dev, "Invalid settling delay (%u)\n",
> +				adapt);
> +			touch_ret = -EINVAL;
> +		} else {
> +			ts->settling_delay = adapt;
> +		}
> +	}
> +
> +	mxs_lradc_ts_hw_init(ts);
> +	for (i = 0; i < lradc->irq_count; i++) {
> +		ret = devm_request_irq(dev, lradc->irq[i],
> +				       mxs_lradc_ts_handle_irq,
> +				       IRQF_SHARED, lradc->irq_name[i], ts);
As with the adc driver, are we actually using all of these?  I'd prefer we
only grab the ones that are actually relevant.
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (!touch_ret) {
> +		ret = mxs_lradc_ts_register(ts);
> +		if (!ret)
> +			goto err_ts_register;
> +	}
> +
> +	return 0;
> +
> +err_ts_register:
> +	mxs_lradc_ts_hw_stop(ts);
> +	return ret;
> +}
> +
> +static int mxs_lradc_ts_remove(struct platform_device *pdev)
> +{
> +	struct mxs_lradc_ts *ts = platform_get_drvdata(pdev);
> +
> +	mxs_lradc_ts_hw_stop(ts);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver mxs_lradc_ts_driver = {
> +	.driver	= {
> +		.name = DRIVER_NAME_TS,
> +	},
> +	.probe	= mxs_lradc_ts_probe,
> +	.remove	= mxs_lradc_ts_remove,
> +};
> +module_platform_driver(mxs_lradc_ts_driver);
> +
> +MODULE_LICENSE("GPL v2");
> 

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
@ 2016-05-02 16:58         ` Dmitry Torokhov
  0 siblings, 0 replies; 40+ messages in thread
From: Dmitry Torokhov @ 2016-05-02 16:58 UTC (permalink / raw)
  To: Marek Vasut
  Cc: Ksenija Stanojevic, linux-kernel, lee.jones, linux-input, jic23,
	knaack.h, lars, pmeerw, linux-iio, harald

On Sat, Apr 30, 2016 at 01:57:33AM +0200, Marek Vasut wrote:
> On 04/30/2016 01:36 AM, Dmitry Torokhov wrote:
> > Hi Ksenija,
> 
> Hi all,
> 
> > On Fri, Apr 29, 2016 at 01:49:11PM +0200, Ksenija Stanojevic wrote:
> >> Add mxs-lradc touchscreen driver.
> >>
> >> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
> >> ---
> >>  drivers/input/touchscreen/Kconfig        |  14 +-
> >>  drivers/input/touchscreen/Makefile       |   1 +
> >>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
> >>  3 files changed, 742 insertions(+), 2 deletions(-)
> >>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
> >>
> >> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> >> index 8ecdc38..d614d248 100644
> >> --- a/drivers/input/touchscreen/Kconfig
> >> +++ b/drivers/input/touchscreen/Kconfig
> >> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
> >>  	depends on SH_HP6XX && SH_ADC
> >>  	help
> >>  	  Say Y here if you have a HP Jornada 620/660/680/690 and want to
> >> -          support the built-in touchscreen.
> >> +	  support the built-in touchscreen.
> >>  
> >>  	  To compile this driver as a module, choose M here: the
> >>  	  module will be called hp680_ts_input.
> >> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
> >>  	  This enables support for the Philips UCB1400 touchscreen interface.
> >>  	  The UCB1400 is an AC97 audio codec.  The touchscreen interface
> >>  	  will be initialized only after the ALSA subsystem has been
> >> -	  brought up and the UCB1400 detected.  You therefore have to
> >> +	  brought up and the UCB1400 detected.	You therefore have to
> > 
> > Why do we have the tab in the middle of the text?
> 
> This shouldn't be a part of the patch.
> 
> >>  	  configure ALSA support as well (either built-in or modular,
> >>  	  independently of whether this driver is itself built-in or
> >>  	  modular) for this driver to work.
> 
> [...]
> 
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static struct platform_driver mxs_lradc_ts_driver = {
> >> +	.driver	= {
> >> +		.name = DRIVER_NAME_TS,
> >> +	},
> >> +	.probe	= mxs_lradc_ts_probe,
> >> +	.remove	= mxs_lradc_ts_remove,
> >> +};
> >> +module_platform_driver(mxs_lradc_ts_driver);
> >> +
> >> +MODULE_LICENSE("GPL v2");
> > 
> > "GPL" since you are not limiting to v2 only.
> 

I was just making sure the MODULE_LICENSE() string matches license
notice in the file. But see below.

> The original driver ( drivers/iio/adc/mxs-lradc.c ) is GPLv2 , but

No it is not. From the license notice at the beginning of
drivers/iio/adc/mxs-lradc.c:

> * This program is free software; you can redistribute it and/or modify
> * it under the terms of the GNU General Public License as published by
> * the Free Software Foundation; either version 2 of the License, or
> * (at your option) any later version.

So even the original driver is GPL v2+, MODULE_LICENSE() string
notwithstanding.

> unless the license gets changed to BSD or somesuch, I don't think anyone
> will really complain if it's changed to a more fitting version(s) of
> GPL. I'm fine with any GPL version here.

Me too, as long as we keep license notice and MODULE_LICENSE() in sync.

Thanks.

-- 
Dmitry

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
@ 2016-05-02 16:58         ` Dmitry Torokhov
  0 siblings, 0 replies; 40+ messages in thread
From: Dmitry Torokhov @ 2016-05-02 16:58 UTC (permalink / raw)
  To: Marek Vasut
  Cc: Ksenija Stanojevic, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	linux-input-u79uwXL29TY76Z2rM5mHXA, jic23-DgEjT+Ai2ygdnm+yROfE0A,
	knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	harald-95f8Dae0BrPYtjvyW6yDsg

On Sat, Apr 30, 2016 at 01:57:33AM +0200, Marek Vasut wrote:
> On 04/30/2016 01:36 AM, Dmitry Torokhov wrote:
> > Hi Ksenija,
> 
> Hi all,
> 
> > On Fri, Apr 29, 2016 at 01:49:11PM +0200, Ksenija Stanojevic wrote:
> >> Add mxs-lradc touchscreen driver.
> >>
> >> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> >> ---
> >>  drivers/input/touchscreen/Kconfig        |  14 +-
> >>  drivers/input/touchscreen/Makefile       |   1 +
> >>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
> >>  3 files changed, 742 insertions(+), 2 deletions(-)
> >>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
> >>
> >> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> >> index 8ecdc38..d614d248 100644
> >> --- a/drivers/input/touchscreen/Kconfig
> >> +++ b/drivers/input/touchscreen/Kconfig
> >> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
> >>  	depends on SH_HP6XX && SH_ADC
> >>  	help
> >>  	  Say Y here if you have a HP Jornada 620/660/680/690 and want to
> >> -          support the built-in touchscreen.
> >> +	  support the built-in touchscreen.
> >>  
> >>  	  To compile this driver as a module, choose M here: the
> >>  	  module will be called hp680_ts_input.
> >> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
> >>  	  This enables support for the Philips UCB1400 touchscreen interface.
> >>  	  The UCB1400 is an AC97 audio codec.  The touchscreen interface
> >>  	  will be initialized only after the ALSA subsystem has been
> >> -	  brought up and the UCB1400 detected.  You therefore have to
> >> +	  brought up and the UCB1400 detected.	You therefore have to
> > 
> > Why do we have the tab in the middle of the text?
> 
> This shouldn't be a part of the patch.
> 
> >>  	  configure ALSA support as well (either built-in or modular,
> >>  	  independently of whether this driver is itself built-in or
> >>  	  modular) for this driver to work.
> 
> [...]
> 
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static struct platform_driver mxs_lradc_ts_driver = {
> >> +	.driver	= {
> >> +		.name = DRIVER_NAME_TS,
> >> +	},
> >> +	.probe	= mxs_lradc_ts_probe,
> >> +	.remove	= mxs_lradc_ts_remove,
> >> +};
> >> +module_platform_driver(mxs_lradc_ts_driver);
> >> +
> >> +MODULE_LICENSE("GPL v2");
> > 
> > "GPL" since you are not limiting to v2 only.
> 

I was just making sure the MODULE_LICENSE() string matches license
notice in the file. But see below.

> The original driver ( drivers/iio/adc/mxs-lradc.c ) is GPLv2 , but

No it is not. From the license notice at the beginning of
drivers/iio/adc/mxs-lradc.c:

> * This program is free software; you can redistribute it and/or modify
> * it under the terms of the GNU General Public License as published by
> * the Free Software Foundation; either version 2 of the License, or
> * (at your option) any later version.

So even the original driver is GPL v2+, MODULE_LICENSE() string
notwithstanding.

> unless the license gets changed to BSD or somesuch, I don't think anyone
> will really complain if it's changed to a more fitting version(s) of
> GPL. I'm fine with any GPL version here.

Me too, as long as we keep license notice and MODULE_LICENSE() in sync.

Thanks.

-- 
Dmitry

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
@ 2016-05-28 17:45       ` Ksenija Stanojević
  0 siblings, 0 replies; 40+ messages in thread
From: Ksenija Stanojević @ 2016-05-28 17:45 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-kernel, Lee Jones, Dmitry Torokhov, linux-input,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Marek Vašut, linux-iio, Harald Geyer

On Sun, May 1, 2016 at 6:47 PM, Jonathan Cameron <jic23@kernel.org> wrote:
> On 29/04/16 12:49, Ksenija Stanojevic wrote:
>> Add mxs-lradc touchscreen driver.
>>
>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
> The only real thing this raises for me is why we are grabbing IRQs that I
> don't think this driver even cares about...
>
> Jonathan
>> ---
>>  drivers/input/touchscreen/Kconfig        |  14 +-
>>  drivers/input/touchscreen/Makefile       |   1 +
>>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
>>  3 files changed, 742 insertions(+), 2 deletions(-)
>>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
>>
>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>> index 8ecdc38..d614d248 100644
>> --- a/drivers/input/touchscreen/Kconfig
>> +++ b/drivers/input/touchscreen/Kconfig
>> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
>>       depends on SH_HP6XX && SH_ADC
>>       help
>>         Say Y here if you have a HP Jornada 620/660/680/690 and want to
>> -          support the built-in touchscreen.
>> +       support the built-in touchscreen.
>>
>>         To compile this driver as a module, choose M here: the
>>         module will be called hp680_ts_input.
>> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
>>         This enables support for the Philips UCB1400 touchscreen interface.
>>         The UCB1400 is an AC97 audio codec.  The touchscreen interface
>>         will be initialized only after the ALSA subsystem has been
>> -       brought up and the UCB1400 detected.  You therefore have to
>> +       brought up and the UCB1400 detected.  You therefore have to
>>         configure ALSA support as well (either built-in or modular,
>>         independently of whether this driver is itself built-in or
>>         modular) for this driver to work.
>> @@ -842,6 +842,16 @@ config TOUCHSCREEN_MX25
>>         To compile this driver as a module, choose M here: the
>>         module will be called fsl-imx25-tcq.
>>
>> +config TOUCHSCREEN_MXS_LRADC
>> +     tristate "Freescale i.MX23/i.MX28 LRADC touchscreen"
>> +     depends on MFD_MXS_LRADC
>> +     help
>> +       Say Y here if you have a touchscreen connected to the low-resolution
>> +       analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor.
>> +
>> +       To compile this driver as a module, choose M here: the module will be
>> +       called mxs-lradc-ts.
>> +
>>  config TOUCHSCREEN_MC13783
>>       tristate "Freescale MC13783 touchscreen input driver"
>>       depends on MFD_MC13XXX
>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
>> index f42975e..513a6ff 100644
>> --- a/drivers/input/touchscreen/Makefile
>> +++ b/drivers/input/touchscreen/Makefile
>> @@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR)             += migor_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_MMS114)     += mms114.o
>>  obj-$(CONFIG_TOUCHSCREEN_MTOUCH)     += mtouch.o
>>  obj-$(CONFIG_TOUCHSCREEN_MK712)              += mk712.o
>> +obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC)  += mxs-lradc-ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_HP600)              += hp680_ts_input.o
>>  obj-$(CONFIG_TOUCHSCREEN_HP7XX)              += jornada720_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o
>> diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c
>> new file mode 100644
>> index 0000000..27abb8e
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/mxs-lradc-ts.c
>> @@ -0,0 +1,729 @@
>> +/*
>> + * Freescale MXS LRADC driver
>> + *
>> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
>> + * Marek Vasut <marex@denx.de>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/mfd/mxs-lradc.h>
>> +#include <linux/platform_device.h>
>> +
>> +/*
>> + * Touchscreen handling
>> + */
>> +enum mxs_lradc_ts_plate {
>> +     LRADC_TOUCH = 0,
>> +     LRADC_SAMPLE_X,
>> +     LRADC_SAMPLE_Y,
>> +     LRADC_SAMPLE_PRESSURE,
>> +     LRADC_SAMPLE_VALID,
>> +};
>> +
>> +struct mxs_lradc_ts {
>> +     struct mxs_lradc        *lradc;
>> +     struct device           *dev;
>> +     /*
>> +      * When the touchscreen is enabled, we give it two private virtual
>> +      * channels: #6 and #7. This means that only 6 virtual channels (instead
>> +      * of 8) will be available for buffered capture.
>> +      */
>> +#define TOUCHSCREEN_VCHANNEL1                7
>> +#define TOUCHSCREEN_VCHANNEL2                6
>> +
>> +     struct input_dev        *ts_input;
>> +
>> +     enum mxs_lradc_ts_plate cur_plate; /* state machine */
>> +     bool                    ts_valid;
>> +     unsigned                ts_x_pos;
>> +     unsigned                ts_y_pos;
>> +     unsigned                ts_pressure;
>> +
>> +     /* handle touchscreen's physical behaviour */
>> +     /* samples per coordinate */
>> +     unsigned                over_sample_cnt;
>> +     /* time clocks between samples */
>> +     unsigned                over_sample_delay;
>> +     /* time in clocks to wait after the plates where switched */
>> +     unsigned                settling_delay;
>> +};
>> +
>> +static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_PLATE_MASK;
>> +     return LRADC_CTRL0_MX28_PLATE_MASK;
>> +}
>> +
>> +static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
>> +     return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE;
>> +}
>> +
>> +static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM;
>> +     return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW;
>> +}
>> +
>> +static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM;
>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW;
>> +}
>> +
>> +static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM;
>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW;
>> +}
>> +
>> +static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
>> +{
>> +     return !!(readl(lradc->base + LRADC_STATUS) &
>> +                                     LRADC_STATUS_TOUCH_DETECT_RAW);
>> +}
>> +
>> +static void mxs_lradc_map_ts_channel(struct mxs_lradc *lradc, unsigned vch,
>> +                                  unsigned ch)
>> +{
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
>> +                         LRADC_CTRL4);
>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4);
>> +}
>> +
>> +static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned ch)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /*
>> +      * prepare for oversampling conversion
>> +      *
>> +      * from the datasheet:
>> +      * "The ACCUMULATE bit in the appropriate channel register
>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
>> +      * otherwise, the IRQs will not fire."
>> +      */
>> +     mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE |
>> +                       LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1),
>> +                       LRADC_CH(ch));
>> +
>> +     /* from the datasheet:
>> +      * "Software must clear this register in preparation for a
>> +      * multi-cycle accumulation.
>> +      */
>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch));
>> +
>> +     /*
>> +      * prepare the delay/loop unit according to the oversampling count
>> +      *
>> +      * from the datasheet:
>> +      * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
>> +      * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
>> +      * the LRADC will not trigger the delay group."
>> +      */
>> +     mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
>> +                       LRADC_DELAY_TRIGGER_DELAYS(0) |
>> +                       LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>> +                       LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>> +                       LRADC_DELAY(3));
>> +
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1);
>> +
>> +     /*
>> +      * after changing the touchscreen plates setting
>> +      * the signals need some initial time to settle. Start the
>> +      * SoC's delay unit and start the conversion later
>> +      * and automatically.
>> +      */
>> +     mxs_lradc_reg_wrt(
>> +             lradc,
>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
>> +             LRADC_DELAY_KICK |
>> +             LRADC_DELAY_DELAY(ts->settling_delay),
>> +             LRADC_DELAY(2));
>> +}
>> +
>> +/*
>> + * Pressure detection is special:
>> + * We want to do both required measurements for the pressure detection in
>> + * one turn. Use the hardware features to chain both conversions and let the
>> + * hardware report one interrupt if both conversions are done
>> + */
>> +static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts, unsigned ch1,
>> +                                     unsigned ch2)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +     u32 reg;
>> +
>> +     /*
>> +      * prepare for oversampling conversion
>> +      *
>> +      * from the datasheet:
>> +      * "The ACCUMULATE bit in the appropriate channel register
>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
>> +      * otherwise, the IRQs will not fire."
>> +      */
>> +     reg = LRADC_CH_ACCUMULATE |
>> +             LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1);
>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
>> +
>> +     /* from the datasheet:
>> +      * "Software must clear this register in preparation for a
>> +      * multi-cycle accumulation.
>> +      */
>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1));
>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2));
>> +
>> +     /* prepare the delay/loop unit according to the oversampling count */
>> +     mxs_lradc_reg_wrt(
>> +                 lradc,
>> +                 LRADC_DELAY_TRIGGER(1 << ch1) |
>> +                 LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */
>> +                 LRADC_DELAY_TRIGGER_DELAYS(0) |
>> +                 LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>> +                 LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>> +                 LRADC_DELAY(3));
>> +
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1);
>> +
>> +     /*
>> +      * after changing the touchscreen plates setting
>> +      * the signals need some initial time to settle. Start the
>> +      * SoC's delay unit and start the conversion later
>> +      * and automatically.
>> +      */
>> +     mxs_lradc_reg_wrt(
>> +             lradc,
>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
>> +             LRADC_DELAY_KICK |
>> +             LRADC_DELAY_DELAY(ts->settling_delay), LRADC_DELAY(2));
>> +}
>> +
>> +static unsigned mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts,
>> +                                           unsigned channel)
>> +{
>> +     u32 reg;
>> +     unsigned num_samples, val;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     reg = readl(lradc->base + LRADC_CH(channel));
>> +     if (reg & LRADC_CH_ACCUMULATE)
>> +             num_samples = ts->over_sample_cnt;
>> +     else
>> +             num_samples = 1;
>> +
>> +     val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
>> +     return val / num_samples;
>> +}
>> +
>> +static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts,
>> +                                        unsigned ch1, unsigned ch2)
>> +{
>> +     u32 reg, mask;
>> +     unsigned pressure, m1, m2;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
>> +     reg = readl(lradc->base + LRADC_CTRL1) & mask;
>> +
>> +     while (reg != mask) {
>> +             reg = readl(lradc->base + LRADC_CTRL1) & mask;
>> +             dev_dbg(ts->dev, "One channel is still busy: %X\n", reg);
>> +     }
>> +
>> +     m1 = mxs_lradc_ts_read_raw_channel(ts, ch1);
>> +     m2 = mxs_lradc_ts_read_raw_channel(ts, ch2);
>> +
>> +     if (m2 == 0) {
>> +             dev_warn(ts->dev, "Cannot calculate pressure\n");
>> +             return 1 << (LRADC_RESOLUTION - 1);
>> +     }
>> +
>> +     /* simply scale the value from 0 ... max ADC resolution */
>> +     pressure = m1;
>> +     pressure *= (1 << LRADC_RESOLUTION);
>> +     pressure /= m2;
>> +
>> +     dev_dbg(ts->dev, "Pressure = %u\n", pressure);
>> +     return pressure;
>> +}
>> +
>> +#define TS_CH_XP 2
>> +#define TS_CH_YP 3
>> +#define TS_CH_XM 4
>> +#define TS_CH_YM 5
>> +
>> +/*
>> + * YP(open)--+-------------+
>> + *        |             |--+
>> + *        |             |  |
>> + *    YM(-)--+-------------+  |
>> + *          +--------------+
>> + *          |              |
>> + *      XP(weak+)        XM(open)
>> + *
>> + * "weak+" means 200k Ohm VDDIO
>> + * (-) means GND
>> + */
>> +static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /*
>> +      * In order to detect a touch event the 'touch detect enable' bit
>> +      * enables:
>> +      *  - a weak pullup to the X+ connector
>> +      *  - a strong ground at the Y- connector
>> +      */
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc),
>> +                       LRADC_CTRL0);
>> +}
>> +
>> +/*
>> + * YP(meas)--+-------------+
>> + *        |             |--+
>> + *        |             |  |
>> + * YM(open)--+-------------+  |
>> + *          +--------------+
>> + *          |              |
>> + *        XP(+)          XM(-)
>> + *
>> + * (+) means here 1.85 V
>> + * (-) means here GND
>> + */
>> +static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
>> +
>> +     ts->cur_plate = LRADC_SAMPLE_X;
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +}
>> +
>> +/*
>> + *   YP(+)--+-------------+
>> + *       |             |--+
>> + *       |             |  |
>> + *   YM(-)--+-------------+  |
>> + *         +--------------+
>> + *         |              |
>> + *      XP(open)        XM(meas)
>> + *
>> + * (+) means here 1.85 V
>> + * (-) means here GND
>> + */
>> +static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
>> +
>> +     ts->cur_plate = LRADC_SAMPLE_Y;
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +}
>> +
>> +/*
>> + *    YP(+)--+-------------+
>> + *        |             |--+
>> + *        |             |  |
>> + * YM(meas)--+-------------+  |
>> + *          +--------------+
>> + *          |              |
>> + *       XP(meas)        XM(-)
>> + *
>> + * (+) means here 1.85 V
>> + * (-) means here GND
>> + */
>> +static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
>> +
>> +     ts->cur_plate = LRADC_SAMPLE_PRESSURE;
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
>> +     mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2,
>> +                                 TOUCHSCREEN_VCHANNEL1);
>> +}
>> +
>> +static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts)
>> +{
>> +     mxs_lradc_setup_touch_detection(ts);
>> +
>> +     ts->cur_plate = LRADC_TOUCH;
>> +     mxs_lradc_reg_clear(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
>> +     mxs_lradc_reg_set(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>> +                       LRADC_CTRL1);
>> +}
>> +
>> +static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts)
>> +{
>> +     mxs_lradc_reg_clear(ts->lradc,
>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>> +                         LRADC_CTRL1);
>> +     mxs_lradc_reg_set(ts->lradc,
>> +                       LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1),
>> +                       LRADC_CTRL1);
>> +     /*
>> +      * start with the Y-pos, because it uses nearly the same plate
>> +      * settings like the touch detection
>> +      */
>> +     mxs_lradc_prepare_y_pos(ts);
>> +}
>> +
>> +static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts)
>> +{
>> +     input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos);
>> +     input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos);
>> +     input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure);
>> +     input_report_key(ts->ts_input, BTN_TOUCH, 1);
>> +     input_sync(ts->ts_input);
>> +}
>> +
>> +static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_setup_touch_detection(ts);
>> +     ts->cur_plate = LRADC_SAMPLE_VALID;
>> +     /*
>> +      * start a dummy conversion to burn time to settle the signals
>> +      * note: we are not interested in the conversion's value
>> +      */
>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
>> +     mxs_lradc_reg_clear(lradc,
>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2),
>> +                         LRADC_CTRL1);
>> +     mxs_lradc_reg_wrt(
>> +                 lradc,
>> +                 LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
>> +                 LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
>> +                 LRADC_DELAY(2));
>> +}
>> +
>> +/*
>> + * in order to avoid false measurements, report only samples where
>> + * the surface is still touched after the position measurement
>> + */
>> +static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /* if it is still touched, report the sample */
>> +     if (valid && mxs_lradc_check_touch_event(lradc)) {
>> +             ts->ts_valid = true;
>> +             mxs_lradc_report_ts_event(ts);
>> +     }
>> +
>> +     /* if it is even still touched, continue with the next measurement */
>> +     if (mxs_lradc_check_touch_event(lradc)) {
>> +             mxs_lradc_prepare_y_pos(ts);
>> +             return;
>> +     }
>> +
>> +     if (ts->ts_valid) {
>> +             /* signal the release */
>> +             ts->ts_valid = false;
>> +             input_report_key(ts->ts_input, BTN_TOUCH, 0);
>> +             input_sync(ts->ts_input);
>> +     }
>> +
>> +     /* if it is released, wait for the next touch via IRQ */
>> +     ts->cur_plate = LRADC_TOUCH;
>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
>> +     mxs_lradc_reg_clear(lradc,
>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ |
>> +                         LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1),
>> +                         LRADC_CTRL1);
>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
>> +}
>> +
>> +/* touchscreen's state machine */
>> +static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     switch (ts->cur_plate) {
>> +     case LRADC_TOUCH:
>> +             if (mxs_lradc_check_touch_event(lradc))
>> +                     mxs_lradc_start_touch_event(ts);
>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
>> +                                 LRADC_CTRL1);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_Y:
>> +             ts->ts_y_pos =
>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +             mxs_lradc_prepare_x_pos(ts);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_X:
>> +             ts->ts_x_pos =
>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +             mxs_lradc_prepare_pressure(ts);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_PRESSURE:
>> +             ts->ts_pressure =
>> +                 mxs_lradc_read_ts_pressure(ts,
>> +                                            TOUCHSCREEN_VCHANNEL2,
>> +                                            TOUCHSCREEN_VCHANNEL1);
>> +             mxs_lradc_complete_touch_event(ts);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_VALID:
>> +             mxs_lradc_finish_touch_event(ts, 1);
>> +             break;
>> +     }
>> +}
>> +
>> +/*
>> + * IRQ Handling
>> + */
>> +static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data)
>> +{
>> +     struct mxs_lradc_ts *ts = data;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +     unsigned long reg = readl(lradc->base + LRADC_CTRL1);
>> +     u32 clr_irq = mxs_lradc_irq_mask(lradc);
>> +     const u32 ts_irq_mask =
>> +             LRADC_CTRL1_TOUCH_DETECT_IRQ |
>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
>> +
>> +     if (!(reg & mxs_lradc_irq_mask(lradc)))
>> +             return IRQ_NONE;
>> +
>> +     if (lradc->use_touchscreen && (reg & ts_irq_mask)) {
>> +             mxs_lradc_handle_touch(ts);
>> +
>> +             /* Make sure we don't clear the next conversion's interrupt. */
>> +             clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>> +                             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
>> +             mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
>> +     }
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +static int mxs_lradc_ts_open(struct input_dev *dev)
>> +{
>> +     struct mxs_lradc_ts *ts = input_get_drvdata(dev);
>> +
>> +     /* Enable the touch-detect circuitry. */
>> +     mxs_lradc_enable_touch_detection(ts);
>> +
>> +     return 0;
>> +}
>> +
>> +static void mxs_lradc_disable_ts(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /* stop all interrupts from firing */
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
>> +
>> +     /* Power-down touchscreen touch-detect circuitry. */
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +}
>> +
>> +static void mxs_lradc_ts_close(struct input_dev *dev)
>> +{
>> +     struct mxs_lradc_ts *ts = input_get_drvdata(dev);
>> +
>> +     mxs_lradc_disable_ts(ts);
>> +}
>> +
>> +static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /* Configure the touchscreen type */
>> +     if (lradc->soc == IMX28_LRADC) {
>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
>> +                                 LRADC_CTRL0);
>> +
>> +             if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
>> +                     mxs_lradc_reg_set(lradc,
>> +                                       LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
>> +                                       LRADC_CTRL0);
>> +     }
>> +}
>> +
>> +static void mxs_lradc_ts_hw_stop(struct mxs_lradc_ts *ts)
>> +{
>> +     int i;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc,
>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
>> +                     LRADC_CTRL1);
>> +
>> +     for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++)
>> +             mxs_lradc_reg_wrt(ts->lradc, 0, LRADC_DELAY(i));
>> +}
>> +
>> +static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts)
>> +{
>> +     struct input_dev *input = ts->ts_input;
>> +     struct device *dev = ts->dev;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     if (!lradc->use_touchscreen)
>> +             return 0;
>> +
>> +     input->name = DRIVER_NAME_TS;
>> +     input->id.bustype = BUS_HOST;
>> +     input->dev.parent = dev;
>> +     input->open = mxs_lradc_ts_open;
>> +     input->close = mxs_lradc_ts_close;
>> +
>> +     __set_bit(EV_ABS, input->evbit);
>> +     __set_bit(EV_KEY, input->evbit);
>> +     __set_bit(BTN_TOUCH, input->keybit);
>> +     __set_bit(INPUT_PROP_DIRECT, input->propbit);
>> +     input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
>> +     input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
>> +     input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
>> +                          0, 0);
>> +
>> +     ts->ts_input = input;
>> +     input_set_drvdata(input, ts);
>> +
>> +     return input_register_device(input);
>> +}
>> +
>> +static int mxs_lradc_ts_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct device_node *node = dev->parent->of_node;
>> +     struct mxs_lradc *lradc = dev_get_platdata(dev);
>> +     struct mxs_lradc_ts *ts;
>> +     struct input_dev *input;
>> +     int touch_ret, ret, i;
>> +     u32 ts_wires = 0, adapt;
>> +
>> +     ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
>> +     input = devm_input_allocate_device(dev);
>> +     if (!ts || !input)
>> +             return -ENOMEM;
>> +
>> +     ts->lradc = lradc;
>> +     ts->dev = dev;
>> +     ts->ts_input = input;
>> +     platform_set_drvdata(pdev, ts);
>> +     input_set_drvdata(input, ts);
>> +
>> +     touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
>> +                                      &ts_wires);
>> +
>> +     if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) {
>> +             ts->over_sample_cnt = 4;
>> +     } else {
>> +             if (adapt < 1 || adapt > 32) {
>> +                     dev_err(ts->dev, "Invalid sample count (%u)\n",
>> +                             adapt);
>> +                     touch_ret = -EINVAL;
>> +             } else {
>> +                     ts->over_sample_cnt = adapt;
>> +             }
>> +     }
>> +
>> +     if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) {
>> +             ts->over_sample_delay = 2;
>> +     } else {
>> +             if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) {
>> +                     dev_err(ts->dev, "Invalid sample delay (%u)\n",
>> +                             adapt);
>> +                     touch_ret = -EINVAL;
>> +             } else {
>> +                     ts->over_sample_delay = adapt;
>> +             }
>> +     }
>> +
>> +     if (of_property_read_u32(node, "fsl,settling", &adapt)) {
>> +             ts->settling_delay = 10;
>> +     } else {
>> +             if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) {
>> +                     dev_err(ts->dev, "Invalid settling delay (%u)\n",
>> +                             adapt);
>> +                     touch_ret = -EINVAL;
>> +             } else {
>> +                     ts->settling_delay = adapt;
>> +             }
>> +     }
>> +
>> +     mxs_lradc_ts_hw_init(ts);
>> +     for (i = 0; i < lradc->irq_count; i++) {
>> +             ret = devm_request_irq(dev, lradc->irq[i],
>> +                                    mxs_lradc_ts_handle_irq,
>> +                                    IRQF_SHARED, lradc->irq_name[i], ts);
> As with the adc driver, are we actually using all of these?  I'd prefer we
> only grab the ones that are actually relevant.

Only irq lines relevant for touchscreen are:
mxs-lradc-touchscreen, mxs-lradc-channel6 and mxs-lradc-channel7
But not all interrupts are beiing used even when I enabled all remaining
channels (not used by touchscreen) for bufferd capture via
echo 1 >/sys/bus/iio/devices/iio\:device0/scan_elements/in_voltagexx_en

So I don't know if it's supposed to work like this...
(It works the same on the original code)

root@cfa100xx:~# cat /proc/interrupts
           CPU0
 16:      13108         -  48 Edge      MXS Timer Tick
 17:       4240         -  82 Edge      mxs-dma
 25:          6         -  96 Edge      80010000.ssp
196:          0         -  68 Edge      mxs-dma
210:         13         -  10 Edge      mxs-lradc-touchscreen
211:          0         -  14 Edge      mxs-lradc-thresh0
212:          0         -  15 Edge      mxs-lradc-thresh1
213:         10         -  16 Edge      mxs-lradc-channel0
214:         10         -  17 Edge      mxs-lradc-channel1
215:          0         -  18 Edge      mxs-lradc-channel2
216:          0         -  19 Edge      mxs-lradc-channel3
217:          0         -  20 Edge      mxs-lradc-channel4
218:          0         -  21 Edge      mxs-lradc-channel5
219:          0         -  22 Edge      mxs-lradc-channel6
220:        412         -  23 Edge      mxs-lradc-channel7
221:          0         -  24 Edge      mxs-lradc-button0
222:          0         -  25 Edge      mxs-lradc-button1
223:          0         -  29 Edge      RTC alarm
224:          0         - 111 Edge      80058000.i2c
228:        174         -  47 Edge      uart-pl011
229:        439         -  93 Edge      80080000.usb
230:          0         -  92 Edge      80090000.usb
231:       3610         - 101 Edge      800f0000.ethernet
232:         10  80050000.lradc-dev0 Edge
Err:          0

>> +             if (ret)
>> +                     return ret;
>> +     }
>> +
>> +     if (!touch_ret) {
>> +             ret = mxs_lradc_ts_register(ts);
>> +             if (!ret)
>> +                     goto err_ts_register;
>> +     }
>> +
>> +     return 0;
>> +
>> +err_ts_register:
>> +     mxs_lradc_ts_hw_stop(ts);
>> +     return ret;
>> +}
>> +
>> +static int mxs_lradc_ts_remove(struct platform_device *pdev)
>> +{
>> +     struct mxs_lradc_ts *ts = platform_get_drvdata(pdev);
>> +
>> +     mxs_lradc_ts_hw_stop(ts);
>> +
>> +     return 0;
>> +}
>> +
>> +static struct platform_driver mxs_lradc_ts_driver = {
>> +     .driver = {
>> +             .name = DRIVER_NAME_TS,
>> +     },
>> +     .probe  = mxs_lradc_ts_probe,
>> +     .remove = mxs_lradc_ts_remove,
>> +};
>> +module_platform_driver(mxs_lradc_ts_driver);
>> +
>> +MODULE_LICENSE("GPL v2");
>>
>

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
@ 2016-05-28 17:45       ` Ksenija Stanojević
  0 siblings, 0 replies; 40+ messages in thread
From: Ksenija Stanojević @ 2016-05-28 17:45 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Lee Jones, Dmitry Torokhov,
	linux-input-u79uwXL29TY76Z2rM5mHXA, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Marek Vašut,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Harald Geyer

On Sun, May 1, 2016 at 6:47 PM, Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On 29/04/16 12:49, Ksenija Stanojevic wrote:
>> Add mxs-lradc touchscreen driver.
>>
>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> The only real thing this raises for me is why we are grabbing IRQs that I
> don't think this driver even cares about...
>
> Jonathan
>> ---
>>  drivers/input/touchscreen/Kconfig        |  14 +-
>>  drivers/input/touchscreen/Makefile       |   1 +
>>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
>>  3 files changed, 742 insertions(+), 2 deletions(-)
>>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
>>
>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>> index 8ecdc38..d614d248 100644
>> --- a/drivers/input/touchscreen/Kconfig
>> +++ b/drivers/input/touchscreen/Kconfig
>> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
>>       depends on SH_HP6XX && SH_ADC
>>       help
>>         Say Y here if you have a HP Jornada 620/660/680/690 and want to
>> -          support the built-in touchscreen.
>> +       support the built-in touchscreen.
>>
>>         To compile this driver as a module, choose M here: the
>>         module will be called hp680_ts_input.
>> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
>>         This enables support for the Philips UCB1400 touchscreen interface.
>>         The UCB1400 is an AC97 audio codec.  The touchscreen interface
>>         will be initialized only after the ALSA subsystem has been
>> -       brought up and the UCB1400 detected.  You therefore have to
>> +       brought up and the UCB1400 detected.  You therefore have to
>>         configure ALSA support as well (either built-in or modular,
>>         independently of whether this driver is itself built-in or
>>         modular) for this driver to work.
>> @@ -842,6 +842,16 @@ config TOUCHSCREEN_MX25
>>         To compile this driver as a module, choose M here: the
>>         module will be called fsl-imx25-tcq.
>>
>> +config TOUCHSCREEN_MXS_LRADC
>> +     tristate "Freescale i.MX23/i.MX28 LRADC touchscreen"
>> +     depends on MFD_MXS_LRADC
>> +     help
>> +       Say Y here if you have a touchscreen connected to the low-resolution
>> +       analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor.
>> +
>> +       To compile this driver as a module, choose M here: the module will be
>> +       called mxs-lradc-ts.
>> +
>>  config TOUCHSCREEN_MC13783
>>       tristate "Freescale MC13783 touchscreen input driver"
>>       depends on MFD_MC13XXX
>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
>> index f42975e..513a6ff 100644
>> --- a/drivers/input/touchscreen/Makefile
>> +++ b/drivers/input/touchscreen/Makefile
>> @@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR)             += migor_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_MMS114)     += mms114.o
>>  obj-$(CONFIG_TOUCHSCREEN_MTOUCH)     += mtouch.o
>>  obj-$(CONFIG_TOUCHSCREEN_MK712)              += mk712.o
>> +obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC)  += mxs-lradc-ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_HP600)              += hp680_ts_input.o
>>  obj-$(CONFIG_TOUCHSCREEN_HP7XX)              += jornada720_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o
>> diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c
>> new file mode 100644
>> index 0000000..27abb8e
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/mxs-lradc-ts.c
>> @@ -0,0 +1,729 @@
>> +/*
>> + * Freescale MXS LRADC driver
>> + *
>> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
>> + * Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/mfd/mxs-lradc.h>
>> +#include <linux/platform_device.h>
>> +
>> +/*
>> + * Touchscreen handling
>> + */
>> +enum mxs_lradc_ts_plate {
>> +     LRADC_TOUCH = 0,
>> +     LRADC_SAMPLE_X,
>> +     LRADC_SAMPLE_Y,
>> +     LRADC_SAMPLE_PRESSURE,
>> +     LRADC_SAMPLE_VALID,
>> +};
>> +
>> +struct mxs_lradc_ts {
>> +     struct mxs_lradc        *lradc;
>> +     struct device           *dev;
>> +     /*
>> +      * When the touchscreen is enabled, we give it two private virtual
>> +      * channels: #6 and #7. This means that only 6 virtual channels (instead
>> +      * of 8) will be available for buffered capture.
>> +      */
>> +#define TOUCHSCREEN_VCHANNEL1                7
>> +#define TOUCHSCREEN_VCHANNEL2                6
>> +
>> +     struct input_dev        *ts_input;
>> +
>> +     enum mxs_lradc_ts_plate cur_plate; /* state machine */
>> +     bool                    ts_valid;
>> +     unsigned                ts_x_pos;
>> +     unsigned                ts_y_pos;
>> +     unsigned                ts_pressure;
>> +
>> +     /* handle touchscreen's physical behaviour */
>> +     /* samples per coordinate */
>> +     unsigned                over_sample_cnt;
>> +     /* time clocks between samples */
>> +     unsigned                over_sample_delay;
>> +     /* time in clocks to wait after the plates where switched */
>> +     unsigned                settling_delay;
>> +};
>> +
>> +static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_PLATE_MASK;
>> +     return LRADC_CTRL0_MX28_PLATE_MASK;
>> +}
>> +
>> +static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
>> +     return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE;
>> +}
>> +
>> +static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM;
>> +     return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW;
>> +}
>> +
>> +static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM;
>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW;
>> +}
>> +
>> +static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM;
>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW;
>> +}
>> +
>> +static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
>> +{
>> +     return !!(readl(lradc->base + LRADC_STATUS) &
>> +                                     LRADC_STATUS_TOUCH_DETECT_RAW);
>> +}
>> +
>> +static void mxs_lradc_map_ts_channel(struct mxs_lradc *lradc, unsigned vch,
>> +                                  unsigned ch)
>> +{
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
>> +                         LRADC_CTRL4);
>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4);
>> +}
>> +
>> +static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned ch)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /*
>> +      * prepare for oversampling conversion
>> +      *
>> +      * from the datasheet:
>> +      * "The ACCUMULATE bit in the appropriate channel register
>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
>> +      * otherwise, the IRQs will not fire."
>> +      */
>> +     mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE |
>> +                       LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1),
>> +                       LRADC_CH(ch));
>> +
>> +     /* from the datasheet:
>> +      * "Software must clear this register in preparation for a
>> +      * multi-cycle accumulation.
>> +      */
>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch));
>> +
>> +     /*
>> +      * prepare the delay/loop unit according to the oversampling count
>> +      *
>> +      * from the datasheet:
>> +      * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
>> +      * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
>> +      * the LRADC will not trigger the delay group."
>> +      */
>> +     mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
>> +                       LRADC_DELAY_TRIGGER_DELAYS(0) |
>> +                       LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>> +                       LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>> +                       LRADC_DELAY(3));
>> +
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1);
>> +
>> +     /*
>> +      * after changing the touchscreen plates setting
>> +      * the signals need some initial time to settle. Start the
>> +      * SoC's delay unit and start the conversion later
>> +      * and automatically.
>> +      */
>> +     mxs_lradc_reg_wrt(
>> +             lradc,
>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
>> +             LRADC_DELAY_KICK |
>> +             LRADC_DELAY_DELAY(ts->settling_delay),
>> +             LRADC_DELAY(2));
>> +}
>> +
>> +/*
>> + * Pressure detection is special:
>> + * We want to do both required measurements for the pressure detection in
>> + * one turn. Use the hardware features to chain both conversions and let the
>> + * hardware report one interrupt if both conversions are done
>> + */
>> +static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts, unsigned ch1,
>> +                                     unsigned ch2)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +     u32 reg;
>> +
>> +     /*
>> +      * prepare for oversampling conversion
>> +      *
>> +      * from the datasheet:
>> +      * "The ACCUMULATE bit in the appropriate channel register
>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
>> +      * otherwise, the IRQs will not fire."
>> +      */
>> +     reg = LRADC_CH_ACCUMULATE |
>> +             LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1);
>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
>> +
>> +     /* from the datasheet:
>> +      * "Software must clear this register in preparation for a
>> +      * multi-cycle accumulation.
>> +      */
>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1));
>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2));
>> +
>> +     /* prepare the delay/loop unit according to the oversampling count */
>> +     mxs_lradc_reg_wrt(
>> +                 lradc,
>> +                 LRADC_DELAY_TRIGGER(1 << ch1) |
>> +                 LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */
>> +                 LRADC_DELAY_TRIGGER_DELAYS(0) |
>> +                 LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>> +                 LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>> +                 LRADC_DELAY(3));
>> +
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1);
>> +
>> +     /*
>> +      * after changing the touchscreen plates setting
>> +      * the signals need some initial time to settle. Start the
>> +      * SoC's delay unit and start the conversion later
>> +      * and automatically.
>> +      */
>> +     mxs_lradc_reg_wrt(
>> +             lradc,
>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
>> +             LRADC_DELAY_KICK |
>> +             LRADC_DELAY_DELAY(ts->settling_delay), LRADC_DELAY(2));
>> +}
>> +
>> +static unsigned mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts,
>> +                                           unsigned channel)
>> +{
>> +     u32 reg;
>> +     unsigned num_samples, val;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     reg = readl(lradc->base + LRADC_CH(channel));
>> +     if (reg & LRADC_CH_ACCUMULATE)
>> +             num_samples = ts->over_sample_cnt;
>> +     else
>> +             num_samples = 1;
>> +
>> +     val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
>> +     return val / num_samples;
>> +}
>> +
>> +static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts,
>> +                                        unsigned ch1, unsigned ch2)
>> +{
>> +     u32 reg, mask;
>> +     unsigned pressure, m1, m2;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
>> +     reg = readl(lradc->base + LRADC_CTRL1) & mask;
>> +
>> +     while (reg != mask) {
>> +             reg = readl(lradc->base + LRADC_CTRL1) & mask;
>> +             dev_dbg(ts->dev, "One channel is still busy: %X\n", reg);
>> +     }
>> +
>> +     m1 = mxs_lradc_ts_read_raw_channel(ts, ch1);
>> +     m2 = mxs_lradc_ts_read_raw_channel(ts, ch2);
>> +
>> +     if (m2 == 0) {
>> +             dev_warn(ts->dev, "Cannot calculate pressure\n");
>> +             return 1 << (LRADC_RESOLUTION - 1);
>> +     }
>> +
>> +     /* simply scale the value from 0 ... max ADC resolution */
>> +     pressure = m1;
>> +     pressure *= (1 << LRADC_RESOLUTION);
>> +     pressure /= m2;
>> +
>> +     dev_dbg(ts->dev, "Pressure = %u\n", pressure);
>> +     return pressure;
>> +}
>> +
>> +#define TS_CH_XP 2
>> +#define TS_CH_YP 3
>> +#define TS_CH_XM 4
>> +#define TS_CH_YM 5
>> +
>> +/*
>> + * YP(open)--+-------------+
>> + *        |             |--+
>> + *        |             |  |
>> + *    YM(-)--+-------------+  |
>> + *          +--------------+
>> + *          |              |
>> + *      XP(weak+)        XM(open)
>> + *
>> + * "weak+" means 200k Ohm VDDIO
>> + * (-) means GND
>> + */
>> +static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /*
>> +      * In order to detect a touch event the 'touch detect enable' bit
>> +      * enables:
>> +      *  - a weak pullup to the X+ connector
>> +      *  - a strong ground at the Y- connector
>> +      */
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc),
>> +                       LRADC_CTRL0);
>> +}
>> +
>> +/*
>> + * YP(meas)--+-------------+
>> + *        |             |--+
>> + *        |             |  |
>> + * YM(open)--+-------------+  |
>> + *          +--------------+
>> + *          |              |
>> + *        XP(+)          XM(-)
>> + *
>> + * (+) means here 1.85 V
>> + * (-) means here GND
>> + */
>> +static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
>> +
>> +     ts->cur_plate = LRADC_SAMPLE_X;
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +}
>> +
>> +/*
>> + *   YP(+)--+-------------+
>> + *       |             |--+
>> + *       |             |  |
>> + *   YM(-)--+-------------+  |
>> + *         +--------------+
>> + *         |              |
>> + *      XP(open)        XM(meas)
>> + *
>> + * (+) means here 1.85 V
>> + * (-) means here GND
>> + */
>> +static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
>> +
>> +     ts->cur_plate = LRADC_SAMPLE_Y;
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +}
>> +
>> +/*
>> + *    YP(+)--+-------------+
>> + *        |             |--+
>> + *        |             |  |
>> + * YM(meas)--+-------------+  |
>> + *          +--------------+
>> + *          |              |
>> + *       XP(meas)        XM(-)
>> + *
>> + * (+) means here 1.85 V
>> + * (-) means here GND
>> + */
>> +static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
>> +
>> +     ts->cur_plate = LRADC_SAMPLE_PRESSURE;
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
>> +     mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2,
>> +                                 TOUCHSCREEN_VCHANNEL1);
>> +}
>> +
>> +static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts)
>> +{
>> +     mxs_lradc_setup_touch_detection(ts);
>> +
>> +     ts->cur_plate = LRADC_TOUCH;
>> +     mxs_lradc_reg_clear(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
>> +     mxs_lradc_reg_set(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>> +                       LRADC_CTRL1);
>> +}
>> +
>> +static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts)
>> +{
>> +     mxs_lradc_reg_clear(ts->lradc,
>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>> +                         LRADC_CTRL1);
>> +     mxs_lradc_reg_set(ts->lradc,
>> +                       LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1),
>> +                       LRADC_CTRL1);
>> +     /*
>> +      * start with the Y-pos, because it uses nearly the same plate
>> +      * settings like the touch detection
>> +      */
>> +     mxs_lradc_prepare_y_pos(ts);
>> +}
>> +
>> +static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts)
>> +{
>> +     input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos);
>> +     input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos);
>> +     input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure);
>> +     input_report_key(ts->ts_input, BTN_TOUCH, 1);
>> +     input_sync(ts->ts_input);
>> +}
>> +
>> +static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_setup_touch_detection(ts);
>> +     ts->cur_plate = LRADC_SAMPLE_VALID;
>> +     /*
>> +      * start a dummy conversion to burn time to settle the signals
>> +      * note: we are not interested in the conversion's value
>> +      */
>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
>> +     mxs_lradc_reg_clear(lradc,
>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2),
>> +                         LRADC_CTRL1);
>> +     mxs_lradc_reg_wrt(
>> +                 lradc,
>> +                 LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
>> +                 LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
>> +                 LRADC_DELAY(2));
>> +}
>> +
>> +/*
>> + * in order to avoid false measurements, report only samples where
>> + * the surface is still touched after the position measurement
>> + */
>> +static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /* if it is still touched, report the sample */
>> +     if (valid && mxs_lradc_check_touch_event(lradc)) {
>> +             ts->ts_valid = true;
>> +             mxs_lradc_report_ts_event(ts);
>> +     }
>> +
>> +     /* if it is even still touched, continue with the next measurement */
>> +     if (mxs_lradc_check_touch_event(lradc)) {
>> +             mxs_lradc_prepare_y_pos(ts);
>> +             return;
>> +     }
>> +
>> +     if (ts->ts_valid) {
>> +             /* signal the release */
>> +             ts->ts_valid = false;
>> +             input_report_key(ts->ts_input, BTN_TOUCH, 0);
>> +             input_sync(ts->ts_input);
>> +     }
>> +
>> +     /* if it is released, wait for the next touch via IRQ */
>> +     ts->cur_plate = LRADC_TOUCH;
>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
>> +     mxs_lradc_reg_clear(lradc,
>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ |
>> +                         LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1),
>> +                         LRADC_CTRL1);
>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
>> +}
>> +
>> +/* touchscreen's state machine */
>> +static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     switch (ts->cur_plate) {
>> +     case LRADC_TOUCH:
>> +             if (mxs_lradc_check_touch_event(lradc))
>> +                     mxs_lradc_start_touch_event(ts);
>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
>> +                                 LRADC_CTRL1);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_Y:
>> +             ts->ts_y_pos =
>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +             mxs_lradc_prepare_x_pos(ts);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_X:
>> +             ts->ts_x_pos =
>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +             mxs_lradc_prepare_pressure(ts);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_PRESSURE:
>> +             ts->ts_pressure =
>> +                 mxs_lradc_read_ts_pressure(ts,
>> +                                            TOUCHSCREEN_VCHANNEL2,
>> +                                            TOUCHSCREEN_VCHANNEL1);
>> +             mxs_lradc_complete_touch_event(ts);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_VALID:
>> +             mxs_lradc_finish_touch_event(ts, 1);
>> +             break;
>> +     }
>> +}
>> +
>> +/*
>> + * IRQ Handling
>> + */
>> +static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data)
>> +{
>> +     struct mxs_lradc_ts *ts = data;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +     unsigned long reg = readl(lradc->base + LRADC_CTRL1);
>> +     u32 clr_irq = mxs_lradc_irq_mask(lradc);
>> +     const u32 ts_irq_mask =
>> +             LRADC_CTRL1_TOUCH_DETECT_IRQ |
>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
>> +
>> +     if (!(reg & mxs_lradc_irq_mask(lradc)))
>> +             return IRQ_NONE;
>> +
>> +     if (lradc->use_touchscreen && (reg & ts_irq_mask)) {
>> +             mxs_lradc_handle_touch(ts);
>> +
>> +             /* Make sure we don't clear the next conversion's interrupt. */
>> +             clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>> +                             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
>> +             mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
>> +     }
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +static int mxs_lradc_ts_open(struct input_dev *dev)
>> +{
>> +     struct mxs_lradc_ts *ts = input_get_drvdata(dev);
>> +
>> +     /* Enable the touch-detect circuitry. */
>> +     mxs_lradc_enable_touch_detection(ts);
>> +
>> +     return 0;
>> +}
>> +
>> +static void mxs_lradc_disable_ts(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /* stop all interrupts from firing */
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
>> +
>> +     /* Power-down touchscreen touch-detect circuitry. */
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +}
>> +
>> +static void mxs_lradc_ts_close(struct input_dev *dev)
>> +{
>> +     struct mxs_lradc_ts *ts = input_get_drvdata(dev);
>> +
>> +     mxs_lradc_disable_ts(ts);
>> +}
>> +
>> +static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /* Configure the touchscreen type */
>> +     if (lradc->soc == IMX28_LRADC) {
>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
>> +                                 LRADC_CTRL0);
>> +
>> +             if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
>> +                     mxs_lradc_reg_set(lradc,
>> +                                       LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
>> +                                       LRADC_CTRL0);
>> +     }
>> +}
>> +
>> +static void mxs_lradc_ts_hw_stop(struct mxs_lradc_ts *ts)
>> +{
>> +     int i;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc,
>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
>> +                     LRADC_CTRL1);
>> +
>> +     for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++)
>> +             mxs_lradc_reg_wrt(ts->lradc, 0, LRADC_DELAY(i));
>> +}
>> +
>> +static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts)
>> +{
>> +     struct input_dev *input = ts->ts_input;
>> +     struct device *dev = ts->dev;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     if (!lradc->use_touchscreen)
>> +             return 0;
>> +
>> +     input->name = DRIVER_NAME_TS;
>> +     input->id.bustype = BUS_HOST;
>> +     input->dev.parent = dev;
>> +     input->open = mxs_lradc_ts_open;
>> +     input->close = mxs_lradc_ts_close;
>> +
>> +     __set_bit(EV_ABS, input->evbit);
>> +     __set_bit(EV_KEY, input->evbit);
>> +     __set_bit(BTN_TOUCH, input->keybit);
>> +     __set_bit(INPUT_PROP_DIRECT, input->propbit);
>> +     input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
>> +     input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
>> +     input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
>> +                          0, 0);
>> +
>> +     ts->ts_input = input;
>> +     input_set_drvdata(input, ts);
>> +
>> +     return input_register_device(input);
>> +}
>> +
>> +static int mxs_lradc_ts_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct device_node *node = dev->parent->of_node;
>> +     struct mxs_lradc *lradc = dev_get_platdata(dev);
>> +     struct mxs_lradc_ts *ts;
>> +     struct input_dev *input;
>> +     int touch_ret, ret, i;
>> +     u32 ts_wires = 0, adapt;
>> +
>> +     ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
>> +     input = devm_input_allocate_device(dev);
>> +     if (!ts || !input)
>> +             return -ENOMEM;
>> +
>> +     ts->lradc = lradc;
>> +     ts->dev = dev;
>> +     ts->ts_input = input;
>> +     platform_set_drvdata(pdev, ts);
>> +     input_set_drvdata(input, ts);
>> +
>> +     touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
>> +                                      &ts_wires);
>> +
>> +     if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) {
>> +             ts->over_sample_cnt = 4;
>> +     } else {
>> +             if (adapt < 1 || adapt > 32) {
>> +                     dev_err(ts->dev, "Invalid sample count (%u)\n",
>> +                             adapt);
>> +                     touch_ret = -EINVAL;
>> +             } else {
>> +                     ts->over_sample_cnt = adapt;
>> +             }
>> +     }
>> +
>> +     if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) {
>> +             ts->over_sample_delay = 2;
>> +     } else {
>> +             if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) {
>> +                     dev_err(ts->dev, "Invalid sample delay (%u)\n",
>> +                             adapt);
>> +                     touch_ret = -EINVAL;
>> +             } else {
>> +                     ts->over_sample_delay = adapt;
>> +             }
>> +     }
>> +
>> +     if (of_property_read_u32(node, "fsl,settling", &adapt)) {
>> +             ts->settling_delay = 10;
>> +     } else {
>> +             if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) {
>> +                     dev_err(ts->dev, "Invalid settling delay (%u)\n",
>> +                             adapt);
>> +                     touch_ret = -EINVAL;
>> +             } else {
>> +                     ts->settling_delay = adapt;
>> +             }
>> +     }
>> +
>> +     mxs_lradc_ts_hw_init(ts);
>> +     for (i = 0; i < lradc->irq_count; i++) {
>> +             ret = devm_request_irq(dev, lradc->irq[i],
>> +                                    mxs_lradc_ts_handle_irq,
>> +                                    IRQF_SHARED, lradc->irq_name[i], ts);
> As with the adc driver, are we actually using all of these?  I'd prefer we
> only grab the ones that are actually relevant.

Only irq lines relevant for touchscreen are:
mxs-lradc-touchscreen, mxs-lradc-channel6 and mxs-lradc-channel7
But not all interrupts are beiing used even when I enabled all remaining
channels (not used by touchscreen) for bufferd capture via
echo 1 >/sys/bus/iio/devices/iio\:device0/scan_elements/in_voltagexx_en

So I don't know if it's supposed to work like this...
(It works the same on the original code)

root@cfa100xx:~# cat /proc/interrupts
           CPU0
 16:      13108         -  48 Edge      MXS Timer Tick
 17:       4240         -  82 Edge      mxs-dma
 25:          6         -  96 Edge      80010000.ssp
196:          0         -  68 Edge      mxs-dma
210:         13         -  10 Edge      mxs-lradc-touchscreen
211:          0         -  14 Edge      mxs-lradc-thresh0
212:          0         -  15 Edge      mxs-lradc-thresh1
213:         10         -  16 Edge      mxs-lradc-channel0
214:         10         -  17 Edge      mxs-lradc-channel1
215:          0         -  18 Edge      mxs-lradc-channel2
216:          0         -  19 Edge      mxs-lradc-channel3
217:          0         -  20 Edge      mxs-lradc-channel4
218:          0         -  21 Edge      mxs-lradc-channel5
219:          0         -  22 Edge      mxs-lradc-channel6
220:        412         -  23 Edge      mxs-lradc-channel7
221:          0         -  24 Edge      mxs-lradc-button0
222:          0         -  25 Edge      mxs-lradc-button1
223:          0         -  29 Edge      RTC alarm
224:          0         - 111 Edge      80058000.i2c
228:        174         -  47 Edge      uart-pl011
229:        439         -  93 Edge      80080000.usb
230:          0         -  92 Edge      80090000.usb
231:       3610         - 101 Edge      800f0000.ethernet
232:         10  80050000.lradc-dev0 Edge
Err:          0

>> +             if (ret)
>> +                     return ret;
>> +     }
>> +
>> +     if (!touch_ret) {
>> +             ret = mxs_lradc_ts_register(ts);
>> +             if (!ret)
>> +                     goto err_ts_register;
>> +     }
>> +
>> +     return 0;
>> +
>> +err_ts_register:
>> +     mxs_lradc_ts_hw_stop(ts);
>> +     return ret;
>> +}
>> +
>> +static int mxs_lradc_ts_remove(struct platform_device *pdev)
>> +{
>> +     struct mxs_lradc_ts *ts = platform_get_drvdata(pdev);
>> +
>> +     mxs_lradc_ts_hw_stop(ts);
>> +
>> +     return 0;
>> +}
>> +
>> +static struct platform_driver mxs_lradc_ts_driver = {
>> +     .driver = {
>> +             .name = DRIVER_NAME_TS,
>> +     },
>> +     .probe  = mxs_lradc_ts_probe,
>> +     .remove = mxs_lradc_ts_remove,
>> +};
>> +module_platform_driver(mxs_lradc_ts_driver);
>> +
>> +MODULE_LICENSE("GPL v2");
>>
>

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
  2016-04-29 23:36     ` Dmitry Torokhov
@ 2016-05-28 17:46       ` Ksenija Stanojević
  -1 siblings, 0 replies; 40+ messages in thread
From: Ksenija Stanojević @ 2016-05-28 17:46 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, Lee Jones, linux-input, Jonathan Cameron,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Marek Vašut, linux-iio, Harald Geyer

On Sat, Apr 30, 2016 at 1:36 AM, Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
> Hi Ksenija,
>
> On Fri, Apr 29, 2016 at 01:49:11PM +0200, Ksenija Stanojevic wrote:
>> Add mxs-lradc touchscreen driver.
>>
>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
>> ---
>>  drivers/input/touchscreen/Kconfig        |  14 +-
>>  drivers/input/touchscreen/Makefile       |   1 +
>>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
>>  3 files changed, 742 insertions(+), 2 deletions(-)
>>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
>>
>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>> index 8ecdc38..d614d248 100644
>> --- a/drivers/input/touchscreen/Kconfig
>> +++ b/drivers/input/touchscreen/Kconfig
>> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
>>       depends on SH_HP6XX && SH_ADC
>>       help
>>         Say Y here if you have a HP Jornada 620/660/680/690 and want to
>> -          support the built-in touchscreen.
>> +       support the built-in touchscreen.
>>
>>         To compile this driver as a module, choose M here: the
>>         module will be called hp680_ts_input.
>> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
>>         This enables support for the Philips UCB1400 touchscreen interface.
>>         The UCB1400 is an AC97 audio codec.  The touchscreen interface
>>         will be initialized only after the ALSA subsystem has been
>> -       brought up and the UCB1400 detected.  You therefore have to
>> +       brought up and the UCB1400 detected.  You therefore have to
>
> Why do we have the tab in the middle of the text?
>
>>         configure ALSA support as well (either built-in or modular,
>>         independently of whether this driver is itself built-in or
>>         modular) for this driver to work.
>> @@ -842,6 +842,16 @@ config TOUCHSCREEN_MX25
>>         To compile this driver as a module, choose M here: the
>>         module will be called fsl-imx25-tcq.
>>
>> +config TOUCHSCREEN_MXS_LRADC
>> +     tristate "Freescale i.MX23/i.MX28 LRADC touchscreen"
>> +     depends on MFD_MXS_LRADC
>> +     help
>> +       Say Y here if you have a touchscreen connected to the low-resolution
>> +       analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor.
>> +
>> +       To compile this driver as a module, choose M here: the module will be
>> +       called mxs-lradc-ts.
>> +
>>  config TOUCHSCREEN_MC13783
>>       tristate "Freescale MC13783 touchscreen input driver"
>>       depends on MFD_MC13XXX
>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
>> index f42975e..513a6ff 100644
>> --- a/drivers/input/touchscreen/Makefile
>> +++ b/drivers/input/touchscreen/Makefile
>> @@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR)             += migor_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_MMS114)     += mms114.o
>>  obj-$(CONFIG_TOUCHSCREEN_MTOUCH)     += mtouch.o
>>  obj-$(CONFIG_TOUCHSCREEN_MK712)              += mk712.o
>> +obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC)  += mxs-lradc-ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_HP600)              += hp680_ts_input.o
>>  obj-$(CONFIG_TOUCHSCREEN_HP7XX)              += jornada720_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o
>> diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c
>> new file mode 100644
>> index 0000000..27abb8e
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/mxs-lradc-ts.c
>> @@ -0,0 +1,729 @@
>> +/*
>> + * Freescale MXS LRADC driver
>> + *
>> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
>> + * Marek Vasut <marex@denx.de>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/mfd/mxs-lradc.h>
>> +#include <linux/platform_device.h>
>> +
>> +/*
>> + * Touchscreen handling
>> + */
>> +enum mxs_lradc_ts_plate {
>> +     LRADC_TOUCH = 0,
>> +     LRADC_SAMPLE_X,
>> +     LRADC_SAMPLE_Y,
>> +     LRADC_SAMPLE_PRESSURE,
>> +     LRADC_SAMPLE_VALID,
>> +};
>> +
>> +struct mxs_lradc_ts {
>> +     struct mxs_lradc        *lradc;
>> +     struct device           *dev;
>> +     /*
>> +      * When the touchscreen is enabled, we give it two private virtual
>> +      * channels: #6 and #7. This means that only 6 virtual channels (instead
>> +      * of 8) will be available for buffered capture.
>> +      */
>> +#define TOUCHSCREEN_VCHANNEL1                7
>> +#define TOUCHSCREEN_VCHANNEL2                6
>> +
>> +     struct input_dev        *ts_input;
>> +
>> +     enum mxs_lradc_ts_plate cur_plate; /* state machine */
>> +     bool                    ts_valid;
>> +     unsigned                ts_x_pos;
>> +     unsigned                ts_y_pos;
>> +     unsigned                ts_pressure;
>> +
>> +     /* handle touchscreen's physical behaviour */
>> +     /* samples per coordinate */
>> +     unsigned                over_sample_cnt;
>> +     /* time clocks between samples */
>> +     unsigned                over_sample_delay;
>> +     /* time in clocks to wait after the plates where switched */
>> +     unsigned                settling_delay;
>> +};
>> +
>> +static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_PLATE_MASK;
>> +     return LRADC_CTRL0_MX28_PLATE_MASK;
>> +}
>> +
>> +static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
>> +     return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE;
>> +}
>> +
>> +static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM;
>> +     return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW;
>> +}
>> +
>> +static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM;
>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW;
>> +}
>> +
>> +static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM;
>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW;
>> +}
>> +
>> +static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
>> +{
>> +     return !!(readl(lradc->base + LRADC_STATUS) &
>> +                                     LRADC_STATUS_TOUCH_DETECT_RAW);
>> +}
>> +
>> +static void mxs_lradc_map_ts_channel(struct mxs_lradc *lradc, unsigned vch,
>> +                                  unsigned ch)
>> +{
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
>> +                         LRADC_CTRL4);
>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4);
>> +}
>> +
>> +static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned ch)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /*
>> +      * prepare for oversampling conversion
>> +      *
>> +      * from the datasheet:
>> +      * "The ACCUMULATE bit in the appropriate channel register
>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
>> +      * otherwise, the IRQs will not fire."
>> +      */
>> +     mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE |
>> +                       LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1),
>> +                       LRADC_CH(ch));
>> +
>> +     /* from the datasheet:
>> +      * "Software must clear this register in preparation for a
>> +      * multi-cycle accumulation.
>> +      */
>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch));
>> +
>> +     /*
>> +      * prepare the delay/loop unit according to the oversampling count
>> +      *
>> +      * from the datasheet:
>> +      * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
>> +      * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
>> +      * the LRADC will not trigger the delay group."
>> +      */
>> +     mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
>> +                       LRADC_DELAY_TRIGGER_DELAYS(0) |
>> +                       LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>> +                       LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>> +                       LRADC_DELAY(3));
>> +
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1);
>> +
>> +     /*
>> +      * after changing the touchscreen plates setting
>> +      * the signals need some initial time to settle. Start the
>> +      * SoC's delay unit and start the conversion later
>> +      * and automatically.
>> +      */
>> +     mxs_lradc_reg_wrt(
>> +             lradc,
>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
>> +             LRADC_DELAY_KICK |
>> +             LRADC_DELAY_DELAY(ts->settling_delay),
>> +             LRADC_DELAY(2));
>> +}
>> +
>> +/*
>> + * Pressure detection is special:
>> + * We want to do both required measurements for the pressure detection in
>> + * one turn. Use the hardware features to chain both conversions and let the
>> + * hardware report one interrupt if both conversions are done
>> + */
>> +static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts, unsigned ch1,
>> +                                     unsigned ch2)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +     u32 reg;
>> +
>> +     /*
>> +      * prepare for oversampling conversion
>> +      *
>> +      * from the datasheet:
>> +      * "The ACCUMULATE bit in the appropriate channel register
>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
>> +      * otherwise, the IRQs will not fire."
>> +      */
>> +     reg = LRADC_CH_ACCUMULATE |
>> +             LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1);
>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
>> +
>> +     /* from the datasheet:
>> +      * "Software must clear this register in preparation for a
>> +      * multi-cycle accumulation.
>> +      */
>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1));
>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2));
>> +
>> +     /* prepare the delay/loop unit according to the oversampling count */
>> +     mxs_lradc_reg_wrt(
>> +                 lradc,
>> +                 LRADC_DELAY_TRIGGER(1 << ch1) |
>> +                 LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */
>> +                 LRADC_DELAY_TRIGGER_DELAYS(0) |
>> +                 LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>> +                 LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>> +                 LRADC_DELAY(3));
>> +
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1);
>> +
>> +     /*
>> +      * after changing the touchscreen plates setting
>> +      * the signals need some initial time to settle. Start the
>> +      * SoC's delay unit and start the conversion later
>> +      * and automatically.
>> +      */
>> +     mxs_lradc_reg_wrt(
>> +             lradc,
>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
>> +             LRADC_DELAY_KICK |
>> +             LRADC_DELAY_DELAY(ts->settling_delay), LRADC_DELAY(2));
>> +}
>> +
>> +static unsigned mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts,
>> +                                           unsigned channel)
>> +{
>> +     u32 reg;
>> +     unsigned num_samples, val;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     reg = readl(lradc->base + LRADC_CH(channel));
>> +     if (reg & LRADC_CH_ACCUMULATE)
>> +             num_samples = ts->over_sample_cnt;
>> +     else
>> +             num_samples = 1;
>> +
>> +     val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
>> +     return val / num_samples;
>> +}
>> +
>> +static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts,
>> +                                        unsigned ch1, unsigned ch2)
>> +{
>> +     u32 reg, mask;
>> +     unsigned pressure, m1, m2;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
>> +     reg = readl(lradc->base + LRADC_CTRL1) & mask;
>> +
>> +     while (reg != mask) {
>> +             reg = readl(lradc->base + LRADC_CTRL1) & mask;
>> +             dev_dbg(ts->dev, "One channel is still busy: %X\n", reg);
>> +     }
>> +
>> +     m1 = mxs_lradc_ts_read_raw_channel(ts, ch1);
>> +     m2 = mxs_lradc_ts_read_raw_channel(ts, ch2);
>> +
>> +     if (m2 == 0) {
>> +             dev_warn(ts->dev, "Cannot calculate pressure\n");
>> +             return 1 << (LRADC_RESOLUTION - 1);
>> +     }
>> +
>> +     /* simply scale the value from 0 ... max ADC resolution */
>> +     pressure = m1;
>> +     pressure *= (1 << LRADC_RESOLUTION);
>> +     pressure /= m2;
>> +
>> +     dev_dbg(ts->dev, "Pressure = %u\n", pressure);
>> +     return pressure;
>> +}
>> +
>> +#define TS_CH_XP 2
>> +#define TS_CH_YP 3
>> +#define TS_CH_XM 4
>> +#define TS_CH_YM 5
>> +
>> +/*
>> + * YP(open)--+-------------+
>> + *        |             |--+
>> + *        |             |  |
>> + *    YM(-)--+-------------+  |
>> + *          +--------------+
>> + *          |              |
>> + *      XP(weak+)        XM(open)
>> + *
>> + * "weak+" means 200k Ohm VDDIO
>> + * (-) means GND
>> + */
>> +static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /*
>> +      * In order to detect a touch event the 'touch detect enable' bit
>> +      * enables:
>> +      *  - a weak pullup to the X+ connector
>> +      *  - a strong ground at the Y- connector
>> +      */
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc),
>> +                       LRADC_CTRL0);
>> +}
>> +
>> +/*
>> + * YP(meas)--+-------------+
>> + *        |             |--+
>> + *        |             |  |
>> + * YM(open)--+-------------+  |
>> + *          +--------------+
>> + *          |              |
>> + *        XP(+)          XM(-)
>> + *
>> + * (+) means here 1.85 V
>> + * (-) means here GND
>> + */
>> +static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
>> +
>> +     ts->cur_plate = LRADC_SAMPLE_X;
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +}
>> +
>> +/*
>> + *   YP(+)--+-------------+
>> + *       |             |--+
>> + *       |             |  |
>> + *   YM(-)--+-------------+  |
>> + *         +--------------+
>> + *         |              |
>> + *      XP(open)        XM(meas)
>> + *
>> + * (+) means here 1.85 V
>> + * (-) means here GND
>> + */
>> +static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
>> +
>> +     ts->cur_plate = LRADC_SAMPLE_Y;
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +}
>> +
>> +/*
>> + *    YP(+)--+-------------+
>> + *        |             |--+
>> + *        |             |  |
>> + * YM(meas)--+-------------+  |
>> + *          +--------------+
>> + *          |              |
>> + *       XP(meas)        XM(-)
>> + *
>> + * (+) means here 1.85 V
>> + * (-) means here GND
>> + */
>> +static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
>> +
>> +     ts->cur_plate = LRADC_SAMPLE_PRESSURE;
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
>> +     mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2,
>> +                                 TOUCHSCREEN_VCHANNEL1);
>> +}
>> +
>> +static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts)
>> +{
>> +     mxs_lradc_setup_touch_detection(ts);
>> +
>> +     ts->cur_plate = LRADC_TOUCH;
>> +     mxs_lradc_reg_clear(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
>> +     mxs_lradc_reg_set(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>> +                       LRADC_CTRL1);
>> +}
>> +
>> +static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts)
>> +{
>> +     mxs_lradc_reg_clear(ts->lradc,
>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>> +                         LRADC_CTRL1);
>> +     mxs_lradc_reg_set(ts->lradc,
>> +                       LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1),
>> +                       LRADC_CTRL1);
>> +     /*
>> +      * start with the Y-pos, because it uses nearly the same plate
>> +      * settings like the touch detection
>> +      */
>> +     mxs_lradc_prepare_y_pos(ts);
>> +}
>> +
>> +static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts)
>> +{
>> +     input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos);
>> +     input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos);
>> +     input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure);
>> +     input_report_key(ts->ts_input, BTN_TOUCH, 1);
>> +     input_sync(ts->ts_input);
>> +}
>> +
>> +static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_setup_touch_detection(ts);
>> +     ts->cur_plate = LRADC_SAMPLE_VALID;
>> +     /*
>> +      * start a dummy conversion to burn time to settle the signals
>> +      * note: we are not interested in the conversion's value
>> +      */
>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
>> +     mxs_lradc_reg_clear(lradc,
>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2),
>> +                         LRADC_CTRL1);
>> +     mxs_lradc_reg_wrt(
>> +                 lradc,
>> +                 LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
>> +                 LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
>> +                 LRADC_DELAY(2));
>> +}
>> +
>> +/*
>> + * in order to avoid false measurements, report only samples where
>> + * the surface is still touched after the position measurement
>> + */
>> +static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /* if it is still touched, report the sample */
>> +     if (valid && mxs_lradc_check_touch_event(lradc)) {
>> +             ts->ts_valid = true;
>> +             mxs_lradc_report_ts_event(ts);
>> +     }
>> +
>> +     /* if it is even still touched, continue with the next measurement */
>> +     if (mxs_lradc_check_touch_event(lradc)) {
>> +             mxs_lradc_prepare_y_pos(ts);
>> +             return;
>> +     }
>> +
>> +     if (ts->ts_valid) {
>> +             /* signal the release */
>> +             ts->ts_valid = false;
>> +             input_report_key(ts->ts_input, BTN_TOUCH, 0);
>> +             input_sync(ts->ts_input);
>> +     }
>> +
>> +     /* if it is released, wait for the next touch via IRQ */
>> +     ts->cur_plate = LRADC_TOUCH;
>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
>> +     mxs_lradc_reg_clear(lradc,
>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ |
>> +                         LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1),
>> +                         LRADC_CTRL1);
>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
>> +}
>> +
>> +/* touchscreen's state machine */
>> +static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     switch (ts->cur_plate) {
>> +     case LRADC_TOUCH:
>> +             if (mxs_lradc_check_touch_event(lradc))
>> +                     mxs_lradc_start_touch_event(ts);
>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
>> +                                 LRADC_CTRL1);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_Y:
>> +             ts->ts_y_pos =
>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +             mxs_lradc_prepare_x_pos(ts);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_X:
>> +             ts->ts_x_pos =
>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +             mxs_lradc_prepare_pressure(ts);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_PRESSURE:
>> +             ts->ts_pressure =
>> +                 mxs_lradc_read_ts_pressure(ts,
>> +                                            TOUCHSCREEN_VCHANNEL2,
>> +                                            TOUCHSCREEN_VCHANNEL1);
>> +             mxs_lradc_complete_touch_event(ts);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_VALID:
>> +             mxs_lradc_finish_touch_event(ts, 1);
>> +             break;
>> +     }
>> +}
>> +
>> +/*
>> + * IRQ Handling
>> + */
>> +static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data)
>> +{
>> +     struct mxs_lradc_ts *ts = data;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +     unsigned long reg = readl(lradc->base + LRADC_CTRL1);
>> +     u32 clr_irq = mxs_lradc_irq_mask(lradc);
>> +     const u32 ts_irq_mask =
>> +             LRADC_CTRL1_TOUCH_DETECT_IRQ |
>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
>> +
>> +     if (!(reg & mxs_lradc_irq_mask(lradc)))
>> +             return IRQ_NONE;
>> +
>> +     if (lradc->use_touchscreen && (reg & ts_irq_mask)) {
>
> Why do we have this flag instead of not creating the platform device by
> MFD parent when touchscreen is not in use?
>
>> +             mxs_lradc_handle_touch(ts);
>> +
>> +             /* Make sure we don't clear the next conversion's interrupt. */
>> +             clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>> +                             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
>> +             mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
>> +     }
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +static int mxs_lradc_ts_open(struct input_dev *dev)
>> +{
>> +     struct mxs_lradc_ts *ts = input_get_drvdata(dev);
>> +
>> +     /* Enable the touch-detect circuitry. */
>> +     mxs_lradc_enable_touch_detection(ts);
>> +
>> +     return 0;
>> +}
>> +
>> +static void mxs_lradc_disable_ts(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /* stop all interrupts from firing */
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
>> +
>> +     /* Power-down touchscreen touch-detect circuitry. */
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +}
>> +
>> +static void mxs_lradc_ts_close(struct input_dev *dev)
>> +{
>> +     struct mxs_lradc_ts *ts = input_get_drvdata(dev);
>> +
>> +     mxs_lradc_disable_ts(ts);
>> +}
>> +
>> +static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /* Configure the touchscreen type */
>> +     if (lradc->soc == IMX28_LRADC) {
>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
>> +                                 LRADC_CTRL0);
>> +
>> +             if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
>> +                     mxs_lradc_reg_set(lradc,
>> +                                       LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
>> +                                       LRADC_CTRL0);
>> +     }
>> +}
>> +
>> +static void mxs_lradc_ts_hw_stop(struct mxs_lradc_ts *ts)
>> +{
>> +     int i;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc,
>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
>> +                     LRADC_CTRL1);
>> +
>> +     for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++)
>> +             mxs_lradc_reg_wrt(ts->lradc, 0, LRADC_DELAY(i));
>> +}
>> +
>> +static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts)
>> +{
>> +     struct input_dev *input = ts->ts_input;
>> +     struct device *dev = ts->dev;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     if (!lradc->use_touchscreen)
>> +             return 0;
>> +
>> +     input->name = DRIVER_NAME_TS;
>> +     input->id.bustype = BUS_HOST;
>> +     input->dev.parent = dev;
>
> Not needed for devm-managed input devices.
>
>> +     input->open = mxs_lradc_ts_open;
>> +     input->close = mxs_lradc_ts_close;
>> +
>> +     __set_bit(EV_ABS, input->evbit);
>> +     __set_bit(EV_KEY, input->evbit);
>> +     __set_bit(BTN_TOUCH, input->keybit);
>> +     __set_bit(INPUT_PROP_DIRECT, input->propbit);
>> +     input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
>> +     input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
>> +     input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
>> +                          0, 0);
>> +
>> +     ts->ts_input = input;
>> +     input_set_drvdata(input, ts);
>> +
>> +     return input_register_device(input);
>> +}
>> +
>> +static int mxs_lradc_ts_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct device_node *node = dev->parent->of_node;
>> +     struct mxs_lradc *lradc = dev_get_platdata(dev);
>> +     struct mxs_lradc_ts *ts;
>> +     struct input_dev *input;
>> +     int touch_ret, ret, i;
>> +     u32 ts_wires = 0, adapt;
>> +
>> +     ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
>> +     input = devm_input_allocate_device(dev);
>
> I'd expect input allocation be done in mxs_lradc_ts_register().
>
>> +     if (!ts || !input)
>> +             return -ENOMEM;
>> +
>> +     ts->lradc = lradc;
>> +     ts->dev = dev;
>> +     ts->ts_input = input;
>> +     platform_set_drvdata(pdev, ts);
>> +     input_set_drvdata(input, ts);
>> +
>> +     touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
>> +                                      &ts_wires);
>> +
>> +     if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) {
>> +             ts->over_sample_cnt = 4;
>> +     } else {
>> +             if (adapt < 1 || adapt > 32) {
>> +                     dev_err(ts->dev, "Invalid sample count (%u)\n",
>> +                             adapt);
>> +                     touch_ret = -EINVAL;
>> +             } else {
>> +                     ts->over_sample_cnt = adapt;
>> +             }
>> +     }
>> +
>> +     if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) {
>> +             ts->over_sample_delay = 2;
>> +     } else {
>> +             if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) {
>> +                     dev_err(ts->dev, "Invalid sample delay (%u)\n",
>> +                             adapt);
>> +                     touch_ret = -EINVAL;
>> +             } else {
>> +                     ts->over_sample_delay = adapt;
>> +             }
>> +     }
>> +
>> +     if (of_property_read_u32(node, "fsl,settling", &adapt)) {
>> +             ts->settling_delay = 10;
>> +     } else {
>> +             if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) {
>> +                     dev_err(ts->dev, "Invalid settling delay (%u)\n",
>> +                             adapt);
>> +                     touch_ret = -EINVAL;
>
> Why are we not aborting here?
>
>> +             } else {
>> +                     ts->settling_delay = adapt;
>> +             }
>> +     }
>> +
>> +     mxs_lradc_ts_hw_init(ts);
>> +     for (i = 0; i < lradc->irq_count; i++) {
>> +             ret = devm_request_irq(dev, lradc->irq[i],
>> +                                    mxs_lradc_ts_handle_irq,
>
> Hmm, if you have several interrupts handled by the same interrupt
> handler you'd need some locking there.

same interrupt handler can run concurrently only on multi cores, so
why do we need locking?

>
>> +                                    IRQF_SHARED, lradc->irq_name[i], ts);
>> +             if (ret)
>> +                     return ret;
>> +     }
>> +
>> +     if (!touch_ret) {
>> +             ret = mxs_lradc_ts_register(ts);
>> +             if (!ret)
>> +                     goto err_ts_register;
>> +     }
>> +
>> +     return 0;
>> +
>> +err_ts_register:
>> +     mxs_lradc_ts_hw_stop(ts);
>> +     return ret;
>
> Would prefer calling this variable "error".
>
>> +}
>> +
>> +static int mxs_lradc_ts_remove(struct platform_device *pdev)
>> +{
>> +     struct mxs_lradc_ts *ts = platform_get_drvdata(pdev);
>> +
>> +     mxs_lradc_ts_hw_stop(ts);
>
> I wonder if mxs_lradc_ts_hw_stop() can be combined with
> mxs_lradc_disable_ts() which is called automatically if input device is
> opened? This way we'd be able to get rid of mxs_lradc_ts_remove().
>
>> +
>> +     return 0;
>> +}
>> +
>> +static struct platform_driver mxs_lradc_ts_driver = {
>> +     .driver = {
>> +             .name = DRIVER_NAME_TS,
>> +     },
>> +     .probe  = mxs_lradc_ts_probe,
>> +     .remove = mxs_lradc_ts_remove,
>> +};
>> +module_platform_driver(mxs_lradc_ts_driver);
>> +
>> +MODULE_LICENSE("GPL v2");
>
> "GPL" since you are not limiting to v2 only.
>
> Thanks.
>
> --
> Dmitry

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
@ 2016-05-28 17:46       ` Ksenija Stanojević
  0 siblings, 0 replies; 40+ messages in thread
From: Ksenija Stanojević @ 2016-05-28 17:46 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Lee Jones,
	linux-input-u79uwXL29TY76Z2rM5mHXA, Jonathan Cameron,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Marek Vašut, linux-iio-u79uwXL29TY76Z2rM5mHXA, Harald Geyer

On Sat, Apr 30, 2016 at 1:36 AM, Dmitry Torokhov
<dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> Hi Ksenija,
>
> On Fri, Apr 29, 2016 at 01:49:11PM +0200, Ksenija Stanojevic wrote:
>> Add mxs-lradc touchscreen driver.
>>
>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> ---
>>  drivers/input/touchscreen/Kconfig        |  14 +-
>>  drivers/input/touchscreen/Makefile       |   1 +
>>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
>>  3 files changed, 742 insertions(+), 2 deletions(-)
>>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
>>
>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>> index 8ecdc38..d614d248 100644
>> --- a/drivers/input/touchscreen/Kconfig
>> +++ b/drivers/input/touchscreen/Kconfig
>> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
>>       depends on SH_HP6XX && SH_ADC
>>       help
>>         Say Y here if you have a HP Jornada 620/660/680/690 and want to
>> -          support the built-in touchscreen.
>> +       support the built-in touchscreen.
>>
>>         To compile this driver as a module, choose M here: the
>>         module will be called hp680_ts_input.
>> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
>>         This enables support for the Philips UCB1400 touchscreen interface.
>>         The UCB1400 is an AC97 audio codec.  The touchscreen interface
>>         will be initialized only after the ALSA subsystem has been
>> -       brought up and the UCB1400 detected.  You therefore have to
>> +       brought up and the UCB1400 detected.  You therefore have to
>
> Why do we have the tab in the middle of the text?
>
>>         configure ALSA support as well (either built-in or modular,
>>         independently of whether this driver is itself built-in or
>>         modular) for this driver to work.
>> @@ -842,6 +842,16 @@ config TOUCHSCREEN_MX25
>>         To compile this driver as a module, choose M here: the
>>         module will be called fsl-imx25-tcq.
>>
>> +config TOUCHSCREEN_MXS_LRADC
>> +     tristate "Freescale i.MX23/i.MX28 LRADC touchscreen"
>> +     depends on MFD_MXS_LRADC
>> +     help
>> +       Say Y here if you have a touchscreen connected to the low-resolution
>> +       analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor.
>> +
>> +       To compile this driver as a module, choose M here: the module will be
>> +       called mxs-lradc-ts.
>> +
>>  config TOUCHSCREEN_MC13783
>>       tristate "Freescale MC13783 touchscreen input driver"
>>       depends on MFD_MC13XXX
>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
>> index f42975e..513a6ff 100644
>> --- a/drivers/input/touchscreen/Makefile
>> +++ b/drivers/input/touchscreen/Makefile
>> @@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR)             += migor_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_MMS114)     += mms114.o
>>  obj-$(CONFIG_TOUCHSCREEN_MTOUCH)     += mtouch.o
>>  obj-$(CONFIG_TOUCHSCREEN_MK712)              += mk712.o
>> +obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC)  += mxs-lradc-ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_HP600)              += hp680_ts_input.o
>>  obj-$(CONFIG_TOUCHSCREEN_HP7XX)              += jornada720_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o
>> diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c
>> new file mode 100644
>> index 0000000..27abb8e
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/mxs-lradc-ts.c
>> @@ -0,0 +1,729 @@
>> +/*
>> + * Freescale MXS LRADC driver
>> + *
>> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
>> + * Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/mfd/mxs-lradc.h>
>> +#include <linux/platform_device.h>
>> +
>> +/*
>> + * Touchscreen handling
>> + */
>> +enum mxs_lradc_ts_plate {
>> +     LRADC_TOUCH = 0,
>> +     LRADC_SAMPLE_X,
>> +     LRADC_SAMPLE_Y,
>> +     LRADC_SAMPLE_PRESSURE,
>> +     LRADC_SAMPLE_VALID,
>> +};
>> +
>> +struct mxs_lradc_ts {
>> +     struct mxs_lradc        *lradc;
>> +     struct device           *dev;
>> +     /*
>> +      * When the touchscreen is enabled, we give it two private virtual
>> +      * channels: #6 and #7. This means that only 6 virtual channels (instead
>> +      * of 8) will be available for buffered capture.
>> +      */
>> +#define TOUCHSCREEN_VCHANNEL1                7
>> +#define TOUCHSCREEN_VCHANNEL2                6
>> +
>> +     struct input_dev        *ts_input;
>> +
>> +     enum mxs_lradc_ts_plate cur_plate; /* state machine */
>> +     bool                    ts_valid;
>> +     unsigned                ts_x_pos;
>> +     unsigned                ts_y_pos;
>> +     unsigned                ts_pressure;
>> +
>> +     /* handle touchscreen's physical behaviour */
>> +     /* samples per coordinate */
>> +     unsigned                over_sample_cnt;
>> +     /* time clocks between samples */
>> +     unsigned                over_sample_delay;
>> +     /* time in clocks to wait after the plates where switched */
>> +     unsigned                settling_delay;
>> +};
>> +
>> +static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_PLATE_MASK;
>> +     return LRADC_CTRL0_MX28_PLATE_MASK;
>> +}
>> +
>> +static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
>> +     return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE;
>> +}
>> +
>> +static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM;
>> +     return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW;
>> +}
>> +
>> +static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM;
>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW;
>> +}
>> +
>> +static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc)
>> +{
>> +     if (lradc->soc == IMX23_LRADC)
>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM;
>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW;
>> +}
>> +
>> +static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
>> +{
>> +     return !!(readl(lradc->base + LRADC_STATUS) &
>> +                                     LRADC_STATUS_TOUCH_DETECT_RAW);
>> +}
>> +
>> +static void mxs_lradc_map_ts_channel(struct mxs_lradc *lradc, unsigned vch,
>> +                                  unsigned ch)
>> +{
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
>> +                         LRADC_CTRL4);
>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4);
>> +}
>> +
>> +static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned ch)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /*
>> +      * prepare for oversampling conversion
>> +      *
>> +      * from the datasheet:
>> +      * "The ACCUMULATE bit in the appropriate channel register
>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
>> +      * otherwise, the IRQs will not fire."
>> +      */
>> +     mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE |
>> +                       LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1),
>> +                       LRADC_CH(ch));
>> +
>> +     /* from the datasheet:
>> +      * "Software must clear this register in preparation for a
>> +      * multi-cycle accumulation.
>> +      */
>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch));
>> +
>> +     /*
>> +      * prepare the delay/loop unit according to the oversampling count
>> +      *
>> +      * from the datasheet:
>> +      * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
>> +      * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
>> +      * the LRADC will not trigger the delay group."
>> +      */
>> +     mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
>> +                       LRADC_DELAY_TRIGGER_DELAYS(0) |
>> +                       LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>> +                       LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>> +                       LRADC_DELAY(3));
>> +
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1);
>> +
>> +     /*
>> +      * after changing the touchscreen plates setting
>> +      * the signals need some initial time to settle. Start the
>> +      * SoC's delay unit and start the conversion later
>> +      * and automatically.
>> +      */
>> +     mxs_lradc_reg_wrt(
>> +             lradc,
>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
>> +             LRADC_DELAY_KICK |
>> +             LRADC_DELAY_DELAY(ts->settling_delay),
>> +             LRADC_DELAY(2));
>> +}
>> +
>> +/*
>> + * Pressure detection is special:
>> + * We want to do both required measurements for the pressure detection in
>> + * one turn. Use the hardware features to chain both conversions and let the
>> + * hardware report one interrupt if both conversions are done
>> + */
>> +static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts, unsigned ch1,
>> +                                     unsigned ch2)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +     u32 reg;
>> +
>> +     /*
>> +      * prepare for oversampling conversion
>> +      *
>> +      * from the datasheet:
>> +      * "The ACCUMULATE bit in the appropriate channel register
>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
>> +      * otherwise, the IRQs will not fire."
>> +      */
>> +     reg = LRADC_CH_ACCUMULATE |
>> +             LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1);
>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
>> +
>> +     /* from the datasheet:
>> +      * "Software must clear this register in preparation for a
>> +      * multi-cycle accumulation.
>> +      */
>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1));
>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2));
>> +
>> +     /* prepare the delay/loop unit according to the oversampling count */
>> +     mxs_lradc_reg_wrt(
>> +                 lradc,
>> +                 LRADC_DELAY_TRIGGER(1 << ch1) |
>> +                 LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */
>> +                 LRADC_DELAY_TRIGGER_DELAYS(0) |
>> +                 LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>> +                 LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>> +                 LRADC_DELAY(3));
>> +
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1);
>> +
>> +     /*
>> +      * after changing the touchscreen plates setting
>> +      * the signals need some initial time to settle. Start the
>> +      * SoC's delay unit and start the conversion later
>> +      * and automatically.
>> +      */
>> +     mxs_lradc_reg_wrt(
>> +             lradc,
>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
>> +             LRADC_DELAY_KICK |
>> +             LRADC_DELAY_DELAY(ts->settling_delay), LRADC_DELAY(2));
>> +}
>> +
>> +static unsigned mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts,
>> +                                           unsigned channel)
>> +{
>> +     u32 reg;
>> +     unsigned num_samples, val;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     reg = readl(lradc->base + LRADC_CH(channel));
>> +     if (reg & LRADC_CH_ACCUMULATE)
>> +             num_samples = ts->over_sample_cnt;
>> +     else
>> +             num_samples = 1;
>> +
>> +     val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
>> +     return val / num_samples;
>> +}
>> +
>> +static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts,
>> +                                        unsigned ch1, unsigned ch2)
>> +{
>> +     u32 reg, mask;
>> +     unsigned pressure, m1, m2;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
>> +     reg = readl(lradc->base + LRADC_CTRL1) & mask;
>> +
>> +     while (reg != mask) {
>> +             reg = readl(lradc->base + LRADC_CTRL1) & mask;
>> +             dev_dbg(ts->dev, "One channel is still busy: %X\n", reg);
>> +     }
>> +
>> +     m1 = mxs_lradc_ts_read_raw_channel(ts, ch1);
>> +     m2 = mxs_lradc_ts_read_raw_channel(ts, ch2);
>> +
>> +     if (m2 == 0) {
>> +             dev_warn(ts->dev, "Cannot calculate pressure\n");
>> +             return 1 << (LRADC_RESOLUTION - 1);
>> +     }
>> +
>> +     /* simply scale the value from 0 ... max ADC resolution */
>> +     pressure = m1;
>> +     pressure *= (1 << LRADC_RESOLUTION);
>> +     pressure /= m2;
>> +
>> +     dev_dbg(ts->dev, "Pressure = %u\n", pressure);
>> +     return pressure;
>> +}
>> +
>> +#define TS_CH_XP 2
>> +#define TS_CH_YP 3
>> +#define TS_CH_XM 4
>> +#define TS_CH_YM 5
>> +
>> +/*
>> + * YP(open)--+-------------+
>> + *        |             |--+
>> + *        |             |  |
>> + *    YM(-)--+-------------+  |
>> + *          +--------------+
>> + *          |              |
>> + *      XP(weak+)        XM(open)
>> + *
>> + * "weak+" means 200k Ohm VDDIO
>> + * (-) means GND
>> + */
>> +static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /*
>> +      * In order to detect a touch event the 'touch detect enable' bit
>> +      * enables:
>> +      *  - a weak pullup to the X+ connector
>> +      *  - a strong ground at the Y- connector
>> +      */
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc),
>> +                       LRADC_CTRL0);
>> +}
>> +
>> +/*
>> + * YP(meas)--+-------------+
>> + *        |             |--+
>> + *        |             |  |
>> + * YM(open)--+-------------+  |
>> + *          +--------------+
>> + *          |              |
>> + *        XP(+)          XM(-)
>> + *
>> + * (+) means here 1.85 V
>> + * (-) means here GND
>> + */
>> +static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
>> +
>> +     ts->cur_plate = LRADC_SAMPLE_X;
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +}
>> +
>> +/*
>> + *   YP(+)--+-------------+
>> + *       |             |--+
>> + *       |             |  |
>> + *   YM(-)--+-------------+  |
>> + *         +--------------+
>> + *         |              |
>> + *      XP(open)        XM(meas)
>> + *
>> + * (+) means here 1.85 V
>> + * (-) means here GND
>> + */
>> +static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
>> +
>> +     ts->cur_plate = LRADC_SAMPLE_Y;
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +}
>> +
>> +/*
>> + *    YP(+)--+-------------+
>> + *        |             |--+
>> + *        |             |  |
>> + * YM(meas)--+-------------+  |
>> + *          +--------------+
>> + *          |              |
>> + *       XP(meas)        XM(-)
>> + *
>> + * (+) means here 1.85 V
>> + * (-) means here GND
>> + */
>> +static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
>> +
>> +     ts->cur_plate = LRADC_SAMPLE_PRESSURE;
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
>> +     mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2,
>> +                                 TOUCHSCREEN_VCHANNEL1);
>> +}
>> +
>> +static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts)
>> +{
>> +     mxs_lradc_setup_touch_detection(ts);
>> +
>> +     ts->cur_plate = LRADC_TOUCH;
>> +     mxs_lradc_reg_clear(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
>> +     mxs_lradc_reg_set(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>> +                       LRADC_CTRL1);
>> +}
>> +
>> +static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts)
>> +{
>> +     mxs_lradc_reg_clear(ts->lradc,
>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>> +                         LRADC_CTRL1);
>> +     mxs_lradc_reg_set(ts->lradc,
>> +                       LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1),
>> +                       LRADC_CTRL1);
>> +     /*
>> +      * start with the Y-pos, because it uses nearly the same plate
>> +      * settings like the touch detection
>> +      */
>> +     mxs_lradc_prepare_y_pos(ts);
>> +}
>> +
>> +static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts)
>> +{
>> +     input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos);
>> +     input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos);
>> +     input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure);
>> +     input_report_key(ts->ts_input, BTN_TOUCH, 1);
>> +     input_sync(ts->ts_input);
>> +}
>> +
>> +static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_setup_touch_detection(ts);
>> +     ts->cur_plate = LRADC_SAMPLE_VALID;
>> +     /*
>> +      * start a dummy conversion to burn time to settle the signals
>> +      * note: we are not interested in the conversion's value
>> +      */
>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
>> +     mxs_lradc_reg_clear(lradc,
>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2),
>> +                         LRADC_CTRL1);
>> +     mxs_lradc_reg_wrt(
>> +                 lradc,
>> +                 LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
>> +                 LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
>> +                 LRADC_DELAY(2));
>> +}
>> +
>> +/*
>> + * in order to avoid false measurements, report only samples where
>> + * the surface is still touched after the position measurement
>> + */
>> +static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /* if it is still touched, report the sample */
>> +     if (valid && mxs_lradc_check_touch_event(lradc)) {
>> +             ts->ts_valid = true;
>> +             mxs_lradc_report_ts_event(ts);
>> +     }
>> +
>> +     /* if it is even still touched, continue with the next measurement */
>> +     if (mxs_lradc_check_touch_event(lradc)) {
>> +             mxs_lradc_prepare_y_pos(ts);
>> +             return;
>> +     }
>> +
>> +     if (ts->ts_valid) {
>> +             /* signal the release */
>> +             ts->ts_valid = false;
>> +             input_report_key(ts->ts_input, BTN_TOUCH, 0);
>> +             input_sync(ts->ts_input);
>> +     }
>> +
>> +     /* if it is released, wait for the next touch via IRQ */
>> +     ts->cur_plate = LRADC_TOUCH;
>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
>> +     mxs_lradc_reg_clear(lradc,
>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ |
>> +                         LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1),
>> +                         LRADC_CTRL1);
>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
>> +}
>> +
>> +/* touchscreen's state machine */
>> +static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     switch (ts->cur_plate) {
>> +     case LRADC_TOUCH:
>> +             if (mxs_lradc_check_touch_event(lradc))
>> +                     mxs_lradc_start_touch_event(ts);
>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
>> +                                 LRADC_CTRL1);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_Y:
>> +             ts->ts_y_pos =
>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +             mxs_lradc_prepare_x_pos(ts);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_X:
>> +             ts->ts_x_pos =
>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
>> +             mxs_lradc_prepare_pressure(ts);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_PRESSURE:
>> +             ts->ts_pressure =
>> +                 mxs_lradc_read_ts_pressure(ts,
>> +                                            TOUCHSCREEN_VCHANNEL2,
>> +                                            TOUCHSCREEN_VCHANNEL1);
>> +             mxs_lradc_complete_touch_event(ts);
>> +             return;
>> +
>> +     case LRADC_SAMPLE_VALID:
>> +             mxs_lradc_finish_touch_event(ts, 1);
>> +             break;
>> +     }
>> +}
>> +
>> +/*
>> + * IRQ Handling
>> + */
>> +static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data)
>> +{
>> +     struct mxs_lradc_ts *ts = data;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +     unsigned long reg = readl(lradc->base + LRADC_CTRL1);
>> +     u32 clr_irq = mxs_lradc_irq_mask(lradc);
>> +     const u32 ts_irq_mask =
>> +             LRADC_CTRL1_TOUCH_DETECT_IRQ |
>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
>> +
>> +     if (!(reg & mxs_lradc_irq_mask(lradc)))
>> +             return IRQ_NONE;
>> +
>> +     if (lradc->use_touchscreen && (reg & ts_irq_mask)) {
>
> Why do we have this flag instead of not creating the platform device by
> MFD parent when touchscreen is not in use?
>
>> +             mxs_lradc_handle_touch(ts);
>> +
>> +             /* Make sure we don't clear the next conversion's interrupt. */
>> +             clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>> +                             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
>> +             mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
>> +     }
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +static int mxs_lradc_ts_open(struct input_dev *dev)
>> +{
>> +     struct mxs_lradc_ts *ts = input_get_drvdata(dev);
>> +
>> +     /* Enable the touch-detect circuitry. */
>> +     mxs_lradc_enable_touch_detection(ts);
>> +
>> +     return 0;
>> +}
>> +
>> +static void mxs_lradc_disable_ts(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /* stop all interrupts from firing */
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
>> +
>> +     /* Power-down touchscreen touch-detect circuitry. */
>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>> +}
>> +
>> +static void mxs_lradc_ts_close(struct input_dev *dev)
>> +{
>> +     struct mxs_lradc_ts *ts = input_get_drvdata(dev);
>> +
>> +     mxs_lradc_disable_ts(ts);
>> +}
>> +
>> +static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts)
>> +{
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     /* Configure the touchscreen type */
>> +     if (lradc->soc == IMX28_LRADC) {
>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
>> +                                 LRADC_CTRL0);
>> +
>> +             if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
>> +                     mxs_lradc_reg_set(lradc,
>> +                                       LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
>> +                                       LRADC_CTRL0);
>> +     }
>> +}
>> +
>> +static void mxs_lradc_ts_hw_stop(struct mxs_lradc_ts *ts)
>> +{
>> +     int i;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc,
>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
>> +                     LRADC_CTRL1);
>> +
>> +     for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++)
>> +             mxs_lradc_reg_wrt(ts->lradc, 0, LRADC_DELAY(i));
>> +}
>> +
>> +static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts)
>> +{
>> +     struct input_dev *input = ts->ts_input;
>> +     struct device *dev = ts->dev;
>> +     struct mxs_lradc *lradc = ts->lradc;
>> +
>> +     if (!lradc->use_touchscreen)
>> +             return 0;
>> +
>> +     input->name = DRIVER_NAME_TS;
>> +     input->id.bustype = BUS_HOST;
>> +     input->dev.parent = dev;
>
> Not needed for devm-managed input devices.
>
>> +     input->open = mxs_lradc_ts_open;
>> +     input->close = mxs_lradc_ts_close;
>> +
>> +     __set_bit(EV_ABS, input->evbit);
>> +     __set_bit(EV_KEY, input->evbit);
>> +     __set_bit(BTN_TOUCH, input->keybit);
>> +     __set_bit(INPUT_PROP_DIRECT, input->propbit);
>> +     input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
>> +     input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
>> +     input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
>> +                          0, 0);
>> +
>> +     ts->ts_input = input;
>> +     input_set_drvdata(input, ts);
>> +
>> +     return input_register_device(input);
>> +}
>> +
>> +static int mxs_lradc_ts_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct device_node *node = dev->parent->of_node;
>> +     struct mxs_lradc *lradc = dev_get_platdata(dev);
>> +     struct mxs_lradc_ts *ts;
>> +     struct input_dev *input;
>> +     int touch_ret, ret, i;
>> +     u32 ts_wires = 0, adapt;
>> +
>> +     ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
>> +     input = devm_input_allocate_device(dev);
>
> I'd expect input allocation be done in mxs_lradc_ts_register().
>
>> +     if (!ts || !input)
>> +             return -ENOMEM;
>> +
>> +     ts->lradc = lradc;
>> +     ts->dev = dev;
>> +     ts->ts_input = input;
>> +     platform_set_drvdata(pdev, ts);
>> +     input_set_drvdata(input, ts);
>> +
>> +     touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
>> +                                      &ts_wires);
>> +
>> +     if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) {
>> +             ts->over_sample_cnt = 4;
>> +     } else {
>> +             if (adapt < 1 || adapt > 32) {
>> +                     dev_err(ts->dev, "Invalid sample count (%u)\n",
>> +                             adapt);
>> +                     touch_ret = -EINVAL;
>> +             } else {
>> +                     ts->over_sample_cnt = adapt;
>> +             }
>> +     }
>> +
>> +     if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) {
>> +             ts->over_sample_delay = 2;
>> +     } else {
>> +             if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) {
>> +                     dev_err(ts->dev, "Invalid sample delay (%u)\n",
>> +                             adapt);
>> +                     touch_ret = -EINVAL;
>> +             } else {
>> +                     ts->over_sample_delay = adapt;
>> +             }
>> +     }
>> +
>> +     if (of_property_read_u32(node, "fsl,settling", &adapt)) {
>> +             ts->settling_delay = 10;
>> +     } else {
>> +             if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) {
>> +                     dev_err(ts->dev, "Invalid settling delay (%u)\n",
>> +                             adapt);
>> +                     touch_ret = -EINVAL;
>
> Why are we not aborting here?
>
>> +             } else {
>> +                     ts->settling_delay = adapt;
>> +             }
>> +     }
>> +
>> +     mxs_lradc_ts_hw_init(ts);
>> +     for (i = 0; i < lradc->irq_count; i++) {
>> +             ret = devm_request_irq(dev, lradc->irq[i],
>> +                                    mxs_lradc_ts_handle_irq,
>
> Hmm, if you have several interrupts handled by the same interrupt
> handler you'd need some locking there.

same interrupt handler can run concurrently only on multi cores, so
why do we need locking?

>
>> +                                    IRQF_SHARED, lradc->irq_name[i], ts);
>> +             if (ret)
>> +                     return ret;
>> +     }
>> +
>> +     if (!touch_ret) {
>> +             ret = mxs_lradc_ts_register(ts);
>> +             if (!ret)
>> +                     goto err_ts_register;
>> +     }
>> +
>> +     return 0;
>> +
>> +err_ts_register:
>> +     mxs_lradc_ts_hw_stop(ts);
>> +     return ret;
>
> Would prefer calling this variable "error".
>
>> +}
>> +
>> +static int mxs_lradc_ts_remove(struct platform_device *pdev)
>> +{
>> +     struct mxs_lradc_ts *ts = platform_get_drvdata(pdev);
>> +
>> +     mxs_lradc_ts_hw_stop(ts);
>
> I wonder if mxs_lradc_ts_hw_stop() can be combined with
> mxs_lradc_disable_ts() which is called automatically if input device is
> opened? This way we'd be able to get rid of mxs_lradc_ts_remove().
>
>> +
>> +     return 0;
>> +}
>> +
>> +static struct platform_driver mxs_lradc_ts_driver = {
>> +     .driver = {
>> +             .name = DRIVER_NAME_TS,
>> +     },
>> +     .probe  = mxs_lradc_ts_probe,
>> +     .remove = mxs_lradc_ts_remove,
>> +};
>> +module_platform_driver(mxs_lradc_ts_driver);
>> +
>> +MODULE_LICENSE("GPL v2");
>
> "GPL" since you are not limiting to v2 only.
>
> Thanks.
>
> --
> Dmitry

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

* Re: [PATCH 2/3] iio: adc: mxs-lradc: Add support for adc driver
@ 2016-05-28 17:49       ` Ksenija Stanojević
  0 siblings, 0 replies; 40+ messages in thread
From: Ksenija Stanojević @ 2016-05-28 17:49 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-kernel, Lee Jones, Dmitry Torokhov, linux-input,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Marek Vašut, linux-iio, Harald Geyer

On Sun, May 1, 2016 at 6:38 PM, Jonathan Cameron <jic23@kernel.org> wrote:
> On 29/04/16 12:48, Ksenija Stanojevic wrote:
>> Add mxs-lradc adc driver.
>>
>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
> Mostly looking good.  A few nitpicks and suggestions inline.
>
> Jonathan
>> ---
>>  drivers/iio/adc/Kconfig         |  37 +-
>>  drivers/iio/adc/Makefile        |   1 +
>>  drivers/iio/adc/mxs-lradc-adc.c | 832 ++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 858 insertions(+), 12 deletions(-)
>>  create mode 100644 drivers/iio/adc/mxs-lradc-adc.c
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 82c718c..50847b8 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -233,6 +233,19 @@ config IMX7D_ADC
>>         This driver can also be built as a module. If so, the module will be
>>         called imx7d_adc.
>>
>> +config MXS_LRADC_ADC
>> +     tristate "Freescale i.MX23/i.MX28 LRADC ADC"
>> +     depends on MFD_MXS_LRADC
>> +     select IIO_BUFFER
>> +     select IIO_TRIGGERED_BUFFER
>> +     help
>> +       Say yes here to build support for the ADC functions of the
>> +       i.MX23/i.MX28 LRADC. This includes general-purpose ADC readings,
>> +       battery voltage measurement, and die temperature measurement.
>> +
>> +       This driver can also be built as a module. If so, the module will be
>> +       called mxs-lradc-adc.
>> +
>>  config LP8788_ADC
>>       tristate "LP8788 ADC driver"
>>       depends on MFD_LP8788
>> @@ -306,18 +319,18 @@ config MEN_Z188_ADC
>>         called men_z188_adc.
>>
>>  config MXS_LRADC
>> -        tristate "Freescale i.MX23/i.MX28 LRADC"
>> -        depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
>> -        depends on INPUT
>> -        select STMP_DEVICE
>> -        select IIO_BUFFER
>> -        select IIO_TRIGGERED_BUFFER
>> -        help
>> -          Say yes here to build support for i.MX23/i.MX28 LRADC convertor
>> -          built into these chips.
>> -
>> -          To compile this driver as a module, choose M here: the
>> -          module will be called mxs-lradc.
>> +     tristate "Freescale i.MX23/i.MX28 LRADC"
>> +     depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
>> +     depends on INPUT
>> +     select STMP_DEVICE
>> +     select IIO_BUFFER
>> +     select IIO_TRIGGERED_BUFFER
>> +     help
>> +       Say yes here to build support for i.MX23/i.MX28 LRADC convertor
>> +       built into these chips.
>> +
>> +       To compile this driver as a module, choose M here: the
>> +       module will be called mxs-lradc.
>>
>>  config NAU7802
>>       tristate "Nuvoton NAU7802 ADC driver"
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index 0cb7921..ca7d6aa 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -31,6 +31,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
>>  obj-$(CONFIG_MCP3422) += mcp3422.o
>>  obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
>>  obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
>> +obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
>>  obj-$(CONFIG_NAU7802) += nau7802.o
>>  obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
>>  obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
>> diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c
>> new file mode 100644
>> index 0000000..793c369
>> --- /dev/null
>> +++ b/drivers/iio/adc/mxs-lradc-adc.c
>> @@ -0,0 +1,832 @@
>> +/*
>> + * Freescale MXS LRADC ADC driver
>> + *
>> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
>> + * Marek Vasut <marex@denx.de>
> You should probably add your own copyright as you are making substantial
> improvements!
>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/mfd/mxs-lradc.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/trigger.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +#include <linux/iio/sysfs.h>
>> +
>> +/*
>> + * Make this runtime configurable if necessary. Currently, if the buffered mode
>> + * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before
>> + * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000)
>> + * seconds. The result is that the samples arrive every 500mS.
>> + */
>> +#define LRADC_DELAY_TIMER_PER        200
>> +#define LRADC_DELAY_TIMER_LOOP       5
>> +
>> +#define VREF_MV_BASE 1850
>> +
>> +static const u32 mxs_lradc_adc_vref_mv[][LRADC_MAX_TOTAL_CHANS] = {
>> +     [IMX23_LRADC] = {
>> +             VREF_MV_BASE,           /* CH0 */
>> +             VREF_MV_BASE,           /* CH1 */
>> +             VREF_MV_BASE,           /* CH2 */
>> +             VREF_MV_BASE,           /* CH3 */
>> +             VREF_MV_BASE,           /* CH4 */
>> +             VREF_MV_BASE,           /* CH5 */
>> +             VREF_MV_BASE * 2,       /* CH6 VDDIO */
>> +             VREF_MV_BASE * 4,       /* CH7 VBATT */
>> +             VREF_MV_BASE,           /* CH8 Temp sense 0 */
>> +             VREF_MV_BASE,           /* CH9 Temp sense 1 */
>> +             VREF_MV_BASE,           /* CH10 */
>> +             VREF_MV_BASE,           /* CH11 */
>> +             VREF_MV_BASE,           /* CH12 USB_DP */
>> +             VREF_MV_BASE,           /* CH13 USB_DN */
>> +             VREF_MV_BASE,           /* CH14 VBG */
>> +             VREF_MV_BASE * 4,       /* CH15 VDD5V */
>> +     },
>> +     [IMX28_LRADC] = {
>> +             VREF_MV_BASE,           /* CH0 */
>> +             VREF_MV_BASE,           /* CH1 */
>> +             VREF_MV_BASE,           /* CH2 */
>> +             VREF_MV_BASE,           /* CH3 */
>> +             VREF_MV_BASE,           /* CH4 */
>> +             VREF_MV_BASE,           /* CH5 */
>> +             VREF_MV_BASE,           /* CH6 */
>> +             VREF_MV_BASE * 4,       /* CH7 VBATT */
>> +             VREF_MV_BASE,           /* CH8 Temp sense 0 */
>> +             VREF_MV_BASE,           /* CH9 Temp sense 1 */
>> +             VREF_MV_BASE * 2,       /* CH10 VDDIO */
>> +             VREF_MV_BASE,           /* CH11 VTH */
>> +             VREF_MV_BASE * 2,       /* CH12 VDDA */
>> +             VREF_MV_BASE,           /* CH13 VDDD */
>> +             VREF_MV_BASE,           /* CH14 VBG */
>> +             VREF_MV_BASE * 4,       /* CH15 VDD5V */
>> +     },
>> +};
>> +
>> +enum mxs_lradc_divbytwo {
>> +     MXS_LRADC_DIV_DISABLED = 0,
>> +     MXS_LRADC_DIV_ENABLED,
>> +};
>> +
>> +struct mxs_lradc_scale {
>> +     unsigned int            integer;
>> +     unsigned int            nano;
>> +};
>> +
>> +struct mxs_lradc_adc {
>> +     struct mxs_lradc        *lradc;
>> +     struct device           *dev;
>> +
>> +     u32                     *buffer;
>> +     struct iio_trigger      *trig;
>> +     struct mutex            lock;
>> +     struct completion       completion;
>> +
>> +     const u32               *vref_mv;
>> +     struct mxs_lradc_scale  scale_avail[LRADC_MAX_TOTAL_CHANS][2];
>> +     unsigned long           is_divided;
>> +};
>> +
>> +/*
>> + * Raw I/O operations
>> + */
>> +static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan,
>> +                                  int *val)
>> +{
>> +     struct mxs_lradc_adc *adc = iio_priv(iio_dev);
>> +     struct mxs_lradc *lradc = adc->lradc;
>> +     int ret;
>> +
>> +     /*
>> +      * See if there is no buffered operation in progress. If there is simply
>> +      * bail out. This can be improved to support both buffered and raw IO at
>> +      * the same time, yet the code becomes horribly complicated. Therefore I
>> +      * applied KISS principle here.
> This is true in lots of devices!  Hence we often have a similar check.
>> +      */
>> +     ret = mutex_trylock(&adc->lock);
>> +     if (!ret)
>> +             return -EBUSY;
> We have standard core infrastructure for this now that should do the trick.
> iio_device_claim_direct_mode() etc.
>> +
>> +     reinit_completion(&adc->completion);
>> +
>> +     /*
>> +      * No buffered operation in progress, map the channel and trigger it.
>> +      * Virtual channel 0 is always used here as the others are always not
>> +      * used if doing raw sampling.
>> +      */
>> +     if (lradc->soc == IMX28_LRADC)
>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0),
>> +                                 LRADC_CTRL1);
>> +     mxs_lradc_reg_clear(lradc, 0x1, LRADC_CTRL0);
>> +
>> +     /* Enable / disable the divider per requirement */
>> +     if (test_bit(chan, &adc->is_divided))
>> +             mxs_lradc_reg_set(lradc,
>> +                               1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
>> +                               LRADC_CTRL2);
>> +     else
>> +             mxs_lradc_reg_clear(lradc,
>> +                                 1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
>> +                                 LRADC_CTRL2);
>> +
>> +     /* Clean the slot's previous content, then set new one. */
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(0),
>> +                         LRADC_CTRL4);
>> +     mxs_lradc_reg_set(lradc, chan, LRADC_CTRL4);
>> +
>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(0));
>> +
>> +     /* Enable the IRQ and start sampling the channel. */
>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
>> +     mxs_lradc_reg_set(lradc, BIT(0), LRADC_CTRL0);
>> +
>> +     /* Wait for completion on the channel, 1 second max. */
>> +     ret = wait_for_completion_killable_timeout(&adc->completion, HZ);
>> +     if (!ret)
>> +             ret = -ETIMEDOUT;
>> +     if (ret < 0)
>> +             goto err;
>> +
>> +     /* Read the data. */
>> +     *val = readl(lradc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
>> +     ret = IIO_VAL_INT;
>> +
>> +err:
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
>> +
>> +     mutex_unlock(&adc->lock);
>> +
>> +     return ret;
>> +}
>> +
>> +static int mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val)
>> +{
>> +     int ret, min, max;
>> +
>> +     ret = mxs_lradc_adc_read_single(iio_dev, 8, &min);
>> +     if (ret != IIO_VAL_INT)
>> +             return ret;
>> +
>> +     ret = mxs_lradc_adc_read_single(iio_dev, 9, &max);
>> +     if (ret != IIO_VAL_INT)
>> +             return ret;
>> +
>> +     *val = max - min;
>> +
>> +     return IIO_VAL_INT;
>> +}
>> +
>> +static int mxs_lradc_adc_read_raw(struct iio_dev *iio_dev,
>> +                           const struct iio_chan_spec *chan,
>> +                           int *val, int *val2, long m)
>> +{
>> +     struct mxs_lradc_adc *adc = iio_priv(iio_dev);
>> +
>> +     switch (m) {
>> +     case IIO_CHAN_INFO_RAW:
>> +             if (chan->type == IIO_TEMP)
>> +                     return mxs_lradc_adc_read_temp(iio_dev, val);
>> +
>> +             return mxs_lradc_adc_read_single(iio_dev, chan->channel, val);
>> +
>> +     case IIO_CHAN_INFO_SCALE:
>> +             if (chan->type == IIO_TEMP) {
>> +                     /* From the datasheet, we have to multiply by 1.012 and
>> +                      * divide by 4
>> +                      */
>> +                     *val = 0;
>> +                     *val2 = 253000;
>> +                     return IIO_VAL_INT_PLUS_MICRO;
>> +             }
>> +
>> +             *val = adc->vref_mv[chan->channel];
>> +             *val2 = chan->scan_type.realbits -
>> +                     test_bit(chan->channel, &adc->is_divided);
> blank line here as you have done elsewhere...
>> +             return IIO_VAL_FRACTIONAL_LOG2;
>> +
>> +     case IIO_CHAN_INFO_OFFSET:
>> +             if (chan->type == IIO_TEMP) {
> standard multiline comment syntax please.  Please fix this throughout.
>
> /*
>  * The ...
>> +                     /* The calculated value from the ADC is in Kelvin, we
>> +                      * want Celsius for hwmon so the offset is -273.15
>> +                      * The offset is applied before scaling so it is
>> +                      * actually -213.15 * 4 / 1.012 = -1079.644268
>> +                      */
>> +                     *val = -1079;
>> +                     *val2 = 644268;
>> +
>> +                     return IIO_VAL_INT_PLUS_MICRO;
>> +             }
>> +
>> +             return -EINVAL;
>> +
>> +     default:
>> +             break;
>> +     }
>> +
>> +     return -EINVAL;
>> +}
>> +
>> +static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev,
>> +                                const struct iio_chan_spec *chan,
>> +                                int val, int val2, long m)
>> +{
>> +     struct mxs_lradc_adc *adc = iio_priv(iio_dev);
>> +     struct mxs_lradc_scale *scale_avail =
>> +                     adc->scale_avail[chan->channel];
>> +     int ret;
>> +
>> +     ret = mutex_trylock(&adc->lock);
>> +     if (!ret)
>> +             return -EBUSY;
>> +
>> +     switch (m) {
>> +     case IIO_CHAN_INFO_SCALE:
>> +             ret = -EINVAL;
>> +             if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
>> +                 val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) {
>> +                     /* divider by two disabled */
>> +                     clear_bit(chan->channel, &adc->is_divided);
>> +                     ret = 0;
>> +             } else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
>> +                        val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) {
>> +                     /* divider by two enabled */
>> +                     set_bit(chan->channel, &adc->is_divided);
>> +                     ret = 0;
>> +             }
>> +
>> +             break;
>> +     default:
>> +             ret = -EINVAL;
>> +             break;
>> +     }
>> +
>> +     mutex_unlock(&adc->lock);
>> +
>> +     return ret;
>> +}
>> +
>> +static int mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev,
>> +                                        const struct iio_chan_spec *chan,
>> +                                        long m)
>> +{
>> +     return IIO_VAL_INT_PLUS_NANO;
>> +}
>> +
>> +static ssize_t mxs_lradc_adc_show_scale_avail_ch(struct device *dev,
>> +                                              struct device_attribute *attr,
>> +                                              char *buf,
>> +                                              int ch)
>> +{
>> +     struct iio_dev *iio = dev_to_iio_dev(dev);
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +     int i, len = 0;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++)
>> +             len += sprintf(buf + len, "%u.%09u ",
>> +                            adc->scale_avail[ch][i].integer,
>> +                            adc->scale_avail[ch][i].nano);
>> +
>> +     len += sprintf(buf + len, "\n");
>> +
>> +     return len;
>> +}
>> +
> I'm unconvinced this wrapper adds anything...
>> +static ssize_t mxs_lradc_adc_show_scale_avail(struct device *dev,
>> +                                           struct device_attribute *attr,
>> +                                           char *buf)
>> +{
>> +     struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
>> +
>> +     return mxs_lradc_adc_show_scale_avail_ch(dev, attr, buf,
>> +                                              iio_attr->address);
>> +}
>> +
>> +#define SHOW_SCALE_AVAILABLE_ATTR(ch)                                        \
>> +static IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, S_IRUGO,    \
>> +                    mxs_lradc_adc_show_scale_avail, NULL, ch)
>> +
>> +SHOW_SCALE_AVAILABLE_ATTR(0);
>> +SHOW_SCALE_AVAILABLE_ATTR(1);
>> +SHOW_SCALE_AVAILABLE_ATTR(2);
>> +SHOW_SCALE_AVAILABLE_ATTR(3);
>> +SHOW_SCALE_AVAILABLE_ATTR(4);
>> +SHOW_SCALE_AVAILABLE_ATTR(5);
>> +SHOW_SCALE_AVAILABLE_ATTR(6);
>> +SHOW_SCALE_AVAILABLE_ATTR(7);
>> +SHOW_SCALE_AVAILABLE_ATTR(10);
>> +SHOW_SCALE_AVAILABLE_ATTR(11);
>> +SHOW_SCALE_AVAILABLE_ATTR(12);
>> +SHOW_SCALE_AVAILABLE_ATTR(13);
>> +SHOW_SCALE_AVAILABLE_ATTR(14);
>> +SHOW_SCALE_AVAILABLE_ATTR(15);
>> +
>> +static struct attribute *mxs_lradc_adc_attributes[] = {
>> +     &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage2_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage3_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage4_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage5_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage6_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage7_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage10_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage11_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage12_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage13_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage14_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage15_scale_available.dev_attr.attr,
>> +     NULL
>> +};
>> +
>> +static const struct attribute_group mxs_lradc_adc_attribute_group = {
>> +     .attrs = mxs_lradc_adc_attributes,
>> +};
>> +
>> +static const struct iio_info mxs_lradc_adc_iio_info = {
>> +     .driver_module          = THIS_MODULE,
>> +     .read_raw               = mxs_lradc_adc_read_raw,
>> +     .write_raw              = mxs_lradc_adc_write_raw,
>> +     .write_raw_get_fmt      = mxs_lradc_adc_write_raw_get_fmt,
>> +     .attrs                  = &mxs_lradc_adc_attribute_group,
>> +};
>> +
>> +/*
>> + * IRQ Handling
>> + */
> Single line comment syntax preferred.
>> +static irqreturn_t mxs_lradc_adc_handle_irq(int irq, void *data)
>> +{
>> +     struct iio_dev *iio = data;
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +     struct mxs_lradc *lradc = adc->lradc;
>> +     unsigned long reg = readl(lradc->base + LRADC_CTRL1);
>> +
>> +     if (!(reg & mxs_lradc_irq_mask(lradc)))
>> +             return IRQ_NONE;
>> +
>> +     if (iio_buffer_enabled(iio)) {
>> +             if (reg & lradc->buffer_vchans)
>> +                     iio_trigger_poll(iio->trig);
>> +     } else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) {
>> +             complete(&adc->completion);
>> +     }
>> +
>> +     mxs_lradc_reg_clear(lradc, reg & mxs_lradc_irq_mask(lradc),
>> +                         LRADC_CTRL1);
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +/*
>> + * Trigger handling
> Single line comment - Clearly it's kind of more of a heading but I'd rather
> not deal with the inevitable patch converting it to the standard single line
> format!
>> + */
>> +static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p)
>> +{
>> +     struct iio_poll_func *pf = p;
>> +     struct iio_dev *iio = pf->indio_dev;
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +     const u32 chan_value = LRADC_CH_ACCUMULATE |
>> +             ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
>> +     unsigned int i, j = 0;
>> +
>> +     for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
>> +             adc->buffer[j] = readl(adc->lradc->base + LRADC_CH(j));
>> +             mxs_lradc_reg_wrt(adc->lradc, chan_value, LRADC_CH(j));
>> +             adc->buffer[j] &= LRADC_CH_VALUE_MASK;
>> +             adc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
>> +             j++;
>> +     }
>> +
>> +     iio_push_to_buffers_with_timestamp(iio, adc->buffer, pf->timestamp);
>> +
>> +     iio_trigger_notify_done(iio->trig);
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +static int mxs_lradc_adc_configure_trigger(struct iio_trigger *trig, bool state)
>> +{
>> +     struct iio_dev *iio = iio_trigger_get_drvdata(trig);
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +     const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
>> +
>> +     mxs_lradc_reg_wrt(adc->lradc, LRADC_DELAY_KICK, LRADC_DELAY(0) + st);
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct iio_trigger_ops mxs_lradc_adc_trigger_ops = {
>> +     .owner = THIS_MODULE,
>> +     .set_trigger_state = &mxs_lradc_adc_configure_trigger,
>> +};
>> +
>> +static int mxs_lradc_adc_trigger_init(struct iio_dev *iio)
>> +{
>> +     int ret;
>> +     struct iio_trigger *trig;
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +
> Could simplify things ever so slightly by using devm_iio_trigger_alloc if you
> were to reorder the trigger init to be before the buffer setup (which I think
> should be fine).  Perhaps it is not worth the hassle...
>> +     trig = iio_trigger_alloc("%s-dev%i", iio->name, iio->id);
>> +     if (!trig)
>> +             return -ENOMEM;
>> +
>> +     trig->dev.parent = adc->dev;
>> +     iio_trigger_set_drvdata(trig, iio);
>> +     trig->ops = &mxs_lradc_adc_trigger_ops;
>> +
>> +     ret = iio_trigger_register(trig);
>> +     if (ret) {
>> +             iio_trigger_free(trig);
>> +             return ret;
>> +     }
>> +
>> +     adc->trig = trig;
>> +
>> +     return 0;
>> +}
>> +
>> +static void mxs_lradc_adc_trigger_remove(struct iio_dev *iio)
>> +{
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +
>> +     iio_trigger_unregister(adc->trig);
>> +     iio_trigger_free(adc->trig);
>> +}
>> +
>> +static int mxs_lradc_adc_buffer_preenable(struct iio_dev *iio)
>> +{
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +     struct mxs_lradc *lradc = adc->lradc;
>> +     int ret = 0, chan, ofs = 0;
>> +     unsigned long enable = 0;
>> +     u32 ctrl4_set = 0;
>> +     u32 ctrl4_clr = 0;
>> +     u32 ctrl1_irq = 0;
>> +     const u32 chan_value = LRADC_CH_ACCUMULATE |
>> +             ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
>> +     const int len = bitmap_weight(iio->active_scan_mask,
>> +                     LRADC_MAX_TOTAL_CHANS);
>> +
>> +     if (!len)
>> +             return -EINVAL;
>> +
>> +     /*
>> +      * Lock the driver so raw access can not be done during buffered
>> +      * operation. This simplifies the code a lot.
>> +      */
>> +     ret = mutex_trylock(&adc->lock);
>> +     if (!ret)
>> +             return -EBUSY;
> Hmm. could this use iio_dev->mlock?  That is what that particular lock is
> for and it has nice wrapper functions to make it clear.
>> +
>> +     adc->buffer = kmalloc_array(len, sizeof(*adc->buffer), GFP_KERNEL);
>> +     if (!adc->buffer) {
>> +             ret = -ENOMEM;
>> +             goto err_mem;
>> +     }
> I wonder if it's worth the hassel of doing this dynamically.  Maybe just have a
> buffer that is big enough to take all possibilities as part of the adc
> struct?
>> +
>> +     if (lradc->soc == IMX28_LRADC)
>> +             mxs_lradc_reg_clear(
>> +                     lradc,
>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
>> +                     LRADC_CTRL1);
>> +     mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
>> +
>> +     for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
>> +             ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
>> +             ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
>> +             ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
>> +             mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(ofs));
>> +             bitmap_set(&enable, ofs, 1);
>> +             ofs++;
>> +     }
>> +
>> +     mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
>> +                         LRADC_DELAY_KICK, LRADC_DELAY(0));
>> +     mxs_lradc_reg_clear(lradc, ctrl4_clr, LRADC_CTRL4);
>> +     mxs_lradc_reg_set(lradc, ctrl4_set, LRADC_CTRL4);
>> +     mxs_lradc_reg_set(lradc, ctrl1_irq, LRADC_CTRL1);
>> +     mxs_lradc_reg_set(lradc, enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
>> +                       LRADC_DELAY(0));
>> +
>> +     return 0;
>> +
>> +err_mem:
>> +     mutex_unlock(&adc->lock);
>> +     return ret;
>> +}
>> +
>> +static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio)
>> +{
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +     struct mxs_lradc *lradc = adc->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
>> +                         LRADC_DELAY_KICK, LRADC_DELAY(0));
>> +
>> +     mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
>> +     if (lradc->soc == IMX28_LRADC)
>> +             mxs_lradc_reg_clear(
>> +                     lradc,
>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
>> +                     LRADC_CTRL1);
>> +
>> +     kfree(adc->buffer);
>> +     mutex_unlock(&adc->lock);
>> +
>> +     return 0;
>> +}
>> +
>> +static bool mxs_lradc_adc_validate_scan_mask(struct iio_dev *iio,
>> +                                          const unsigned long *mask)
>> +{
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +     struct mxs_lradc *lradc = adc->lradc;
>> +     const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS);
>> +     int rsvd_chans = 0;
>> +     unsigned long rsvd_mask = 0;
>> +
>> +     if (lradc->use_touchbutton)
>> +             rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
>> +     if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_4WIRE)
>> +             rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
>> +     if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
>> +             rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
>> +
>> +     if (lradc->use_touchbutton)
>> +             rsvd_chans++;
>> +     if (lradc->use_touchscreen)
>> +             rsvd_chans += 2;
>> +
>> +     /* Test for attempts to map channels with special mode of operation. */
>> +     if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
>> +             return false;
>> +
>> +     /* Test for attempts to map more channels then available slots. */
>> +     if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
>> +             return false;
>> +
>> +     return true;
>> +}
>> +
>> +static const struct iio_buffer_setup_ops mxs_lradc_adc_buffer_ops = {
>> +     .preenable = &mxs_lradc_adc_buffer_preenable,
>> +     .postenable = &iio_triggered_buffer_postenable,
>> +     .predisable = &iio_triggered_buffer_predisable,
>> +     .postdisable = &mxs_lradc_adc_buffer_postdisable,
>> +     .validate_scan_mask = &mxs_lradc_adc_validate_scan_mask,
>> +};
>> +
>> +/*
>> + * Driver initialization
>> + */
>> +
>> +#define MXS_ADC_CHAN(idx, chan_type, name) {                 \
>> +     .type = (chan_type),                                    \
>> +     .indexed = 1,                                           \
>> +     .scan_index = (idx),                                    \
>> +     .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |          \
>> +                           BIT(IIO_CHAN_INFO_SCALE),         \
>> +     .channel = (idx),                                       \
>> +     .address = (idx),                                       \
>> +     .scan_type = {                                          \
>> +             .sign = 'u',                                    \
>> +             .realbits = LRADC_RESOLUTION,                   \
>> +             .storagebits = 32,                              \
>> +     },                                                      \
>> +     .datasheet_name = (name),                               \
>> +}
>> +
>> +static const struct iio_chan_spec mx23_lradc_chan_spec[] = {
>> +     MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
>> +     MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
>> +     MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
>> +     MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
>> +     MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
>> +     MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
>> +     MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"),
>> +     MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
>> +     /* Combined Temperature sensors */
>> +     {
>> +             .type = IIO_TEMP,
>> +             .indexed = 1,
>> +             .scan_index = 8,
>> +             .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +                                   BIT(IIO_CHAN_INFO_OFFSET) |
>> +                                   BIT(IIO_CHAN_INFO_SCALE),
>> +             .channel = 8,
>> +             .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
>> +             .datasheet_name = "TEMP_DIE",
>> +     },
>> +     /* Hidden channel to keep indexes */
>> +     {
>> +             .type = IIO_TEMP,
>> +             .indexed = 1,
>> +             .scan_index = -1,
>> +             .channel = 9,
>> +     },
>> +     MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL),
>> +     MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL),
>> +     MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"),
>> +     MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"),
>> +     MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
>> +     MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
>> +};
>> +
>> +static const struct iio_chan_spec mx28_lradc_chan_spec[] = {
>> +     MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
>> +     MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
>> +     MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
>> +     MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
>> +     MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
>> +     MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
>> +     MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"),
>> +     MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
>> +     /* Combined Temperature sensors */
>> +     {
>> +             .type = IIO_TEMP,
>> +             .indexed = 1,
>> +             .scan_index = 8,
>> +             .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +                                   BIT(IIO_CHAN_INFO_OFFSET) |
>> +                                   BIT(IIO_CHAN_INFO_SCALE),
>> +             .channel = 8,
>> +             .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
>> +             .datasheet_name = "TEMP_DIE",
>> +     },
>> +     /* Hidden channel to keep indexes */
>> +     {
>> +             .type = IIO_TEMP,
>> +             .indexed = 1,
>> +             .scan_index = -1,
>> +             .channel = 9,
>> +     },
>> +     MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"),
>> +     MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"),
>> +     MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"),
>> +     MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"),
>> +     MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
>> +     MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
>> +};
>> +
>> +static void mxs_lradc_adc_hw_init(struct mxs_lradc_adc *adc)
>> +{
>> +     struct mxs_lradc *lradc = adc->lradc;
>> +
>> +     /* The ADC always uses DELAY CHANNEL 0. */
>> +     const u32 adc_cfg =
>> +             (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
>> +             (LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
>> +
>> +     /* Configure DELAY CHANNEL 0 for generic ADC sampling. */
>> +     mxs_lradc_reg_wrt(lradc, adc_cfg, LRADC_DELAY(0));
>> +
>> +     /* Start internal temperature sensing. */
> We start internal temperature sensing, do we ever want to stop it?

According to manual[1] there is no need to:
To use the internal die temperature sensor,
HW_LRADC_CTRL2_TEMPSENSE_PWD should be cleared. (This bit can be left
cleared after power up. There is no need to toggle it on and off.)

>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CTRL2);
>> +}
>> +
>> +static void mxs_lradc_adc_hw_stop(struct mxs_lradc_adc *adc)
>> +{
>> +     mxs_lradc_reg_wrt(adc->lradc, 0, LRADC_DELAY(0));
>> +}
>> +
>> +static int mxs_lradc_adc_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct mxs_lradc *lradc = dev_get_platdata(dev);
>> +     struct mxs_lradc_adc *adc;
>> +     struct iio_dev *iio;
>> +     int ret, i, s;
>> +     u64 scale_uv;
>> +
>> +     /* Allocate the IIO device. */
>> +     iio = devm_iio_device_alloc(dev, sizeof(*adc));
>> +     if (!iio) {
>> +             dev_err(dev, "Failed to allocate IIO device\n");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     adc = iio_priv(iio);
>> +     adc->lradc = lradc;
>> +     adc->dev = dev;
>> +
>> +     init_completion(&adc->completion);
>> +     mutex_init(&adc->lock);
>> +
>> +     for (i = 0; i < lradc->irq_count; i++) {
>> +             ret = devm_request_irq(dev, lradc->irq[i],
>> +                                    mxs_lradc_adc_handle_irq,
>> +                                    IRQF_SHARED, lradc->irq_name[i], iio);
>> +             if (ret)
>> +                     return ret;
> This seems to be getting a whole load of irq's whether or not they are actually
> going to be used by the ADC?  Aren't some of these going to be typically used by
> the touchscreen?
>> +     }
>> +
>> +     platform_set_drvdata(pdev, iio);
>> +
>> +     iio->name = pdev->name;
>> +     iio->dev.parent = dev;
>> +     iio->dev.of_node = dev->parent->of_node;
>> +     iio->info = &mxs_lradc_adc_iio_info;
>> +     iio->modes = INDIO_DIRECT_MODE;
>> +     iio->masklength = LRADC_MAX_TOTAL_CHANS;
>> +
>> +     if (lradc->soc == IMX23_LRADC) {
>> +             iio->channels = mx23_lradc_chan_spec;
>> +             iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec);
>> +     } else {
>> +             iio->channels = mx28_lradc_chan_spec;
>> +             iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec);
>> +     }
>> +
>> +     ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
>> +                                      &mxs_lradc_adc_trigger_handler,
>> +                                      &mxs_lradc_adc_buffer_ops);
>> +     if (ret)
>> +             return ret;
>> +
>> +     ret = mxs_lradc_adc_trigger_init(iio);
>> +     if (ret)
>> +             goto err_trig;
>> +
>> +     adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc];
>> +
>> +     /* Populate available ADC input ranges */
>> +     for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) {
>> +             for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) {
>> +                     /*
>> +                      * [s=0] = optional divider by two disabled (default)
>> +                      * [s=1] = optional divider by two enabled
>> +                      *
>> +                      * The scale is calculated by doing:
>> +                      *   Vref >> (realbits - s)
>> +                      * which multiplies by two on the second component
>> +                      * of the array.
>> +                      */
>> +                     scale_uv = ((u64)adc->vref_mv[i] * 100000000) >>
>> +                                (LRADC_RESOLUTION - s);
>> +                     adc->scale_avail[i][s].nano =
>> +                                     do_div(scale_uv, 100000000) * 10;
>> +                     adc->scale_avail[i][s].integer = scale_uv;
>> +             }
>> +     }
>> +
>> +     /* Configure the hardware. */
>> +     mxs_lradc_adc_hw_init(adc);
>> +
>> +     /* Register IIO device. */
>> +     ret = iio_device_register(iio);
>> +     if (ret) {
>> +             dev_err(dev, "Failed to register IIO device\n");
>> +             goto err_dev;
>> +     }
>> +
>> +     return 0;
>> +
>> +err_dev:
>> +     mxs_lradc_adc_hw_stop(adc);
>> +     mxs_lradc_adc_trigger_remove(iio);
>> +err_trig:
>> +     iio_triggered_buffer_cleanup(iio);
>> +     return ret;
>> +}
>> +
>> +static int mxs_lradc_adc_remove(struct platform_device *pdev)
>> +{
>> +     struct iio_dev *iio = platform_get_drvdata(pdev);
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +
>> +     iio_device_unregister(iio);
>> +     mxs_lradc_adc_hw_stop(adc);
>> +     mxs_lradc_adc_trigger_remove(iio);
>> +     iio_triggered_buffer_cleanup(iio);
>> +
>> +     return 0;
>> +}
>> +
>> +static struct platform_driver mxs_lradc_adc_driver = {
>> +     .driver = {
>> +             .name   = DRIVER_NAME_ADC,
>> +     },
>> +     .probe  = mxs_lradc_adc_probe,
>> +     .remove = mxs_lradc_adc_remove,
>> +};
>> +
>> +module_platform_driver(mxs_lradc_adc_driver);
>> +
>> +MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
>> +MODULE_DESCRIPTION("Freescale MXS LRADC driver general purpose ADC driver");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:" DRIVER_NAME_ADC);
>> +
> Bonus blank line at end of file...
>> +
>>
>
[1] http://cache.freescale.com/files/dsp/doc/ref_manual/MCIMX28RM.pdf

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

* Re: [PATCH 2/3] iio: adc: mxs-lradc: Add support for adc driver
@ 2016-05-28 17:49       ` Ksenija Stanojević
  0 siblings, 0 replies; 40+ messages in thread
From: Ksenija Stanojević @ 2016-05-28 17:49 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Lee Jones, Dmitry Torokhov,
	linux-input-u79uwXL29TY76Z2rM5mHXA, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Marek Vašut,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Harald Geyer

On Sun, May 1, 2016 at 6:38 PM, Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On 29/04/16 12:48, Ksenija Stanojevic wrote:
>> Add mxs-lradc adc driver.
>>
>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Mostly looking good.  A few nitpicks and suggestions inline.
>
> Jonathan
>> ---
>>  drivers/iio/adc/Kconfig         |  37 +-
>>  drivers/iio/adc/Makefile        |   1 +
>>  drivers/iio/adc/mxs-lradc-adc.c | 832 ++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 858 insertions(+), 12 deletions(-)
>>  create mode 100644 drivers/iio/adc/mxs-lradc-adc.c
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 82c718c..50847b8 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -233,6 +233,19 @@ config IMX7D_ADC
>>         This driver can also be built as a module. If so, the module will be
>>         called imx7d_adc.
>>
>> +config MXS_LRADC_ADC
>> +     tristate "Freescale i.MX23/i.MX28 LRADC ADC"
>> +     depends on MFD_MXS_LRADC
>> +     select IIO_BUFFER
>> +     select IIO_TRIGGERED_BUFFER
>> +     help
>> +       Say yes here to build support for the ADC functions of the
>> +       i.MX23/i.MX28 LRADC. This includes general-purpose ADC readings,
>> +       battery voltage measurement, and die temperature measurement.
>> +
>> +       This driver can also be built as a module. If so, the module will be
>> +       called mxs-lradc-adc.
>> +
>>  config LP8788_ADC
>>       tristate "LP8788 ADC driver"
>>       depends on MFD_LP8788
>> @@ -306,18 +319,18 @@ config MEN_Z188_ADC
>>         called men_z188_adc.
>>
>>  config MXS_LRADC
>> -        tristate "Freescale i.MX23/i.MX28 LRADC"
>> -        depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
>> -        depends on INPUT
>> -        select STMP_DEVICE
>> -        select IIO_BUFFER
>> -        select IIO_TRIGGERED_BUFFER
>> -        help
>> -          Say yes here to build support for i.MX23/i.MX28 LRADC convertor
>> -          built into these chips.
>> -
>> -          To compile this driver as a module, choose M here: the
>> -          module will be called mxs-lradc.
>> +     tristate "Freescale i.MX23/i.MX28 LRADC"
>> +     depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
>> +     depends on INPUT
>> +     select STMP_DEVICE
>> +     select IIO_BUFFER
>> +     select IIO_TRIGGERED_BUFFER
>> +     help
>> +       Say yes here to build support for i.MX23/i.MX28 LRADC convertor
>> +       built into these chips.
>> +
>> +       To compile this driver as a module, choose M here: the
>> +       module will be called mxs-lradc.
>>
>>  config NAU7802
>>       tristate "Nuvoton NAU7802 ADC driver"
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index 0cb7921..ca7d6aa 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -31,6 +31,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
>>  obj-$(CONFIG_MCP3422) += mcp3422.o
>>  obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
>>  obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
>> +obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
>>  obj-$(CONFIG_NAU7802) += nau7802.o
>>  obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
>>  obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
>> diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c
>> new file mode 100644
>> index 0000000..793c369
>> --- /dev/null
>> +++ b/drivers/iio/adc/mxs-lradc-adc.c
>> @@ -0,0 +1,832 @@
>> +/*
>> + * Freescale MXS LRADC ADC driver
>> + *
>> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
>> + * Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>
> You should probably add your own copyright as you are making substantial
> improvements!
>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/mfd/mxs-lradc.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/trigger.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +#include <linux/iio/sysfs.h>
>> +
>> +/*
>> + * Make this runtime configurable if necessary. Currently, if the buffered mode
>> + * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before
>> + * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000)
>> + * seconds. The result is that the samples arrive every 500mS.
>> + */
>> +#define LRADC_DELAY_TIMER_PER        200
>> +#define LRADC_DELAY_TIMER_LOOP       5
>> +
>> +#define VREF_MV_BASE 1850
>> +
>> +static const u32 mxs_lradc_adc_vref_mv[][LRADC_MAX_TOTAL_CHANS] = {
>> +     [IMX23_LRADC] = {
>> +             VREF_MV_BASE,           /* CH0 */
>> +             VREF_MV_BASE,           /* CH1 */
>> +             VREF_MV_BASE,           /* CH2 */
>> +             VREF_MV_BASE,           /* CH3 */
>> +             VREF_MV_BASE,           /* CH4 */
>> +             VREF_MV_BASE,           /* CH5 */
>> +             VREF_MV_BASE * 2,       /* CH6 VDDIO */
>> +             VREF_MV_BASE * 4,       /* CH7 VBATT */
>> +             VREF_MV_BASE,           /* CH8 Temp sense 0 */
>> +             VREF_MV_BASE,           /* CH9 Temp sense 1 */
>> +             VREF_MV_BASE,           /* CH10 */
>> +             VREF_MV_BASE,           /* CH11 */
>> +             VREF_MV_BASE,           /* CH12 USB_DP */
>> +             VREF_MV_BASE,           /* CH13 USB_DN */
>> +             VREF_MV_BASE,           /* CH14 VBG */
>> +             VREF_MV_BASE * 4,       /* CH15 VDD5V */
>> +     },
>> +     [IMX28_LRADC] = {
>> +             VREF_MV_BASE,           /* CH0 */
>> +             VREF_MV_BASE,           /* CH1 */
>> +             VREF_MV_BASE,           /* CH2 */
>> +             VREF_MV_BASE,           /* CH3 */
>> +             VREF_MV_BASE,           /* CH4 */
>> +             VREF_MV_BASE,           /* CH5 */
>> +             VREF_MV_BASE,           /* CH6 */
>> +             VREF_MV_BASE * 4,       /* CH7 VBATT */
>> +             VREF_MV_BASE,           /* CH8 Temp sense 0 */
>> +             VREF_MV_BASE,           /* CH9 Temp sense 1 */
>> +             VREF_MV_BASE * 2,       /* CH10 VDDIO */
>> +             VREF_MV_BASE,           /* CH11 VTH */
>> +             VREF_MV_BASE * 2,       /* CH12 VDDA */
>> +             VREF_MV_BASE,           /* CH13 VDDD */
>> +             VREF_MV_BASE,           /* CH14 VBG */
>> +             VREF_MV_BASE * 4,       /* CH15 VDD5V */
>> +     },
>> +};
>> +
>> +enum mxs_lradc_divbytwo {
>> +     MXS_LRADC_DIV_DISABLED = 0,
>> +     MXS_LRADC_DIV_ENABLED,
>> +};
>> +
>> +struct mxs_lradc_scale {
>> +     unsigned int            integer;
>> +     unsigned int            nano;
>> +};
>> +
>> +struct mxs_lradc_adc {
>> +     struct mxs_lradc        *lradc;
>> +     struct device           *dev;
>> +
>> +     u32                     *buffer;
>> +     struct iio_trigger      *trig;
>> +     struct mutex            lock;
>> +     struct completion       completion;
>> +
>> +     const u32               *vref_mv;
>> +     struct mxs_lradc_scale  scale_avail[LRADC_MAX_TOTAL_CHANS][2];
>> +     unsigned long           is_divided;
>> +};
>> +
>> +/*
>> + * Raw I/O operations
>> + */
>> +static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan,
>> +                                  int *val)
>> +{
>> +     struct mxs_lradc_adc *adc = iio_priv(iio_dev);
>> +     struct mxs_lradc *lradc = adc->lradc;
>> +     int ret;
>> +
>> +     /*
>> +      * See if there is no buffered operation in progress. If there is simply
>> +      * bail out. This can be improved to support both buffered and raw IO at
>> +      * the same time, yet the code becomes horribly complicated. Therefore I
>> +      * applied KISS principle here.
> This is true in lots of devices!  Hence we often have a similar check.
>> +      */
>> +     ret = mutex_trylock(&adc->lock);
>> +     if (!ret)
>> +             return -EBUSY;
> We have standard core infrastructure for this now that should do the trick.
> iio_device_claim_direct_mode() etc.
>> +
>> +     reinit_completion(&adc->completion);
>> +
>> +     /*
>> +      * No buffered operation in progress, map the channel and trigger it.
>> +      * Virtual channel 0 is always used here as the others are always not
>> +      * used if doing raw sampling.
>> +      */
>> +     if (lradc->soc == IMX28_LRADC)
>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0),
>> +                                 LRADC_CTRL1);
>> +     mxs_lradc_reg_clear(lradc, 0x1, LRADC_CTRL0);
>> +
>> +     /* Enable / disable the divider per requirement */
>> +     if (test_bit(chan, &adc->is_divided))
>> +             mxs_lradc_reg_set(lradc,
>> +                               1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
>> +                               LRADC_CTRL2);
>> +     else
>> +             mxs_lradc_reg_clear(lradc,
>> +                                 1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
>> +                                 LRADC_CTRL2);
>> +
>> +     /* Clean the slot's previous content, then set new one. */
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(0),
>> +                         LRADC_CTRL4);
>> +     mxs_lradc_reg_set(lradc, chan, LRADC_CTRL4);
>> +
>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(0));
>> +
>> +     /* Enable the IRQ and start sampling the channel. */
>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
>> +     mxs_lradc_reg_set(lradc, BIT(0), LRADC_CTRL0);
>> +
>> +     /* Wait for completion on the channel, 1 second max. */
>> +     ret = wait_for_completion_killable_timeout(&adc->completion, HZ);
>> +     if (!ret)
>> +             ret = -ETIMEDOUT;
>> +     if (ret < 0)
>> +             goto err;
>> +
>> +     /* Read the data. */
>> +     *val = readl(lradc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
>> +     ret = IIO_VAL_INT;
>> +
>> +err:
>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
>> +
>> +     mutex_unlock(&adc->lock);
>> +
>> +     return ret;
>> +}
>> +
>> +static int mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val)
>> +{
>> +     int ret, min, max;
>> +
>> +     ret = mxs_lradc_adc_read_single(iio_dev, 8, &min);
>> +     if (ret != IIO_VAL_INT)
>> +             return ret;
>> +
>> +     ret = mxs_lradc_adc_read_single(iio_dev, 9, &max);
>> +     if (ret != IIO_VAL_INT)
>> +             return ret;
>> +
>> +     *val = max - min;
>> +
>> +     return IIO_VAL_INT;
>> +}
>> +
>> +static int mxs_lradc_adc_read_raw(struct iio_dev *iio_dev,
>> +                           const struct iio_chan_spec *chan,
>> +                           int *val, int *val2, long m)
>> +{
>> +     struct mxs_lradc_adc *adc = iio_priv(iio_dev);
>> +
>> +     switch (m) {
>> +     case IIO_CHAN_INFO_RAW:
>> +             if (chan->type == IIO_TEMP)
>> +                     return mxs_lradc_adc_read_temp(iio_dev, val);
>> +
>> +             return mxs_lradc_adc_read_single(iio_dev, chan->channel, val);
>> +
>> +     case IIO_CHAN_INFO_SCALE:
>> +             if (chan->type == IIO_TEMP) {
>> +                     /* From the datasheet, we have to multiply by 1.012 and
>> +                      * divide by 4
>> +                      */
>> +                     *val = 0;
>> +                     *val2 = 253000;
>> +                     return IIO_VAL_INT_PLUS_MICRO;
>> +             }
>> +
>> +             *val = adc->vref_mv[chan->channel];
>> +             *val2 = chan->scan_type.realbits -
>> +                     test_bit(chan->channel, &adc->is_divided);
> blank line here as you have done elsewhere...
>> +             return IIO_VAL_FRACTIONAL_LOG2;
>> +
>> +     case IIO_CHAN_INFO_OFFSET:
>> +             if (chan->type == IIO_TEMP) {
> standard multiline comment syntax please.  Please fix this throughout.
>
> /*
>  * The ...
>> +                     /* The calculated value from the ADC is in Kelvin, we
>> +                      * want Celsius for hwmon so the offset is -273.15
>> +                      * The offset is applied before scaling so it is
>> +                      * actually -213.15 * 4 / 1.012 = -1079.644268
>> +                      */
>> +                     *val = -1079;
>> +                     *val2 = 644268;
>> +
>> +                     return IIO_VAL_INT_PLUS_MICRO;
>> +             }
>> +
>> +             return -EINVAL;
>> +
>> +     default:
>> +             break;
>> +     }
>> +
>> +     return -EINVAL;
>> +}
>> +
>> +static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev,
>> +                                const struct iio_chan_spec *chan,
>> +                                int val, int val2, long m)
>> +{
>> +     struct mxs_lradc_adc *adc = iio_priv(iio_dev);
>> +     struct mxs_lradc_scale *scale_avail =
>> +                     adc->scale_avail[chan->channel];
>> +     int ret;
>> +
>> +     ret = mutex_trylock(&adc->lock);
>> +     if (!ret)
>> +             return -EBUSY;
>> +
>> +     switch (m) {
>> +     case IIO_CHAN_INFO_SCALE:
>> +             ret = -EINVAL;
>> +             if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
>> +                 val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) {
>> +                     /* divider by two disabled */
>> +                     clear_bit(chan->channel, &adc->is_divided);
>> +                     ret = 0;
>> +             } else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
>> +                        val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) {
>> +                     /* divider by two enabled */
>> +                     set_bit(chan->channel, &adc->is_divided);
>> +                     ret = 0;
>> +             }
>> +
>> +             break;
>> +     default:
>> +             ret = -EINVAL;
>> +             break;
>> +     }
>> +
>> +     mutex_unlock(&adc->lock);
>> +
>> +     return ret;
>> +}
>> +
>> +static int mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev,
>> +                                        const struct iio_chan_spec *chan,
>> +                                        long m)
>> +{
>> +     return IIO_VAL_INT_PLUS_NANO;
>> +}
>> +
>> +static ssize_t mxs_lradc_adc_show_scale_avail_ch(struct device *dev,
>> +                                              struct device_attribute *attr,
>> +                                              char *buf,
>> +                                              int ch)
>> +{
>> +     struct iio_dev *iio = dev_to_iio_dev(dev);
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +     int i, len = 0;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++)
>> +             len += sprintf(buf + len, "%u.%09u ",
>> +                            adc->scale_avail[ch][i].integer,
>> +                            adc->scale_avail[ch][i].nano);
>> +
>> +     len += sprintf(buf + len, "\n");
>> +
>> +     return len;
>> +}
>> +
> I'm unconvinced this wrapper adds anything...
>> +static ssize_t mxs_lradc_adc_show_scale_avail(struct device *dev,
>> +                                           struct device_attribute *attr,
>> +                                           char *buf)
>> +{
>> +     struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
>> +
>> +     return mxs_lradc_adc_show_scale_avail_ch(dev, attr, buf,
>> +                                              iio_attr->address);
>> +}
>> +
>> +#define SHOW_SCALE_AVAILABLE_ATTR(ch)                                        \
>> +static IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, S_IRUGO,    \
>> +                    mxs_lradc_adc_show_scale_avail, NULL, ch)
>> +
>> +SHOW_SCALE_AVAILABLE_ATTR(0);
>> +SHOW_SCALE_AVAILABLE_ATTR(1);
>> +SHOW_SCALE_AVAILABLE_ATTR(2);
>> +SHOW_SCALE_AVAILABLE_ATTR(3);
>> +SHOW_SCALE_AVAILABLE_ATTR(4);
>> +SHOW_SCALE_AVAILABLE_ATTR(5);
>> +SHOW_SCALE_AVAILABLE_ATTR(6);
>> +SHOW_SCALE_AVAILABLE_ATTR(7);
>> +SHOW_SCALE_AVAILABLE_ATTR(10);
>> +SHOW_SCALE_AVAILABLE_ATTR(11);
>> +SHOW_SCALE_AVAILABLE_ATTR(12);
>> +SHOW_SCALE_AVAILABLE_ATTR(13);
>> +SHOW_SCALE_AVAILABLE_ATTR(14);
>> +SHOW_SCALE_AVAILABLE_ATTR(15);
>> +
>> +static struct attribute *mxs_lradc_adc_attributes[] = {
>> +     &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage2_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage3_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage4_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage5_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage6_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage7_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage10_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage11_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage12_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage13_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage14_scale_available.dev_attr.attr,
>> +     &iio_dev_attr_in_voltage15_scale_available.dev_attr.attr,
>> +     NULL
>> +};
>> +
>> +static const struct attribute_group mxs_lradc_adc_attribute_group = {
>> +     .attrs = mxs_lradc_adc_attributes,
>> +};
>> +
>> +static const struct iio_info mxs_lradc_adc_iio_info = {
>> +     .driver_module          = THIS_MODULE,
>> +     .read_raw               = mxs_lradc_adc_read_raw,
>> +     .write_raw              = mxs_lradc_adc_write_raw,
>> +     .write_raw_get_fmt      = mxs_lradc_adc_write_raw_get_fmt,
>> +     .attrs                  = &mxs_lradc_adc_attribute_group,
>> +};
>> +
>> +/*
>> + * IRQ Handling
>> + */
> Single line comment syntax preferred.
>> +static irqreturn_t mxs_lradc_adc_handle_irq(int irq, void *data)
>> +{
>> +     struct iio_dev *iio = data;
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +     struct mxs_lradc *lradc = adc->lradc;
>> +     unsigned long reg = readl(lradc->base + LRADC_CTRL1);
>> +
>> +     if (!(reg & mxs_lradc_irq_mask(lradc)))
>> +             return IRQ_NONE;
>> +
>> +     if (iio_buffer_enabled(iio)) {
>> +             if (reg & lradc->buffer_vchans)
>> +                     iio_trigger_poll(iio->trig);
>> +     } else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) {
>> +             complete(&adc->completion);
>> +     }
>> +
>> +     mxs_lradc_reg_clear(lradc, reg & mxs_lradc_irq_mask(lradc),
>> +                         LRADC_CTRL1);
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +/*
>> + * Trigger handling
> Single line comment - Clearly it's kind of more of a heading but I'd rather
> not deal with the inevitable patch converting it to the standard single line
> format!
>> + */
>> +static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p)
>> +{
>> +     struct iio_poll_func *pf = p;
>> +     struct iio_dev *iio = pf->indio_dev;
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +     const u32 chan_value = LRADC_CH_ACCUMULATE |
>> +             ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
>> +     unsigned int i, j = 0;
>> +
>> +     for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
>> +             adc->buffer[j] = readl(adc->lradc->base + LRADC_CH(j));
>> +             mxs_lradc_reg_wrt(adc->lradc, chan_value, LRADC_CH(j));
>> +             adc->buffer[j] &= LRADC_CH_VALUE_MASK;
>> +             adc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
>> +             j++;
>> +     }
>> +
>> +     iio_push_to_buffers_with_timestamp(iio, adc->buffer, pf->timestamp);
>> +
>> +     iio_trigger_notify_done(iio->trig);
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +static int mxs_lradc_adc_configure_trigger(struct iio_trigger *trig, bool state)
>> +{
>> +     struct iio_dev *iio = iio_trigger_get_drvdata(trig);
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +     const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
>> +
>> +     mxs_lradc_reg_wrt(adc->lradc, LRADC_DELAY_KICK, LRADC_DELAY(0) + st);
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct iio_trigger_ops mxs_lradc_adc_trigger_ops = {
>> +     .owner = THIS_MODULE,
>> +     .set_trigger_state = &mxs_lradc_adc_configure_trigger,
>> +};
>> +
>> +static int mxs_lradc_adc_trigger_init(struct iio_dev *iio)
>> +{
>> +     int ret;
>> +     struct iio_trigger *trig;
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +
> Could simplify things ever so slightly by using devm_iio_trigger_alloc if you
> were to reorder the trigger init to be before the buffer setup (which I think
> should be fine).  Perhaps it is not worth the hassle...
>> +     trig = iio_trigger_alloc("%s-dev%i", iio->name, iio->id);
>> +     if (!trig)
>> +             return -ENOMEM;
>> +
>> +     trig->dev.parent = adc->dev;
>> +     iio_trigger_set_drvdata(trig, iio);
>> +     trig->ops = &mxs_lradc_adc_trigger_ops;
>> +
>> +     ret = iio_trigger_register(trig);
>> +     if (ret) {
>> +             iio_trigger_free(trig);
>> +             return ret;
>> +     }
>> +
>> +     adc->trig = trig;
>> +
>> +     return 0;
>> +}
>> +
>> +static void mxs_lradc_adc_trigger_remove(struct iio_dev *iio)
>> +{
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +
>> +     iio_trigger_unregister(adc->trig);
>> +     iio_trigger_free(adc->trig);
>> +}
>> +
>> +static int mxs_lradc_adc_buffer_preenable(struct iio_dev *iio)
>> +{
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +     struct mxs_lradc *lradc = adc->lradc;
>> +     int ret = 0, chan, ofs = 0;
>> +     unsigned long enable = 0;
>> +     u32 ctrl4_set = 0;
>> +     u32 ctrl4_clr = 0;
>> +     u32 ctrl1_irq = 0;
>> +     const u32 chan_value = LRADC_CH_ACCUMULATE |
>> +             ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
>> +     const int len = bitmap_weight(iio->active_scan_mask,
>> +                     LRADC_MAX_TOTAL_CHANS);
>> +
>> +     if (!len)
>> +             return -EINVAL;
>> +
>> +     /*
>> +      * Lock the driver so raw access can not be done during buffered
>> +      * operation. This simplifies the code a lot.
>> +      */
>> +     ret = mutex_trylock(&adc->lock);
>> +     if (!ret)
>> +             return -EBUSY;
> Hmm. could this use iio_dev->mlock?  That is what that particular lock is
> for and it has nice wrapper functions to make it clear.
>> +
>> +     adc->buffer = kmalloc_array(len, sizeof(*adc->buffer), GFP_KERNEL);
>> +     if (!adc->buffer) {
>> +             ret = -ENOMEM;
>> +             goto err_mem;
>> +     }
> I wonder if it's worth the hassel of doing this dynamically.  Maybe just have a
> buffer that is big enough to take all possibilities as part of the adc
> struct?
>> +
>> +     if (lradc->soc == IMX28_LRADC)
>> +             mxs_lradc_reg_clear(
>> +                     lradc,
>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
>> +                     LRADC_CTRL1);
>> +     mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
>> +
>> +     for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
>> +             ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
>> +             ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
>> +             ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
>> +             mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(ofs));
>> +             bitmap_set(&enable, ofs, 1);
>> +             ofs++;
>> +     }
>> +
>> +     mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
>> +                         LRADC_DELAY_KICK, LRADC_DELAY(0));
>> +     mxs_lradc_reg_clear(lradc, ctrl4_clr, LRADC_CTRL4);
>> +     mxs_lradc_reg_set(lradc, ctrl4_set, LRADC_CTRL4);
>> +     mxs_lradc_reg_set(lradc, ctrl1_irq, LRADC_CTRL1);
>> +     mxs_lradc_reg_set(lradc, enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
>> +                       LRADC_DELAY(0));
>> +
>> +     return 0;
>> +
>> +err_mem:
>> +     mutex_unlock(&adc->lock);
>> +     return ret;
>> +}
>> +
>> +static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio)
>> +{
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +     struct mxs_lradc *lradc = adc->lradc;
>> +
>> +     mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
>> +                         LRADC_DELAY_KICK, LRADC_DELAY(0));
>> +
>> +     mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
>> +     if (lradc->soc == IMX28_LRADC)
>> +             mxs_lradc_reg_clear(
>> +                     lradc,
>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
>> +                     LRADC_CTRL1);
>> +
>> +     kfree(adc->buffer);
>> +     mutex_unlock(&adc->lock);
>> +
>> +     return 0;
>> +}
>> +
>> +static bool mxs_lradc_adc_validate_scan_mask(struct iio_dev *iio,
>> +                                          const unsigned long *mask)
>> +{
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +     struct mxs_lradc *lradc = adc->lradc;
>> +     const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS);
>> +     int rsvd_chans = 0;
>> +     unsigned long rsvd_mask = 0;
>> +
>> +     if (lradc->use_touchbutton)
>> +             rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
>> +     if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_4WIRE)
>> +             rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
>> +     if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
>> +             rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
>> +
>> +     if (lradc->use_touchbutton)
>> +             rsvd_chans++;
>> +     if (lradc->use_touchscreen)
>> +             rsvd_chans += 2;
>> +
>> +     /* Test for attempts to map channels with special mode of operation. */
>> +     if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
>> +             return false;
>> +
>> +     /* Test for attempts to map more channels then available slots. */
>> +     if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
>> +             return false;
>> +
>> +     return true;
>> +}
>> +
>> +static const struct iio_buffer_setup_ops mxs_lradc_adc_buffer_ops = {
>> +     .preenable = &mxs_lradc_adc_buffer_preenable,
>> +     .postenable = &iio_triggered_buffer_postenable,
>> +     .predisable = &iio_triggered_buffer_predisable,
>> +     .postdisable = &mxs_lradc_adc_buffer_postdisable,
>> +     .validate_scan_mask = &mxs_lradc_adc_validate_scan_mask,
>> +};
>> +
>> +/*
>> + * Driver initialization
>> + */
>> +
>> +#define MXS_ADC_CHAN(idx, chan_type, name) {                 \
>> +     .type = (chan_type),                                    \
>> +     .indexed = 1,                                           \
>> +     .scan_index = (idx),                                    \
>> +     .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |          \
>> +                           BIT(IIO_CHAN_INFO_SCALE),         \
>> +     .channel = (idx),                                       \
>> +     .address = (idx),                                       \
>> +     .scan_type = {                                          \
>> +             .sign = 'u',                                    \
>> +             .realbits = LRADC_RESOLUTION,                   \
>> +             .storagebits = 32,                              \
>> +     },                                                      \
>> +     .datasheet_name = (name),                               \
>> +}
>> +
>> +static const struct iio_chan_spec mx23_lradc_chan_spec[] = {
>> +     MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
>> +     MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
>> +     MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
>> +     MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
>> +     MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
>> +     MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
>> +     MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"),
>> +     MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
>> +     /* Combined Temperature sensors */
>> +     {
>> +             .type = IIO_TEMP,
>> +             .indexed = 1,
>> +             .scan_index = 8,
>> +             .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +                                   BIT(IIO_CHAN_INFO_OFFSET) |
>> +                                   BIT(IIO_CHAN_INFO_SCALE),
>> +             .channel = 8,
>> +             .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
>> +             .datasheet_name = "TEMP_DIE",
>> +     },
>> +     /* Hidden channel to keep indexes */
>> +     {
>> +             .type = IIO_TEMP,
>> +             .indexed = 1,
>> +             .scan_index = -1,
>> +             .channel = 9,
>> +     },
>> +     MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL),
>> +     MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL),
>> +     MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"),
>> +     MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"),
>> +     MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
>> +     MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
>> +};
>> +
>> +static const struct iio_chan_spec mx28_lradc_chan_spec[] = {
>> +     MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
>> +     MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
>> +     MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
>> +     MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
>> +     MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
>> +     MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
>> +     MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"),
>> +     MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
>> +     /* Combined Temperature sensors */
>> +     {
>> +             .type = IIO_TEMP,
>> +             .indexed = 1,
>> +             .scan_index = 8,
>> +             .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +                                   BIT(IIO_CHAN_INFO_OFFSET) |
>> +                                   BIT(IIO_CHAN_INFO_SCALE),
>> +             .channel = 8,
>> +             .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
>> +             .datasheet_name = "TEMP_DIE",
>> +     },
>> +     /* Hidden channel to keep indexes */
>> +     {
>> +             .type = IIO_TEMP,
>> +             .indexed = 1,
>> +             .scan_index = -1,
>> +             .channel = 9,
>> +     },
>> +     MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"),
>> +     MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"),
>> +     MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"),
>> +     MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"),
>> +     MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
>> +     MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
>> +};
>> +
>> +static void mxs_lradc_adc_hw_init(struct mxs_lradc_adc *adc)
>> +{
>> +     struct mxs_lradc *lradc = adc->lradc;
>> +
>> +     /* The ADC always uses DELAY CHANNEL 0. */
>> +     const u32 adc_cfg =
>> +             (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
>> +             (LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
>> +
>> +     /* Configure DELAY CHANNEL 0 for generic ADC sampling. */
>> +     mxs_lradc_reg_wrt(lradc, adc_cfg, LRADC_DELAY(0));
>> +
>> +     /* Start internal temperature sensing. */
> We start internal temperature sensing, do we ever want to stop it?

According to manual[1] there is no need to:
To use the internal die temperature sensor,
HW_LRADC_CTRL2_TEMPSENSE_PWD should be cleared. (This bit can be left
cleared after power up. There is no need to toggle it on and off.)

>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CTRL2);
>> +}
>> +
>> +static void mxs_lradc_adc_hw_stop(struct mxs_lradc_adc *adc)
>> +{
>> +     mxs_lradc_reg_wrt(adc->lradc, 0, LRADC_DELAY(0));
>> +}
>> +
>> +static int mxs_lradc_adc_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct mxs_lradc *lradc = dev_get_platdata(dev);
>> +     struct mxs_lradc_adc *adc;
>> +     struct iio_dev *iio;
>> +     int ret, i, s;
>> +     u64 scale_uv;
>> +
>> +     /* Allocate the IIO device. */
>> +     iio = devm_iio_device_alloc(dev, sizeof(*adc));
>> +     if (!iio) {
>> +             dev_err(dev, "Failed to allocate IIO device\n");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     adc = iio_priv(iio);
>> +     adc->lradc = lradc;
>> +     adc->dev = dev;
>> +
>> +     init_completion(&adc->completion);
>> +     mutex_init(&adc->lock);
>> +
>> +     for (i = 0; i < lradc->irq_count; i++) {
>> +             ret = devm_request_irq(dev, lradc->irq[i],
>> +                                    mxs_lradc_adc_handle_irq,
>> +                                    IRQF_SHARED, lradc->irq_name[i], iio);
>> +             if (ret)
>> +                     return ret;
> This seems to be getting a whole load of irq's whether or not they are actually
> going to be used by the ADC?  Aren't some of these going to be typically used by
> the touchscreen?
>> +     }
>> +
>> +     platform_set_drvdata(pdev, iio);
>> +
>> +     iio->name = pdev->name;
>> +     iio->dev.parent = dev;
>> +     iio->dev.of_node = dev->parent->of_node;
>> +     iio->info = &mxs_lradc_adc_iio_info;
>> +     iio->modes = INDIO_DIRECT_MODE;
>> +     iio->masklength = LRADC_MAX_TOTAL_CHANS;
>> +
>> +     if (lradc->soc == IMX23_LRADC) {
>> +             iio->channels = mx23_lradc_chan_spec;
>> +             iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec);
>> +     } else {
>> +             iio->channels = mx28_lradc_chan_spec;
>> +             iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec);
>> +     }
>> +
>> +     ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
>> +                                      &mxs_lradc_adc_trigger_handler,
>> +                                      &mxs_lradc_adc_buffer_ops);
>> +     if (ret)
>> +             return ret;
>> +
>> +     ret = mxs_lradc_adc_trigger_init(iio);
>> +     if (ret)
>> +             goto err_trig;
>> +
>> +     adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc];
>> +
>> +     /* Populate available ADC input ranges */
>> +     for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) {
>> +             for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) {
>> +                     /*
>> +                      * [s=0] = optional divider by two disabled (default)
>> +                      * [s=1] = optional divider by two enabled
>> +                      *
>> +                      * The scale is calculated by doing:
>> +                      *   Vref >> (realbits - s)
>> +                      * which multiplies by two on the second component
>> +                      * of the array.
>> +                      */
>> +                     scale_uv = ((u64)adc->vref_mv[i] * 100000000) >>
>> +                                (LRADC_RESOLUTION - s);
>> +                     adc->scale_avail[i][s].nano =
>> +                                     do_div(scale_uv, 100000000) * 10;
>> +                     adc->scale_avail[i][s].integer = scale_uv;
>> +             }
>> +     }
>> +
>> +     /* Configure the hardware. */
>> +     mxs_lradc_adc_hw_init(adc);
>> +
>> +     /* Register IIO device. */
>> +     ret = iio_device_register(iio);
>> +     if (ret) {
>> +             dev_err(dev, "Failed to register IIO device\n");
>> +             goto err_dev;
>> +     }
>> +
>> +     return 0;
>> +
>> +err_dev:
>> +     mxs_lradc_adc_hw_stop(adc);
>> +     mxs_lradc_adc_trigger_remove(iio);
>> +err_trig:
>> +     iio_triggered_buffer_cleanup(iio);
>> +     return ret;
>> +}
>> +
>> +static int mxs_lradc_adc_remove(struct platform_device *pdev)
>> +{
>> +     struct iio_dev *iio = platform_get_drvdata(pdev);
>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>> +
>> +     iio_device_unregister(iio);
>> +     mxs_lradc_adc_hw_stop(adc);
>> +     mxs_lradc_adc_trigger_remove(iio);
>> +     iio_triggered_buffer_cleanup(iio);
>> +
>> +     return 0;
>> +}
>> +
>> +static struct platform_driver mxs_lradc_adc_driver = {
>> +     .driver = {
>> +             .name   = DRIVER_NAME_ADC,
>> +     },
>> +     .probe  = mxs_lradc_adc_probe,
>> +     .remove = mxs_lradc_adc_remove,
>> +};
>> +
>> +module_platform_driver(mxs_lradc_adc_driver);
>> +
>> +MODULE_AUTHOR("Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>");
>> +MODULE_DESCRIPTION("Freescale MXS LRADC driver general purpose ADC driver");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:" DRIVER_NAME_ADC);
>> +
> Bonus blank line at end of file...
>> +
>>
>
[1] http://cache.freescale.com/files/dsp/doc/ref_manual/MCIMX28RM.pdf

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

* Re: [PATCH 2/3] iio: adc: mxs-lradc: Add support for adc driver
  2016-05-28 17:49       ` Ksenija Stanojević
@ 2016-05-29 16:46         ` Jonathan Cameron
  -1 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2016-05-29 16:46 UTC (permalink / raw)
  To: Ksenija Stanojević
  Cc: linux-kernel, Lee Jones, Dmitry Torokhov, linux-input,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Marek Vašut, linux-iio, Harald Geyer

On 28/05/16 18:49, Ksenija Stanojević wrote:
> On Sun, May 1, 2016 at 6:38 PM, Jonathan Cameron <jic23@kernel.org> wrote:
>> On 29/04/16 12:48, Ksenija Stanojevic wrote:
>>> Add mxs-lradc adc driver.
>>>
>>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
>> Mostly looking good.  A few nitpicks and suggestions inline.
>>
>> Jonathan
>>> ---
>>>  drivers/iio/adc/Kconfig         |  37 +-
>>>  drivers/iio/adc/Makefile        |   1 +
>>>  drivers/iio/adc/mxs-lradc-adc.c | 832 ++++++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 858 insertions(+), 12 deletions(-)
>>>  create mode 100644 drivers/iio/adc/mxs-lradc-adc.c
>>>
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index 82c718c..50847b8 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -233,6 +233,19 @@ config IMX7D_ADC
>>>         This driver can also be built as a module. If so, the module will be
>>>         called imx7d_adc.
>>>
>>> +config MXS_LRADC_ADC
>>> +     tristate "Freescale i.MX23/i.MX28 LRADC ADC"
>>> +     depends on MFD_MXS_LRADC
>>> +     select IIO_BUFFER
>>> +     select IIO_TRIGGERED_BUFFER
>>> +     help
>>> +       Say yes here to build support for the ADC functions of the
>>> +       i.MX23/i.MX28 LRADC. This includes general-purpose ADC readings,
>>> +       battery voltage measurement, and die temperature measurement.
>>> +
>>> +       This driver can also be built as a module. If so, the module will be
>>> +       called mxs-lradc-adc.
>>> +
>>>  config LP8788_ADC
>>>       tristate "LP8788 ADC driver"
>>>       depends on MFD_LP8788
>>> @@ -306,18 +319,18 @@ config MEN_Z188_ADC
>>>         called men_z188_adc.
>>>
>>>  config MXS_LRADC
>>> -        tristate "Freescale i.MX23/i.MX28 LRADC"
>>> -        depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
>>> -        depends on INPUT
>>> -        select STMP_DEVICE
>>> -        select IIO_BUFFER
>>> -        select IIO_TRIGGERED_BUFFER
>>> -        help
>>> -          Say yes here to build support for i.MX23/i.MX28 LRADC convertor
>>> -          built into these chips.
>>> -
>>> -          To compile this driver as a module, choose M here: the
>>> -          module will be called mxs-lradc.
>>> +     tristate "Freescale i.MX23/i.MX28 LRADC"
>>> +     depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
>>> +     depends on INPUT
>>> +     select STMP_DEVICE
>>> +     select IIO_BUFFER
>>> +     select IIO_TRIGGERED_BUFFER
>>> +     help
>>> +       Say yes here to build support for i.MX23/i.MX28 LRADC convertor
>>> +       built into these chips.
>>> +
>>> +       To compile this driver as a module, choose M here: the
>>> +       module will be called mxs-lradc.
>>>
>>>  config NAU7802
>>>       tristate "Nuvoton NAU7802 ADC driver"
>>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>>> index 0cb7921..ca7d6aa 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -31,6 +31,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
>>>  obj-$(CONFIG_MCP3422) += mcp3422.o
>>>  obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
>>>  obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
>>> +obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
>>>  obj-$(CONFIG_NAU7802) += nau7802.o
>>>  obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
>>>  obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
>>> diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c
>>> new file mode 100644
>>> index 0000000..793c369
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/mxs-lradc-adc.c
>>> @@ -0,0 +1,832 @@
>>> +/*
>>> + * Freescale MXS LRADC ADC driver
>>> + *
>>> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
>>> + * Marek Vasut <marex@denx.de>
>> You should probably add your own copyright as you are making substantial
>> improvements!
>>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License as published by
>>> + * the Free Software Foundation; either version 2 of the License, or
>>> + * (at your option) any later version.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>> + * GNU General Public License for more details.
>>> + */
>>> +
>>> +#include <linux/mfd/mxs-lradc.h>
>>> +#include <linux/module.h>
>>> +#include <linux/platform_device.h>
>>> +
>>> +#include <linux/iio/buffer.h>
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/iio/trigger.h>
>>> +#include <linux/iio/trigger_consumer.h>
>>> +#include <linux/iio/triggered_buffer.h>
>>> +#include <linux/iio/sysfs.h>
>>> +
>>> +/*
>>> + * Make this runtime configurable if necessary. Currently, if the buffered mode
>>> + * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before
>>> + * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000)
>>> + * seconds. The result is that the samples arrive every 500mS.
>>> + */
>>> +#define LRADC_DELAY_TIMER_PER        200
>>> +#define LRADC_DELAY_TIMER_LOOP       5
>>> +
>>> +#define VREF_MV_BASE 1850
>>> +
>>> +static const u32 mxs_lradc_adc_vref_mv[][LRADC_MAX_TOTAL_CHANS] = {
>>> +     [IMX23_LRADC] = {
>>> +             VREF_MV_BASE,           /* CH0 */
>>> +             VREF_MV_BASE,           /* CH1 */
>>> +             VREF_MV_BASE,           /* CH2 */
>>> +             VREF_MV_BASE,           /* CH3 */
>>> +             VREF_MV_BASE,           /* CH4 */
>>> +             VREF_MV_BASE,           /* CH5 */
>>> +             VREF_MV_BASE * 2,       /* CH6 VDDIO */
>>> +             VREF_MV_BASE * 4,       /* CH7 VBATT */
>>> +             VREF_MV_BASE,           /* CH8 Temp sense 0 */
>>> +             VREF_MV_BASE,           /* CH9 Temp sense 1 */
>>> +             VREF_MV_BASE,           /* CH10 */
>>> +             VREF_MV_BASE,           /* CH11 */
>>> +             VREF_MV_BASE,           /* CH12 USB_DP */
>>> +             VREF_MV_BASE,           /* CH13 USB_DN */
>>> +             VREF_MV_BASE,           /* CH14 VBG */
>>> +             VREF_MV_BASE * 4,       /* CH15 VDD5V */
>>> +     },
>>> +     [IMX28_LRADC] = {
>>> +             VREF_MV_BASE,           /* CH0 */
>>> +             VREF_MV_BASE,           /* CH1 */
>>> +             VREF_MV_BASE,           /* CH2 */
>>> +             VREF_MV_BASE,           /* CH3 */
>>> +             VREF_MV_BASE,           /* CH4 */
>>> +             VREF_MV_BASE,           /* CH5 */
>>> +             VREF_MV_BASE,           /* CH6 */
>>> +             VREF_MV_BASE * 4,       /* CH7 VBATT */
>>> +             VREF_MV_BASE,           /* CH8 Temp sense 0 */
>>> +             VREF_MV_BASE,           /* CH9 Temp sense 1 */
>>> +             VREF_MV_BASE * 2,       /* CH10 VDDIO */
>>> +             VREF_MV_BASE,           /* CH11 VTH */
>>> +             VREF_MV_BASE * 2,       /* CH12 VDDA */
>>> +             VREF_MV_BASE,           /* CH13 VDDD */
>>> +             VREF_MV_BASE,           /* CH14 VBG */
>>> +             VREF_MV_BASE * 4,       /* CH15 VDD5V */
>>> +     },
>>> +};
>>> +
>>> +enum mxs_lradc_divbytwo {
>>> +     MXS_LRADC_DIV_DISABLED = 0,
>>> +     MXS_LRADC_DIV_ENABLED,
>>> +};
>>> +
>>> +struct mxs_lradc_scale {
>>> +     unsigned int            integer;
>>> +     unsigned int            nano;
>>> +};
>>> +
>>> +struct mxs_lradc_adc {
>>> +     struct mxs_lradc        *lradc;
>>> +     struct device           *dev;
>>> +
>>> +     u32                     *buffer;
>>> +     struct iio_trigger      *trig;
>>> +     struct mutex            lock;
>>> +     struct completion       completion;
>>> +
>>> +     const u32               *vref_mv;
>>> +     struct mxs_lradc_scale  scale_avail[LRADC_MAX_TOTAL_CHANS][2];
>>> +     unsigned long           is_divided;
>>> +};
>>> +
>>> +/*
>>> + * Raw I/O operations
>>> + */
>>> +static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan,
>>> +                                  int *val)
>>> +{
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio_dev);
>>> +     struct mxs_lradc *lradc = adc->lradc;
>>> +     int ret;
>>> +
>>> +     /*
>>> +      * See if there is no buffered operation in progress. If there is simply
>>> +      * bail out. This can be improved to support both buffered and raw IO at
>>> +      * the same time, yet the code becomes horribly complicated. Therefore I
>>> +      * applied KISS principle here.
>> This is true in lots of devices!  Hence we often have a similar check.
>>> +      */
>>> +     ret = mutex_trylock(&adc->lock);
>>> +     if (!ret)
>>> +             return -EBUSY;
>> We have standard core infrastructure for this now that should do the trick.
>> iio_device_claim_direct_mode() etc.
>>> +
>>> +     reinit_completion(&adc->completion);
>>> +
>>> +     /*
>>> +      * No buffered operation in progress, map the channel and trigger it.
>>> +      * Virtual channel 0 is always used here as the others are always not
>>> +      * used if doing raw sampling.
>>> +      */
>>> +     if (lradc->soc == IMX28_LRADC)
>>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0),
>>> +                                 LRADC_CTRL1);
>>> +     mxs_lradc_reg_clear(lradc, 0x1, LRADC_CTRL0);
>>> +
>>> +     /* Enable / disable the divider per requirement */
>>> +     if (test_bit(chan, &adc->is_divided))
>>> +             mxs_lradc_reg_set(lradc,
>>> +                               1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
>>> +                               LRADC_CTRL2);
>>> +     else
>>> +             mxs_lradc_reg_clear(lradc,
>>> +                                 1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
>>> +                                 LRADC_CTRL2);
>>> +
>>> +     /* Clean the slot's previous content, then set new one. */
>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(0),
>>> +                         LRADC_CTRL4);
>>> +     mxs_lradc_reg_set(lradc, chan, LRADC_CTRL4);
>>> +
>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(0));
>>> +
>>> +     /* Enable the IRQ and start sampling the channel. */
>>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
>>> +     mxs_lradc_reg_set(lradc, BIT(0), LRADC_CTRL0);
>>> +
>>> +     /* Wait for completion on the channel, 1 second max. */
>>> +     ret = wait_for_completion_killable_timeout(&adc->completion, HZ);
>>> +     if (!ret)
>>> +             ret = -ETIMEDOUT;
>>> +     if (ret < 0)
>>> +             goto err;
>>> +
>>> +     /* Read the data. */
>>> +     *val = readl(lradc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
>>> +     ret = IIO_VAL_INT;
>>> +
>>> +err:
>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
>>> +
>>> +     mutex_unlock(&adc->lock);
>>> +
>>> +     return ret;
>>> +}
>>> +
>>> +static int mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val)
>>> +{
>>> +     int ret, min, max;
>>> +
>>> +     ret = mxs_lradc_adc_read_single(iio_dev, 8, &min);
>>> +     if (ret != IIO_VAL_INT)
>>> +             return ret;
>>> +
>>> +     ret = mxs_lradc_adc_read_single(iio_dev, 9, &max);
>>> +     if (ret != IIO_VAL_INT)
>>> +             return ret;
>>> +
>>> +     *val = max - min;
>>> +
>>> +     return IIO_VAL_INT;
>>> +}
>>> +
>>> +static int mxs_lradc_adc_read_raw(struct iio_dev *iio_dev,
>>> +                           const struct iio_chan_spec *chan,
>>> +                           int *val, int *val2, long m)
>>> +{
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio_dev);
>>> +
>>> +     switch (m) {
>>> +     case IIO_CHAN_INFO_RAW:
>>> +             if (chan->type == IIO_TEMP)
>>> +                     return mxs_lradc_adc_read_temp(iio_dev, val);
>>> +
>>> +             return mxs_lradc_adc_read_single(iio_dev, chan->channel, val);
>>> +
>>> +     case IIO_CHAN_INFO_SCALE:
>>> +             if (chan->type == IIO_TEMP) {
>>> +                     /* From the datasheet, we have to multiply by 1.012 and
>>> +                      * divide by 4
>>> +                      */
>>> +                     *val = 0;
>>> +                     *val2 = 253000;
>>> +                     return IIO_VAL_INT_PLUS_MICRO;
>>> +             }
>>> +
>>> +             *val = adc->vref_mv[chan->channel];
>>> +             *val2 = chan->scan_type.realbits -
>>> +                     test_bit(chan->channel, &adc->is_divided);
>> blank line here as you have done elsewhere...
>>> +             return IIO_VAL_FRACTIONAL_LOG2;
>>> +
>>> +     case IIO_CHAN_INFO_OFFSET:
>>> +             if (chan->type == IIO_TEMP) {
>> standard multiline comment syntax please.  Please fix this throughout.
>>
>> /*
>>  * The ...
>>> +                     /* The calculated value from the ADC is in Kelvin, we
>>> +                      * want Celsius for hwmon so the offset is -273.15
>>> +                      * The offset is applied before scaling so it is
>>> +                      * actually -213.15 * 4 / 1.012 = -1079.644268
>>> +                      */
>>> +                     *val = -1079;
>>> +                     *val2 = 644268;
>>> +
>>> +                     return IIO_VAL_INT_PLUS_MICRO;
>>> +             }
>>> +
>>> +             return -EINVAL;
>>> +
>>> +     default:
>>> +             break;
>>> +     }
>>> +
>>> +     return -EINVAL;
>>> +}
>>> +
>>> +static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev,
>>> +                                const struct iio_chan_spec *chan,
>>> +                                int val, int val2, long m)
>>> +{
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio_dev);
>>> +     struct mxs_lradc_scale *scale_avail =
>>> +                     adc->scale_avail[chan->channel];
>>> +     int ret;
>>> +
>>> +     ret = mutex_trylock(&adc->lock);
>>> +     if (!ret)
>>> +             return -EBUSY;
>>> +
>>> +     switch (m) {
>>> +     case IIO_CHAN_INFO_SCALE:
>>> +             ret = -EINVAL;
>>> +             if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
>>> +                 val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) {
>>> +                     /* divider by two disabled */
>>> +                     clear_bit(chan->channel, &adc->is_divided);
>>> +                     ret = 0;
>>> +             } else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
>>> +                        val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) {
>>> +                     /* divider by two enabled */
>>> +                     set_bit(chan->channel, &adc->is_divided);
>>> +                     ret = 0;
>>> +             }
>>> +
>>> +             break;
>>> +     default:
>>> +             ret = -EINVAL;
>>> +             break;
>>> +     }
>>> +
>>> +     mutex_unlock(&adc->lock);
>>> +
>>> +     return ret;
>>> +}
>>> +
>>> +static int mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev,
>>> +                                        const struct iio_chan_spec *chan,
>>> +                                        long m)
>>> +{
>>> +     return IIO_VAL_INT_PLUS_NANO;
>>> +}
>>> +
>>> +static ssize_t mxs_lradc_adc_show_scale_avail_ch(struct device *dev,
>>> +                                              struct device_attribute *attr,
>>> +                                              char *buf,
>>> +                                              int ch)
>>> +{
>>> +     struct iio_dev *iio = dev_to_iio_dev(dev);
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +     int i, len = 0;
>>> +
>>> +     for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++)
>>> +             len += sprintf(buf + len, "%u.%09u ",
>>> +                            adc->scale_avail[ch][i].integer,
>>> +                            adc->scale_avail[ch][i].nano);
>>> +
>>> +     len += sprintf(buf + len, "\n");
>>> +
>>> +     return len;
>>> +}
>>> +
>> I'm unconvinced this wrapper adds anything...
>>> +static ssize_t mxs_lradc_adc_show_scale_avail(struct device *dev,
>>> +                                           struct device_attribute *attr,
>>> +                                           char *buf)
>>> +{
>>> +     struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
>>> +
>>> +     return mxs_lradc_adc_show_scale_avail_ch(dev, attr, buf,
>>> +                                              iio_attr->address);
>>> +}
>>> +
>>> +#define SHOW_SCALE_AVAILABLE_ATTR(ch)                                        \
>>> +static IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, S_IRUGO,    \
>>> +                    mxs_lradc_adc_show_scale_avail, NULL, ch)
>>> +
>>> +SHOW_SCALE_AVAILABLE_ATTR(0);
>>> +SHOW_SCALE_AVAILABLE_ATTR(1);
>>> +SHOW_SCALE_AVAILABLE_ATTR(2);
>>> +SHOW_SCALE_AVAILABLE_ATTR(3);
>>> +SHOW_SCALE_AVAILABLE_ATTR(4);
>>> +SHOW_SCALE_AVAILABLE_ATTR(5);
>>> +SHOW_SCALE_AVAILABLE_ATTR(6);
>>> +SHOW_SCALE_AVAILABLE_ATTR(7);
>>> +SHOW_SCALE_AVAILABLE_ATTR(10);
>>> +SHOW_SCALE_AVAILABLE_ATTR(11);
>>> +SHOW_SCALE_AVAILABLE_ATTR(12);
>>> +SHOW_SCALE_AVAILABLE_ATTR(13);
>>> +SHOW_SCALE_AVAILABLE_ATTR(14);
>>> +SHOW_SCALE_AVAILABLE_ATTR(15);
>>> +
>>> +static struct attribute *mxs_lradc_adc_attributes[] = {
>>> +     &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage2_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage3_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage4_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage5_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage6_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage7_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage10_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage11_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage12_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage13_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage14_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage15_scale_available.dev_attr.attr,
>>> +     NULL
>>> +};
>>> +
>>> +static const struct attribute_group mxs_lradc_adc_attribute_group = {
>>> +     .attrs = mxs_lradc_adc_attributes,
>>> +};
>>> +
>>> +static const struct iio_info mxs_lradc_adc_iio_info = {
>>> +     .driver_module          = THIS_MODULE,
>>> +     .read_raw               = mxs_lradc_adc_read_raw,
>>> +     .write_raw              = mxs_lradc_adc_write_raw,
>>> +     .write_raw_get_fmt      = mxs_lradc_adc_write_raw_get_fmt,
>>> +     .attrs                  = &mxs_lradc_adc_attribute_group,
>>> +};
>>> +
>>> +/*
>>> + * IRQ Handling
>>> + */
>> Single line comment syntax preferred.
>>> +static irqreturn_t mxs_lradc_adc_handle_irq(int irq, void *data)
>>> +{
>>> +     struct iio_dev *iio = data;
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +     struct mxs_lradc *lradc = adc->lradc;
>>> +     unsigned long reg = readl(lradc->base + LRADC_CTRL1);
>>> +
>>> +     if (!(reg & mxs_lradc_irq_mask(lradc)))
>>> +             return IRQ_NONE;
>>> +
>>> +     if (iio_buffer_enabled(iio)) {
>>> +             if (reg & lradc->buffer_vchans)
>>> +                     iio_trigger_poll(iio->trig);
>>> +     } else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) {
>>> +             complete(&adc->completion);
>>> +     }
>>> +
>>> +     mxs_lradc_reg_clear(lradc, reg & mxs_lradc_irq_mask(lradc),
>>> +                         LRADC_CTRL1);
>>> +
>>> +     return IRQ_HANDLED;
>>> +}
>>> +
>>> +/*
>>> + * Trigger handling
>> Single line comment - Clearly it's kind of more of a heading but I'd rather
>> not deal with the inevitable patch converting it to the standard single line
>> format!
>>> + */
>>> +static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p)
>>> +{
>>> +     struct iio_poll_func *pf = p;
>>> +     struct iio_dev *iio = pf->indio_dev;
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +     const u32 chan_value = LRADC_CH_ACCUMULATE |
>>> +             ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
>>> +     unsigned int i, j = 0;
>>> +
>>> +     for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
>>> +             adc->buffer[j] = readl(adc->lradc->base + LRADC_CH(j));
>>> +             mxs_lradc_reg_wrt(adc->lradc, chan_value, LRADC_CH(j));
>>> +             adc->buffer[j] &= LRADC_CH_VALUE_MASK;
>>> +             adc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
>>> +             j++;
>>> +     }
>>> +
>>> +     iio_push_to_buffers_with_timestamp(iio, adc->buffer, pf->timestamp);
>>> +
>>> +     iio_trigger_notify_done(iio->trig);
>>> +
>>> +     return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int mxs_lradc_adc_configure_trigger(struct iio_trigger *trig, bool state)
>>> +{
>>> +     struct iio_dev *iio = iio_trigger_get_drvdata(trig);
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +     const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
>>> +
>>> +     mxs_lradc_reg_wrt(adc->lradc, LRADC_DELAY_KICK, LRADC_DELAY(0) + st);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static const struct iio_trigger_ops mxs_lradc_adc_trigger_ops = {
>>> +     .owner = THIS_MODULE,
>>> +     .set_trigger_state = &mxs_lradc_adc_configure_trigger,
>>> +};
>>> +
>>> +static int mxs_lradc_adc_trigger_init(struct iio_dev *iio)
>>> +{
>>> +     int ret;
>>> +     struct iio_trigger *trig;
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +
>> Could simplify things ever so slightly by using devm_iio_trigger_alloc if you
>> were to reorder the trigger init to be before the buffer setup (which I think
>> should be fine).  Perhaps it is not worth the hassle...
>>> +     trig = iio_trigger_alloc("%s-dev%i", iio->name, iio->id);
>>> +     if (!trig)
>>> +             return -ENOMEM;
>>> +
>>> +     trig->dev.parent = adc->dev;
>>> +     iio_trigger_set_drvdata(trig, iio);
>>> +     trig->ops = &mxs_lradc_adc_trigger_ops;
>>> +
>>> +     ret = iio_trigger_register(trig);
>>> +     if (ret) {
>>> +             iio_trigger_free(trig);
>>> +             return ret;
>>> +     }
>>> +
>>> +     adc->trig = trig;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static void mxs_lradc_adc_trigger_remove(struct iio_dev *iio)
>>> +{
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +
>>> +     iio_trigger_unregister(adc->trig);
>>> +     iio_trigger_free(adc->trig);
>>> +}
>>> +
>>> +static int mxs_lradc_adc_buffer_preenable(struct iio_dev *iio)
>>> +{
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +     struct mxs_lradc *lradc = adc->lradc;
>>> +     int ret = 0, chan, ofs = 0;
>>> +     unsigned long enable = 0;
>>> +     u32 ctrl4_set = 0;
>>> +     u32 ctrl4_clr = 0;
>>> +     u32 ctrl1_irq = 0;
>>> +     const u32 chan_value = LRADC_CH_ACCUMULATE |
>>> +             ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
>>> +     const int len = bitmap_weight(iio->active_scan_mask,
>>> +                     LRADC_MAX_TOTAL_CHANS);
>>> +
>>> +     if (!len)
>>> +             return -EINVAL;
>>> +
>>> +     /*
>>> +      * Lock the driver so raw access can not be done during buffered
>>> +      * operation. This simplifies the code a lot.
>>> +      */
>>> +     ret = mutex_trylock(&adc->lock);
>>> +     if (!ret)
>>> +             return -EBUSY;
>> Hmm. could this use iio_dev->mlock?  That is what that particular lock is
>> for and it has nice wrapper functions to make it clear.
>>> +
>>> +     adc->buffer = kmalloc_array(len, sizeof(*adc->buffer), GFP_KERNEL);
>>> +     if (!adc->buffer) {
>>> +             ret = -ENOMEM;
>>> +             goto err_mem;
>>> +     }
>> I wonder if it's worth the hassel of doing this dynamically.  Maybe just have a
>> buffer that is big enough to take all possibilities as part of the adc
>> struct?
>>> +
>>> +     if (lradc->soc == IMX28_LRADC)
>>> +             mxs_lradc_reg_clear(
>>> +                     lradc,
>>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
>>> +                     LRADC_CTRL1);
>>> +     mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
>>> +
>>> +     for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
>>> +             ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
>>> +             ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
>>> +             ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
>>> +             mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(ofs));
>>> +             bitmap_set(&enable, ofs, 1);
>>> +             ofs++;
>>> +     }
>>> +
>>> +     mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
>>> +                         LRADC_DELAY_KICK, LRADC_DELAY(0));
>>> +     mxs_lradc_reg_clear(lradc, ctrl4_clr, LRADC_CTRL4);
>>> +     mxs_lradc_reg_set(lradc, ctrl4_set, LRADC_CTRL4);
>>> +     mxs_lradc_reg_set(lradc, ctrl1_irq, LRADC_CTRL1);
>>> +     mxs_lradc_reg_set(lradc, enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
>>> +                       LRADC_DELAY(0));
>>> +
>>> +     return 0;
>>> +
>>> +err_mem:
>>> +     mutex_unlock(&adc->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio)
>>> +{
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +     struct mxs_lradc *lradc = adc->lradc;
>>> +
>>> +     mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
>>> +                         LRADC_DELAY_KICK, LRADC_DELAY(0));
>>> +
>>> +     mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
>>> +     if (lradc->soc == IMX28_LRADC)
>>> +             mxs_lradc_reg_clear(
>>> +                     lradc,
>>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
>>> +                     LRADC_CTRL1);
>>> +
>>> +     kfree(adc->buffer);
>>> +     mutex_unlock(&adc->lock);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static bool mxs_lradc_adc_validate_scan_mask(struct iio_dev *iio,
>>> +                                          const unsigned long *mask)
>>> +{
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +     struct mxs_lradc *lradc = adc->lradc;
>>> +     const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS);
>>> +     int rsvd_chans = 0;
>>> +     unsigned long rsvd_mask = 0;
>>> +
>>> +     if (lradc->use_touchbutton)
>>> +             rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
>>> +     if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_4WIRE)
>>> +             rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
>>> +     if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
>>> +             rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
>>> +
>>> +     if (lradc->use_touchbutton)
>>> +             rsvd_chans++;
>>> +     if (lradc->use_touchscreen)
>>> +             rsvd_chans += 2;
>>> +
>>> +     /* Test for attempts to map channels with special mode of operation. */
>>> +     if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
>>> +             return false;
>>> +
>>> +     /* Test for attempts to map more channels then available slots. */
>>> +     if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
>>> +             return false;
>>> +
>>> +     return true;
>>> +}
>>> +
>>> +static const struct iio_buffer_setup_ops mxs_lradc_adc_buffer_ops = {
>>> +     .preenable = &mxs_lradc_adc_buffer_preenable,
>>> +     .postenable = &iio_triggered_buffer_postenable,
>>> +     .predisable = &iio_triggered_buffer_predisable,
>>> +     .postdisable = &mxs_lradc_adc_buffer_postdisable,
>>> +     .validate_scan_mask = &mxs_lradc_adc_validate_scan_mask,
>>> +};
>>> +
>>> +/*
>>> + * Driver initialization
>>> + */
>>> +
>>> +#define MXS_ADC_CHAN(idx, chan_type, name) {                 \
>>> +     .type = (chan_type),                                    \
>>> +     .indexed = 1,                                           \
>>> +     .scan_index = (idx),                                    \
>>> +     .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |          \
>>> +                           BIT(IIO_CHAN_INFO_SCALE),         \
>>> +     .channel = (idx),                                       \
>>> +     .address = (idx),                                       \
>>> +     .scan_type = {                                          \
>>> +             .sign = 'u',                                    \
>>> +             .realbits = LRADC_RESOLUTION,                   \
>>> +             .storagebits = 32,                              \
>>> +     },                                                      \
>>> +     .datasheet_name = (name),                               \
>>> +}
>>> +
>>> +static const struct iio_chan_spec mx23_lradc_chan_spec[] = {
>>> +     MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
>>> +     MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
>>> +     MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
>>> +     MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
>>> +     MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
>>> +     MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
>>> +     MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"),
>>> +     MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
>>> +     /* Combined Temperature sensors */
>>> +     {
>>> +             .type = IIO_TEMP,
>>> +             .indexed = 1,
>>> +             .scan_index = 8,
>>> +             .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>>> +                                   BIT(IIO_CHAN_INFO_OFFSET) |
>>> +                                   BIT(IIO_CHAN_INFO_SCALE),
>>> +             .channel = 8,
>>> +             .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
>>> +             .datasheet_name = "TEMP_DIE",
>>> +     },
>>> +     /* Hidden channel to keep indexes */
>>> +     {
>>> +             .type = IIO_TEMP,
>>> +             .indexed = 1,
>>> +             .scan_index = -1,
>>> +             .channel = 9,
>>> +     },
>>> +     MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL),
>>> +     MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL),
>>> +     MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"),
>>> +     MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"),
>>> +     MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
>>> +     MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
>>> +};
>>> +
>>> +static const struct iio_chan_spec mx28_lradc_chan_spec[] = {
>>> +     MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
>>> +     MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
>>> +     MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
>>> +     MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
>>> +     MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
>>> +     MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
>>> +     MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"),
>>> +     MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
>>> +     /* Combined Temperature sensors */
>>> +     {
>>> +             .type = IIO_TEMP,
>>> +             .indexed = 1,
>>> +             .scan_index = 8,
>>> +             .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>>> +                                   BIT(IIO_CHAN_INFO_OFFSET) |
>>> +                                   BIT(IIO_CHAN_INFO_SCALE),
>>> +             .channel = 8,
>>> +             .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
>>> +             .datasheet_name = "TEMP_DIE",
>>> +     },
>>> +     /* Hidden channel to keep indexes */
>>> +     {
>>> +             .type = IIO_TEMP,
>>> +             .indexed = 1,
>>> +             .scan_index = -1,
>>> +             .channel = 9,
>>> +     },
>>> +     MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"),
>>> +     MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"),
>>> +     MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"),
>>> +     MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"),
>>> +     MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
>>> +     MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
>>> +};
>>> +
>>> +static void mxs_lradc_adc_hw_init(struct mxs_lradc_adc *adc)
>>> +{
>>> +     struct mxs_lradc *lradc = adc->lradc;
>>> +
>>> +     /* The ADC always uses DELAY CHANNEL 0. */
>>> +     const u32 adc_cfg =
>>> +             (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
>>> +             (LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
>>> +
>>> +     /* Configure DELAY CHANNEL 0 for generic ADC sampling. */
>>> +     mxs_lradc_reg_wrt(lradc, adc_cfg, LRADC_DELAY(0));
>>> +
>>> +     /* Start internal temperature sensing. */
>> We start internal temperature sensing, do we ever want to stop it?
> 
> According to manual[1] there is no need to:
> To use the internal die temperature sensor,
> HW_LRADC_CTRL2_TEMPSENSE_PWD should be cleared. (This bit can be left
> cleared after power up. There is no need to toggle it on and off.)
Fair enough - perhaps a note here to make that clear to future readers?
> 
>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CTRL2);
>>> +}
>>> +
>>> +static void mxs_lradc_adc_hw_stop(struct mxs_lradc_adc *adc)
>>> +{
>>> +     mxs_lradc_reg_wrt(adc->lradc, 0, LRADC_DELAY(0));
>>> +}
>>> +
>>> +static int mxs_lradc_adc_probe(struct platform_device *pdev)
>>> +{
>>> +     struct device *dev = &pdev->dev;
>>> +     struct mxs_lradc *lradc = dev_get_platdata(dev);
>>> +     struct mxs_lradc_adc *adc;
>>> +     struct iio_dev *iio;
>>> +     int ret, i, s;
>>> +     u64 scale_uv;
>>> +
>>> +     /* Allocate the IIO device. */
>>> +     iio = devm_iio_device_alloc(dev, sizeof(*adc));
>>> +     if (!iio) {
>>> +             dev_err(dev, "Failed to allocate IIO device\n");
>>> +             return -ENOMEM;
>>> +     }
>>> +
>>> +     adc = iio_priv(iio);
>>> +     adc->lradc = lradc;
>>> +     adc->dev = dev;
>>> +
>>> +     init_completion(&adc->completion);
>>> +     mutex_init(&adc->lock);
>>> +
>>> +     for (i = 0; i < lradc->irq_count; i++) {
>>> +             ret = devm_request_irq(dev, lradc->irq[i],
>>> +                                    mxs_lradc_adc_handle_irq,
>>> +                                    IRQF_SHARED, lradc->irq_name[i], iio);
>>> +             if (ret)
>>> +                     return ret;
>> This seems to be getting a whole load of irq's whether or not they are actually
>> going to be used by the ADC?  Aren't some of these going to be typically used by
>> the touchscreen?
>>> +     }
>>> +
>>> +     platform_set_drvdata(pdev, iio);
>>> +
>>> +     iio->name = pdev->name;
>>> +     iio->dev.parent = dev;
>>> +     iio->dev.of_node = dev->parent->of_node;
>>> +     iio->info = &mxs_lradc_adc_iio_info;
>>> +     iio->modes = INDIO_DIRECT_MODE;
>>> +     iio->masklength = LRADC_MAX_TOTAL_CHANS;
>>> +
>>> +     if (lradc->soc == IMX23_LRADC) {
>>> +             iio->channels = mx23_lradc_chan_spec;
>>> +             iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec);
>>> +     } else {
>>> +             iio->channels = mx28_lradc_chan_spec;
>>> +             iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec);
>>> +     }
>>> +
>>> +     ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
>>> +                                      &mxs_lradc_adc_trigger_handler,
>>> +                                      &mxs_lradc_adc_buffer_ops);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     ret = mxs_lradc_adc_trigger_init(iio);
>>> +     if (ret)
>>> +             goto err_trig;
>>> +
>>> +     adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc];
>>> +
>>> +     /* Populate available ADC input ranges */
>>> +     for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) {
>>> +             for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) {
>>> +                     /*
>>> +                      * [s=0] = optional divider by two disabled (default)
>>> +                      * [s=1] = optional divider by two enabled
>>> +                      *
>>> +                      * The scale is calculated by doing:
>>> +                      *   Vref >> (realbits - s)
>>> +                      * which multiplies by two on the second component
>>> +                      * of the array.
>>> +                      */
>>> +                     scale_uv = ((u64)adc->vref_mv[i] * 100000000) >>
>>> +                                (LRADC_RESOLUTION - s);
>>> +                     adc->scale_avail[i][s].nano =
>>> +                                     do_div(scale_uv, 100000000) * 10;
>>> +                     adc->scale_avail[i][s].integer = scale_uv;
>>> +             }
>>> +     }
>>> +
>>> +     /* Configure the hardware. */
>>> +     mxs_lradc_adc_hw_init(adc);
>>> +
>>> +     /* Register IIO device. */
>>> +     ret = iio_device_register(iio);
>>> +     if (ret) {
>>> +             dev_err(dev, "Failed to register IIO device\n");
>>> +             goto err_dev;
>>> +     }
>>> +
>>> +     return 0;
>>> +
>>> +err_dev:
>>> +     mxs_lradc_adc_hw_stop(adc);
>>> +     mxs_lradc_adc_trigger_remove(iio);
>>> +err_trig:
>>> +     iio_triggered_buffer_cleanup(iio);
>>> +     return ret;
>>> +}
>>> +
>>> +static int mxs_lradc_adc_remove(struct platform_device *pdev)
>>> +{
>>> +     struct iio_dev *iio = platform_get_drvdata(pdev);
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +
>>> +     iio_device_unregister(iio);
>>> +     mxs_lradc_adc_hw_stop(adc);
>>> +     mxs_lradc_adc_trigger_remove(iio);
>>> +     iio_triggered_buffer_cleanup(iio);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static struct platform_driver mxs_lradc_adc_driver = {
>>> +     .driver = {
>>> +             .name   = DRIVER_NAME_ADC,
>>> +     },
>>> +     .probe  = mxs_lradc_adc_probe,
>>> +     .remove = mxs_lradc_adc_remove,
>>> +};
>>> +
>>> +module_platform_driver(mxs_lradc_adc_driver);
>>> +
>>> +MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
>>> +MODULE_DESCRIPTION("Freescale MXS LRADC driver general purpose ADC driver");
>>> +MODULE_LICENSE("GPL v2");
>>> +MODULE_ALIAS("platform:" DRIVER_NAME_ADC);
>>> +
>> Bonus blank line at end of file...
>>> +
>>>
>>
> [1] http://cache.freescale.com/files/dsp/doc/ref_manual/MCIMX28RM.pdf
> 

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

* Re: [PATCH 2/3] iio: adc: mxs-lradc: Add support for adc driver
@ 2016-05-29 16:46         ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2016-05-29 16:46 UTC (permalink / raw)
  To: Ksenija Stanojević
  Cc: linux-kernel, Lee Jones, Dmitry Torokhov, linux-input,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Marek Vašut, linux-iio, Harald Geyer

On 28/05/16 18:49, Ksenija Stanojević wrote:
> On Sun, May 1, 2016 at 6:38 PM, Jonathan Cameron <jic23@kernel.org> wrote:
>> On 29/04/16 12:48, Ksenija Stanojevic wrote:
>>> Add mxs-lradc adc driver.
>>>
>>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
>> Mostly looking good.  A few nitpicks and suggestions inline.
>>
>> Jonathan
>>> ---
>>>  drivers/iio/adc/Kconfig         |  37 +-
>>>  drivers/iio/adc/Makefile        |   1 +
>>>  drivers/iio/adc/mxs-lradc-adc.c | 832 ++++++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 858 insertions(+), 12 deletions(-)
>>>  create mode 100644 drivers/iio/adc/mxs-lradc-adc.c
>>>
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index 82c718c..50847b8 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -233,6 +233,19 @@ config IMX7D_ADC
>>>         This driver can also be built as a module. If so, the module will be
>>>         called imx7d_adc.
>>>
>>> +config MXS_LRADC_ADC
>>> +     tristate "Freescale i.MX23/i.MX28 LRADC ADC"
>>> +     depends on MFD_MXS_LRADC
>>> +     select IIO_BUFFER
>>> +     select IIO_TRIGGERED_BUFFER
>>> +     help
>>> +       Say yes here to build support for the ADC functions of the
>>> +       i.MX23/i.MX28 LRADC. This includes general-purpose ADC readings,
>>> +       battery voltage measurement, and die temperature measurement.
>>> +
>>> +       This driver can also be built as a module. If so, the module will be
>>> +       called mxs-lradc-adc.
>>> +
>>>  config LP8788_ADC
>>>       tristate "LP8788 ADC driver"
>>>       depends on MFD_LP8788
>>> @@ -306,18 +319,18 @@ config MEN_Z188_ADC
>>>         called men_z188_adc.
>>>
>>>  config MXS_LRADC
>>> -        tristate "Freescale i.MX23/i.MX28 LRADC"
>>> -        depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
>>> -        depends on INPUT
>>> -        select STMP_DEVICE
>>> -        select IIO_BUFFER
>>> -        select IIO_TRIGGERED_BUFFER
>>> -        help
>>> -          Say yes here to build support for i.MX23/i.MX28 LRADC convertor
>>> -          built into these chips.
>>> -
>>> -          To compile this driver as a module, choose M here: the
>>> -          module will be called mxs-lradc.
>>> +     tristate "Freescale i.MX23/i.MX28 LRADC"
>>> +     depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
>>> +     depends on INPUT
>>> +     select STMP_DEVICE
>>> +     select IIO_BUFFER
>>> +     select IIO_TRIGGERED_BUFFER
>>> +     help
>>> +       Say yes here to build support for i.MX23/i.MX28 LRADC convertor
>>> +       built into these chips.
>>> +
>>> +       To compile this driver as a module, choose M here: the
>>> +       module will be called mxs-lradc.
>>>
>>>  config NAU7802
>>>       tristate "Nuvoton NAU7802 ADC driver"
>>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>>> index 0cb7921..ca7d6aa 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -31,6 +31,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
>>>  obj-$(CONFIG_MCP3422) += mcp3422.o
>>>  obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
>>>  obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
>>> +obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
>>>  obj-$(CONFIG_NAU7802) += nau7802.o
>>>  obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
>>>  obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
>>> diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c
>>> new file mode 100644
>>> index 0000000..793c369
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/mxs-lradc-adc.c
>>> @@ -0,0 +1,832 @@
>>> +/*
>>> + * Freescale MXS LRADC ADC driver
>>> + *
>>> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
>>> + * Marek Vasut <marex@denx.de>
>> You should probably add your own copyright as you are making substantial
>> improvements!
>>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License as published by
>>> + * the Free Software Foundation; either version 2 of the License, or
>>> + * (at your option) any later version.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>> + * GNU General Public License for more details.
>>> + */
>>> +
>>> +#include <linux/mfd/mxs-lradc.h>
>>> +#include <linux/module.h>
>>> +#include <linux/platform_device.h>
>>> +
>>> +#include <linux/iio/buffer.h>
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/iio/trigger.h>
>>> +#include <linux/iio/trigger_consumer.h>
>>> +#include <linux/iio/triggered_buffer.h>
>>> +#include <linux/iio/sysfs.h>
>>> +
>>> +/*
>>> + * Make this runtime configurable if necessary. Currently, if the buffered mode
>>> + * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before
>>> + * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000)
>>> + * seconds. The result is that the samples arrive every 500mS.
>>> + */
>>> +#define LRADC_DELAY_TIMER_PER        200
>>> +#define LRADC_DELAY_TIMER_LOOP       5
>>> +
>>> +#define VREF_MV_BASE 1850
>>> +
>>> +static const u32 mxs_lradc_adc_vref_mv[][LRADC_MAX_TOTAL_CHANS] = {
>>> +     [IMX23_LRADC] = {
>>> +             VREF_MV_BASE,           /* CH0 */
>>> +             VREF_MV_BASE,           /* CH1 */
>>> +             VREF_MV_BASE,           /* CH2 */
>>> +             VREF_MV_BASE,           /* CH3 */
>>> +             VREF_MV_BASE,           /* CH4 */
>>> +             VREF_MV_BASE,           /* CH5 */
>>> +             VREF_MV_BASE * 2,       /* CH6 VDDIO */
>>> +             VREF_MV_BASE * 4,       /* CH7 VBATT */
>>> +             VREF_MV_BASE,           /* CH8 Temp sense 0 */
>>> +             VREF_MV_BASE,           /* CH9 Temp sense 1 */
>>> +             VREF_MV_BASE,           /* CH10 */
>>> +             VREF_MV_BASE,           /* CH11 */
>>> +             VREF_MV_BASE,           /* CH12 USB_DP */
>>> +             VREF_MV_BASE,           /* CH13 USB_DN */
>>> +             VREF_MV_BASE,           /* CH14 VBG */
>>> +             VREF_MV_BASE * 4,       /* CH15 VDD5V */
>>> +     },
>>> +     [IMX28_LRADC] = {
>>> +             VREF_MV_BASE,           /* CH0 */
>>> +             VREF_MV_BASE,           /* CH1 */
>>> +             VREF_MV_BASE,           /* CH2 */
>>> +             VREF_MV_BASE,           /* CH3 */
>>> +             VREF_MV_BASE,           /* CH4 */
>>> +             VREF_MV_BASE,           /* CH5 */
>>> +             VREF_MV_BASE,           /* CH6 */
>>> +             VREF_MV_BASE * 4,       /* CH7 VBATT */
>>> +             VREF_MV_BASE,           /* CH8 Temp sense 0 */
>>> +             VREF_MV_BASE,           /* CH9 Temp sense 1 */
>>> +             VREF_MV_BASE * 2,       /* CH10 VDDIO */
>>> +             VREF_MV_BASE,           /* CH11 VTH */
>>> +             VREF_MV_BASE * 2,       /* CH12 VDDA */
>>> +             VREF_MV_BASE,           /* CH13 VDDD */
>>> +             VREF_MV_BASE,           /* CH14 VBG */
>>> +             VREF_MV_BASE * 4,       /* CH15 VDD5V */
>>> +     },
>>> +};
>>> +
>>> +enum mxs_lradc_divbytwo {
>>> +     MXS_LRADC_DIV_DISABLED = 0,
>>> +     MXS_LRADC_DIV_ENABLED,
>>> +};
>>> +
>>> +struct mxs_lradc_scale {
>>> +     unsigned int            integer;
>>> +     unsigned int            nano;
>>> +};
>>> +
>>> +struct mxs_lradc_adc {
>>> +     struct mxs_lradc        *lradc;
>>> +     struct device           *dev;
>>> +
>>> +     u32                     *buffer;
>>> +     struct iio_trigger      *trig;
>>> +     struct mutex            lock;
>>> +     struct completion       completion;
>>> +
>>> +     const u32               *vref_mv;
>>> +     struct mxs_lradc_scale  scale_avail[LRADC_MAX_TOTAL_CHANS][2];
>>> +     unsigned long           is_divided;
>>> +};
>>> +
>>> +/*
>>> + * Raw I/O operations
>>> + */
>>> +static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan,
>>> +                                  int *val)
>>> +{
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio_dev);
>>> +     struct mxs_lradc *lradc = adc->lradc;
>>> +     int ret;
>>> +
>>> +     /*
>>> +      * See if there is no buffered operation in progress. If there is simply
>>> +      * bail out. This can be improved to support both buffered and raw IO at
>>> +      * the same time, yet the code becomes horribly complicated. Therefore I
>>> +      * applied KISS principle here.
>> This is true in lots of devices!  Hence we often have a similar check.
>>> +      */
>>> +     ret = mutex_trylock(&adc->lock);
>>> +     if (!ret)
>>> +             return -EBUSY;
>> We have standard core infrastructure for this now that should do the trick.
>> iio_device_claim_direct_mode() etc.
>>> +
>>> +     reinit_completion(&adc->completion);
>>> +
>>> +     /*
>>> +      * No buffered operation in progress, map the channel and trigger it.
>>> +      * Virtual channel 0 is always used here as the others are always not
>>> +      * used if doing raw sampling.
>>> +      */
>>> +     if (lradc->soc == IMX28_LRADC)
>>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0),
>>> +                                 LRADC_CTRL1);
>>> +     mxs_lradc_reg_clear(lradc, 0x1, LRADC_CTRL0);
>>> +
>>> +     /* Enable / disable the divider per requirement */
>>> +     if (test_bit(chan, &adc->is_divided))
>>> +             mxs_lradc_reg_set(lradc,
>>> +                               1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
>>> +                               LRADC_CTRL2);
>>> +     else
>>> +             mxs_lradc_reg_clear(lradc,
>>> +                                 1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
>>> +                                 LRADC_CTRL2);
>>> +
>>> +     /* Clean the slot's previous content, then set new one. */
>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(0),
>>> +                         LRADC_CTRL4);
>>> +     mxs_lradc_reg_set(lradc, chan, LRADC_CTRL4);
>>> +
>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(0));
>>> +
>>> +     /* Enable the IRQ and start sampling the channel. */
>>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
>>> +     mxs_lradc_reg_set(lradc, BIT(0), LRADC_CTRL0);
>>> +
>>> +     /* Wait for completion on the channel, 1 second max. */
>>> +     ret = wait_for_completion_killable_timeout(&adc->completion, HZ);
>>> +     if (!ret)
>>> +             ret = -ETIMEDOUT;
>>> +     if (ret < 0)
>>> +             goto err;
>>> +
>>> +     /* Read the data. */
>>> +     *val = readl(lradc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
>>> +     ret = IIO_VAL_INT;
>>> +
>>> +err:
>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
>>> +
>>> +     mutex_unlock(&adc->lock);
>>> +
>>> +     return ret;
>>> +}
>>> +
>>> +static int mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val)
>>> +{
>>> +     int ret, min, max;
>>> +
>>> +     ret = mxs_lradc_adc_read_single(iio_dev, 8, &min);
>>> +     if (ret != IIO_VAL_INT)
>>> +             return ret;
>>> +
>>> +     ret = mxs_lradc_adc_read_single(iio_dev, 9, &max);
>>> +     if (ret != IIO_VAL_INT)
>>> +             return ret;
>>> +
>>> +     *val = max - min;
>>> +
>>> +     return IIO_VAL_INT;
>>> +}
>>> +
>>> +static int mxs_lradc_adc_read_raw(struct iio_dev *iio_dev,
>>> +                           const struct iio_chan_spec *chan,
>>> +                           int *val, int *val2, long m)
>>> +{
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio_dev);
>>> +
>>> +     switch (m) {
>>> +     case IIO_CHAN_INFO_RAW:
>>> +             if (chan->type == IIO_TEMP)
>>> +                     return mxs_lradc_adc_read_temp(iio_dev, val);
>>> +
>>> +             return mxs_lradc_adc_read_single(iio_dev, chan->channel, val);
>>> +
>>> +     case IIO_CHAN_INFO_SCALE:
>>> +             if (chan->type == IIO_TEMP) {
>>> +                     /* From the datasheet, we have to multiply by 1.012 and
>>> +                      * divide by 4
>>> +                      */
>>> +                     *val = 0;
>>> +                     *val2 = 253000;
>>> +                     return IIO_VAL_INT_PLUS_MICRO;
>>> +             }
>>> +
>>> +             *val = adc->vref_mv[chan->channel];
>>> +             *val2 = chan->scan_type.realbits -
>>> +                     test_bit(chan->channel, &adc->is_divided);
>> blank line here as you have done elsewhere...
>>> +             return IIO_VAL_FRACTIONAL_LOG2;
>>> +
>>> +     case IIO_CHAN_INFO_OFFSET:
>>> +             if (chan->type == IIO_TEMP) {
>> standard multiline comment syntax please.  Please fix this throughout.
>>
>> /*
>>  * The ...
>>> +                     /* The calculated value from the ADC is in Kelvin, we
>>> +                      * want Celsius for hwmon so the offset is -273.15
>>> +                      * The offset is applied before scaling so it is
>>> +                      * actually -213.15 * 4 / 1.012 = -1079.644268
>>> +                      */
>>> +                     *val = -1079;
>>> +                     *val2 = 644268;
>>> +
>>> +                     return IIO_VAL_INT_PLUS_MICRO;
>>> +             }
>>> +
>>> +             return -EINVAL;
>>> +
>>> +     default:
>>> +             break;
>>> +     }
>>> +
>>> +     return -EINVAL;
>>> +}
>>> +
>>> +static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev,
>>> +                                const struct iio_chan_spec *chan,
>>> +                                int val, int val2, long m)
>>> +{
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio_dev);
>>> +     struct mxs_lradc_scale *scale_avail =
>>> +                     adc->scale_avail[chan->channel];
>>> +     int ret;
>>> +
>>> +     ret = mutex_trylock(&adc->lock);
>>> +     if (!ret)
>>> +             return -EBUSY;
>>> +
>>> +     switch (m) {
>>> +     case IIO_CHAN_INFO_SCALE:
>>> +             ret = -EINVAL;
>>> +             if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
>>> +                 val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) {
>>> +                     /* divider by two disabled */
>>> +                     clear_bit(chan->channel, &adc->is_divided);
>>> +                     ret = 0;
>>> +             } else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
>>> +                        val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) {
>>> +                     /* divider by two enabled */
>>> +                     set_bit(chan->channel, &adc->is_divided);
>>> +                     ret = 0;
>>> +             }
>>> +
>>> +             break;
>>> +     default:
>>> +             ret = -EINVAL;
>>> +             break;
>>> +     }
>>> +
>>> +     mutex_unlock(&adc->lock);
>>> +
>>> +     return ret;
>>> +}
>>> +
>>> +static int mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev,
>>> +                                        const struct iio_chan_spec *chan,
>>> +                                        long m)
>>> +{
>>> +     return IIO_VAL_INT_PLUS_NANO;
>>> +}
>>> +
>>> +static ssize_t mxs_lradc_adc_show_scale_avail_ch(struct device *dev,
>>> +                                              struct device_attribute *attr,
>>> +                                              char *buf,
>>> +                                              int ch)
>>> +{
>>> +     struct iio_dev *iio = dev_to_iio_dev(dev);
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +     int i, len = 0;
>>> +
>>> +     for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++)
>>> +             len += sprintf(buf + len, "%u.%09u ",
>>> +                            adc->scale_avail[ch][i].integer,
>>> +                            adc->scale_avail[ch][i].nano);
>>> +
>>> +     len += sprintf(buf + len, "\n");
>>> +
>>> +     return len;
>>> +}
>>> +
>> I'm unconvinced this wrapper adds anything...
>>> +static ssize_t mxs_lradc_adc_show_scale_avail(struct device *dev,
>>> +                                           struct device_attribute *attr,
>>> +                                           char *buf)
>>> +{
>>> +     struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
>>> +
>>> +     return mxs_lradc_adc_show_scale_avail_ch(dev, attr, buf,
>>> +                                              iio_attr->address);
>>> +}
>>> +
>>> +#define SHOW_SCALE_AVAILABLE_ATTR(ch)                                        \
>>> +static IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, S_IRUGO,    \
>>> +                    mxs_lradc_adc_show_scale_avail, NULL, ch)
>>> +
>>> +SHOW_SCALE_AVAILABLE_ATTR(0);
>>> +SHOW_SCALE_AVAILABLE_ATTR(1);
>>> +SHOW_SCALE_AVAILABLE_ATTR(2);
>>> +SHOW_SCALE_AVAILABLE_ATTR(3);
>>> +SHOW_SCALE_AVAILABLE_ATTR(4);
>>> +SHOW_SCALE_AVAILABLE_ATTR(5);
>>> +SHOW_SCALE_AVAILABLE_ATTR(6);
>>> +SHOW_SCALE_AVAILABLE_ATTR(7);
>>> +SHOW_SCALE_AVAILABLE_ATTR(10);
>>> +SHOW_SCALE_AVAILABLE_ATTR(11);
>>> +SHOW_SCALE_AVAILABLE_ATTR(12);
>>> +SHOW_SCALE_AVAILABLE_ATTR(13);
>>> +SHOW_SCALE_AVAILABLE_ATTR(14);
>>> +SHOW_SCALE_AVAILABLE_ATTR(15);
>>> +
>>> +static struct attribute *mxs_lradc_adc_attributes[] = {
>>> +     &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage2_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage3_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage4_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage5_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage6_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage7_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage10_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage11_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage12_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage13_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage14_scale_available.dev_attr.attr,
>>> +     &iio_dev_attr_in_voltage15_scale_available.dev_attr.attr,
>>> +     NULL
>>> +};
>>> +
>>> +static const struct attribute_group mxs_lradc_adc_attribute_group = {
>>> +     .attrs = mxs_lradc_adc_attributes,
>>> +};
>>> +
>>> +static const struct iio_info mxs_lradc_adc_iio_info = {
>>> +     .driver_module          = THIS_MODULE,
>>> +     .read_raw               = mxs_lradc_adc_read_raw,
>>> +     .write_raw              = mxs_lradc_adc_write_raw,
>>> +     .write_raw_get_fmt      = mxs_lradc_adc_write_raw_get_fmt,
>>> +     .attrs                  = &mxs_lradc_adc_attribute_group,
>>> +};
>>> +
>>> +/*
>>> + * IRQ Handling
>>> + */
>> Single line comment syntax preferred.
>>> +static irqreturn_t mxs_lradc_adc_handle_irq(int irq, void *data)
>>> +{
>>> +     struct iio_dev *iio = data;
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +     struct mxs_lradc *lradc = adc->lradc;
>>> +     unsigned long reg = readl(lradc->base + LRADC_CTRL1);
>>> +
>>> +     if (!(reg & mxs_lradc_irq_mask(lradc)))
>>> +             return IRQ_NONE;
>>> +
>>> +     if (iio_buffer_enabled(iio)) {
>>> +             if (reg & lradc->buffer_vchans)
>>> +                     iio_trigger_poll(iio->trig);
>>> +     } else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) {
>>> +             complete(&adc->completion);
>>> +     }
>>> +
>>> +     mxs_lradc_reg_clear(lradc, reg & mxs_lradc_irq_mask(lradc),
>>> +                         LRADC_CTRL1);
>>> +
>>> +     return IRQ_HANDLED;
>>> +}
>>> +
>>> +/*
>>> + * Trigger handling
>> Single line comment - Clearly it's kind of more of a heading but I'd rather
>> not deal with the inevitable patch converting it to the standard single line
>> format!
>>> + */
>>> +static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p)
>>> +{
>>> +     struct iio_poll_func *pf = p;
>>> +     struct iio_dev *iio = pf->indio_dev;
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +     const u32 chan_value = LRADC_CH_ACCUMULATE |
>>> +             ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
>>> +     unsigned int i, j = 0;
>>> +
>>> +     for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
>>> +             adc->buffer[j] = readl(adc->lradc->base + LRADC_CH(j));
>>> +             mxs_lradc_reg_wrt(adc->lradc, chan_value, LRADC_CH(j));
>>> +             adc->buffer[j] &= LRADC_CH_VALUE_MASK;
>>> +             adc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
>>> +             j++;
>>> +     }
>>> +
>>> +     iio_push_to_buffers_with_timestamp(iio, adc->buffer, pf->timestamp);
>>> +
>>> +     iio_trigger_notify_done(iio->trig);
>>> +
>>> +     return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int mxs_lradc_adc_configure_trigger(struct iio_trigger *trig, bool state)
>>> +{
>>> +     struct iio_dev *iio = iio_trigger_get_drvdata(trig);
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +     const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
>>> +
>>> +     mxs_lradc_reg_wrt(adc->lradc, LRADC_DELAY_KICK, LRADC_DELAY(0) + st);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static const struct iio_trigger_ops mxs_lradc_adc_trigger_ops = {
>>> +     .owner = THIS_MODULE,
>>> +     .set_trigger_state = &mxs_lradc_adc_configure_trigger,
>>> +};
>>> +
>>> +static int mxs_lradc_adc_trigger_init(struct iio_dev *iio)
>>> +{
>>> +     int ret;
>>> +     struct iio_trigger *trig;
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +
>> Could simplify things ever so slightly by using devm_iio_trigger_alloc if you
>> were to reorder the trigger init to be before the buffer setup (which I think
>> should be fine).  Perhaps it is not worth the hassle...
>>> +     trig = iio_trigger_alloc("%s-dev%i", iio->name, iio->id);
>>> +     if (!trig)
>>> +             return -ENOMEM;
>>> +
>>> +     trig->dev.parent = adc->dev;
>>> +     iio_trigger_set_drvdata(trig, iio);
>>> +     trig->ops = &mxs_lradc_adc_trigger_ops;
>>> +
>>> +     ret = iio_trigger_register(trig);
>>> +     if (ret) {
>>> +             iio_trigger_free(trig);
>>> +             return ret;
>>> +     }
>>> +
>>> +     adc->trig = trig;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static void mxs_lradc_adc_trigger_remove(struct iio_dev *iio)
>>> +{
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +
>>> +     iio_trigger_unregister(adc->trig);
>>> +     iio_trigger_free(adc->trig);
>>> +}
>>> +
>>> +static int mxs_lradc_adc_buffer_preenable(struct iio_dev *iio)
>>> +{
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +     struct mxs_lradc *lradc = adc->lradc;
>>> +     int ret = 0, chan, ofs = 0;
>>> +     unsigned long enable = 0;
>>> +     u32 ctrl4_set = 0;
>>> +     u32 ctrl4_clr = 0;
>>> +     u32 ctrl1_irq = 0;
>>> +     const u32 chan_value = LRADC_CH_ACCUMULATE |
>>> +             ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
>>> +     const int len = bitmap_weight(iio->active_scan_mask,
>>> +                     LRADC_MAX_TOTAL_CHANS);
>>> +
>>> +     if (!len)
>>> +             return -EINVAL;
>>> +
>>> +     /*
>>> +      * Lock the driver so raw access can not be done during buffered
>>> +      * operation. This simplifies the code a lot.
>>> +      */
>>> +     ret = mutex_trylock(&adc->lock);
>>> +     if (!ret)
>>> +             return -EBUSY;
>> Hmm. could this use iio_dev->mlock?  That is what that particular lock is
>> for and it has nice wrapper functions to make it clear.
>>> +
>>> +     adc->buffer = kmalloc_array(len, sizeof(*adc->buffer), GFP_KERNEL);
>>> +     if (!adc->buffer) {
>>> +             ret = -ENOMEM;
>>> +             goto err_mem;
>>> +     }
>> I wonder if it's worth the hassel of doing this dynamically.  Maybe just have a
>> buffer that is big enough to take all possibilities as part of the adc
>> struct?
>>> +
>>> +     if (lradc->soc == IMX28_LRADC)
>>> +             mxs_lradc_reg_clear(
>>> +                     lradc,
>>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
>>> +                     LRADC_CTRL1);
>>> +     mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
>>> +
>>> +     for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
>>> +             ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
>>> +             ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
>>> +             ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
>>> +             mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(ofs));
>>> +             bitmap_set(&enable, ofs, 1);
>>> +             ofs++;
>>> +     }
>>> +
>>> +     mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
>>> +                         LRADC_DELAY_KICK, LRADC_DELAY(0));
>>> +     mxs_lradc_reg_clear(lradc, ctrl4_clr, LRADC_CTRL4);
>>> +     mxs_lradc_reg_set(lradc, ctrl4_set, LRADC_CTRL4);
>>> +     mxs_lradc_reg_set(lradc, ctrl1_irq, LRADC_CTRL1);
>>> +     mxs_lradc_reg_set(lradc, enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
>>> +                       LRADC_DELAY(0));
>>> +
>>> +     return 0;
>>> +
>>> +err_mem:
>>> +     mutex_unlock(&adc->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio)
>>> +{
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +     struct mxs_lradc *lradc = adc->lradc;
>>> +
>>> +     mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
>>> +                         LRADC_DELAY_KICK, LRADC_DELAY(0));
>>> +
>>> +     mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
>>> +     if (lradc->soc == IMX28_LRADC)
>>> +             mxs_lradc_reg_clear(
>>> +                     lradc,
>>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
>>> +                     LRADC_CTRL1);
>>> +
>>> +     kfree(adc->buffer);
>>> +     mutex_unlock(&adc->lock);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static bool mxs_lradc_adc_validate_scan_mask(struct iio_dev *iio,
>>> +                                          const unsigned long *mask)
>>> +{
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +     struct mxs_lradc *lradc = adc->lradc;
>>> +     const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS);
>>> +     int rsvd_chans = 0;
>>> +     unsigned long rsvd_mask = 0;
>>> +
>>> +     if (lradc->use_touchbutton)
>>> +             rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
>>> +     if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_4WIRE)
>>> +             rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
>>> +     if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
>>> +             rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
>>> +
>>> +     if (lradc->use_touchbutton)
>>> +             rsvd_chans++;
>>> +     if (lradc->use_touchscreen)
>>> +             rsvd_chans += 2;
>>> +
>>> +     /* Test for attempts to map channels with special mode of operation. */
>>> +     if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
>>> +             return false;
>>> +
>>> +     /* Test for attempts to map more channels then available slots. */
>>> +     if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
>>> +             return false;
>>> +
>>> +     return true;
>>> +}
>>> +
>>> +static const struct iio_buffer_setup_ops mxs_lradc_adc_buffer_ops = {
>>> +     .preenable = &mxs_lradc_adc_buffer_preenable,
>>> +     .postenable = &iio_triggered_buffer_postenable,
>>> +     .predisable = &iio_triggered_buffer_predisable,
>>> +     .postdisable = &mxs_lradc_adc_buffer_postdisable,
>>> +     .validate_scan_mask = &mxs_lradc_adc_validate_scan_mask,
>>> +};
>>> +
>>> +/*
>>> + * Driver initialization
>>> + */
>>> +
>>> +#define MXS_ADC_CHAN(idx, chan_type, name) {                 \
>>> +     .type = (chan_type),                                    \
>>> +     .indexed = 1,                                           \
>>> +     .scan_index = (idx),                                    \
>>> +     .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |          \
>>> +                           BIT(IIO_CHAN_INFO_SCALE),         \
>>> +     .channel = (idx),                                       \
>>> +     .address = (idx),                                       \
>>> +     .scan_type = {                                          \
>>> +             .sign = 'u',                                    \
>>> +             .realbits = LRADC_RESOLUTION,                   \
>>> +             .storagebits = 32,                              \
>>> +     },                                                      \
>>> +     .datasheet_name = (name),                               \
>>> +}
>>> +
>>> +static const struct iio_chan_spec mx23_lradc_chan_spec[] = {
>>> +     MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
>>> +     MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
>>> +     MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
>>> +     MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
>>> +     MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
>>> +     MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
>>> +     MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"),
>>> +     MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
>>> +     /* Combined Temperature sensors */
>>> +     {
>>> +             .type = IIO_TEMP,
>>> +             .indexed = 1,
>>> +             .scan_index = 8,
>>> +             .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>>> +                                   BIT(IIO_CHAN_INFO_OFFSET) |
>>> +                                   BIT(IIO_CHAN_INFO_SCALE),
>>> +             .channel = 8,
>>> +             .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
>>> +             .datasheet_name = "TEMP_DIE",
>>> +     },
>>> +     /* Hidden channel to keep indexes */
>>> +     {
>>> +             .type = IIO_TEMP,
>>> +             .indexed = 1,
>>> +             .scan_index = -1,
>>> +             .channel = 9,
>>> +     },
>>> +     MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL),
>>> +     MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL),
>>> +     MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"),
>>> +     MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"),
>>> +     MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
>>> +     MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
>>> +};
>>> +
>>> +static const struct iio_chan_spec mx28_lradc_chan_spec[] = {
>>> +     MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
>>> +     MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
>>> +     MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
>>> +     MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
>>> +     MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
>>> +     MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
>>> +     MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"),
>>> +     MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
>>> +     /* Combined Temperature sensors */
>>> +     {
>>> +             .type = IIO_TEMP,
>>> +             .indexed = 1,
>>> +             .scan_index = 8,
>>> +             .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>>> +                                   BIT(IIO_CHAN_INFO_OFFSET) |
>>> +                                   BIT(IIO_CHAN_INFO_SCALE),
>>> +             .channel = 8,
>>> +             .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
>>> +             .datasheet_name = "TEMP_DIE",
>>> +     },
>>> +     /* Hidden channel to keep indexes */
>>> +     {
>>> +             .type = IIO_TEMP,
>>> +             .indexed = 1,
>>> +             .scan_index = -1,
>>> +             .channel = 9,
>>> +     },
>>> +     MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"),
>>> +     MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"),
>>> +     MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"),
>>> +     MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"),
>>> +     MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
>>> +     MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
>>> +};
>>> +
>>> +static void mxs_lradc_adc_hw_init(struct mxs_lradc_adc *adc)
>>> +{
>>> +     struct mxs_lradc *lradc = adc->lradc;
>>> +
>>> +     /* The ADC always uses DELAY CHANNEL 0. */
>>> +     const u32 adc_cfg =
>>> +             (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
>>> +             (LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
>>> +
>>> +     /* Configure DELAY CHANNEL 0 for generic ADC sampling. */
>>> +     mxs_lradc_reg_wrt(lradc, adc_cfg, LRADC_DELAY(0));
>>> +
>>> +     /* Start internal temperature sensing. */
>> We start internal temperature sensing, do we ever want to stop it?
> 
> According to manual[1] there is no need to:
> To use the internal die temperature sensor,
> HW_LRADC_CTRL2_TEMPSENSE_PWD should be cleared. (This bit can be left
> cleared after power up. There is no need to toggle it on and off.)
Fair enough - perhaps a note here to make that clear to future readers?
> 
>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CTRL2);
>>> +}
>>> +
>>> +static void mxs_lradc_adc_hw_stop(struct mxs_lradc_adc *adc)
>>> +{
>>> +     mxs_lradc_reg_wrt(adc->lradc, 0, LRADC_DELAY(0));
>>> +}
>>> +
>>> +static int mxs_lradc_adc_probe(struct platform_device *pdev)
>>> +{
>>> +     struct device *dev = &pdev->dev;
>>> +     struct mxs_lradc *lradc = dev_get_platdata(dev);
>>> +     struct mxs_lradc_adc *adc;
>>> +     struct iio_dev *iio;
>>> +     int ret, i, s;
>>> +     u64 scale_uv;
>>> +
>>> +     /* Allocate the IIO device. */
>>> +     iio = devm_iio_device_alloc(dev, sizeof(*adc));
>>> +     if (!iio) {
>>> +             dev_err(dev, "Failed to allocate IIO device\n");
>>> +             return -ENOMEM;
>>> +     }
>>> +
>>> +     adc = iio_priv(iio);
>>> +     adc->lradc = lradc;
>>> +     adc->dev = dev;
>>> +
>>> +     init_completion(&adc->completion);
>>> +     mutex_init(&adc->lock);
>>> +
>>> +     for (i = 0; i < lradc->irq_count; i++) {
>>> +             ret = devm_request_irq(dev, lradc->irq[i],
>>> +                                    mxs_lradc_adc_handle_irq,
>>> +                                    IRQF_SHARED, lradc->irq_name[i], iio);
>>> +             if (ret)
>>> +                     return ret;
>> This seems to be getting a whole load of irq's whether or not they are actually
>> going to be used by the ADC?  Aren't some of these going to be typically used by
>> the touchscreen?
>>> +     }
>>> +
>>> +     platform_set_drvdata(pdev, iio);
>>> +
>>> +     iio->name = pdev->name;
>>> +     iio->dev.parent = dev;
>>> +     iio->dev.of_node = dev->parent->of_node;
>>> +     iio->info = &mxs_lradc_adc_iio_info;
>>> +     iio->modes = INDIO_DIRECT_MODE;
>>> +     iio->masklength = LRADC_MAX_TOTAL_CHANS;
>>> +
>>> +     if (lradc->soc == IMX23_LRADC) {
>>> +             iio->channels = mx23_lradc_chan_spec;
>>> +             iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec);
>>> +     } else {
>>> +             iio->channels = mx28_lradc_chan_spec;
>>> +             iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec);
>>> +     }
>>> +
>>> +     ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
>>> +                                      &mxs_lradc_adc_trigger_handler,
>>> +                                      &mxs_lradc_adc_buffer_ops);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     ret = mxs_lradc_adc_trigger_init(iio);
>>> +     if (ret)
>>> +             goto err_trig;
>>> +
>>> +     adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc];
>>> +
>>> +     /* Populate available ADC input ranges */
>>> +     for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) {
>>> +             for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) {
>>> +                     /*
>>> +                      * [s=0] = optional divider by two disabled (default)
>>> +                      * [s=1] = optional divider by two enabled
>>> +                      *
>>> +                      * The scale is calculated by doing:
>>> +                      *   Vref >> (realbits - s)
>>> +                      * which multiplies by two on the second component
>>> +                      * of the array.
>>> +                      */
>>> +                     scale_uv = ((u64)adc->vref_mv[i] * 100000000) >>
>>> +                                (LRADC_RESOLUTION - s);
>>> +                     adc->scale_avail[i][s].nano =
>>> +                                     do_div(scale_uv, 100000000) * 10;
>>> +                     adc->scale_avail[i][s].integer = scale_uv;
>>> +             }
>>> +     }
>>> +
>>> +     /* Configure the hardware. */
>>> +     mxs_lradc_adc_hw_init(adc);
>>> +
>>> +     /* Register IIO device. */
>>> +     ret = iio_device_register(iio);
>>> +     if (ret) {
>>> +             dev_err(dev, "Failed to register IIO device\n");
>>> +             goto err_dev;
>>> +     }
>>> +
>>> +     return 0;
>>> +
>>> +err_dev:
>>> +     mxs_lradc_adc_hw_stop(adc);
>>> +     mxs_lradc_adc_trigger_remove(iio);
>>> +err_trig:
>>> +     iio_triggered_buffer_cleanup(iio);
>>> +     return ret;
>>> +}
>>> +
>>> +static int mxs_lradc_adc_remove(struct platform_device *pdev)
>>> +{
>>> +     struct iio_dev *iio = platform_get_drvdata(pdev);
>>> +     struct mxs_lradc_adc *adc = iio_priv(iio);
>>> +
>>> +     iio_device_unregister(iio);
>>> +     mxs_lradc_adc_hw_stop(adc);
>>> +     mxs_lradc_adc_trigger_remove(iio);
>>> +     iio_triggered_buffer_cleanup(iio);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static struct platform_driver mxs_lradc_adc_driver = {
>>> +     .driver = {
>>> +             .name   = DRIVER_NAME_ADC,
>>> +     },
>>> +     .probe  = mxs_lradc_adc_probe,
>>> +     .remove = mxs_lradc_adc_remove,
>>> +};
>>> +
>>> +module_platform_driver(mxs_lradc_adc_driver);
>>> +
>>> +MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
>>> +MODULE_DESCRIPTION("Freescale MXS LRADC driver general purpose ADC driver");
>>> +MODULE_LICENSE("GPL v2");
>>> +MODULE_ALIAS("platform:" DRIVER_NAME_ADC);
>>> +
>> Bonus blank line at end of file...
>>> +
>>>
>>
> [1] http://cache.freescale.com/files/dsp/doc/ref_manual/MCIMX28RM.pdf
> 

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

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
  2016-05-28 17:45       ` Ksenija Stanojević
  (?)
@ 2016-05-29 16:49       ` Jonathan Cameron
  2016-05-29 18:00           ` Ksenija Stanojević
  -1 siblings, 1 reply; 40+ messages in thread
From: Jonathan Cameron @ 2016-05-29 16:49 UTC (permalink / raw)
  To: Ksenija Stanojević
  Cc: linux-kernel, Lee Jones, Dmitry Torokhov, linux-input,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Marek Vašut, linux-iio, Harald Geyer

On 28/05/16 18:45, Ksenija Stanojević wrote:
> On Sun, May 1, 2016 at 6:47 PM, Jonathan Cameron <jic23@kernel.org> wrote:
>> On 29/04/16 12:49, Ksenija Stanojevic wrote:
>>> Add mxs-lradc touchscreen driver.
>>>
>>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
>> The only real thing this raises for me is why we are grabbing IRQs that I
>> don't think this driver even cares about...
>>
>> Jonathan
>>> ---
>>>  drivers/input/touchscreen/Kconfig        |  14 +-
>>>  drivers/input/touchscreen/Makefile       |   1 +
>>>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
>>>  3 files changed, 742 insertions(+), 2 deletions(-)
>>>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
>>>
>>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>>> index 8ecdc38..d614d248 100644
>>> --- a/drivers/input/touchscreen/Kconfig
>>> +++ b/drivers/input/touchscreen/Kconfig
>>> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
>>>       depends on SH_HP6XX && SH_ADC
>>>       help
>>>         Say Y here if you have a HP Jornada 620/660/680/690 and want to
>>> -          support the built-in touchscreen.
>>> +       support the built-in touchscreen.
>>>
>>>         To compile this driver as a module, choose M here: the
>>>         module will be called hp680_ts_input.
>>> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
>>>         This enables support for the Philips UCB1400 touchscreen interface.
>>>         The UCB1400 is an AC97 audio codec.  The touchscreen interface
>>>         will be initialized only after the ALSA subsystem has been
>>> -       brought up and the UCB1400 detected.  You therefore have to
>>> +       brought up and the UCB1400 detected.  You therefore have to
>>>         configure ALSA support as well (either built-in or modular,
>>>         independently of whether this driver is itself built-in or
>>>         modular) for this driver to work.
>>> @@ -842,6 +842,16 @@ config TOUCHSCREEN_MX25
>>>         To compile this driver as a module, choose M here: the
>>>         module will be called fsl-imx25-tcq.
>>>
>>> +config TOUCHSCREEN_MXS_LRADC
>>> +     tristate "Freescale i.MX23/i.MX28 LRADC touchscreen"
>>> +     depends on MFD_MXS_LRADC
>>> +     help
>>> +       Say Y here if you have a touchscreen connected to the low-resolution
>>> +       analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor.
>>> +
>>> +       To compile this driver as a module, choose M here: the module will be
>>> +       called mxs-lradc-ts.
>>> +
>>>  config TOUCHSCREEN_MC13783
>>>       tristate "Freescale MC13783 touchscreen input driver"
>>>       depends on MFD_MC13XXX
>>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
>>> index f42975e..513a6ff 100644
>>> --- a/drivers/input/touchscreen/Makefile
>>> +++ b/drivers/input/touchscreen/Makefile
>>> @@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR)             += migor_ts.o
>>>  obj-$(CONFIG_TOUCHSCREEN_MMS114)     += mms114.o
>>>  obj-$(CONFIG_TOUCHSCREEN_MTOUCH)     += mtouch.o
>>>  obj-$(CONFIG_TOUCHSCREEN_MK712)              += mk712.o
>>> +obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC)  += mxs-lradc-ts.o
>>>  obj-$(CONFIG_TOUCHSCREEN_HP600)              += hp680_ts_input.o
>>>  obj-$(CONFIG_TOUCHSCREEN_HP7XX)              += jornada720_ts.o
>>>  obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o
>>> diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c
>>> new file mode 100644
>>> index 0000000..27abb8e
>>> --- /dev/null
>>> +++ b/drivers/input/touchscreen/mxs-lradc-ts.c
>>> @@ -0,0 +1,729 @@
>>> +/*
>>> + * Freescale MXS LRADC driver
>>> + *
>>> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
>>> + * Marek Vasut <marex@denx.de>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License as published by
>>> + * the Free Software Foundation; either version 2 of the License, or
>>> + * (at your option) any later version.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>> + * GNU General Public License for more details.
>>> + */
>>> +
>>> +#include <linux/module.h>
>>> +#include <linux/mfd/mxs-lradc.h>
>>> +#include <linux/platform_device.h>
>>> +
>>> +/*
>>> + * Touchscreen handling
>>> + */
>>> +enum mxs_lradc_ts_plate {
>>> +     LRADC_TOUCH = 0,
>>> +     LRADC_SAMPLE_X,
>>> +     LRADC_SAMPLE_Y,
>>> +     LRADC_SAMPLE_PRESSURE,
>>> +     LRADC_SAMPLE_VALID,
>>> +};
>>> +
>>> +struct mxs_lradc_ts {
>>> +     struct mxs_lradc        *lradc;
>>> +     struct device           *dev;
>>> +     /*
>>> +      * When the touchscreen is enabled, we give it two private virtual
>>> +      * channels: #6 and #7. This means that only 6 virtual channels (instead
>>> +      * of 8) will be available for buffered capture.
>>> +      */
>>> +#define TOUCHSCREEN_VCHANNEL1                7
>>> +#define TOUCHSCREEN_VCHANNEL2                6
>>> +
>>> +     struct input_dev        *ts_input;
>>> +
>>> +     enum mxs_lradc_ts_plate cur_plate; /* state machine */
>>> +     bool                    ts_valid;
>>> +     unsigned                ts_x_pos;
>>> +     unsigned                ts_y_pos;
>>> +     unsigned                ts_pressure;
>>> +
>>> +     /* handle touchscreen's physical behaviour */
>>> +     /* samples per coordinate */
>>> +     unsigned                over_sample_cnt;
>>> +     /* time clocks between samples */
>>> +     unsigned                over_sample_delay;
>>> +     /* time in clocks to wait after the plates where switched */
>>> +     unsigned                settling_delay;
>>> +};
>>> +
>>> +static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
>>> +{
>>> +     if (lradc->soc == IMX23_LRADC)
>>> +             return LRADC_CTRL0_MX23_PLATE_MASK;
>>> +     return LRADC_CTRL0_MX28_PLATE_MASK;
>>> +}
>>> +
>>> +static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
>>> +{
>>> +     if (lradc->soc == IMX23_LRADC)
>>> +             return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
>>> +     return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE;
>>> +}
>>> +
>>> +static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc)
>>> +{
>>> +     if (lradc->soc == IMX23_LRADC)
>>> +             return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM;
>>> +     return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW;
>>> +}
>>> +
>>> +static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc)
>>> +{
>>> +     if (lradc->soc == IMX23_LRADC)
>>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM;
>>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW;
>>> +}
>>> +
>>> +static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc)
>>> +{
>>> +     if (lradc->soc == IMX23_LRADC)
>>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM;
>>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW;
>>> +}
>>> +
>>> +static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
>>> +{
>>> +     return !!(readl(lradc->base + LRADC_STATUS) &
>>> +                                     LRADC_STATUS_TOUCH_DETECT_RAW);
>>> +}
>>> +
>>> +static void mxs_lradc_map_ts_channel(struct mxs_lradc *lradc, unsigned vch,
>>> +                                  unsigned ch)
>>> +{
>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
>>> +                         LRADC_CTRL4);
>>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4);
>>> +}
>>> +
>>> +static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned ch)
>>> +{
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +
>>> +     /*
>>> +      * prepare for oversampling conversion
>>> +      *
>>> +      * from the datasheet:
>>> +      * "The ACCUMULATE bit in the appropriate channel register
>>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
>>> +      * otherwise, the IRQs will not fire."
>>> +      */
>>> +     mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE |
>>> +                       LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1),
>>> +                       LRADC_CH(ch));
>>> +
>>> +     /* from the datasheet:
>>> +      * "Software must clear this register in preparation for a
>>> +      * multi-cycle accumulation.
>>> +      */
>>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch));
>>> +
>>> +     /*
>>> +      * prepare the delay/loop unit according to the oversampling count
>>> +      *
>>> +      * from the datasheet:
>>> +      * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
>>> +      * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
>>> +      * the LRADC will not trigger the delay group."
>>> +      */
>>> +     mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
>>> +                       LRADC_DELAY_TRIGGER_DELAYS(0) |
>>> +                       LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>>> +                       LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>>> +                       LRADC_DELAY(3));
>>> +
>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1);
>>> +
>>> +     /*
>>> +      * after changing the touchscreen plates setting
>>> +      * the signals need some initial time to settle. Start the
>>> +      * SoC's delay unit and start the conversion later
>>> +      * and automatically.
>>> +      */
>>> +     mxs_lradc_reg_wrt(
>>> +             lradc,
>>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
>>> +             LRADC_DELAY_KICK |
>>> +             LRADC_DELAY_DELAY(ts->settling_delay),
>>> +             LRADC_DELAY(2));
>>> +}
>>> +
>>> +/*
>>> + * Pressure detection is special:
>>> + * We want to do both required measurements for the pressure detection in
>>> + * one turn. Use the hardware features to chain both conversions and let the
>>> + * hardware report one interrupt if both conversions are done
>>> + */
>>> +static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts, unsigned ch1,
>>> +                                     unsigned ch2)
>>> +{
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +     u32 reg;
>>> +
>>> +     /*
>>> +      * prepare for oversampling conversion
>>> +      *
>>> +      * from the datasheet:
>>> +      * "The ACCUMULATE bit in the appropriate channel register
>>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
>>> +      * otherwise, the IRQs will not fire."
>>> +      */
>>> +     reg = LRADC_CH_ACCUMULATE |
>>> +             LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1);
>>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
>>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
>>> +
>>> +     /* from the datasheet:
>>> +      * "Software must clear this register in preparation for a
>>> +      * multi-cycle accumulation.
>>> +      */
>>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1));
>>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2));
>>> +
>>> +     /* prepare the delay/loop unit according to the oversampling count */
>>> +     mxs_lradc_reg_wrt(
>>> +                 lradc,
>>> +                 LRADC_DELAY_TRIGGER(1 << ch1) |
>>> +                 LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */
>>> +                 LRADC_DELAY_TRIGGER_DELAYS(0) |
>>> +                 LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>>> +                 LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>>> +                 LRADC_DELAY(3));
>>> +
>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1);
>>> +
>>> +     /*
>>> +      * after changing the touchscreen plates setting
>>> +      * the signals need some initial time to settle. Start the
>>> +      * SoC's delay unit and start the conversion later
>>> +      * and automatically.
>>> +      */
>>> +     mxs_lradc_reg_wrt(
>>> +             lradc,
>>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
>>> +             LRADC_DELAY_KICK |
>>> +             LRADC_DELAY_DELAY(ts->settling_delay), LRADC_DELAY(2));
>>> +}
>>> +
>>> +static unsigned mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts,
>>> +                                           unsigned channel)
>>> +{
>>> +     u32 reg;
>>> +     unsigned num_samples, val;
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +
>>> +     reg = readl(lradc->base + LRADC_CH(channel));
>>> +     if (reg & LRADC_CH_ACCUMULATE)
>>> +             num_samples = ts->over_sample_cnt;
>>> +     else
>>> +             num_samples = 1;
>>> +
>>> +     val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
>>> +     return val / num_samples;
>>> +}
>>> +
>>> +static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts,
>>> +                                        unsigned ch1, unsigned ch2)
>>> +{
>>> +     u32 reg, mask;
>>> +     unsigned pressure, m1, m2;
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +
>>> +     mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
>>> +     reg = readl(lradc->base + LRADC_CTRL1) & mask;
>>> +
>>> +     while (reg != mask) {
>>> +             reg = readl(lradc->base + LRADC_CTRL1) & mask;
>>> +             dev_dbg(ts->dev, "One channel is still busy: %X\n", reg);
>>> +     }
>>> +
>>> +     m1 = mxs_lradc_ts_read_raw_channel(ts, ch1);
>>> +     m2 = mxs_lradc_ts_read_raw_channel(ts, ch2);
>>> +
>>> +     if (m2 == 0) {
>>> +             dev_warn(ts->dev, "Cannot calculate pressure\n");
>>> +             return 1 << (LRADC_RESOLUTION - 1);
>>> +     }
>>> +
>>> +     /* simply scale the value from 0 ... max ADC resolution */
>>> +     pressure = m1;
>>> +     pressure *= (1 << LRADC_RESOLUTION);
>>> +     pressure /= m2;
>>> +
>>> +     dev_dbg(ts->dev, "Pressure = %u\n", pressure);
>>> +     return pressure;
>>> +}
>>> +
>>> +#define TS_CH_XP 2
>>> +#define TS_CH_YP 3
>>> +#define TS_CH_XM 4
>>> +#define TS_CH_YM 5
>>> +
>>> +/*
>>> + * YP(open)--+-------------+
>>> + *        |             |--+
>>> + *        |             |  |
>>> + *    YM(-)--+-------------+  |
>>> + *          +--------------+
>>> + *          |              |
>>> + *      XP(weak+)        XM(open)
>>> + *
>>> + * "weak+" means 200k Ohm VDDIO
>>> + * (-) means GND
>>> + */
>>> +static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts)
>>> +{
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +
>>> +     /*
>>> +      * In order to detect a touch event the 'touch detect enable' bit
>>> +      * enables:
>>> +      *  - a weak pullup to the X+ connector
>>> +      *  - a strong ground at the Y- connector
>>> +      */
>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc),
>>> +                       LRADC_CTRL0);
>>> +}
>>> +
>>> +/*
>>> + * YP(meas)--+-------------+
>>> + *        |             |--+
>>> + *        |             |  |
>>> + * YM(open)--+-------------+  |
>>> + *          +--------------+
>>> + *          |              |
>>> + *        XP(+)          XM(-)
>>> + *
>>> + * (+) means here 1.85 V
>>> + * (-) means here GND
>>> + */
>>> +static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts)
>>> +{
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +
>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
>>> +
>>> +     ts->cur_plate = LRADC_SAMPLE_X;
>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
>>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>>> +}
>>> +
>>> +/*
>>> + *   YP(+)--+-------------+
>>> + *       |             |--+
>>> + *       |             |  |
>>> + *   YM(-)--+-------------+  |
>>> + *         +--------------+
>>> + *         |              |
>>> + *      XP(open)        XM(meas)
>>> + *
>>> + * (+) means here 1.85 V
>>> + * (-) means here GND
>>> + */
>>> +static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts)
>>> +{
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +
>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
>>> +
>>> +     ts->cur_plate = LRADC_SAMPLE_Y;
>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
>>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>>> +}
>>> +
>>> +/*
>>> + *    YP(+)--+-------------+
>>> + *        |             |--+
>>> + *        |             |  |
>>> + * YM(meas)--+-------------+  |
>>> + *          +--------------+
>>> + *          |              |
>>> + *       XP(meas)        XM(-)
>>> + *
>>> + * (+) means here 1.85 V
>>> + * (-) means here GND
>>> + */
>>> +static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts)
>>> +{
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +
>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
>>> +
>>> +     ts->cur_plate = LRADC_SAMPLE_PRESSURE;
>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
>>> +     mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2,
>>> +                                 TOUCHSCREEN_VCHANNEL1);
>>> +}
>>> +
>>> +static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts)
>>> +{
>>> +     mxs_lradc_setup_touch_detection(ts);
>>> +
>>> +     ts->cur_plate = LRADC_TOUCH;
>>> +     mxs_lradc_reg_clear(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
>>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
>>> +     mxs_lradc_reg_set(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>>> +                       LRADC_CTRL1);
>>> +}
>>> +
>>> +static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts)
>>> +{
>>> +     mxs_lradc_reg_clear(ts->lradc,
>>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>>> +                         LRADC_CTRL1);
>>> +     mxs_lradc_reg_set(ts->lradc,
>>> +                       LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1),
>>> +                       LRADC_CTRL1);
>>> +     /*
>>> +      * start with the Y-pos, because it uses nearly the same plate
>>> +      * settings like the touch detection
>>> +      */
>>> +     mxs_lradc_prepare_y_pos(ts);
>>> +}
>>> +
>>> +static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts)
>>> +{
>>> +     input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos);
>>> +     input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos);
>>> +     input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure);
>>> +     input_report_key(ts->ts_input, BTN_TOUCH, 1);
>>> +     input_sync(ts->ts_input);
>>> +}
>>> +
>>> +static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts)
>>> +{
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +
>>> +     mxs_lradc_setup_touch_detection(ts);
>>> +     ts->cur_plate = LRADC_SAMPLE_VALID;
>>> +     /*
>>> +      * start a dummy conversion to burn time to settle the signals
>>> +      * note: we are not interested in the conversion's value
>>> +      */
>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
>>> +     mxs_lradc_reg_clear(lradc,
>>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2),
>>> +                         LRADC_CTRL1);
>>> +     mxs_lradc_reg_wrt(
>>> +                 lradc,
>>> +                 LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
>>> +                 LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
>>> +                 LRADC_DELAY(2));
>>> +}
>>> +
>>> +/*
>>> + * in order to avoid false measurements, report only samples where
>>> + * the surface is still touched after the position measurement
>>> + */
>>> +static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid)
>>> +{
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +
>>> +     /* if it is still touched, report the sample */
>>> +     if (valid && mxs_lradc_check_touch_event(lradc)) {
>>> +             ts->ts_valid = true;
>>> +             mxs_lradc_report_ts_event(ts);
>>> +     }
>>> +
>>> +     /* if it is even still touched, continue with the next measurement */
>>> +     if (mxs_lradc_check_touch_event(lradc)) {
>>> +             mxs_lradc_prepare_y_pos(ts);
>>> +             return;
>>> +     }
>>> +
>>> +     if (ts->ts_valid) {
>>> +             /* signal the release */
>>> +             ts->ts_valid = false;
>>> +             input_report_key(ts->ts_input, BTN_TOUCH, 0);
>>> +             input_sync(ts->ts_input);
>>> +     }
>>> +
>>> +     /* if it is released, wait for the next touch via IRQ */
>>> +     ts->cur_plate = LRADC_TOUCH;
>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
>>> +     mxs_lradc_reg_clear(lradc,
>>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ |
>>> +                         LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
>>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1),
>>> +                         LRADC_CTRL1);
>>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
>>> +}
>>> +
>>> +/* touchscreen's state machine */
>>> +static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts)
>>> +{
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +
>>> +     switch (ts->cur_plate) {
>>> +     case LRADC_TOUCH:
>>> +             if (mxs_lradc_check_touch_event(lradc))
>>> +                     mxs_lradc_start_touch_event(ts);
>>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
>>> +                                 LRADC_CTRL1);
>>> +             return;
>>> +
>>> +     case LRADC_SAMPLE_Y:
>>> +             ts->ts_y_pos =
>>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
>>> +             mxs_lradc_prepare_x_pos(ts);
>>> +             return;
>>> +
>>> +     case LRADC_SAMPLE_X:
>>> +             ts->ts_x_pos =
>>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
>>> +             mxs_lradc_prepare_pressure(ts);
>>> +             return;
>>> +
>>> +     case LRADC_SAMPLE_PRESSURE:
>>> +             ts->ts_pressure =
>>> +                 mxs_lradc_read_ts_pressure(ts,
>>> +                                            TOUCHSCREEN_VCHANNEL2,
>>> +                                            TOUCHSCREEN_VCHANNEL1);
>>> +             mxs_lradc_complete_touch_event(ts);
>>> +             return;
>>> +
>>> +     case LRADC_SAMPLE_VALID:
>>> +             mxs_lradc_finish_touch_event(ts, 1);
>>> +             break;
>>> +     }
>>> +}
>>> +
>>> +/*
>>> + * IRQ Handling
>>> + */
>>> +static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data)
>>> +{
>>> +     struct mxs_lradc_ts *ts = data;
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +     unsigned long reg = readl(lradc->base + LRADC_CTRL1);
>>> +     u32 clr_irq = mxs_lradc_irq_mask(lradc);
>>> +     const u32 ts_irq_mask =
>>> +             LRADC_CTRL1_TOUCH_DETECT_IRQ |
>>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
>>> +
>>> +     if (!(reg & mxs_lradc_irq_mask(lradc)))
>>> +             return IRQ_NONE;
>>> +
>>> +     if (lradc->use_touchscreen && (reg & ts_irq_mask)) {
>>> +             mxs_lradc_handle_touch(ts);
>>> +
>>> +             /* Make sure we don't clear the next conversion's interrupt. */
>>> +             clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>>> +                             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
>>> +             mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
>>> +     }
>>> +
>>> +     return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int mxs_lradc_ts_open(struct input_dev *dev)
>>> +{
>>> +     struct mxs_lradc_ts *ts = input_get_drvdata(dev);
>>> +
>>> +     /* Enable the touch-detect circuitry. */
>>> +     mxs_lradc_enable_touch_detection(ts);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static void mxs_lradc_disable_ts(struct mxs_lradc_ts *ts)
>>> +{
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +
>>> +     /* stop all interrupts from firing */
>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
>>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
>>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
>>> +
>>> +     /* Power-down touchscreen touch-detect circuitry. */
>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>>> +}
>>> +
>>> +static void mxs_lradc_ts_close(struct input_dev *dev)
>>> +{
>>> +     struct mxs_lradc_ts *ts = input_get_drvdata(dev);
>>> +
>>> +     mxs_lradc_disable_ts(ts);
>>> +}
>>> +
>>> +static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts)
>>> +{
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +
>>> +     /* Configure the touchscreen type */
>>> +     if (lradc->soc == IMX28_LRADC) {
>>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
>>> +                                 LRADC_CTRL0);
>>> +
>>> +             if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
>>> +                     mxs_lradc_reg_set(lradc,
>>> +                                       LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
>>> +                                       LRADC_CTRL0);
>>> +     }
>>> +}
>>> +
>>> +static void mxs_lradc_ts_hw_stop(struct mxs_lradc_ts *ts)
>>> +{
>>> +     int i;
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +
>>> +     mxs_lradc_reg_clear(lradc,
>>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
>>> +                     LRADC_CTRL1);
>>> +
>>> +     for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++)
>>> +             mxs_lradc_reg_wrt(ts->lradc, 0, LRADC_DELAY(i));
>>> +}
>>> +
>>> +static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts)
>>> +{
>>> +     struct input_dev *input = ts->ts_input;
>>> +     struct device *dev = ts->dev;
>>> +     struct mxs_lradc *lradc = ts->lradc;
>>> +
>>> +     if (!lradc->use_touchscreen)
>>> +             return 0;
>>> +
>>> +     input->name = DRIVER_NAME_TS;
>>> +     input->id.bustype = BUS_HOST;
>>> +     input->dev.parent = dev;
>>> +     input->open = mxs_lradc_ts_open;
>>> +     input->close = mxs_lradc_ts_close;
>>> +
>>> +     __set_bit(EV_ABS, input->evbit);
>>> +     __set_bit(EV_KEY, input->evbit);
>>> +     __set_bit(BTN_TOUCH, input->keybit);
>>> +     __set_bit(INPUT_PROP_DIRECT, input->propbit);
>>> +     input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
>>> +     input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
>>> +     input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
>>> +                          0, 0);
>>> +
>>> +     ts->ts_input = input;
>>> +     input_set_drvdata(input, ts);
>>> +
>>> +     return input_register_device(input);
>>> +}
>>> +
>>> +static int mxs_lradc_ts_probe(struct platform_device *pdev)
>>> +{
>>> +     struct device *dev = &pdev->dev;
>>> +     struct device_node *node = dev->parent->of_node;
>>> +     struct mxs_lradc *lradc = dev_get_platdata(dev);
>>> +     struct mxs_lradc_ts *ts;
>>> +     struct input_dev *input;
>>> +     int touch_ret, ret, i;
>>> +     u32 ts_wires = 0, adapt;
>>> +
>>> +     ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
>>> +     input = devm_input_allocate_device(dev);
>>> +     if (!ts || !input)
>>> +             return -ENOMEM;
>>> +
>>> +     ts->lradc = lradc;
>>> +     ts->dev = dev;
>>> +     ts->ts_input = input;
>>> +     platform_set_drvdata(pdev, ts);
>>> +     input_set_drvdata(input, ts);
>>> +
>>> +     touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
>>> +                                      &ts_wires);
>>> +
>>> +     if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) {
>>> +             ts->over_sample_cnt = 4;
>>> +     } else {
>>> +             if (adapt < 1 || adapt > 32) {
>>> +                     dev_err(ts->dev, "Invalid sample count (%u)\n",
>>> +                             adapt);
>>> +                     touch_ret = -EINVAL;
>>> +             } else {
>>> +                     ts->over_sample_cnt = adapt;
>>> +             }
>>> +     }
>>> +
>>> +     if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) {
>>> +             ts->over_sample_delay = 2;
>>> +     } else {
>>> +             if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) {
>>> +                     dev_err(ts->dev, "Invalid sample delay (%u)\n",
>>> +                             adapt);
>>> +                     touch_ret = -EINVAL;
>>> +             } else {
>>> +                     ts->over_sample_delay = adapt;
>>> +             }
>>> +     }
>>> +
>>> +     if (of_property_read_u32(node, "fsl,settling", &adapt)) {
>>> +             ts->settling_delay = 10;
>>> +     } else {
>>> +             if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) {
>>> +                     dev_err(ts->dev, "Invalid settling delay (%u)\n",
>>> +                             adapt);
>>> +                     touch_ret = -EINVAL;
>>> +             } else {
>>> +                     ts->settling_delay = adapt;
>>> +             }
>>> +     }
>>> +
>>> +     mxs_lradc_ts_hw_init(ts);
>>> +     for (i = 0; i < lradc->irq_count; i++) {
>>> +             ret = devm_request_irq(dev, lradc->irq[i],
>>> +                                    mxs_lradc_ts_handle_irq,
>>> +                                    IRQF_SHARED, lradc->irq_name[i], ts);
>> As with the adc driver, are we actually using all of these?  I'd prefer we
>> only grab the ones that are actually relevant.
> 
> Only irq lines relevant for touchscreen are:
> mxs-lradc-touchscreen, mxs-lradc-channel6 and mxs-lradc-channel7
> But not all interrupts are beiing used even when I enabled all remaining
> channels (not used by touchscreen) for bufferd capture via
> echo 1 >/sys/bus/iio/devices/iio\:device0/scan_elements/in_voltagexx_en
> 
> So I don't know if it's supposed to work like this...
> (It works the same on the original code)
Certainly should only grab the relevant ones to touch screen use in here..
Original code probably being overly enthusiastic and we never noticed ;)
> 
> root@cfa100xx:~# cat /proc/interrupts
>            CPU0
>  16:      13108         -  48 Edge      MXS Timer Tick
>  17:       4240         -  82 Edge      mxs-dma
>  25:          6         -  96 Edge      80010000.ssp
> 196:          0         -  68 Edge      mxs-dma
> 210:         13         -  10 Edge      mxs-lradc-touchscreen
> 211:          0         -  14 Edge      mxs-lradc-thresh0
> 212:          0         -  15 Edge      mxs-lradc-thresh1
> 213:         10         -  16 Edge      mxs-lradc-channel0
> 214:         10         -  17 Edge      mxs-lradc-channel1
> 215:          0         -  18 Edge      mxs-lradc-channel2
> 216:          0         -  19 Edge      mxs-lradc-channel3
> 217:          0         -  20 Edge      mxs-lradc-channel4
> 218:          0         -  21 Edge      mxs-lradc-channel5
> 219:          0         -  22 Edge      mxs-lradc-channel6
> 220:        412         -  23 Edge      mxs-lradc-channel7
> 221:          0         -  24 Edge      mxs-lradc-button0
> 222:          0         -  25 Edge      mxs-lradc-button1
> 223:          0         -  29 Edge      RTC alarm
> 224:          0         - 111 Edge      80058000.i2c
> 228:        174         -  47 Edge      uart-pl011
> 229:        439         -  93 Edge      80080000.usb
> 230:          0         -  92 Edge      80090000.usb
> 231:       3610         - 101 Edge      800f0000.ethernet
> 232:         10  80050000.lradc-dev0 Edge
> Err:          0
> 
>>> +             if (ret)
>>> +                     return ret;
>>> +     }
>>> +
>>> +     if (!touch_ret) {
>>> +             ret = mxs_lradc_ts_register(ts);
>>> +             if (!ret)
>>> +                     goto err_ts_register;
>>> +     }
>>> +
>>> +     return 0;
>>> +
>>> +err_ts_register:
>>> +     mxs_lradc_ts_hw_stop(ts);
>>> +     return ret;
>>> +}
>>> +
>>> +static int mxs_lradc_ts_remove(struct platform_device *pdev)
>>> +{
>>> +     struct mxs_lradc_ts *ts = platform_get_drvdata(pdev);
>>> +
>>> +     mxs_lradc_ts_hw_stop(ts);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static struct platform_driver mxs_lradc_ts_driver = {
>>> +     .driver = {
>>> +             .name = DRIVER_NAME_TS,
>>> +     },
>>> +     .probe  = mxs_lradc_ts_probe,
>>> +     .remove = mxs_lradc_ts_remove,
>>> +};
>>> +module_platform_driver(mxs_lradc_ts_driver);
>>> +
>>> +MODULE_LICENSE("GPL v2");
>>>
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
@ 2016-05-29 18:00           ` Ksenija Stanojević
  0 siblings, 0 replies; 40+ messages in thread
From: Ksenija Stanojević @ 2016-05-29 18:00 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-kernel, Lee Jones, Dmitry Torokhov, linux-input,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Marek Vašut, linux-iio, Harald Geyer

On Sun, May 29, 2016 at 6:49 PM, Jonathan Cameron <jic23@kernel.org> wrote:
> On 28/05/16 18:45, Ksenija Stanojević wrote:
>> On Sun, May 1, 2016 at 6:47 PM, Jonathan Cameron <jic23@kernel.org> wrote:
>>> On 29/04/16 12:49, Ksenija Stanojevic wrote:
>>>> Add mxs-lradc touchscreen driver.
>>>>
>>>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
>>> The only real thing this raises for me is why we are grabbing IRQs that I
>>> don't think this driver even cares about...
>>>
>>> Jonathan
>>>> ---
>>>>  drivers/input/touchscreen/Kconfig        |  14 +-
>>>>  drivers/input/touchscreen/Makefile       |   1 +
>>>>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
>>>>  3 files changed, 742 insertions(+), 2 deletions(-)
>>>>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
>>>>
>>>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>>>> index 8ecdc38..d614d248 100644
>>>> --- a/drivers/input/touchscreen/Kconfig
>>>> +++ b/drivers/input/touchscreen/Kconfig
>>>> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
>>>>       depends on SH_HP6XX && SH_ADC
>>>>       help
>>>>         Say Y here if you have a HP Jornada 620/660/680/690 and want to
>>>> -          support the built-in touchscreen.
>>>> +       support the built-in touchscreen.
>>>>
>>>>         To compile this driver as a module, choose M here: the
>>>>         module will be called hp680_ts_input.
>>>> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
>>>>         This enables support for the Philips UCB1400 touchscreen interface.
>>>>         The UCB1400 is an AC97 audio codec.  The touchscreen interface
>>>>         will be initialized only after the ALSA subsystem has been
>>>> -       brought up and the UCB1400 detected.  You therefore have to
>>>> +       brought up and the UCB1400 detected.  You therefore have to
>>>>         configure ALSA support as well (either built-in or modular,
>>>>         independently of whether this driver is itself built-in or
>>>>         modular) for this driver to work.
>>>> @@ -842,6 +842,16 @@ config TOUCHSCREEN_MX25
>>>>         To compile this driver as a module, choose M here: the
>>>>         module will be called fsl-imx25-tcq.
>>>>
>>>> +config TOUCHSCREEN_MXS_LRADC
>>>> +     tristate "Freescale i.MX23/i.MX28 LRADC touchscreen"
>>>> +     depends on MFD_MXS_LRADC
>>>> +     help
>>>> +       Say Y here if you have a touchscreen connected to the low-resolution
>>>> +       analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor.
>>>> +
>>>> +       To compile this driver as a module, choose M here: the module will be
>>>> +       called mxs-lradc-ts.
>>>> +
>>>>  config TOUCHSCREEN_MC13783
>>>>       tristate "Freescale MC13783 touchscreen input driver"
>>>>       depends on MFD_MC13XXX
>>>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
>>>> index f42975e..513a6ff 100644
>>>> --- a/drivers/input/touchscreen/Makefile
>>>> +++ b/drivers/input/touchscreen/Makefile
>>>> @@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR)             += migor_ts.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_MMS114)     += mms114.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_MTOUCH)     += mtouch.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_MK712)              += mk712.o
>>>> +obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC)  += mxs-lradc-ts.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_HP600)              += hp680_ts_input.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_HP7XX)              += jornada720_ts.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o
>>>> diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c
>>>> new file mode 100644
>>>> index 0000000..27abb8e
>>>> --- /dev/null
>>>> +++ b/drivers/input/touchscreen/mxs-lradc-ts.c
>>>> @@ -0,0 +1,729 @@
>>>> +/*
>>>> + * Freescale MXS LRADC driver
>>>> + *
>>>> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
>>>> + * Marek Vasut <marex@denx.de>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify
>>>> + * it under the terms of the GNU General Public License as published by
>>>> + * the Free Software Foundation; either version 2 of the License, or
>>>> + * (at your option) any later version.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful,
>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>>> + * GNU General Public License for more details.
>>>> + */
>>>> +
>>>> +#include <linux/module.h>
>>>> +#include <linux/mfd/mxs-lradc.h>
>>>> +#include <linux/platform_device.h>
>>>> +
>>>> +/*
>>>> + * Touchscreen handling
>>>> + */
>>>> +enum mxs_lradc_ts_plate {
>>>> +     LRADC_TOUCH = 0,
>>>> +     LRADC_SAMPLE_X,
>>>> +     LRADC_SAMPLE_Y,
>>>> +     LRADC_SAMPLE_PRESSURE,
>>>> +     LRADC_SAMPLE_VALID,
>>>> +};
>>>> +
>>>> +struct mxs_lradc_ts {
>>>> +     struct mxs_lradc        *lradc;
>>>> +     struct device           *dev;
>>>> +     /*
>>>> +      * When the touchscreen is enabled, we give it two private virtual
>>>> +      * channels: #6 and #7. This means that only 6 virtual channels (instead
>>>> +      * of 8) will be available for buffered capture.
>>>> +      */
>>>> +#define TOUCHSCREEN_VCHANNEL1                7
>>>> +#define TOUCHSCREEN_VCHANNEL2                6
>>>> +
>>>> +     struct input_dev        *ts_input;
>>>> +
>>>> +     enum mxs_lradc_ts_plate cur_plate; /* state machine */
>>>> +     bool                    ts_valid;
>>>> +     unsigned                ts_x_pos;
>>>> +     unsigned                ts_y_pos;
>>>> +     unsigned                ts_pressure;
>>>> +
>>>> +     /* handle touchscreen's physical behaviour */
>>>> +     /* samples per coordinate */
>>>> +     unsigned                over_sample_cnt;
>>>> +     /* time clocks between samples */
>>>> +     unsigned                over_sample_delay;
>>>> +     /* time in clocks to wait after the plates where switched */
>>>> +     unsigned                settling_delay;
>>>> +};
>>>> +
>>>> +static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
>>>> +{
>>>> +     if (lradc->soc == IMX23_LRADC)
>>>> +             return LRADC_CTRL0_MX23_PLATE_MASK;
>>>> +     return LRADC_CTRL0_MX28_PLATE_MASK;
>>>> +}
>>>> +
>>>> +static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
>>>> +{
>>>> +     if (lradc->soc == IMX23_LRADC)
>>>> +             return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
>>>> +     return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE;
>>>> +}
>>>> +
>>>> +static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc)
>>>> +{
>>>> +     if (lradc->soc == IMX23_LRADC)
>>>> +             return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM;
>>>> +     return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW;
>>>> +}
>>>> +
>>>> +static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc)
>>>> +{
>>>> +     if (lradc->soc == IMX23_LRADC)
>>>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM;
>>>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW;
>>>> +}
>>>> +
>>>> +static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc)
>>>> +{
>>>> +     if (lradc->soc == IMX23_LRADC)
>>>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM;
>>>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW;
>>>> +}
>>>> +
>>>> +static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
>>>> +{
>>>> +     return !!(readl(lradc->base + LRADC_STATUS) &
>>>> +                                     LRADC_STATUS_TOUCH_DETECT_RAW);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_map_ts_channel(struct mxs_lradc *lradc, unsigned vch,
>>>> +                                  unsigned ch)
>>>> +{
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
>>>> +                         LRADC_CTRL4);
>>>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned ch)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     /*
>>>> +      * prepare for oversampling conversion
>>>> +      *
>>>> +      * from the datasheet:
>>>> +      * "The ACCUMULATE bit in the appropriate channel register
>>>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
>>>> +      * otherwise, the IRQs will not fire."
>>>> +      */
>>>> +     mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE |
>>>> +                       LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1),
>>>> +                       LRADC_CH(ch));
>>>> +
>>>> +     /* from the datasheet:
>>>> +      * "Software must clear this register in preparation for a
>>>> +      * multi-cycle accumulation.
>>>> +      */
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch));
>>>> +
>>>> +     /*
>>>> +      * prepare the delay/loop unit according to the oversampling count
>>>> +      *
>>>> +      * from the datasheet:
>>>> +      * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
>>>> +      * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
>>>> +      * the LRADC will not trigger the delay group."
>>>> +      */
>>>> +     mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
>>>> +                       LRADC_DELAY_TRIGGER_DELAYS(0) |
>>>> +                       LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>>>> +                       LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>>>> +                       LRADC_DELAY(3));
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1);
>>>> +
>>>> +     /*
>>>> +      * after changing the touchscreen plates setting
>>>> +      * the signals need some initial time to settle. Start the
>>>> +      * SoC's delay unit and start the conversion later
>>>> +      * and automatically.
>>>> +      */
>>>> +     mxs_lradc_reg_wrt(
>>>> +             lradc,
>>>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>>>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
>>>> +             LRADC_DELAY_KICK |
>>>> +             LRADC_DELAY_DELAY(ts->settling_delay),
>>>> +             LRADC_DELAY(2));
>>>> +}
>>>> +
>>>> +/*
>>>> + * Pressure detection is special:
>>>> + * We want to do both required measurements for the pressure detection in
>>>> + * one turn. Use the hardware features to chain both conversions and let the
>>>> + * hardware report one interrupt if both conversions are done
>>>> + */
>>>> +static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts, unsigned ch1,
>>>> +                                     unsigned ch2)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +     u32 reg;
>>>> +
>>>> +     /*
>>>> +      * prepare for oversampling conversion
>>>> +      *
>>>> +      * from the datasheet:
>>>> +      * "The ACCUMULATE bit in the appropriate channel register
>>>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
>>>> +      * otherwise, the IRQs will not fire."
>>>> +      */
>>>> +     reg = LRADC_CH_ACCUMULATE |
>>>> +             LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1);
>>>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
>>>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
>>>> +
>>>> +     /* from the datasheet:
>>>> +      * "Software must clear this register in preparation for a
>>>> +      * multi-cycle accumulation.
>>>> +      */
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1));
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2));
>>>> +
>>>> +     /* prepare the delay/loop unit according to the oversampling count */
>>>> +     mxs_lradc_reg_wrt(
>>>> +                 lradc,
>>>> +                 LRADC_DELAY_TRIGGER(1 << ch1) |
>>>> +                 LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */
>>>> +                 LRADC_DELAY_TRIGGER_DELAYS(0) |
>>>> +                 LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>>>> +                 LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>>>> +                 LRADC_DELAY(3));
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1);
>>>> +
>>>> +     /*
>>>> +      * after changing the touchscreen plates setting
>>>> +      * the signals need some initial time to settle. Start the
>>>> +      * SoC's delay unit and start the conversion later
>>>> +      * and automatically.
>>>> +      */
>>>> +     mxs_lradc_reg_wrt(
>>>> +             lradc,
>>>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>>>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
>>>> +             LRADC_DELAY_KICK |
>>>> +             LRADC_DELAY_DELAY(ts->settling_delay), LRADC_DELAY(2));
>>>> +}
>>>> +
>>>> +static unsigned mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts,
>>>> +                                           unsigned channel)
>>>> +{
>>>> +     u32 reg;
>>>> +     unsigned num_samples, val;
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     reg = readl(lradc->base + LRADC_CH(channel));
>>>> +     if (reg & LRADC_CH_ACCUMULATE)
>>>> +             num_samples = ts->over_sample_cnt;
>>>> +     else
>>>> +             num_samples = 1;
>>>> +
>>>> +     val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
>>>> +     return val / num_samples;
>>>> +}
>>>> +
>>>> +static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts,
>>>> +                                        unsigned ch1, unsigned ch2)
>>>> +{
>>>> +     u32 reg, mask;
>>>> +     unsigned pressure, m1, m2;
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
>>>> +     reg = readl(lradc->base + LRADC_CTRL1) & mask;
>>>> +
>>>> +     while (reg != mask) {
>>>> +             reg = readl(lradc->base + LRADC_CTRL1) & mask;
>>>> +             dev_dbg(ts->dev, "One channel is still busy: %X\n", reg);
>>>> +     }
>>>> +
>>>> +     m1 = mxs_lradc_ts_read_raw_channel(ts, ch1);
>>>> +     m2 = mxs_lradc_ts_read_raw_channel(ts, ch2);
>>>> +
>>>> +     if (m2 == 0) {
>>>> +             dev_warn(ts->dev, "Cannot calculate pressure\n");
>>>> +             return 1 << (LRADC_RESOLUTION - 1);
>>>> +     }
>>>> +
>>>> +     /* simply scale the value from 0 ... max ADC resolution */
>>>> +     pressure = m1;
>>>> +     pressure *= (1 << LRADC_RESOLUTION);
>>>> +     pressure /= m2;
>>>> +
>>>> +     dev_dbg(ts->dev, "Pressure = %u\n", pressure);
>>>> +     return pressure;
>>>> +}
>>>> +
>>>> +#define TS_CH_XP 2
>>>> +#define TS_CH_YP 3
>>>> +#define TS_CH_XM 4
>>>> +#define TS_CH_YM 5
>>>> +
>>>> +/*
>>>> + * YP(open)--+-------------+
>>>> + *        |             |--+
>>>> + *        |             |  |
>>>> + *    YM(-)--+-------------+  |
>>>> + *          +--------------+
>>>> + *          |              |
>>>> + *      XP(weak+)        XM(open)
>>>> + *
>>>> + * "weak+" means 200k Ohm VDDIO
>>>> + * (-) means GND
>>>> + */
>>>> +static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     /*
>>>> +      * In order to detect a touch event the 'touch detect enable' bit
>>>> +      * enables:
>>>> +      *  - a weak pullup to the X+ connector
>>>> +      *  - a strong ground at the Y- connector
>>>> +      */
>>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc),
>>>> +                       LRADC_CTRL0);
>>>> +}
>>>> +
>>>> +/*
>>>> + * YP(meas)--+-------------+
>>>> + *        |             |--+
>>>> + *        |             |  |
>>>> + * YM(open)--+-------------+  |
>>>> + *          +--------------+
>>>> + *          |              |
>>>> + *        XP(+)          XM(-)
>>>> + *
>>>> + * (+) means here 1.85 V
>>>> + * (-) means here GND
>>>> + */
>>>> +static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
>>>> +
>>>> +     ts->cur_plate = LRADC_SAMPLE_X;
>>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
>>>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>>>> +}
>>>> +
>>>> +/*
>>>> + *   YP(+)--+-------------+
>>>> + *       |             |--+
>>>> + *       |             |  |
>>>> + *   YM(-)--+-------------+  |
>>>> + *         +--------------+
>>>> + *         |              |
>>>> + *      XP(open)        XM(meas)
>>>> + *
>>>> + * (+) means here 1.85 V
>>>> + * (-) means here GND
>>>> + */
>>>> +static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
>>>> +
>>>> +     ts->cur_plate = LRADC_SAMPLE_Y;
>>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
>>>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>>>> +}
>>>> +
>>>> +/*
>>>> + *    YP(+)--+-------------+
>>>> + *        |             |--+
>>>> + *        |             |  |
>>>> + * YM(meas)--+-------------+  |
>>>> + *          +--------------+
>>>> + *          |              |
>>>> + *       XP(meas)        XM(-)
>>>> + *
>>>> + * (+) means here 1.85 V
>>>> + * (-) means here GND
>>>> + */
>>>> +static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
>>>> +
>>>> +     ts->cur_plate = LRADC_SAMPLE_PRESSURE;
>>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
>>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
>>>> +     mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2,
>>>> +                                 TOUCHSCREEN_VCHANNEL1);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     mxs_lradc_setup_touch_detection(ts);
>>>> +
>>>> +     ts->cur_plate = LRADC_TOUCH;
>>>> +     mxs_lradc_reg_clear(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
>>>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
>>>> +     mxs_lradc_reg_set(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>>>> +                       LRADC_CTRL1);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     mxs_lradc_reg_clear(ts->lradc,
>>>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>>>> +                         LRADC_CTRL1);
>>>> +     mxs_lradc_reg_set(ts->lradc,
>>>> +                       LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1),
>>>> +                       LRADC_CTRL1);
>>>> +     /*
>>>> +      * start with the Y-pos, because it uses nearly the same plate
>>>> +      * settings like the touch detection
>>>> +      */
>>>> +     mxs_lradc_prepare_y_pos(ts);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos);
>>>> +     input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos);
>>>> +     input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure);
>>>> +     input_report_key(ts->ts_input, BTN_TOUCH, 1);
>>>> +     input_sync(ts->ts_input);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     mxs_lradc_setup_touch_detection(ts);
>>>> +     ts->cur_plate = LRADC_SAMPLE_VALID;
>>>> +     /*
>>>> +      * start a dummy conversion to burn time to settle the signals
>>>> +      * note: we are not interested in the conversion's value
>>>> +      */
>>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
>>>> +     mxs_lradc_reg_clear(lradc,
>>>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>>>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2),
>>>> +                         LRADC_CTRL1);
>>>> +     mxs_lradc_reg_wrt(
>>>> +                 lradc,
>>>> +                 LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
>>>> +                 LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
>>>> +                 LRADC_DELAY(2));
>>>> +}
>>>> +
>>>> +/*
>>>> + * in order to avoid false measurements, report only samples where
>>>> + * the surface is still touched after the position measurement
>>>> + */
>>>> +static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     /* if it is still touched, report the sample */
>>>> +     if (valid && mxs_lradc_check_touch_event(lradc)) {
>>>> +             ts->ts_valid = true;
>>>> +             mxs_lradc_report_ts_event(ts);
>>>> +     }
>>>> +
>>>> +     /* if it is even still touched, continue with the next measurement */
>>>> +     if (mxs_lradc_check_touch_event(lradc)) {
>>>> +             mxs_lradc_prepare_y_pos(ts);
>>>> +             return;
>>>> +     }
>>>> +
>>>> +     if (ts->ts_valid) {
>>>> +             /* signal the release */
>>>> +             ts->ts_valid = false;
>>>> +             input_report_key(ts->ts_input, BTN_TOUCH, 0);
>>>> +             input_sync(ts->ts_input);
>>>> +     }
>>>> +
>>>> +     /* if it is released, wait for the next touch via IRQ */
>>>> +     ts->cur_plate = LRADC_TOUCH;
>>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
>>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
>>>> +     mxs_lradc_reg_clear(lradc,
>>>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ |
>>>> +                         LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
>>>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1),
>>>> +                         LRADC_CTRL1);
>>>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
>>>> +}
>>>> +
>>>> +/* touchscreen's state machine */
>>>> +static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     switch (ts->cur_plate) {
>>>> +     case LRADC_TOUCH:
>>>> +             if (mxs_lradc_check_touch_event(lradc))
>>>> +                     mxs_lradc_start_touch_event(ts);
>>>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
>>>> +                                 LRADC_CTRL1);
>>>> +             return;
>>>> +
>>>> +     case LRADC_SAMPLE_Y:
>>>> +             ts->ts_y_pos =
>>>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
>>>> +             mxs_lradc_prepare_x_pos(ts);
>>>> +             return;
>>>> +
>>>> +     case LRADC_SAMPLE_X:
>>>> +             ts->ts_x_pos =
>>>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
>>>> +             mxs_lradc_prepare_pressure(ts);
>>>> +             return;
>>>> +
>>>> +     case LRADC_SAMPLE_PRESSURE:
>>>> +             ts->ts_pressure =
>>>> +                 mxs_lradc_read_ts_pressure(ts,
>>>> +                                            TOUCHSCREEN_VCHANNEL2,
>>>> +                                            TOUCHSCREEN_VCHANNEL1);
>>>> +             mxs_lradc_complete_touch_event(ts);
>>>> +             return;
>>>> +
>>>> +     case LRADC_SAMPLE_VALID:
>>>> +             mxs_lradc_finish_touch_event(ts, 1);
>>>> +             break;
>>>> +     }
>>>> +}
>>>> +
>>>> +/*
>>>> + * IRQ Handling
>>>> + */
>>>> +static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data)
>>>> +{
>>>> +     struct mxs_lradc_ts *ts = data;
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +     unsigned long reg = readl(lradc->base + LRADC_CTRL1);
>>>> +     u32 clr_irq = mxs_lradc_irq_mask(lradc);
>>>> +     const u32 ts_irq_mask =
>>>> +             LRADC_CTRL1_TOUCH_DETECT_IRQ |
>>>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>>>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
>>>> +
>>>> +     if (!(reg & mxs_lradc_irq_mask(lradc)))
>>>> +             return IRQ_NONE;
>>>> +
>>>> +     if (lradc->use_touchscreen && (reg & ts_irq_mask)) {
>>>> +             mxs_lradc_handle_touch(ts);
>>>> +
>>>> +             /* Make sure we don't clear the next conversion's interrupt. */
>>>> +             clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>>>> +                             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
>>>> +             mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
>>>> +     }
>>>> +
>>>> +     return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +static int mxs_lradc_ts_open(struct input_dev *dev)
>>>> +{
>>>> +     struct mxs_lradc_ts *ts = input_get_drvdata(dev);
>>>> +
>>>> +     /* Enable the touch-detect circuitry. */
>>>> +     mxs_lradc_enable_touch_detection(ts);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static void mxs_lradc_disable_ts(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     /* stop all interrupts from firing */
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
>>>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
>>>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
>>>> +
>>>> +     /* Power-down touchscreen touch-detect circuitry. */
>>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_ts_close(struct input_dev *dev)
>>>> +{
>>>> +     struct mxs_lradc_ts *ts = input_get_drvdata(dev);
>>>> +
>>>> +     mxs_lradc_disable_ts(ts);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     /* Configure the touchscreen type */
>>>> +     if (lradc->soc == IMX28_LRADC) {
>>>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
>>>> +                                 LRADC_CTRL0);
>>>> +
>>>> +             if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
>>>> +                     mxs_lradc_reg_set(lradc,
>>>> +                                       LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
>>>> +                                       LRADC_CTRL0);
>>>> +     }
>>>> +}
>>>> +
>>>> +static void mxs_lradc_ts_hw_stop(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     int i;
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc,
>>>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
>>>> +                     LRADC_CTRL1);
>>>> +
>>>> +     for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++)
>>>> +             mxs_lradc_reg_wrt(ts->lradc, 0, LRADC_DELAY(i));
>>>> +}
>>>> +
>>>> +static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct input_dev *input = ts->ts_input;
>>>> +     struct device *dev = ts->dev;
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     if (!lradc->use_touchscreen)
>>>> +             return 0;
>>>> +
>>>> +     input->name = DRIVER_NAME_TS;
>>>> +     input->id.bustype = BUS_HOST;
>>>> +     input->dev.parent = dev;
>>>> +     input->open = mxs_lradc_ts_open;
>>>> +     input->close = mxs_lradc_ts_close;
>>>> +
>>>> +     __set_bit(EV_ABS, input->evbit);
>>>> +     __set_bit(EV_KEY, input->evbit);
>>>> +     __set_bit(BTN_TOUCH, input->keybit);
>>>> +     __set_bit(INPUT_PROP_DIRECT, input->propbit);
>>>> +     input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
>>>> +     input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
>>>> +     input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
>>>> +                          0, 0);
>>>> +
>>>> +     ts->ts_input = input;
>>>> +     input_set_drvdata(input, ts);
>>>> +
>>>> +     return input_register_device(input);
>>>> +}
>>>> +
>>>> +static int mxs_lradc_ts_probe(struct platform_device *pdev)
>>>> +{
>>>> +     struct device *dev = &pdev->dev;
>>>> +     struct device_node *node = dev->parent->of_node;
>>>> +     struct mxs_lradc *lradc = dev_get_platdata(dev);
>>>> +     struct mxs_lradc_ts *ts;
>>>> +     struct input_dev *input;
>>>> +     int touch_ret, ret, i;
>>>> +     u32 ts_wires = 0, adapt;
>>>> +
>>>> +     ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
>>>> +     input = devm_input_allocate_device(dev);
>>>> +     if (!ts || !input)
>>>> +             return -ENOMEM;
>>>> +
>>>> +     ts->lradc = lradc;
>>>> +     ts->dev = dev;
>>>> +     ts->ts_input = input;
>>>> +     platform_set_drvdata(pdev, ts);
>>>> +     input_set_drvdata(input, ts);
>>>> +
>>>> +     touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
>>>> +                                      &ts_wires);
>>>> +
>>>> +     if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) {
>>>> +             ts->over_sample_cnt = 4;
>>>> +     } else {
>>>> +             if (adapt < 1 || adapt > 32) {
>>>> +                     dev_err(ts->dev, "Invalid sample count (%u)\n",
>>>> +                             adapt);
>>>> +                     touch_ret = -EINVAL;
>>>> +             } else {
>>>> +                     ts->over_sample_cnt = adapt;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) {
>>>> +             ts->over_sample_delay = 2;
>>>> +     } else {
>>>> +             if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) {
>>>> +                     dev_err(ts->dev, "Invalid sample delay (%u)\n",
>>>> +                             adapt);
>>>> +                     touch_ret = -EINVAL;
>>>> +             } else {
>>>> +                     ts->over_sample_delay = adapt;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     if (of_property_read_u32(node, "fsl,settling", &adapt)) {
>>>> +             ts->settling_delay = 10;
>>>> +     } else {
>>>> +             if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) {
>>>> +                     dev_err(ts->dev, "Invalid settling delay (%u)\n",
>>>> +                             adapt);
>>>> +                     touch_ret = -EINVAL;
>>>> +             } else {
>>>> +                     ts->settling_delay = adapt;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     mxs_lradc_ts_hw_init(ts);
>>>> +     for (i = 0; i < lradc->irq_count; i++) {
>>>> +             ret = devm_request_irq(dev, lradc->irq[i],
>>>> +                                    mxs_lradc_ts_handle_irq,
>>>> +                                    IRQF_SHARED, lradc->irq_name[i], ts);
>>> As with the adc driver, are we actually using all of these?  I'd prefer we
>>> only grab the ones that are actually relevant.
>>
>> Only irq lines relevant for touchscreen are:
>> mxs-lradc-touchscreen, mxs-lradc-channel6 and mxs-lradc-channel7
>> But not all interrupts are beiing used even when I enabled all remaining
>> channels (not used by touchscreen) for bufferd capture via
>> echo 1 >/sys/bus/iio/devices/iio\:device0/scan_elements/in_voltagexx_en
>>
>> So I don't know if it's supposed to work like this...
>> (It works the same on the original code)
> Certainly should only grab the relevant ones to touch screen use in here..
> Original code probably being overly enthusiastic and we never noticed ;)

Sorry for being unclear...
The point I was trying to make was that even though touchscreen and
adc don't share irq lines, irqs mxs-lradc-channel(2,3,4,5,6) are never
being used.
So touchscreen is only use 2 out of 3 irqs assigned to it:
mxs-lradc-touchscreen and mxs-lradc-channel7  . And adc
is only using 2 irq lines mxs-lradc-channel(0,1) during buffered
capture, even when all virtal channels are enabled.
I found that odd...

>> root@cfa100xx:~# cat /proc/interrupts
>>            CPU0
>>  16:      13108         -  48 Edge      MXS Timer Tick
>>  17:       4240         -  82 Edge      mxs-dma
>>  25:          6         -  96 Edge      80010000.ssp
>> 196:          0         -  68 Edge      mxs-dma
>> 210:         13         -  10 Edge      mxs-lradc-touchscreen
>> 211:          0         -  14 Edge      mxs-lradc-thresh0
>> 212:          0         -  15 Edge      mxs-lradc-thresh1
>> 213:         10         -  16 Edge      mxs-lradc-channel0
>> 214:         10         -  17 Edge      mxs-lradc-channel1
>> 215:          0         -  18 Edge      mxs-lradc-channel2
>> 216:          0         -  19 Edge      mxs-lradc-channel3
>> 217:          0         -  20 Edge      mxs-lradc-channel4
>> 218:          0         -  21 Edge      mxs-lradc-channel5
>> 219:          0         -  22 Edge      mxs-lradc-channel6
>> 220:        412         -  23 Edge      mxs-lradc-channel7
>> 221:          0         -  24 Edge      mxs-lradc-button0
>> 222:          0         -  25 Edge      mxs-lradc-button1
>> 223:          0         -  29 Edge      RTC alarm
>> 224:          0         - 111 Edge      80058000.i2c
>> 228:        174         -  47 Edge      uart-pl011
>> 229:        439         -  93 Edge      80080000.usb
>> 230:          0         -  92 Edge      80090000.usb
>> 231:       3610         - 101 Edge      800f0000.ethernet
>> 232:         10  80050000.lradc-dev0 Edge
>> Err:          0
>>
>>>> +             if (ret)
>>>> +                     return ret;
>>>> +     }
>>>> +
>>>> +     if (!touch_ret) {
>>>> +             ret = mxs_lradc_ts_register(ts);
>>>> +             if (!ret)
>>>> +                     goto err_ts_register;
>>>> +     }
>>>> +
>>>> +     return 0;
>>>> +
>>>> +err_ts_register:
>>>> +     mxs_lradc_ts_hw_stop(ts);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int mxs_lradc_ts_remove(struct platform_device *pdev)
>>>> +{
>>>> +     struct mxs_lradc_ts *ts = platform_get_drvdata(pdev);
>>>> +
>>>> +     mxs_lradc_ts_hw_stop(ts);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static struct platform_driver mxs_lradc_ts_driver = {
>>>> +     .driver = {
>>>> +             .name = DRIVER_NAME_TS,
>>>> +     },
>>>> +     .probe  = mxs_lradc_ts_probe,
>>>> +     .remove = mxs_lradc_ts_remove,
>>>> +};
>>>> +module_platform_driver(mxs_lradc_ts_driver);
>>>> +
>>>> +MODULE_LICENSE("GPL v2");
>>>>
>>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
@ 2016-05-29 18:00           ` Ksenija Stanojević
  0 siblings, 0 replies; 40+ messages in thread
From: Ksenija Stanojević @ 2016-05-29 18:00 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Lee Jones, Dmitry Torokhov,
	linux-input-u79uwXL29TY76Z2rM5mHXA, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Marek Vašut,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Harald Geyer

On Sun, May 29, 2016 at 6:49 PM, Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On 28/05/16 18:45, Ksenija Stanojević wrote:
>> On Sun, May 1, 2016 at 6:47 PM, Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
>>> On 29/04/16 12:49, Ksenija Stanojevic wrote:
>>>> Add mxs-lradc touchscreen driver.
>>>>
>>>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>>> The only real thing this raises for me is why we are grabbing IRQs that I
>>> don't think this driver even cares about...
>>>
>>> Jonathan
>>>> ---
>>>>  drivers/input/touchscreen/Kconfig        |  14 +-
>>>>  drivers/input/touchscreen/Makefile       |   1 +
>>>>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++
>>>>  3 files changed, 742 insertions(+), 2 deletions(-)
>>>>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
>>>>
>>>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>>>> index 8ecdc38..d614d248 100644
>>>> --- a/drivers/input/touchscreen/Kconfig
>>>> +++ b/drivers/input/touchscreen/Kconfig
>>>> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
>>>>       depends on SH_HP6XX && SH_ADC
>>>>       help
>>>>         Say Y here if you have a HP Jornada 620/660/680/690 and want to
>>>> -          support the built-in touchscreen.
>>>> +       support the built-in touchscreen.
>>>>
>>>>         To compile this driver as a module, choose M here: the
>>>>         module will be called hp680_ts_input.
>>>> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
>>>>         This enables support for the Philips UCB1400 touchscreen interface.
>>>>         The UCB1400 is an AC97 audio codec.  The touchscreen interface
>>>>         will be initialized only after the ALSA subsystem has been
>>>> -       brought up and the UCB1400 detected.  You therefore have to
>>>> +       brought up and the UCB1400 detected.  You therefore have to
>>>>         configure ALSA support as well (either built-in or modular,
>>>>         independently of whether this driver is itself built-in or
>>>>         modular) for this driver to work.
>>>> @@ -842,6 +842,16 @@ config TOUCHSCREEN_MX25
>>>>         To compile this driver as a module, choose M here: the
>>>>         module will be called fsl-imx25-tcq.
>>>>
>>>> +config TOUCHSCREEN_MXS_LRADC
>>>> +     tristate "Freescale i.MX23/i.MX28 LRADC touchscreen"
>>>> +     depends on MFD_MXS_LRADC
>>>> +     help
>>>> +       Say Y here if you have a touchscreen connected to the low-resolution
>>>> +       analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor.
>>>> +
>>>> +       To compile this driver as a module, choose M here: the module will be
>>>> +       called mxs-lradc-ts.
>>>> +
>>>>  config TOUCHSCREEN_MC13783
>>>>       tristate "Freescale MC13783 touchscreen input driver"
>>>>       depends on MFD_MC13XXX
>>>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
>>>> index f42975e..513a6ff 100644
>>>> --- a/drivers/input/touchscreen/Makefile
>>>> +++ b/drivers/input/touchscreen/Makefile
>>>> @@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR)             += migor_ts.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_MMS114)     += mms114.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_MTOUCH)     += mtouch.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_MK712)              += mk712.o
>>>> +obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC)  += mxs-lradc-ts.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_HP600)              += hp680_ts_input.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_HP7XX)              += jornada720_ts.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o
>>>> diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c
>>>> new file mode 100644
>>>> index 0000000..27abb8e
>>>> --- /dev/null
>>>> +++ b/drivers/input/touchscreen/mxs-lradc-ts.c
>>>> @@ -0,0 +1,729 @@
>>>> +/*
>>>> + * Freescale MXS LRADC driver
>>>> + *
>>>> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
>>>> + * Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify
>>>> + * it under the terms of the GNU General Public License as published by
>>>> + * the Free Software Foundation; either version 2 of the License, or
>>>> + * (at your option) any later version.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful,
>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>>> + * GNU General Public License for more details.
>>>> + */
>>>> +
>>>> +#include <linux/module.h>
>>>> +#include <linux/mfd/mxs-lradc.h>
>>>> +#include <linux/platform_device.h>
>>>> +
>>>> +/*
>>>> + * Touchscreen handling
>>>> + */
>>>> +enum mxs_lradc_ts_plate {
>>>> +     LRADC_TOUCH = 0,
>>>> +     LRADC_SAMPLE_X,
>>>> +     LRADC_SAMPLE_Y,
>>>> +     LRADC_SAMPLE_PRESSURE,
>>>> +     LRADC_SAMPLE_VALID,
>>>> +};
>>>> +
>>>> +struct mxs_lradc_ts {
>>>> +     struct mxs_lradc        *lradc;
>>>> +     struct device           *dev;
>>>> +     /*
>>>> +      * When the touchscreen is enabled, we give it two private virtual
>>>> +      * channels: #6 and #7. This means that only 6 virtual channels (instead
>>>> +      * of 8) will be available for buffered capture.
>>>> +      */
>>>> +#define TOUCHSCREEN_VCHANNEL1                7
>>>> +#define TOUCHSCREEN_VCHANNEL2                6
>>>> +
>>>> +     struct input_dev        *ts_input;
>>>> +
>>>> +     enum mxs_lradc_ts_plate cur_plate; /* state machine */
>>>> +     bool                    ts_valid;
>>>> +     unsigned                ts_x_pos;
>>>> +     unsigned                ts_y_pos;
>>>> +     unsigned                ts_pressure;
>>>> +
>>>> +     /* handle touchscreen's physical behaviour */
>>>> +     /* samples per coordinate */
>>>> +     unsigned                over_sample_cnt;
>>>> +     /* time clocks between samples */
>>>> +     unsigned                over_sample_delay;
>>>> +     /* time in clocks to wait after the plates where switched */
>>>> +     unsigned                settling_delay;
>>>> +};
>>>> +
>>>> +static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
>>>> +{
>>>> +     if (lradc->soc == IMX23_LRADC)
>>>> +             return LRADC_CTRL0_MX23_PLATE_MASK;
>>>> +     return LRADC_CTRL0_MX28_PLATE_MASK;
>>>> +}
>>>> +
>>>> +static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
>>>> +{
>>>> +     if (lradc->soc == IMX23_LRADC)
>>>> +             return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
>>>> +     return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE;
>>>> +}
>>>> +
>>>> +static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc)
>>>> +{
>>>> +     if (lradc->soc == IMX23_LRADC)
>>>> +             return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM;
>>>> +     return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW;
>>>> +}
>>>> +
>>>> +static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc)
>>>> +{
>>>> +     if (lradc->soc == IMX23_LRADC)
>>>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM;
>>>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW;
>>>> +}
>>>> +
>>>> +static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc)
>>>> +{
>>>> +     if (lradc->soc == IMX23_LRADC)
>>>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM;
>>>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW;
>>>> +}
>>>> +
>>>> +static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
>>>> +{
>>>> +     return !!(readl(lradc->base + LRADC_STATUS) &
>>>> +                                     LRADC_STATUS_TOUCH_DETECT_RAW);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_map_ts_channel(struct mxs_lradc *lradc, unsigned vch,
>>>> +                                  unsigned ch)
>>>> +{
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
>>>> +                         LRADC_CTRL4);
>>>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned ch)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     /*
>>>> +      * prepare for oversampling conversion
>>>> +      *
>>>> +      * from the datasheet:
>>>> +      * "The ACCUMULATE bit in the appropriate channel register
>>>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
>>>> +      * otherwise, the IRQs will not fire."
>>>> +      */
>>>> +     mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE |
>>>> +                       LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1),
>>>> +                       LRADC_CH(ch));
>>>> +
>>>> +     /* from the datasheet:
>>>> +      * "Software must clear this register in preparation for a
>>>> +      * multi-cycle accumulation.
>>>> +      */
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch));
>>>> +
>>>> +     /*
>>>> +      * prepare the delay/loop unit according to the oversampling count
>>>> +      *
>>>> +      * from the datasheet:
>>>> +      * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
>>>> +      * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
>>>> +      * the LRADC will not trigger the delay group."
>>>> +      */
>>>> +     mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
>>>> +                       LRADC_DELAY_TRIGGER_DELAYS(0) |
>>>> +                       LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>>>> +                       LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>>>> +                       LRADC_DELAY(3));
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1);
>>>> +
>>>> +     /*
>>>> +      * after changing the touchscreen plates setting
>>>> +      * the signals need some initial time to settle. Start the
>>>> +      * SoC's delay unit and start the conversion later
>>>> +      * and automatically.
>>>> +      */
>>>> +     mxs_lradc_reg_wrt(
>>>> +             lradc,
>>>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>>>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
>>>> +             LRADC_DELAY_KICK |
>>>> +             LRADC_DELAY_DELAY(ts->settling_delay),
>>>> +             LRADC_DELAY(2));
>>>> +}
>>>> +
>>>> +/*
>>>> + * Pressure detection is special:
>>>> + * We want to do both required measurements for the pressure detection in
>>>> + * one turn. Use the hardware features to chain both conversions and let the
>>>> + * hardware report one interrupt if both conversions are done
>>>> + */
>>>> +static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts, unsigned ch1,
>>>> +                                     unsigned ch2)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +     u32 reg;
>>>> +
>>>> +     /*
>>>> +      * prepare for oversampling conversion
>>>> +      *
>>>> +      * from the datasheet:
>>>> +      * "The ACCUMULATE bit in the appropriate channel register
>>>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
>>>> +      * otherwise, the IRQs will not fire."
>>>> +      */
>>>> +     reg = LRADC_CH_ACCUMULATE |
>>>> +             LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1);
>>>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
>>>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
>>>> +
>>>> +     /* from the datasheet:
>>>> +      * "Software must clear this register in preparation for a
>>>> +      * multi-cycle accumulation.
>>>> +      */
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1));
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2));
>>>> +
>>>> +     /* prepare the delay/loop unit according to the oversampling count */
>>>> +     mxs_lradc_reg_wrt(
>>>> +                 lradc,
>>>> +                 LRADC_DELAY_TRIGGER(1 << ch1) |
>>>> +                 LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */
>>>> +                 LRADC_DELAY_TRIGGER_DELAYS(0) |
>>>> +                 LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>>>> +                 LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>>>> +                 LRADC_DELAY(3));
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1);
>>>> +
>>>> +     /*
>>>> +      * after changing the touchscreen plates setting
>>>> +      * the signals need some initial time to settle. Start the
>>>> +      * SoC's delay unit and start the conversion later
>>>> +      * and automatically.
>>>> +      */
>>>> +     mxs_lradc_reg_wrt(
>>>> +             lradc,
>>>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>>>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
>>>> +             LRADC_DELAY_KICK |
>>>> +             LRADC_DELAY_DELAY(ts->settling_delay), LRADC_DELAY(2));
>>>> +}
>>>> +
>>>> +static unsigned mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts,
>>>> +                                           unsigned channel)
>>>> +{
>>>> +     u32 reg;
>>>> +     unsigned num_samples, val;
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     reg = readl(lradc->base + LRADC_CH(channel));
>>>> +     if (reg & LRADC_CH_ACCUMULATE)
>>>> +             num_samples = ts->over_sample_cnt;
>>>> +     else
>>>> +             num_samples = 1;
>>>> +
>>>> +     val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
>>>> +     return val / num_samples;
>>>> +}
>>>> +
>>>> +static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts,
>>>> +                                        unsigned ch1, unsigned ch2)
>>>> +{
>>>> +     u32 reg, mask;
>>>> +     unsigned pressure, m1, m2;
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
>>>> +     reg = readl(lradc->base + LRADC_CTRL1) & mask;
>>>> +
>>>> +     while (reg != mask) {
>>>> +             reg = readl(lradc->base + LRADC_CTRL1) & mask;
>>>> +             dev_dbg(ts->dev, "One channel is still busy: %X\n", reg);
>>>> +     }
>>>> +
>>>> +     m1 = mxs_lradc_ts_read_raw_channel(ts, ch1);
>>>> +     m2 = mxs_lradc_ts_read_raw_channel(ts, ch2);
>>>> +
>>>> +     if (m2 == 0) {
>>>> +             dev_warn(ts->dev, "Cannot calculate pressure\n");
>>>> +             return 1 << (LRADC_RESOLUTION - 1);
>>>> +     }
>>>> +
>>>> +     /* simply scale the value from 0 ... max ADC resolution */
>>>> +     pressure = m1;
>>>> +     pressure *= (1 << LRADC_RESOLUTION);
>>>> +     pressure /= m2;
>>>> +
>>>> +     dev_dbg(ts->dev, "Pressure = %u\n", pressure);
>>>> +     return pressure;
>>>> +}
>>>> +
>>>> +#define TS_CH_XP 2
>>>> +#define TS_CH_YP 3
>>>> +#define TS_CH_XM 4
>>>> +#define TS_CH_YM 5
>>>> +
>>>> +/*
>>>> + * YP(open)--+-------------+
>>>> + *        |             |--+
>>>> + *        |             |  |
>>>> + *    YM(-)--+-------------+  |
>>>> + *          +--------------+
>>>> + *          |              |
>>>> + *      XP(weak+)        XM(open)
>>>> + *
>>>> + * "weak+" means 200k Ohm VDDIO
>>>> + * (-) means GND
>>>> + */
>>>> +static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     /*
>>>> +      * In order to detect a touch event the 'touch detect enable' bit
>>>> +      * enables:
>>>> +      *  - a weak pullup to the X+ connector
>>>> +      *  - a strong ground at the Y- connector
>>>> +      */
>>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc),
>>>> +                       LRADC_CTRL0);
>>>> +}
>>>> +
>>>> +/*
>>>> + * YP(meas)--+-------------+
>>>> + *        |             |--+
>>>> + *        |             |  |
>>>> + * YM(open)--+-------------+  |
>>>> + *          +--------------+
>>>> + *          |              |
>>>> + *        XP(+)          XM(-)
>>>> + *
>>>> + * (+) means here 1.85 V
>>>> + * (-) means here GND
>>>> + */
>>>> +static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
>>>> +
>>>> +     ts->cur_plate = LRADC_SAMPLE_X;
>>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
>>>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>>>> +}
>>>> +
>>>> +/*
>>>> + *   YP(+)--+-------------+
>>>> + *       |             |--+
>>>> + *       |             |  |
>>>> + *   YM(-)--+-------------+  |
>>>> + *         +--------------+
>>>> + *         |              |
>>>> + *      XP(open)        XM(meas)
>>>> + *
>>>> + * (+) means here 1.85 V
>>>> + * (-) means here GND
>>>> + */
>>>> +static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
>>>> +
>>>> +     ts->cur_plate = LRADC_SAMPLE_Y;
>>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
>>>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>>>> +}
>>>> +
>>>> +/*
>>>> + *    YP(+)--+-------------+
>>>> + *        |             |--+
>>>> + *        |             |  |
>>>> + * YM(meas)--+-------------+  |
>>>> + *          +--------------+
>>>> + *          |              |
>>>> + *       XP(meas)        XM(-)
>>>> + *
>>>> + * (+) means here 1.85 V
>>>> + * (-) means here GND
>>>> + */
>>>> +static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
>>>> +
>>>> +     ts->cur_plate = LRADC_SAMPLE_PRESSURE;
>>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
>>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
>>>> +     mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2,
>>>> +                                 TOUCHSCREEN_VCHANNEL1);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     mxs_lradc_setup_touch_detection(ts);
>>>> +
>>>> +     ts->cur_plate = LRADC_TOUCH;
>>>> +     mxs_lradc_reg_clear(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
>>>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
>>>> +     mxs_lradc_reg_set(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>>>> +                       LRADC_CTRL1);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     mxs_lradc_reg_clear(ts->lradc,
>>>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>>>> +                         LRADC_CTRL1);
>>>> +     mxs_lradc_reg_set(ts->lradc,
>>>> +                       LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1),
>>>> +                       LRADC_CTRL1);
>>>> +     /*
>>>> +      * start with the Y-pos, because it uses nearly the same plate
>>>> +      * settings like the touch detection
>>>> +      */
>>>> +     mxs_lradc_prepare_y_pos(ts);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos);
>>>> +     input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos);
>>>> +     input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure);
>>>> +     input_report_key(ts->ts_input, BTN_TOUCH, 1);
>>>> +     input_sync(ts->ts_input);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     mxs_lradc_setup_touch_detection(ts);
>>>> +     ts->cur_plate = LRADC_SAMPLE_VALID;
>>>> +     /*
>>>> +      * start a dummy conversion to burn time to settle the signals
>>>> +      * note: we are not interested in the conversion's value
>>>> +      */
>>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
>>>> +     mxs_lradc_reg_clear(lradc,
>>>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>>>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2),
>>>> +                         LRADC_CTRL1);
>>>> +     mxs_lradc_reg_wrt(
>>>> +                 lradc,
>>>> +                 LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
>>>> +                 LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
>>>> +                 LRADC_DELAY(2));
>>>> +}
>>>> +
>>>> +/*
>>>> + * in order to avoid false measurements, report only samples where
>>>> + * the surface is still touched after the position measurement
>>>> + */
>>>> +static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     /* if it is still touched, report the sample */
>>>> +     if (valid && mxs_lradc_check_touch_event(lradc)) {
>>>> +             ts->ts_valid = true;
>>>> +             mxs_lradc_report_ts_event(ts);
>>>> +     }
>>>> +
>>>> +     /* if it is even still touched, continue with the next measurement */
>>>> +     if (mxs_lradc_check_touch_event(lradc)) {
>>>> +             mxs_lradc_prepare_y_pos(ts);
>>>> +             return;
>>>> +     }
>>>> +
>>>> +     if (ts->ts_valid) {
>>>> +             /* signal the release */
>>>> +             ts->ts_valid = false;
>>>> +             input_report_key(ts->ts_input, BTN_TOUCH, 0);
>>>> +             input_sync(ts->ts_input);
>>>> +     }
>>>> +
>>>> +     /* if it is released, wait for the next touch via IRQ */
>>>> +     ts->cur_plate = LRADC_TOUCH;
>>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
>>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
>>>> +     mxs_lradc_reg_clear(lradc,
>>>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ |
>>>> +                         LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
>>>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1),
>>>> +                         LRADC_CTRL1);
>>>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
>>>> +}
>>>> +
>>>> +/* touchscreen's state machine */
>>>> +static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     switch (ts->cur_plate) {
>>>> +     case LRADC_TOUCH:
>>>> +             if (mxs_lradc_check_touch_event(lradc))
>>>> +                     mxs_lradc_start_touch_event(ts);
>>>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
>>>> +                                 LRADC_CTRL1);
>>>> +             return;
>>>> +
>>>> +     case LRADC_SAMPLE_Y:
>>>> +             ts->ts_y_pos =
>>>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
>>>> +             mxs_lradc_prepare_x_pos(ts);
>>>> +             return;
>>>> +
>>>> +     case LRADC_SAMPLE_X:
>>>> +             ts->ts_x_pos =
>>>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
>>>> +             mxs_lradc_prepare_pressure(ts);
>>>> +             return;
>>>> +
>>>> +     case LRADC_SAMPLE_PRESSURE:
>>>> +             ts->ts_pressure =
>>>> +                 mxs_lradc_read_ts_pressure(ts,
>>>> +                                            TOUCHSCREEN_VCHANNEL2,
>>>> +                                            TOUCHSCREEN_VCHANNEL1);
>>>> +             mxs_lradc_complete_touch_event(ts);
>>>> +             return;
>>>> +
>>>> +     case LRADC_SAMPLE_VALID:
>>>> +             mxs_lradc_finish_touch_event(ts, 1);
>>>> +             break;
>>>> +     }
>>>> +}
>>>> +
>>>> +/*
>>>> + * IRQ Handling
>>>> + */
>>>> +static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data)
>>>> +{
>>>> +     struct mxs_lradc_ts *ts = data;
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +     unsigned long reg = readl(lradc->base + LRADC_CTRL1);
>>>> +     u32 clr_irq = mxs_lradc_irq_mask(lradc);
>>>> +     const u32 ts_irq_mask =
>>>> +             LRADC_CTRL1_TOUCH_DETECT_IRQ |
>>>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>>>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
>>>> +
>>>> +     if (!(reg & mxs_lradc_irq_mask(lradc)))
>>>> +             return IRQ_NONE;
>>>> +
>>>> +     if (lradc->use_touchscreen && (reg & ts_irq_mask)) {
>>>> +             mxs_lradc_handle_touch(ts);
>>>> +
>>>> +             /* Make sure we don't clear the next conversion's interrupt. */
>>>> +             clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>>>> +                             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
>>>> +             mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
>>>> +     }
>>>> +
>>>> +     return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +static int mxs_lradc_ts_open(struct input_dev *dev)
>>>> +{
>>>> +     struct mxs_lradc_ts *ts = input_get_drvdata(dev);
>>>> +
>>>> +     /* Enable the touch-detect circuitry. */
>>>> +     mxs_lradc_enable_touch_detection(ts);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static void mxs_lradc_disable_ts(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     /* stop all interrupts from firing */
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
>>>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
>>>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
>>>> +
>>>> +     /* Power-down touchscreen touch-detect circuitry. */
>>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_ts_close(struct input_dev *dev)
>>>> +{
>>>> +     struct mxs_lradc_ts *ts = input_get_drvdata(dev);
>>>> +
>>>> +     mxs_lradc_disable_ts(ts);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     /* Configure the touchscreen type */
>>>> +     if (lradc->soc == IMX28_LRADC) {
>>>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
>>>> +                                 LRADC_CTRL0);
>>>> +
>>>> +             if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
>>>> +                     mxs_lradc_reg_set(lradc,
>>>> +                                       LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
>>>> +                                       LRADC_CTRL0);
>>>> +     }
>>>> +}
>>>> +
>>>> +static void mxs_lradc_ts_hw_stop(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     int i;
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc,
>>>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
>>>> +                     LRADC_CTRL1);
>>>> +
>>>> +     for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++)
>>>> +             mxs_lradc_reg_wrt(ts->lradc, 0, LRADC_DELAY(i));
>>>> +}
>>>> +
>>>> +static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct input_dev *input = ts->ts_input;
>>>> +     struct device *dev = ts->dev;
>>>> +     struct mxs_lradc *lradc = ts->lradc;
>>>> +
>>>> +     if (!lradc->use_touchscreen)
>>>> +             return 0;
>>>> +
>>>> +     input->name = DRIVER_NAME_TS;
>>>> +     input->id.bustype = BUS_HOST;
>>>> +     input->dev.parent = dev;
>>>> +     input->open = mxs_lradc_ts_open;
>>>> +     input->close = mxs_lradc_ts_close;
>>>> +
>>>> +     __set_bit(EV_ABS, input->evbit);
>>>> +     __set_bit(EV_KEY, input->evbit);
>>>> +     __set_bit(BTN_TOUCH, input->keybit);
>>>> +     __set_bit(INPUT_PROP_DIRECT, input->propbit);
>>>> +     input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
>>>> +     input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
>>>> +     input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
>>>> +                          0, 0);
>>>> +
>>>> +     ts->ts_input = input;
>>>> +     input_set_drvdata(input, ts);
>>>> +
>>>> +     return input_register_device(input);
>>>> +}
>>>> +
>>>> +static int mxs_lradc_ts_probe(struct platform_device *pdev)
>>>> +{
>>>> +     struct device *dev = &pdev->dev;
>>>> +     struct device_node *node = dev->parent->of_node;
>>>> +     struct mxs_lradc *lradc = dev_get_platdata(dev);
>>>> +     struct mxs_lradc_ts *ts;
>>>> +     struct input_dev *input;
>>>> +     int touch_ret, ret, i;
>>>> +     u32 ts_wires = 0, adapt;
>>>> +
>>>> +     ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
>>>> +     input = devm_input_allocate_device(dev);
>>>> +     if (!ts || !input)
>>>> +             return -ENOMEM;
>>>> +
>>>> +     ts->lradc = lradc;
>>>> +     ts->dev = dev;
>>>> +     ts->ts_input = input;
>>>> +     platform_set_drvdata(pdev, ts);
>>>> +     input_set_drvdata(input, ts);
>>>> +
>>>> +     touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
>>>> +                                      &ts_wires);
>>>> +
>>>> +     if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) {
>>>> +             ts->over_sample_cnt = 4;
>>>> +     } else {
>>>> +             if (adapt < 1 || adapt > 32) {
>>>> +                     dev_err(ts->dev, "Invalid sample count (%u)\n",
>>>> +                             adapt);
>>>> +                     touch_ret = -EINVAL;
>>>> +             } else {
>>>> +                     ts->over_sample_cnt = adapt;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) {
>>>> +             ts->over_sample_delay = 2;
>>>> +     } else {
>>>> +             if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) {
>>>> +                     dev_err(ts->dev, "Invalid sample delay (%u)\n",
>>>> +                             adapt);
>>>> +                     touch_ret = -EINVAL;
>>>> +             } else {
>>>> +                     ts->over_sample_delay = adapt;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     if (of_property_read_u32(node, "fsl,settling", &adapt)) {
>>>> +             ts->settling_delay = 10;
>>>> +     } else {
>>>> +             if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) {
>>>> +                     dev_err(ts->dev, "Invalid settling delay (%u)\n",
>>>> +                             adapt);
>>>> +                     touch_ret = -EINVAL;
>>>> +             } else {
>>>> +                     ts->settling_delay = adapt;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     mxs_lradc_ts_hw_init(ts);
>>>> +     for (i = 0; i < lradc->irq_count; i++) {
>>>> +             ret = devm_request_irq(dev, lradc->irq[i],
>>>> +                                    mxs_lradc_ts_handle_irq,
>>>> +                                    IRQF_SHARED, lradc->irq_name[i], ts);
>>> As with the adc driver, are we actually using all of these?  I'd prefer we
>>> only grab the ones that are actually relevant.
>>
>> Only irq lines relevant for touchscreen are:
>> mxs-lradc-touchscreen, mxs-lradc-channel6 and mxs-lradc-channel7
>> But not all interrupts are beiing used even when I enabled all remaining
>> channels (not used by touchscreen) for bufferd capture via
>> echo 1 >/sys/bus/iio/devices/iio\:device0/scan_elements/in_voltagexx_en
>>
>> So I don't know if it's supposed to work like this...
>> (It works the same on the original code)
> Certainly should only grab the relevant ones to touch screen use in here..
> Original code probably being overly enthusiastic and we never noticed ;)

Sorry for being unclear...
The point I was trying to make was that even though touchscreen and
adc don't share irq lines, irqs mxs-lradc-channel(2,3,4,5,6) are never
being used.
So touchscreen is only use 2 out of 3 irqs assigned to it:
mxs-lradc-touchscreen and mxs-lradc-channel7  . And adc
is only using 2 irq lines mxs-lradc-channel(0,1) during buffered
capture, even when all virtal channels are enabled.
I found that odd...

>> root@cfa100xx:~# cat /proc/interrupts
>>            CPU0
>>  16:      13108         -  48 Edge      MXS Timer Tick
>>  17:       4240         -  82 Edge      mxs-dma
>>  25:          6         -  96 Edge      80010000.ssp
>> 196:          0         -  68 Edge      mxs-dma
>> 210:         13         -  10 Edge      mxs-lradc-touchscreen
>> 211:          0         -  14 Edge      mxs-lradc-thresh0
>> 212:          0         -  15 Edge      mxs-lradc-thresh1
>> 213:         10         -  16 Edge      mxs-lradc-channel0
>> 214:         10         -  17 Edge      mxs-lradc-channel1
>> 215:          0         -  18 Edge      mxs-lradc-channel2
>> 216:          0         -  19 Edge      mxs-lradc-channel3
>> 217:          0         -  20 Edge      mxs-lradc-channel4
>> 218:          0         -  21 Edge      mxs-lradc-channel5
>> 219:          0         -  22 Edge      mxs-lradc-channel6
>> 220:        412         -  23 Edge      mxs-lradc-channel7
>> 221:          0         -  24 Edge      mxs-lradc-button0
>> 222:          0         -  25 Edge      mxs-lradc-button1
>> 223:          0         -  29 Edge      RTC alarm
>> 224:          0         - 111 Edge      80058000.i2c
>> 228:        174         -  47 Edge      uart-pl011
>> 229:        439         -  93 Edge      80080000.usb
>> 230:          0         -  92 Edge      80090000.usb
>> 231:       3610         - 101 Edge      800f0000.ethernet
>> 232:         10  80050000.lradc-dev0 Edge
>> Err:          0
>>
>>>> +             if (ret)
>>>> +                     return ret;
>>>> +     }
>>>> +
>>>> +     if (!touch_ret) {
>>>> +             ret = mxs_lradc_ts_register(ts);
>>>> +             if (!ret)
>>>> +                     goto err_ts_register;
>>>> +     }
>>>> +
>>>> +     return 0;
>>>> +
>>>> +err_ts_register:
>>>> +     mxs_lradc_ts_hw_stop(ts);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int mxs_lradc_ts_remove(struct platform_device *pdev)
>>>> +{
>>>> +     struct mxs_lradc_ts *ts = platform_get_drvdata(pdev);
>>>> +
>>>> +     mxs_lradc_ts_hw_stop(ts);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static struct platform_driver mxs_lradc_ts_driver = {
>>>> +     .driver = {
>>>> +             .name = DRIVER_NAME_TS,
>>>> +     },
>>>> +     .probe  = mxs_lradc_ts_probe,
>>>> +     .remove = mxs_lradc_ts_remove,
>>>> +};
>>>> +module_platform_driver(mxs_lradc_ts_driver);
>>>> +
>>>> +MODULE_LICENSE("GPL v2");
>>>>
>>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
@ 2016-05-29 18:00           ` Ksenija Stanojević
  0 siblings, 0 replies; 40+ messages in thread
From: Ksenija Stanojević @ 2016-05-29 18:00 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-kernel, Lee Jones, Dmitry Torokhov, linux-input,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Marek Vašut, linux-iio, Harald Geyer

On Sun, May 29, 2016 at 6:49 PM, Jonathan Cameron <jic23@kernel.org> wrote:
> On 28/05/16 18:45, Ksenija Stanojevi=C4=87 wrote:
>> On Sun, May 1, 2016 at 6:47 PM, Jonathan Cameron <jic23@kernel.org> wrot=
e:
>>> On 29/04/16 12:49, Ksenija Stanojevic wrote:
>>>> Add mxs-lradc touchscreen driver.
>>>>
>>>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
>>> The only real thing this raises for me is why we are grabbing IRQs that=
 I
>>> don't think this driver even cares about...
>>>
>>> Jonathan
>>>> ---
>>>>  drivers/input/touchscreen/Kconfig        |  14 +-
>>>>  drivers/input/touchscreen/Makefile       |   1 +
>>>>  drivers/input/touchscreen/mxs-lradc-ts.c | 729 ++++++++++++++++++++++=
+++++++++
>>>>  3 files changed, 742 insertions(+), 2 deletions(-)
>>>>  create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c
>>>>
>>>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchsc=
reen/Kconfig
>>>> index 8ecdc38..d614d248 100644
>>>> --- a/drivers/input/touchscreen/Kconfig
>>>> +++ b/drivers/input/touchscreen/Kconfig
>>>> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600
>>>>       depends on SH_HP6XX && SH_ADC
>>>>       help
>>>>         Say Y here if you have a HP Jornada 620/660/680/690 and want t=
o
>>>> -          support the built-in touchscreen.
>>>> +       support the built-in touchscreen.
>>>>
>>>>         To compile this driver as a module, choose M here: the
>>>>         module will be called hp680_ts_input.
>>>> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400
>>>>         This enables support for the Philips UCB1400 touchscreen inter=
face.
>>>>         The UCB1400 is an AC97 audio codec.  The touchscreen interface
>>>>         will be initialized only after the ALSA subsystem has been
>>>> -       brought up and the UCB1400 detected.  You therefore have to
>>>> +       brought up and the UCB1400 detected.  You therefore have to
>>>>         configure ALSA support as well (either built-in or modular,
>>>>         independently of whether this driver is itself built-in or
>>>>         modular) for this driver to work.
>>>> @@ -842,6 +842,16 @@ config TOUCHSCREEN_MX25
>>>>         To compile this driver as a module, choose M here: the
>>>>         module will be called fsl-imx25-tcq.
>>>>
>>>> +config TOUCHSCREEN_MXS_LRADC
>>>> +     tristate "Freescale i.MX23/i.MX28 LRADC touchscreen"
>>>> +     depends on MFD_MXS_LRADC
>>>> +     help
>>>> +       Say Y here if you have a touchscreen connected to the low-reso=
lution
>>>> +       analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 pro=
cessor.
>>>> +
>>>> +       To compile this driver as a module, choose M here: the module =
will be
>>>> +       called mxs-lradc-ts.
>>>> +
>>>>  config TOUCHSCREEN_MC13783
>>>>       tristate "Freescale MC13783 touchscreen input driver"
>>>>       depends on MFD_MC13XXX
>>>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchs=
creen/Makefile
>>>> index f42975e..513a6ff 100644
>>>> --- a/drivers/input/touchscreen/Makefile
>>>> +++ b/drivers/input/touchscreen/Makefile
>>>> @@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR)             +=3D mig=
or_ts.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_MMS114)     +=3D mms114.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_MTOUCH)     +=3D mtouch.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_MK712)              +=3D mk712.o
>>>> +obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC)  +=3D mxs-lradc-ts.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_HP600)              +=3D hp680_ts_input.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_HP7XX)              +=3D jornada720_ts.o
>>>>  obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) +=3D ipaq-micro-ts.o
>>>> diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/=
touchscreen/mxs-lradc-ts.c
>>>> new file mode 100644
>>>> index 0000000..27abb8e
>>>> --- /dev/null
>>>> +++ b/drivers/input/touchscreen/mxs-lradc-ts.c
>>>> @@ -0,0 +1,729 @@
>>>> +/*
>>>> + * Freescale MXS LRADC driver
>>>> + *
>>>> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
>>>> + * Marek Vasut <marex@denx.de>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modi=
fy
>>>> + * it under the terms of the GNU General Public License as published =
by
>>>> + * the Free Software Foundation; either version 2 of the License, or
>>>> + * (at your option) any later version.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful,
>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>>> + * GNU General Public License for more details.
>>>> + */
>>>> +
>>>> +#include <linux/module.h>
>>>> +#include <linux/mfd/mxs-lradc.h>
>>>> +#include <linux/platform_device.h>
>>>> +
>>>> +/*
>>>> + * Touchscreen handling
>>>> + */
>>>> +enum mxs_lradc_ts_plate {
>>>> +     LRADC_TOUCH =3D 0,
>>>> +     LRADC_SAMPLE_X,
>>>> +     LRADC_SAMPLE_Y,
>>>> +     LRADC_SAMPLE_PRESSURE,
>>>> +     LRADC_SAMPLE_VALID,
>>>> +};
>>>> +
>>>> +struct mxs_lradc_ts {
>>>> +     struct mxs_lradc        *lradc;
>>>> +     struct device           *dev;
>>>> +     /*
>>>> +      * When the touchscreen is enabled, we give it two private virtu=
al
>>>> +      * channels: #6 and #7. This means that only 6 virtual channels =
(instead
>>>> +      * of 8) will be available for buffered capture.
>>>> +      */
>>>> +#define TOUCHSCREEN_VCHANNEL1                7
>>>> +#define TOUCHSCREEN_VCHANNEL2                6
>>>> +
>>>> +     struct input_dev        *ts_input;
>>>> +
>>>> +     enum mxs_lradc_ts_plate cur_plate; /* state machine */
>>>> +     bool                    ts_valid;
>>>> +     unsigned                ts_x_pos;
>>>> +     unsigned                ts_y_pos;
>>>> +     unsigned                ts_pressure;
>>>> +
>>>> +     /* handle touchscreen's physical behaviour */
>>>> +     /* samples per coordinate */
>>>> +     unsigned                over_sample_cnt;
>>>> +     /* time clocks between samples */
>>>> +     unsigned                over_sample_delay;
>>>> +     /* time in clocks to wait after the plates where switched */
>>>> +     unsigned                settling_delay;
>>>> +};
>>>> +
>>>> +static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
>>>> +{
>>>> +     if (lradc->soc =3D=3D IMX23_LRADC)
>>>> +             return LRADC_CTRL0_MX23_PLATE_MASK;
>>>> +     return LRADC_CTRL0_MX28_PLATE_MASK;
>>>> +}
>>>> +
>>>> +static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
>>>> +{
>>>> +     if (lradc->soc =3D=3D IMX23_LRADC)
>>>> +             return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
>>>> +     return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE;
>>>> +}
>>>> +
>>>> +static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc)
>>>> +{
>>>> +     if (lradc->soc =3D=3D IMX23_LRADC)
>>>> +             return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM;
>>>> +     return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW;
>>>> +}
>>>> +
>>>> +static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc)
>>>> +{
>>>> +     if (lradc->soc =3D=3D IMX23_LRADC)
>>>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM;
>>>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW;
>>>> +}
>>>> +
>>>> +static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc)
>>>> +{
>>>> +     if (lradc->soc =3D=3D IMX23_LRADC)
>>>> +             return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM;
>>>> +     return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW;
>>>> +}
>>>> +
>>>> +static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
>>>> +{
>>>> +     return !!(readl(lradc->base + LRADC_STATUS) &
>>>> +                                     LRADC_STATUS_TOUCH_DETECT_RAW);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_map_ts_channel(struct mxs_lradc *lradc, unsigne=
d vch,
>>>> +                                  unsigned ch)
>>>> +{
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
>>>> +                         LRADC_CTRL4);
>>>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC=
_CTRL4);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsig=
ned ch)
>>>> +{
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +
>>>> +     /*
>>>> +      * prepare for oversampling conversion
>>>> +      *
>>>> +      * from the datasheet:
>>>> +      * "The ACCUMULATE bit in the appropriate channel register
>>>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then =
0;
>>>> +      * otherwise, the IRQs will not fire."
>>>> +      */
>>>> +     mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE |
>>>> +                       LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1),
>>>> +                       LRADC_CH(ch));
>>>> +
>>>> +     /* from the datasheet:
>>>> +      * "Software must clear this register in preparation for a
>>>> +      * multi-cycle accumulation.
>>>> +      */
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch));
>>>> +
>>>> +     /*
>>>> +      * prepare the delay/loop unit according to the oversampling cou=
nt
>>>> +      *
>>>> +      * from the datasheet:
>>>> +      * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
>>>> +      * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherw=
ise,
>>>> +      * the LRADC will not trigger the delay group."
>>>> +      */
>>>> +     mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
>>>> +                       LRADC_DELAY_TRIGGER_DELAYS(0) |
>>>> +                       LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>>>> +                       LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>>>> +                       LRADC_DELAY(3));
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL=
1);
>>>> +
>>>> +     /*
>>>> +      * after changing the touchscreen plates setting
>>>> +      * the signals need some initial time to settle. Start the
>>>> +      * SoC's delay unit and start the conversion later
>>>> +      * and automatically.
>>>> +      */
>>>> +     mxs_lradc_reg_wrt(
>>>> +             lradc,
>>>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>>>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY un=
it#3 */
>>>> +             LRADC_DELAY_KICK |
>>>> +             LRADC_DELAY_DELAY(ts->settling_delay),
>>>> +             LRADC_DELAY(2));
>>>> +}
>>>> +
>>>> +/*
>>>> + * Pressure detection is special:
>>>> + * We want to do both required measurements for the pressure detectio=
n in
>>>> + * one turn. Use the hardware features to chain both conversions and =
let the
>>>> + * hardware report one interrupt if both conversions are done
>>>> + */
>>>> +static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts, unsi=
gned ch1,
>>>> +                                     unsigned ch2)
>>>> +{
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +     u32 reg;
>>>> +
>>>> +     /*
>>>> +      * prepare for oversampling conversion
>>>> +      *
>>>> +      * from the datasheet:
>>>> +      * "The ACCUMULATE bit in the appropriate channel register
>>>> +      * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then =
0;
>>>> +      * otherwise, the IRQs will not fire."
>>>> +      */
>>>> +     reg =3D LRADC_CH_ACCUMULATE |
>>>> +             LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1);
>>>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
>>>> +     mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
>>>> +
>>>> +     /* from the datasheet:
>>>> +      * "Software must clear this register in preparation for a
>>>> +      * multi-cycle accumulation.
>>>> +      */
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1));
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2));
>>>> +
>>>> +     /* prepare the delay/loop unit according to the oversampling cou=
nt */
>>>> +     mxs_lradc_reg_wrt(
>>>> +                 lradc,
>>>> +                 LRADC_DELAY_TRIGGER(1 << ch1) |
>>>> +                 LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channe=
ls */
>>>> +                 LRADC_DELAY_TRIGGER_DELAYS(0) |
>>>> +                 LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
>>>> +                 LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
>>>> +                 LRADC_DELAY(3));
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTR=
L1);
>>>> +
>>>> +     /*
>>>> +      * after changing the touchscreen plates setting
>>>> +      * the signals need some initial time to settle. Start the
>>>> +      * SoC's delay unit and start the conversion later
>>>> +      * and automatically.
>>>> +      */
>>>> +     mxs_lradc_reg_wrt(
>>>> +             lradc,
>>>> +             LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
>>>> +             LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY un=
it#3 */
>>>> +             LRADC_DELAY_KICK |
>>>> +             LRADC_DELAY_DELAY(ts->settling_delay), LRADC_DELAY(2));
>>>> +}
>>>> +
>>>> +static unsigned mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts=
,
>>>> +                                           unsigned channel)
>>>> +{
>>>> +     u32 reg;
>>>> +     unsigned num_samples, val;
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +
>>>> +     reg =3D readl(lradc->base + LRADC_CH(channel));
>>>> +     if (reg & LRADC_CH_ACCUMULATE)
>>>> +             num_samples =3D ts->over_sample_cnt;
>>>> +     else
>>>> +             num_samples =3D 1;
>>>> +
>>>> +     val =3D (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
>>>> +     return val / num_samples;
>>>> +}
>>>> +
>>>> +static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts,
>>>> +                                        unsigned ch1, unsigned ch2)
>>>> +{
>>>> +     u32 reg, mask;
>>>> +     unsigned pressure, m1, m2;
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +
>>>> +     mask =3D LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2)=
;
>>>> +     reg =3D readl(lradc->base + LRADC_CTRL1) & mask;
>>>> +
>>>> +     while (reg !=3D mask) {
>>>> +             reg =3D readl(lradc->base + LRADC_CTRL1) & mask;
>>>> +             dev_dbg(ts->dev, "One channel is still busy: %X\n", reg)=
;
>>>> +     }
>>>> +
>>>> +     m1 =3D mxs_lradc_ts_read_raw_channel(ts, ch1);
>>>> +     m2 =3D mxs_lradc_ts_read_raw_channel(ts, ch2);
>>>> +
>>>> +     if (m2 =3D=3D 0) {
>>>> +             dev_warn(ts->dev, "Cannot calculate pressure\n");
>>>> +             return 1 << (LRADC_RESOLUTION - 1);
>>>> +     }
>>>> +
>>>> +     /* simply scale the value from 0 ... max ADC resolution */
>>>> +     pressure =3D m1;
>>>> +     pressure *=3D (1 << LRADC_RESOLUTION);
>>>> +     pressure /=3D m2;
>>>> +
>>>> +     dev_dbg(ts->dev, "Pressure =3D %u\n", pressure);
>>>> +     return pressure;
>>>> +}
>>>> +
>>>> +#define TS_CH_XP 2
>>>> +#define TS_CH_YP 3
>>>> +#define TS_CH_XM 4
>>>> +#define TS_CH_YM 5
>>>> +
>>>> +/*
>>>> + * YP(open)--+-------------+
>>>> + *        |             |--+
>>>> + *        |             |  |
>>>> + *    YM(-)--+-------------+  |
>>>> + *          +--------------+
>>>> + *          |              |
>>>> + *      XP(weak+)        XM(open)
>>>> + *
>>>> + * "weak+" means 200k Ohm VDDIO
>>>> + * (-) means GND
>>>> + */
>>>> +static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +
>>>> +     /*
>>>> +      * In order to detect a touch event the 'touch detect enable' bi=
t
>>>> +      * enables:
>>>> +      *  - a weak pullup to the X+ connector
>>>> +      *  - a strong ground at the Y- connector
>>>> +      */
>>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CT=
RL0);
>>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc),
>>>> +                       LRADC_CTRL0);
>>>> +}
>>>> +
>>>> +/*
>>>> + * YP(meas)--+-------------+
>>>> + *        |             |--+
>>>> + *        |             |  |
>>>> + * YM(open)--+-------------+  |
>>>> + *          +--------------+
>>>> + *          |              |
>>>> + *        XP(+)          XM(-)
>>>> + *
>>>> + * (+) means here 1.85 V
>>>> + * (-) means here GND
>>>> + */
>>>> +static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CT=
RL0);
>>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_C=
TRL0);
>>>> +
>>>> +     ts->cur_plate =3D LRADC_SAMPLE_X;
>>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP)=
;
>>>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>>>> +}
>>>> +
>>>> +/*
>>>> + *   YP(+)--+-------------+
>>>> + *       |             |--+
>>>> + *       |             |  |
>>>> + *   YM(-)--+-------------+  |
>>>> + *         +--------------+
>>>> + *         |              |
>>>> + *      XP(open)        XM(meas)
>>>> + *
>>>> + * (+) means here 1.85 V
>>>> + * (-) means here GND
>>>> + */
>>>> +static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CT=
RL0);
>>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_C=
TRL0);
>>>> +
>>>> +     ts->cur_plate =3D LRADC_SAMPLE_Y;
>>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM)=
;
>>>> +     mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
>>>> +}
>>>> +
>>>> +/*
>>>> + *    YP(+)--+-------------+
>>>> + *        |             |--+
>>>> + *        |             |  |
>>>> + * YM(meas)--+-------------+  |
>>>> + *          +--------------+
>>>> + *          |              |
>>>> + *       XP(meas)        XM(-)
>>>> + *
>>>> + * (+) means here 1.85 V
>>>> + * (-) means here GND
>>>> + */
>>>> +static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CT=
RL0);
>>>> +     mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_=
CTRL0);
>>>> +
>>>> +     ts->cur_plate =3D LRADC_SAMPLE_PRESSURE;
>>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM)=
;
>>>> +     mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP)=
;
>>>> +     mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2,
>>>> +                                 TOUCHSCREEN_VCHANNEL1);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     mxs_lradc_setup_touch_detection(ts);
>>>> +
>>>> +     ts->cur_plate =3D LRADC_TOUCH;
>>>> +     mxs_lradc_reg_clear(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
>>>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1=
);
>>>> +     mxs_lradc_reg_set(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>>>> +                       LRADC_CTRL1);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     mxs_lradc_reg_clear(ts->lradc,
>>>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
>>>> +                         LRADC_CTRL1);
>>>> +     mxs_lradc_reg_set(ts->lradc,
>>>> +                       LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1=
),
>>>> +                       LRADC_CTRL1);
>>>> +     /*
>>>> +      * start with the Y-pos, because it uses nearly the same plate
>>>> +      * settings like the touch detection
>>>> +      */
>>>> +     mxs_lradc_prepare_y_pos(ts);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos);
>>>> +     input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos);
>>>> +     input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure);
>>>> +     input_report_key(ts->ts_input, BTN_TOUCH, 1);
>>>> +     input_sync(ts->ts_input);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +
>>>> +     mxs_lradc_setup_touch_detection(ts);
>>>> +     ts->cur_plate =3D LRADC_SAMPLE_VALID;
>>>> +     /*
>>>> +      * start a dummy conversion to burn time to settle the signals
>>>> +      * note: we are not interested in the conversion's value
>>>> +      */
>>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
>>>> +     mxs_lradc_reg_clear(lradc,
>>>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1)=
 |
>>>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2)=
,
>>>> +                         LRADC_CTRL1);
>>>> +     mxs_lradc_reg_wrt(
>>>> +                 lradc,
>>>> +                 LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
>>>> +                 LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5=
 ms */
>>>> +                 LRADC_DELAY(2));
>>>> +}
>>>> +
>>>> +/*
>>>> + * in order to avoid false measurements, report only samples where
>>>> + * the surface is still touched after the position measurement
>>>> + */
>>>> +static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, boo=
l valid)
>>>> +{
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +
>>>> +     /* if it is still touched, report the sample */
>>>> +     if (valid && mxs_lradc_check_touch_event(lradc)) {
>>>> +             ts->ts_valid =3D true;
>>>> +             mxs_lradc_report_ts_event(ts);
>>>> +     }
>>>> +
>>>> +     /* if it is even still touched, continue with the next measureme=
nt */
>>>> +     if (mxs_lradc_check_touch_event(lradc)) {
>>>> +             mxs_lradc_prepare_y_pos(ts);
>>>> +             return;
>>>> +     }
>>>> +
>>>> +     if (ts->ts_valid) {
>>>> +             /* signal the release */
>>>> +             ts->ts_valid =3D false;
>>>> +             input_report_key(ts->ts_input, BTN_TOUCH, 0);
>>>> +             input_sync(ts->ts_input);
>>>> +     }
>>>> +
>>>> +     /* if it is released, wait for the next touch via IRQ */
>>>> +     ts->cur_plate =3D LRADC_TOUCH;
>>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
>>>> +     mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
>>>> +     mxs_lradc_reg_clear(lradc,
>>>> +                         LRADC_CTRL1_TOUCH_DETECT_IRQ |
>>>> +                         LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNE=
L1) |
>>>> +                         LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1)=
,
>>>> +                         LRADC_CTRL1);
>>>> +     mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_=
CTRL1);
>>>> +}
>>>> +
>>>> +/* touchscreen's state machine */
>>>> +static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +
>>>> +     switch (ts->cur_plate) {
>>>> +     case LRADC_TOUCH:
>>>> +             if (mxs_lradc_check_touch_event(lradc))
>>>> +                     mxs_lradc_start_touch_event(ts);
>>>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
>>>> +                                 LRADC_CTRL1);
>>>> +             return;
>>>> +
>>>> +     case LRADC_SAMPLE_Y:
>>>> +             ts->ts_y_pos =3D
>>>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANN=
EL1);
>>>> +             mxs_lradc_prepare_x_pos(ts);
>>>> +             return;
>>>> +
>>>> +     case LRADC_SAMPLE_X:
>>>> +             ts->ts_x_pos =3D
>>>> +                 mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANN=
EL1);
>>>> +             mxs_lradc_prepare_pressure(ts);
>>>> +             return;
>>>> +
>>>> +     case LRADC_SAMPLE_PRESSURE:
>>>> +             ts->ts_pressure =3D
>>>> +                 mxs_lradc_read_ts_pressure(ts,
>>>> +                                            TOUCHSCREEN_VCHANNEL2,
>>>> +                                            TOUCHSCREEN_VCHANNEL1);
>>>> +             mxs_lradc_complete_touch_event(ts);
>>>> +             return;
>>>> +
>>>> +     case LRADC_SAMPLE_VALID:
>>>> +             mxs_lradc_finish_touch_event(ts, 1);
>>>> +             break;
>>>> +     }
>>>> +}
>>>> +
>>>> +/*
>>>> + * IRQ Handling
>>>> + */
>>>> +static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data)
>>>> +{
>>>> +     struct mxs_lradc_ts *ts =3D data;
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +     unsigned long reg =3D readl(lradc->base + LRADC_CTRL1);
>>>> +     u32 clr_irq =3D mxs_lradc_irq_mask(lradc);
>>>> +     const u32 ts_irq_mask =3D
>>>> +             LRADC_CTRL1_TOUCH_DETECT_IRQ |
>>>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
>>>> +             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
>>>> +
>>>> +     if (!(reg & mxs_lradc_irq_mask(lradc)))
>>>> +             return IRQ_NONE;
>>>> +
>>>> +     if (lradc->use_touchscreen && (reg & ts_irq_mask)) {
>>>> +             mxs_lradc_handle_touch(ts);
>>>> +
>>>> +             /* Make sure we don't clear the next conversion's interr=
upt. */
>>>> +             clr_irq &=3D ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNE=
L1) |
>>>> +                             LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANN=
EL2));
>>>> +             mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
>>>> +     }
>>>> +
>>>> +     return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +static int mxs_lradc_ts_open(struct input_dev *dev)
>>>> +{
>>>> +     struct mxs_lradc_ts *ts =3D input_get_drvdata(dev);
>>>> +
>>>> +     /* Enable the touch-detect circuitry. */
>>>> +     mxs_lradc_enable_touch_detection(ts);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static void mxs_lradc_disable_ts(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +
>>>> +     /* stop all interrupts from firing */
>>>> +     mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
>>>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
>>>> +             LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_C=
TRL1);
>>>> +
>>>> +     /* Power-down touchscreen touch-detect circuitry. */
>>>> +     mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CT=
RL0);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_ts_close(struct input_dev *dev)
>>>> +{
>>>> +     struct mxs_lradc_ts *ts =3D input_get_drvdata(dev);
>>>> +
>>>> +     mxs_lradc_disable_ts(ts);
>>>> +}
>>>> +
>>>> +static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +
>>>> +     /* Configure the touchscreen type */
>>>> +     if (lradc->soc =3D=3D IMX28_LRADC) {
>>>> +             mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN=
_TYPE,
>>>> +                                 LRADC_CTRL0);
>>>> +
>>>> +             if (lradc->use_touchscreen =3D=3D MXS_LRADC_TOUCHSCREEN_=
5WIRE)
>>>> +                     mxs_lradc_reg_set(lradc,
>>>> +                                       LRADC_CTRL0_MX28_TOUCH_SCREEN_=
TYPE,
>>>> +                                       LRADC_CTRL0);
>>>> +     }
>>>> +}
>>>> +
>>>> +static void mxs_lradc_ts_hw_stop(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     int i;
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +
>>>> +     mxs_lradc_reg_clear(lradc,
>>>> +                     lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN=
_OFFSET,
>>>> +                     LRADC_CTRL1);
>>>> +
>>>> +     for (i =3D 1; i < LRADC_MAX_DELAY_CHANS; i++)
>>>> +             mxs_lradc_reg_wrt(ts->lradc, 0, LRADC_DELAY(i));
>>>> +}
>>>> +
>>>> +static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts)
>>>> +{
>>>> +     struct input_dev *input =3D ts->ts_input;
>>>> +     struct device *dev =3D ts->dev;
>>>> +     struct mxs_lradc *lradc =3D ts->lradc;
>>>> +
>>>> +     if (!lradc->use_touchscreen)
>>>> +             return 0;
>>>> +
>>>> +     input->name =3D DRIVER_NAME_TS;
>>>> +     input->id.bustype =3D BUS_HOST;
>>>> +     input->dev.parent =3D dev;
>>>> +     input->open =3D mxs_lradc_ts_open;
>>>> +     input->close =3D mxs_lradc_ts_close;
>>>> +
>>>> +     __set_bit(EV_ABS, input->evbit);
>>>> +     __set_bit(EV_KEY, input->evbit);
>>>> +     __set_bit(BTN_TOUCH, input->keybit);
>>>> +     __set_bit(INPUT_PROP_DIRECT, input->propbit);
>>>> +     input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, =
0, 0);
>>>> +     input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, =
0, 0);
>>>> +     input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE=
_MASK,
>>>> +                          0, 0);
>>>> +
>>>> +     ts->ts_input =3D input;
>>>> +     input_set_drvdata(input, ts);
>>>> +
>>>> +     return input_register_device(input);
>>>> +}
>>>> +
>>>> +static int mxs_lradc_ts_probe(struct platform_device *pdev)
>>>> +{
>>>> +     struct device *dev =3D &pdev->dev;
>>>> +     struct device_node *node =3D dev->parent->of_node;
>>>> +     struct mxs_lradc *lradc =3D dev_get_platdata(dev);
>>>> +     struct mxs_lradc_ts *ts;
>>>> +     struct input_dev *input;
>>>> +     int touch_ret, ret, i;
>>>> +     u32 ts_wires =3D 0, adapt;
>>>> +
>>>> +     ts =3D devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
>>>> +     input =3D devm_input_allocate_device(dev);
>>>> +     if (!ts || !input)
>>>> +             return -ENOMEM;
>>>> +
>>>> +     ts->lradc =3D lradc;
>>>> +     ts->dev =3D dev;
>>>> +     ts->ts_input =3D input;
>>>> +     platform_set_drvdata(pdev, ts);
>>>> +     input_set_drvdata(input, ts);
>>>> +
>>>> +     touch_ret =3D of_property_read_u32(node, "fsl,lradc-touchscreen-=
wires",
>>>> +                                      &ts_wires);
>>>> +
>>>> +     if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) {
>>>> +             ts->over_sample_cnt =3D 4;
>>>> +     } else {
>>>> +             if (adapt < 1 || adapt > 32) {
>>>> +                     dev_err(ts->dev, "Invalid sample count (%u)\n",
>>>> +                             adapt);
>>>> +                     touch_ret =3D -EINVAL;
>>>> +             } else {
>>>> +                     ts->over_sample_cnt =3D adapt;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) {
>>>> +             ts->over_sample_delay =3D 2;
>>>> +     } else {
>>>> +             if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) {
>>>> +                     dev_err(ts->dev, "Invalid sample delay (%u)\n",
>>>> +                             adapt);
>>>> +                     touch_ret =3D -EINVAL;
>>>> +             } else {
>>>> +                     ts->over_sample_delay =3D adapt;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     if (of_property_read_u32(node, "fsl,settling", &adapt)) {
>>>> +             ts->settling_delay =3D 10;
>>>> +     } else {
>>>> +             if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) {
>>>> +                     dev_err(ts->dev, "Invalid settling delay (%u)\n"=
,
>>>> +                             adapt);
>>>> +                     touch_ret =3D -EINVAL;
>>>> +             } else {
>>>> +                     ts->settling_delay =3D adapt;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     mxs_lradc_ts_hw_init(ts);
>>>> +     for (i =3D 0; i < lradc->irq_count; i++) {
>>>> +             ret =3D devm_request_irq(dev, lradc->irq[i],
>>>> +                                    mxs_lradc_ts_handle_irq,
>>>> +                                    IRQF_SHARED, lradc->irq_name[i], =
ts);
>>> As with the adc driver, are we actually using all of these?  I'd prefer=
 we
>>> only grab the ones that are actually relevant.
>>
>> Only irq lines relevant for touchscreen are:
>> mxs-lradc-touchscreen, mxs-lradc-channel6 and mxs-lradc-channel7
>> But not all interrupts are beiing used even when I enabled all remaining
>> channels (not used by touchscreen) for bufferd capture via
>> echo 1 >/sys/bus/iio/devices/iio\:device0/scan_elements/in_voltagexx_en
>>
>> So I don't know if it's supposed to work like this...
>> (It works the same on the original code)
> Certainly should only grab the relevant ones to touch screen use in here.=
.
> Original code probably being overly enthusiastic and we never noticed ;)

Sorry for being unclear...
The point I was trying to make was that even though touchscreen and
adc don't share irq lines, irqs mxs-lradc-channel(2,3,4,5,6) are never
being used.
So touchscreen is only use 2 out of 3 irqs assigned to it:
mxs-lradc-touchscreen and mxs-lradc-channel7  . And adc
is only using 2 irq lines mxs-lradc-channel(0,1) during buffered
capture, even when all virtal channels are enabled.
I found that odd...

>> root@cfa100xx:~# cat /proc/interrupts
>>            CPU0
>>  16:      13108         -  48 Edge      MXS Timer Tick
>>  17:       4240         -  82 Edge      mxs-dma
>>  25:          6         -  96 Edge      80010000.ssp
>> 196:          0         -  68 Edge      mxs-dma
>> 210:         13         -  10 Edge      mxs-lradc-touchscreen
>> 211:          0         -  14 Edge      mxs-lradc-thresh0
>> 212:          0         -  15 Edge      mxs-lradc-thresh1
>> 213:         10         -  16 Edge      mxs-lradc-channel0
>> 214:         10         -  17 Edge      mxs-lradc-channel1
>> 215:          0         -  18 Edge      mxs-lradc-channel2
>> 216:          0         -  19 Edge      mxs-lradc-channel3
>> 217:          0         -  20 Edge      mxs-lradc-channel4
>> 218:          0         -  21 Edge      mxs-lradc-channel5
>> 219:          0         -  22 Edge      mxs-lradc-channel6
>> 220:        412         -  23 Edge      mxs-lradc-channel7
>> 221:          0         -  24 Edge      mxs-lradc-button0
>> 222:          0         -  25 Edge      mxs-lradc-button1
>> 223:          0         -  29 Edge      RTC alarm
>> 224:          0         - 111 Edge      80058000.i2c
>> 228:        174         -  47 Edge      uart-pl011
>> 229:        439         -  93 Edge      80080000.usb
>> 230:          0         -  92 Edge      80090000.usb
>> 231:       3610         - 101 Edge      800f0000.ethernet
>> 232:         10  80050000.lradc-dev0 Edge
>> Err:          0
>>
>>>> +             if (ret)
>>>> +                     return ret;
>>>> +     }
>>>> +
>>>> +     if (!touch_ret) {
>>>> +             ret =3D mxs_lradc_ts_register(ts);
>>>> +             if (!ret)
>>>> +                     goto err_ts_register;
>>>> +     }
>>>> +
>>>> +     return 0;
>>>> +
>>>> +err_ts_register:
>>>> +     mxs_lradc_ts_hw_stop(ts);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int mxs_lradc_ts_remove(struct platform_device *pdev)
>>>> +{
>>>> +     struct mxs_lradc_ts *ts =3D platform_get_drvdata(pdev);
>>>> +
>>>> +     mxs_lradc_ts_hw_stop(ts);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static struct platform_driver mxs_lradc_ts_driver =3D {
>>>> +     .driver =3D {
>>>> +             .name =3D DRIVER_NAME_TS,
>>>> +     },
>>>> +     .probe  =3D mxs_lradc_ts_probe,
>>>> +     .remove =3D mxs_lradc_ts_remove,
>>>> +};
>>>> +module_platform_driver(mxs_lradc_ts_driver);
>>>> +
>>>> +MODULE_LICENSE("GPL v2");
>>>>
>>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
  2016-05-29 18:00           ` Ksenija Stanojević
  (?)
  (?)
@ 2016-05-29 19:53           ` Jonathan Cameron
  -1 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2016-05-29 19:53 UTC (permalink / raw)
  To: Ksenija Stanojević
  Cc: linux-kernel, Lee Jones, Dmitry Torokhov, linux-input,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Marek Vašut, linux-iio, Harald Geyer

<snip>
>>>>> +     if (of_property_read_u32(node, "fsl,settling", &adapt)) {
>>>>> +             ts->settling_delay = 10;
>>>>> +     } else {
>>>>> +             if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) {
>>>>> +                     dev_err(ts->dev, "Invalid settling delay (%u)\n",
>>>>> +                             adapt);
>>>>> +                     touch_ret = -EINVAL;
>>>>> +             } else {
>>>>> +                     ts->settling_delay = adapt;
>>>>> +             }
>>>>> +     }
>>>>> +
>>>>> +     mxs_lradc_ts_hw_init(ts);
>>>>> +     for (i = 0; i < lradc->irq_count; i++) {
>>>>> +             ret = devm_request_irq(dev, lradc->irq[i],
>>>>> +                                    mxs_lradc_ts_handle_irq,
>>>>> +                                    IRQF_SHARED, lradc->irq_name[i], ts);
>>>> As with the adc driver, are we actually using all of these?  I'd prefer we
>>>> only grab the ones that are actually relevant.
>>>
>>> Only irq lines relevant for touchscreen are:
>>> mxs-lradc-touchscreen, mxs-lradc-channel6 and mxs-lradc-channel7
>>> But not all interrupts are beiing used even when I enabled all remaining
>>> channels (not used by touchscreen) for bufferd capture via
>>> echo 1 >/sys/bus/iio/devices/iio\:device0/scan_elements/in_voltagexx_en
>>>
>>> So I don't know if it's supposed to work like this...
>>> (It works the same on the original code)
>> Certainly should only grab the relevant ones to touch screen use in here..
>> Original code probably being overly enthusiastic and we never noticed ;)
> 
> Sorry for being unclear...
> The point I was trying to make was that even though touchscreen and
> adc don't share irq lines, irqs mxs-lradc-channel(2,3,4,5,6) are never
> being used.
> So touchscreen is only use 2 out of 3 irqs assigned to it:
> mxs-lradc-touchscreen and mxs-lradc-channel7  . And adc
> is only using 2 irq lines mxs-lradc-channel(0,1) during buffered
> capture, even when all virtal channels are enabled.
> I found that odd...
odd indeed. It's odd hardware ;)

>From what I recall this hardware is capable of scheduling things in really
complex ways, but the driver elects not to use that, basically because it became
unmanageably complex and KISS was applied....

If there is an application needing that complexity we can revisit.

Of course there may be a better explanation ;)

Jonathan
> 
>>> root@cfa100xx:~# cat /proc/interrupts
>>>            CPU0
>>>  16:      13108         -  48 Edge      MXS Timer Tick
>>>  17:       4240         -  82 Edge      mxs-dma
>>>  25:          6         -  96 Edge      80010000.ssp
>>> 196:          0         -  68 Edge      mxs-dma
>>> 210:         13         -  10 Edge      mxs-lradc-touchscreen
>>> 211:          0         -  14 Edge      mxs-lradc-thresh0
>>> 212:          0         -  15 Edge      mxs-lradc-thresh1
>>> 213:         10         -  16 Edge      mxs-lradc-channel0
>>> 214:         10         -  17 Edge      mxs-lradc-channel1
>>> 215:          0         -  18 Edge      mxs-lradc-channel2
>>> 216:          0         -  19 Edge      mxs-lradc-channel3
>>> 217:          0         -  20 Edge      mxs-lradc-channel4
>>> 218:          0         -  21 Edge      mxs-lradc-channel5
>>> 219:          0         -  22 Edge      mxs-lradc-channel6
>>> 220:        412         -  23 Edge      mxs-lradc-channel7
>>> 221:          0         -  24 Edge      mxs-lradc-button0
>>> 222:          0         -  25 Edge      mxs-lradc-button1
>>> 223:          0         -  29 Edge      RTC alarm
>>> 224:          0         - 111 Edge      80058000.i2c
>>> 228:        174         -  47 Edge      uart-pl011
>>> 229:        439         -  93 Edge      80080000.usb
>>> 230:          0         -  92 Edge      80090000.usb
>>> 231:       3610         - 101 Edge      800f0000.ethernet
>>> 232:         10  80050000.lradc-dev0 Edge
>>> Err:          0
>>>
>>>>> +             if (ret)
>>>>> +                     return ret;
>>>>> +     }
>>>>> +
>>>>> +     if (!touch_ret) {
>>>>> +             ret = mxs_lradc_ts_register(ts);
>>>>> +             if (!ret)
>>>>> +                     goto err_ts_register;
>>>>> +     }
>>>>> +
>>>>> +     return 0;
>>>>> +
>>>>> +err_ts_register:
>>>>> +     mxs_lradc_ts_hw_stop(ts);
>>>>> +     return ret;
>>>>> +}
>>>>> +
>>>>> +static int mxs_lradc_ts_remove(struct platform_device *pdev)
>>>>> +{
>>>>> +     struct mxs_lradc_ts *ts = platform_get_drvdata(pdev);
>>>>> +
>>>>> +     mxs_lradc_ts_hw_stop(ts);
>>>>> +
>>>>> +     return 0;
>>>>> +}
>>>>> +
>>>>> +static struct platform_driver mxs_lradc_ts_driver = {
>>>>> +     .driver = {
>>>>> +             .name = DRIVER_NAME_TS,
>>>>> +     },
>>>>> +     .probe  = mxs_lradc_ts_probe,
>>>>> +     .remove = mxs_lradc_ts_remove,
>>>>> +};
>>>>> +module_platform_driver(mxs_lradc_ts_driver);
>>>>> +
>>>>> +MODULE_LICENSE("GPL v2");
>>>>>
>>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>>> the body of a message to majordomo@vger.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>
>>
> 

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
  2016-05-28 17:46       ` Ksenija Stanojević
@ 2016-06-01 18:29         ` Dmitry Torokhov
  -1 siblings, 0 replies; 40+ messages in thread
From: Dmitry Torokhov @ 2016-06-01 18:29 UTC (permalink / raw)
  To: Ksenija Stanojević
  Cc: linux-kernel, Lee Jones, linux-input, Jonathan Cameron,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Marek Vašut, linux-iio, Harald Geyer

On Sat, May 28, 2016 at 07:46:07PM +0200, Ksenija Stanojević wrote:
> On Sat, Apr 30, 2016 at 1:36 AM, Dmitry Torokhov
> <dmitry.torokhov@gmail.com> wrote:
> > Hi Ksenija,
> >
> > On Fri, Apr 29, 2016 at 01:49:11PM +0200, Ksenija Stanojevic wrote:
> >> +
> >> +     mxs_lradc_ts_hw_init(ts);
> >> +     for (i = 0; i < lradc->irq_count; i++) {
> >> +             ret = devm_request_irq(dev, lradc->irq[i],
> >> +                                    mxs_lradc_ts_handle_irq,
> >
> > Hmm, if you have several interrupts handled by the same interrupt
> > handler you'd need some locking there.
> 
> same interrupt handler can run concurrently only on multi cores, so
> why do we need locking?

We do not necessarily need it on single core (unless someone will turn
it int threaded IRQ down the road), but adding it costs almost nothing
and will ensure that the driver will continue working even if block is
moved to multi-core SOC in the future...

Thanks.

-- 
Dmitry

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

* Re: [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen
@ 2016-06-01 18:29         ` Dmitry Torokhov
  0 siblings, 0 replies; 40+ messages in thread
From: Dmitry Torokhov @ 2016-06-01 18:29 UTC (permalink / raw)
  To: Ksenija Stanojević
  Cc: linux-kernel, Lee Jones, linux-input, Jonathan Cameron,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Marek Vašut, linux-iio, Harald Geyer

On Sat, May 28, 2016 at 07:46:07PM +0200, Ksenija Stanojević wrote:
> On Sat, Apr 30, 2016 at 1:36 AM, Dmitry Torokhov
> <dmitry.torokhov@gmail.com> wrote:
> > Hi Ksenija,
> >
> > On Fri, Apr 29, 2016 at 01:49:11PM +0200, Ksenija Stanojevic wrote:
> >> +
> >> +     mxs_lradc_ts_hw_init(ts);
> >> +     for (i = 0; i < lradc->irq_count; i++) {
> >> +             ret = devm_request_irq(dev, lradc->irq[i],
> >> +                                    mxs_lradc_ts_handle_irq,
> >
> > Hmm, if you have several interrupts handled by the same interrupt
> > handler you'd need some locking there.
> 
> same interrupt handler can run concurrently only on multi cores, so
> why do we need locking?

We do not necessarily need it on single core (unless someone will turn
it int threaded IRQ down the road), but adding it costs almost nothing
and will ensure that the driver will continue working even if block is
moved to multi-core SOC in the future...

Thanks.

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

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

end of thread, other threads:[~2016-06-01 18:29 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-29 11:46 [PATCH 0/3] mxs-lradc: Split driver into MFD Ksenija Stanojevic
2016-04-29 11:46 ` Ksenija Stanojevic
2016-04-29 11:47 ` [PATCH 1/3] mfd: mxs-lradc: Add support for mxs-lradc MFD Ksenija Stanojevic
2016-04-29 11:47   ` Ksenija Stanojevic
2016-04-29 13:15   ` Marek Vasut
2016-04-29 13:15     ` Marek Vasut
2016-04-29 13:43     ` Ksenija Stanojević
2016-04-29 14:12       ` Marek Vasut
2016-04-29 14:12         ` Marek Vasut
2016-04-29 17:19   ` Harald Geyer
2016-04-29 17:19     ` Harald Geyer
2016-05-01 13:06   ` Jonathan Cameron
2016-04-29 11:48 ` [PATCH 2/3] iio: adc: mxs-lradc: Add support for adc driver Ksenija Stanojevic
2016-04-29 13:21   ` Marek Vasut
2016-05-01 16:38   ` Jonathan Cameron
2016-05-01 16:38     ` Jonathan Cameron
2016-05-28 17:49     ` Ksenija Stanojević
2016-05-28 17:49       ` Ksenija Stanojević
2016-05-29 16:46       ` Jonathan Cameron
2016-05-29 16:46         ` Jonathan Cameron
2016-04-29 11:49 ` [PATCH 3/3] input: touchscreen: mxs-lradc: Add support for touchscreen Ksenija Stanojevic
2016-04-29 13:22   ` Marek Vasut
2016-04-29 23:36   ` Dmitry Torokhov
2016-04-29 23:36     ` Dmitry Torokhov
2016-04-29 23:57     ` Marek Vasut
2016-04-29 23:57       ` Marek Vasut
2016-05-02 16:58       ` Dmitry Torokhov
2016-05-02 16:58         ` Dmitry Torokhov
2016-05-28 17:46     ` Ksenija Stanojević
2016-05-28 17:46       ` Ksenija Stanojević
2016-06-01 18:29       ` Dmitry Torokhov
2016-06-01 18:29         ` Dmitry Torokhov
2016-05-01 16:47   ` Jonathan Cameron
2016-05-28 17:45     ` Ksenija Stanojević
2016-05-28 17:45       ` Ksenija Stanojević
2016-05-29 16:49       ` Jonathan Cameron
2016-05-29 18:00         ` Ksenija Stanojević
2016-05-29 18:00           ` Ksenija Stanojević
2016-05-29 18:00           ` Ksenija Stanojević
2016-05-29 19:53           ` Jonathan Cameron

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