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

From: Jonathan Bakker
Date: Fri May 15 2020 - 22:27:14 EST


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@xxxxxxxxxx>
Signed-off-by: Jonathan Bakker <xc-racer2@xxxxxxx>
---
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 --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@xxxxxxx>
+L: linux-iio@xxxxxxxxxxxxxxx
+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@xxxxxxxxxxxx>
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@xxxxxxx>
+ */
+
+#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@xxxxxxx>");
+MODULE_LICENSE("GPL v2");
--
2.20.1