[PATCH 1/3] iio: mxs-lradc: Add regulators for current sources

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




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@xxxxxxxxx>
---
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

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



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux