diff mbox series

[2/2] iio: adc: Add current-from-voltage driver

Message ID BN6PR04MB066086A09D671ACB0287B6BAA3BA0@BN6PR04MB0660.namprd04.prod.outlook.com (mailing list archive)
State New, archived
Headers show
Series iio: adc: Add a current from voltage driver | expand

Commit Message

Jonathan Bakker May 16, 2020, 2:26 a.m. UTC
Some devices may require a current adc, but only have a voltage
ADC onboard.  In order to read the current, they have a resistor
connected to the ADC.

Suggested-by: Jonathan Cameron <jic23@kernel.org>
Signed-off-by: Jonathan Bakker <xc-racer2@live.ca>
---
 MAINTAINERS                            |   8 ++
 drivers/iio/adc/Kconfig                |   9 ++
 drivers/iio/adc/Makefile               |   1 +
 drivers/iio/adc/current-from-voltage.c | 123 +++++++++++++++++++++++++
 4 files changed, 141 insertions(+)
 create mode 100644 drivers/iio/adc/current-from-voltage.c
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 2926327e4976..094cf512b403 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4503,6 +4503,14 @@  T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml
 F:	drivers/media/platform/sunxi/sun6i-csi/
 
+CURRENT ADC FROM VOLTAGE ADC DRIVER
+M:	Jonathan Bakker <xc-racer2@live.ca>
+L:	linux-iio@vger.kernel.org
+S:	Maintained
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git
+F:	Documentation/devicetree/bindings/iio/adc/linux,current-from-voltage.yaml
+F:	drivers/iio/adc/current-from-voltage.c
+
 CW1200 WLAN driver
 M:	Solomon Peachy <pizza@shaftnet.org>
 S:	Maintained
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 12bb8b7ca1ff..84e6ccb36024 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -344,6 +344,15 @@  config CPCAP_ADC
 	  This driver can also be built as a module. If so, the module will be
 	  called cpcap-adc.
 
+config CURRENT_FROM_VOLTAGE
+	tristate "Current from voltage shim driver"
+	help
+	  Say yes here to build support for a shim driver converting a voltage
+	  ADC coupled with a resistor to a current ADC.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called current-from-voltage.
+
 config DA9150_GPADC
 	tristate "Dialog DA9150 GPADC driver support"
 	depends on MFD_DA9150
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 637807861112..d293184fc32a 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -35,6 +35,7 @@  obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
 obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
 obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
 obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
+obj-$(CONFIG_CURRENT_FROM_VOLTAGE) += current-from-voltage.o
 obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
 obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
 obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
diff --git a/drivers/iio/adc/current-from-voltage.c b/drivers/iio/adc/current-from-voltage.c
new file mode 100644
index 000000000000..69cb18e0995b
--- /dev/null
+++ b/drivers/iio/adc/current-from-voltage.c
@@ -0,0 +1,123 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for converting a resistor and a voltage ADC to a current ADC
+ *
+ * Copyright (C) 2020 Jonathan Bakker <xc-racer2@live.ca>
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+struct shim {
+	struct iio_channel *adc;
+	u32 resistor_value;
+};
+
+static int shim_read_raw(struct iio_dev *indio_dev,
+				      struct iio_chan_spec const *chan,
+				      int *val, int *val2, long mask)
+{
+	struct shim *shim = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = iio_read_channel_processed(shim->adc, val);
+		if (ret < 0) {
+			dev_err(&indio_dev->dev, "fail reading voltage ADC\n");
+			return ret;
+		}
+
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 1;
+		*val2 = shim->resistor_value;
+
+		return IIO_VAL_FRACTIONAL;
+	}
+
+	return -EINVAL;
+}
+
+static const struct iio_chan_spec shim_iio_channel = {
+	.type = IIO_CURRENT,
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+		| BIT(IIO_CHAN_INFO_SCALE),
+};
+
+static const struct iio_info shim_info = {
+	.read_raw = &shim_read_raw,
+};
+
+static int shim_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct iio_dev *indio_dev;
+	struct shim *shim;
+	enum iio_chan_type type;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*shim));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, indio_dev);
+	shim = iio_priv(indio_dev);
+
+	indio_dev->name = dev_name(dev);
+	indio_dev->dev.parent = dev;
+	indio_dev->dev.of_node = dev->of_node;
+	indio_dev->info = &shim_info;
+	indio_dev->channels = &shim_iio_channel;
+	indio_dev->num_channels = 1;
+
+	shim->adc = devm_iio_channel_get(dev, "adc");
+	if (IS_ERR(shim->adc)) {
+		if (PTR_ERR(shim->adc) != -EPROBE_DEFER)
+			dev_err(dev, "failed to get adc input channel\n");
+		return PTR_ERR(shim->adc);
+	}
+
+	ret = iio_get_channel_type(shim->adc, &type);
+	if (ret < 0)
+		return ret;
+
+	if (type != IIO_VOLTAGE) {
+		dev_err(dev, "ADC is of the wrong type\n");
+		return -EINVAL;
+	}
+
+	ret = device_property_read_u32(dev, "linux,resistor-ohms",
+				      &shim->resistor_value);
+	if (ret < 0) {
+		dev_err(dev, "no resistor value found\n");
+		return ret;
+	}
+
+	return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id shim_match[] = {
+	{ .compatible = "linux,current-from-voltage", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, shim_match);
+
+static struct platform_driver shim_driver = {
+	.probe = shim_probe,
+	.driver = {
+		.name = "current-from-voltage",
+		.of_match_table = shim_match,
+	},
+};
+module_platform_driver(shim_driver);
+
+MODULE_DESCRIPTION("Current ADC from voltage ADC and resistor");
+MODULE_AUTHOR("Jonathan Bakker <xc-racer2@live.ca>");
+MODULE_LICENSE("GPL v2");