All of lore.kernel.org
 help / color / mirror / Atom feed
From: Harald Geyer <harald-95f8Dae0BrPYtjvyW6yDsg@public.gmane.org>
To: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Shawn Guo <shawnguo-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>,
	Sascha Hauer <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Cc: Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org>,
	Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	Harald Geyer <harald-95f8Dae0BrPYtjvyW6yDsg@public.gmane.org>
Subject: [PATCH 1/3] iio: mxs-lradc: Add regulators for current sources
Date: Fri, 22 Apr 2016 13:52:25 +0000	[thread overview]
Message-ID: <1461333147-11873-2-git-send-email-harald@ccbib.org> (raw)
In-Reply-To: <1461333147-11873-1-git-send-email-harald-95f8Dae0BrPYtjvyW6yDsg@public.gmane.org>

The hardware has two current sources ISRC0 and ISRC1 to allow measuring
resistors without additional circuitry. This commit makes them available
as regulators.

Tested on an imx233-olinuxino board.

Signed-off-by: Harald Geyer <harald-95f8Dae0BrPYtjvyW6yDsg@public.gmane.org>
---
The current regulator API doesn't fit this type of device very well: Typically
consumers will want to set a defined current, ie. min_uA == max_uA, but they
can't without help from configuration data, because the valid values aren't
reported by the API for current regulators. I have been thinking about
extending the API, but currently AFAIK no such consumers exist and most
users, like myself, will force the regulator to a defined value in
devicetree anyway.

 .../bindings/staging/iio/adc/mxs-lradc.txt         |  29 ++++
 drivers/iio/adc/Kconfig                            |   1 +
 drivers/iio/adc/mxs-lradc.c                        | 152 +++++++++++++++++++++
 3 files changed, 182 insertions(+)

diff --git a/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt b/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt
index 555fb11..983952c 100644
--- a/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt
+++ b/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt
@@ -19,6 +19,15 @@ Optional properties:
 - fsl,settling: delay between plate switch to next sample. Allowed value is
                 1 ... 2047. It counts at 2 kHz and its default is
                 10 (= 5 ms)
+- ISRC0: A node describing the regulator of internal current source 0
+- ISRC1: A node describing the regulator of internal current source 1
+
+Required properties for the ISRCx sub-nodes:
+- regulator-max-microamp: See standard regulator binding documentation.
+                          Valid values are from 0 to 300 in steps of 20.
+
+Optional properties for the ISRCx sub-nodes:
+Any standard regulator properties that apply to current regulators.
 
 Example for i.MX23 SoC:
 
@@ -31,6 +40,16 @@ Example for i.MX23 SoC:
 		fsl,ave-ctrl = <4>;
 		fsl,ave-delay = <2>;
 		fsl,settling = <10>;
+
+		isrc_lradc0: ISRC0 {
+			regulator-max-microamp = <300>;
+		};
+
+		isrc_lradc1: ISRC1 {
+			regulator-max-microamp = <120>;
+			regulator-min-microamp = <120>;
+			regulator-always-on;
+		};
 	};
 
 Example for i.MX28 SoC:
@@ -44,4 +63,14 @@ Example for i.MX28 SoC:
 		fsl,ave-ctrl = <4>;
 		fsl,ave-delay = <2>;
 		fsl,settling = <10>;
+
+		isrc_lradc0: ISRC0 {
+			regulator-max-microamp = <300>;
+		};
+
+		isrc_lradc6: ISRC1 {
+			regulator-max-microamp = <120>;
+			regulator-min-microamp = <120>;
+			regulator-always-on;
+		};
 	};
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 5937030..1968d1c 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -319,6 +319,7 @@ config MXS_LRADC
         tristate "Freescale i.MX23/i.MX28 LRADC"
         depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
         depends on INPUT
+        depends on REGULATOR
         select STMP_DEVICE
         select IIO_BUFFER
         select IIO_TRIGGERED_BUFFER
diff --git a/drivers/iio/adc/mxs-lradc.c b/drivers/iio/adc/mxs-lradc.c
index 33051b8..f22f339 100644
--- a/drivers/iio/adc/mxs-lradc.c
+++ b/drivers/iio/adc/mxs-lradc.c
@@ -40,6 +40,10 @@
 #include <linux/iio/triggered_buffer.h>
 #include <linux/iio/sysfs.h>
 
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
 #define DRIVER_NAME		"mxs-lradc"
 
 #define LRADC_MAX_DELAY_CHANS	4
@@ -261,6 +265,9 @@ struct mxs_lradc {
 	unsigned		over_sample_delay;
 	/* time in clocks to wait after the plates where switched */
 	unsigned		settling_delay;
+
+	struct regulator_desc isrc0;
+	struct regulator_desc isrc1;
 };
 
 #define	LRADC_CTRL0				0x00
@@ -305,6 +312,11 @@ struct mxs_lradc {
 #define	LRADC_CTRL2				0x20
 #define	LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET	24
 #define	LRADC_CTRL2_TEMPSENSE_PWD		BIT(15)
+#define	LRADC_CTRL2_TEMP_SENSOR_IENABLE1	BIT(9)
+#define	LRADC_CTRL2_TEMP_SENSOR_IENABLE0	BIT(8)
+#define	LRADC_CTRL2_TEMP_ISRC1_OFFSET		4
+#define	LRADC_CTRL2_TEMP_ISRC0_OFFSET		0
+#define	LRADC_CTRL2_TEMP_ISRC_MASK		0x0f
 
 #define	LRADC_STATUS				0x40
 #define	LRADC_STATUS_TOUCH_DETECT_RAW		BIT(0)
@@ -1383,6 +1395,109 @@ static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = {
 	.validate_scan_mask = &mxs_lradc_validate_scan_mask,
 };
 
+static int mxs_lradc_regulator_is_enabled(struct regulator_dev *dev)
+{
+	struct mxs_lradc *lradc = rdev_get_drvdata(dev);
+	int reg = readl(lradc->base + LRADC_CTRL2);
+
+	if (dev->desc == &lradc->isrc0)
+		return reg & LRADC_CTRL2_TEMP_SENSOR_IENABLE0;
+	else if (dev->desc == &lradc->isrc1)
+		return reg & LRADC_CTRL2_TEMP_SENSOR_IENABLE1;
+
+	/* This should never happen */
+	return -ENODEV;
+}
+
+#define LRADC_REGVALUE2uA(regval, offset) \
+	(20 * ((regval >> offset) & LRADC_CTRL2_TEMP_ISRC_MASK))
+
+static int mxs_lradc_regulator_get_current_limit(struct regulator_dev *dev)
+{
+	struct mxs_lradc *lradc = rdev_get_drvdata(dev);
+	int reg = readl(lradc->base + LRADC_CTRL2);
+
+	if (dev->desc == &lradc->isrc0)
+		return LRADC_REGVALUE2uA(reg, LRADC_CTRL2_TEMP_ISRC0_OFFSET);
+	else if (dev->desc == &lradc->isrc1)
+		return LRADC_REGVALUE2uA(reg, LRADC_CTRL2_TEMP_ISRC1_OFFSET);
+
+	/* This should never happen */
+	return -ENODEV;
+}
+
+static int mxs_lradc_regulator_enable(struct regulator_dev *dev)
+{
+	struct mxs_lradc *lradc = rdev_get_drvdata(dev);
+
+	if (dev->desc == &lradc->isrc0)
+		mxs_lradc_reg_set(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE0,
+				  LRADC_CTRL2);
+	else if (dev->desc == &lradc->isrc1)
+		mxs_lradc_reg_set(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE1,
+				  LRADC_CTRL2);
+	else
+		/* This should never happen */
+		return -ENODEV;
+
+	return 0;
+}
+
+static int mxs_lradc_regulator_disable(struct regulator_dev *dev)
+{
+	struct mxs_lradc *lradc = rdev_get_drvdata(dev);
+
+	if (dev->desc == &lradc->isrc0)
+		mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE0,
+				    LRADC_CTRL2);
+	else if (dev->desc == &lradc->isrc1)
+		mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE1,
+				    LRADC_CTRL2);
+	else
+		/* This should never happen */
+		return -ENODEV;
+
+	return 0;
+}
+
+static int mxs_lradc_regulator_set_current_limit(struct regulator_dev *dev,
+						 int min_uA, int max_uA)
+{
+	struct mxs_lradc *lradc = rdev_get_drvdata(dev);
+	int offset, value;
+
+	if (dev->desc == &lradc->isrc0)
+		offset = LRADC_CTRL2_TEMP_ISRC0_OFFSET;
+	else if (dev->desc == &lradc->isrc1)
+		offset = LRADC_CTRL2_TEMP_ISRC1_OFFSET;
+	else
+		/* This should never happen */
+		return -ENODEV;
+
+	value = min_uA / 20;
+	if (min_uA % 20)
+		value++;
+	if (value * 20 > max_uA)
+		return -EINVAL;
+	if (value & ~LRADC_CTRL2_TEMP_ISRC_MASK)
+		/* This should never happen */
+		return -EPERM;
+
+	mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_ISRC_MASK << offset,
+			    LRADC_CTRL2);
+	mxs_lradc_reg_set(lradc, value << offset, LRADC_CTRL2);
+
+	return 0;
+}
+
+static struct regulator_ops mxs_lradc_regulator_current_ops = {
+	.enable = mxs_lradc_regulator_enable,
+	.is_enabled = mxs_lradc_regulator_is_enabled,
+	.disable = mxs_lradc_regulator_disable,
+	.get_current_limit = mxs_lradc_regulator_get_current_limit,
+	.set_current_limit = mxs_lradc_regulator_set_current_limit,
+};
+
 /*
  * Driver initialization
  */
@@ -1519,6 +1634,10 @@ static void mxs_lradc_hw_stop(struct mxs_lradc *lradc)
 
 	for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++)
 		mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(i));
+
+	mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE0 |
+					LRADC_CTRL2_TEMP_SENSOR_IENABLE1,
+			    LRADC_CTRL2);
 }
 
 static const struct of_device_id mxs_lradc_dt_ids[] = {
@@ -1592,6 +1711,32 @@ static int mxs_lradc_probe_touchscreen(struct mxs_lradc *lradc,
 	return 0;
 }
 
+static void mxs_lradc_reg_helper(struct device_node *np, const char *name,
+				 struct regulator_config *conf,
+				 struct regulator_desc *desc)
+{
+	struct regulator_dev *ret;
+
+	conf->of_node = of_get_child_by_name(np, name);
+	if (!conf->of_node)
+		return;
+
+	desc->name = name;
+	desc->owner = THIS_MODULE;
+	desc->type = REGULATOR_CURRENT;
+	desc->ops = &mxs_lradc_regulator_current_ops;
+
+	conf->init_data = of_get_regulator_init_data(conf->dev, conf->of_node,
+						     desc);
+	ret = devm_regulator_register(conf->dev, desc, conf);
+	if (IS_ERR(ret))
+		/* Just pretend the regulator isn't there */
+		dev_err(conf->dev, "Failed to register regulator %s: %ld\n",
+			desc->name, PTR_ERR(ret));
+
+	of_node_put(conf->of_node);
+}
+
 static int mxs_lradc_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *of_id =
@@ -1603,6 +1748,7 @@ static int mxs_lradc_probe(struct platform_device *pdev)
 	struct mxs_lradc *lradc;
 	struct iio_dev *iio;
 	struct resource *iores;
+	struct regulator_config regconf;
 	int ret = 0, touch_ret;
 	int i, s;
 	u64 scale_uv;
@@ -1727,6 +1873,12 @@ static int mxs_lradc_probe(struct platform_device *pdev)
 		goto err_ts;
 	}
 
+	/* Setup regulator devices for current source. */
+	regconf.dev = dev;
+	regconf.driver_data = lradc;
+	mxs_lradc_reg_helper(node, "ISRC0", &regconf, &lradc->isrc0);
+	mxs_lradc_reg_helper(node, "ISRC1", &regconf, &lradc->isrc1);
+
 	return 0;
 
 err_ts:
-- 
2.1.4

WARNING: multiple messages have this Message-ID (diff)
From: Harald Geyer <harald@ccbib.org>
To: Jonathan Cameron <jic23@kernel.org>,
	devicetree@vger.kernel.org, linux-iio@vger.kernel.org,
	Shawn Guo <shawnguo@kernel.org>,
	Sascha Hauer <kernel@pengutronix.de>
Cc: Stefan Wahren <stefan.wahren@i2se.com>,
	Marek Vasut <marex@denx.de>,
	linux-arm-kernel@lists.infradead.org,
	Harald Geyer <harald@ccbib.org>
Subject: [PATCH 1/3] iio: mxs-lradc: Add regulators for current sources
Date: Fri, 22 Apr 2016 13:52:25 +0000	[thread overview]
Message-ID: <1461333147-11873-2-git-send-email-harald@ccbib.org> (raw)
In-Reply-To: <1461333147-11873-1-git-send-email-harald@ccbib.org>

The hardware has two current sources ISRC0 and ISRC1 to allow measuring
resistors without additional circuitry. This commit makes them available
as regulators.

Tested on an imx233-olinuxino board.

Signed-off-by: Harald Geyer <harald@ccbib.org>
---
The current regulator API doesn't fit this type of device very well: Typically
consumers will want to set a defined current, ie. min_uA == max_uA, but they
can't without help from configuration data, because the valid values aren't
reported by the API for current regulators. I have been thinking about
extending the API, but currently AFAIK no such consumers exist and most
users, like myself, will force the regulator to a defined value in
devicetree anyway.

 .../bindings/staging/iio/adc/mxs-lradc.txt         |  29 ++++
 drivers/iio/adc/Kconfig                            |   1 +
 drivers/iio/adc/mxs-lradc.c                        | 152 +++++++++++++++++++++
 3 files changed, 182 insertions(+)

diff --git a/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt b/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt
index 555fb11..983952c 100644
--- a/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt
+++ b/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt
@@ -19,6 +19,15 @@ Optional properties:
 - fsl,settling: delay between plate switch to next sample. Allowed value is
                 1 ... 2047. It counts at 2 kHz and its default is
                 10 (= 5 ms)
+- ISRC0: A node describing the regulator of internal current source 0
+- ISRC1: A node describing the regulator of internal current source 1
+
+Required properties for the ISRCx sub-nodes:
+- regulator-max-microamp: See standard regulator binding documentation.
+                          Valid values are from 0 to 300 in steps of 20.
+
+Optional properties for the ISRCx sub-nodes:
+Any standard regulator properties that apply to current regulators.
 
 Example for i.MX23 SoC:
 
@@ -31,6 +40,16 @@ Example for i.MX23 SoC:
 		fsl,ave-ctrl = <4>;
 		fsl,ave-delay = <2>;
 		fsl,settling = <10>;
+
+		isrc_lradc0: ISRC0 {
+			regulator-max-microamp = <300>;
+		};
+
+		isrc_lradc1: ISRC1 {
+			regulator-max-microamp = <120>;
+			regulator-min-microamp = <120>;
+			regulator-always-on;
+		};
 	};
 
 Example for i.MX28 SoC:
@@ -44,4 +63,14 @@ Example for i.MX28 SoC:
 		fsl,ave-ctrl = <4>;
 		fsl,ave-delay = <2>;
 		fsl,settling = <10>;
+
+		isrc_lradc0: ISRC0 {
+			regulator-max-microamp = <300>;
+		};
+
+		isrc_lradc6: ISRC1 {
+			regulator-max-microamp = <120>;
+			regulator-min-microamp = <120>;
+			regulator-always-on;
+		};
 	};
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 5937030..1968d1c 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -319,6 +319,7 @@ config MXS_LRADC
         tristate "Freescale i.MX23/i.MX28 LRADC"
         depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
         depends on INPUT
+        depends on REGULATOR
         select STMP_DEVICE
         select IIO_BUFFER
         select IIO_TRIGGERED_BUFFER
diff --git a/drivers/iio/adc/mxs-lradc.c b/drivers/iio/adc/mxs-lradc.c
index 33051b8..f22f339 100644
--- a/drivers/iio/adc/mxs-lradc.c
+++ b/drivers/iio/adc/mxs-lradc.c
@@ -40,6 +40,10 @@
 #include <linux/iio/triggered_buffer.h>
 #include <linux/iio/sysfs.h>
 
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
 #define DRIVER_NAME		"mxs-lradc"
 
 #define LRADC_MAX_DELAY_CHANS	4
@@ -261,6 +265,9 @@ struct mxs_lradc {
 	unsigned		over_sample_delay;
 	/* time in clocks to wait after the plates where switched */
 	unsigned		settling_delay;
+
+	struct regulator_desc isrc0;
+	struct regulator_desc isrc1;
 };
 
 #define	LRADC_CTRL0				0x00
@@ -305,6 +312,11 @@ struct mxs_lradc {
 #define	LRADC_CTRL2				0x20
 #define	LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET	24
 #define	LRADC_CTRL2_TEMPSENSE_PWD		BIT(15)
+#define	LRADC_CTRL2_TEMP_SENSOR_IENABLE1	BIT(9)
+#define	LRADC_CTRL2_TEMP_SENSOR_IENABLE0	BIT(8)
+#define	LRADC_CTRL2_TEMP_ISRC1_OFFSET		4
+#define	LRADC_CTRL2_TEMP_ISRC0_OFFSET		0
+#define	LRADC_CTRL2_TEMP_ISRC_MASK		0x0f
 
 #define	LRADC_STATUS				0x40
 #define	LRADC_STATUS_TOUCH_DETECT_RAW		BIT(0)
@@ -1383,6 +1395,109 @@ static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = {
 	.validate_scan_mask = &mxs_lradc_validate_scan_mask,
 };
 
+static int mxs_lradc_regulator_is_enabled(struct regulator_dev *dev)
+{
+	struct mxs_lradc *lradc = rdev_get_drvdata(dev);
+	int reg = readl(lradc->base + LRADC_CTRL2);
+
+	if (dev->desc == &lradc->isrc0)
+		return reg & LRADC_CTRL2_TEMP_SENSOR_IENABLE0;
+	else if (dev->desc == &lradc->isrc1)
+		return reg & LRADC_CTRL2_TEMP_SENSOR_IENABLE1;
+
+	/* This should never happen */
+	return -ENODEV;
+}
+
+#define LRADC_REGVALUE2uA(regval, offset) \
+	(20 * ((regval >> offset) & LRADC_CTRL2_TEMP_ISRC_MASK))
+
+static int mxs_lradc_regulator_get_current_limit(struct regulator_dev *dev)
+{
+	struct mxs_lradc *lradc = rdev_get_drvdata(dev);
+	int reg = readl(lradc->base + LRADC_CTRL2);
+
+	if (dev->desc == &lradc->isrc0)
+		return LRADC_REGVALUE2uA(reg, LRADC_CTRL2_TEMP_ISRC0_OFFSET);
+	else if (dev->desc == &lradc->isrc1)
+		return LRADC_REGVALUE2uA(reg, LRADC_CTRL2_TEMP_ISRC1_OFFSET);
+
+	/* This should never happen */
+	return -ENODEV;
+}
+
+static int mxs_lradc_regulator_enable(struct regulator_dev *dev)
+{
+	struct mxs_lradc *lradc = rdev_get_drvdata(dev);
+
+	if (dev->desc == &lradc->isrc0)
+		mxs_lradc_reg_set(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE0,
+				  LRADC_CTRL2);
+	else if (dev->desc == &lradc->isrc1)
+		mxs_lradc_reg_set(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE1,
+				  LRADC_CTRL2);
+	else
+		/* This should never happen */
+		return -ENODEV;
+
+	return 0;
+}
+
+static int mxs_lradc_regulator_disable(struct regulator_dev *dev)
+{
+	struct mxs_lradc *lradc = rdev_get_drvdata(dev);
+
+	if (dev->desc == &lradc->isrc0)
+		mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE0,
+				    LRADC_CTRL2);
+	else if (dev->desc == &lradc->isrc1)
+		mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE1,
+				    LRADC_CTRL2);
+	else
+		/* This should never happen */
+		return -ENODEV;
+
+	return 0;
+}
+
+static int mxs_lradc_regulator_set_current_limit(struct regulator_dev *dev,
+						 int min_uA, int max_uA)
+{
+	struct mxs_lradc *lradc = rdev_get_drvdata(dev);
+	int offset, value;
+
+	if (dev->desc == &lradc->isrc0)
+		offset = LRADC_CTRL2_TEMP_ISRC0_OFFSET;
+	else if (dev->desc == &lradc->isrc1)
+		offset = LRADC_CTRL2_TEMP_ISRC1_OFFSET;
+	else
+		/* This should never happen */
+		return -ENODEV;
+
+	value = min_uA / 20;
+	if (min_uA % 20)
+		value++;
+	if (value * 20 > max_uA)
+		return -EINVAL;
+	if (value & ~LRADC_CTRL2_TEMP_ISRC_MASK)
+		/* This should never happen */
+		return -EPERM;
+
+	mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_ISRC_MASK << offset,
+			    LRADC_CTRL2);
+	mxs_lradc_reg_set(lradc, value << offset, LRADC_CTRL2);
+
+	return 0;
+}
+
+static struct regulator_ops mxs_lradc_regulator_current_ops = {
+	.enable = mxs_lradc_regulator_enable,
+	.is_enabled = mxs_lradc_regulator_is_enabled,
+	.disable = mxs_lradc_regulator_disable,
+	.get_current_limit = mxs_lradc_regulator_get_current_limit,
+	.set_current_limit = mxs_lradc_regulator_set_current_limit,
+};
+
 /*
  * Driver initialization
  */
@@ -1519,6 +1634,10 @@ static void mxs_lradc_hw_stop(struct mxs_lradc *lradc)
 
 	for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++)
 		mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(i));
+
+	mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE0 |
+					LRADC_CTRL2_TEMP_SENSOR_IENABLE1,
+			    LRADC_CTRL2);
 }
 
 static const struct of_device_id mxs_lradc_dt_ids[] = {
@@ -1592,6 +1711,32 @@ static int mxs_lradc_probe_touchscreen(struct mxs_lradc *lradc,
 	return 0;
 }
 
+static void mxs_lradc_reg_helper(struct device_node *np, const char *name,
+				 struct regulator_config *conf,
+				 struct regulator_desc *desc)
+{
+	struct regulator_dev *ret;
+
+	conf->of_node = of_get_child_by_name(np, name);
+	if (!conf->of_node)
+		return;
+
+	desc->name = name;
+	desc->owner = THIS_MODULE;
+	desc->type = REGULATOR_CURRENT;
+	desc->ops = &mxs_lradc_regulator_current_ops;
+
+	conf->init_data = of_get_regulator_init_data(conf->dev, conf->of_node,
+						     desc);
+	ret = devm_regulator_register(conf->dev, desc, conf);
+	if (IS_ERR(ret))
+		/* Just pretend the regulator isn't there */
+		dev_err(conf->dev, "Failed to register regulator %s: %ld\n",
+			desc->name, PTR_ERR(ret));
+
+	of_node_put(conf->of_node);
+}
+
 static int mxs_lradc_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *of_id =
@@ -1603,6 +1748,7 @@ static int mxs_lradc_probe(struct platform_device *pdev)
 	struct mxs_lradc *lradc;
 	struct iio_dev *iio;
 	struct resource *iores;
+	struct regulator_config regconf;
 	int ret = 0, touch_ret;
 	int i, s;
 	u64 scale_uv;
@@ -1727,6 +1873,12 @@ static int mxs_lradc_probe(struct platform_device *pdev)
 		goto err_ts;
 	}
 
+	/* Setup regulator devices for current source. */
+	regconf.dev = dev;
+	regconf.driver_data = lradc;
+	mxs_lradc_reg_helper(node, "ISRC0", &regconf, &lradc->isrc0);
+	mxs_lradc_reg_helper(node, "ISRC1", &regconf, &lradc->isrc1);
+
 	return 0;
 
 err_ts:
-- 
2.1.4


WARNING: multiple messages have this Message-ID (diff)
From: harald@ccbib.org (Harald Geyer)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 1/3] iio: mxs-lradc: Add regulators for current sources
Date: Fri, 22 Apr 2016 13:52:25 +0000	[thread overview]
Message-ID: <1461333147-11873-2-git-send-email-harald@ccbib.org> (raw)
In-Reply-To: <1461333147-11873-1-git-send-email-harald@ccbib.org>

The hardware has two current sources ISRC0 and ISRC1 to allow measuring
resistors without additional circuitry. This commit makes them available
as regulators.

Tested on an imx233-olinuxino board.

Signed-off-by: Harald Geyer <harald@ccbib.org>
---
The current regulator API doesn't fit this type of device very well: Typically
consumers will want to set a defined current, ie. min_uA == max_uA, but they
can't without help from configuration data, because the valid values aren't
reported by the API for current regulators. I have been thinking about
extending the API, but currently AFAIK no such consumers exist and most
users, like myself, will force the regulator to a defined value in
devicetree anyway.

 .../bindings/staging/iio/adc/mxs-lradc.txt         |  29 ++++
 drivers/iio/adc/Kconfig                            |   1 +
 drivers/iio/adc/mxs-lradc.c                        | 152 +++++++++++++++++++++
 3 files changed, 182 insertions(+)

diff --git a/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt b/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt
index 555fb11..983952c 100644
--- a/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt
+++ b/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt
@@ -19,6 +19,15 @@ Optional properties:
 - fsl,settling: delay between plate switch to next sample. Allowed value is
                 1 ... 2047. It counts at 2 kHz and its default is
                 10 (= 5 ms)
+- ISRC0: A node describing the regulator of internal current source 0
+- ISRC1: A node describing the regulator of internal current source 1
+
+Required properties for the ISRCx sub-nodes:
+- regulator-max-microamp: See standard regulator binding documentation.
+                          Valid values are from 0 to 300 in steps of 20.
+
+Optional properties for the ISRCx sub-nodes:
+Any standard regulator properties that apply to current regulators.
 
 Example for i.MX23 SoC:
 
@@ -31,6 +40,16 @@ Example for i.MX23 SoC:
 		fsl,ave-ctrl = <4>;
 		fsl,ave-delay = <2>;
 		fsl,settling = <10>;
+
+		isrc_lradc0: ISRC0 {
+			regulator-max-microamp = <300>;
+		};
+
+		isrc_lradc1: ISRC1 {
+			regulator-max-microamp = <120>;
+			regulator-min-microamp = <120>;
+			regulator-always-on;
+		};
 	};
 
 Example for i.MX28 SoC:
@@ -44,4 +63,14 @@ Example for i.MX28 SoC:
 		fsl,ave-ctrl = <4>;
 		fsl,ave-delay = <2>;
 		fsl,settling = <10>;
+
+		isrc_lradc0: ISRC0 {
+			regulator-max-microamp = <300>;
+		};
+
+		isrc_lradc6: ISRC1 {
+			regulator-max-microamp = <120>;
+			regulator-min-microamp = <120>;
+			regulator-always-on;
+		};
 	};
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 5937030..1968d1c 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -319,6 +319,7 @@ config MXS_LRADC
         tristate "Freescale i.MX23/i.MX28 LRADC"
         depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
         depends on INPUT
+        depends on REGULATOR
         select STMP_DEVICE
         select IIO_BUFFER
         select IIO_TRIGGERED_BUFFER
diff --git a/drivers/iio/adc/mxs-lradc.c b/drivers/iio/adc/mxs-lradc.c
index 33051b8..f22f339 100644
--- a/drivers/iio/adc/mxs-lradc.c
+++ b/drivers/iio/adc/mxs-lradc.c
@@ -40,6 +40,10 @@
 #include <linux/iio/triggered_buffer.h>
 #include <linux/iio/sysfs.h>
 
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
 #define DRIVER_NAME		"mxs-lradc"
 
 #define LRADC_MAX_DELAY_CHANS	4
@@ -261,6 +265,9 @@ struct mxs_lradc {
 	unsigned		over_sample_delay;
 	/* time in clocks to wait after the plates where switched */
 	unsigned		settling_delay;
+
+	struct regulator_desc isrc0;
+	struct regulator_desc isrc1;
 };
 
 #define	LRADC_CTRL0				0x00
@@ -305,6 +312,11 @@ struct mxs_lradc {
 #define	LRADC_CTRL2				0x20
 #define	LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET	24
 #define	LRADC_CTRL2_TEMPSENSE_PWD		BIT(15)
+#define	LRADC_CTRL2_TEMP_SENSOR_IENABLE1	BIT(9)
+#define	LRADC_CTRL2_TEMP_SENSOR_IENABLE0	BIT(8)
+#define	LRADC_CTRL2_TEMP_ISRC1_OFFSET		4
+#define	LRADC_CTRL2_TEMP_ISRC0_OFFSET		0
+#define	LRADC_CTRL2_TEMP_ISRC_MASK		0x0f
 
 #define	LRADC_STATUS				0x40
 #define	LRADC_STATUS_TOUCH_DETECT_RAW		BIT(0)
@@ -1383,6 +1395,109 @@ static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = {
 	.validate_scan_mask = &mxs_lradc_validate_scan_mask,
 };
 
+static int mxs_lradc_regulator_is_enabled(struct regulator_dev *dev)
+{
+	struct mxs_lradc *lradc = rdev_get_drvdata(dev);
+	int reg = readl(lradc->base + LRADC_CTRL2);
+
+	if (dev->desc == &lradc->isrc0)
+		return reg & LRADC_CTRL2_TEMP_SENSOR_IENABLE0;
+	else if (dev->desc == &lradc->isrc1)
+		return reg & LRADC_CTRL2_TEMP_SENSOR_IENABLE1;
+
+	/* This should never happen */
+	return -ENODEV;
+}
+
+#define LRADC_REGVALUE2uA(regval, offset) \
+	(20 * ((regval >> offset) & LRADC_CTRL2_TEMP_ISRC_MASK))
+
+static int mxs_lradc_regulator_get_current_limit(struct regulator_dev *dev)
+{
+	struct mxs_lradc *lradc = rdev_get_drvdata(dev);
+	int reg = readl(lradc->base + LRADC_CTRL2);
+
+	if (dev->desc == &lradc->isrc0)
+		return LRADC_REGVALUE2uA(reg, LRADC_CTRL2_TEMP_ISRC0_OFFSET);
+	else if (dev->desc == &lradc->isrc1)
+		return LRADC_REGVALUE2uA(reg, LRADC_CTRL2_TEMP_ISRC1_OFFSET);
+
+	/* This should never happen */
+	return -ENODEV;
+}
+
+static int mxs_lradc_regulator_enable(struct regulator_dev *dev)
+{
+	struct mxs_lradc *lradc = rdev_get_drvdata(dev);
+
+	if (dev->desc == &lradc->isrc0)
+		mxs_lradc_reg_set(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE0,
+				  LRADC_CTRL2);
+	else if (dev->desc == &lradc->isrc1)
+		mxs_lradc_reg_set(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE1,
+				  LRADC_CTRL2);
+	else
+		/* This should never happen */
+		return -ENODEV;
+
+	return 0;
+}
+
+static int mxs_lradc_regulator_disable(struct regulator_dev *dev)
+{
+	struct mxs_lradc *lradc = rdev_get_drvdata(dev);
+
+	if (dev->desc == &lradc->isrc0)
+		mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE0,
+				    LRADC_CTRL2);
+	else if (dev->desc == &lradc->isrc1)
+		mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE1,
+				    LRADC_CTRL2);
+	else
+		/* This should never happen */
+		return -ENODEV;
+
+	return 0;
+}
+
+static int mxs_lradc_regulator_set_current_limit(struct regulator_dev *dev,
+						 int min_uA, int max_uA)
+{
+	struct mxs_lradc *lradc = rdev_get_drvdata(dev);
+	int offset, value;
+
+	if (dev->desc == &lradc->isrc0)
+		offset = LRADC_CTRL2_TEMP_ISRC0_OFFSET;
+	else if (dev->desc == &lradc->isrc1)
+		offset = LRADC_CTRL2_TEMP_ISRC1_OFFSET;
+	else
+		/* This should never happen */
+		return -ENODEV;
+
+	value = min_uA / 20;
+	if (min_uA % 20)
+		value++;
+	if (value * 20 > max_uA)
+		return -EINVAL;
+	if (value & ~LRADC_CTRL2_TEMP_ISRC_MASK)
+		/* This should never happen */
+		return -EPERM;
+
+	mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_ISRC_MASK << offset,
+			    LRADC_CTRL2);
+	mxs_lradc_reg_set(lradc, value << offset, LRADC_CTRL2);
+
+	return 0;
+}
+
+static struct regulator_ops mxs_lradc_regulator_current_ops = {
+	.enable = mxs_lradc_regulator_enable,
+	.is_enabled = mxs_lradc_regulator_is_enabled,
+	.disable = mxs_lradc_regulator_disable,
+	.get_current_limit = mxs_lradc_regulator_get_current_limit,
+	.set_current_limit = mxs_lradc_regulator_set_current_limit,
+};
+
 /*
  * Driver initialization
  */
@@ -1519,6 +1634,10 @@ static void mxs_lradc_hw_stop(struct mxs_lradc *lradc)
 
 	for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++)
 		mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(i));
+
+	mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE0 |
+					LRADC_CTRL2_TEMP_SENSOR_IENABLE1,
+			    LRADC_CTRL2);
 }
 
 static const struct of_device_id mxs_lradc_dt_ids[] = {
@@ -1592,6 +1711,32 @@ static int mxs_lradc_probe_touchscreen(struct mxs_lradc *lradc,
 	return 0;
 }
 
+static void mxs_lradc_reg_helper(struct device_node *np, const char *name,
+				 struct regulator_config *conf,
+				 struct regulator_desc *desc)
+{
+	struct regulator_dev *ret;
+
+	conf->of_node = of_get_child_by_name(np, name);
+	if (!conf->of_node)
+		return;
+
+	desc->name = name;
+	desc->owner = THIS_MODULE;
+	desc->type = REGULATOR_CURRENT;
+	desc->ops = &mxs_lradc_regulator_current_ops;
+
+	conf->init_data = of_get_regulator_init_data(conf->dev, conf->of_node,
+						     desc);
+	ret = devm_regulator_register(conf->dev, desc, conf);
+	if (IS_ERR(ret))
+		/* Just pretend the regulator isn't there */
+		dev_err(conf->dev, "Failed to register regulator %s: %ld\n",
+			desc->name, PTR_ERR(ret));
+
+	of_node_put(conf->of_node);
+}
+
 static int mxs_lradc_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *of_id =
@@ -1603,6 +1748,7 @@ static int mxs_lradc_probe(struct platform_device *pdev)
 	struct mxs_lradc *lradc;
 	struct iio_dev *iio;
 	struct resource *iores;
+	struct regulator_config regconf;
 	int ret = 0, touch_ret;
 	int i, s;
 	u64 scale_uv;
@@ -1727,6 +1873,12 @@ static int mxs_lradc_probe(struct platform_device *pdev)
 		goto err_ts;
 	}
 
+	/* Setup regulator devices for current source. */
+	regconf.dev = dev;
+	regconf.driver_data = lradc;
+	mxs_lradc_reg_helper(node, "ISRC0", &regconf, &lradc->isrc0);
+	mxs_lradc_reg_helper(node, "ISRC1", &regconf, &lradc->isrc1);
+
 	return 0;
 
 err_ts:
-- 
2.1.4

  parent reply	other threads:[~2016-04-22 13:52 UTC|newest]

Thread overview: 54+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-04-22 13:52 [PATCH 0/3] mxs-lradc: Add support for current sources Harald Geyer
2016-04-22 13:52 ` Harald Geyer
2016-04-22 13:52 ` Harald Geyer
     [not found] ` <1461333147-11873-1-git-send-email-harald-95f8Dae0BrPYtjvyW6yDsg@public.gmane.org>
2016-04-22 13:52   ` Harald Geyer [this message]
2016-04-22 13:52     ` [PATCH 1/3] iio: mxs-lradc: Add regulators " Harald Geyer
2016-04-22 13:52     ` Harald Geyer
     [not found]     ` <1461333147-11873-2-git-send-email-harald-95f8Dae0BrPYtjvyW6yDsg@public.gmane.org>
2016-04-22 15:50       ` Marek Vasut
2016-04-22 15:50         ` Marek Vasut
2016-04-22 15:50         ` Marek Vasut
     [not found]         ` <571A4831.2020604-ynQEQJNshbs@public.gmane.org>
2016-04-22 17:00           ` Ksenija Stanojević
2016-04-22 17:00             ` Ksenija Stanojević
2016-04-22 17:00             ` Ksenija Stanojević
     [not found]             ` <CAL7P5jKYp0JrF2MHdLkLTBvgYb0KSDwPEMuNPGxcctcxCRnazg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2016-04-22 19:23               ` Harald Geyer
2016-04-22 19:23                 ` Harald Geyer
2016-04-22 19:23                 ` Harald Geyer
     [not found]                 ` <f4c00c5c2242f6a951f1f8d8eca56357-95f8Dae0BrPYtjvyW6yDsg@public.gmane.org>
2016-04-23 21:08                   ` Jonathan Cameron
2016-04-23 21:08                     ` Jonathan Cameron
2016-04-23 21:08                     ` Jonathan Cameron
2016-04-22 16:11       ` Harald Geyer
2016-04-22 16:11         ` Harald Geyer
2016-04-22 16:11         ` Harald Geyer
2016-05-03 11:07       ` Stefan Wahren
2016-05-03 11:07         ` Stefan Wahren
2016-05-03 11:07         ` Stefan Wahren
     [not found]         ` <57288674.9050601-eS4NqCHxEME@public.gmane.org>
2016-05-03 11:22           ` Harald Geyer
2016-05-03 11:22             ` Harald Geyer
2016-05-03 11:22             ` Harald Geyer
     [not found]             ` <6cdd4f858dacf72e192a292f29c12feb-95f8Dae0BrPYtjvyW6yDsg@public.gmane.org>
2016-05-04  7:15               ` Jonathan Cameron
2016-05-04  7:15                 ` Jonathan Cameron
2016-05-04  7:15                 ` Jonathan Cameron
     [not found]                 ` <1e7d5f3a-22c1-94d4-9620-7aa112602e39-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
2016-05-04 11:38                   ` Harald Geyer
2016-05-04 11:38                     ` Harald Geyer
2016-05-04 11:38                     ` Harald Geyer
2016-04-22 13:52   ` [PATCH 2/3] ARM: dts: imx23: Provide regulators for the current sources of the LRADC Harald Geyer
2016-04-22 13:52     ` Harald Geyer
2016-04-22 13:52     ` Harald Geyer
2016-04-22 13:52   ` [PATCH 3/3] ARM: dts: imx28: " Harald Geyer
2016-04-22 13:52     ` Harald Geyer
2016-04-22 13:52     ` Harald Geyer
2016-04-29 15:12   ` [PATCH 0/3] mxs-lradc: Add support for current sources Stefan Wahren
2016-04-29 15:12     ` Stefan Wahren
2016-04-29 15:12     ` Stefan Wahren
     [not found]     ` <572379F5.8000501-eS4NqCHxEME@public.gmane.org>
2016-04-29 17:45       ` Harald Geyer
2016-04-29 17:45         ` Harald Geyer
2016-04-29 17:45         ` Harald Geyer
2016-05-01 18:02         ` Jonathan Cameron
2016-05-01 18:02           ` Jonathan Cameron
2016-05-01 18:02           ` Jonathan Cameron
     [not found]           ` <ac57f3e9-f8f2-a6ef-52fa-0a13d7df5d4f-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
2016-05-02 12:25             ` Harald Geyer
2016-05-02 12:25               ` Harald Geyer
2016-05-02 12:25               ` Harald Geyer
2016-05-02 12:29             ` Stefan Wahren
2016-05-02 12:29               ` Stefan Wahren
2016-05-02 12:29               ` Stefan Wahren

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1461333147-11873-2-git-send-email-harald@ccbib.org \
    --to=harald-95f8dae0brpytjvyw6ydsg@public.gmane.org \
    --cc=devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org \
    --cc=kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org \
    --cc=linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
    --cc=linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=marex-ynQEQJNshbs@public.gmane.org \
    --cc=shawnguo-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org \
    --cc=stefan.wahren-eS4NqCHxEME@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.