All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] iio: adc: Add support for DLN2 ADC
       [not found] <b5ede0cc2cb36695b86edf44c0dddbc4736a6420>
@ 2017-06-22 22:31 ` Jack Andersen
  2017-06-23  7:25   ` Peter Meerwald-Stadler
  2017-06-28  0:53 ` [PATCH v3] " Jack Andersen
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Jack Andersen @ 2017-06-22 22:31 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: Jack Andersen, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler

This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
ADC is the fourth and final component of the DLN2 for the kernel.

Signed-off-by: Jack Andersen <jackoalan@gmail.com>
---
 drivers/iio/adc/Kconfig    |   9 +
 drivers/iio/adc/Makefile   |   1 +
 drivers/iio/adc/dln2-adc.c | 644 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/mfd/dln2.c         |  12 +
 4 files changed, 666 insertions(+)
 create mode 100644 drivers/iio/adc/dln2-adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 401f47b..78d7455 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -239,6 +239,15 @@ config DA9150_GPADC
 	  To compile this driver as a module, choose M here: the module will be
 	  called berlin2-adc.
 
+config DLN2_ADC
+	tristate "Diolan DLN-2 ADC driver support"
+	depends on MFD_DLN2
+	help
+	  Say yes here to build support for Diolan DLN-2 ADC.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called adc_dln2.
+
 config ENVELOPE_DETECTOR
 	tristate "Envelope detector using a DAC and a comparator"
 	depends on OF
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 9339bec..378bc65 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
 obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
 obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
 obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
+obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
 obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
 obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
new file mode 100644
index 0000000..6c58b76
--- /dev/null
+++ b/drivers/iio/adc/dln2-adc.c
@@ -0,0 +1,644 @@
+/*
+ * Driver for the Diolan DLN-2 USB-ADC adapter
+ *
+ * Copyright (c) 2017 Jack Andersen
+ *
+ * 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, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/dln2.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+
+#define DLN2_ADC_MOD_NAME "dln2-adc"
+
+#define DLN2_ADC_ID             0x06
+
+#define DLN2_ADC_GET_CHANNEL_COUNT	DLN2_CMD(0x01, DLN2_ADC_ID)
+#define DLN2_ADC_ENABLE			DLN2_CMD(0x02, DLN2_ADC_ID)
+#define DLN2_ADC_DISABLE		DLN2_CMD(0x03, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_ENABLE		DLN2_CMD(0x05, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_DISABLE	DLN2_CMD(0x06, DLN2_ADC_ID)
+#define DLN2_ADC_SET_RESOLUTION		DLN2_CMD(0x08, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_VAL	DLN2_CMD(0x0A, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_ALL_VAL	DLN2_CMD(0x0B, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_SET_CFG	DLN2_CMD(0x0C, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_CFG	DLN2_CMD(0x0D, DLN2_ADC_ID)
+#define DLN2_ADC_CONDITION_MET_EV	DLN2_CMD(0x10, DLN2_ADC_ID)
+
+#define DLN2_ADC_EVENT_NONE		0
+#define DLN2_ADC_EVENT_BELOW		1
+#define DLN2_ADC_EVENT_LEVEL_ABOVE	2
+#define DLN2_ADC_EVENT_OUTSIDE		3
+#define DLN2_ADC_EVENT_INSIDE		4
+#define DLN2_ADC_EVENT_ALWAYS		5
+
+#define DLN2_ADC_MAX_CHANNELS 8
+#define DLN2_ADC_DATA_BITS 10
+
+struct dln2_adc {
+	struct platform_device *pdev;
+	int port;
+	struct iio_trigger *trig;
+	struct mutex mutex;
+	/* Set once initialized */
+	bool port_enabled;
+	/* Set once resolution request made to HW */
+	bool resolution_set;
+	/* Bitmask requesting enabled channels */
+	unsigned long chans_requested;
+	/* Bitmask indicating enabled channels on HW */
+	unsigned long chans_enabled;
+	/* Channel that is arbitrated for event trigger */
+	int trigger_chan;
+};
+
+struct dln2_adc_port_chan {
+	u8 port;
+	u8 chan;
+};
+
+struct dln2_adc_get_all_vals {
+	__le16 channel_mask;
+	__le16 values[DLN2_ADC_MAX_CHANNELS];
+};
+
+static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
+{
+	int ret;
+	u8 port = dln2->port;
+	u8 count;
+	int olen = sizeof(count);
+
+	ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
+			    &port, sizeof(port), &count, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+	if (olen < sizeof(count))
+		return -EPROTO;
+
+	return count;
+}
+
+static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
+{
+	int ret;
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = DLN2_ADC_DATA_BITS,
+	};
+
+	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
+			       &port_chan, sizeof(port_chan));
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
+				     int channel, bool enable)
+{
+	int ret;
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = channel,
+	};
+
+	u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
+
+	ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable)
+{
+	int ret;
+	u8 port = dln2->port;
+	__le16 conflict;
+	int olen = sizeof(conflict);
+
+	u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
+
+	ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
+			    &conflict, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
+			__func__, (int)enable);
+		return ret;
+	}
+	if (enable && olen < sizeof(conflict))
+		return -EPROTO;
+
+	return 0;
+}
+
+/*
+ * ADC channels are lazily enabled due to the pins being shared with GPIO
+ * channels. Enabling channels requires taking the ADC port offline, specifying
+ * the resolution, individually enabling channels, then putting the port back
+ * online. If GPIO pins have already been exported by gpio_dln2, EINVAL is
+ * reported.
+ */
+static int dln2_adc_update_enabled_chans(struct dln2_adc *dln2)
+{
+	int ret, i, chan_count;
+	struct iio_dev *indio_dev;
+
+	if (dln2->chans_enabled == dln2->chans_requested)
+		return 0;
+
+	indio_dev = platform_get_drvdata(dln2->pdev);
+	chan_count = indio_dev->num_channels;
+
+	if (dln2->port_enabled) {
+		ret = dln2_adc_set_port_enabled(dln2, false);
+		if (ret < 0)
+			return ret;
+		dln2->port_enabled = false;
+	}
+
+	if (!dln2->resolution_set) {
+		ret = dln2_adc_set_port_resolution(dln2);
+		if (ret < 0)
+			return ret;
+		dln2->resolution_set = true;
+	}
+
+	for (i = 0; i < chan_count; ++i) {
+		bool requested = dln2->chans_requested & (1 << i);
+		bool enabled = dln2->chans_enabled & (1 << i);
+
+		if (requested == enabled)
+			continue;
+		ret = dln2_adc_set_chan_enabled(dln2, i, requested);
+		if (ret < 0)
+			return ret;
+	}
+
+	dln2->chans_enabled = dln2->chans_requested;
+
+	ret = dln2_adc_set_port_enabled(dln2, true);
+	if (ret < 0)
+		return ret;
+	dln2->port_enabled = true;
+
+	return 0;
+}
+
+static int dln2_adc_get_chan_freq(struct dln2_adc *dln2, unsigned int channel)
+{
+	int ret;
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = channel,
+	};
+	struct {
+		__u8 type;
+		__le16 period;
+		__le16 low;
+		__le16 high;
+	} __packed get_cfg;
+	int olen = sizeof(get_cfg);
+
+	ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_CFG,
+			    &port_chan, sizeof(port_chan), &get_cfg, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+	if (olen < sizeof(get_cfg))
+		return -EPROTO;
+
+	return get_cfg.period;
+}
+
+static int dln2_adc_set_chan_freq(struct dln2_adc *dln2, unsigned int channel,
+				  unsigned int freq)
+{
+	int ret;
+	struct {
+		struct dln2_adc_port_chan port_chan;
+		__u8 type;
+		__le16 period;
+		__le16 low;
+		__le16 high;
+	} __packed set_cfg = {
+		.port_chan.port = dln2->port,
+		.port_chan.chan = channel,
+		.type = freq ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
+		.period = cpu_to_le16(freq)
+	};
+
+	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
+			       &set_cfg, sizeof(set_cfg));
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+	return 0;
+}
+
+static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
+{
+	int ret;
+	int old_chans_requested = dln2->chans_requested;
+
+	dln2->chans_requested |= (1 << channel);
+	ret = dln2_adc_update_enabled_chans(dln2);
+	if (ret < 0) {
+		dln2->chans_requested = old_chans_requested;
+		return ret;
+	}
+	dln2->port_enabled = true;
+
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = channel,
+	};
+	__le16 value;
+	int olen = sizeof(value);
+
+	ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
+			    &port_chan, sizeof(port_chan), &value, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+	if (olen < sizeof(value))
+		return -EPROTO;
+
+	return le16_to_cpu(value);
+}
+
+static int dln2_adc_read_all(struct dln2_adc *dln2,
+			     struct dln2_adc_get_all_vals *get_all_vals)
+{
+	int ret;
+	__u8 port = dln2->port;
+	int olen = sizeof(*get_all_vals);
+
+	ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
+			    &port, sizeof(port), get_all_vals, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+	if (olen < sizeof(*get_all_vals))
+		return -EPROTO;
+
+	return 0;
+}
+
+static int dln2_adc_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int *val,
+			     int *val2,
+			     long mask)
+{
+	int ret;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&dln2->mutex);
+		ret = dln2_adc_read(dln2, chan->channel);
+		mutex_unlock(&dln2->mutex);
+
+		if (ret < 0)
+			return ret;
+
+		*val = ret;
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		/* 3.3 / (1 << 10) * 1000000000 */
+		*val2 = 3222656;
+		return IIO_VAL_INT_PLUS_NANO;
+
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		mutex_lock(&dln2->mutex);
+		if (dln2->trigger_chan == -1)
+			ret = 0;
+		else
+			ret = dln2_adc_get_chan_freq(dln2, dln2->trigger_chan);
+		mutex_unlock(&dln2->mutex);
+
+		if (ret < 0)
+			return ret;
+
+		*val = ret / 1000;
+		*val2 = (ret % 1000) * 1000;
+		return IIO_VAL_INT_PLUS_MICRO;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int dln2_adc_write_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int val,
+			      int val2,
+			      long mask)
+{
+	int ret;
+	unsigned int freq;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		freq = val * 1000 + val2 / 1000;
+		if (freq > 65535) {
+			freq = 65535;
+			dev_warn(&dln2->pdev->dev,
+				 "clamping freq to 65535ms\n");
+		}
+		mutex_lock(&dln2->mutex);
+
+		/*
+		 * The first requested channel is arbitrated as a shared
+		 * trigger source, so only one event is registered with the DLN.
+		 * The event handler will then read all enabled channel values
+		 * using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
+		 * synchronization between ADC readings.
+		 */
+		if (dln2->trigger_chan == -1)
+			dln2->trigger_chan = chan->channel;
+		ret = dln2_adc_set_chan_freq(dln2, dln2->trigger_chan, freq);
+
+		mutex_unlock(&dln2->mutex);
+
+		if (ret < 0)
+			return ret;
+
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+#define DLN2_ADC_CHAN(idx) {					\
+	.type = IIO_VOLTAGE,					\
+	.channel = idx,						\
+	.indexed = 1,						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |	\
+				   BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+	.scan_index = idx,					\
+	.scan_type.sign = 'u',					\
+	.scan_type.realbits = DLN2_ADC_DATA_BITS,		\
+	.scan_type.storagebits = 16,				\
+	.scan_type.endianness = IIO_LE,				\
+}
+
+static const struct iio_chan_spec dln2_adc_iio_channels[] = {
+	DLN2_ADC_CHAN(0),
+	DLN2_ADC_CHAN(1),
+	DLN2_ADC_CHAN(2),
+	DLN2_ADC_CHAN(3),
+	DLN2_ADC_CHAN(4),
+	DLN2_ADC_CHAN(5),
+	DLN2_ADC_CHAN(6),
+	DLN2_ADC_CHAN(7),
+	IIO_CHAN_SOFT_TIMESTAMP(8),
+};
+
+static const struct iio_info dln2_adc_info = {
+	.read_raw = dln2_adc_read_raw,
+	.write_raw = dln2_adc_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct {
+		__le16 values[DLN2_ADC_MAX_CHANNELS];
+		int64_t timestamp_space;
+	} data;
+	struct dln2_adc_get_all_vals dev_data;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+	int old_chans_requested;
+	int i, j;
+
+	mutex_lock(&dln2->mutex);
+
+	old_chans_requested = dln2->chans_requested;
+	dln2->chans_requested |= *indio_dev->active_scan_mask;
+	if (dln2_adc_update_enabled_chans(dln2) < 0) {
+		dln2->chans_requested = old_chans_requested;
+		mutex_unlock(&dln2->mutex);
+		goto done;
+	}
+
+	if (dln2_adc_read_all(dln2, &dev_data) < 0) {
+		mutex_unlock(&dln2->mutex);
+		goto done;
+	}
+
+	mutex_unlock(&dln2->mutex);
+
+	for (i = 0, j = 0;
+	     i < bitmap_weight(indio_dev->active_scan_mask,
+			       indio_dev->masklength); i++, j++) {
+		j = find_next_bit(indio_dev->active_scan_mask,
+				  indio_dev->masklength, j);
+		data.values[i] = dev_data.values[j];
+	}
+
+	iio_push_to_buffers_with_timestamp(indio_dev, &data,
+					   iio_get_time_ns(indio_dev));
+
+done:
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	mutex_lock(&dln2->mutex);
+	dln2->chans_requested |= *indio_dev->active_scan_mask;
+	ret = dln2_adc_update_enabled_chans(dln2);
+	mutex_unlock(&dln2->mutex);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+
+	return iio_triggered_buffer_postenable(indio_dev);
+}
+
+static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
+	.postenable = dln2_adc_triggered_buffer_postenable,
+	.predisable = iio_triggered_buffer_predisable,
+};
+
+static void dln2_adc_event(struct platform_device *pdev, u16 echo,
+			   const void *data, int len)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	iio_trigger_poll(dln2->trig);
+}
+
+static const struct iio_trigger_ops dln2_adc_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+static int dln2_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct dln2_adc *dln2;
+	struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	struct iio_dev *indio_dev;
+	struct iio_buffer *buffer;
+	int ret;
+	int chans;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(struct dln2_adc));
+	if (!indio_dev) {
+		dev_err(dev, "failed allocating iio device\n");
+		return -ENOMEM;
+	}
+
+	dln2 = iio_priv(indio_dev);
+	dln2->pdev = pdev;
+	dln2->port = pdata->port;
+	mutex_init(&dln2->mutex);
+	dln2->port_enabled = false;
+	dln2->resolution_set = false;
+	dln2->chans_requested = 0;
+	dln2->chans_enabled = 0;
+	dln2->trigger_chan = -1;
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	chans = dln2_adc_get_chan_count(dln2);
+	if (chans < 0) {
+		dev_err(dev, "failed to get channel count: %d\n", chans);
+		ret = chans;
+		goto dealloc_dev;
+	}
+	if (chans > DLN2_ADC_MAX_CHANNELS) {
+		chans = DLN2_ADC_MAX_CHANNELS;
+		dev_warn(dev, "clamping channels to %d\n",
+			 DLN2_ADC_MAX_CHANNELS);
+	}
+
+	indio_dev->name = DLN2_ADC_MOD_NAME;
+	indio_dev->dev.parent = dev;
+	indio_dev->info = &dln2_adc_info;
+	indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
+	indio_dev->channels = dln2_adc_iio_channels;
+	indio_dev->num_channels = chans + 1;
+	indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
+
+	dln2->trig = devm_iio_trigger_alloc(dev, "samplerate");
+	if (!dln2->trig) {
+		dev_err(dev, "failed to allocate trigger\n");
+		ret = -ENOMEM;
+		goto dealloc_dev;
+	}
+	dln2->trig->ops = &dln2_adc_trigger_ops;
+	iio_trigger_set_drvdata(dln2->trig, dln2);
+	iio_trigger_register(dln2->trig);
+	iio_trigger_set_immutable(indio_dev, dln2->trig);
+
+	buffer = devm_iio_kfifo_allocate(dev);
+	if (!buffer) {
+		dev_err(dev, "failed to allocate kfifo\n");
+		ret = -ENOMEM;
+		goto dealloc_trigger;
+	}
+
+	iio_device_attach_buffer(indio_dev, buffer);
+
+	indio_dev->pollfunc = iio_alloc_pollfunc(NULL,
+						 &dln2_adc_trigger_h,
+						 IRQF_ONESHOT,
+						 indio_dev,
+						 "samplerate");
+
+	if (!indio_dev->pollfunc) {
+		ret = -ENOMEM;
+		goto dealloc_kfifo;
+	}
+
+	ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
+				     dln2_adc_event);
+	if (ret) {
+		dev_err(dev, "failed to register event cb: %d\n", ret);
+		goto dealloc_pollfunc;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(dev, "failed to register iio device: %d\n", ret);
+		goto dealloc_pollfunc;
+	}
+
+	return 0;
+
+dealloc_pollfunc:
+	iio_dealloc_pollfunc(indio_dev->pollfunc);
+dealloc_kfifo:
+dealloc_trigger:
+	iio_trigger_unregister(dln2->trig);
+dealloc_dev:
+
+	return ret;
+}
+
+static int dln2_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
+	iio_trigger_unregister(dln2->trig);
+	iio_dealloc_pollfunc(indio_dev->pollfunc);
+
+	return 0;
+}
+
+static struct platform_driver dln2_adc_driver = {
+	.driver.name	= DLN2_ADC_MOD_NAME,
+	.probe		= dln2_adc_probe,
+	.remove		= dln2_adc_remove,
+};
+
+module_platform_driver(dln2_adc_driver);
+
+MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
+MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dln2-adc");
diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
index 704e189..a22ab8c 100644
--- a/drivers/mfd/dln2.c
+++ b/drivers/mfd/dln2.c
@@ -53,6 +53,7 @@ enum dln2_handle {
 	DLN2_HANDLE_GPIO,
 	DLN2_HANDLE_I2C,
 	DLN2_HANDLE_SPI,
+	DLN2_HANDLE_ADC,
 	DLN2_HANDLES
 };
 
@@ -663,6 +664,12 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
 	.port = 0,
 };
 
+/* Only one ADC port supported */
+static struct dln2_platform_data dln2_pdata_adc = {
+	.handle = DLN2_HANDLE_ADC,
+	.port = 0,
+};
+
 static const struct mfd_cell dln2_devs[] = {
 	{
 		.name = "dln2-gpio",
@@ -679,6 +686,11 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
 		.platform_data = &dln2_pdata_spi,
 		.pdata_size = sizeof(struct dln2_platform_data),
 	},
+	{
+		.name = "dln2-adc",
+		.platform_data = &dln2_pdata_adc,
+		.pdata_size = sizeof(struct dln2_platform_data),
+	},
 };
 
 static void dln2_stop(struct dln2_dev *dln2)
-- 
1.9.1

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

* Re: [PATCH v2] iio: adc: Add support for DLN2 ADC
  2017-06-22 22:31 ` [PATCH v2] iio: adc: Add support for DLN2 ADC Jack Andersen
@ 2017-06-23  7:25   ` Peter Meerwald-Stadler
       [not found]     ` <20170624203801.7e982ad0@kernel.org>
  0 siblings, 1 reply; 23+ messages in thread
From: Peter Meerwald-Stadler @ 2017-06-23  7:25 UTC (permalink / raw)
  To: Jack Andersen
  Cc: linux-iio, linux-kernel, Jonathan Cameron, Lars-Peter Clausen


> This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
> ADC is the fourth and final component of the DLN2 for the kernel.

I am missing a changelog or information if and how review comments have 
been addressed

thanks for the super-fast turnaround, anyway

p.

> Signed-off-by: Jack Andersen <jackoalan@gmail.com>
> ---
>  drivers/iio/adc/Kconfig    |   9 +
>  drivers/iio/adc/Makefile   |   1 +
>  drivers/iio/adc/dln2-adc.c | 644 +++++++++++++++++++++++++++++++++++++++++++++
>  drivers/mfd/dln2.c         |  12 +
>  4 files changed, 666 insertions(+)
>  create mode 100644 drivers/iio/adc/dln2-adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 401f47b..78d7455 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -239,6 +239,15 @@ config DA9150_GPADC
>  	  To compile this driver as a module, choose M here: the module will be
>  	  called berlin2-adc.
>  
> +config DLN2_ADC
> +	tristate "Diolan DLN-2 ADC driver support"
> +	depends on MFD_DLN2
> +	help
> +	  Say yes here to build support for Diolan DLN-2 ADC.
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called adc_dln2.
> +
>  config ENVELOPE_DETECTOR
>  	tristate "Envelope detector using a DAC and a comparator"
>  	depends on OF
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 9339bec..378bc65 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>  obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>  obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
>  obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
> +obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
>  obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
> diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
> new file mode 100644
> index 0000000..6c58b76
> --- /dev/null
> +++ b/drivers/iio/adc/dln2-adc.c
> @@ -0,0 +1,644 @@
> +/*
> + * Driver for the Diolan DLN-2 USB-ADC adapter
> + *
> + * Copyright (c) 2017 Jack Andersen
> + *
> + * 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, version 2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/platform_device.h>
> +#include <linux/mfd/dln2.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/kfifo_buf.h>
> +
> +#define DLN2_ADC_MOD_NAME "dln2-adc"
> +
> +#define DLN2_ADC_ID             0x06
> +
> +#define DLN2_ADC_GET_CHANNEL_COUNT	DLN2_CMD(0x01, DLN2_ADC_ID)
> +#define DLN2_ADC_ENABLE			DLN2_CMD(0x02, DLN2_ADC_ID)
> +#define DLN2_ADC_DISABLE		DLN2_CMD(0x03, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_ENABLE		DLN2_CMD(0x05, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_DISABLE	DLN2_CMD(0x06, DLN2_ADC_ID)
> +#define DLN2_ADC_SET_RESOLUTION		DLN2_CMD(0x08, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_GET_VAL	DLN2_CMD(0x0A, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_GET_ALL_VAL	DLN2_CMD(0x0B, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_SET_CFG	DLN2_CMD(0x0C, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_GET_CFG	DLN2_CMD(0x0D, DLN2_ADC_ID)
> +#define DLN2_ADC_CONDITION_MET_EV	DLN2_CMD(0x10, DLN2_ADC_ID)
> +
> +#define DLN2_ADC_EVENT_NONE		0
> +#define DLN2_ADC_EVENT_BELOW		1
> +#define DLN2_ADC_EVENT_LEVEL_ABOVE	2
> +#define DLN2_ADC_EVENT_OUTSIDE		3
> +#define DLN2_ADC_EVENT_INSIDE		4
> +#define DLN2_ADC_EVENT_ALWAYS		5
> +
> +#define DLN2_ADC_MAX_CHANNELS 8
> +#define DLN2_ADC_DATA_BITS 10
> +
> +struct dln2_adc {
> +	struct platform_device *pdev;
> +	int port;
> +	struct iio_trigger *trig;
> +	struct mutex mutex;
> +	/* Set once initialized */
> +	bool port_enabled;
> +	/* Set once resolution request made to HW */
> +	bool resolution_set;
> +	/* Bitmask requesting enabled channels */
> +	unsigned long chans_requested;
> +	/* Bitmask indicating enabled channels on HW */
> +	unsigned long chans_enabled;
> +	/* Channel that is arbitrated for event trigger */
> +	int trigger_chan;
> +};
> +
> +struct dln2_adc_port_chan {
> +	u8 port;
> +	u8 chan;
> +};
> +
> +struct dln2_adc_get_all_vals {
> +	__le16 channel_mask;
> +	__le16 values[DLN2_ADC_MAX_CHANNELS];
> +};
> +
> +static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
> +{
> +	int ret;
> +	u8 port = dln2->port;
> +	u8 count;
> +	int olen = sizeof(count);
> +
> +	ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
> +			    &port, sizeof(port), &count, &olen);
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +	if (olen < sizeof(count))
> +		return -EPROTO;
> +
> +	return count;
> +}
> +
> +static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
> +{
> +	int ret;
> +	struct dln2_adc_port_chan port_chan = {
> +		.port = dln2->port,
> +		.chan = DLN2_ADC_DATA_BITS,
> +	};
> +
> +	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
> +			       &port_chan, sizeof(port_chan));
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
> +				     int channel, bool enable)
> +{
> +	int ret;
> +	struct dln2_adc_port_chan port_chan = {
> +		.port = dln2->port,
> +		.chan = channel,
> +	};
> +
> +	u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
> +
> +	ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable)
> +{
> +	int ret;
> +	u8 port = dln2->port;
> +	__le16 conflict;
> +	int olen = sizeof(conflict);
> +
> +	u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
> +
> +	ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
> +			    &conflict, &olen);
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
> +			__func__, (int)enable);
> +		return ret;
> +	}
> +	if (enable && olen < sizeof(conflict))
> +		return -EPROTO;
> +
> +	return 0;
> +}
> +
> +/*
> + * ADC channels are lazily enabled due to the pins being shared with GPIO
> + * channels. Enabling channels requires taking the ADC port offline, specifying
> + * the resolution, individually enabling channels, then putting the port back
> + * online. If GPIO pins have already been exported by gpio_dln2, EINVAL is
> + * reported.
> + */
> +static int dln2_adc_update_enabled_chans(struct dln2_adc *dln2)
> +{
> +	int ret, i, chan_count;
> +	struct iio_dev *indio_dev;
> +
> +	if (dln2->chans_enabled == dln2->chans_requested)
> +		return 0;
> +
> +	indio_dev = platform_get_drvdata(dln2->pdev);
> +	chan_count = indio_dev->num_channels;
> +
> +	if (dln2->port_enabled) {
> +		ret = dln2_adc_set_port_enabled(dln2, false);
> +		if (ret < 0)
> +			return ret;
> +		dln2->port_enabled = false;
> +	}
> +
> +	if (!dln2->resolution_set) {
> +		ret = dln2_adc_set_port_resolution(dln2);
> +		if (ret < 0)
> +			return ret;
> +		dln2->resolution_set = true;
> +	}
> +
> +	for (i = 0; i < chan_count; ++i) {
> +		bool requested = dln2->chans_requested & (1 << i);
> +		bool enabled = dln2->chans_enabled & (1 << i);
> +
> +		if (requested == enabled)
> +			continue;
> +		ret = dln2_adc_set_chan_enabled(dln2, i, requested);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	dln2->chans_enabled = dln2->chans_requested;
> +
> +	ret = dln2_adc_set_port_enabled(dln2, true);
> +	if (ret < 0)
> +		return ret;
> +	dln2->port_enabled = true;
> +
> +	return 0;
> +}
> +
> +static int dln2_adc_get_chan_freq(struct dln2_adc *dln2, unsigned int channel)
> +{
> +	int ret;
> +	struct dln2_adc_port_chan port_chan = {
> +		.port = dln2->port,
> +		.chan = channel,
> +	};
> +	struct {
> +		__u8 type;
> +		__le16 period;
> +		__le16 low;
> +		__le16 high;
> +	} __packed get_cfg;
> +	int olen = sizeof(get_cfg);
> +
> +	ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_CFG,
> +			    &port_chan, sizeof(port_chan), &get_cfg, &olen);
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +	if (olen < sizeof(get_cfg))
> +		return -EPROTO;
> +
> +	return get_cfg.period;
> +}
> +
> +static int dln2_adc_set_chan_freq(struct dln2_adc *dln2, unsigned int channel,
> +				  unsigned int freq)
> +{
> +	int ret;
> +	struct {
> +		struct dln2_adc_port_chan port_chan;
> +		__u8 type;
> +		__le16 period;
> +		__le16 low;
> +		__le16 high;
> +	} __packed set_cfg = {
> +		.port_chan.port = dln2->port,
> +		.port_chan.chan = channel,
> +		.type = freq ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
> +		.period = cpu_to_le16(freq)
> +	};
> +
> +	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
> +			       &set_cfg, sizeof(set_cfg));
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
> +{
> +	int ret;
> +	int old_chans_requested = dln2->chans_requested;
> +
> +	dln2->chans_requested |= (1 << channel);
> +	ret = dln2_adc_update_enabled_chans(dln2);
> +	if (ret < 0) {
> +		dln2->chans_requested = old_chans_requested;
> +		return ret;
> +	}
> +	dln2->port_enabled = true;
> +
> +	struct dln2_adc_port_chan port_chan = {
> +		.port = dln2->port,
> +		.chan = channel,
> +	};
> +	__le16 value;
> +	int olen = sizeof(value);
> +
> +	ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
> +			    &port_chan, sizeof(port_chan), &value, &olen);
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +	if (olen < sizeof(value))
> +		return -EPROTO;
> +
> +	return le16_to_cpu(value);
> +}
> +
> +static int dln2_adc_read_all(struct dln2_adc *dln2,
> +			     struct dln2_adc_get_all_vals *get_all_vals)
> +{
> +	int ret;
> +	__u8 port = dln2->port;
> +	int olen = sizeof(*get_all_vals);
> +
> +	ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
> +			    &port, sizeof(port), get_all_vals, &olen);
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +	if (olen < sizeof(*get_all_vals))
> +		return -EPROTO;
> +
> +	return 0;
> +}
> +
> +static int dln2_adc_read_raw(struct iio_dev *indio_dev,
> +			     struct iio_chan_spec const *chan,
> +			     int *val,
> +			     int *val2,
> +			     long mask)
> +{
> +	int ret;
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		mutex_lock(&dln2->mutex);
> +		ret = dln2_adc_read(dln2, chan->channel);
> +		mutex_unlock(&dln2->mutex);
> +
> +		if (ret < 0)
> +			return ret;
> +
> +		*val = ret;
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = 0;
> +		/* 3.3 / (1 << 10) * 1000000000 */
> +		*val2 = 3222656;
> +		return IIO_VAL_INT_PLUS_NANO;
> +
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		mutex_lock(&dln2->mutex);
> +		if (dln2->trigger_chan == -1)
> +			ret = 0;
> +		else
> +			ret = dln2_adc_get_chan_freq(dln2, dln2->trigger_chan);
> +		mutex_unlock(&dln2->mutex);
> +
> +		if (ret < 0)
> +			return ret;
> +
> +		*val = ret / 1000;
> +		*val2 = (ret % 1000) * 1000;
> +		return IIO_VAL_INT_PLUS_MICRO;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int dln2_adc_write_raw(struct iio_dev *indio_dev,
> +			      struct iio_chan_spec const *chan,
> +			      int val,
> +			      int val2,
> +			      long mask)
> +{
> +	int ret;
> +	unsigned int freq;
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		freq = val * 1000 + val2 / 1000;
> +		if (freq > 65535) {
> +			freq = 65535;
> +			dev_warn(&dln2->pdev->dev,
> +				 "clamping freq to 65535ms\n");
> +		}
> +		mutex_lock(&dln2->mutex);
> +
> +		/*
> +		 * The first requested channel is arbitrated as a shared
> +		 * trigger source, so only one event is registered with the DLN.
> +		 * The event handler will then read all enabled channel values
> +		 * using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
> +		 * synchronization between ADC readings.
> +		 */
> +		if (dln2->trigger_chan == -1)
> +			dln2->trigger_chan = chan->channel;
> +		ret = dln2_adc_set_chan_freq(dln2, dln2->trigger_chan, freq);
> +
> +		mutex_unlock(&dln2->mutex);
> +
> +		if (ret < 0)
> +			return ret;
> +
> +		return 0;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +#define DLN2_ADC_CHAN(idx) {					\
> +	.type = IIO_VOLTAGE,					\
> +	.channel = idx,						\
> +	.indexed = 1,						\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
> +	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |	\
> +				   BIT(IIO_CHAN_INFO_SAMP_FREQ),\
> +	.scan_index = idx,					\
> +	.scan_type.sign = 'u',					\
> +	.scan_type.realbits = DLN2_ADC_DATA_BITS,		\
> +	.scan_type.storagebits = 16,				\
> +	.scan_type.endianness = IIO_LE,				\
> +}
> +
> +static const struct iio_chan_spec dln2_adc_iio_channels[] = {
> +	DLN2_ADC_CHAN(0),
> +	DLN2_ADC_CHAN(1),
> +	DLN2_ADC_CHAN(2),
> +	DLN2_ADC_CHAN(3),
> +	DLN2_ADC_CHAN(4),
> +	DLN2_ADC_CHAN(5),
> +	DLN2_ADC_CHAN(6),
> +	DLN2_ADC_CHAN(7),
> +	IIO_CHAN_SOFT_TIMESTAMP(8),
> +};
> +
> +static const struct iio_info dln2_adc_info = {
> +	.read_raw = dln2_adc_read_raw,
> +	.write_raw = dln2_adc_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct {
> +		__le16 values[DLN2_ADC_MAX_CHANNELS];
> +		int64_t timestamp_space;
> +	} data;
> +	struct dln2_adc_get_all_vals dev_data;
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +	int old_chans_requested;
> +	int i, j;
> +
> +	mutex_lock(&dln2->mutex);
> +
> +	old_chans_requested = dln2->chans_requested;
> +	dln2->chans_requested |= *indio_dev->active_scan_mask;
> +	if (dln2_adc_update_enabled_chans(dln2) < 0) {
> +		dln2->chans_requested = old_chans_requested;
> +		mutex_unlock(&dln2->mutex);
> +		goto done;
> +	}
> +
> +	if (dln2_adc_read_all(dln2, &dev_data) < 0) {
> +		mutex_unlock(&dln2->mutex);
> +		goto done;
> +	}
> +
> +	mutex_unlock(&dln2->mutex);
> +
> +	for (i = 0, j = 0;
> +	     i < bitmap_weight(indio_dev->active_scan_mask,
> +			       indio_dev->masklength); i++, j++) {
> +		j = find_next_bit(indio_dev->active_scan_mask,
> +				  indio_dev->masklength, j);
> +		data.values[i] = dev_data.values[j];
> +	}
> +
> +	iio_push_to_buffers_with_timestamp(indio_dev, &data,
> +					   iio_get_time_ns(indio_dev));
> +
> +done:
> +	iio_trigger_notify_done(indio_dev->trig);
> +	return IRQ_HANDLED;
> +}
> +
> +static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
> +{
> +	int ret;
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +	mutex_lock(&dln2->mutex);
> +	dln2->chans_requested |= *indio_dev->active_scan_mask;
> +	ret = dln2_adc_update_enabled_chans(dln2);
> +	mutex_unlock(&dln2->mutex);
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +
> +	return iio_triggered_buffer_postenable(indio_dev);
> +}
> +
> +static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
> +	.postenable = dln2_adc_triggered_buffer_postenable,
> +	.predisable = iio_triggered_buffer_predisable,
> +};
> +
> +static void dln2_adc_event(struct platform_device *pdev, u16 echo,
> +			   const void *data, int len)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +	iio_trigger_poll(dln2->trig);
> +}
> +
> +static const struct iio_trigger_ops dln2_adc_trigger_ops = {
> +	.owner = THIS_MODULE,
> +};
> +
> +static int dln2_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct dln2_adc *dln2;
> +	struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
> +	struct iio_dev *indio_dev;
> +	struct iio_buffer *buffer;
> +	int ret;
> +	int chans;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(struct dln2_adc));
> +	if (!indio_dev) {
> +		dev_err(dev, "failed allocating iio device\n");
> +		return -ENOMEM;
> +	}
> +
> +	dln2 = iio_priv(indio_dev);
> +	dln2->pdev = pdev;
> +	dln2->port = pdata->port;
> +	mutex_init(&dln2->mutex);
> +	dln2->port_enabled = false;
> +	dln2->resolution_set = false;
> +	dln2->chans_requested = 0;
> +	dln2->chans_enabled = 0;
> +	dln2->trigger_chan = -1;
> +
> +	platform_set_drvdata(pdev, indio_dev);
> +
> +	chans = dln2_adc_get_chan_count(dln2);
> +	if (chans < 0) {
> +		dev_err(dev, "failed to get channel count: %d\n", chans);
> +		ret = chans;
> +		goto dealloc_dev;
> +	}
> +	if (chans > DLN2_ADC_MAX_CHANNELS) {
> +		chans = DLN2_ADC_MAX_CHANNELS;
> +		dev_warn(dev, "clamping channels to %d\n",
> +			 DLN2_ADC_MAX_CHANNELS);
> +	}
> +
> +	indio_dev->name = DLN2_ADC_MOD_NAME;
> +	indio_dev->dev.parent = dev;
> +	indio_dev->info = &dln2_adc_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
> +	indio_dev->channels = dln2_adc_iio_channels;
> +	indio_dev->num_channels = chans + 1;
> +	indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
> +
> +	dln2->trig = devm_iio_trigger_alloc(dev, "samplerate");
> +	if (!dln2->trig) {
> +		dev_err(dev, "failed to allocate trigger\n");
> +		ret = -ENOMEM;
> +		goto dealloc_dev;
> +	}
> +	dln2->trig->ops = &dln2_adc_trigger_ops;
> +	iio_trigger_set_drvdata(dln2->trig, dln2);
> +	iio_trigger_register(dln2->trig);
> +	iio_trigger_set_immutable(indio_dev, dln2->trig);
> +
> +	buffer = devm_iio_kfifo_allocate(dev);
> +	if (!buffer) {
> +		dev_err(dev, "failed to allocate kfifo\n");
> +		ret = -ENOMEM;
> +		goto dealloc_trigger;
> +	}
> +
> +	iio_device_attach_buffer(indio_dev, buffer);
> +
> +	indio_dev->pollfunc = iio_alloc_pollfunc(NULL,
> +						 &dln2_adc_trigger_h,
> +						 IRQF_ONESHOT,
> +						 indio_dev,
> +						 "samplerate");
> +
> +	if (!indio_dev->pollfunc) {
> +		ret = -ENOMEM;
> +		goto dealloc_kfifo;
> +	}
> +
> +	ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
> +				     dln2_adc_event);
> +	if (ret) {
> +		dev_err(dev, "failed to register event cb: %d\n", ret);
> +		goto dealloc_pollfunc;
> +	}
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret) {
> +		dev_err(dev, "failed to register iio device: %d\n", ret);
> +		goto dealloc_pollfunc;
> +	}
> +
> +	return 0;
> +
> +dealloc_pollfunc:
> +	iio_dealloc_pollfunc(indio_dev->pollfunc);
> +dealloc_kfifo:
> +dealloc_trigger:
> +	iio_trigger_unregister(dln2->trig);
> +dealloc_dev:
> +
> +	return ret;
> +}
> +
> +static int dln2_adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +	iio_device_unregister(indio_dev);
> +	dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
> +	iio_trigger_unregister(dln2->trig);
> +	iio_dealloc_pollfunc(indio_dev->pollfunc);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver dln2_adc_driver = {
> +	.driver.name	= DLN2_ADC_MOD_NAME,
> +	.probe		= dln2_adc_probe,
> +	.remove		= dln2_adc_remove,
> +};
> +
> +module_platform_driver(dln2_adc_driver);
> +
> +MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
> +MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:dln2-adc");
> diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
> index 704e189..a22ab8c 100644
> --- a/drivers/mfd/dln2.c
> +++ b/drivers/mfd/dln2.c
> @@ -53,6 +53,7 @@ enum dln2_handle {
>  	DLN2_HANDLE_GPIO,
>  	DLN2_HANDLE_I2C,
>  	DLN2_HANDLE_SPI,
> +	DLN2_HANDLE_ADC,
>  	DLN2_HANDLES
>  };
>  
> @@ -663,6 +664,12 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
>  	.port = 0,
>  };
>  
> +/* Only one ADC port supported */
> +static struct dln2_platform_data dln2_pdata_adc = {
> +	.handle = DLN2_HANDLE_ADC,
> +	.port = 0,
> +};
> +
>  static const struct mfd_cell dln2_devs[] = {
>  	{
>  		.name = "dln2-gpio",
> @@ -679,6 +686,11 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
>  		.platform_data = &dln2_pdata_spi,
>  		.pdata_size = sizeof(struct dln2_platform_data),
>  	},
> +	{
> +		.name = "dln2-adc",
> +		.platform_data = &dln2_pdata_adc,
> +		.pdata_size = sizeof(struct dln2_platform_data),
> +	},
>  };
>  
>  static void dln2_stop(struct dln2_dev *dln2)
> 

-- 

Peter Meerwald-Stadler
Mobile: +43 664 24 44 418

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

* Re: [PATCH v2] iio: adc: Add support for DLN2 ADC
       [not found]       ` <CAPHBK3ZeVFVtJgsNQhqhGPkiKTL4mi6h0nLi4Jjm6BUHM5u96Q@mail.gmail.com>
@ 2017-06-25 15:58         ` Jonathan Cameron
  2017-06-25 17:06           ` Jack Andersen
  0 siblings, 1 reply; 23+ messages in thread
From: Jonathan Cameron @ 2017-06-25 15:58 UTC (permalink / raw)
  To: Jack Andersen
  Cc: Peter Meerwald-Stadler, Hartmut Knaack, Lars-Peter Clausen,
	Lee Jones, linux-iio

On Sat, 24 Jun 2017 12:18:58 -1000
Jack Andersen <jackoalan@gmail.com> wrote:

> You're right about the fiddly device part. The important thing
> to note about the DLN2 is it's a multifunction device with a
> unified command message interface. All four functions (GPIO, ADC,
> I2C, SPI) use this interface for every possible action. Commands
> are performed synchronously and return at least an error code.
> 
> The dln2.c module in MFD implements this command interface and
> manages 'handles' for all four functions as well as a single
> event handle for handling USB transfers of events like GPIO
> low/high and ADC thresholds / periodic timers. Existing modules
> like dln2_gpio and dln2_i2c all share the symbols exported by
> dln2.
> 
> I'll revaluate the code when I'm back in the office on Monday.
> 
> More explanations inline.
> 
> On 24 June 2017 at 09:38, Jonathan Cameron <jic23@kernel.org> wrote:
> > On Fri, 23 Jun 2017 09:25:58 +0200 (CEST)
> > Peter Meerwald-Stadler <pmeerw@pmeerw.net> wrote:
> >  
> >> > This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
> >> > ADC is the fourth and final component of the DLN2 for the kernel.  
> >>
> >> I am missing a changelog or information if and how review comments have
> >> been addressed
> >>
> >> thanks for the super-fast turnaround, anyway
> >>
> >> p.  
> > A few bits and pieces inline.  Fiddly device.  Out of curiosity
> > are then any relevant docs or is this a reverse engineering job?
> >
> > There were a few unusual elements so would be nice to verify they
> > line up with docs if available!
> >
> > Jonathan  
> Reverse engineering wasn't really necessary; Diolan has macros
> of all the command enums and structures in their userspace
> headers (for some reason). The calls implemented in their binary
> blob are simple libusb transfer wrappers; I essentially stuck to
> the transfer style used by dln2_gpio instead.
> 
> For reference:
> http://dlnware.com/downloads/linux-setup
> Unzip and refer to examples/common/dln_adc.h
> >>  
> >> > Signed-off-by: Jack Andersen <jackoalan@gmail.com>
> >> > ---
> >> >  drivers/iio/adc/Kconfig    |   9 +
> >> >  drivers/iio/adc/Makefile   |   1 +
> >> >  drivers/iio/adc/dln2-adc.c | 644 +++++++++++++++++++++++++++++++++++++++++++++
> >> >  drivers/mfd/dln2.c         |  12 +  
> > touching an mfd file so needs an ack from Lee. Cc'd.  
> >> >  4 files changed, 666 insertions(+)
> >> >  create mode 100644 drivers/iio/adc/dln2-adc.c
> >> >
> >> > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> >> > index 401f47b..78d7455 100644
> >> > --- a/drivers/iio/adc/Kconfig
> >> > +++ b/drivers/iio/adc/Kconfig
> >> > @@ -239,6 +239,15 @@ config DA9150_GPADC
> >> >       To compile this driver as a module, choose M here: the module will be
> >> >       called berlin2-adc.
> >> >
> >> > +config DLN2_ADC
> >> > +   tristate "Diolan DLN-2 ADC driver support"
> >> > +   depends on MFD_DLN2
> >> > +   help
> >> > +     Say yes here to build support for Diolan DLN-2 ADC.
> >> > +
> >> > +     This driver can also be built as a module. If so, the module will be
> >> > +     called adc_dln2.
> >> > +
> >> >  config ENVELOPE_DETECTOR
> >> >     tristate "Envelope detector using a DAC and a comparator"
> >> >     depends on OF
> >> > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> >> > index 9339bec..378bc65 100644
> >> > --- a/drivers/iio/adc/Makefile
> >> > +++ b/drivers/iio/adc/Makefile
> >> > @@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
> >> >  obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
> >> >  obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
> >> >  obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
> >> > +obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
> >> >  obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
> >> >  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
> >> >  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
> >> > diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
> >> > new file mode 100644
> >> > index 0000000..6c58b76
> >> > --- /dev/null
> >> > +++ b/drivers/iio/adc/dln2-adc.c
> >> > @@ -0,0 +1,644 @@
> >> > +/*
> >> > + * Driver for the Diolan DLN-2 USB-ADC adapter
> >> > + *
> >> > + * Copyright (c) 2017 Jack Andersen
> >> > + *
> >> > + * 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, version 2.
> >> > + */
> >> > +
> >> > +#include <linux/kernel.h>
> >> > +#include <linux/module.h>
> >> > +#include <linux/types.h>
> >> > +#include <linux/platform_device.h>
> >> > +#include <linux/mfd/dln2.h>
> >> > +
> >> > +#include <linux/iio/iio.h>
> >> > +#include <linux/iio/sysfs.h>
> >> > +#include <linux/iio/trigger.h>
> >> > +#include <linux/iio/trigger_consumer.h>
> >> > +#include <linux/iio/buffer.h>
> >> > +#include <linux/iio/kfifo_buf.h>
> >> > +
> >> > +#define DLN2_ADC_MOD_NAME "dln2-adc"
> >> > +
> >> > +#define DLN2_ADC_ID             0x06
> >> > +
> >> > +#define DLN2_ADC_GET_CHANNEL_COUNT DLN2_CMD(0x01, DLN2_ADC_ID)
> >> > +#define DLN2_ADC_ENABLE                    DLN2_CMD(0x02, DLN2_ADC_ID)
> >> > +#define DLN2_ADC_DISABLE           DLN2_CMD(0x03, DLN2_ADC_ID)
> >> > +#define DLN2_ADC_CHANNEL_ENABLE            DLN2_CMD(0x05, DLN2_ADC_ID)
> >> > +#define DLN2_ADC_CHANNEL_DISABLE   DLN2_CMD(0x06, DLN2_ADC_ID)
> >> > +#define DLN2_ADC_SET_RESOLUTION            DLN2_CMD(0x08, DLN2_ADC_ID)
> >> > +#define DLN2_ADC_CHANNEL_GET_VAL   DLN2_CMD(0x0A, DLN2_ADC_ID)
> >> > +#define DLN2_ADC_CHANNEL_GET_ALL_VAL       DLN2_CMD(0x0B, DLN2_ADC_ID)
> >> > +#define DLN2_ADC_CHANNEL_SET_CFG   DLN2_CMD(0x0C, DLN2_ADC_ID)
> >> > +#define DLN2_ADC_CHANNEL_GET_CFG   DLN2_CMD(0x0D, DLN2_ADC_ID)
> >> > +#define DLN2_ADC_CONDITION_MET_EV  DLN2_CMD(0x10, DLN2_ADC_ID)
> >> > +
> >> > +#define DLN2_ADC_EVENT_NONE                0
> >> > +#define DLN2_ADC_EVENT_BELOW               1
> >> > +#define DLN2_ADC_EVENT_LEVEL_ABOVE 2
> >> > +#define DLN2_ADC_EVENT_OUTSIDE             3
> >> > +#define DLN2_ADC_EVENT_INSIDE              4
> >> > +#define DLN2_ADC_EVENT_ALWAYS              5
> >> > +
> >> > +#define DLN2_ADC_MAX_CHANNELS 8
> >> > +#define DLN2_ADC_DATA_BITS 10
> >> > +
> >> > +struct dln2_adc {
> >> > +   struct platform_device *pdev;
> >> > +   int port;
> >> > +   struct iio_trigger *trig;
> >> > +   struct mutex mutex;
> >> > +   /* Set once initialized */
> >> > +   bool port_enabled;
> >> > +   /* Set once resolution request made to HW */
> >> > +   bool resolution_set;
> >> > +   /* Bitmask requesting enabled channels */
> >> > +   unsigned long chans_requested;
> >> > +   /* Bitmask indicating enabled channels on HW */
> >> > +   unsigned long chans_enabled;
> >> > +   /* Channel that is arbitrated for event trigger */
> >> > +   int trigger_chan;
> >> > +};
> >> > +
> >> > +struct dln2_adc_port_chan {
> >> > +   u8 port;
> >> > +   u8 chan;
> >> > +};
> >> > +
> >> > +struct dln2_adc_get_all_vals {
> >> > +   __le16 channel_mask;
> >> > +   __le16 values[DLN2_ADC_MAX_CHANNELS];
> >> > +};
> >> > +
> >> > +static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
> >> > +{
> >> > +   int ret;
> >> > +   u8 port = dln2->port;
> >> > +   u8 count;
> >> > +   int olen = sizeof(count);
> >> > +
> >> > +   ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
> >> > +                       &port, sizeof(port), &count, &olen);
> >> > +   if (ret < 0) {
> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> > +           return ret;
> >> > +   }
> >> > +   if (olen < sizeof(count))
> >> > +           return -EPROTO;
> >> > +
> >> > +   return count;
> >> > +}
> >> > +
> >> > +static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
> >> > +{
> >> > +   int ret;
> >> > +   struct dln2_adc_port_chan port_chan = {
> >> > +           .port = dln2->port,
> >> > +           .chan = DLN2_ADC_DATA_BITS,
> >> > +   };
> >> > +
> >> > +   ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
> >> > +                          &port_chan, sizeof(port_chan));
> >> > +   if (ret < 0) {
> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> > +           return ret;
> >> > +   }
> >> > +
> >> > +   return 0;
> >> > +}
> >> > +
> >> > +static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
> >> > +                                int channel, bool enable)
> >> > +{
> >> > +   int ret;
> >> > +   struct dln2_adc_port_chan port_chan = {
> >> > +           .port = dln2->port,
> >> > +           .chan = channel,
> >> > +   };
> >> > +
> >> > +   u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
> >> > +
> >> > +   ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
> >> > +   if (ret < 0) {
> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> > +           return ret;
> >> > +   }
> >> > +
> >> > +   return 0;
> >> > +}
> >> > +
> >> > +static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable)
> >> > +{
> >> > +   int ret;
> >> > +   u8 port = dln2->port;
> >> > +   __le16 conflict;
> >> > +   int olen = sizeof(conflict);
> >> > +
> >> > +   u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
> >> > +
> >> > +   ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
> >> > +                       &conflict, &olen);
> >> > +   if (ret < 0) {
> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
> >> > +                   __func__, (int)enable);
> >> > +           return ret;
> >> > +   }
> >> > +   if (enable && olen < sizeof(conflict))
> >> > +           return -EPROTO;
> >> > +
> >> > +   return 0;
> >> > +}
> >> > +
> >> > +/*
> >> > + * ADC channels are lazily enabled due to the pins being shared with GPIO
> >> > + * channels. Enabling channels requires taking the ADC port offline, specifying
> >> > + * the resolution, individually enabling channels, then putting the port back
> >> > + * online. If GPIO pins have already been exported by gpio_dln2, EINVAL is
> >> > + * reported.
> >> > + */  
> > Ah, now it's all starting to make sense.  Let me check I have this right.
> >
> > 1) You have an 8 channel adc.
> > 2) You can only (or it's more efficient) to read all 8 channels at once.
> > 3) As the pins are shared, not all 8 channels are actually wired to anything.
> > 4) This prevents you just using an available_scan_mask entry to enable them all.
> >    Hence you have rolled your own demux.  If you need to do this quick then
> >    take a look at the precomputed mux tables in the core.  If not I guess
> >    what you have works fine.  
> Yes, that code was borrowed from the simple dummy example module.
> I'm new to IIO, so I didn't realize a more efficient remuxing
> mechanism was available. I can look into integrating that on
> Monday.
I'm thinking it's a non starter because of the lazy enabling, but maybe
there is some way to make it work.
> >> > +static int dln2_adc_update_enabled_chans(struct dln2_adc *dln2)
> >> > +{
> >> > +   int ret, i, chan_count;
> >> > +   struct iio_dev *indio_dev;
> >> > +
> >> > +   if (dln2->chans_enabled == dln2->chans_requested)
> >> > +           return 0;
> >> > +
> >> > +   indio_dev = platform_get_drvdata(dln2->pdev);
> >> > +   chan_count = indio_dev->num_channels;
> >> > +
> >> > +   if (dln2->port_enabled) {
> >> > +           ret = dln2_adc_set_port_enabled(dln2, false);
> >> > +           if (ret < 0)
> >> > +                   return ret;
> >> > +           dln2->port_enabled = false;
> >> > +   }
> >> > +
> >> > +   if (!dln2->resolution_set) {
> >> > +           ret = dln2_adc_set_port_resolution(dln2);
> >> > +           if (ret < 0)
> >> > +                   return ret;
> >> > +           dln2->resolution_set = true;
> >> > +   }
> >> > +
> >> > +   for (i = 0; i < chan_count; ++i) {
> >> > +           bool requested = dln2->chans_requested & (1 << i);
> >> > +           bool enabled = dln2->chans_enabled & (1 << i);
> >> > +
> >> > +           if (requested == enabled)
> >> > +                   continue;
> >> > +           ret = dln2_adc_set_chan_enabled(dln2, i, requested);
> >> > +           if (ret < 0)
> >> > +                   return ret;
> >> > +   }
> >> > +
> >> > +   dln2->chans_enabled = dln2->chans_requested;
> >> > +
> >> > +   ret = dln2_adc_set_port_enabled(dln2, true);
> >> > +   if (ret < 0)
> >> > +           return ret;
> >> > +   dln2->port_enabled = true;
> >> > +
> >> > +   return 0;
> >> > +}
> >> > +
> >> > +static int dln2_adc_get_chan_freq(struct dln2_adc *dln2, unsigned int channel)
> >> > +{
> >> > +   int ret;
> >> > +   struct dln2_adc_port_chan port_chan = {
> >> > +           .port = dln2->port,
> >> > +           .chan = channel,
> >> > +   };
> >> > +   struct {
> >> > +           __u8 type;
> >> > +           __le16 period;
> >> > +           __le16 low;
> >> > +           __le16 high;
> >> > +   } __packed get_cfg;
> >> > +   int olen = sizeof(get_cfg);
> >> > +
> >> > +   ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_CFG,
> >> > +                       &port_chan, sizeof(port_chan), &get_cfg, &olen);
> >> > +   if (ret < 0) {
> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> > +           return ret;
> >> > +   }
> >> > +   if (olen < sizeof(get_cfg))
> >> > +           return -EPROTO;
> >> > +
> >> > +   return get_cfg.period;  
> > Returning a value called period for a function claiming to be reading the
> > frequency is unusual. It's got to be converted to the frequency.  
> Yea, I screwed up there. Diolan's command interface uses periods
> in milliseconds so I'll make sure the necessary reciprocal gets
> put in and the function is clearly named.
> >> > +}
> >> > +
> >> > +static int dln2_adc_set_chan_freq(struct dln2_adc *dln2, unsigned int channel,
> >> > +                             unsigned int freq)
> >> > +{
> >> > +   int ret;
> >> > +   struct {
> >> > +           struct dln2_adc_port_chan port_chan;
> >> > +           __u8 type;
> >> > +           __le16 period;
> >> > +           __le16 low;
> >> > +           __le16 high;
> >> > +   } __packed set_cfg = {
> >> > +           .port_chan.port = dln2->port,
> >> > +           .port_chan.chan = channel,
> >> > +           .type = freq ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
> >> > +           .period = cpu_to_le16(freq)
> >> > +   };
> >> > +
> >> > +   ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
> >> > +                          &set_cfg, sizeof(set_cfg));
> >> > +   if (ret < 0) {
> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> > +           return ret;
> >> > +   }
> >> > +   return 0;
> >> > +}
> >> > +
> >> > +static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
> >> > +{
> >> > +   int ret;
> >> > +   int old_chans_requested = dln2->chans_requested;
> >> > +
> >> > +   dln2->chans_requested |= (1 << channel);  
> > Ah, so this changes the enabled channels and can lead to the
> > changes in what is enabled when buffered mode is going on.
> >
> > I'd just use the iio_claim_direct_mode set of functions
> > to prevent direct reads when the buffer is operating.
> > They add a lot of complexity for what is usually a fairly
> > unusual requirement.  
> Will do.
> >> > +   ret = dln2_adc_update_enabled_chans(dln2);
> >> > +   if (ret < 0) {
> >> > +           dln2->chans_requested = old_chans_requested;
> >> > +           return ret;
> >> > +   }
> >> > +   dln2->port_enabled = true;
> >> > +
> >> > +   struct dln2_adc_port_chan port_chan = {
> >> > +           .port = dln2->port,
> >> > +           .chan = channel,
> >> > +   };
> >> > +   __le16 value;
> >> > +   int olen = sizeof(value);
> >> > +
> >> > +   ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
> >> > +                       &port_chan, sizeof(port_chan), &value, &olen);
> >> > +   if (ret < 0) {
> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> > +           return ret;
> >> > +   }
> >> > +   if (olen < sizeof(value))
> >> > +           return -EPROTO;
> >> > +
> >> > +   return le16_to_cpu(value);
> >> > +}
> >> > +
> >> > +static int dln2_adc_read_all(struct dln2_adc *dln2,
> >> > +                        struct dln2_adc_get_all_vals *get_all_vals)
> >> > +{
> >> > +   int ret;
> >> > +   __u8 port = dln2->port;
> >> > +   int olen = sizeof(*get_all_vals);
> >> > +
> >> > +   ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
> >> > +                       &port, sizeof(port), get_all_vals, &olen);  
> > Getting channels that aren't wired to anything?  Fair enough if true,
> > but unusual.  
> I had the same thought myself; the adc example in Diolan's
> package reads in a fixed 8 channels every time, apparently
> zeroing the disabled ones.
It would make their firmware simpler probably.  Push the complexity to
the more powerful main processor.
> >> > +   if (ret < 0) {
> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> > +           return ret;
> >> > +   }
> >> > +   if (olen < sizeof(*get_all_vals))
> >> > +           return -EPROTO;
> >> > +
> >> > +   return 0;
> >> > +}
> >> > +
> >> > +static int dln2_adc_read_raw(struct iio_dev *indio_dev,
> >> > +                        struct iio_chan_spec const *chan,
> >> > +                        int *val,
> >> > +                        int *val2,
> >> > +                        long mask)
> >> > +{
> >> > +   int ret;
> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
> >> > +
> >> > +   switch (mask) {
> >> > +   case IIO_CHAN_INFO_RAW:
> >> > +           mutex_lock(&dln2->mutex);
> >> > +           ret = dln2_adc_read(dln2, chan->channel);
> >> > +           mutex_unlock(&dln2->mutex);
> >> > +
> >> > +           if (ret < 0)
> >> > +                   return ret;
> >> > +
> >> > +           *val = ret;
> >> > +           return IIO_VAL_INT;
> >> > +
> >> > +   case IIO_CHAN_INFO_SCALE:
> >> > +           *val = 0;
> >> > +           /* 3.3 / (1 << 10) * 1000000000 */
> >> > +           *val2 = 3222656;
> >> > +           return IIO_VAL_INT_PLUS_NANO;
> >> > +
> >> > +   case IIO_CHAN_INFO_SAMP_FREQ:
> >> > +           mutex_lock(&dln2->mutex);
> >> > +           if (dln2->trigger_chan == -1)
> >> > +                   ret = 0;
> >> > +           else
> >> > +                   ret = dln2_adc_get_chan_freq(dln2, dln2->trigger_chan);
> >> > +           mutex_unlock(&dln2->mutex);
> >> > +
> >> > +           if (ret < 0)
> >> > +                   return ret;
> >> > +
> >> > +           *val = ret / 1000;
> >> > +           *val2 = (ret % 1000) * 1000;
> >> > +           return IIO_VAL_INT_PLUS_MICRO;
> >> > +
> >> > +   default:
> >> > +           return -EINVAL;
> >> > +   }
> >> > +}
> >> > +
> >> > +static int dln2_adc_write_raw(struct iio_dev *indio_dev,
> >> > +                         struct iio_chan_spec const *chan,
> >> > +                         int val,
> >> > +                         int val2,
> >> > +                         long mask)
> >> > +{
> >> > +   int ret;
> >> > +   unsigned int freq;
> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
> >> > +
> >> > +   switch (mask) {
> >> > +   case IIO_CHAN_INFO_SAMP_FREQ:
> >> > +           freq = val * 1000 + val2 / 1000;
> >> > +           if (freq > 65535) {
> >> > +                   freq = 65535;
> >> > +                   dev_warn(&dln2->pdev->dev,
> >> > +                            "clamping freq to 65535ms\n");  
> > frequency measured in a unit of time?  
> Will fix Monday
> >> > +           }
> >> > +           mutex_lock(&dln2->mutex);
> >> > +
> >> > +           /*
> >> > +            * The first requested channel is arbitrated as a shared
> >> > +            * trigger source, so only one event is registered with the DLN.
> >> > +            * The event handler will then read all enabled channel values
> >> > +            * using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
> >> > +            * synchronization between ADC readings.
> >> > +            */
> >> > +           if (dln2->trigger_chan == -1)
> >> > +                   dln2->trigger_chan = chan->channel;  
> > This looks dangerous. either it is always fine to use a random channel or it's
> > not...
> >
> > I'd cache the frequency and only actually set it when bringing up the buffer
> > - that way if the setup is sane you will have a channel.  
> Fair point, I'll refactor it that way.
> >> > +           ret = dln2_adc_set_chan_freq(dln2, dln2->trigger_chan, freq);
> >> > +
> >> > +           mutex_unlock(&dln2->mutex);
> >> > +
> >> > +           if (ret < 0)
> >> > +                   return ret;
> >> > +
> >> > +           return 0;  
> > Just return ret and loose the other path.  I chased through the code
> > and we get 0 for success.  
> Ok
> >> > +
> >> > +   default:
> >> > +           return -EINVAL;
> >> > +   }
> >> > +}
> >> > +
> >> > +#define DLN2_ADC_CHAN(idx) {                                       \
> >> > +   .type = IIO_VOLTAGE,                                    \
> >> > +   .channel = idx,                                         \
> >> > +   .indexed = 1,                                           \
> >> > +   .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
> >> > +   .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |   \
> >> > +                              BIT(IIO_CHAN_INFO_SAMP_FREQ),\
> >> > +   .scan_index = idx,                                      \  
> > .scan_type = {
> >         .sign = 'u', etc is easier to read.  
> Ok
> >> > +   .scan_type.sign = 'u',                                  \
> >> > +   .scan_type.realbits = DLN2_ADC_DATA_BITS,               \
> >> > +   .scan_type.storagebits = 16,                            \
> >> > +   .scan_type.endianness = IIO_LE,                         \
> >> > +}
> >> > +
> >> > +static const struct iio_chan_spec dln2_adc_iio_channels[] = {
> >> > +   DLN2_ADC_CHAN(0),
> >> > +   DLN2_ADC_CHAN(1),
> >> > +   DLN2_ADC_CHAN(2),
> >> > +   DLN2_ADC_CHAN(3),
> >> > +   DLN2_ADC_CHAN(4),
> >> > +   DLN2_ADC_CHAN(5),
> >> > +   DLN2_ADC_CHAN(6),
> >> > +   DLN2_ADC_CHAN(7),
> >> > +   IIO_CHAN_SOFT_TIMESTAMP(8),
> >> > +};
> >> > +
> >> > +static const struct iio_info dln2_adc_info = {
> >> > +   .read_raw = dln2_adc_read_raw,
> >> > +   .write_raw = dln2_adc_write_raw,
> >> > +   .driver_module = THIS_MODULE,
> >> > +};
> >> > +
> >> > +static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
> >> > +{
> >> > +   struct iio_poll_func *pf = p;
> >> > +   struct iio_dev *indio_dev = pf->indio_dev;
> >> > +   struct {
> >> > +           __le16 values[DLN2_ADC_MAX_CHANNELS];
> >> > +           int64_t timestamp_space;
> >> > +   } data;
> >> > +   struct dln2_adc_get_all_vals dev_data;
> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
> >> > +   int old_chans_requested;
> >> > +   int i, j;
> >> > +
> >> > +   mutex_lock(&dln2->mutex);
> >> > +
> >> > +   old_chans_requested = dln2->chans_requested;
> >> > +   dln2->chans_requested |= *indio_dev->active_scan_mask;
> >> > +   if (dln2_adc_update_enabled_chans(dln2) < 0) {
> >> > +           dln2->chans_requested = old_chans_requested;  
> > I'm confused. Why would this change here?  Shouldn't change
> > whilst data is being captured unless something very odd is going on.
> > Ah, found it - you want to allow sysfs reads at the same time.
> > Given the complexity required to do that safely, I'd just not
> > do it for now - maybe add it as a follow up patch later.  
> >> > +           mutex_unlock(&dln2->mutex);
> >> > +           goto done;
> >> > +   }
> >> > +
> >> > +   if (dln2_adc_read_all(dln2, &dev_data) < 0) {
> >> > +           mutex_unlock(&dln2->mutex);
> >> > +           goto done;
> >> > +   }
> >> > +
> >> > +   mutex_unlock(&dln2->mutex);
> >> > +
> >> > +   for (i = 0, j = 0;  
> >
> > Having found the note about lazy enabling above I think I now
> > understand what you are doing here.  You can't use the
> > core infrastructure and set available_scan_masks to 0x1FF because
> > that would result in enabling all the pins, some of which are wanted
> > for gpio usage perhaps.
> >
> > Hence you end up rolling your own demux.  You are reading all
> > the ADC channels, but some aren't necessarily connected to any actual
> > pins.
> >  
> Precisely. The lazy enabling adapts to the demand of the user.
> Since IIO doesn't really have an export mechanism like the GPIO
> subsystem, I can't preemptively enable channels unless I make the
> driver buffer-only (which I'd rather not do cause direct reads
> come in handy).
one of those fiddly 'boundary' cases for defining connectivity.
If we were talking hard wired pins on a board this would all go
in device tree, but in reality most of the time we have
no idea what is connected to a given DLN2.
> >> > +        i < bitmap_weight(indio_dev->active_scan_mask,
> >> > +                          indio_dev->masklength); i++, j++) {
> >> > +           j = find_next_bit(indio_dev->active_scan_mask,
> >> > +                             indio_dev->masklength, j);
> >> > +           data.values[i] = dev_data.values[j];
> >> > +   }
> >> > +
> >> > +   iio_push_to_buffers_with_timestamp(indio_dev, &data,
> >> > +                                      iio_get_time_ns(indio_dev));
> >> > +
> >> > +done:
> >> > +   iio_trigger_notify_done(indio_dev->trig);
> >> > +   return IRQ_HANDLED;
> >> > +}
> >> > +
> >> > +static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
> >> > +{
> >> > +   int ret;
> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
> >> > +
> >> > +   mutex_lock(&dln2->mutex);
> >> > +   dln2->chans_requested |= *indio_dev->active_scan_mask;  
> > Perhaps should be in the update_scan_mask callback rather than here?
> > You could have multiple consumers of this data which would cause
> > this path to be 'interesting'.
> >
> > I'm also a little curious to know why it isn't just an =
> > Also why don't you want to disable them when then buffer is disabled.  
> This allows the enabled channels to remain enabled if the user
> opts for direct raw reads. If I were to enable a channel
> temporarily for a raw read, that's 6 or 7 synchronous commands:
> - Set resolution (if not already done)
> - Enable channel
> - Enable port
> - Get value
> - Get value (a second one is needed because the first read after
>   enabling always seems to return 0, must be a firmware bug)
> - Disable port
> - Disable channel
> 
> This just strikes me as inefficient, but it would be more
> consistent state-wise. If you think this is an ok trade-off,
> I'll go for the enable/disable approach. I suppose it's also
> possible to reset the lazy channel mask when the user exits
> buffered mode. The initial zero-return after enabling channels is
> a real kicker, and makes me want to keep disables/enables to a
> minimum.
That is a pain.  Still more consistent to clear the channel mask
again after a buffer disable + prevent single channel reads
whilst it is running.  At least, do this for the first merge
of the driver then we can revisit if you have a usecase
where the juggling is really necessary.

> >> > +   ret = dln2_adc_update_enabled_chans(dln2);
> >> > +   mutex_unlock(&dln2->mutex);
> >> > +   if (ret < 0) {
> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> > +           return ret;
> >> > +   }
> >> > +
> >> > +   return iio_triggered_buffer_postenable(indio_dev);
> >> > +}
> >> > +
> >> > +static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
> >> > +   .postenable = dln2_adc_triggered_buffer_postenable,
> >> > +   .predisable = iio_triggered_buffer_predisable,
> >> > +};
> >> > +
> >> > +static void dln2_adc_event(struct platform_device *pdev, u16 echo,
> >> > +                      const void *data, int len)
> >> > +{
> >> > +   struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
> >> > +
> >> > +   iio_trigger_poll(dln2->trig);  
> > Is this in interrupt context?
> > if not should be iio_trigger_poll_chained(dln2->trig);  
> I'll double check that on Monday, the MFD code would determine
> that.
> >> > +}
> >> > +
> >> > +static const struct iio_trigger_ops dln2_adc_trigger_ops = {
> >> > +   .owner = THIS_MODULE,  
> > Hohum.  Patch set to remove the need for this is waiting for me
> > to get around to revising it.  Interestingly checking why you
> > had this at all lead me to a check in the trigger register that
> > I wasn't getting rid of in that patch set. oops.
> >
> > Upshot is that the equivalent is done via macro magic rather
> > than an explicit entry in this structure.  It's possible this
> > driver will cross with that series, in which case we'll get
> > some build issues that will need fixing up.  I'll hopefully
> > remember to deal with them when applying though.  
> >> > +};
> >> > +
> >> > +static int dln2_adc_probe(struct platform_device *pdev)
> >> > +{
> >> > +   struct device *dev = pdev->dev;
> >> > +   struct dln2_adc *dln2;
> >> > +   struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
> >> > +   struct iio_dev *indio_dev;
> >> > +   struct iio_buffer *buffer;
> >> > +   int ret;
> >> > +   int chans;
> >> > +
> >> > +   indio_dev = devm_iio_device_alloc(dev, sizeof(struct dln2_adc));  
> > Slight preference for sizeof(*dln2)  
> Agreed
> >> > +   if (!indio_dev) {
> >> > +           dev_err(dev, "failed allocating iio device\n");
> >> > +           return -ENOMEM;
> >> > +   }
> >> > +
> >> > +   dln2 = iio_priv(indio_dev);
> >> > +   dln2->pdev = pdev;
> >> > +   dln2->port = pdata->port;
> >> > +   mutex_init(&dln2->mutex);
> >> > +   dln2->port_enabled = false;
> >> > +   dln2->resolution_set = false;
> >> > +   dln2->chans_requested = 0;
> >> > +   dln2->chans_enabled = 0;
> >> > +   dln2->trigger_chan = -1;  
> > This is fine at setup, but isn't reset to -1 if the channels are all disabled.  
> Ok
> >> > +
> >> > +   platform_set_drvdata(pdev, indio_dev);
> >> > +
> >> > +   chans = dln2_adc_get_chan_count(dln2);
> >> > +   if (chans < 0) {
> >> > +           dev_err(dev, "failed to get channel count: %d\n", chans);
> >> > +           ret = chans;
> >> > +           goto dealloc_dev;
> >> > +   }
> >> > +   if (chans > DLN2_ADC_MAX_CHANNELS) {
> >> > +           chans = DLN2_ADC_MAX_CHANNELS;
> >> > +           dev_warn(dev, "clamping channels to %d\n",
> >> > +                    DLN2_ADC_MAX_CHANNELS);
> >> > +   }
> >> > +
> >> > +   indio_dev->name = DLN2_ADC_MOD_NAME;
> >> > +   indio_dev->dev.parent = dev;
> >> > +   indio_dev->info = &dln2_adc_info;
> >> > +   indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
> >> > +   indio_dev->channels = dln2_adc_iio_channels;
> >> > +   indio_dev->num_channels = chans + 1;  
> > Why +1?  Presumably to include the timestamp.  I guess this
> > means that the code that gets the channel count always gets 8.
> > You need to check that if you are going to use a const channel array.  
> Yea, I see in the docs that's supposed to be monotonic.
> I'll assemble that array on the stack instead at probe time to be
> safe.
> >> > +   indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
> >> > +
> >> > +   dln2->trig = devm_iio_trigger_alloc(dev, "samplerate");  
> > Interesting naming for a trigger... rather too generic + doesn't
> > cope with multiple dln2's connect to same machine. Admittedly
> > a number of historical drivers have that problem but we can't
> > fix them now without ABI breakage.
> >  
> Good point, is there a preferred unique identifier to suffix that
> name?
we've never restricted this particularly as it's just a magic string
to match things up.

Quite a few drivers stick with "magicname-dev%d",  ..., indio_dev->id 
> >> > +   if (!dln2->trig) {
> >> > +           dev_err(dev, "failed to allocate trigger\n");
> >> > +           ret = -ENOMEM;
> >> > +           goto dealloc_dev;
> >> > +   }
> >> > +   dln2->trig->ops = &dln2_adc_trigger_ops;
> >> > +   iio_trigger_set_drvdata(dln2->trig, dln2);
> >> > +   iio_trigger_register(dln2->trig);
> >> > +   iio_trigger_set_immutable(indio_dev, dln2->trig);
> >> > +
> >> > +   buffer = devm_iio_kfifo_allocate(dev);
> >> > +   if (!buffer) {
> >> > +           dev_err(dev, "failed to allocate kfifo\n");
> >> > +           ret = -ENOMEM;
> >> > +           goto dealloc_trigger;
> >> > +   }
> >> > +
> >> > +   iio_device_attach_buffer(indio_dev, buffer);
> >> > +
> >> > +   indio_dev->pollfunc = iio_alloc_pollfunc(NULL,
> >> > +                                            &dln2_adc_trigger_h,
> >> > +                                            IRQF_ONESHOT,
> >> > +                                            indio_dev,
> >> > +                                            "samplerate");  
> > Again very generic naming + shouldn't be the same as the trigger name.
> > Almost no drivers do this manually any more.  Why does the
> > iio_triggered_buffer_setup boilerplate function not work here?  
> I read about this in the IIO presentation from back when the
> subsystem was first introduced. Didn't realize it was
> depreciated.
It's not deprecated as such, just that there are utility functions
to make the job easier if you happen to fit with the standard forms.
Just code repetition reduction really.  This approach may still
be necessary if something more 'interesting' is going on.
> >> > +
> >> > +   if (!indio_dev->pollfunc) {
> >> > +           ret = -ENOMEM;
> >> > +           goto dealloc_kfifo;
> >> > +   }
> >> > +
> >> > +   ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
> >> > +                                dln2_adc_event);
> >> > +   if (ret) {
> >> > +           dev_err(dev, "failed to register event cb: %d\n", ret);
> >> > +           goto dealloc_pollfunc;
> >> > +   }
> >> > +
> >> > +   ret = iio_device_register(indio_dev);
> >> > +   if (ret) {
> >> > +           dev_err(dev, "failed to register iio device: %d\n", ret);
> >> > +           goto dealloc_pollfunc;
> >> > +   }
> >> > +
> >> > +   return 0;
> >> > +
> >> > +dealloc_pollfunc:
> >> > +   iio_dealloc_pollfunc(indio_dev->pollfunc);
> >> > +dealloc_kfifo:
> >> > +dealloc_trigger:
> >> > +   iio_trigger_unregister(dln2->trig);
> >> > +dealloc_dev:
> >> > +
> >> > +   return ret;
> >> > +}
> >> > +
> >> > +static int dln2_adc_remove(struct platform_device *pdev)
> >> > +{
> >> > +   struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
> >> > +
> >> > +   iio_device_unregister(indio_dev);
> >> > +   dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
> >> > +   iio_trigger_unregister(dln2->trig);
> >> > +   iio_dealloc_pollfunc(indio_dev->pollfunc);
> >> > +
> >> > +   return 0;
> >> > +}
> >> > +
> >> > +static struct platform_driver dln2_adc_driver = {
> >> > +   .driver.name    = DLN2_ADC_MOD_NAME,
> >> > +   .probe          = dln2_adc_probe,
> >> > +   .remove         = dln2_adc_remove,
> >> > +};
> >> > +
> >> > +module_platform_driver(dln2_adc_driver);
> >> > +
> >> > +MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
> >> > +MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
> >> > +MODULE_LICENSE("GPL v2");
> >> > +MODULE_ALIAS("platform:dln2-adc");
> >> > diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
> >> > index 704e189..a22ab8c 100644
> >> > --- a/drivers/mfd/dln2.c
> >> > +++ b/drivers/mfd/dln2.c
> >> > @@ -53,6 +53,7 @@ enum dln2_handle {
> >> >     DLN2_HANDLE_GPIO,
> >> >     DLN2_HANDLE_I2C,
> >> >     DLN2_HANDLE_SPI,
> >> > +   DLN2_HANDLE_ADC,
> >> >     DLN2_HANDLES
> >> >  };
> >> >
> >> > @@ -663,6 +664,12 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
> >> >     .port = 0,
> >> >  };
> >> >
> >> > +/* Only one ADC port supported */
> >> > +static struct dln2_platform_data dln2_pdata_adc = {
> >> > +   .handle = DLN2_HANDLE_ADC,
> >> > +   .port = 0,
> >> > +};
> >> > +
> >> >  static const struct mfd_cell dln2_devs[] = {
> >> >     {
> >> >             .name = "dln2-gpio",
> >> > @@ -679,6 +686,11 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
> >> >             .platform_data = &dln2_pdata_spi,
> >> >             .pdata_size = sizeof(struct dln2_platform_data),
> >> >     },
> >> > +   {
> >> > +           .name = "dln2-adc",
> >> > +           .platform_data = &dln2_pdata_adc,
> >> > +           .pdata_size = sizeof(struct dln2_platform_data),
> >> > +   },
> >> >  };
> >> >
> >> >  static void dln2_stop(struct dln2_dev *dln2)
> >> >  
> >>  
> >  


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

* Re: [PATCH v2] iio: adc: Add support for DLN2 ADC
  2017-06-25 15:58         ` Jonathan Cameron
@ 2017-06-25 17:06           ` Jack Andersen
  2017-06-26 16:51             ` Jonathan Cameron
  0 siblings, 1 reply; 23+ messages in thread
From: Jack Andersen @ 2017-06-25 17:06 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Peter Meerwald-Stadler, Hartmut Knaack, Lars-Peter Clausen,
	Lee Jones, linux-iio

Ok, the temporary enables for raw reads are fine with me; my
applications all use buffered access anyway.

It would be better to allow the dynamic release of unused ADC
channels for GPIO pins, which lazy enabling prevents.

On 25 June 2017 at 05:58, Jonathan Cameron <jic23@kernel.org> wrote:
> On Sat, 24 Jun 2017 12:18:58 -1000
> Jack Andersen <jackoalan@gmail.com> wrote:
>
>> You're right about the fiddly device part. The important thing
>> to note about the DLN2 is it's a multifunction device with a
>> unified command message interface. All four functions (GPIO, ADC,
>> I2C, SPI) use this interface for every possible action. Commands
>> are performed synchronously and return at least an error code.
>>
>> The dln2.c module in MFD implements this command interface and
>> manages 'handles' for all four functions as well as a single
>> event handle for handling USB transfers of events like GPIO
>> low/high and ADC thresholds / periodic timers. Existing modules
>> like dln2_gpio and dln2_i2c all share the symbols exported by
>> dln2.
>>
>> I'll revaluate the code when I'm back in the office on Monday.
>>
>> More explanations inline.
>>
>> On 24 June 2017 at 09:38, Jonathan Cameron <jic23@kernel.org> wrote:
>> > On Fri, 23 Jun 2017 09:25:58 +0200 (CEST)
>> > Peter Meerwald-Stadler <pmeerw@pmeerw.net> wrote:
>> >
>> >> > This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
>> >> > ADC is the fourth and final component of the DLN2 for the kernel.
>> >>
>> >> I am missing a changelog or information if and how review comments have
>> >> been addressed
>> >>
>> >> thanks for the super-fast turnaround, anyway
>> >>
>> >> p.
>> > A few bits and pieces inline.  Fiddly device.  Out of curiosity
>> > are then any relevant docs or is this a reverse engineering job?
>> >
>> > There were a few unusual elements so would be nice to verify they
>> > line up with docs if available!
>> >
>> > Jonathan
>> Reverse engineering wasn't really necessary; Diolan has macros
>> of all the command enums and structures in their userspace
>> headers (for some reason). The calls implemented in their binary
>> blob are simple libusb transfer wrappers; I essentially stuck to
>> the transfer style used by dln2_gpio instead.
>>
>> For reference:
>> http://dlnware.com/downloads/linux-setup
>> Unzip and refer to examples/common/dln_adc.h
>> >>
>> >> > Signed-off-by: Jack Andersen <jackoalan@gmail.com>
>> >> > ---
>> >> >  drivers/iio/adc/Kconfig    |   9 +
>> >> >  drivers/iio/adc/Makefile   |   1 +
>> >> >  drivers/iio/adc/dln2-adc.c | 644 +++++++++++++++++++++++++++++++++++++++++++++
>> >> >  drivers/mfd/dln2.c         |  12 +
>> > touching an mfd file so needs an ack from Lee. Cc'd.
>> >> >  4 files changed, 666 insertions(+)
>> >> >  create mode 100644 drivers/iio/adc/dln2-adc.c
>> >> >
>> >> > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> >> > index 401f47b..78d7455 100644
>> >> > --- a/drivers/iio/adc/Kconfig
>> >> > +++ b/drivers/iio/adc/Kconfig
>> >> > @@ -239,6 +239,15 @@ config DA9150_GPADC
>> >> >       To compile this driver as a module, choose M here: the module will be
>> >> >       called berlin2-adc.
>> >> >
>> >> > +config DLN2_ADC
>> >> > +   tristate "Diolan DLN-2 ADC driver support"
>> >> > +   depends on MFD_DLN2
>> >> > +   help
>> >> > +     Say yes here to build support for Diolan DLN-2 ADC.
>> >> > +
>> >> > +     This driver can also be built as a module. If so, the module will be
>> >> > +     called adc_dln2.
>> >> > +
>> >> >  config ENVELOPE_DETECTOR
>> >> >     tristate "Envelope detector using a DAC and a comparator"
>> >> >     depends on OF
>> >> > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> >> > index 9339bec..378bc65 100644
>> >> > --- a/drivers/iio/adc/Makefile
>> >> > +++ b/drivers/iio/adc/Makefile
>> >> > @@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>> >> >  obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>> >> >  obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
>> >> >  obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>> >> > +obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
>> >> >  obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
>> >> >  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>> >> >  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
>> >> > diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
>> >> > new file mode 100644
>> >> > index 0000000..6c58b76
>> >> > --- /dev/null
>> >> > +++ b/drivers/iio/adc/dln2-adc.c
>> >> > @@ -0,0 +1,644 @@
>> >> > +/*
>> >> > + * Driver for the Diolan DLN-2 USB-ADC adapter
>> >> > + *
>> >> > + * Copyright (c) 2017 Jack Andersen
>> >> > + *
>> >> > + * 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, version 2.
>> >> > + */
>> >> > +
>> >> > +#include <linux/kernel.h>
>> >> > +#include <linux/module.h>
>> >> > +#include <linux/types.h>
>> >> > +#include <linux/platform_device.h>
>> >> > +#include <linux/mfd/dln2.h>
>> >> > +
>> >> > +#include <linux/iio/iio.h>
>> >> > +#include <linux/iio/sysfs.h>
>> >> > +#include <linux/iio/trigger.h>
>> >> > +#include <linux/iio/trigger_consumer.h>
>> >> > +#include <linux/iio/buffer.h>
>> >> > +#include <linux/iio/kfifo_buf.h>
>> >> > +
>> >> > +#define DLN2_ADC_MOD_NAME "dln2-adc"
>> >> > +
>> >> > +#define DLN2_ADC_ID             0x06
>> >> > +
>> >> > +#define DLN2_ADC_GET_CHANNEL_COUNT DLN2_CMD(0x01, DLN2_ADC_ID)
>> >> > +#define DLN2_ADC_ENABLE                    DLN2_CMD(0x02, DLN2_ADC_ID)
>> >> > +#define DLN2_ADC_DISABLE           DLN2_CMD(0x03, DLN2_ADC_ID)
>> >> > +#define DLN2_ADC_CHANNEL_ENABLE            DLN2_CMD(0x05, DLN2_ADC_ID)
>> >> > +#define DLN2_ADC_CHANNEL_DISABLE   DLN2_CMD(0x06, DLN2_ADC_ID)
>> >> > +#define DLN2_ADC_SET_RESOLUTION            DLN2_CMD(0x08, DLN2_ADC_ID)
>> >> > +#define DLN2_ADC_CHANNEL_GET_VAL   DLN2_CMD(0x0A, DLN2_ADC_ID)
>> >> > +#define DLN2_ADC_CHANNEL_GET_ALL_VAL       DLN2_CMD(0x0B, DLN2_ADC_ID)
>> >> > +#define DLN2_ADC_CHANNEL_SET_CFG   DLN2_CMD(0x0C, DLN2_ADC_ID)
>> >> > +#define DLN2_ADC_CHANNEL_GET_CFG   DLN2_CMD(0x0D, DLN2_ADC_ID)
>> >> > +#define DLN2_ADC_CONDITION_MET_EV  DLN2_CMD(0x10, DLN2_ADC_ID)
>> >> > +
>> >> > +#define DLN2_ADC_EVENT_NONE                0
>> >> > +#define DLN2_ADC_EVENT_BELOW               1
>> >> > +#define DLN2_ADC_EVENT_LEVEL_ABOVE 2
>> >> > +#define DLN2_ADC_EVENT_OUTSIDE             3
>> >> > +#define DLN2_ADC_EVENT_INSIDE              4
>> >> > +#define DLN2_ADC_EVENT_ALWAYS              5
>> >> > +
>> >> > +#define DLN2_ADC_MAX_CHANNELS 8
>> >> > +#define DLN2_ADC_DATA_BITS 10
>> >> > +
>> >> > +struct dln2_adc {
>> >> > +   struct platform_device *pdev;
>> >> > +   int port;
>> >> > +   struct iio_trigger *trig;
>> >> > +   struct mutex mutex;
>> >> > +   /* Set once initialized */
>> >> > +   bool port_enabled;
>> >> > +   /* Set once resolution request made to HW */
>> >> > +   bool resolution_set;
>> >> > +   /* Bitmask requesting enabled channels */
>> >> > +   unsigned long chans_requested;
>> >> > +   /* Bitmask indicating enabled channels on HW */
>> >> > +   unsigned long chans_enabled;
>> >> > +   /* Channel that is arbitrated for event trigger */
>> >> > +   int trigger_chan;
>> >> > +};
>> >> > +
>> >> > +struct dln2_adc_port_chan {
>> >> > +   u8 port;
>> >> > +   u8 chan;
>> >> > +};
>> >> > +
>> >> > +struct dln2_adc_get_all_vals {
>> >> > +   __le16 channel_mask;
>> >> > +   __le16 values[DLN2_ADC_MAX_CHANNELS];
>> >> > +};
>> >> > +
>> >> > +static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
>> >> > +{
>> >> > +   int ret;
>> >> > +   u8 port = dln2->port;
>> >> > +   u8 count;
>> >> > +   int olen = sizeof(count);
>> >> > +
>> >> > +   ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
>> >> > +                       &port, sizeof(port), &count, &olen);
>> >> > +   if (ret < 0) {
>> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> >> > +           return ret;
>> >> > +   }
>> >> > +   if (olen < sizeof(count))
>> >> > +           return -EPROTO;
>> >> > +
>> >> > +   return count;
>> >> > +}
>> >> > +
>> >> > +static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
>> >> > +{
>> >> > +   int ret;
>> >> > +   struct dln2_adc_port_chan port_chan = {
>> >> > +           .port = dln2->port,
>> >> > +           .chan = DLN2_ADC_DATA_BITS,
>> >> > +   };
>> >> > +
>> >> > +   ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
>> >> > +                          &port_chan, sizeof(port_chan));
>> >> > +   if (ret < 0) {
>> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> >> > +           return ret;
>> >> > +   }
>> >> > +
>> >> > +   return 0;
>> >> > +}
>> >> > +
>> >> > +static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
>> >> > +                                int channel, bool enable)
>> >> > +{
>> >> > +   int ret;
>> >> > +   struct dln2_adc_port_chan port_chan = {
>> >> > +           .port = dln2->port,
>> >> > +           .chan = channel,
>> >> > +   };
>> >> > +
>> >> > +   u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
>> >> > +
>> >> > +   ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
>> >> > +   if (ret < 0) {
>> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> >> > +           return ret;
>> >> > +   }
>> >> > +
>> >> > +   return 0;
>> >> > +}
>> >> > +
>> >> > +static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable)
>> >> > +{
>> >> > +   int ret;
>> >> > +   u8 port = dln2->port;
>> >> > +   __le16 conflict;
>> >> > +   int olen = sizeof(conflict);
>> >> > +
>> >> > +   u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
>> >> > +
>> >> > +   ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
>> >> > +                       &conflict, &olen);
>> >> > +   if (ret < 0) {
>> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
>> >> > +                   __func__, (int)enable);
>> >> > +           return ret;
>> >> > +   }
>> >> > +   if (enable && olen < sizeof(conflict))
>> >> > +           return -EPROTO;
>> >> > +
>> >> > +   return 0;
>> >> > +}
>> >> > +
>> >> > +/*
>> >> > + * ADC channels are lazily enabled due to the pins being shared with GPIO
>> >> > + * channels. Enabling channels requires taking the ADC port offline, specifying
>> >> > + * the resolution, individually enabling channels, then putting the port back
>> >> > + * online. If GPIO pins have already been exported by gpio_dln2, EINVAL is
>> >> > + * reported.
>> >> > + */
>> > Ah, now it's all starting to make sense.  Let me check I have this right.
>> >
>> > 1) You have an 8 channel adc.
>> > 2) You can only (or it's more efficient) to read all 8 channels at once.
>> > 3) As the pins are shared, not all 8 channels are actually wired to anything.
>> > 4) This prevents you just using an available_scan_mask entry to enable them all.
>> >    Hence you have rolled your own demux.  If you need to do this quick then
>> >    take a look at the precomputed mux tables in the core.  If not I guess
>> >    what you have works fine.
>> Yes, that code was borrowed from the simple dummy example module.
>> I'm new to IIO, so I didn't realize a more efficient remuxing
>> mechanism was available. I can look into integrating that on
>> Monday.
> I'm thinking it's a non starter because of the lazy enabling, but maybe
> there is some way to make it work.
>> >> > +static int dln2_adc_update_enabled_chans(struct dln2_adc *dln2)
>> >> > +{
>> >> > +   int ret, i, chan_count;
>> >> > +   struct iio_dev *indio_dev;
>> >> > +
>> >> > +   if (dln2->chans_enabled == dln2->chans_requested)
>> >> > +           return 0;
>> >> > +
>> >> > +   indio_dev = platform_get_drvdata(dln2->pdev);
>> >> > +   chan_count = indio_dev->num_channels;
>> >> > +
>> >> > +   if (dln2->port_enabled) {
>> >> > +           ret = dln2_adc_set_port_enabled(dln2, false);
>> >> > +           if (ret < 0)
>> >> > +                   return ret;
>> >> > +           dln2->port_enabled = false;
>> >> > +   }
>> >> > +
>> >> > +   if (!dln2->resolution_set) {
>> >> > +           ret = dln2_adc_set_port_resolution(dln2);
>> >> > +           if (ret < 0)
>> >> > +                   return ret;
>> >> > +           dln2->resolution_set = true;
>> >> > +   }
>> >> > +
>> >> > +   for (i = 0; i < chan_count; ++i) {
>> >> > +           bool requested = dln2->chans_requested & (1 << i);
>> >> > +           bool enabled = dln2->chans_enabled & (1 << i);
>> >> > +
>> >> > +           if (requested == enabled)
>> >> > +                   continue;
>> >> > +           ret = dln2_adc_set_chan_enabled(dln2, i, requested);
>> >> > +           if (ret < 0)
>> >> > +                   return ret;
>> >> > +   }
>> >> > +
>> >> > +   dln2->chans_enabled = dln2->chans_requested;
>> >> > +
>> >> > +   ret = dln2_adc_set_port_enabled(dln2, true);
>> >> > +   if (ret < 0)
>> >> > +           return ret;
>> >> > +   dln2->port_enabled = true;
>> >> > +
>> >> > +   return 0;
>> >> > +}
>> >> > +
>> >> > +static int dln2_adc_get_chan_freq(struct dln2_adc *dln2, unsigned int channel)
>> >> > +{
>> >> > +   int ret;
>> >> > +   struct dln2_adc_port_chan port_chan = {
>> >> > +           .port = dln2->port,
>> >> > +           .chan = channel,
>> >> > +   };
>> >> > +   struct {
>> >> > +           __u8 type;
>> >> > +           __le16 period;
>> >> > +           __le16 low;
>> >> > +           __le16 high;
>> >> > +   } __packed get_cfg;
>> >> > +   int olen = sizeof(get_cfg);
>> >> > +
>> >> > +   ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_CFG,
>> >> > +                       &port_chan, sizeof(port_chan), &get_cfg, &olen);
>> >> > +   if (ret < 0) {
>> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> >> > +           return ret;
>> >> > +   }
>> >> > +   if (olen < sizeof(get_cfg))
>> >> > +           return -EPROTO;
>> >> > +
>> >> > +   return get_cfg.period;
>> > Returning a value called period for a function claiming to be reading the
>> > frequency is unusual. It's got to be converted to the frequency.
>> Yea, I screwed up there. Diolan's command interface uses periods
>> in milliseconds so I'll make sure the necessary reciprocal gets
>> put in and the function is clearly named.
>> >> > +}
>> >> > +
>> >> > +static int dln2_adc_set_chan_freq(struct dln2_adc *dln2, unsigned int channel,
>> >> > +                             unsigned int freq)
>> >> > +{
>> >> > +   int ret;
>> >> > +   struct {
>> >> > +           struct dln2_adc_port_chan port_chan;
>> >> > +           __u8 type;
>> >> > +           __le16 period;
>> >> > +           __le16 low;
>> >> > +           __le16 high;
>> >> > +   } __packed set_cfg = {
>> >> > +           .port_chan.port = dln2->port,
>> >> > +           .port_chan.chan = channel,
>> >> > +           .type = freq ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
>> >> > +           .period = cpu_to_le16(freq)
>> >> > +   };
>> >> > +
>> >> > +   ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
>> >> > +                          &set_cfg, sizeof(set_cfg));
>> >> > +   if (ret < 0) {
>> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> >> > +           return ret;
>> >> > +   }
>> >> > +   return 0;
>> >> > +}
>> >> > +
>> >> > +static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
>> >> > +{
>> >> > +   int ret;
>> >> > +   int old_chans_requested = dln2->chans_requested;
>> >> > +
>> >> > +   dln2->chans_requested |= (1 << channel);
>> > Ah, so this changes the enabled channels and can lead to the
>> > changes in what is enabled when buffered mode is going on.
>> >
>> > I'd just use the iio_claim_direct_mode set of functions
>> > to prevent direct reads when the buffer is operating.
>> > They add a lot of complexity for what is usually a fairly
>> > unusual requirement.
>> Will do.
>> >> > +   ret = dln2_adc_update_enabled_chans(dln2);
>> >> > +   if (ret < 0) {
>> >> > +           dln2->chans_requested = old_chans_requested;
>> >> > +           return ret;
>> >> > +   }
>> >> > +   dln2->port_enabled = true;
>> >> > +
>> >> > +   struct dln2_adc_port_chan port_chan = {
>> >> > +           .port = dln2->port,
>> >> > +           .chan = channel,
>> >> > +   };
>> >> > +   __le16 value;
>> >> > +   int olen = sizeof(value);
>> >> > +
>> >> > +   ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
>> >> > +                       &port_chan, sizeof(port_chan), &value, &olen);
>> >> > +   if (ret < 0) {
>> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> >> > +           return ret;
>> >> > +   }
>> >> > +   if (olen < sizeof(value))
>> >> > +           return -EPROTO;
>> >> > +
>> >> > +   return le16_to_cpu(value);
>> >> > +}
>> >> > +
>> >> > +static int dln2_adc_read_all(struct dln2_adc *dln2,
>> >> > +                        struct dln2_adc_get_all_vals *get_all_vals)
>> >> > +{
>> >> > +   int ret;
>> >> > +   __u8 port = dln2->port;
>> >> > +   int olen = sizeof(*get_all_vals);
>> >> > +
>> >> > +   ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
>> >> > +                       &port, sizeof(port), get_all_vals, &olen);
>> > Getting channels that aren't wired to anything?  Fair enough if true,
>> > but unusual.
>> I had the same thought myself; the adc example in Diolan's
>> package reads in a fixed 8 channels every time, apparently
>> zeroing the disabled ones.
> It would make their firmware simpler probably.  Push the complexity to
> the more powerful main processor.
>> >> > +   if (ret < 0) {
>> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> >> > +           return ret;
>> >> > +   }
>> >> > +   if (olen < sizeof(*get_all_vals))
>> >> > +           return -EPROTO;
>> >> > +
>> >> > +   return 0;
>> >> > +}
>> >> > +
>> >> > +static int dln2_adc_read_raw(struct iio_dev *indio_dev,
>> >> > +                        struct iio_chan_spec const *chan,
>> >> > +                        int *val,
>> >> > +                        int *val2,
>> >> > +                        long mask)
>> >> > +{
>> >> > +   int ret;
>> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
>> >> > +
>> >> > +   switch (mask) {
>> >> > +   case IIO_CHAN_INFO_RAW:
>> >> > +           mutex_lock(&dln2->mutex);
>> >> > +           ret = dln2_adc_read(dln2, chan->channel);
>> >> > +           mutex_unlock(&dln2->mutex);
>> >> > +
>> >> > +           if (ret < 0)
>> >> > +                   return ret;
>> >> > +
>> >> > +           *val = ret;
>> >> > +           return IIO_VAL_INT;
>> >> > +
>> >> > +   case IIO_CHAN_INFO_SCALE:
>> >> > +           *val = 0;
>> >> > +           /* 3.3 / (1 << 10) * 1000000000 */
>> >> > +           *val2 = 3222656;
>> >> > +           return IIO_VAL_INT_PLUS_NANO;
>> >> > +
>> >> > +   case IIO_CHAN_INFO_SAMP_FREQ:
>> >> > +           mutex_lock(&dln2->mutex);
>> >> > +           if (dln2->trigger_chan == -1)
>> >> > +                   ret = 0;
>> >> > +           else
>> >> > +                   ret = dln2_adc_get_chan_freq(dln2, dln2->trigger_chan);
>> >> > +           mutex_unlock(&dln2->mutex);
>> >> > +
>> >> > +           if (ret < 0)
>> >> > +                   return ret;
>> >> > +
>> >> > +           *val = ret / 1000;
>> >> > +           *val2 = (ret % 1000) * 1000;
>> >> > +           return IIO_VAL_INT_PLUS_MICRO;
>> >> > +
>> >> > +   default:
>> >> > +           return -EINVAL;
>> >> > +   }
>> >> > +}
>> >> > +
>> >> > +static int dln2_adc_write_raw(struct iio_dev *indio_dev,
>> >> > +                         struct iio_chan_spec const *chan,
>> >> > +                         int val,
>> >> > +                         int val2,
>> >> > +                         long mask)
>> >> > +{
>> >> > +   int ret;
>> >> > +   unsigned int freq;
>> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
>> >> > +
>> >> > +   switch (mask) {
>> >> > +   case IIO_CHAN_INFO_SAMP_FREQ:
>> >> > +           freq = val * 1000 + val2 / 1000;
>> >> > +           if (freq > 65535) {
>> >> > +                   freq = 65535;
>> >> > +                   dev_warn(&dln2->pdev->dev,
>> >> > +                            "clamping freq to 65535ms\n");
>> > frequency measured in a unit of time?
>> Will fix Monday
>> >> > +           }
>> >> > +           mutex_lock(&dln2->mutex);
>> >> > +
>> >> > +           /*
>> >> > +            * The first requested channel is arbitrated as a shared
>> >> > +            * trigger source, so only one event is registered with the DLN.
>> >> > +            * The event handler will then read all enabled channel values
>> >> > +            * using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
>> >> > +            * synchronization between ADC readings.
>> >> > +            */
>> >> > +           if (dln2->trigger_chan == -1)
>> >> > +                   dln2->trigger_chan = chan->channel;
>> > This looks dangerous. either it is always fine to use a random channel or it's
>> > not...
>> >
>> > I'd cache the frequency and only actually set it when bringing up the buffer
>> > - that way if the setup is sane you will have a channel.
>> Fair point, I'll refactor it that way.
>> >> > +           ret = dln2_adc_set_chan_freq(dln2, dln2->trigger_chan, freq);
>> >> > +
>> >> > +           mutex_unlock(&dln2->mutex);
>> >> > +
>> >> > +           if (ret < 0)
>> >> > +                   return ret;
>> >> > +
>> >> > +           return 0;
>> > Just return ret and loose the other path.  I chased through the code
>> > and we get 0 for success.
>> Ok
>> >> > +
>> >> > +   default:
>> >> > +           return -EINVAL;
>> >> > +   }
>> >> > +}
>> >> > +
>> >> > +#define DLN2_ADC_CHAN(idx) {                                       \
>> >> > +   .type = IIO_VOLTAGE,                                    \
>> >> > +   .channel = idx,                                         \
>> >> > +   .indexed = 1,                                           \
>> >> > +   .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
>> >> > +   .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |   \
>> >> > +                              BIT(IIO_CHAN_INFO_SAMP_FREQ),\
>> >> > +   .scan_index = idx,                                      \
>> > .scan_type = {
>> >         .sign = 'u', etc is easier to read.
>> Ok
>> >> > +   .scan_type.sign = 'u',                                  \
>> >> > +   .scan_type.realbits = DLN2_ADC_DATA_BITS,               \
>> >> > +   .scan_type.storagebits = 16,                            \
>> >> > +   .scan_type.endianness = IIO_LE,                         \
>> >> > +}
>> >> > +
>> >> > +static const struct iio_chan_spec dln2_adc_iio_channels[] = {
>> >> > +   DLN2_ADC_CHAN(0),
>> >> > +   DLN2_ADC_CHAN(1),
>> >> > +   DLN2_ADC_CHAN(2),
>> >> > +   DLN2_ADC_CHAN(3),
>> >> > +   DLN2_ADC_CHAN(4),
>> >> > +   DLN2_ADC_CHAN(5),
>> >> > +   DLN2_ADC_CHAN(6),
>> >> > +   DLN2_ADC_CHAN(7),
>> >> > +   IIO_CHAN_SOFT_TIMESTAMP(8),
>> >> > +};
>> >> > +
>> >> > +static const struct iio_info dln2_adc_info = {
>> >> > +   .read_raw = dln2_adc_read_raw,
>> >> > +   .write_raw = dln2_adc_write_raw,
>> >> > +   .driver_module = THIS_MODULE,
>> >> > +};
>> >> > +
>> >> > +static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
>> >> > +{
>> >> > +   struct iio_poll_func *pf = p;
>> >> > +   struct iio_dev *indio_dev = pf->indio_dev;
>> >> > +   struct {
>> >> > +           __le16 values[DLN2_ADC_MAX_CHANNELS];
>> >> > +           int64_t timestamp_space;
>> >> > +   } data;
>> >> > +   struct dln2_adc_get_all_vals dev_data;
>> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
>> >> > +   int old_chans_requested;
>> >> > +   int i, j;
>> >> > +
>> >> > +   mutex_lock(&dln2->mutex);
>> >> > +
>> >> > +   old_chans_requested = dln2->chans_requested;
>> >> > +   dln2->chans_requested |= *indio_dev->active_scan_mask;
>> >> > +   if (dln2_adc_update_enabled_chans(dln2) < 0) {
>> >> > +           dln2->chans_requested = old_chans_requested;
>> > I'm confused. Why would this change here?  Shouldn't change
>> > whilst data is being captured unless something very odd is going on.
>> > Ah, found it - you want to allow sysfs reads at the same time.
>> > Given the complexity required to do that safely, I'd just not
>> > do it for now - maybe add it as a follow up patch later.
>> >> > +           mutex_unlock(&dln2->mutex);
>> >> > +           goto done;
>> >> > +   }
>> >> > +
>> >> > +   if (dln2_adc_read_all(dln2, &dev_data) < 0) {
>> >> > +           mutex_unlock(&dln2->mutex);
>> >> > +           goto done;
>> >> > +   }
>> >> > +
>> >> > +   mutex_unlock(&dln2->mutex);
>> >> > +
>> >> > +   for (i = 0, j = 0;
>> >
>> > Having found the note about lazy enabling above I think I now
>> > understand what you are doing here.  You can't use the
>> > core infrastructure and set available_scan_masks to 0x1FF because
>> > that would result in enabling all the pins, some of which are wanted
>> > for gpio usage perhaps.
>> >
>> > Hence you end up rolling your own demux.  You are reading all
>> > the ADC channels, but some aren't necessarily connected to any actual
>> > pins.
>> >
>> Precisely. The lazy enabling adapts to the demand of the user.
>> Since IIO doesn't really have an export mechanism like the GPIO
>> subsystem, I can't preemptively enable channels unless I make the
>> driver buffer-only (which I'd rather not do cause direct reads
>> come in handy).
> one of those fiddly 'boundary' cases for defining connectivity.
> If we were talking hard wired pins on a board this would all go
> in device tree, but in reality most of the time we have
> no idea what is connected to a given DLN2.
>> >> > +        i < bitmap_weight(indio_dev->active_scan_mask,
>> >> > +                          indio_dev->masklength); i++, j++) {
>> >> > +           j = find_next_bit(indio_dev->active_scan_mask,
>> >> > +                             indio_dev->masklength, j);
>> >> > +           data.values[i] = dev_data.values[j];
>> >> > +   }
>> >> > +
>> >> > +   iio_push_to_buffers_with_timestamp(indio_dev, &data,
>> >> > +                                      iio_get_time_ns(indio_dev));
>> >> > +
>> >> > +done:
>> >> > +   iio_trigger_notify_done(indio_dev->trig);
>> >> > +   return IRQ_HANDLED;
>> >> > +}
>> >> > +
>> >> > +static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
>> >> > +{
>> >> > +   int ret;
>> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
>> >> > +
>> >> > +   mutex_lock(&dln2->mutex);
>> >> > +   dln2->chans_requested |= *indio_dev->active_scan_mask;
>> > Perhaps should be in the update_scan_mask callback rather than here?
>> > You could have multiple consumers of this data which would cause
>> > this path to be 'interesting'.
>> >
>> > I'm also a little curious to know why it isn't just an =
>> > Also why don't you want to disable them when then buffer is disabled.
>> This allows the enabled channels to remain enabled if the user
>> opts for direct raw reads. If I were to enable a channel
>> temporarily for a raw read, that's 6 or 7 synchronous commands:
>> - Set resolution (if not already done)
>> - Enable channel
>> - Enable port
>> - Get value
>> - Get value (a second one is needed because the first read after
>>   enabling always seems to return 0, must be a firmware bug)
>> - Disable port
>> - Disable channel
>>
>> This just strikes me as inefficient, but it would be more
>> consistent state-wise. If you think this is an ok trade-off,
>> I'll go for the enable/disable approach. I suppose it's also
>> possible to reset the lazy channel mask when the user exits
>> buffered mode. The initial zero-return after enabling channels is
>> a real kicker, and makes me want to keep disables/enables to a
>> minimum.
> That is a pain.  Still more consistent to clear the channel mask
> again after a buffer disable + prevent single channel reads
> whilst it is running.  At least, do this for the first merge
> of the driver then we can revisit if you have a usecase
> where the juggling is really necessary.
>
>> >> > +   ret = dln2_adc_update_enabled_chans(dln2);
>> >> > +   mutex_unlock(&dln2->mutex);
>> >> > +   if (ret < 0) {
>> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> >> > +           return ret;
>> >> > +   }
>> >> > +
>> >> > +   return iio_triggered_buffer_postenable(indio_dev);
>> >> > +}
>> >> > +
>> >> > +static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
>> >> > +   .postenable = dln2_adc_triggered_buffer_postenable,
>> >> > +   .predisable = iio_triggered_buffer_predisable,
>> >> > +};
>> >> > +
>> >> > +static void dln2_adc_event(struct platform_device *pdev, u16 echo,
>> >> > +                      const void *data, int len)
>> >> > +{
>> >> > +   struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
>> >> > +
>> >> > +   iio_trigger_poll(dln2->trig);
>> > Is this in interrupt context?
>> > if not should be iio_trigger_poll_chained(dln2->trig);
>> I'll double check that on Monday, the MFD code would determine
>> that.
>> >> > +}
>> >> > +
>> >> > +static const struct iio_trigger_ops dln2_adc_trigger_ops = {
>> >> > +   .owner = THIS_MODULE,
>> > Hohum.  Patch set to remove the need for this is waiting for me
>> > to get around to revising it.  Interestingly checking why you
>> > had this at all lead me to a check in the trigger register that
>> > I wasn't getting rid of in that patch set. oops.
>> >
>> > Upshot is that the equivalent is done via macro magic rather
>> > than an explicit entry in this structure.  It's possible this
>> > driver will cross with that series, in which case we'll get
>> > some build issues that will need fixing up.  I'll hopefully
>> > remember to deal with them when applying though.
>> >> > +};
>> >> > +
>> >> > +static int dln2_adc_probe(struct platform_device *pdev)
>> >> > +{
>> >> > +   struct device *dev = pdev->dev;
>> >> > +   struct dln2_adc *dln2;
>> >> > +   struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
>> >> > +   struct iio_dev *indio_dev;
>> >> > +   struct iio_buffer *buffer;
>> >> > +   int ret;
>> >> > +   int chans;
>> >> > +
>> >> > +   indio_dev = devm_iio_device_alloc(dev, sizeof(struct dln2_adc));
>> > Slight preference for sizeof(*dln2)
>> Agreed
>> >> > +   if (!indio_dev) {
>> >> > +           dev_err(dev, "failed allocating iio device\n");
>> >> > +           return -ENOMEM;
>> >> > +   }
>> >> > +
>> >> > +   dln2 = iio_priv(indio_dev);
>> >> > +   dln2->pdev = pdev;
>> >> > +   dln2->port = pdata->port;
>> >> > +   mutex_init(&dln2->mutex);
>> >> > +   dln2->port_enabled = false;
>> >> > +   dln2->resolution_set = false;
>> >> > +   dln2->chans_requested = 0;
>> >> > +   dln2->chans_enabled = 0;
>> >> > +   dln2->trigger_chan = -1;
>> > This is fine at setup, but isn't reset to -1 if the channels are all disabled.
>> Ok
>> >> > +
>> >> > +   platform_set_drvdata(pdev, indio_dev);
>> >> > +
>> >> > +   chans = dln2_adc_get_chan_count(dln2);
>> >> > +   if (chans < 0) {
>> >> > +           dev_err(dev, "failed to get channel count: %d\n", chans);
>> >> > +           ret = chans;
>> >> > +           goto dealloc_dev;
>> >> > +   }
>> >> > +   if (chans > DLN2_ADC_MAX_CHANNELS) {
>> >> > +           chans = DLN2_ADC_MAX_CHANNELS;
>> >> > +           dev_warn(dev, "clamping channels to %d\n",
>> >> > +                    DLN2_ADC_MAX_CHANNELS);
>> >> > +   }
>> >> > +
>> >> > +   indio_dev->name = DLN2_ADC_MOD_NAME;
>> >> > +   indio_dev->dev.parent = dev;
>> >> > +   indio_dev->info = &dln2_adc_info;
>> >> > +   indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
>> >> > +   indio_dev->channels = dln2_adc_iio_channels;
>> >> > +   indio_dev->num_channels = chans + 1;
>> > Why +1?  Presumably to include the timestamp.  I guess this
>> > means that the code that gets the channel count always gets 8.
>> > You need to check that if you are going to use a const channel array.
>> Yea, I see in the docs that's supposed to be monotonic.
>> I'll assemble that array on the stack instead at probe time to be
>> safe.
>> >> > +   indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
>> >> > +
>> >> > +   dln2->trig = devm_iio_trigger_alloc(dev, "samplerate");
>> > Interesting naming for a trigger... rather too generic + doesn't
>> > cope with multiple dln2's connect to same machine. Admittedly
>> > a number of historical drivers have that problem but we can't
>> > fix them now without ABI breakage.
>> >
>> Good point, is there a preferred unique identifier to suffix that
>> name?
> we've never restricted this particularly as it's just a magic string
> to match things up.
>
> Quite a few drivers stick with "magicname-dev%d",  ..., indio_dev->id
>> >> > +   if (!dln2->trig) {
>> >> > +           dev_err(dev, "failed to allocate trigger\n");
>> >> > +           ret = -ENOMEM;
>> >> > +           goto dealloc_dev;
>> >> > +   }
>> >> > +   dln2->trig->ops = &dln2_adc_trigger_ops;
>> >> > +   iio_trigger_set_drvdata(dln2->trig, dln2);
>> >> > +   iio_trigger_register(dln2->trig);
>> >> > +   iio_trigger_set_immutable(indio_dev, dln2->trig);
>> >> > +
>> >> > +   buffer = devm_iio_kfifo_allocate(dev);
>> >> > +   if (!buffer) {
>> >> > +           dev_err(dev, "failed to allocate kfifo\n");
>> >> > +           ret = -ENOMEM;
>> >> > +           goto dealloc_trigger;
>> >> > +   }
>> >> > +
>> >> > +   iio_device_attach_buffer(indio_dev, buffer);
>> >> > +
>> >> > +   indio_dev->pollfunc = iio_alloc_pollfunc(NULL,
>> >> > +                                            &dln2_adc_trigger_h,
>> >> > +                                            IRQF_ONESHOT,
>> >> > +                                            indio_dev,
>> >> > +                                            "samplerate");
>> > Again very generic naming + shouldn't be the same as the trigger name.
>> > Almost no drivers do this manually any more.  Why does the
>> > iio_triggered_buffer_setup boilerplate function not work here?
>> I read about this in the IIO presentation from back when the
>> subsystem was first introduced. Didn't realize it was
>> depreciated.
> It's not deprecated as such, just that there are utility functions
> to make the job easier if you happen to fit with the standard forms.
> Just code repetition reduction really.  This approach may still
> be necessary if something more 'interesting' is going on.
>> >> > +
>> >> > +   if (!indio_dev->pollfunc) {
>> >> > +           ret = -ENOMEM;
>> >> > +           goto dealloc_kfifo;
>> >> > +   }
>> >> > +
>> >> > +   ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
>> >> > +                                dln2_adc_event);
>> >> > +   if (ret) {
>> >> > +           dev_err(dev, "failed to register event cb: %d\n", ret);
>> >> > +           goto dealloc_pollfunc;
>> >> > +   }
>> >> > +
>> >> > +   ret = iio_device_register(indio_dev);
>> >> > +   if (ret) {
>> >> > +           dev_err(dev, "failed to register iio device: %d\n", ret);
>> >> > +           goto dealloc_pollfunc;
>> >> > +   }
>> >> > +
>> >> > +   return 0;
>> >> > +
>> >> > +dealloc_pollfunc:
>> >> > +   iio_dealloc_pollfunc(indio_dev->pollfunc);
>> >> > +dealloc_kfifo:
>> >> > +dealloc_trigger:
>> >> > +   iio_trigger_unregister(dln2->trig);
>> >> > +dealloc_dev:
>> >> > +
>> >> > +   return ret;
>> >> > +}
>> >> > +
>> >> > +static int dln2_adc_remove(struct platform_device *pdev)
>> >> > +{
>> >> > +   struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
>> >> > +
>> >> > +   iio_device_unregister(indio_dev);
>> >> > +   dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
>> >> > +   iio_trigger_unregister(dln2->trig);
>> >> > +   iio_dealloc_pollfunc(indio_dev->pollfunc);
>> >> > +
>> >> > +   return 0;
>> >> > +}
>> >> > +
>> >> > +static struct platform_driver dln2_adc_driver = {
>> >> > +   .driver.name    = DLN2_ADC_MOD_NAME,
>> >> > +   .probe          = dln2_adc_probe,
>> >> > +   .remove         = dln2_adc_remove,
>> >> > +};
>> >> > +
>> >> > +module_platform_driver(dln2_adc_driver);
>> >> > +
>> >> > +MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
>> >> > +MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
>> >> > +MODULE_LICENSE("GPL v2");
>> >> > +MODULE_ALIAS("platform:dln2-adc");
>> >> > diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
>> >> > index 704e189..a22ab8c 100644
>> >> > --- a/drivers/mfd/dln2.c
>> >> > +++ b/drivers/mfd/dln2.c
>> >> > @@ -53,6 +53,7 @@ enum dln2_handle {
>> >> >     DLN2_HANDLE_GPIO,
>> >> >     DLN2_HANDLE_I2C,
>> >> >     DLN2_HANDLE_SPI,
>> >> > +   DLN2_HANDLE_ADC,
>> >> >     DLN2_HANDLES
>> >> >  };
>> >> >
>> >> > @@ -663,6 +664,12 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
>> >> >     .port = 0,
>> >> >  };
>> >> >
>> >> > +/* Only one ADC port supported */
>> >> > +static struct dln2_platform_data dln2_pdata_adc = {
>> >> > +   .handle = DLN2_HANDLE_ADC,
>> >> > +   .port = 0,
>> >> > +};
>> >> > +
>> >> >  static const struct mfd_cell dln2_devs[] = {
>> >> >     {
>> >> >             .name = "dln2-gpio",
>> >> > @@ -679,6 +686,11 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
>> >> >             .platform_data = &dln2_pdata_spi,
>> >> >             .pdata_size = sizeof(struct dln2_platform_data),
>> >> >     },
>> >> > +   {
>> >> > +           .name = "dln2-adc",
>> >> > +           .platform_data = &dln2_pdata_adc,
>> >> > +           .pdata_size = sizeof(struct dln2_platform_data),
>> >> > +   },
>> >> >  };
>> >> >
>> >> >  static void dln2_stop(struct dln2_dev *dln2)
>> >> >
>> >>
>> >
>

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

* Re: [PATCH v2] iio: adc: Add support for DLN2 ADC
  2017-06-25 17:06           ` Jack Andersen
@ 2017-06-26 16:51             ` Jonathan Cameron
  0 siblings, 0 replies; 23+ messages in thread
From: Jonathan Cameron @ 2017-06-26 16:51 UTC (permalink / raw)
  To: Jack Andersen
  Cc: Peter Meerwald-Stadler, Hartmut Knaack, Lars-Peter Clausen,
	Lee Jones, linux-iio

On Sun, 25 Jun 2017 07:06:17 -1000
Jack Andersen <jackoalan@gmail.com> wrote:

> Ok, the temporary enables for raw reads are fine with me; my
> applications all use buffered access anyway.
> 
> It would be better to allow the dynamic release of unused ADC
> channels for GPIO pins, which lazy enabling prevents.
Why does it prevent it?  It moves the failure point later, but that's
all.  So if you end up with a pin being assigned to be ADC after
it has been grabbed as a gpio, fail at the update_scan_mask call
-EBUSY would make sense I think.

I guess it might take some effort to make that info available though...

J
> 
> On 25 June 2017 at 05:58, Jonathan Cameron <jic23@kernel.org> wrote:
> > On Sat, 24 Jun 2017 12:18:58 -1000
> > Jack Andersen <jackoalan@gmail.com> wrote:
> >  
> >> You're right about the fiddly device part. The important thing
> >> to note about the DLN2 is it's a multifunction device with a
> >> unified command message interface. All four functions (GPIO, ADC,
> >> I2C, SPI) use this interface for every possible action. Commands
> >> are performed synchronously and return at least an error code.
> >>
> >> The dln2.c module in MFD implements this command interface and
> >> manages 'handles' for all four functions as well as a single
> >> event handle for handling USB transfers of events like GPIO
> >> low/high and ADC thresholds / periodic timers. Existing modules
> >> like dln2_gpio and dln2_i2c all share the symbols exported by
> >> dln2.
> >>
> >> I'll revaluate the code when I'm back in the office on Monday.
> >>
> >> More explanations inline.
> >>
> >> On 24 June 2017 at 09:38, Jonathan Cameron <jic23@kernel.org> wrote:  
> >> > On Fri, 23 Jun 2017 09:25:58 +0200 (CEST)
> >> > Peter Meerwald-Stadler <pmeerw@pmeerw.net> wrote:
> >> >  
> >> >> > This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
> >> >> > ADC is the fourth and final component of the DLN2 for the kernel.  
> >> >>
> >> >> I am missing a changelog or information if and how review comments have
> >> >> been addressed
> >> >>
> >> >> thanks for the super-fast turnaround, anyway
> >> >>
> >> >> p.  
> >> > A few bits and pieces inline.  Fiddly device.  Out of curiosity
> >> > are then any relevant docs or is this a reverse engineering job?
> >> >
> >> > There were a few unusual elements so would be nice to verify they
> >> > line up with docs if available!
> >> >
> >> > Jonathan  
> >> Reverse engineering wasn't really necessary; Diolan has macros
> >> of all the command enums and structures in their userspace
> >> headers (for some reason). The calls implemented in their binary
> >> blob are simple libusb transfer wrappers; I essentially stuck to
> >> the transfer style used by dln2_gpio instead.
> >>
> >> For reference:
> >> http://dlnware.com/downloads/linux-setup
> >> Unzip and refer to examples/common/dln_adc.h  
> >> >>  
> >> >> > Signed-off-by: Jack Andersen <jackoalan@gmail.com>
> >> >> > ---
> >> >> >  drivers/iio/adc/Kconfig    |   9 +
> >> >> >  drivers/iio/adc/Makefile   |   1 +
> >> >> >  drivers/iio/adc/dln2-adc.c | 644 +++++++++++++++++++++++++++++++++++++++++++++
> >> >> >  drivers/mfd/dln2.c         |  12 +  
> >> > touching an mfd file so needs an ack from Lee. Cc'd.  
> >> >> >  4 files changed, 666 insertions(+)
> >> >> >  create mode 100644 drivers/iio/adc/dln2-adc.c
> >> >> >
> >> >> > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> >> >> > index 401f47b..78d7455 100644
> >> >> > --- a/drivers/iio/adc/Kconfig
> >> >> > +++ b/drivers/iio/adc/Kconfig
> >> >> > @@ -239,6 +239,15 @@ config DA9150_GPADC
> >> >> >       To compile this driver as a module, choose M here: the module will be
> >> >> >       called berlin2-adc.
> >> >> >
> >> >> > +config DLN2_ADC
> >> >> > +   tristate "Diolan DLN-2 ADC driver support"
> >> >> > +   depends on MFD_DLN2
> >> >> > +   help
> >> >> > +     Say yes here to build support for Diolan DLN-2 ADC.
> >> >> > +
> >> >> > +     This driver can also be built as a module. If so, the module will be
> >> >> > +     called adc_dln2.
> >> >> > +
> >> >> >  config ENVELOPE_DETECTOR
> >> >> >     tristate "Envelope detector using a DAC and a comparator"
> >> >> >     depends on OF
> >> >> > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> >> >> > index 9339bec..378bc65 100644
> >> >> > --- a/drivers/iio/adc/Makefile
> >> >> > +++ b/drivers/iio/adc/Makefile
> >> >> > @@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
> >> >> >  obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
> >> >> >  obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
> >> >> >  obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
> >> >> > +obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
> >> >> >  obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
> >> >> >  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
> >> >> >  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
> >> >> > diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
> >> >> > new file mode 100644
> >> >> > index 0000000..6c58b76
> >> >> > --- /dev/null
> >> >> > +++ b/drivers/iio/adc/dln2-adc.c
> >> >> > @@ -0,0 +1,644 @@
> >> >> > +/*
> >> >> > + * Driver for the Diolan DLN-2 USB-ADC adapter
> >> >> > + *
> >> >> > + * Copyright (c) 2017 Jack Andersen
> >> >> > + *
> >> >> > + * 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, version 2.
> >> >> > + */
> >> >> > +
> >> >> > +#include <linux/kernel.h>
> >> >> > +#include <linux/module.h>
> >> >> > +#include <linux/types.h>
> >> >> > +#include <linux/platform_device.h>
> >> >> > +#include <linux/mfd/dln2.h>
> >> >> > +
> >> >> > +#include <linux/iio/iio.h>
> >> >> > +#include <linux/iio/sysfs.h>
> >> >> > +#include <linux/iio/trigger.h>
> >> >> > +#include <linux/iio/trigger_consumer.h>
> >> >> > +#include <linux/iio/buffer.h>
> >> >> > +#include <linux/iio/kfifo_buf.h>
> >> >> > +
> >> >> > +#define DLN2_ADC_MOD_NAME "dln2-adc"
> >> >> > +
> >> >> > +#define DLN2_ADC_ID             0x06
> >> >> > +
> >> >> > +#define DLN2_ADC_GET_CHANNEL_COUNT DLN2_CMD(0x01, DLN2_ADC_ID)
> >> >> > +#define DLN2_ADC_ENABLE                    DLN2_CMD(0x02, DLN2_ADC_ID)
> >> >> > +#define DLN2_ADC_DISABLE           DLN2_CMD(0x03, DLN2_ADC_ID)
> >> >> > +#define DLN2_ADC_CHANNEL_ENABLE            DLN2_CMD(0x05, DLN2_ADC_ID)
> >> >> > +#define DLN2_ADC_CHANNEL_DISABLE   DLN2_CMD(0x06, DLN2_ADC_ID)
> >> >> > +#define DLN2_ADC_SET_RESOLUTION            DLN2_CMD(0x08, DLN2_ADC_ID)
> >> >> > +#define DLN2_ADC_CHANNEL_GET_VAL   DLN2_CMD(0x0A, DLN2_ADC_ID)
> >> >> > +#define DLN2_ADC_CHANNEL_GET_ALL_VAL       DLN2_CMD(0x0B, DLN2_ADC_ID)
> >> >> > +#define DLN2_ADC_CHANNEL_SET_CFG   DLN2_CMD(0x0C, DLN2_ADC_ID)
> >> >> > +#define DLN2_ADC_CHANNEL_GET_CFG   DLN2_CMD(0x0D, DLN2_ADC_ID)
> >> >> > +#define DLN2_ADC_CONDITION_MET_EV  DLN2_CMD(0x10, DLN2_ADC_ID)
> >> >> > +
> >> >> > +#define DLN2_ADC_EVENT_NONE                0
> >> >> > +#define DLN2_ADC_EVENT_BELOW               1
> >> >> > +#define DLN2_ADC_EVENT_LEVEL_ABOVE 2
> >> >> > +#define DLN2_ADC_EVENT_OUTSIDE             3
> >> >> > +#define DLN2_ADC_EVENT_INSIDE              4
> >> >> > +#define DLN2_ADC_EVENT_ALWAYS              5
> >> >> > +
> >> >> > +#define DLN2_ADC_MAX_CHANNELS 8
> >> >> > +#define DLN2_ADC_DATA_BITS 10
> >> >> > +
> >> >> > +struct dln2_adc {
> >> >> > +   struct platform_device *pdev;
> >> >> > +   int port;
> >> >> > +   struct iio_trigger *trig;
> >> >> > +   struct mutex mutex;
> >> >> > +   /* Set once initialized */
> >> >> > +   bool port_enabled;
> >> >> > +   /* Set once resolution request made to HW */
> >> >> > +   bool resolution_set;
> >> >> > +   /* Bitmask requesting enabled channels */
> >> >> > +   unsigned long chans_requested;
> >> >> > +   /* Bitmask indicating enabled channels on HW */
> >> >> > +   unsigned long chans_enabled;
> >> >> > +   /* Channel that is arbitrated for event trigger */
> >> >> > +   int trigger_chan;
> >> >> > +};
> >> >> > +
> >> >> > +struct dln2_adc_port_chan {
> >> >> > +   u8 port;
> >> >> > +   u8 chan;
> >> >> > +};
> >> >> > +
> >> >> > +struct dln2_adc_get_all_vals {
> >> >> > +   __le16 channel_mask;
> >> >> > +   __le16 values[DLN2_ADC_MAX_CHANNELS];
> >> >> > +};
> >> >> > +
> >> >> > +static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
> >> >> > +{
> >> >> > +   int ret;
> >> >> > +   u8 port = dln2->port;
> >> >> > +   u8 count;
> >> >> > +   int olen = sizeof(count);
> >> >> > +
> >> >> > +   ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
> >> >> > +                       &port, sizeof(port), &count, &olen);
> >> >> > +   if (ret < 0) {
> >> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> >> > +           return ret;
> >> >> > +   }
> >> >> > +   if (olen < sizeof(count))
> >> >> > +           return -EPROTO;
> >> >> > +
> >> >> > +   return count;
> >> >> > +}
> >> >> > +
> >> >> > +static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
> >> >> > +{
> >> >> > +   int ret;
> >> >> > +   struct dln2_adc_port_chan port_chan = {
> >> >> > +           .port = dln2->port,
> >> >> > +           .chan = DLN2_ADC_DATA_BITS,
> >> >> > +   };
> >> >> > +
> >> >> > +   ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
> >> >> > +                          &port_chan, sizeof(port_chan));
> >> >> > +   if (ret < 0) {
> >> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> >> > +           return ret;
> >> >> > +   }
> >> >> > +
> >> >> > +   return 0;
> >> >> > +}
> >> >> > +
> >> >> > +static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
> >> >> > +                                int channel, bool enable)
> >> >> > +{
> >> >> > +   int ret;
> >> >> > +   struct dln2_adc_port_chan port_chan = {
> >> >> > +           .port = dln2->port,
> >> >> > +           .chan = channel,
> >> >> > +   };
> >> >> > +
> >> >> > +   u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
> >> >> > +
> >> >> > +   ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
> >> >> > +   if (ret < 0) {
> >> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> >> > +           return ret;
> >> >> > +   }
> >> >> > +
> >> >> > +   return 0;
> >> >> > +}
> >> >> > +
> >> >> > +static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable)
> >> >> > +{
> >> >> > +   int ret;
> >> >> > +   u8 port = dln2->port;
> >> >> > +   __le16 conflict;
> >> >> > +   int olen = sizeof(conflict);
> >> >> > +
> >> >> > +   u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
> >> >> > +
> >> >> > +   ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
> >> >> > +                       &conflict, &olen);
> >> >> > +   if (ret < 0) {
> >> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
> >> >> > +                   __func__, (int)enable);
> >> >> > +           return ret;
> >> >> > +   }
> >> >> > +   if (enable && olen < sizeof(conflict))
> >> >> > +           return -EPROTO;
> >> >> > +
> >> >> > +   return 0;
> >> >> > +}
> >> >> > +
> >> >> > +/*
> >> >> > + * ADC channels are lazily enabled due to the pins being shared with GPIO
> >> >> > + * channels. Enabling channels requires taking the ADC port offline, specifying
> >> >> > + * the resolution, individually enabling channels, then putting the port back
> >> >> > + * online. If GPIO pins have already been exported by gpio_dln2, EINVAL is
> >> >> > + * reported.
> >> >> > + */  
> >> > Ah, now it's all starting to make sense.  Let me check I have this right.
> >> >
> >> > 1) You have an 8 channel adc.
> >> > 2) You can only (or it's more efficient) to read all 8 channels at once.
> >> > 3) As the pins are shared, not all 8 channels are actually wired to anything.
> >> > 4) This prevents you just using an available_scan_mask entry to enable them all.
> >> >    Hence you have rolled your own demux.  If you need to do this quick then
> >> >    take a look at the precomputed mux tables in the core.  If not I guess
> >> >    what you have works fine.  
> >> Yes, that code was borrowed from the simple dummy example module.
> >> I'm new to IIO, so I didn't realize a more efficient remuxing
> >> mechanism was available. I can look into integrating that on
> >> Monday.  
> > I'm thinking it's a non starter because of the lazy enabling, but maybe
> > there is some way to make it work.  
> >> >> > +static int dln2_adc_update_enabled_chans(struct dln2_adc *dln2)
> >> >> > +{
> >> >> > +   int ret, i, chan_count;
> >> >> > +   struct iio_dev *indio_dev;
> >> >> > +
> >> >> > +   if (dln2->chans_enabled == dln2->chans_requested)
> >> >> > +           return 0;
> >> >> > +
> >> >> > +   indio_dev = platform_get_drvdata(dln2->pdev);
> >> >> > +   chan_count = indio_dev->num_channels;
> >> >> > +
> >> >> > +   if (dln2->port_enabled) {
> >> >> > +           ret = dln2_adc_set_port_enabled(dln2, false);
> >> >> > +           if (ret < 0)
> >> >> > +                   return ret;
> >> >> > +           dln2->port_enabled = false;
> >> >> > +   }
> >> >> > +
> >> >> > +   if (!dln2->resolution_set) {
> >> >> > +           ret = dln2_adc_set_port_resolution(dln2);
> >> >> > +           if (ret < 0)
> >> >> > +                   return ret;
> >> >> > +           dln2->resolution_set = true;
> >> >> > +   }
> >> >> > +
> >> >> > +   for (i = 0; i < chan_count; ++i) {
> >> >> > +           bool requested = dln2->chans_requested & (1 << i);
> >> >> > +           bool enabled = dln2->chans_enabled & (1 << i);
> >> >> > +
> >> >> > +           if (requested == enabled)
> >> >> > +                   continue;
> >> >> > +           ret = dln2_adc_set_chan_enabled(dln2, i, requested);
> >> >> > +           if (ret < 0)
> >> >> > +                   return ret;
> >> >> > +   }
> >> >> > +
> >> >> > +   dln2->chans_enabled = dln2->chans_requested;
> >> >> > +
> >> >> > +   ret = dln2_adc_set_port_enabled(dln2, true);
> >> >> > +   if (ret < 0)
> >> >> > +           return ret;
> >> >> > +   dln2->port_enabled = true;
> >> >> > +
> >> >> > +   return 0;
> >> >> > +}
> >> >> > +
> >> >> > +static int dln2_adc_get_chan_freq(struct dln2_adc *dln2, unsigned int channel)
> >> >> > +{
> >> >> > +   int ret;
> >> >> > +   struct dln2_adc_port_chan port_chan = {
> >> >> > +           .port = dln2->port,
> >> >> > +           .chan = channel,
> >> >> > +   };
> >> >> > +   struct {
> >> >> > +           __u8 type;
> >> >> > +           __le16 period;
> >> >> > +           __le16 low;
> >> >> > +           __le16 high;
> >> >> > +   } __packed get_cfg;
> >> >> > +   int olen = sizeof(get_cfg);
> >> >> > +
> >> >> > +   ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_CFG,
> >> >> > +                       &port_chan, sizeof(port_chan), &get_cfg, &olen);
> >> >> > +   if (ret < 0) {
> >> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> >> > +           return ret;
> >> >> > +   }
> >> >> > +   if (olen < sizeof(get_cfg))
> >> >> > +           return -EPROTO;
> >> >> > +
> >> >> > +   return get_cfg.period;  
> >> > Returning a value called period for a function claiming to be reading the
> >> > frequency is unusual. It's got to be converted to the frequency.  
> >> Yea, I screwed up there. Diolan's command interface uses periods
> >> in milliseconds so I'll make sure the necessary reciprocal gets
> >> put in and the function is clearly named.  
> >> >> > +}
> >> >> > +
> >> >> > +static int dln2_adc_set_chan_freq(struct dln2_adc *dln2, unsigned int channel,
> >> >> > +                             unsigned int freq)
> >> >> > +{
> >> >> > +   int ret;
> >> >> > +   struct {
> >> >> > +           struct dln2_adc_port_chan port_chan;
> >> >> > +           __u8 type;
> >> >> > +           __le16 period;
> >> >> > +           __le16 low;
> >> >> > +           __le16 high;
> >> >> > +   } __packed set_cfg = {
> >> >> > +           .port_chan.port = dln2->port,
> >> >> > +           .port_chan.chan = channel,
> >> >> > +           .type = freq ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
> >> >> > +           .period = cpu_to_le16(freq)
> >> >> > +   };
> >> >> > +
> >> >> > +   ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
> >> >> > +                          &set_cfg, sizeof(set_cfg));
> >> >> > +   if (ret < 0) {
> >> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> >> > +           return ret;
> >> >> > +   }
> >> >> > +   return 0;
> >> >> > +}
> >> >> > +
> >> >> > +static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
> >> >> > +{
> >> >> > +   int ret;
> >> >> > +   int old_chans_requested = dln2->chans_requested;
> >> >> > +
> >> >> > +   dln2->chans_requested |= (1 << channel);  
> >> > Ah, so this changes the enabled channels and can lead to the
> >> > changes in what is enabled when buffered mode is going on.
> >> >
> >> > I'd just use the iio_claim_direct_mode set of functions
> >> > to prevent direct reads when the buffer is operating.
> >> > They add a lot of complexity for what is usually a fairly
> >> > unusual requirement.  
> >> Will do.  
> >> >> > +   ret = dln2_adc_update_enabled_chans(dln2);
> >> >> > +   if (ret < 0) {
> >> >> > +           dln2->chans_requested = old_chans_requested;
> >> >> > +           return ret;
> >> >> > +   }
> >> >> > +   dln2->port_enabled = true;
> >> >> > +
> >> >> > +   struct dln2_adc_port_chan port_chan = {
> >> >> > +           .port = dln2->port,
> >> >> > +           .chan = channel,
> >> >> > +   };
> >> >> > +   __le16 value;
> >> >> > +   int olen = sizeof(value);
> >> >> > +
> >> >> > +   ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
> >> >> > +                       &port_chan, sizeof(port_chan), &value, &olen);
> >> >> > +   if (ret < 0) {
> >> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> >> > +           return ret;
> >> >> > +   }
> >> >> > +   if (olen < sizeof(value))
> >> >> > +           return -EPROTO;
> >> >> > +
> >> >> > +   return le16_to_cpu(value);
> >> >> > +}
> >> >> > +
> >> >> > +static int dln2_adc_read_all(struct dln2_adc *dln2,
> >> >> > +                        struct dln2_adc_get_all_vals *get_all_vals)
> >> >> > +{
> >> >> > +   int ret;
> >> >> > +   __u8 port = dln2->port;
> >> >> > +   int olen = sizeof(*get_all_vals);
> >> >> > +
> >> >> > +   ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
> >> >> > +                       &port, sizeof(port), get_all_vals, &olen);  
> >> > Getting channels that aren't wired to anything?  Fair enough if true,
> >> > but unusual.  
> >> I had the same thought myself; the adc example in Diolan's
> >> package reads in a fixed 8 channels every time, apparently
> >> zeroing the disabled ones.  
> > It would make their firmware simpler probably.  Push the complexity to
> > the more powerful main processor.  
> >> >> > +   if (ret < 0) {
> >> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> >> > +           return ret;
> >> >> > +   }
> >> >> > +   if (olen < sizeof(*get_all_vals))
> >> >> > +           return -EPROTO;
> >> >> > +
> >> >> > +   return 0;
> >> >> > +}
> >> >> > +
> >> >> > +static int dln2_adc_read_raw(struct iio_dev *indio_dev,
> >> >> > +                        struct iio_chan_spec const *chan,
> >> >> > +                        int *val,
> >> >> > +                        int *val2,
> >> >> > +                        long mask)
> >> >> > +{
> >> >> > +   int ret;
> >> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
> >> >> > +
> >> >> > +   switch (mask) {
> >> >> > +   case IIO_CHAN_INFO_RAW:
> >> >> > +           mutex_lock(&dln2->mutex);
> >> >> > +           ret = dln2_adc_read(dln2, chan->channel);
> >> >> > +           mutex_unlock(&dln2->mutex);
> >> >> > +
> >> >> > +           if (ret < 0)
> >> >> > +                   return ret;
> >> >> > +
> >> >> > +           *val = ret;
> >> >> > +           return IIO_VAL_INT;
> >> >> > +
> >> >> > +   case IIO_CHAN_INFO_SCALE:
> >> >> > +           *val = 0;
> >> >> > +           /* 3.3 / (1 << 10) * 1000000000 */
> >> >> > +           *val2 = 3222656;
> >> >> > +           return IIO_VAL_INT_PLUS_NANO;
> >> >> > +
> >> >> > +   case IIO_CHAN_INFO_SAMP_FREQ:
> >> >> > +           mutex_lock(&dln2->mutex);
> >> >> > +           if (dln2->trigger_chan == -1)
> >> >> > +                   ret = 0;
> >> >> > +           else
> >> >> > +                   ret = dln2_adc_get_chan_freq(dln2, dln2->trigger_chan);
> >> >> > +           mutex_unlock(&dln2->mutex);
> >> >> > +
> >> >> > +           if (ret < 0)
> >> >> > +                   return ret;
> >> >> > +
> >> >> > +           *val = ret / 1000;
> >> >> > +           *val2 = (ret % 1000) * 1000;
> >> >> > +           return IIO_VAL_INT_PLUS_MICRO;
> >> >> > +
> >> >> > +   default:
> >> >> > +           return -EINVAL;
> >> >> > +   }
> >> >> > +}
> >> >> > +
> >> >> > +static int dln2_adc_write_raw(struct iio_dev *indio_dev,
> >> >> > +                         struct iio_chan_spec const *chan,
> >> >> > +                         int val,
> >> >> > +                         int val2,
> >> >> > +                         long mask)
> >> >> > +{
> >> >> > +   int ret;
> >> >> > +   unsigned int freq;
> >> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
> >> >> > +
> >> >> > +   switch (mask) {
> >> >> > +   case IIO_CHAN_INFO_SAMP_FREQ:
> >> >> > +           freq = val * 1000 + val2 / 1000;
> >> >> > +           if (freq > 65535) {
> >> >> > +                   freq = 65535;
> >> >> > +                   dev_warn(&dln2->pdev->dev,
> >> >> > +                            "clamping freq to 65535ms\n");  
> >> > frequency measured in a unit of time?  
> >> Will fix Monday  
> >> >> > +           }
> >> >> > +           mutex_lock(&dln2->mutex);
> >> >> > +
> >> >> > +           /*
> >> >> > +            * The first requested channel is arbitrated as a shared
> >> >> > +            * trigger source, so only one event is registered with the DLN.
> >> >> > +            * The event handler will then read all enabled channel values
> >> >> > +            * using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
> >> >> > +            * synchronization between ADC readings.
> >> >> > +            */
> >> >> > +           if (dln2->trigger_chan == -1)
> >> >> > +                   dln2->trigger_chan = chan->channel;  
> >> > This looks dangerous. either it is always fine to use a random channel or it's
> >> > not...
> >> >
> >> > I'd cache the frequency and only actually set it when bringing up the buffer
> >> > - that way if the setup is sane you will have a channel.  
> >> Fair point, I'll refactor it that way.  
> >> >> > +           ret = dln2_adc_set_chan_freq(dln2, dln2->trigger_chan, freq);
> >> >> > +
> >> >> > +           mutex_unlock(&dln2->mutex);
> >> >> > +
> >> >> > +           if (ret < 0)
> >> >> > +                   return ret;
> >> >> > +
> >> >> > +           return 0;  
> >> > Just return ret and loose the other path.  I chased through the code
> >> > and we get 0 for success.  
> >> Ok  
> >> >> > +
> >> >> > +   default:
> >> >> > +           return -EINVAL;
> >> >> > +   }
> >> >> > +}
> >> >> > +
> >> >> > +#define DLN2_ADC_CHAN(idx) {                                       \
> >> >> > +   .type = IIO_VOLTAGE,                                    \
> >> >> > +   .channel = idx,                                         \
> >> >> > +   .indexed = 1,                                           \
> >> >> > +   .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
> >> >> > +   .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |   \
> >> >> > +                              BIT(IIO_CHAN_INFO_SAMP_FREQ),\
> >> >> > +   .scan_index = idx,                                      \  
> >> > .scan_type = {
> >> >         .sign = 'u', etc is easier to read.  
> >> Ok  
> >> >> > +   .scan_type.sign = 'u',                                  \
> >> >> > +   .scan_type.realbits = DLN2_ADC_DATA_BITS,               \
> >> >> > +   .scan_type.storagebits = 16,                            \
> >> >> > +   .scan_type.endianness = IIO_LE,                         \
> >> >> > +}
> >> >> > +
> >> >> > +static const struct iio_chan_spec dln2_adc_iio_channels[] = {
> >> >> > +   DLN2_ADC_CHAN(0),
> >> >> > +   DLN2_ADC_CHAN(1),
> >> >> > +   DLN2_ADC_CHAN(2),
> >> >> > +   DLN2_ADC_CHAN(3),
> >> >> > +   DLN2_ADC_CHAN(4),
> >> >> > +   DLN2_ADC_CHAN(5),
> >> >> > +   DLN2_ADC_CHAN(6),
> >> >> > +   DLN2_ADC_CHAN(7),
> >> >> > +   IIO_CHAN_SOFT_TIMESTAMP(8),
> >> >> > +};
> >> >> > +
> >> >> > +static const struct iio_info dln2_adc_info = {
> >> >> > +   .read_raw = dln2_adc_read_raw,
> >> >> > +   .write_raw = dln2_adc_write_raw,
> >> >> > +   .driver_module = THIS_MODULE,
> >> >> > +};
> >> >> > +
> >> >> > +static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
> >> >> > +{
> >> >> > +   struct iio_poll_func *pf = p;
> >> >> > +   struct iio_dev *indio_dev = pf->indio_dev;
> >> >> > +   struct {
> >> >> > +           __le16 values[DLN2_ADC_MAX_CHANNELS];
> >> >> > +           int64_t timestamp_space;
> >> >> > +   } data;
> >> >> > +   struct dln2_adc_get_all_vals dev_data;
> >> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
> >> >> > +   int old_chans_requested;
> >> >> > +   int i, j;
> >> >> > +
> >> >> > +   mutex_lock(&dln2->mutex);
> >> >> > +
> >> >> > +   old_chans_requested = dln2->chans_requested;
> >> >> > +   dln2->chans_requested |= *indio_dev->active_scan_mask;
> >> >> > +   if (dln2_adc_update_enabled_chans(dln2) < 0) {
> >> >> > +           dln2->chans_requested = old_chans_requested;  
> >> > I'm confused. Why would this change here?  Shouldn't change
> >> > whilst data is being captured unless something very odd is going on.
> >> > Ah, found it - you want to allow sysfs reads at the same time.
> >> > Given the complexity required to do that safely, I'd just not
> >> > do it for now - maybe add it as a follow up patch later.  
> >> >> > +           mutex_unlock(&dln2->mutex);
> >> >> > +           goto done;
> >> >> > +   }
> >> >> > +
> >> >> > +   if (dln2_adc_read_all(dln2, &dev_data) < 0) {
> >> >> > +           mutex_unlock(&dln2->mutex);
> >> >> > +           goto done;
> >> >> > +   }
> >> >> > +
> >> >> > +   mutex_unlock(&dln2->mutex);
> >> >> > +
> >> >> > +   for (i = 0, j = 0;  
> >> >
> >> > Having found the note about lazy enabling above I think I now
> >> > understand what you are doing here.  You can't use the
> >> > core infrastructure and set available_scan_masks to 0x1FF because
> >> > that would result in enabling all the pins, some of which are wanted
> >> > for gpio usage perhaps.
> >> >
> >> > Hence you end up rolling your own demux.  You are reading all
> >> > the ADC channels, but some aren't necessarily connected to any actual
> >> > pins.
> >> >  
> >> Precisely. The lazy enabling adapts to the demand of the user.
> >> Since IIO doesn't really have an export mechanism like the GPIO
> >> subsystem, I can't preemptively enable channels unless I make the
> >> driver buffer-only (which I'd rather not do cause direct reads
> >> come in handy).  
> > one of those fiddly 'boundary' cases for defining connectivity.
> > If we were talking hard wired pins on a board this would all go
> > in device tree, but in reality most of the time we have
> > no idea what is connected to a given DLN2.  
> >> >> > +        i < bitmap_weight(indio_dev->active_scan_mask,
> >> >> > +                          indio_dev->masklength); i++, j++) {
> >> >> > +           j = find_next_bit(indio_dev->active_scan_mask,
> >> >> > +                             indio_dev->masklength, j);
> >> >> > +           data.values[i] = dev_data.values[j];
> >> >> > +   }
> >> >> > +
> >> >> > +   iio_push_to_buffers_with_timestamp(indio_dev, &data,
> >> >> > +                                      iio_get_time_ns(indio_dev));
> >> >> > +
> >> >> > +done:
> >> >> > +   iio_trigger_notify_done(indio_dev->trig);
> >> >> > +   return IRQ_HANDLED;
> >> >> > +}
> >> >> > +
> >> >> > +static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
> >> >> > +{
> >> >> > +   int ret;
> >> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
> >> >> > +
> >> >> > +   mutex_lock(&dln2->mutex);
> >> >> > +   dln2->chans_requested |= *indio_dev->active_scan_mask;  
> >> > Perhaps should be in the update_scan_mask callback rather than here?
> >> > You could have multiple consumers of this data which would cause
> >> > this path to be 'interesting'.
> >> >
> >> > I'm also a little curious to know why it isn't just an =
> >> > Also why don't you want to disable them when then buffer is disabled.  
> >> This allows the enabled channels to remain enabled if the user
> >> opts for direct raw reads. If I were to enable a channel
> >> temporarily for a raw read, that's 6 or 7 synchronous commands:
> >> - Set resolution (if not already done)
> >> - Enable channel
> >> - Enable port
> >> - Get value
> >> - Get value (a second one is needed because the first read after
> >>   enabling always seems to return 0, must be a firmware bug)
> >> - Disable port
> >> - Disable channel
> >>
> >> This just strikes me as inefficient, but it would be more
> >> consistent state-wise. If you think this is an ok trade-off,
> >> I'll go for the enable/disable approach. I suppose it's also
> >> possible to reset the lazy channel mask when the user exits
> >> buffered mode. The initial zero-return after enabling channels is
> >> a real kicker, and makes me want to keep disables/enables to a
> >> minimum.  
> > That is a pain.  Still more consistent to clear the channel mask
> > again after a buffer disable + prevent single channel reads
> > whilst it is running.  At least, do this for the first merge
> > of the driver then we can revisit if you have a usecase
> > where the juggling is really necessary.
> >  
> >> >> > +   ret = dln2_adc_update_enabled_chans(dln2);
> >> >> > +   mutex_unlock(&dln2->mutex);
> >> >> > +   if (ret < 0) {
> >> >> > +           dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> >> >> > +           return ret;
> >> >> > +   }
> >> >> > +
> >> >> > +   return iio_triggered_buffer_postenable(indio_dev);
> >> >> > +}
> >> >> > +
> >> >> > +static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
> >> >> > +   .postenable = dln2_adc_triggered_buffer_postenable,
> >> >> > +   .predisable = iio_triggered_buffer_predisable,
> >> >> > +};
> >> >> > +
> >> >> > +static void dln2_adc_event(struct platform_device *pdev, u16 echo,
> >> >> > +                      const void *data, int len)
> >> >> > +{
> >> >> > +   struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> >> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
> >> >> > +
> >> >> > +   iio_trigger_poll(dln2->trig);  
> >> > Is this in interrupt context?
> >> > if not should be iio_trigger_poll_chained(dln2->trig);  
> >> I'll double check that on Monday, the MFD code would determine
> >> that.  
> >> >> > +}
> >> >> > +
> >> >> > +static const struct iio_trigger_ops dln2_adc_trigger_ops = {
> >> >> > +   .owner = THIS_MODULE,  
> >> > Hohum.  Patch set to remove the need for this is waiting for me
> >> > to get around to revising it.  Interestingly checking why you
> >> > had this at all lead me to a check in the trigger register that
> >> > I wasn't getting rid of in that patch set. oops.
> >> >
> >> > Upshot is that the equivalent is done via macro magic rather
> >> > than an explicit entry in this structure.  It's possible this
> >> > driver will cross with that series, in which case we'll get
> >> > some build issues that will need fixing up.  I'll hopefully
> >> > remember to deal with them when applying though.  
> >> >> > +};
> >> >> > +
> >> >> > +static int dln2_adc_probe(struct platform_device *pdev)
> >> >> > +{
> >> >> > +   struct device *dev = pdev->dev;
> >> >> > +   struct dln2_adc *dln2;
> >> >> > +   struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
> >> >> > +   struct iio_dev *indio_dev;
> >> >> > +   struct iio_buffer *buffer;
> >> >> > +   int ret;
> >> >> > +   int chans;
> >> >> > +
> >> >> > +   indio_dev = devm_iio_device_alloc(dev, sizeof(struct dln2_adc));  
> >> > Slight preference for sizeof(*dln2)  
> >> Agreed  
> >> >> > +   if (!indio_dev) {
> >> >> > +           dev_err(dev, "failed allocating iio device\n");
> >> >> > +           return -ENOMEM;
> >> >> > +   }
> >> >> > +
> >> >> > +   dln2 = iio_priv(indio_dev);
> >> >> > +   dln2->pdev = pdev;
> >> >> > +   dln2->port = pdata->port;
> >> >> > +   mutex_init(&dln2->mutex);
> >> >> > +   dln2->port_enabled = false;
> >> >> > +   dln2->resolution_set = false;
> >> >> > +   dln2->chans_requested = 0;
> >> >> > +   dln2->chans_enabled = 0;
> >> >> > +   dln2->trigger_chan = -1;  
> >> > This is fine at setup, but isn't reset to -1 if the channels are all disabled.  
> >> Ok  
> >> >> > +
> >> >> > +   platform_set_drvdata(pdev, indio_dev);
> >> >> > +
> >> >> > +   chans = dln2_adc_get_chan_count(dln2);
> >> >> > +   if (chans < 0) {
> >> >> > +           dev_err(dev, "failed to get channel count: %d\n", chans);
> >> >> > +           ret = chans;
> >> >> > +           goto dealloc_dev;
> >> >> > +   }
> >> >> > +   if (chans > DLN2_ADC_MAX_CHANNELS) {
> >> >> > +           chans = DLN2_ADC_MAX_CHANNELS;
> >> >> > +           dev_warn(dev, "clamping channels to %d\n",
> >> >> > +                    DLN2_ADC_MAX_CHANNELS);
> >> >> > +   }
> >> >> > +
> >> >> > +   indio_dev->name = DLN2_ADC_MOD_NAME;
> >> >> > +   indio_dev->dev.parent = dev;
> >> >> > +   indio_dev->info = &dln2_adc_info;
> >> >> > +   indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
> >> >> > +   indio_dev->channels = dln2_adc_iio_channels;
> >> >> > +   indio_dev->num_channels = chans + 1;  
> >> > Why +1?  Presumably to include the timestamp.  I guess this
> >> > means that the code that gets the channel count always gets 8.
> >> > You need to check that if you are going to use a const channel array.  
> >> Yea, I see in the docs that's supposed to be monotonic.
> >> I'll assemble that array on the stack instead at probe time to be
> >> safe.  
> >> >> > +   indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
> >> >> > +
> >> >> > +   dln2->trig = devm_iio_trigger_alloc(dev, "samplerate");  
> >> > Interesting naming for a trigger... rather too generic + doesn't
> >> > cope with multiple dln2's connect to same machine. Admittedly
> >> > a number of historical drivers have that problem but we can't
> >> > fix them now without ABI breakage.
> >> >  
> >> Good point, is there a preferred unique identifier to suffix that
> >> name?  
> > we've never restricted this particularly as it's just a magic string
> > to match things up.
> >
> > Quite a few drivers stick with "magicname-dev%d",  ..., indio_dev->id  
> >> >> > +   if (!dln2->trig) {
> >> >> > +           dev_err(dev, "failed to allocate trigger\n");
> >> >> > +           ret = -ENOMEM;
> >> >> > +           goto dealloc_dev;
> >> >> > +   }
> >> >> > +   dln2->trig->ops = &dln2_adc_trigger_ops;
> >> >> > +   iio_trigger_set_drvdata(dln2->trig, dln2);
> >> >> > +   iio_trigger_register(dln2->trig);
> >> >> > +   iio_trigger_set_immutable(indio_dev, dln2->trig);
> >> >> > +
> >> >> > +   buffer = devm_iio_kfifo_allocate(dev);
> >> >> > +   if (!buffer) {
> >> >> > +           dev_err(dev, "failed to allocate kfifo\n");
> >> >> > +           ret = -ENOMEM;
> >> >> > +           goto dealloc_trigger;
> >> >> > +   }
> >> >> > +
> >> >> > +   iio_device_attach_buffer(indio_dev, buffer);
> >> >> > +
> >> >> > +   indio_dev->pollfunc = iio_alloc_pollfunc(NULL,
> >> >> > +                                            &dln2_adc_trigger_h,
> >> >> > +                                            IRQF_ONESHOT,
> >> >> > +                                            indio_dev,
> >> >> > +                                            "samplerate");  
> >> > Again very generic naming + shouldn't be the same as the trigger name.
> >> > Almost no drivers do this manually any more.  Why does the
> >> > iio_triggered_buffer_setup boilerplate function not work here?  
> >> I read about this in the IIO presentation from back when the
> >> subsystem was first introduced. Didn't realize it was
> >> depreciated.  
> > It's not deprecated as such, just that there are utility functions
> > to make the job easier if you happen to fit with the standard forms.
> > Just code repetition reduction really.  This approach may still
> > be necessary if something more 'interesting' is going on.  
> >> >> > +
> >> >> > +   if (!indio_dev->pollfunc) {
> >> >> > +           ret = -ENOMEM;
> >> >> > +           goto dealloc_kfifo;
> >> >> > +   }
> >> >> > +
> >> >> > +   ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
> >> >> > +                                dln2_adc_event);
> >> >> > +   if (ret) {
> >> >> > +           dev_err(dev, "failed to register event cb: %d\n", ret);
> >> >> > +           goto dealloc_pollfunc;
> >> >> > +   }
> >> >> > +
> >> >> > +   ret = iio_device_register(indio_dev);
> >> >> > +   if (ret) {
> >> >> > +           dev_err(dev, "failed to register iio device: %d\n", ret);
> >> >> > +           goto dealloc_pollfunc;
> >> >> > +   }
> >> >> > +
> >> >> > +   return 0;
> >> >> > +
> >> >> > +dealloc_pollfunc:
> >> >> > +   iio_dealloc_pollfunc(indio_dev->pollfunc);
> >> >> > +dealloc_kfifo:
> >> >> > +dealloc_trigger:
> >> >> > +   iio_trigger_unregister(dln2->trig);
> >> >> > +dealloc_dev:
> >> >> > +
> >> >> > +   return ret;
> >> >> > +}
> >> >> > +
> >> >> > +static int dln2_adc_remove(struct platform_device *pdev)
> >> >> > +{
> >> >> > +   struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> >> >> > +   struct dln2_adc *dln2 = iio_priv(indio_dev);
> >> >> > +
> >> >> > +   iio_device_unregister(indio_dev);
> >> >> > +   dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
> >> >> > +   iio_trigger_unregister(dln2->trig);
> >> >> > +   iio_dealloc_pollfunc(indio_dev->pollfunc);
> >> >> > +
> >> >> > +   return 0;
> >> >> > +}
> >> >> > +
> >> >> > +static struct platform_driver dln2_adc_driver = {
> >> >> > +   .driver.name    = DLN2_ADC_MOD_NAME,
> >> >> > +   .probe          = dln2_adc_probe,
> >> >> > +   .remove         = dln2_adc_remove,
> >> >> > +};
> >> >> > +
> >> >> > +module_platform_driver(dln2_adc_driver);
> >> >> > +
> >> >> > +MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
> >> >> > +MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
> >> >> > +MODULE_LICENSE("GPL v2");
> >> >> > +MODULE_ALIAS("platform:dln2-adc");
> >> >> > diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
> >> >> > index 704e189..a22ab8c 100644
> >> >> > --- a/drivers/mfd/dln2.c
> >> >> > +++ b/drivers/mfd/dln2.c
> >> >> > @@ -53,6 +53,7 @@ enum dln2_handle {
> >> >> >     DLN2_HANDLE_GPIO,
> >> >> >     DLN2_HANDLE_I2C,
> >> >> >     DLN2_HANDLE_SPI,
> >> >> > +   DLN2_HANDLE_ADC,
> >> >> >     DLN2_HANDLES
> >> >> >  };
> >> >> >
> >> >> > @@ -663,6 +664,12 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
> >> >> >     .port = 0,
> >> >> >  };
> >> >> >
> >> >> > +/* Only one ADC port supported */
> >> >> > +static struct dln2_platform_data dln2_pdata_adc = {
> >> >> > +   .handle = DLN2_HANDLE_ADC,
> >> >> > +   .port = 0,
> >> >> > +};
> >> >> > +
> >> >> >  static const struct mfd_cell dln2_devs[] = {
> >> >> >     {
> >> >> >             .name = "dln2-gpio",
> >> >> > @@ -679,6 +686,11 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
> >> >> >             .platform_data = &dln2_pdata_spi,
> >> >> >             .pdata_size = sizeof(struct dln2_platform_data),
> >> >> >     },
> >> >> > +   {
> >> >> > +           .name = "dln2-adc",
> >> >> > +           .platform_data = &dln2_pdata_adc,
> >> >> > +           .pdata_size = sizeof(struct dln2_platform_data),
> >> >> > +   },
> >> >> >  };
> >> >> >
> >> >> >  static void dln2_stop(struct dln2_dev *dln2)
> >> >> >  
> >> >>  
> >> >  
> >  
> --
> 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] 23+ messages in thread

* [PATCH v3] iio: adc: Add support for DLN2 ADC
       [not found] <b5ede0cc2cb36695b86edf44c0dddbc4736a6420>
  2017-06-22 22:31 ` [PATCH v2] iio: adc: Add support for DLN2 ADC Jack Andersen
@ 2017-06-28  0:53 ` Jack Andersen
  2017-06-28  0:57   ` Jack Andersen
  2017-06-28  1:05 ` [PATCH v4] " Jack Andersen
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Jack Andersen @ 2017-06-28  0:53 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: Jack Andersen, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler

This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
ADC is the fourth and final component of the DLN2 for the kernel.

Signed-off-by: Jack Andersen <jackoalan@gmail.com>
---
Changes in v2:
- Address Peter's remarks; bringing consistency to C99 syntax, dev_dbg usage,
  and error handling.
- Remove probe/remove logging.
- Utilize stack-allocated buffer for pushing into IIO.
- Add timestamp channel to IIO spec.

Suggested-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
---
Changes in v3:
- Remove lazy channel enabling; integrating with update_channel_mode instead.
- Implement memcpy demux table similar to subsystem core.
- Use iio_triggered_buffer_setup to create buffer.

Suggested-by: Jonathan Cameron <jic23@kernel.org>
---
 drivers/iio/adc/Kconfig    |   9 +
 drivers/iio/adc/Makefile   |   1 +
 drivers/iio/adc/dln2-adc.c | 733 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/mfd/dln2.c         |  12 +
 tools/gpio/lsgpio.c        |   4 +-
 5 files changed, 757 insertions(+), 2 deletions(-)
 create mode 100644 drivers/iio/adc/dln2-adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 401f47b..78d7455 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -239,6 +239,15 @@ config DA9150_GPADC
 	  To compile this driver as a module, choose M here: the module will be
 	  called berlin2-adc.
 
+config DLN2_ADC
+	tristate "Diolan DLN-2 ADC driver support"
+	depends on MFD_DLN2
+	help
+	  Say yes here to build support for Diolan DLN-2 ADC.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called adc_dln2.
+
 config ENVELOPE_DETECTOR
 	tristate "Envelope detector using a DAC and a comparator"
 	depends on OF
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 9339bec..378bc65 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
 obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
 obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
 obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
+obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
 obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
 obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
new file mode 100644
index 0000000..07569d5
--- /dev/null
+++ b/drivers/iio/adc/dln2-adc.c
@@ -0,0 +1,733 @@
+/*
+ * Driver for the Diolan DLN-2 USB-ADC adapter
+ *
+ * Copyright (c) 2017 Jack Andersen
+ *
+ * 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, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/dln2.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+
+#define DLN2_ADC_MOD_NAME "dln2-adc"
+
+#define DLN2_ADC_ID             0x06
+
+#define DLN2_ADC_GET_CHANNEL_COUNT	DLN2_CMD(0x01, DLN2_ADC_ID)
+#define DLN2_ADC_ENABLE			DLN2_CMD(0x02, DLN2_ADC_ID)
+#define DLN2_ADC_DISABLE		DLN2_CMD(0x03, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_ENABLE		DLN2_CMD(0x05, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_DISABLE	DLN2_CMD(0x06, DLN2_ADC_ID)
+#define DLN2_ADC_SET_RESOLUTION		DLN2_CMD(0x08, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_VAL	DLN2_CMD(0x0A, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_ALL_VAL	DLN2_CMD(0x0B, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_SET_CFG	DLN2_CMD(0x0C, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_CFG	DLN2_CMD(0x0D, DLN2_ADC_ID)
+#define DLN2_ADC_CONDITION_MET_EV	DLN2_CMD(0x10, DLN2_ADC_ID)
+
+#define DLN2_ADC_EVENT_NONE		0
+#define DLN2_ADC_EVENT_BELOW		1
+#define DLN2_ADC_EVENT_LEVEL_ABOVE	2
+#define DLN2_ADC_EVENT_OUTSIDE		3
+#define DLN2_ADC_EVENT_INSIDE		4
+#define DLN2_ADC_EVENT_ALWAYS		5
+
+#define DLN2_ADC_MAX_CHANNELS 8
+#define DLN2_ADC_DATA_BITS 10
+
+/*
+ * Plays similar role to iio_demux_table in subsystem core; except allocated
+ * in a fixed 8-element array.
+ */
+struct dln2_adc_demux_table {
+	unsigned int from;
+	unsigned int to;
+	unsigned int length;
+};
+
+struct dln2_adc {
+	struct platform_device *pdev;
+	struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1];
+	int port, trigger_chan;
+	struct iio_trigger *trig;
+	struct mutex mutex;
+	/* Cached sample period in milliseconds */
+	unsigned int sample_period;
+	/* Demux table */
+	unsigned int demux_count;
+	struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS];
+	/* Precomputed timestamp padding offset and length */
+	unsigned int ts_pad_offset, ts_pad_length;
+};
+
+struct dln2_adc_port_chan {
+	u8 port;
+	u8 chan;
+};
+
+struct dln2_adc_get_all_vals {
+	__le16 channel_mask;
+	__le16 values[DLN2_ADC_MAX_CHANNELS];
+};
+
+static void dln2_adc_add_demux(struct dln2_adc *dln2,
+	unsigned int in_loc, unsigned int out_loc,
+	unsigned int length)
+{
+	struct dln2_adc_demux_table *p = dln2->demux_count ?
+		&dln2->demux[dln2->demux_count - 1] : NULL;
+	if (p && p->from + p->length == in_loc &&
+		p->to + p->length == out_loc) {
+		p->length += length;
+	} else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) {
+		p = &dln2->demux[dln2->demux_count++];
+		p->from = in_loc;
+		p->to = out_loc;
+		p->length = length;
+	}
+}
+
+static void dln2_adc_update_demux(struct dln2_adc *dln2)
+{
+	int in_ind = -1, out_ind;
+	unsigned int in_loc = 0, out_loc = 0;
+	struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
+
+	/* Clear out any old demux */
+	dln2->demux_count = 0;
+
+	/* Optimize all 8-channels case */
+	if (indio_dev->masklength &&
+	    (*indio_dev->active_scan_mask & 0xff) == 0xff) {
+		dln2_adc_add_demux(dln2, 0, 0, 16);
+		dln2->ts_pad_offset = 0;
+		dln2->ts_pad_length = 0;
+		return;
+	}
+
+	/* Build demux table from fixed 8-channels to active_scan_mask */
+	for_each_set_bit(out_ind,
+			 indio_dev->active_scan_mask,
+			 indio_dev->masklength) {
+		/* Handle timestamp separately */
+		if (out_ind == DLN2_ADC_MAX_CHANNELS)
+			break;
+		for (++in_ind; in_ind != out_ind; ++in_ind)
+			in_loc += 2;
+		dln2_adc_add_demux(dln2, in_loc, out_loc, 2);
+		out_loc += 2;
+		in_loc += 2;
+	}
+
+	if (indio_dev->scan_timestamp) {
+		size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1;
+
+		dln2->ts_pad_offset = out_loc;
+		dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc;
+	} else {
+		dln2->ts_pad_offset = 0;
+		dln2->ts_pad_length = 0;
+	}
+}
+
+static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
+{
+	int ret;
+	u8 port = dln2->port;
+	u8 count;
+	int olen = sizeof(count);
+
+	ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
+			    &port, sizeof(port), &count, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+	if (olen < sizeof(count))
+		return -EPROTO;
+
+	return count;
+}
+
+static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
+{
+	int ret;
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = DLN2_ADC_DATA_BITS,
+	};
+
+	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
+			       &port_chan, sizeof(port_chan));
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
+				     int channel, bool enable)
+{
+	int ret;
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = channel,
+	};
+
+	u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
+
+	ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable,
+				     u16 *conflict_out)
+{
+	int ret;
+	u8 port = dln2->port;
+	__le16 conflict;
+	int olen = sizeof(conflict);
+
+	u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
+
+	if (conflict_out)
+		*conflict_out = 0;
+
+	ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
+			    &conflict, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
+			__func__, (int)enable);
+		if (conflict_out && enable && olen >= sizeof(conflict))
+			*conflict_out = le16_to_cpu(conflict);
+		return ret;
+	}
+	if (enable && olen < sizeof(conflict))
+		return -EPROTO;
+
+	return 0;
+}
+
+static int dln2_adc_set_chan_period(struct dln2_adc *dln2,
+	unsigned int channel, unsigned int period)
+{
+	int ret;
+	struct {
+		struct dln2_adc_port_chan port_chan;
+		__u8 type;
+		__le16 period;
+		__le16 low;
+		__le16 high;
+	} __packed set_cfg = {
+		.port_chan.port = dln2->port,
+		.port_chan.chan = channel,
+		.type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
+		.period = cpu_to_le16(period)
+	};
+
+	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
+			       &set_cfg, sizeof(set_cfg));
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+	return 0;
+}
+
+static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
+{
+	int ret, i;
+	struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
+	u16 conflict;
+
+	if (iio_buffer_enabled(indio_dev)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ret = dln2_adc_set_chan_enabled(dln2, channel, true);
+	if (ret < 0)
+		goto out;
+
+	ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
+	if (ret < 0) {
+		if (conflict) {
+			dev_err(&dln2->pdev->dev,
+				"ADC pins conflict with mask %04X\n",
+				(int)conflict);
+			ret = -EBUSY;
+		}
+		goto disable_chan;
+	}
+
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = channel,
+	};
+	__le16 value;
+	int olen = sizeof(value);
+
+	/*
+	 * Call GET_VAL twice due to initial zero-return immediately after
+	 * enabling channel.
+	 */
+	for (i = 0; i < 2; ++i) {
+		ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
+				    &port_chan, sizeof(port_chan),
+				    &value, &olen);
+		if (ret < 0) {
+			dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+			goto disable_port;
+		}
+		if (olen < sizeof(value)) {
+			ret = -EPROTO;
+			goto disable_port;
+		}
+	}
+
+	ret = le16_to_cpu(value);
+
+disable_port:
+	dln2_adc_set_port_enabled(dln2, false, NULL);
+disable_chan:
+	dln2_adc_set_chan_enabled(dln2, channel, false);
+out:
+	return ret;
+}
+
+static int dln2_adc_read_all(struct dln2_adc *dln2,
+			     struct dln2_adc_get_all_vals *get_all_vals)
+{
+	int ret;
+	__u8 port = dln2->port;
+	int olen = sizeof(*get_all_vals);
+
+	ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
+			    &port, sizeof(port), get_all_vals, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+	if (olen < sizeof(*get_all_vals))
+		return -EPROTO;
+
+	return 0;
+}
+
+static int dln2_adc_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int *val,
+			     int *val2,
+			     long mask)
+{
+	int ret;
+	unsigned int microhertz;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&dln2->mutex);
+		ret = dln2_adc_read(dln2, chan->channel);
+		mutex_unlock(&dln2->mutex);
+
+		if (ret < 0)
+			return ret;
+
+		*val = ret;
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		/*
+		 * Voltage reference is fixed at 3.3v
+		 *  3.3 / (1 << 10) * 1000000000
+		 */
+		*val = 0;
+		*val2 = 3222656;
+		return IIO_VAL_INT_PLUS_NANO;
+
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (dln2->sample_period) {
+			microhertz = 1000000000 / dln2->sample_period;
+			*val = microhertz / 1000000;
+			*val2 = microhertz % 1000000;
+		} else {
+			*val = 0;
+			*val2 = 0;
+		}
+
+		return IIO_VAL_INT_PLUS_MICRO;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int dln2_adc_write_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int val,
+			      int val2,
+			      long mask)
+{
+	int ret;
+	unsigned int microhertz;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		microhertz = 1000000 * val + val2;
+
+		mutex_lock(&dln2->mutex);
+
+		dln2->sample_period =
+			microhertz ? 1000000000 / microhertz : UINT_MAX;
+		if (dln2->sample_period > 65535) {
+			dln2->sample_period = 65535;
+			dev_warn(&dln2->pdev->dev,
+				 "clamping period to 65535ms\n");
+		}
+
+		/*
+		 * The first requested channel is arbitrated as a shared
+		 * trigger source, so only one event is registered with the
+		 * DLN. The event handler will then read all enabled channel
+		 * values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
+		 * synchronization between ADC readings.
+		 */
+		if (dln2->trigger_chan != -1)
+			ret = dln2_adc_set_chan_period(dln2,
+				dln2->trigger_chan, dln2->sample_period);
+		else
+			ret = 0;
+
+		mutex_unlock(&dln2->mutex);
+
+		return ret;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int dln2_update_scan_mode(struct iio_dev *indio_dev,
+				 const unsigned long *scan_mask)
+{
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+	int chan_count = indio_dev->num_channels - 1;
+	int ret, i, j;
+
+	mutex_lock(&dln2->mutex);
+
+	for (i = 0; i < chan_count; ++i) {
+		ret = dln2_adc_set_chan_enabled(dln2, i,
+						test_bit(i, scan_mask));
+		if (ret < 0) {
+			for (j = 0; j < i; ++j)
+				dln2_adc_set_chan_enabled(dln2, j, false);
+			mutex_unlock(&dln2->mutex);
+			dev_err(&dln2->pdev->dev,
+				"Unable to enable ADC channel %d\n", i);
+			return -EBUSY;
+		}
+	}
+
+	dln2_adc_update_demux(dln2);
+
+	mutex_unlock(&dln2->mutex);
+
+	return 0;
+}
+
+#define DLN2_ADC_CHAN(idx) {					\
+	.type = IIO_VOLTAGE,					\
+	.channel = idx,						\
+	.indexed = 1,						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |	\
+				   BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+	.scan_index = idx,					\
+	.scan_type = {						\
+		.sign = 'u',					\
+		.realbits = DLN2_ADC_DATA_BITS,			\
+		.storagebits = 16,				\
+		.endianness = IIO_LE,				\
+	}							\
+}
+
+static const struct iio_info dln2_adc_info = {
+	.read_raw = dln2_adc_read_raw,
+	.write_raw = dln2_adc_write_raw,
+	.update_scan_mode = dln2_update_scan_mode,
+	.driver_module = THIS_MODULE,
+};
+
+static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct {
+		__le16 values[DLN2_ADC_MAX_CHANNELS];
+		int64_t timestamp_space;
+	} data;
+	struct dln2_adc_get_all_vals dev_data;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+	const struct dln2_adc_demux_table *t;
+	int ret, i;
+
+	mutex_lock(&dln2->mutex);
+	ret = dln2_adc_read_all(dln2, &dev_data);
+	mutex_unlock(&dln2->mutex);
+	if (ret < 0)
+		goto done;
+
+	/* Demux operation */
+	for (i = 0; i < dln2->demux_count; ++i) {
+		t = &dln2->demux[i];
+		memcpy((void *)data.values + t->to,
+		       (void *)dev_data.values + t->from, t->length);
+	}
+
+	/* Zero padding space between values and timestamp */
+	if (dln2->ts_pad_length)
+		memset((void *)data.values + dln2->ts_pad_offset,
+		       0, dln2->ts_pad_length);
+
+	iio_push_to_buffers_with_timestamp(indio_dev, &data,
+					   iio_get_time_ns(indio_dev));
+
+done:
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+	u16 conflict;
+
+	mutex_lock(&dln2->mutex);
+
+	/* Enable ADC */
+	ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
+	if (ret < 0) {
+		mutex_unlock(&dln2->mutex);
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		if (conflict) {
+			dev_err(&dln2->pdev->dev,
+				"ADC pins conflict with mask %04X\n",
+				(int)conflict);
+			ret = -EBUSY;
+		}
+		return ret;
+	}
+
+	/* Assign trigger channel based on first enabled channel */
+	unsigned int trigger_chan =
+		find_first_bit(indio_dev->active_scan_mask,
+			       indio_dev->masklength);
+	if (trigger_chan < DLN2_ADC_MAX_CHANNELS) {
+		dln2->trigger_chan = trigger_chan;
+		ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan,
+					       dln2->sample_period);
+		mutex_unlock(&dln2->mutex);
+		if (ret < 0) {
+			dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+			return ret;
+		}
+	} else {
+		dln2->trigger_chan = -1;
+		mutex_unlock(&dln2->mutex);
+	}
+
+	return iio_triggered_buffer_postenable(indio_dev);
+}
+
+static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	mutex_lock(&dln2->mutex);
+
+	/* Disable trigger channel */
+	if (dln2->trigger_chan != -1) {
+		dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0);
+		dln2->trigger_chan = -1;
+	}
+
+	/* Disable ADC */
+	ret = dln2_adc_set_port_enabled(dln2, false, NULL);
+
+	mutex_unlock(&dln2->mutex);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+
+	return iio_triggered_buffer_predisable(indio_dev);
+}
+
+static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
+	.postenable = dln2_adc_triggered_buffer_postenable,
+	.predisable = dln2_adc_triggered_buffer_predisable,
+};
+
+static void dln2_adc_event(struct platform_device *pdev, u16 echo,
+			   const void *data, int len)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	/* Called via URB completion handler */
+	iio_trigger_poll(dln2->trig);
+}
+
+static const struct iio_trigger_ops dln2_adc_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+static int dln2_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct dln2_adc *dln2;
+	struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	struct iio_dev *indio_dev;
+	int i, ret, chans;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2));
+	if (!indio_dev) {
+		dev_err(dev, "failed allocating iio device\n");
+		return -ENOMEM;
+	}
+
+	dln2 = iio_priv(indio_dev);
+	dln2->pdev = pdev;
+	dln2->port = pdata->port;
+	dln2->trigger_chan = -1;
+	mutex_init(&dln2->mutex);
+	dln2->sample_period = 0;
+	dln2->demux_count = 0;
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	ret = dln2_adc_set_port_resolution(dln2);
+	if (ret < 0) {
+		dev_err(dev, "failed to set ADC resolution to 10 bits\n");
+		goto dealloc_dev;
+	}
+
+	chans = dln2_adc_get_chan_count(dln2);
+	if (chans < 0) {
+		dev_err(dev, "failed to get channel count: %d\n", chans);
+		ret = chans;
+		goto dealloc_dev;
+	}
+	if (chans > DLN2_ADC_MAX_CHANNELS) {
+		chans = DLN2_ADC_MAX_CHANNELS;
+		dev_warn(dev, "clamping channels to %d\n",
+			 DLN2_ADC_MAX_CHANNELS);
+	}
+
+	for (i = 0; i < chans; ++i) {
+		struct iio_chan_spec chan_spec = DLN2_ADC_CHAN(i);
+		dln2->iio_channels[i] = chan_spec;
+	}
+	struct iio_chan_spec timestamp_spec = IIO_CHAN_SOFT_TIMESTAMP(i);
+	dln2->iio_channels[i] = timestamp_spec;
+
+	indio_dev->name = DLN2_ADC_MOD_NAME;
+	indio_dev->dev.parent = dev;
+	indio_dev->info = &dln2_adc_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = dln2->iio_channels;
+	indio_dev->num_channels = chans + 1;
+	indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
+
+	dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
+					    indio_dev->name, indio_dev->id);
+	if (!dln2->trig) {
+		dev_err(dev, "failed to allocate trigger\n");
+		ret = -ENOMEM;
+		goto dealloc_dev;
+	}
+	dln2->trig->ops = &dln2_adc_trigger_ops;
+	iio_trigger_set_drvdata(dln2->trig, dln2);
+	iio_trigger_register(dln2->trig);
+	iio_trigger_set_immutable(indio_dev, dln2->trig);
+
+	ret = iio_triggered_buffer_setup(indio_dev, NULL, dln2_adc_trigger_h,
+					 &dln2_adc_buffer_setup_ops);
+	if (ret) {
+		dev_err(dev, "failed to allocate triggered buffer: %d\n", ret);
+		goto dealloc_trigger;
+	}
+
+	ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
+				     dln2_adc_event);
+	if (ret) {
+		dev_err(dev, "failed to register event cb: %d\n", ret);
+		goto dealloc_pollfunc;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(dev, "failed to register iio device: %d\n", ret);
+		goto dealloc_pollfunc;
+	}
+
+	return 0;
+
+dealloc_pollfunc:
+	iio_triggered_buffer_cleanup(indio_dev);
+dealloc_trigger:
+	iio_trigger_unregister(dln2->trig);
+dealloc_dev:
+
+	return ret;
+}
+
+static int dln2_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
+	iio_trigger_unregister(dln2->trig);
+	iio_triggered_buffer_cleanup(indio_dev);
+
+	return 0;
+}
+
+static struct platform_driver dln2_adc_driver = {
+	.driver.name	= DLN2_ADC_MOD_NAME,
+	.probe		= dln2_adc_probe,
+	.remove		= dln2_adc_remove,
+};
+
+module_platform_driver(dln2_adc_driver);
+
+MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
+MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dln2-adc");
diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
index 704e189..a22ab8c 100644
--- a/drivers/mfd/dln2.c
+++ b/drivers/mfd/dln2.c
@@ -53,6 +53,7 @@ enum dln2_handle {
 	DLN2_HANDLE_GPIO,
 	DLN2_HANDLE_I2C,
 	DLN2_HANDLE_SPI,
+	DLN2_HANDLE_ADC,
 	DLN2_HANDLES
 };
 
@@ -663,6 +664,12 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
 	.port = 0,
 };
 
+/* Only one ADC port supported */
+static struct dln2_platform_data dln2_pdata_adc = {
+	.handle = DLN2_HANDLE_ADC,
+	.port = 0,
+};
+
 static const struct mfd_cell dln2_devs[] = {
 	{
 		.name = "dln2-gpio",
@@ -679,6 +686,11 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
 		.platform_data = &dln2_pdata_spi,
 		.pdata_size = sizeof(struct dln2_platform_data),
 	},
+	{
+		.name = "dln2-adc",
+		.platform_data = &dln2_pdata_adc,
+		.pdata_size = sizeof(struct dln2_platform_data),
+	},
 };
 
 static void dln2_stop(struct dln2_dev *dln2)
diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c
index eb3f56e..58ef924 100644
--- a/tools/gpio/lsgpio.c
+++ b/tools/gpio/lsgpio.c
@@ -77,7 +77,7 @@ int list_device(const char *device_name)
 	int ret;
 	int i;
 
-	ret = asprintf(&chrdev_name, "/dev/%s", device_name);
+	ret = asprintf(&chrdev_name, "/sys/class/gpio/%s", device_name);
 	if (ret < 0)
 		return -ENOMEM;
 
@@ -169,7 +169,7 @@ int main(int argc, char **argv)
 		DIR *dp;
 
 		/* List all GPIO devices one at a time */
-		dp = opendir("/dev");
+		dp = opendir("/sys/class/gpio");
 		if (!dp) {
 			ret = -errno;
 			goto error_out;
-- 
1.9.1

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

* Re: [PATCH v3] iio: adc: Add support for DLN2 ADC
  2017-06-28  0:53 ` [PATCH v3] " Jack Andersen
@ 2017-06-28  0:57   ` Jack Andersen
  0 siblings, 0 replies; 23+ messages in thread
From: Jack Andersen @ 2017-06-28  0:57 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: Jack Andersen, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler

Please ignore this patch, a stray modification to lsgpio.c got in.

On 27 June 2017 at 14:53, Jack Andersen <jackoalan@gmail.com> wrote:
> This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
> ADC is the fourth and final component of the DLN2 for the kernel.
>
> Signed-off-by: Jack Andersen <jackoalan@gmail.com>
> ---
> Changes in v2:
> - Address Peter's remarks; bringing consistency to C99 syntax, dev_dbg usage,
>   and error handling.
> - Remove probe/remove logging.
> - Utilize stack-allocated buffer for pushing into IIO.
> - Add timestamp channel to IIO spec.
>
> Suggested-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
> ---
> Changes in v3:
> - Remove lazy channel enabling; integrating with update_channel_mode instead.
> - Implement memcpy demux table similar to subsystem core.
> - Use iio_triggered_buffer_setup to create buffer.
>
> Suggested-by: Jonathan Cameron <jic23@kernel.org>
> ---
>  drivers/iio/adc/Kconfig    |   9 +
>  drivers/iio/adc/Makefile   |   1 +
>  drivers/iio/adc/dln2-adc.c | 733 +++++++++++++++++++++++++++++++++++++++++++++
>  drivers/mfd/dln2.c         |  12 +
>  tools/gpio/lsgpio.c        |   4 +-
>  5 files changed, 757 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/iio/adc/dln2-adc.c
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 401f47b..78d7455 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -239,6 +239,15 @@ config DA9150_GPADC
>           To compile this driver as a module, choose M here: the module will be
>           called berlin2-adc.
>
> +config DLN2_ADC
> +       tristate "Diolan DLN-2 ADC driver support"
> +       depends on MFD_DLN2
> +       help
> +         Say yes here to build support for Diolan DLN-2 ADC.
> +
> +         This driver can also be built as a module. If so, the module will be
> +         called adc_dln2.
> +
>  config ENVELOPE_DETECTOR
>         tristate "Envelope detector using a DAC and a comparator"
>         depends on OF
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 9339bec..378bc65 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>  obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>  obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
>  obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
> +obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
>  obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
> diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
> new file mode 100644
> index 0000000..07569d5
> --- /dev/null
> +++ b/drivers/iio/adc/dln2-adc.c
> @@ -0,0 +1,733 @@
> +/*
> + * Driver for the Diolan DLN-2 USB-ADC adapter
> + *
> + * Copyright (c) 2017 Jack Andersen
> + *
> + * 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, version 2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/platform_device.h>
> +#include <linux/mfd/dln2.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/kfifo_buf.h>
> +
> +#define DLN2_ADC_MOD_NAME "dln2-adc"
> +
> +#define DLN2_ADC_ID             0x06
> +
> +#define DLN2_ADC_GET_CHANNEL_COUNT     DLN2_CMD(0x01, DLN2_ADC_ID)
> +#define DLN2_ADC_ENABLE                        DLN2_CMD(0x02, DLN2_ADC_ID)
> +#define DLN2_ADC_DISABLE               DLN2_CMD(0x03, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_ENABLE                DLN2_CMD(0x05, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_DISABLE       DLN2_CMD(0x06, DLN2_ADC_ID)
> +#define DLN2_ADC_SET_RESOLUTION                DLN2_CMD(0x08, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_GET_VAL       DLN2_CMD(0x0A, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_GET_ALL_VAL   DLN2_CMD(0x0B, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_SET_CFG       DLN2_CMD(0x0C, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_GET_CFG       DLN2_CMD(0x0D, DLN2_ADC_ID)
> +#define DLN2_ADC_CONDITION_MET_EV      DLN2_CMD(0x10, DLN2_ADC_ID)
> +
> +#define DLN2_ADC_EVENT_NONE            0
> +#define DLN2_ADC_EVENT_BELOW           1
> +#define DLN2_ADC_EVENT_LEVEL_ABOVE     2
> +#define DLN2_ADC_EVENT_OUTSIDE         3
> +#define DLN2_ADC_EVENT_INSIDE          4
> +#define DLN2_ADC_EVENT_ALWAYS          5
> +
> +#define DLN2_ADC_MAX_CHANNELS 8
> +#define DLN2_ADC_DATA_BITS 10
> +
> +/*
> + * Plays similar role to iio_demux_table in subsystem core; except allocated
> + * in a fixed 8-element array.
> + */
> +struct dln2_adc_demux_table {
> +       unsigned int from;
> +       unsigned int to;
> +       unsigned int length;
> +};
> +
> +struct dln2_adc {
> +       struct platform_device *pdev;
> +       struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1];
> +       int port, trigger_chan;
> +       struct iio_trigger *trig;
> +       struct mutex mutex;
> +       /* Cached sample period in milliseconds */
> +       unsigned int sample_period;
> +       /* Demux table */
> +       unsigned int demux_count;
> +       struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS];
> +       /* Precomputed timestamp padding offset and length */
> +       unsigned int ts_pad_offset, ts_pad_length;
> +};
> +
> +struct dln2_adc_port_chan {
> +       u8 port;
> +       u8 chan;
> +};
> +
> +struct dln2_adc_get_all_vals {
> +       __le16 channel_mask;
> +       __le16 values[DLN2_ADC_MAX_CHANNELS];
> +};
> +
> +static void dln2_adc_add_demux(struct dln2_adc *dln2,
> +       unsigned int in_loc, unsigned int out_loc,
> +       unsigned int length)
> +{
> +       struct dln2_adc_demux_table *p = dln2->demux_count ?
> +               &dln2->demux[dln2->demux_count - 1] : NULL;
> +       if (p && p->from + p->length == in_loc &&
> +               p->to + p->length == out_loc) {
> +               p->length += length;
> +       } else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) {
> +               p = &dln2->demux[dln2->demux_count++];
> +               p->from = in_loc;
> +               p->to = out_loc;
> +               p->length = length;
> +       }
> +}
> +
> +static void dln2_adc_update_demux(struct dln2_adc *dln2)
> +{
> +       int in_ind = -1, out_ind;
> +       unsigned int in_loc = 0, out_loc = 0;
> +       struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
> +
> +       /* Clear out any old demux */
> +       dln2->demux_count = 0;
> +
> +       /* Optimize all 8-channels case */
> +       if (indio_dev->masklength &&
> +           (*indio_dev->active_scan_mask & 0xff) == 0xff) {
> +               dln2_adc_add_demux(dln2, 0, 0, 16);
> +               dln2->ts_pad_offset = 0;
> +               dln2->ts_pad_length = 0;
> +               return;
> +       }
> +
> +       /* Build demux table from fixed 8-channels to active_scan_mask */
> +       for_each_set_bit(out_ind,
> +                        indio_dev->active_scan_mask,
> +                        indio_dev->masklength) {
> +               /* Handle timestamp separately */
> +               if (out_ind == DLN2_ADC_MAX_CHANNELS)
> +                       break;
> +               for (++in_ind; in_ind != out_ind; ++in_ind)
> +                       in_loc += 2;
> +               dln2_adc_add_demux(dln2, in_loc, out_loc, 2);
> +               out_loc += 2;
> +               in_loc += 2;
> +       }
> +
> +       if (indio_dev->scan_timestamp) {
> +               size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1;
> +
> +               dln2->ts_pad_offset = out_loc;
> +               dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc;
> +       } else {
> +               dln2->ts_pad_offset = 0;
> +               dln2->ts_pad_length = 0;
> +       }
> +}
> +
> +static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
> +{
> +       int ret;
> +       u8 port = dln2->port;
> +       u8 count;
> +       int olen = sizeof(count);
> +
> +       ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
> +                           &port, sizeof(port), &count, &olen);
> +       if (ret < 0) {
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +               return ret;
> +       }
> +       if (olen < sizeof(count))
> +               return -EPROTO;
> +
> +       return count;
> +}
> +
> +static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
> +{
> +       int ret;
> +       struct dln2_adc_port_chan port_chan = {
> +               .port = dln2->port,
> +               .chan = DLN2_ADC_DATA_BITS,
> +       };
> +
> +       ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
> +                              &port_chan, sizeof(port_chan));
> +       if (ret < 0) {
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
> +                                    int channel, bool enable)
> +{
> +       int ret;
> +       struct dln2_adc_port_chan port_chan = {
> +               .port = dln2->port,
> +               .chan = channel,
> +       };
> +
> +       u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
> +
> +       ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
> +       if (ret < 0) {
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable,
> +                                    u16 *conflict_out)
> +{
> +       int ret;
> +       u8 port = dln2->port;
> +       __le16 conflict;
> +       int olen = sizeof(conflict);
> +
> +       u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
> +
> +       if (conflict_out)
> +               *conflict_out = 0;
> +
> +       ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
> +                           &conflict, &olen);
> +       if (ret < 0) {
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
> +                       __func__, (int)enable);
> +               if (conflict_out && enable && olen >= sizeof(conflict))
> +                       *conflict_out = le16_to_cpu(conflict);
> +               return ret;
> +       }
> +       if (enable && olen < sizeof(conflict))
> +               return -EPROTO;
> +
> +       return 0;
> +}
> +
> +static int dln2_adc_set_chan_period(struct dln2_adc *dln2,
> +       unsigned int channel, unsigned int period)
> +{
> +       int ret;
> +       struct {
> +               struct dln2_adc_port_chan port_chan;
> +               __u8 type;
> +               __le16 period;
> +               __le16 low;
> +               __le16 high;
> +       } __packed set_cfg = {
> +               .port_chan.port = dln2->port,
> +               .port_chan.chan = channel,
> +               .type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
> +               .period = cpu_to_le16(period)
> +       };
> +
> +       ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
> +                              &set_cfg, sizeof(set_cfg));
> +       if (ret < 0) {
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +               return ret;
> +       }
> +       return 0;
> +}
> +
> +static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
> +{
> +       int ret, i;
> +       struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
> +       u16 conflict;
> +
> +       if (iio_buffer_enabled(indio_dev)) {
> +               ret = -EBUSY;
> +               goto out;
> +       }
> +
> +       ret = dln2_adc_set_chan_enabled(dln2, channel, true);
> +       if (ret < 0)
> +               goto out;
> +
> +       ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
> +       if (ret < 0) {
> +               if (conflict) {
> +                       dev_err(&dln2->pdev->dev,
> +                               "ADC pins conflict with mask %04X\n",
> +                               (int)conflict);
> +                       ret = -EBUSY;
> +               }
> +               goto disable_chan;
> +       }
> +
> +       struct dln2_adc_port_chan port_chan = {
> +               .port = dln2->port,
> +               .chan = channel,
> +       };
> +       __le16 value;
> +       int olen = sizeof(value);
> +
> +       /*
> +        * Call GET_VAL twice due to initial zero-return immediately after
> +        * enabling channel.
> +        */
> +       for (i = 0; i < 2; ++i) {
> +               ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
> +                                   &port_chan, sizeof(port_chan),
> +                                   &value, &olen);
> +               if (ret < 0) {
> +                       dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +                       goto disable_port;
> +               }
> +               if (olen < sizeof(value)) {
> +                       ret = -EPROTO;
> +                       goto disable_port;
> +               }
> +       }
> +
> +       ret = le16_to_cpu(value);
> +
> +disable_port:
> +       dln2_adc_set_port_enabled(dln2, false, NULL);
> +disable_chan:
> +       dln2_adc_set_chan_enabled(dln2, channel, false);
> +out:
> +       return ret;
> +}
> +
> +static int dln2_adc_read_all(struct dln2_adc *dln2,
> +                            struct dln2_adc_get_all_vals *get_all_vals)
> +{
> +       int ret;
> +       __u8 port = dln2->port;
> +       int olen = sizeof(*get_all_vals);
> +
> +       ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
> +                           &port, sizeof(port), get_all_vals, &olen);
> +       if (ret < 0) {
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +               return ret;
> +       }
> +       if (olen < sizeof(*get_all_vals))
> +               return -EPROTO;
> +
> +       return 0;
> +}
> +
> +static int dln2_adc_read_raw(struct iio_dev *indio_dev,
> +                            struct iio_chan_spec const *chan,
> +                            int *val,
> +                            int *val2,
> +                            long mask)
> +{
> +       int ret;
> +       unsigned int microhertz;
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_RAW:
> +               mutex_lock(&dln2->mutex);
> +               ret = dln2_adc_read(dln2, chan->channel);
> +               mutex_unlock(&dln2->mutex);
> +
> +               if (ret < 0)
> +                       return ret;
> +
> +               *val = ret;
> +               return IIO_VAL_INT;
> +
> +       case IIO_CHAN_INFO_SCALE:
> +               /*
> +                * Voltage reference is fixed at 3.3v
> +                *  3.3 / (1 << 10) * 1000000000
> +                */
> +               *val = 0;
> +               *val2 = 3222656;
> +               return IIO_VAL_INT_PLUS_NANO;
> +
> +       case IIO_CHAN_INFO_SAMP_FREQ:
> +               if (dln2->sample_period) {
> +                       microhertz = 1000000000 / dln2->sample_period;
> +                       *val = microhertz / 1000000;
> +                       *val2 = microhertz % 1000000;
> +               } else {
> +                       *val = 0;
> +                       *val2 = 0;
> +               }
> +
> +               return IIO_VAL_INT_PLUS_MICRO;
> +
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static int dln2_adc_write_raw(struct iio_dev *indio_dev,
> +                             struct iio_chan_spec const *chan,
> +                             int val,
> +                             int val2,
> +                             long mask)
> +{
> +       int ret;
> +       unsigned int microhertz;
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_SAMP_FREQ:
> +               microhertz = 1000000 * val + val2;
> +
> +               mutex_lock(&dln2->mutex);
> +
> +               dln2->sample_period =
> +                       microhertz ? 1000000000 / microhertz : UINT_MAX;
> +               if (dln2->sample_period > 65535) {
> +                       dln2->sample_period = 65535;
> +                       dev_warn(&dln2->pdev->dev,
> +                                "clamping period to 65535ms\n");
> +               }
> +
> +               /*
> +                * The first requested channel is arbitrated as a shared
> +                * trigger source, so only one event is registered with the
> +                * DLN. The event handler will then read all enabled channel
> +                * values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
> +                * synchronization between ADC readings.
> +                */
> +               if (dln2->trigger_chan != -1)
> +                       ret = dln2_adc_set_chan_period(dln2,
> +                               dln2->trigger_chan, dln2->sample_period);
> +               else
> +                       ret = 0;
> +
> +               mutex_unlock(&dln2->mutex);
> +
> +               return ret;
> +
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static int dln2_update_scan_mode(struct iio_dev *indio_dev,
> +                                const unsigned long *scan_mask)
> +{
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +       int chan_count = indio_dev->num_channels - 1;
> +       int ret, i, j;
> +
> +       mutex_lock(&dln2->mutex);
> +
> +       for (i = 0; i < chan_count; ++i) {
> +               ret = dln2_adc_set_chan_enabled(dln2, i,
> +                                               test_bit(i, scan_mask));
> +               if (ret < 0) {
> +                       for (j = 0; j < i; ++j)
> +                               dln2_adc_set_chan_enabled(dln2, j, false);
> +                       mutex_unlock(&dln2->mutex);
> +                       dev_err(&dln2->pdev->dev,
> +                               "Unable to enable ADC channel %d\n", i);
> +                       return -EBUSY;
> +               }
> +       }
> +
> +       dln2_adc_update_demux(dln2);
> +
> +       mutex_unlock(&dln2->mutex);
> +
> +       return 0;
> +}
> +
> +#define DLN2_ADC_CHAN(idx) {                                   \
> +       .type = IIO_VOLTAGE,                                    \
> +       .channel = idx,                                         \
> +       .indexed = 1,                                           \
> +       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
> +       .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |   \
> +                                  BIT(IIO_CHAN_INFO_SAMP_FREQ),\
> +       .scan_index = idx,                                      \
> +       .scan_type = {                                          \
> +               .sign = 'u',                                    \
> +               .realbits = DLN2_ADC_DATA_BITS,                 \
> +               .storagebits = 16,                              \
> +               .endianness = IIO_LE,                           \
> +       }                                                       \
> +}
> +
> +static const struct iio_info dln2_adc_info = {
> +       .read_raw = dln2_adc_read_raw,
> +       .write_raw = dln2_adc_write_raw,
> +       .update_scan_mode = dln2_update_scan_mode,
> +       .driver_module = THIS_MODULE,
> +};
> +
> +static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
> +{
> +       struct iio_poll_func *pf = p;
> +       struct iio_dev *indio_dev = pf->indio_dev;
> +       struct {
> +               __le16 values[DLN2_ADC_MAX_CHANNELS];
> +               int64_t timestamp_space;
> +       } data;
> +       struct dln2_adc_get_all_vals dev_data;
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +       const struct dln2_adc_demux_table *t;
> +       int ret, i;
> +
> +       mutex_lock(&dln2->mutex);
> +       ret = dln2_adc_read_all(dln2, &dev_data);
> +       mutex_unlock(&dln2->mutex);
> +       if (ret < 0)
> +               goto done;
> +
> +       /* Demux operation */
> +       for (i = 0; i < dln2->demux_count; ++i) {
> +               t = &dln2->demux[i];
> +               memcpy((void *)data.values + t->to,
> +                      (void *)dev_data.values + t->from, t->length);
> +       }
> +
> +       /* Zero padding space between values and timestamp */
> +       if (dln2->ts_pad_length)
> +               memset((void *)data.values + dln2->ts_pad_offset,
> +                      0, dln2->ts_pad_length);
> +
> +       iio_push_to_buffers_with_timestamp(indio_dev, &data,
> +                                          iio_get_time_ns(indio_dev));
> +
> +done:
> +       iio_trigger_notify_done(indio_dev->trig);
> +       return IRQ_HANDLED;
> +}
> +
> +static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
> +{
> +       int ret;
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +       u16 conflict;
> +
> +       mutex_lock(&dln2->mutex);
> +
> +       /* Enable ADC */
> +       ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
> +       if (ret < 0) {
> +               mutex_unlock(&dln2->mutex);
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +               if (conflict) {
> +                       dev_err(&dln2->pdev->dev,
> +                               "ADC pins conflict with mask %04X\n",
> +                               (int)conflict);
> +                       ret = -EBUSY;
> +               }
> +               return ret;
> +       }
> +
> +       /* Assign trigger channel based on first enabled channel */
> +       unsigned int trigger_chan =
> +               find_first_bit(indio_dev->active_scan_mask,
> +                              indio_dev->masklength);
> +       if (trigger_chan < DLN2_ADC_MAX_CHANNELS) {
> +               dln2->trigger_chan = trigger_chan;
> +               ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan,
> +                                              dln2->sample_period);
> +               mutex_unlock(&dln2->mutex);
> +               if (ret < 0) {
> +                       dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +                       return ret;
> +               }
> +       } else {
> +               dln2->trigger_chan = -1;
> +               mutex_unlock(&dln2->mutex);
> +       }
> +
> +       return iio_triggered_buffer_postenable(indio_dev);
> +}
> +
> +static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
> +{
> +       int ret;
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +       mutex_lock(&dln2->mutex);
> +
> +       /* Disable trigger channel */
> +       if (dln2->trigger_chan != -1) {
> +               dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0);
> +               dln2->trigger_chan = -1;
> +       }
> +
> +       /* Disable ADC */
> +       ret = dln2_adc_set_port_enabled(dln2, false, NULL);
> +
> +       mutex_unlock(&dln2->mutex);
> +       if (ret < 0) {
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +               return ret;
> +       }
> +
> +       return iio_triggered_buffer_predisable(indio_dev);
> +}
> +
> +static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
> +       .postenable = dln2_adc_triggered_buffer_postenable,
> +       .predisable = dln2_adc_triggered_buffer_predisable,
> +};
> +
> +static void dln2_adc_event(struct platform_device *pdev, u16 echo,
> +                          const void *data, int len)
> +{
> +       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +       /* Called via URB completion handler */
> +       iio_trigger_poll(dln2->trig);
> +}
> +
> +static const struct iio_trigger_ops dln2_adc_trigger_ops = {
> +       .owner = THIS_MODULE,
> +};
> +
> +static int dln2_adc_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct dln2_adc *dln2;
> +       struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
> +       struct iio_dev *indio_dev;
> +       int i, ret, chans;
> +
> +       indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2));
> +       if (!indio_dev) {
> +               dev_err(dev, "failed allocating iio device\n");
> +               return -ENOMEM;
> +       }
> +
> +       dln2 = iio_priv(indio_dev);
> +       dln2->pdev = pdev;
> +       dln2->port = pdata->port;
> +       dln2->trigger_chan = -1;
> +       mutex_init(&dln2->mutex);
> +       dln2->sample_period = 0;
> +       dln2->demux_count = 0;
> +
> +       platform_set_drvdata(pdev, indio_dev);
> +
> +       ret = dln2_adc_set_port_resolution(dln2);
> +       if (ret < 0) {
> +               dev_err(dev, "failed to set ADC resolution to 10 bits\n");
> +               goto dealloc_dev;
> +       }
> +
> +       chans = dln2_adc_get_chan_count(dln2);
> +       if (chans < 0) {
> +               dev_err(dev, "failed to get channel count: %d\n", chans);
> +               ret = chans;
> +               goto dealloc_dev;
> +       }
> +       if (chans > DLN2_ADC_MAX_CHANNELS) {
> +               chans = DLN2_ADC_MAX_CHANNELS;
> +               dev_warn(dev, "clamping channels to %d\n",
> +                        DLN2_ADC_MAX_CHANNELS);
> +       }
> +
> +       for (i = 0; i < chans; ++i) {
> +               struct iio_chan_spec chan_spec = DLN2_ADC_CHAN(i);
> +               dln2->iio_channels[i] = chan_spec;
> +       }
> +       struct iio_chan_spec timestamp_spec = IIO_CHAN_SOFT_TIMESTAMP(i);
> +       dln2->iio_channels[i] = timestamp_spec;
> +
> +       indio_dev->name = DLN2_ADC_MOD_NAME;
> +       indio_dev->dev.parent = dev;
> +       indio_dev->info = &dln2_adc_info;
> +       indio_dev->modes = INDIO_DIRECT_MODE;
> +       indio_dev->channels = dln2->iio_channels;
> +       indio_dev->num_channels = chans + 1;
> +       indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
> +
> +       dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
> +                                           indio_dev->name, indio_dev->id);
> +       if (!dln2->trig) {
> +               dev_err(dev, "failed to allocate trigger\n");
> +               ret = -ENOMEM;
> +               goto dealloc_dev;
> +       }
> +       dln2->trig->ops = &dln2_adc_trigger_ops;
> +       iio_trigger_set_drvdata(dln2->trig, dln2);
> +       iio_trigger_register(dln2->trig);
> +       iio_trigger_set_immutable(indio_dev, dln2->trig);
> +
> +       ret = iio_triggered_buffer_setup(indio_dev, NULL, dln2_adc_trigger_h,
> +                                        &dln2_adc_buffer_setup_ops);
> +       if (ret) {
> +               dev_err(dev, "failed to allocate triggered buffer: %d\n", ret);
> +               goto dealloc_trigger;
> +       }
> +
> +       ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
> +                                    dln2_adc_event);
> +       if (ret) {
> +               dev_err(dev, "failed to register event cb: %d\n", ret);
> +               goto dealloc_pollfunc;
> +       }
> +
> +       ret = iio_device_register(indio_dev);
> +       if (ret) {
> +               dev_err(dev, "failed to register iio device: %d\n", ret);
> +               goto dealloc_pollfunc;
> +       }
> +
> +       return 0;
> +
> +dealloc_pollfunc:
> +       iio_triggered_buffer_cleanup(indio_dev);
> +dealloc_trigger:
> +       iio_trigger_unregister(dln2->trig);
> +dealloc_dev:
> +
> +       return ret;
> +}
> +
> +static int dln2_adc_remove(struct platform_device *pdev)
> +{
> +       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +       iio_device_unregister(indio_dev);
> +       dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
> +       iio_trigger_unregister(dln2->trig);
> +       iio_triggered_buffer_cleanup(indio_dev);
> +
> +       return 0;
> +}
> +
> +static struct platform_driver dln2_adc_driver = {
> +       .driver.name    = DLN2_ADC_MOD_NAME,
> +       .probe          = dln2_adc_probe,
> +       .remove         = dln2_adc_remove,
> +};
> +
> +module_platform_driver(dln2_adc_driver);
> +
> +MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
> +MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:dln2-adc");
> diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
> index 704e189..a22ab8c 100644
> --- a/drivers/mfd/dln2.c
> +++ b/drivers/mfd/dln2.c
> @@ -53,6 +53,7 @@ enum dln2_handle {
>         DLN2_HANDLE_GPIO,
>         DLN2_HANDLE_I2C,
>         DLN2_HANDLE_SPI,
> +       DLN2_HANDLE_ADC,
>         DLN2_HANDLES
>  };
>
> @@ -663,6 +664,12 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
>         .port = 0,
>  };
>
> +/* Only one ADC port supported */
> +static struct dln2_platform_data dln2_pdata_adc = {
> +       .handle = DLN2_HANDLE_ADC,
> +       .port = 0,
> +};
> +
>  static const struct mfd_cell dln2_devs[] = {
>         {
>                 .name = "dln2-gpio",
> @@ -679,6 +686,11 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
>                 .platform_data = &dln2_pdata_spi,
>                 .pdata_size = sizeof(struct dln2_platform_data),
>         },
> +       {
> +               .name = "dln2-adc",
> +               .platform_data = &dln2_pdata_adc,
> +               .pdata_size = sizeof(struct dln2_platform_data),
> +       },
>  };
>
>  static void dln2_stop(struct dln2_dev *dln2)
> diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c
> index eb3f56e..58ef924 100644
> --- a/tools/gpio/lsgpio.c
> +++ b/tools/gpio/lsgpio.c
> @@ -77,7 +77,7 @@ int list_device(const char *device_name)
>         int ret;
>         int i;
>
> -       ret = asprintf(&chrdev_name, "/dev/%s", device_name);
> +       ret = asprintf(&chrdev_name, "/sys/class/gpio/%s", device_name);
>         if (ret < 0)
>                 return -ENOMEM;
>
> @@ -169,7 +169,7 @@ int main(int argc, char **argv)
>                 DIR *dp;
>
>                 /* List all GPIO devices one at a time */
> -               dp = opendir("/dev");
> +               dp = opendir("/sys/class/gpio");
>                 if (!dp) {
>                         ret = -errno;
>                         goto error_out;
> --
> 1.9.1
>

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

* [PATCH v4] iio: adc: Add support for DLN2 ADC
       [not found] <b5ede0cc2cb36695b86edf44c0dddbc4736a6420>
  2017-06-22 22:31 ` [PATCH v2] iio: adc: Add support for DLN2 ADC Jack Andersen
  2017-06-28  0:53 ` [PATCH v3] " Jack Andersen
@ 2017-06-28  1:05 ` Jack Andersen
  2017-06-28  1:18   ` Jack Andersen
  2017-07-01 11:57   ` Jonathan Cameron
  2017-07-05 21:12 ` [PATCH v5 1/3] " Jack Andersen
                   ` (4 subsequent siblings)
  7 siblings, 2 replies; 23+ messages in thread
From: Jack Andersen @ 2017-06-28  1:05 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: Jack Andersen, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler

This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
ADC is the fourth and final component of the DLN2 for the kernel.

Signed-off-by: Jack Andersen <jackoalan@gmail.com>
---
Changes in v2:
- Address Peter's remarks; bringing consistency to C99 syntax, dev_dbg usage,
  and error handling.
- Remove probe/remove logging.
- Utilize stack-allocated buffer for pushing into IIO.
- Add timestamp channel to IIO spec.

Suggested-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
---
Changes in v3:
- Remove lazy channel enabling; integrating with update_channel_mode instead.
- Implement memcpy demux table similar to subsystem core.
- Use iio_triggered_buffer_setup to create buffer.

Suggested-by: Jonathan Cameron <jic23@kernel.org>
---
Changes in v4:
- Remove unrelated, erronous change to lsgpio.c
---
 drivers/iio/adc/Kconfig    |   9 +
 drivers/iio/adc/Makefile   |   1 +
 drivers/iio/adc/dln2-adc.c | 733 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/mfd/dln2.c         |  12 +
 4 files changed, 755 insertions(+)
 create mode 100644 drivers/iio/adc/dln2-adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 401f47b..78d7455 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -239,6 +239,15 @@ config DA9150_GPADC
 	  To compile this driver as a module, choose M here: the module will be
 	  called berlin2-adc.
 
+config DLN2_ADC
+	tristate "Diolan DLN-2 ADC driver support"
+	depends on MFD_DLN2
+	help
+	  Say yes here to build support for Diolan DLN-2 ADC.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called adc_dln2.
+
 config ENVELOPE_DETECTOR
 	tristate "Envelope detector using a DAC and a comparator"
 	depends on OF
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 9339bec..378bc65 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
 obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
 obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
 obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
+obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
 obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
 obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
new file mode 100644
index 0000000..07569d5
--- /dev/null
+++ b/drivers/iio/adc/dln2-adc.c
@@ -0,0 +1,733 @@
+/*
+ * Driver for the Diolan DLN-2 USB-ADC adapter
+ *
+ * Copyright (c) 2017 Jack Andersen
+ *
+ * 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, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/dln2.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+
+#define DLN2_ADC_MOD_NAME "dln2-adc"
+
+#define DLN2_ADC_ID             0x06
+
+#define DLN2_ADC_GET_CHANNEL_COUNT	DLN2_CMD(0x01, DLN2_ADC_ID)
+#define DLN2_ADC_ENABLE			DLN2_CMD(0x02, DLN2_ADC_ID)
+#define DLN2_ADC_DISABLE		DLN2_CMD(0x03, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_ENABLE		DLN2_CMD(0x05, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_DISABLE	DLN2_CMD(0x06, DLN2_ADC_ID)
+#define DLN2_ADC_SET_RESOLUTION		DLN2_CMD(0x08, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_VAL	DLN2_CMD(0x0A, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_ALL_VAL	DLN2_CMD(0x0B, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_SET_CFG	DLN2_CMD(0x0C, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_CFG	DLN2_CMD(0x0D, DLN2_ADC_ID)
+#define DLN2_ADC_CONDITION_MET_EV	DLN2_CMD(0x10, DLN2_ADC_ID)
+
+#define DLN2_ADC_EVENT_NONE		0
+#define DLN2_ADC_EVENT_BELOW		1
+#define DLN2_ADC_EVENT_LEVEL_ABOVE	2
+#define DLN2_ADC_EVENT_OUTSIDE		3
+#define DLN2_ADC_EVENT_INSIDE		4
+#define DLN2_ADC_EVENT_ALWAYS		5
+
+#define DLN2_ADC_MAX_CHANNELS 8
+#define DLN2_ADC_DATA_BITS 10
+
+/*
+ * Plays similar role to iio_demux_table in subsystem core; except allocated
+ * in a fixed 8-element array.
+ */
+struct dln2_adc_demux_table {
+	unsigned int from;
+	unsigned int to;
+	unsigned int length;
+};
+
+struct dln2_adc {
+	struct platform_device *pdev;
+	struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1];
+	int port, trigger_chan;
+	struct iio_trigger *trig;
+	struct mutex mutex;
+	/* Cached sample period in milliseconds */
+	unsigned int sample_period;
+	/* Demux table */
+	unsigned int demux_count;
+	struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS];
+	/* Precomputed timestamp padding offset and length */
+	unsigned int ts_pad_offset, ts_pad_length;
+};
+
+struct dln2_adc_port_chan {
+	u8 port;
+	u8 chan;
+};
+
+struct dln2_adc_get_all_vals {
+	__le16 channel_mask;
+	__le16 values[DLN2_ADC_MAX_CHANNELS];
+};
+
+static void dln2_adc_add_demux(struct dln2_adc *dln2,
+	unsigned int in_loc, unsigned int out_loc,
+	unsigned int length)
+{
+	struct dln2_adc_demux_table *p = dln2->demux_count ?
+		&dln2->demux[dln2->demux_count - 1] : NULL;
+	if (p && p->from + p->length == in_loc &&
+		p->to + p->length == out_loc) {
+		p->length += length;
+	} else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) {
+		p = &dln2->demux[dln2->demux_count++];
+		p->from = in_loc;
+		p->to = out_loc;
+		p->length = length;
+	}
+}
+
+static void dln2_adc_update_demux(struct dln2_adc *dln2)
+{
+	int in_ind = -1, out_ind;
+	unsigned int in_loc = 0, out_loc = 0;
+	struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
+
+	/* Clear out any old demux */
+	dln2->demux_count = 0;
+
+	/* Optimize all 8-channels case */
+	if (indio_dev->masklength &&
+	    (*indio_dev->active_scan_mask & 0xff) == 0xff) {
+		dln2_adc_add_demux(dln2, 0, 0, 16);
+		dln2->ts_pad_offset = 0;
+		dln2->ts_pad_length = 0;
+		return;
+	}
+
+	/* Build demux table from fixed 8-channels to active_scan_mask */
+	for_each_set_bit(out_ind,
+			 indio_dev->active_scan_mask,
+			 indio_dev->masklength) {
+		/* Handle timestamp separately */
+		if (out_ind == DLN2_ADC_MAX_CHANNELS)
+			break;
+		for (++in_ind; in_ind != out_ind; ++in_ind)
+			in_loc += 2;
+		dln2_adc_add_demux(dln2, in_loc, out_loc, 2);
+		out_loc += 2;
+		in_loc += 2;
+	}
+
+	if (indio_dev->scan_timestamp) {
+		size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1;
+
+		dln2->ts_pad_offset = out_loc;
+		dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc;
+	} else {
+		dln2->ts_pad_offset = 0;
+		dln2->ts_pad_length = 0;
+	}
+}
+
+static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
+{
+	int ret;
+	u8 port = dln2->port;
+	u8 count;
+	int olen = sizeof(count);
+
+	ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
+			    &port, sizeof(port), &count, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+	if (olen < sizeof(count))
+		return -EPROTO;
+
+	return count;
+}
+
+static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
+{
+	int ret;
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = DLN2_ADC_DATA_BITS,
+	};
+
+	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
+			       &port_chan, sizeof(port_chan));
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
+				     int channel, bool enable)
+{
+	int ret;
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = channel,
+	};
+
+	u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
+
+	ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable,
+				     u16 *conflict_out)
+{
+	int ret;
+	u8 port = dln2->port;
+	__le16 conflict;
+	int olen = sizeof(conflict);
+
+	u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
+
+	if (conflict_out)
+		*conflict_out = 0;
+
+	ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
+			    &conflict, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
+			__func__, (int)enable);
+		if (conflict_out && enable && olen >= sizeof(conflict))
+			*conflict_out = le16_to_cpu(conflict);
+		return ret;
+	}
+	if (enable && olen < sizeof(conflict))
+		return -EPROTO;
+
+	return 0;
+}
+
+static int dln2_adc_set_chan_period(struct dln2_adc *dln2,
+	unsigned int channel, unsigned int period)
+{
+	int ret;
+	struct {
+		struct dln2_adc_port_chan port_chan;
+		__u8 type;
+		__le16 period;
+		__le16 low;
+		__le16 high;
+	} __packed set_cfg = {
+		.port_chan.port = dln2->port,
+		.port_chan.chan = channel,
+		.type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
+		.period = cpu_to_le16(period)
+	};
+
+	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
+			       &set_cfg, sizeof(set_cfg));
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+	return 0;
+}
+
+static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
+{
+	int ret, i;
+	struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
+	u16 conflict;
+
+	if (iio_buffer_enabled(indio_dev)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ret = dln2_adc_set_chan_enabled(dln2, channel, true);
+	if (ret < 0)
+		goto out;
+
+	ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
+	if (ret < 0) {
+		if (conflict) {
+			dev_err(&dln2->pdev->dev,
+				"ADC pins conflict with mask %04X\n",
+				(int)conflict);
+			ret = -EBUSY;
+		}
+		goto disable_chan;
+	}
+
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = channel,
+	};
+	__le16 value;
+	int olen = sizeof(value);
+
+	/*
+	 * Call GET_VAL twice due to initial zero-return immediately after
+	 * enabling channel.
+	 */
+	for (i = 0; i < 2; ++i) {
+		ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
+				    &port_chan, sizeof(port_chan),
+				    &value, &olen);
+		if (ret < 0) {
+			dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+			goto disable_port;
+		}
+		if (olen < sizeof(value)) {
+			ret = -EPROTO;
+			goto disable_port;
+		}
+	}
+
+	ret = le16_to_cpu(value);
+
+disable_port:
+	dln2_adc_set_port_enabled(dln2, false, NULL);
+disable_chan:
+	dln2_adc_set_chan_enabled(dln2, channel, false);
+out:
+	return ret;
+}
+
+static int dln2_adc_read_all(struct dln2_adc *dln2,
+			     struct dln2_adc_get_all_vals *get_all_vals)
+{
+	int ret;
+	__u8 port = dln2->port;
+	int olen = sizeof(*get_all_vals);
+
+	ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
+			    &port, sizeof(port), get_all_vals, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+	if (olen < sizeof(*get_all_vals))
+		return -EPROTO;
+
+	return 0;
+}
+
+static int dln2_adc_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int *val,
+			     int *val2,
+			     long mask)
+{
+	int ret;
+	unsigned int microhertz;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&dln2->mutex);
+		ret = dln2_adc_read(dln2, chan->channel);
+		mutex_unlock(&dln2->mutex);
+
+		if (ret < 0)
+			return ret;
+
+		*val = ret;
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		/*
+		 * Voltage reference is fixed at 3.3v
+		 *  3.3 / (1 << 10) * 1000000000
+		 */
+		*val = 0;
+		*val2 = 3222656;
+		return IIO_VAL_INT_PLUS_NANO;
+
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (dln2->sample_period) {
+			microhertz = 1000000000 / dln2->sample_period;
+			*val = microhertz / 1000000;
+			*val2 = microhertz % 1000000;
+		} else {
+			*val = 0;
+			*val2 = 0;
+		}
+
+		return IIO_VAL_INT_PLUS_MICRO;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int dln2_adc_write_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int val,
+			      int val2,
+			      long mask)
+{
+	int ret;
+	unsigned int microhertz;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		microhertz = 1000000 * val + val2;
+
+		mutex_lock(&dln2->mutex);
+
+		dln2->sample_period =
+			microhertz ? 1000000000 / microhertz : UINT_MAX;
+		if (dln2->sample_period > 65535) {
+			dln2->sample_period = 65535;
+			dev_warn(&dln2->pdev->dev,
+				 "clamping period to 65535ms\n");
+		}
+
+		/*
+		 * The first requested channel is arbitrated as a shared
+		 * trigger source, so only one event is registered with the
+		 * DLN. The event handler will then read all enabled channel
+		 * values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
+		 * synchronization between ADC readings.
+		 */
+		if (dln2->trigger_chan != -1)
+			ret = dln2_adc_set_chan_period(dln2,
+				dln2->trigger_chan, dln2->sample_period);
+		else
+			ret = 0;
+
+		mutex_unlock(&dln2->mutex);
+
+		return ret;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int dln2_update_scan_mode(struct iio_dev *indio_dev,
+				 const unsigned long *scan_mask)
+{
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+	int chan_count = indio_dev->num_channels - 1;
+	int ret, i, j;
+
+	mutex_lock(&dln2->mutex);
+
+	for (i = 0; i < chan_count; ++i) {
+		ret = dln2_adc_set_chan_enabled(dln2, i,
+						test_bit(i, scan_mask));
+		if (ret < 0) {
+			for (j = 0; j < i; ++j)
+				dln2_adc_set_chan_enabled(dln2, j, false);
+			mutex_unlock(&dln2->mutex);
+			dev_err(&dln2->pdev->dev,
+				"Unable to enable ADC channel %d\n", i);
+			return -EBUSY;
+		}
+	}
+
+	dln2_adc_update_demux(dln2);
+
+	mutex_unlock(&dln2->mutex);
+
+	return 0;
+}
+
+#define DLN2_ADC_CHAN(idx) {					\
+	.type = IIO_VOLTAGE,					\
+	.channel = idx,						\
+	.indexed = 1,						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |	\
+				   BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+	.scan_index = idx,					\
+	.scan_type = {						\
+		.sign = 'u',					\
+		.realbits = DLN2_ADC_DATA_BITS,			\
+		.storagebits = 16,				\
+		.endianness = IIO_LE,				\
+	}							\
+}
+
+static const struct iio_info dln2_adc_info = {
+	.read_raw = dln2_adc_read_raw,
+	.write_raw = dln2_adc_write_raw,
+	.update_scan_mode = dln2_update_scan_mode,
+	.driver_module = THIS_MODULE,
+};
+
+static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct {
+		__le16 values[DLN2_ADC_MAX_CHANNELS];
+		int64_t timestamp_space;
+	} data;
+	struct dln2_adc_get_all_vals dev_data;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+	const struct dln2_adc_demux_table *t;
+	int ret, i;
+
+	mutex_lock(&dln2->mutex);
+	ret = dln2_adc_read_all(dln2, &dev_data);
+	mutex_unlock(&dln2->mutex);
+	if (ret < 0)
+		goto done;
+
+	/* Demux operation */
+	for (i = 0; i < dln2->demux_count; ++i) {
+		t = &dln2->demux[i];
+		memcpy((void *)data.values + t->to,
+		       (void *)dev_data.values + t->from, t->length);
+	}
+
+	/* Zero padding space between values and timestamp */
+	if (dln2->ts_pad_length)
+		memset((void *)data.values + dln2->ts_pad_offset,
+		       0, dln2->ts_pad_length);
+
+	iio_push_to_buffers_with_timestamp(indio_dev, &data,
+					   iio_get_time_ns(indio_dev));
+
+done:
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+	u16 conflict;
+
+	mutex_lock(&dln2->mutex);
+
+	/* Enable ADC */
+	ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
+	if (ret < 0) {
+		mutex_unlock(&dln2->mutex);
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		if (conflict) {
+			dev_err(&dln2->pdev->dev,
+				"ADC pins conflict with mask %04X\n",
+				(int)conflict);
+			ret = -EBUSY;
+		}
+		return ret;
+	}
+
+	/* Assign trigger channel based on first enabled channel */
+	unsigned int trigger_chan =
+		find_first_bit(indio_dev->active_scan_mask,
+			       indio_dev->masklength);
+	if (trigger_chan < DLN2_ADC_MAX_CHANNELS) {
+		dln2->trigger_chan = trigger_chan;
+		ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan,
+					       dln2->sample_period);
+		mutex_unlock(&dln2->mutex);
+		if (ret < 0) {
+			dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+			return ret;
+		}
+	} else {
+		dln2->trigger_chan = -1;
+		mutex_unlock(&dln2->mutex);
+	}
+
+	return iio_triggered_buffer_postenable(indio_dev);
+}
+
+static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	mutex_lock(&dln2->mutex);
+
+	/* Disable trigger channel */
+	if (dln2->trigger_chan != -1) {
+		dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0);
+		dln2->trigger_chan = -1;
+	}
+
+	/* Disable ADC */
+	ret = dln2_adc_set_port_enabled(dln2, false, NULL);
+
+	mutex_unlock(&dln2->mutex);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+
+	return iio_triggered_buffer_predisable(indio_dev);
+}
+
+static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
+	.postenable = dln2_adc_triggered_buffer_postenable,
+	.predisable = dln2_adc_triggered_buffer_predisable,
+};
+
+static void dln2_adc_event(struct platform_device *pdev, u16 echo,
+			   const void *data, int len)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	/* Called via URB completion handler */
+	iio_trigger_poll(dln2->trig);
+}
+
+static const struct iio_trigger_ops dln2_adc_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+static int dln2_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct dln2_adc *dln2;
+	struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	struct iio_dev *indio_dev;
+	int i, ret, chans;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2));
+	if (!indio_dev) {
+		dev_err(dev, "failed allocating iio device\n");
+		return -ENOMEM;
+	}
+
+	dln2 = iio_priv(indio_dev);
+	dln2->pdev = pdev;
+	dln2->port = pdata->port;
+	dln2->trigger_chan = -1;
+	mutex_init(&dln2->mutex);
+	dln2->sample_period = 0;
+	dln2->demux_count = 0;
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	ret = dln2_adc_set_port_resolution(dln2);
+	if (ret < 0) {
+		dev_err(dev, "failed to set ADC resolution to 10 bits\n");
+		goto dealloc_dev;
+	}
+
+	chans = dln2_adc_get_chan_count(dln2);
+	if (chans < 0) {
+		dev_err(dev, "failed to get channel count: %d\n", chans);
+		ret = chans;
+		goto dealloc_dev;
+	}
+	if (chans > DLN2_ADC_MAX_CHANNELS) {
+		chans = DLN2_ADC_MAX_CHANNELS;
+		dev_warn(dev, "clamping channels to %d\n",
+			 DLN2_ADC_MAX_CHANNELS);
+	}
+
+	for (i = 0; i < chans; ++i) {
+		struct iio_chan_spec chan_spec = DLN2_ADC_CHAN(i);
+		dln2->iio_channels[i] = chan_spec;
+	}
+	struct iio_chan_spec timestamp_spec = IIO_CHAN_SOFT_TIMESTAMP(i);
+	dln2->iio_channels[i] = timestamp_spec;
+
+	indio_dev->name = DLN2_ADC_MOD_NAME;
+	indio_dev->dev.parent = dev;
+	indio_dev->info = &dln2_adc_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = dln2->iio_channels;
+	indio_dev->num_channels = chans + 1;
+	indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
+
+	dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
+					    indio_dev->name, indio_dev->id);
+	if (!dln2->trig) {
+		dev_err(dev, "failed to allocate trigger\n");
+		ret = -ENOMEM;
+		goto dealloc_dev;
+	}
+	dln2->trig->ops = &dln2_adc_trigger_ops;
+	iio_trigger_set_drvdata(dln2->trig, dln2);
+	iio_trigger_register(dln2->trig);
+	iio_trigger_set_immutable(indio_dev, dln2->trig);
+
+	ret = iio_triggered_buffer_setup(indio_dev, NULL, dln2_adc_trigger_h,
+					 &dln2_adc_buffer_setup_ops);
+	if (ret) {
+		dev_err(dev, "failed to allocate triggered buffer: %d\n", ret);
+		goto dealloc_trigger;
+	}
+
+	ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
+				     dln2_adc_event);
+	if (ret) {
+		dev_err(dev, "failed to register event cb: %d\n", ret);
+		goto dealloc_pollfunc;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(dev, "failed to register iio device: %d\n", ret);
+		goto dealloc_pollfunc;
+	}
+
+	return 0;
+
+dealloc_pollfunc:
+	iio_triggered_buffer_cleanup(indio_dev);
+dealloc_trigger:
+	iio_trigger_unregister(dln2->trig);
+dealloc_dev:
+
+	return ret;
+}
+
+static int dln2_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
+	iio_trigger_unregister(dln2->trig);
+	iio_triggered_buffer_cleanup(indio_dev);
+
+	return 0;
+}
+
+static struct platform_driver dln2_adc_driver = {
+	.driver.name	= DLN2_ADC_MOD_NAME,
+	.probe		= dln2_adc_probe,
+	.remove		= dln2_adc_remove,
+};
+
+module_platform_driver(dln2_adc_driver);
+
+MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
+MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dln2-adc");
diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
index 704e189..a22ab8c 100644
--- a/drivers/mfd/dln2.c
+++ b/drivers/mfd/dln2.c
@@ -53,6 +53,7 @@ enum dln2_handle {
 	DLN2_HANDLE_GPIO,
 	DLN2_HANDLE_I2C,
 	DLN2_HANDLE_SPI,
+	DLN2_HANDLE_ADC,
 	DLN2_HANDLES
 };
 
@@ -663,6 +664,12 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
 	.port = 0,
 };
 
+/* Only one ADC port supported */
+static struct dln2_platform_data dln2_pdata_adc = {
+	.handle = DLN2_HANDLE_ADC,
+	.port = 0,
+};
+
 static const struct mfd_cell dln2_devs[] = {
 	{
 		.name = "dln2-gpio",
@@ -679,6 +686,11 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
 		.platform_data = &dln2_pdata_spi,
 		.pdata_size = sizeof(struct dln2_platform_data),
 	},
+	{
+		.name = "dln2-adc",
+		.platform_data = &dln2_pdata_adc,
+		.pdata_size = sizeof(struct dln2_platform_data),
+	},
 };
 
 static void dln2_stop(struct dln2_dev *dln2)
-- 
1.9.1

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

* Re: [PATCH v4] iio: adc: Add support for DLN2 ADC
  2017-06-28  1:05 ` [PATCH v4] " Jack Andersen
@ 2017-06-28  1:18   ` Jack Andersen
  2017-07-01 10:22     ` Jonathan Cameron
  2017-07-01 11:57   ` Jonathan Cameron
  1 sibling, 1 reply; 23+ messages in thread
From: Jack Andersen @ 2017-06-28  1:18 UTC (permalink / raw)
  To: linux-iio
  Cc: Jack Andersen, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler

While putting this revision together, I attempted replacing iio_trigger_poll
with iio_trigger_poll_chained. This resulted in a
`bad: scheduling from the idle thread!' log followed by kernel panic.

It seems odd to me, cause the URB completion handler appears to be called in
an interrupt context. A kdump trace even confirms this.

On 27 June 2017 at 15:05, Jack Andersen <jackoalan@gmail.com> wrote:
> This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
> ADC is the fourth and final component of the DLN2 for the kernel.
>
> Signed-off-by: Jack Andersen <jackoalan@gmail.com>
> ---
> Changes in v2:
> - Address Peter's remarks; bringing consistency to C99 syntax, dev_dbg usage,
>   and error handling.
> - Remove probe/remove logging.
> - Utilize stack-allocated buffer for pushing into IIO.
> - Add timestamp channel to IIO spec.
>
> Suggested-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
> ---
> Changes in v3:
> - Remove lazy channel enabling; integrating with update_channel_mode instead.
> - Implement memcpy demux table similar to subsystem core.
> - Use iio_triggered_buffer_setup to create buffer.
>
> Suggested-by: Jonathan Cameron <jic23@kernel.org>
> ---
> Changes in v4:
> - Remove unrelated, erronous change to lsgpio.c
> ---
>  drivers/iio/adc/Kconfig    |   9 +
>  drivers/iio/adc/Makefile   |   1 +
>  drivers/iio/adc/dln2-adc.c | 733 +++++++++++++++++++++++++++++++++++++++++++++
>  drivers/mfd/dln2.c         |  12 +
>  4 files changed, 755 insertions(+)
>  create mode 100644 drivers/iio/adc/dln2-adc.c
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 401f47b..78d7455 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -239,6 +239,15 @@ config DA9150_GPADC
>           To compile this driver as a module, choose M here: the module will be
>           called berlin2-adc.
>
> +config DLN2_ADC
> +       tristate "Diolan DLN-2 ADC driver support"
> +       depends on MFD_DLN2
> +       help
> +         Say yes here to build support for Diolan DLN-2 ADC.
> +
> +         This driver can also be built as a module. If so, the module will be
> +         called adc_dln2.
> +
>  config ENVELOPE_DETECTOR
>         tristate "Envelope detector using a DAC and a comparator"
>         depends on OF
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 9339bec..378bc65 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>  obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>  obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
>  obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
> +obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
>  obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
> diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
> new file mode 100644
> index 0000000..07569d5
> --- /dev/null
> +++ b/drivers/iio/adc/dln2-adc.c
> @@ -0,0 +1,733 @@
> +/*
> + * Driver for the Diolan DLN-2 USB-ADC adapter
> + *
> + * Copyright (c) 2017 Jack Andersen
> + *
> + * 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, version 2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/platform_device.h>
> +#include <linux/mfd/dln2.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/kfifo_buf.h>
> +
> +#define DLN2_ADC_MOD_NAME "dln2-adc"
> +
> +#define DLN2_ADC_ID             0x06
> +
> +#define DLN2_ADC_GET_CHANNEL_COUNT     DLN2_CMD(0x01, DLN2_ADC_ID)
> +#define DLN2_ADC_ENABLE                        DLN2_CMD(0x02, DLN2_ADC_ID)
> +#define DLN2_ADC_DISABLE               DLN2_CMD(0x03, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_ENABLE                DLN2_CMD(0x05, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_DISABLE       DLN2_CMD(0x06, DLN2_ADC_ID)
> +#define DLN2_ADC_SET_RESOLUTION                DLN2_CMD(0x08, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_GET_VAL       DLN2_CMD(0x0A, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_GET_ALL_VAL   DLN2_CMD(0x0B, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_SET_CFG       DLN2_CMD(0x0C, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_GET_CFG       DLN2_CMD(0x0D, DLN2_ADC_ID)
> +#define DLN2_ADC_CONDITION_MET_EV      DLN2_CMD(0x10, DLN2_ADC_ID)
> +
> +#define DLN2_ADC_EVENT_NONE            0
> +#define DLN2_ADC_EVENT_BELOW           1
> +#define DLN2_ADC_EVENT_LEVEL_ABOVE     2
> +#define DLN2_ADC_EVENT_OUTSIDE         3
> +#define DLN2_ADC_EVENT_INSIDE          4
> +#define DLN2_ADC_EVENT_ALWAYS          5
> +
> +#define DLN2_ADC_MAX_CHANNELS 8
> +#define DLN2_ADC_DATA_BITS 10
> +
> +/*
> + * Plays similar role to iio_demux_table in subsystem core; except allocated
> + * in a fixed 8-element array.
> + */
> +struct dln2_adc_demux_table {
> +       unsigned int from;
> +       unsigned int to;
> +       unsigned int length;
> +};
> +
> +struct dln2_adc {
> +       struct platform_device *pdev;
> +       struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1];
> +       int port, trigger_chan;
> +       struct iio_trigger *trig;
> +       struct mutex mutex;
> +       /* Cached sample period in milliseconds */
> +       unsigned int sample_period;
> +       /* Demux table */
> +       unsigned int demux_count;
> +       struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS];
> +       /* Precomputed timestamp padding offset and length */
> +       unsigned int ts_pad_offset, ts_pad_length;
> +};
> +
> +struct dln2_adc_port_chan {
> +       u8 port;
> +       u8 chan;
> +};
> +
> +struct dln2_adc_get_all_vals {
> +       __le16 channel_mask;
> +       __le16 values[DLN2_ADC_MAX_CHANNELS];
> +};
> +
> +static void dln2_adc_add_demux(struct dln2_adc *dln2,
> +       unsigned int in_loc, unsigned int out_loc,
> +       unsigned int length)
> +{
> +       struct dln2_adc_demux_table *p = dln2->demux_count ?
> +               &dln2->demux[dln2->demux_count - 1] : NULL;
> +       if (p && p->from + p->length == in_loc &&
> +               p->to + p->length == out_loc) {
> +               p->length += length;
> +       } else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) {
> +               p = &dln2->demux[dln2->demux_count++];
> +               p->from = in_loc;
> +               p->to = out_loc;
> +               p->length = length;
> +       }
> +}
> +
> +static void dln2_adc_update_demux(struct dln2_adc *dln2)
> +{
> +       int in_ind = -1, out_ind;
> +       unsigned int in_loc = 0, out_loc = 0;
> +       struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
> +
> +       /* Clear out any old demux */
> +       dln2->demux_count = 0;
> +
> +       /* Optimize all 8-channels case */
> +       if (indio_dev->masklength &&
> +           (*indio_dev->active_scan_mask & 0xff) == 0xff) {
> +               dln2_adc_add_demux(dln2, 0, 0, 16);
> +               dln2->ts_pad_offset = 0;
> +               dln2->ts_pad_length = 0;
> +               return;
> +       }
> +
> +       /* Build demux table from fixed 8-channels to active_scan_mask */
> +       for_each_set_bit(out_ind,
> +                        indio_dev->active_scan_mask,
> +                        indio_dev->masklength) {
> +               /* Handle timestamp separately */
> +               if (out_ind == DLN2_ADC_MAX_CHANNELS)
> +                       break;
> +               for (++in_ind; in_ind != out_ind; ++in_ind)
> +                       in_loc += 2;
> +               dln2_adc_add_demux(dln2, in_loc, out_loc, 2);
> +               out_loc += 2;
> +               in_loc += 2;
> +       }
> +
> +       if (indio_dev->scan_timestamp) {
> +               size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1;
> +
> +               dln2->ts_pad_offset = out_loc;
> +               dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc;
> +       } else {
> +               dln2->ts_pad_offset = 0;
> +               dln2->ts_pad_length = 0;
> +       }
> +}
> +
> +static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
> +{
> +       int ret;
> +       u8 port = dln2->port;
> +       u8 count;
> +       int olen = sizeof(count);
> +
> +       ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
> +                           &port, sizeof(port), &count, &olen);
> +       if (ret < 0) {
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +               return ret;
> +       }
> +       if (olen < sizeof(count))
> +               return -EPROTO;
> +
> +       return count;
> +}
> +
> +static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
> +{
> +       int ret;
> +       struct dln2_adc_port_chan port_chan = {
> +               .port = dln2->port,
> +               .chan = DLN2_ADC_DATA_BITS,
> +       };
> +
> +       ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
> +                              &port_chan, sizeof(port_chan));
> +       if (ret < 0) {
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
> +                                    int channel, bool enable)
> +{
> +       int ret;
> +       struct dln2_adc_port_chan port_chan = {
> +               .port = dln2->port,
> +               .chan = channel,
> +       };
> +
> +       u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
> +
> +       ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
> +       if (ret < 0) {
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable,
> +                                    u16 *conflict_out)
> +{
> +       int ret;
> +       u8 port = dln2->port;
> +       __le16 conflict;
> +       int olen = sizeof(conflict);
> +
> +       u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
> +
> +       if (conflict_out)
> +               *conflict_out = 0;
> +
> +       ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
> +                           &conflict, &olen);
> +       if (ret < 0) {
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
> +                       __func__, (int)enable);
> +               if (conflict_out && enable && olen >= sizeof(conflict))
> +                       *conflict_out = le16_to_cpu(conflict);
> +               return ret;
> +       }
> +       if (enable && olen < sizeof(conflict))
> +               return -EPROTO;
> +
> +       return 0;
> +}
> +
> +static int dln2_adc_set_chan_period(struct dln2_adc *dln2,
> +       unsigned int channel, unsigned int period)
> +{
> +       int ret;
> +       struct {
> +               struct dln2_adc_port_chan port_chan;
> +               __u8 type;
> +               __le16 period;
> +               __le16 low;
> +               __le16 high;
> +       } __packed set_cfg = {
> +               .port_chan.port = dln2->port,
> +               .port_chan.chan = channel,
> +               .type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
> +               .period = cpu_to_le16(period)
> +       };
> +
> +       ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
> +                              &set_cfg, sizeof(set_cfg));
> +       if (ret < 0) {
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +               return ret;
> +       }
> +       return 0;
> +}
> +
> +static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
> +{
> +       int ret, i;
> +       struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
> +       u16 conflict;
> +
> +       if (iio_buffer_enabled(indio_dev)) {
> +               ret = -EBUSY;
> +               goto out;
> +       }
> +
> +       ret = dln2_adc_set_chan_enabled(dln2, channel, true);
> +       if (ret < 0)
> +               goto out;
> +
> +       ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
> +       if (ret < 0) {
> +               if (conflict) {
> +                       dev_err(&dln2->pdev->dev,
> +                               "ADC pins conflict with mask %04X\n",
> +                               (int)conflict);
> +                       ret = -EBUSY;
> +               }
> +               goto disable_chan;
> +       }
> +
> +       struct dln2_adc_port_chan port_chan = {
> +               .port = dln2->port,
> +               .chan = channel,
> +       };
> +       __le16 value;
> +       int olen = sizeof(value);
> +
> +       /*
> +        * Call GET_VAL twice due to initial zero-return immediately after
> +        * enabling channel.
> +        */
> +       for (i = 0; i < 2; ++i) {
> +               ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
> +                                   &port_chan, sizeof(port_chan),
> +                                   &value, &olen);
> +               if (ret < 0) {
> +                       dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +                       goto disable_port;
> +               }
> +               if (olen < sizeof(value)) {
> +                       ret = -EPROTO;
> +                       goto disable_port;
> +               }
> +       }
> +
> +       ret = le16_to_cpu(value);
> +
> +disable_port:
> +       dln2_adc_set_port_enabled(dln2, false, NULL);
> +disable_chan:
> +       dln2_adc_set_chan_enabled(dln2, channel, false);
> +out:
> +       return ret;
> +}
> +
> +static int dln2_adc_read_all(struct dln2_adc *dln2,
> +                            struct dln2_adc_get_all_vals *get_all_vals)
> +{
> +       int ret;
> +       __u8 port = dln2->port;
> +       int olen = sizeof(*get_all_vals);
> +
> +       ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
> +                           &port, sizeof(port), get_all_vals, &olen);
> +       if (ret < 0) {
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +               return ret;
> +       }
> +       if (olen < sizeof(*get_all_vals))
> +               return -EPROTO;
> +
> +       return 0;
> +}
> +
> +static int dln2_adc_read_raw(struct iio_dev *indio_dev,
> +                            struct iio_chan_spec const *chan,
> +                            int *val,
> +                            int *val2,
> +                            long mask)
> +{
> +       int ret;
> +       unsigned int microhertz;
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_RAW:
> +               mutex_lock(&dln2->mutex);
> +               ret = dln2_adc_read(dln2, chan->channel);
> +               mutex_unlock(&dln2->mutex);
> +
> +               if (ret < 0)
> +                       return ret;
> +
> +               *val = ret;
> +               return IIO_VAL_INT;
> +
> +       case IIO_CHAN_INFO_SCALE:
> +               /*
> +                * Voltage reference is fixed at 3.3v
> +                *  3.3 / (1 << 10) * 1000000000
> +                */
> +               *val = 0;
> +               *val2 = 3222656;
> +               return IIO_VAL_INT_PLUS_NANO;
> +
> +       case IIO_CHAN_INFO_SAMP_FREQ:
> +               if (dln2->sample_period) {
> +                       microhertz = 1000000000 / dln2->sample_period;
> +                       *val = microhertz / 1000000;
> +                       *val2 = microhertz % 1000000;
> +               } else {
> +                       *val = 0;
> +                       *val2 = 0;
> +               }
> +
> +               return IIO_VAL_INT_PLUS_MICRO;
> +
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static int dln2_adc_write_raw(struct iio_dev *indio_dev,
> +                             struct iio_chan_spec const *chan,
> +                             int val,
> +                             int val2,
> +                             long mask)
> +{
> +       int ret;
> +       unsigned int microhertz;
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_SAMP_FREQ:
> +               microhertz = 1000000 * val + val2;
> +
> +               mutex_lock(&dln2->mutex);
> +
> +               dln2->sample_period =
> +                       microhertz ? 1000000000 / microhertz : UINT_MAX;
> +               if (dln2->sample_period > 65535) {
> +                       dln2->sample_period = 65535;
> +                       dev_warn(&dln2->pdev->dev,
> +                                "clamping period to 65535ms\n");
> +               }
> +
> +               /*
> +                * The first requested channel is arbitrated as a shared
> +                * trigger source, so only one event is registered with the
> +                * DLN. The event handler will then read all enabled channel
> +                * values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
> +                * synchronization between ADC readings.
> +                */
> +               if (dln2->trigger_chan != -1)
> +                       ret = dln2_adc_set_chan_period(dln2,
> +                               dln2->trigger_chan, dln2->sample_period);
> +               else
> +                       ret = 0;
> +
> +               mutex_unlock(&dln2->mutex);
> +
> +               return ret;
> +
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static int dln2_update_scan_mode(struct iio_dev *indio_dev,
> +                                const unsigned long *scan_mask)
> +{
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +       int chan_count = indio_dev->num_channels - 1;
> +       int ret, i, j;
> +
> +       mutex_lock(&dln2->mutex);
> +
> +       for (i = 0; i < chan_count; ++i) {
> +               ret = dln2_adc_set_chan_enabled(dln2, i,
> +                                               test_bit(i, scan_mask));
> +               if (ret < 0) {
> +                       for (j = 0; j < i; ++j)
> +                               dln2_adc_set_chan_enabled(dln2, j, false);
> +                       mutex_unlock(&dln2->mutex);
> +                       dev_err(&dln2->pdev->dev,
> +                               "Unable to enable ADC channel %d\n", i);
> +                       return -EBUSY;
> +               }
> +       }
> +
> +       dln2_adc_update_demux(dln2);
> +
> +       mutex_unlock(&dln2->mutex);
> +
> +       return 0;
> +}
> +
> +#define DLN2_ADC_CHAN(idx) {                                   \
> +       .type = IIO_VOLTAGE,                                    \
> +       .channel = idx,                                         \
> +       .indexed = 1,                                           \
> +       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
> +       .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |   \
> +                                  BIT(IIO_CHAN_INFO_SAMP_FREQ),\
> +       .scan_index = idx,                                      \
> +       .scan_type = {                                          \
> +               .sign = 'u',                                    \
> +               .realbits = DLN2_ADC_DATA_BITS,                 \
> +               .storagebits = 16,                              \
> +               .endianness = IIO_LE,                           \
> +       }                                                       \
> +}
> +
> +static const struct iio_info dln2_adc_info = {
> +       .read_raw = dln2_adc_read_raw,
> +       .write_raw = dln2_adc_write_raw,
> +       .update_scan_mode = dln2_update_scan_mode,
> +       .driver_module = THIS_MODULE,
> +};
> +
> +static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
> +{
> +       struct iio_poll_func *pf = p;
> +       struct iio_dev *indio_dev = pf->indio_dev;
> +       struct {
> +               __le16 values[DLN2_ADC_MAX_CHANNELS];
> +               int64_t timestamp_space;
> +       } data;
> +       struct dln2_adc_get_all_vals dev_data;
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +       const struct dln2_adc_demux_table *t;
> +       int ret, i;
> +
> +       mutex_lock(&dln2->mutex);
> +       ret = dln2_adc_read_all(dln2, &dev_data);
> +       mutex_unlock(&dln2->mutex);
> +       if (ret < 0)
> +               goto done;
> +
> +       /* Demux operation */
> +       for (i = 0; i < dln2->demux_count; ++i) {
> +               t = &dln2->demux[i];
> +               memcpy((void *)data.values + t->to,
> +                      (void *)dev_data.values + t->from, t->length);
> +       }
> +
> +       /* Zero padding space between values and timestamp */
> +       if (dln2->ts_pad_length)
> +               memset((void *)data.values + dln2->ts_pad_offset,
> +                      0, dln2->ts_pad_length);
> +
> +       iio_push_to_buffers_with_timestamp(indio_dev, &data,
> +                                          iio_get_time_ns(indio_dev));
> +
> +done:
> +       iio_trigger_notify_done(indio_dev->trig);
> +       return IRQ_HANDLED;
> +}
> +
> +static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
> +{
> +       int ret;
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +       u16 conflict;
> +
> +       mutex_lock(&dln2->mutex);
> +
> +       /* Enable ADC */
> +       ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
> +       if (ret < 0) {
> +               mutex_unlock(&dln2->mutex);
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +               if (conflict) {
> +                       dev_err(&dln2->pdev->dev,
> +                               "ADC pins conflict with mask %04X\n",
> +                               (int)conflict);
> +                       ret = -EBUSY;
> +               }
> +               return ret;
> +       }
> +
> +       /* Assign trigger channel based on first enabled channel */
> +       unsigned int trigger_chan =
> +               find_first_bit(indio_dev->active_scan_mask,
> +                              indio_dev->masklength);
> +       if (trigger_chan < DLN2_ADC_MAX_CHANNELS) {
> +               dln2->trigger_chan = trigger_chan;
> +               ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan,
> +                                              dln2->sample_period);
> +               mutex_unlock(&dln2->mutex);
> +               if (ret < 0) {
> +                       dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +                       return ret;
> +               }
> +       } else {
> +               dln2->trigger_chan = -1;
> +               mutex_unlock(&dln2->mutex);
> +       }
> +
> +       return iio_triggered_buffer_postenable(indio_dev);
> +}
> +
> +static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
> +{
> +       int ret;
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +       mutex_lock(&dln2->mutex);
> +
> +       /* Disable trigger channel */
> +       if (dln2->trigger_chan != -1) {
> +               dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0);
> +               dln2->trigger_chan = -1;
> +       }
> +
> +       /* Disable ADC */
> +       ret = dln2_adc_set_port_enabled(dln2, false, NULL);
> +
> +       mutex_unlock(&dln2->mutex);
> +       if (ret < 0) {
> +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +               return ret;
> +       }
> +
> +       return iio_triggered_buffer_predisable(indio_dev);
> +}
> +
> +static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
> +       .postenable = dln2_adc_triggered_buffer_postenable,
> +       .predisable = dln2_adc_triggered_buffer_predisable,
> +};
> +
> +static void dln2_adc_event(struct platform_device *pdev, u16 echo,
> +                          const void *data, int len)
> +{
> +       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +       /* Called via URB completion handler */
> +       iio_trigger_poll(dln2->trig);
> +}
> +
> +static const struct iio_trigger_ops dln2_adc_trigger_ops = {
> +       .owner = THIS_MODULE,
> +};
> +
> +static int dln2_adc_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct dln2_adc *dln2;
> +       struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
> +       struct iio_dev *indio_dev;
> +       int i, ret, chans;
> +
> +       indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2));
> +       if (!indio_dev) {
> +               dev_err(dev, "failed allocating iio device\n");
> +               return -ENOMEM;
> +       }
> +
> +       dln2 = iio_priv(indio_dev);
> +       dln2->pdev = pdev;
> +       dln2->port = pdata->port;
> +       dln2->trigger_chan = -1;
> +       mutex_init(&dln2->mutex);
> +       dln2->sample_period = 0;
> +       dln2->demux_count = 0;
> +
> +       platform_set_drvdata(pdev, indio_dev);
> +
> +       ret = dln2_adc_set_port_resolution(dln2);
> +       if (ret < 0) {
> +               dev_err(dev, "failed to set ADC resolution to 10 bits\n");
> +               goto dealloc_dev;
> +       }
> +
> +       chans = dln2_adc_get_chan_count(dln2);
> +       if (chans < 0) {
> +               dev_err(dev, "failed to get channel count: %d\n", chans);
> +               ret = chans;
> +               goto dealloc_dev;
> +       }
> +       if (chans > DLN2_ADC_MAX_CHANNELS) {
> +               chans = DLN2_ADC_MAX_CHANNELS;
> +               dev_warn(dev, "clamping channels to %d\n",
> +                        DLN2_ADC_MAX_CHANNELS);
> +       }
> +
> +       for (i = 0; i < chans; ++i) {
> +               struct iio_chan_spec chan_spec = DLN2_ADC_CHAN(i);
> +               dln2->iio_channels[i] = chan_spec;
> +       }
> +       struct iio_chan_spec timestamp_spec = IIO_CHAN_SOFT_TIMESTAMP(i);
> +       dln2->iio_channels[i] = timestamp_spec;
> +
> +       indio_dev->name = DLN2_ADC_MOD_NAME;
> +       indio_dev->dev.parent = dev;
> +       indio_dev->info = &dln2_adc_info;
> +       indio_dev->modes = INDIO_DIRECT_MODE;
> +       indio_dev->channels = dln2->iio_channels;
> +       indio_dev->num_channels = chans + 1;
> +       indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
> +
> +       dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
> +                                           indio_dev->name, indio_dev->id);
> +       if (!dln2->trig) {
> +               dev_err(dev, "failed to allocate trigger\n");
> +               ret = -ENOMEM;
> +               goto dealloc_dev;
> +       }
> +       dln2->trig->ops = &dln2_adc_trigger_ops;
> +       iio_trigger_set_drvdata(dln2->trig, dln2);
> +       iio_trigger_register(dln2->trig);
> +       iio_trigger_set_immutable(indio_dev, dln2->trig);
> +
> +       ret = iio_triggered_buffer_setup(indio_dev, NULL, dln2_adc_trigger_h,
> +                                        &dln2_adc_buffer_setup_ops);
> +       if (ret) {
> +               dev_err(dev, "failed to allocate triggered buffer: %d\n", ret);
> +               goto dealloc_trigger;
> +       }
> +
> +       ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
> +                                    dln2_adc_event);
> +       if (ret) {
> +               dev_err(dev, "failed to register event cb: %d\n", ret);
> +               goto dealloc_pollfunc;
> +       }
> +
> +       ret = iio_device_register(indio_dev);
> +       if (ret) {
> +               dev_err(dev, "failed to register iio device: %d\n", ret);
> +               goto dealloc_pollfunc;
> +       }
> +
> +       return 0;
> +
> +dealloc_pollfunc:
> +       iio_triggered_buffer_cleanup(indio_dev);
> +dealloc_trigger:
> +       iio_trigger_unregister(dln2->trig);
> +dealloc_dev:
> +
> +       return ret;
> +}
> +
> +static int dln2_adc_remove(struct platform_device *pdev)
> +{
> +       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +       iio_device_unregister(indio_dev);
> +       dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
> +       iio_trigger_unregister(dln2->trig);
> +       iio_triggered_buffer_cleanup(indio_dev);
> +
> +       return 0;
> +}
> +
> +static struct platform_driver dln2_adc_driver = {
> +       .driver.name    = DLN2_ADC_MOD_NAME,
> +       .probe          = dln2_adc_probe,
> +       .remove         = dln2_adc_remove,
> +};
> +
> +module_platform_driver(dln2_adc_driver);
> +
> +MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
> +MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:dln2-adc");
> diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
> index 704e189..a22ab8c 100644
> --- a/drivers/mfd/dln2.c
> +++ b/drivers/mfd/dln2.c
> @@ -53,6 +53,7 @@ enum dln2_handle {
>         DLN2_HANDLE_GPIO,
>         DLN2_HANDLE_I2C,
>         DLN2_HANDLE_SPI,
> +       DLN2_HANDLE_ADC,
>         DLN2_HANDLES
>  };
>
> @@ -663,6 +664,12 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
>         .port = 0,
>  };
>
> +/* Only one ADC port supported */
> +static struct dln2_platform_data dln2_pdata_adc = {
> +       .handle = DLN2_HANDLE_ADC,
> +       .port = 0,
> +};
> +
>  static const struct mfd_cell dln2_devs[] = {
>         {
>                 .name = "dln2-gpio",
> @@ -679,6 +686,11 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
>                 .platform_data = &dln2_pdata_spi,
>                 .pdata_size = sizeof(struct dln2_platform_data),
>         },
> +       {
> +               .name = "dln2-adc",
> +               .platform_data = &dln2_pdata_adc,
> +               .pdata_size = sizeof(struct dln2_platform_data),
> +       },
>  };
>
>  static void dln2_stop(struct dln2_dev *dln2)
> --
> 1.9.1
>

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

* Re: [PATCH v4] iio: adc: Add support for DLN2 ADC
  2017-06-28  1:18   ` Jack Andersen
@ 2017-07-01 10:22     ` Jonathan Cameron
  0 siblings, 0 replies; 23+ messages in thread
From: Jonathan Cameron @ 2017-07-01 10:22 UTC (permalink / raw)
  To: Jack Andersen
  Cc: linux-iio, Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler

On Tue, 27 Jun 2017 15:18:43 -1000
Jack Andersen <jackoalan@gmail.com> wrote:

> While putting this revision together, I attempted replacing iio_trigger_poll
> with iio_trigger_poll_chained. This resulted in a
> `bad: scheduling from the idle thread!' log followed by kernel panic.
> 
> It seems odd to me, cause the URB completion handler appears to be called in
> an interrupt context. A kdump trace even confirms this.
Yeah, that function is rather miss named.  I keep meaning to come up with
a better name and fix that once and for all.

The iio_trigger_poll_chained can only be called from non interrupt
contexts - typically bottom halves or interrupt threads.

So this is exactly as expected.  I didn't know enough about the usb core
to know if it was in interrupt context - but it clearly is!

The resulting message is less than entirely obvious though :)

Jonathan
> 
> On 27 June 2017 at 15:05, Jack Andersen <jackoalan@gmail.com> wrote:
> > This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
> > ADC is the fourth and final component of the DLN2 for the kernel.
> >
> > Signed-off-by: Jack Andersen <jackoalan@gmail.com>
> > ---
> > Changes in v2:
> > - Address Peter's remarks; bringing consistency to C99 syntax, dev_dbg usage,
> >   and error handling.
> > - Remove probe/remove logging.
> > - Utilize stack-allocated buffer for pushing into IIO.
> > - Add timestamp channel to IIO spec.
> >
> > Suggested-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
> > ---
> > Changes in v3:
> > - Remove lazy channel enabling; integrating with update_channel_mode instead.
> > - Implement memcpy demux table similar to subsystem core.
> > - Use iio_triggered_buffer_setup to create buffer.
> >
> > Suggested-by: Jonathan Cameron <jic23@kernel.org>
> > ---
> > Changes in v4:
> > - Remove unrelated, erronous change to lsgpio.c
> > ---
> >  drivers/iio/adc/Kconfig    |   9 +
> >  drivers/iio/adc/Makefile   |   1 +
> >  drivers/iio/adc/dln2-adc.c | 733 +++++++++++++++++++++++++++++++++++++++++++++
> >  drivers/mfd/dln2.c         |  12 +
> >  4 files changed, 755 insertions(+)
> >  create mode 100644 drivers/iio/adc/dln2-adc.c
> >
> > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> > index 401f47b..78d7455 100644
> > --- a/drivers/iio/adc/Kconfig
> > +++ b/drivers/iio/adc/Kconfig
> > @@ -239,6 +239,15 @@ config DA9150_GPADC
> >           To compile this driver as a module, choose M here: the module will be
> >           called berlin2-adc.
> >
> > +config DLN2_ADC
> > +       tristate "Diolan DLN-2 ADC driver support"
> > +       depends on MFD_DLN2
> > +       help
> > +         Say yes here to build support for Diolan DLN-2 ADC.
> > +
> > +         This driver can also be built as a module. If so, the module will be
> > +         called adc_dln2.
> > +
> >  config ENVELOPE_DETECTOR
> >         tristate "Envelope detector using a DAC and a comparator"
> >         depends on OF
> > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> > index 9339bec..378bc65 100644
> > --- a/drivers/iio/adc/Makefile
> > +++ b/drivers/iio/adc/Makefile
> > @@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
> >  obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
> >  obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
> >  obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
> > +obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
> >  obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
> >  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
> >  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
> > diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
> > new file mode 100644
> > index 0000000..07569d5
> > --- /dev/null
> > +++ b/drivers/iio/adc/dln2-adc.c
> > @@ -0,0 +1,733 @@
> > +/*
> > + * Driver for the Diolan DLN-2 USB-ADC adapter
> > + *
> > + * Copyright (c) 2017 Jack Andersen
> > + *
> > + * 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, version 2.
> > + */
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/types.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/mfd/dln2.h>
> > +
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/sysfs.h>
> > +#include <linux/iio/trigger.h>
> > +#include <linux/iio/trigger_consumer.h>
> > +#include <linux/iio/triggered_buffer.h>
> > +#include <linux/iio/buffer.h>
> > +#include <linux/iio/kfifo_buf.h>
> > +
> > +#define DLN2_ADC_MOD_NAME "dln2-adc"
> > +
> > +#define DLN2_ADC_ID             0x06
> > +
> > +#define DLN2_ADC_GET_CHANNEL_COUNT     DLN2_CMD(0x01, DLN2_ADC_ID)
> > +#define DLN2_ADC_ENABLE                        DLN2_CMD(0x02, DLN2_ADC_ID)
> > +#define DLN2_ADC_DISABLE               DLN2_CMD(0x03, DLN2_ADC_ID)
> > +#define DLN2_ADC_CHANNEL_ENABLE                DLN2_CMD(0x05, DLN2_ADC_ID)
> > +#define DLN2_ADC_CHANNEL_DISABLE       DLN2_CMD(0x06, DLN2_ADC_ID)
> > +#define DLN2_ADC_SET_RESOLUTION                DLN2_CMD(0x08, DLN2_ADC_ID)
> > +#define DLN2_ADC_CHANNEL_GET_VAL       DLN2_CMD(0x0A, DLN2_ADC_ID)
> > +#define DLN2_ADC_CHANNEL_GET_ALL_VAL   DLN2_CMD(0x0B, DLN2_ADC_ID)
> > +#define DLN2_ADC_CHANNEL_SET_CFG       DLN2_CMD(0x0C, DLN2_ADC_ID)
> > +#define DLN2_ADC_CHANNEL_GET_CFG       DLN2_CMD(0x0D, DLN2_ADC_ID)
> > +#define DLN2_ADC_CONDITION_MET_EV      DLN2_CMD(0x10, DLN2_ADC_ID)
> > +
> > +#define DLN2_ADC_EVENT_NONE            0
> > +#define DLN2_ADC_EVENT_BELOW           1
> > +#define DLN2_ADC_EVENT_LEVEL_ABOVE     2
> > +#define DLN2_ADC_EVENT_OUTSIDE         3
> > +#define DLN2_ADC_EVENT_INSIDE          4
> > +#define DLN2_ADC_EVENT_ALWAYS          5
> > +
> > +#define DLN2_ADC_MAX_CHANNELS 8
> > +#define DLN2_ADC_DATA_BITS 10
> > +
> > +/*
> > + * Plays similar role to iio_demux_table in subsystem core; except allocated
> > + * in a fixed 8-element array.
> > + */
> > +struct dln2_adc_demux_table {
> > +       unsigned int from;
> > +       unsigned int to;
> > +       unsigned int length;
> > +};
> > +
> > +struct dln2_adc {
> > +       struct platform_device *pdev;
> > +       struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1];
> > +       int port, trigger_chan;
> > +       struct iio_trigger *trig;
> > +       struct mutex mutex;
> > +       /* Cached sample period in milliseconds */
> > +       unsigned int sample_period;
> > +       /* Demux table */
> > +       unsigned int demux_count;
> > +       struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS];
> > +       /* Precomputed timestamp padding offset and length */
> > +       unsigned int ts_pad_offset, ts_pad_length;
> > +};
> > +
> > +struct dln2_adc_port_chan {
> > +       u8 port;
> > +       u8 chan;
> > +};
> > +
> > +struct dln2_adc_get_all_vals {
> > +       __le16 channel_mask;
> > +       __le16 values[DLN2_ADC_MAX_CHANNELS];
> > +};
> > +
> > +static void dln2_adc_add_demux(struct dln2_adc *dln2,
> > +       unsigned int in_loc, unsigned int out_loc,
> > +       unsigned int length)
> > +{
> > +       struct dln2_adc_demux_table *p = dln2->demux_count ?
> > +               &dln2->demux[dln2->demux_count - 1] : NULL;
> > +       if (p && p->from + p->length == in_loc &&
> > +               p->to + p->length == out_loc) {
> > +               p->length += length;
> > +       } else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) {
> > +               p = &dln2->demux[dln2->demux_count++];
> > +               p->from = in_loc;
> > +               p->to = out_loc;
> > +               p->length = length;
> > +       }
> > +}
> > +
> > +static void dln2_adc_update_demux(struct dln2_adc *dln2)
> > +{
> > +       int in_ind = -1, out_ind;
> > +       unsigned int in_loc = 0, out_loc = 0;
> > +       struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
> > +
> > +       /* Clear out any old demux */
> > +       dln2->demux_count = 0;
> > +
> > +       /* Optimize all 8-channels case */
> > +       if (indio_dev->masklength &&
> > +           (*indio_dev->active_scan_mask & 0xff) == 0xff) {
> > +               dln2_adc_add_demux(dln2, 0, 0, 16);
> > +               dln2->ts_pad_offset = 0;
> > +               dln2->ts_pad_length = 0;
> > +               return;
> > +       }
> > +
> > +       /* Build demux table from fixed 8-channels to active_scan_mask */
> > +       for_each_set_bit(out_ind,
> > +                        indio_dev->active_scan_mask,
> > +                        indio_dev->masklength) {
> > +               /* Handle timestamp separately */
> > +               if (out_ind == DLN2_ADC_MAX_CHANNELS)
> > +                       break;
> > +               for (++in_ind; in_ind != out_ind; ++in_ind)
> > +                       in_loc += 2;
> > +               dln2_adc_add_demux(dln2, in_loc, out_loc, 2);
> > +               out_loc += 2;
> > +               in_loc += 2;
> > +       }
> > +
> > +       if (indio_dev->scan_timestamp) {
> > +               size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1;
> > +
> > +               dln2->ts_pad_offset = out_loc;
> > +               dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc;
> > +       } else {
> > +               dln2->ts_pad_offset = 0;
> > +               dln2->ts_pad_length = 0;
> > +       }
> > +}
> > +
> > +static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
> > +{
> > +       int ret;
> > +       u8 port = dln2->port;
> > +       u8 count;
> > +       int olen = sizeof(count);
> > +
> > +       ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
> > +                           &port, sizeof(port), &count, &olen);
> > +       if (ret < 0) {
> > +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> > +               return ret;
> > +       }
> > +       if (olen < sizeof(count))
> > +               return -EPROTO;
> > +
> > +       return count;
> > +}
> > +
> > +static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
> > +{
> > +       int ret;
> > +       struct dln2_adc_port_chan port_chan = {
> > +               .port = dln2->port,
> > +               .chan = DLN2_ADC_DATA_BITS,
> > +       };
> > +
> > +       ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
> > +                              &port_chan, sizeof(port_chan));
> > +       if (ret < 0) {
> > +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> > +               return ret;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
> > +                                    int channel, bool enable)
> > +{
> > +       int ret;
> > +       struct dln2_adc_port_chan port_chan = {
> > +               .port = dln2->port,
> > +               .chan = channel,
> > +       };
> > +
> > +       u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
> > +
> > +       ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
> > +       if (ret < 0) {
> > +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> > +               return ret;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable,
> > +                                    u16 *conflict_out)
> > +{
> > +       int ret;
> > +       u8 port = dln2->port;
> > +       __le16 conflict;
> > +       int olen = sizeof(conflict);
> > +
> > +       u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
> > +
> > +       if (conflict_out)
> > +               *conflict_out = 0;
> > +
> > +       ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
> > +                           &conflict, &olen);
> > +       if (ret < 0) {
> > +               dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
> > +                       __func__, (int)enable);
> > +               if (conflict_out && enable && olen >= sizeof(conflict))
> > +                       *conflict_out = le16_to_cpu(conflict);
> > +               return ret;
> > +       }
> > +       if (enable && olen < sizeof(conflict))
> > +               return -EPROTO;
> > +
> > +       return 0;
> > +}
> > +
> > +static int dln2_adc_set_chan_period(struct dln2_adc *dln2,
> > +       unsigned int channel, unsigned int period)
> > +{
> > +       int ret;
> > +       struct {
> > +               struct dln2_adc_port_chan port_chan;
> > +               __u8 type;
> > +               __le16 period;
> > +               __le16 low;
> > +               __le16 high;
> > +       } __packed set_cfg = {
> > +               .port_chan.port = dln2->port,
> > +               .port_chan.chan = channel,
> > +               .type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
> > +               .period = cpu_to_le16(period)
> > +       };
> > +
> > +       ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
> > +                              &set_cfg, sizeof(set_cfg));
> > +       if (ret < 0) {
> > +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> > +               return ret;
> > +       }
> > +       return 0;
> > +}
> > +
> > +static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
> > +{
> > +       int ret, i;
> > +       struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
> > +       u16 conflict;
> > +
> > +       if (iio_buffer_enabled(indio_dev)) {
> > +               ret = -EBUSY;
> > +               goto out;
> > +       }
> > +
> > +       ret = dln2_adc_set_chan_enabled(dln2, channel, true);
> > +       if (ret < 0)
> > +               goto out;
> > +
> > +       ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
> > +       if (ret < 0) {
> > +               if (conflict) {
> > +                       dev_err(&dln2->pdev->dev,
> > +                               "ADC pins conflict with mask %04X\n",
> > +                               (int)conflict);
> > +                       ret = -EBUSY;
> > +               }
> > +               goto disable_chan;
> > +       }
> > +
> > +       struct dln2_adc_port_chan port_chan = {
> > +               .port = dln2->port,
> > +               .chan = channel,
> > +       };
> > +       __le16 value;
> > +       int olen = sizeof(value);
> > +
> > +       /*
> > +        * Call GET_VAL twice due to initial zero-return immediately after
> > +        * enabling channel.
> > +        */
> > +       for (i = 0; i < 2; ++i) {
> > +               ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
> > +                                   &port_chan, sizeof(port_chan),
> > +                                   &value, &olen);
> > +               if (ret < 0) {
> > +                       dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> > +                       goto disable_port;
> > +               }
> > +               if (olen < sizeof(value)) {
> > +                       ret = -EPROTO;
> > +                       goto disable_port;
> > +               }
> > +       }
> > +
> > +       ret = le16_to_cpu(value);
> > +
> > +disable_port:
> > +       dln2_adc_set_port_enabled(dln2, false, NULL);
> > +disable_chan:
> > +       dln2_adc_set_chan_enabled(dln2, channel, false);
> > +out:
> > +       return ret;
> > +}
> > +
> > +static int dln2_adc_read_all(struct dln2_adc *dln2,
> > +                            struct dln2_adc_get_all_vals *get_all_vals)
> > +{
> > +       int ret;
> > +       __u8 port = dln2->port;
> > +       int olen = sizeof(*get_all_vals);
> > +
> > +       ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
> > +                           &port, sizeof(port), get_all_vals, &olen);
> > +       if (ret < 0) {
> > +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> > +               return ret;
> > +       }
> > +       if (olen < sizeof(*get_all_vals))
> > +               return -EPROTO;
> > +
> > +       return 0;
> > +}
> > +
> > +static int dln2_adc_read_raw(struct iio_dev *indio_dev,
> > +                            struct iio_chan_spec const *chan,
> > +                            int *val,
> > +                            int *val2,
> > +                            long mask)
> > +{
> > +       int ret;
> > +       unsigned int microhertz;
> > +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> > +
> > +       switch (mask) {
> > +       case IIO_CHAN_INFO_RAW:
> > +               mutex_lock(&dln2->mutex);
> > +               ret = dln2_adc_read(dln2, chan->channel);
> > +               mutex_unlock(&dln2->mutex);
> > +
> > +               if (ret < 0)
> > +                       return ret;
> > +
> > +               *val = ret;
> > +               return IIO_VAL_INT;
> > +
> > +       case IIO_CHAN_INFO_SCALE:
> > +               /*
> > +                * Voltage reference is fixed at 3.3v
> > +                *  3.3 / (1 << 10) * 1000000000
> > +                */
> > +               *val = 0;
> > +               *val2 = 3222656;
> > +               return IIO_VAL_INT_PLUS_NANO;
> > +
> > +       case IIO_CHAN_INFO_SAMP_FREQ:
> > +               if (dln2->sample_period) {
> > +                       microhertz = 1000000000 / dln2->sample_period;
> > +                       *val = microhertz / 1000000;
> > +                       *val2 = microhertz % 1000000;
> > +               } else {
> > +                       *val = 0;
> > +                       *val2 = 0;
> > +               }
> > +
> > +               return IIO_VAL_INT_PLUS_MICRO;
> > +
> > +       default:
> > +               return -EINVAL;
> > +       }
> > +}
> > +
> > +static int dln2_adc_write_raw(struct iio_dev *indio_dev,
> > +                             struct iio_chan_spec const *chan,
> > +                             int val,
> > +                             int val2,
> > +                             long mask)
> > +{
> > +       int ret;
> > +       unsigned int microhertz;
> > +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> > +
> > +       switch (mask) {
> > +       case IIO_CHAN_INFO_SAMP_FREQ:
> > +               microhertz = 1000000 * val + val2;
> > +
> > +               mutex_lock(&dln2->mutex);
> > +
> > +               dln2->sample_period =
> > +                       microhertz ? 1000000000 / microhertz : UINT_MAX;
> > +               if (dln2->sample_period > 65535) {
> > +                       dln2->sample_period = 65535;
> > +                       dev_warn(&dln2->pdev->dev,
> > +                                "clamping period to 65535ms\n");
> > +               }
> > +
> > +               /*
> > +                * The first requested channel is arbitrated as a shared
> > +                * trigger source, so only one event is registered with the
> > +                * DLN. The event handler will then read all enabled channel
> > +                * values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
> > +                * synchronization between ADC readings.
> > +                */
> > +               if (dln2->trigger_chan != -1)
> > +                       ret = dln2_adc_set_chan_period(dln2,
> > +                               dln2->trigger_chan, dln2->sample_period);
> > +               else
> > +                       ret = 0;
> > +
> > +               mutex_unlock(&dln2->mutex);
> > +
> > +               return ret;
> > +
> > +       default:
> > +               return -EINVAL;
> > +       }
> > +}
> > +
> > +static int dln2_update_scan_mode(struct iio_dev *indio_dev,
> > +                                const unsigned long *scan_mask)
> > +{
> > +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> > +       int chan_count = indio_dev->num_channels - 1;
> > +       int ret, i, j;
> > +
> > +       mutex_lock(&dln2->mutex);
> > +
> > +       for (i = 0; i < chan_count; ++i) {
> > +               ret = dln2_adc_set_chan_enabled(dln2, i,
> > +                                               test_bit(i, scan_mask));
> > +               if (ret < 0) {
> > +                       for (j = 0; j < i; ++j)
> > +                               dln2_adc_set_chan_enabled(dln2, j, false);
> > +                       mutex_unlock(&dln2->mutex);
> > +                       dev_err(&dln2->pdev->dev,
> > +                               "Unable to enable ADC channel %d\n", i);
> > +                       return -EBUSY;
> > +               }
> > +       }
> > +
> > +       dln2_adc_update_demux(dln2);
> > +
> > +       mutex_unlock(&dln2->mutex);
> > +
> > +       return 0;
> > +}
> > +
> > +#define DLN2_ADC_CHAN(idx) {                                   \
> > +       .type = IIO_VOLTAGE,                                    \
> > +       .channel = idx,                                         \
> > +       .indexed = 1,                                           \
> > +       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
> > +       .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |   \
> > +                                  BIT(IIO_CHAN_INFO_SAMP_FREQ),\
> > +       .scan_index = idx,                                      \
> > +       .scan_type = {                                          \
> > +               .sign = 'u',                                    \
> > +               .realbits = DLN2_ADC_DATA_BITS,                 \
> > +               .storagebits = 16,                              \
> > +               .endianness = IIO_LE,                           \
> > +       }                                                       \
> > +}
> > +
> > +static const struct iio_info dln2_adc_info = {
> > +       .read_raw = dln2_adc_read_raw,
> > +       .write_raw = dln2_adc_write_raw,
> > +       .update_scan_mode = dln2_update_scan_mode,
> > +       .driver_module = THIS_MODULE,
> > +};
> > +
> > +static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
> > +{
> > +       struct iio_poll_func *pf = p;
> > +       struct iio_dev *indio_dev = pf->indio_dev;
> > +       struct {
> > +               __le16 values[DLN2_ADC_MAX_CHANNELS];
> > +               int64_t timestamp_space;
> > +       } data;
> > +       struct dln2_adc_get_all_vals dev_data;
> > +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> > +       const struct dln2_adc_demux_table *t;
> > +       int ret, i;
> > +
> > +       mutex_lock(&dln2->mutex);
> > +       ret = dln2_adc_read_all(dln2, &dev_data);
> > +       mutex_unlock(&dln2->mutex);
> > +       if (ret < 0)
> > +               goto done;
> > +
> > +       /* Demux operation */
> > +       for (i = 0; i < dln2->demux_count; ++i) {
> > +               t = &dln2->demux[i];
> > +               memcpy((void *)data.values + t->to,
> > +                      (void *)dev_data.values + t->from, t->length);
> > +       }
> > +
> > +       /* Zero padding space between values and timestamp */
> > +       if (dln2->ts_pad_length)
> > +               memset((void *)data.values + dln2->ts_pad_offset,
> > +                      0, dln2->ts_pad_length);
> > +
> > +       iio_push_to_buffers_with_timestamp(indio_dev, &data,
> > +                                          iio_get_time_ns(indio_dev));
> > +
> > +done:
> > +       iio_trigger_notify_done(indio_dev->trig);
> > +       return IRQ_HANDLED;
> > +}
> > +
> > +static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
> > +{
> > +       int ret;
> > +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> > +       u16 conflict;
> > +
> > +       mutex_lock(&dln2->mutex);
> > +
> > +       /* Enable ADC */
> > +       ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
> > +       if (ret < 0) {
> > +               mutex_unlock(&dln2->mutex);
> > +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> > +               if (conflict) {
> > +                       dev_err(&dln2->pdev->dev,
> > +                               "ADC pins conflict with mask %04X\n",
> > +                               (int)conflict);
> > +                       ret = -EBUSY;
> > +               }
> > +               return ret;
> > +       }
> > +
> > +       /* Assign trigger channel based on first enabled channel */
> > +       unsigned int trigger_chan =
> > +               find_first_bit(indio_dev->active_scan_mask,
> > +                              indio_dev->masklength);
> > +       if (trigger_chan < DLN2_ADC_MAX_CHANNELS) {
> > +               dln2->trigger_chan = trigger_chan;
> > +               ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan,
> > +                                              dln2->sample_period);
> > +               mutex_unlock(&dln2->mutex);
> > +               if (ret < 0) {
> > +                       dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> > +                       return ret;
> > +               }
> > +       } else {
> > +               dln2->trigger_chan = -1;
> > +               mutex_unlock(&dln2->mutex);
> > +       }
> > +
> > +       return iio_triggered_buffer_postenable(indio_dev);
> > +}
> > +
> > +static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
> > +{
> > +       int ret;
> > +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> > +
> > +       mutex_lock(&dln2->mutex);
> > +
> > +       /* Disable trigger channel */
> > +       if (dln2->trigger_chan != -1) {
> > +               dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0);
> > +               dln2->trigger_chan = -1;
> > +       }
> > +
> > +       /* Disable ADC */
> > +       ret = dln2_adc_set_port_enabled(dln2, false, NULL);
> > +
> > +       mutex_unlock(&dln2->mutex);
> > +       if (ret < 0) {
> > +               dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> > +               return ret;
> > +       }
> > +
> > +       return iio_triggered_buffer_predisable(indio_dev);
> > +}
> > +
> > +static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
> > +       .postenable = dln2_adc_triggered_buffer_postenable,
> > +       .predisable = dln2_adc_triggered_buffer_predisable,
> > +};
> > +
> > +static void dln2_adc_event(struct platform_device *pdev, u16 echo,
> > +                          const void *data, int len)
> > +{
> > +       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> > +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> > +
> > +       /* Called via URB completion handler */
> > +       iio_trigger_poll(dln2->trig);
> > +}
> > +
> > +static const struct iio_trigger_ops dln2_adc_trigger_ops = {
> > +       .owner = THIS_MODULE,
> > +};
> > +
> > +static int dln2_adc_probe(struct platform_device *pdev)
> > +{
> > +       struct device *dev = &pdev->dev;
> > +       struct dln2_adc *dln2;
> > +       struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
> > +       struct iio_dev *indio_dev;
> > +       int i, ret, chans;
> > +
> > +       indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2));
> > +       if (!indio_dev) {
> > +               dev_err(dev, "failed allocating iio device\n");
> > +               return -ENOMEM;
> > +       }
> > +
> > +       dln2 = iio_priv(indio_dev);
> > +       dln2->pdev = pdev;
> > +       dln2->port = pdata->port;
> > +       dln2->trigger_chan = -1;
> > +       mutex_init(&dln2->mutex);
> > +       dln2->sample_period = 0;
> > +       dln2->demux_count = 0;
> > +
> > +       platform_set_drvdata(pdev, indio_dev);
> > +
> > +       ret = dln2_adc_set_port_resolution(dln2);
> > +       if (ret < 0) {
> > +               dev_err(dev, "failed to set ADC resolution to 10 bits\n");
> > +               goto dealloc_dev;
> > +       }
> > +
> > +       chans = dln2_adc_get_chan_count(dln2);
> > +       if (chans < 0) {
> > +               dev_err(dev, "failed to get channel count: %d\n", chans);
> > +               ret = chans;
> > +               goto dealloc_dev;
> > +       }
> > +       if (chans > DLN2_ADC_MAX_CHANNELS) {
> > +               chans = DLN2_ADC_MAX_CHANNELS;
> > +               dev_warn(dev, "clamping channels to %d\n",
> > +                        DLN2_ADC_MAX_CHANNELS);
> > +       }
> > +
> > +       for (i = 0; i < chans; ++i) {
> > +               struct iio_chan_spec chan_spec = DLN2_ADC_CHAN(i);
> > +               dln2->iio_channels[i] = chan_spec;
> > +       }
> > +       struct iio_chan_spec timestamp_spec = IIO_CHAN_SOFT_TIMESTAMP(i);
> > +       dln2->iio_channels[i] = timestamp_spec;
> > +
> > +       indio_dev->name = DLN2_ADC_MOD_NAME;
> > +       indio_dev->dev.parent = dev;
> > +       indio_dev->info = &dln2_adc_info;
> > +       indio_dev->modes = INDIO_DIRECT_MODE;
> > +       indio_dev->channels = dln2->iio_channels;
> > +       indio_dev->num_channels = chans + 1;
> > +       indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
> > +
> > +       dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
> > +                                           indio_dev->name, indio_dev->id);
> > +       if (!dln2->trig) {
> > +               dev_err(dev, "failed to allocate trigger\n");
> > +               ret = -ENOMEM;
> > +               goto dealloc_dev;
> > +       }
> > +       dln2->trig->ops = &dln2_adc_trigger_ops;
> > +       iio_trigger_set_drvdata(dln2->trig, dln2);
> > +       iio_trigger_register(dln2->trig);
> > +       iio_trigger_set_immutable(indio_dev, dln2->trig);
> > +
> > +       ret = iio_triggered_buffer_setup(indio_dev, NULL, dln2_adc_trigger_h,
> > +                                        &dln2_adc_buffer_setup_ops);
> > +       if (ret) {
> > +               dev_err(dev, "failed to allocate triggered buffer: %d\n", ret);
> > +               goto dealloc_trigger;
> > +       }
> > +
> > +       ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
> > +                                    dln2_adc_event);
> > +       if (ret) {
> > +               dev_err(dev, "failed to register event cb: %d\n", ret);
> > +               goto dealloc_pollfunc;
> > +       }
> > +
> > +       ret = iio_device_register(indio_dev);
> > +       if (ret) {
> > +               dev_err(dev, "failed to register iio device: %d\n", ret);
> > +               goto dealloc_pollfunc;
> > +       }
> > +
> > +       return 0;
> > +
> > +dealloc_pollfunc:
> > +       iio_triggered_buffer_cleanup(indio_dev);
> > +dealloc_trigger:
> > +       iio_trigger_unregister(dln2->trig);
> > +dealloc_dev:
> > +
> > +       return ret;
> > +}
> > +
> > +static int dln2_adc_remove(struct platform_device *pdev)
> > +{
> > +       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> > +       struct dln2_adc *dln2 = iio_priv(indio_dev);
> > +
> > +       iio_device_unregister(indio_dev);
> > +       dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
> > +       iio_trigger_unregister(dln2->trig);
> > +       iio_triggered_buffer_cleanup(indio_dev);
> > +
> > +       return 0;
> > +}
> > +
> > +static struct platform_driver dln2_adc_driver = {
> > +       .driver.name    = DLN2_ADC_MOD_NAME,
> > +       .probe          = dln2_adc_probe,
> > +       .remove         = dln2_adc_remove,
> > +};
> > +
> > +module_platform_driver(dln2_adc_driver);
> > +
> > +MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
> > +MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_ALIAS("platform:dln2-adc");
> > diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
> > index 704e189..a22ab8c 100644
> > --- a/drivers/mfd/dln2.c
> > +++ b/drivers/mfd/dln2.c
> > @@ -53,6 +53,7 @@ enum dln2_handle {
> >         DLN2_HANDLE_GPIO,
> >         DLN2_HANDLE_I2C,
> >         DLN2_HANDLE_SPI,
> > +       DLN2_HANDLE_ADC,
> >         DLN2_HANDLES
> >  };
> >
> > @@ -663,6 +664,12 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
> >         .port = 0,
> >  };
> >
> > +/* Only one ADC port supported */
> > +static struct dln2_platform_data dln2_pdata_adc = {
> > +       .handle = DLN2_HANDLE_ADC,
> > +       .port = 0,
> > +};
> > +
> >  static const struct mfd_cell dln2_devs[] = {
> >         {
> >                 .name = "dln2-gpio",
> > @@ -679,6 +686,11 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
> >                 .platform_data = &dln2_pdata_spi,
> >                 .pdata_size = sizeof(struct dln2_platform_data),
> >         },
> > +       {
> > +               .name = "dln2-adc",
> > +               .platform_data = &dln2_pdata_adc,
> > +               .pdata_size = sizeof(struct dln2_platform_data),
> > +       },
> >  };
> >
> >  static void dln2_stop(struct dln2_dev *dln2)
> > --
> > 1.9.1
> >  
> --
> 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] 23+ messages in thread

* Re: [PATCH v4] iio: adc: Add support for DLN2 ADC
  2017-06-28  1:05 ` [PATCH v4] " Jack Andersen
  2017-06-28  1:18   ` Jack Andersen
@ 2017-07-01 11:57   ` Jonathan Cameron
  1 sibling, 0 replies; 23+ messages in thread
From: Jonathan Cameron @ 2017-07-01 11:57 UTC (permalink / raw)
  To: Jack Andersen
  Cc: linux-iio, linux-kernel, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Lee Jones

On Tue, 27 Jun 2017 15:05:19 -1000
Jack Andersen <jackoalan@gmail.com> wrote:

> This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
> ADC is the fourth and final component of the DLN2 for the kernel.
> 
> Signed-off-by: Jack Andersen <jackoalan@gmail.com>
Given it touches files in mfd, this needs an ack from Lee.

Please make sure to cc him in future as it'll speed things up!

Fundamentals are all fine, but a few details to cleanup inline.

We've almost certainly missed the upcoming merge window now, so
lets take our time to get a really polished result!

Jonathan

> ---
> Changes in v2:
> - Address Peter's remarks; bringing consistency to C99 syntax, dev_dbg usage,
>   and error handling.
> - Remove probe/remove logging.
> - Utilize stack-allocated buffer for pushing into IIO.
> - Add timestamp channel to IIO spec.
> 
> Suggested-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
> ---
> Changes in v3:
> - Remove lazy channel enabling; integrating with update_channel_mode instead.
> - Implement memcpy demux table similar to subsystem core.
> - Use iio_triggered_buffer_setup to create buffer.
> 
> Suggested-by: Jonathan Cameron <jic23@kernel.org>
> ---
> Changes in v4:
> - Remove unrelated, erronous change to lsgpio.c
> ---
>  drivers/iio/adc/Kconfig    |   9 +
>  drivers/iio/adc/Makefile   |   1 +
>  drivers/iio/adc/dln2-adc.c | 733 +++++++++++++++++++++++++++++++++++++++++++++
>  drivers/mfd/dln2.c         |  12 +
>  4 files changed, 755 insertions(+)
>  create mode 100644 drivers/iio/adc/dln2-adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 401f47b..78d7455 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -239,6 +239,15 @@ config DA9150_GPADC
>  	  To compile this driver as a module, choose M here: the module will be
>  	  called berlin2-adc.
>  
> +config DLN2_ADC
> +	tristate "Diolan DLN-2 ADC driver support"
> +	depends on MFD_DLN2
> +	help
> +	  Say yes here to build support for Diolan DLN-2 ADC.
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called adc_dln2.
> +
>  config ENVELOPE_DETECTOR
>  	tristate "Envelope detector using a DAC and a comparator"
>  	depends on OF
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 9339bec..378bc65 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>  obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>  obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
>  obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
> +obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
>  obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
> diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
> new file mode 100644
> index 0000000..07569d5
> --- /dev/null
> +++ b/drivers/iio/adc/dln2-adc.c
> @@ -0,0 +1,733 @@
> +/*
> + * Driver for the Diolan DLN-2 USB-ADC adapter
> + *
> + * Copyright (c) 2017 Jack Andersen
> + *
> + * 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, version 2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/platform_device.h>
> +#include <linux/mfd/dln2.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/kfifo_buf.h>
> +
> +#define DLN2_ADC_MOD_NAME "dln2-adc"
> +
> +#define DLN2_ADC_ID             0x06
> +
> +#define DLN2_ADC_GET_CHANNEL_COUNT	DLN2_CMD(0x01, DLN2_ADC_ID)
> +#define DLN2_ADC_ENABLE			DLN2_CMD(0x02, DLN2_ADC_ID)
> +#define DLN2_ADC_DISABLE		DLN2_CMD(0x03, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_ENABLE		DLN2_CMD(0x05, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_DISABLE	DLN2_CMD(0x06, DLN2_ADC_ID)
> +#define DLN2_ADC_SET_RESOLUTION		DLN2_CMD(0x08, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_GET_VAL	DLN2_CMD(0x0A, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_GET_ALL_VAL	DLN2_CMD(0x0B, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_SET_CFG	DLN2_CMD(0x0C, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_GET_CFG	DLN2_CMD(0x0D, DLN2_ADC_ID)
> +#define DLN2_ADC_CONDITION_MET_EV	DLN2_CMD(0x10, DLN2_ADC_ID)
> +
> +#define DLN2_ADC_EVENT_NONE		0
> +#define DLN2_ADC_EVENT_BELOW		1
> +#define DLN2_ADC_EVENT_LEVEL_ABOVE	2
> +#define DLN2_ADC_EVENT_OUTSIDE		3
> +#define DLN2_ADC_EVENT_INSIDE		4
> +#define DLN2_ADC_EVENT_ALWAYS		5
> +
> +#define DLN2_ADC_MAX_CHANNELS 8
> +#define DLN2_ADC_DATA_BITS 10
> +
> +/*
> + * Plays similar role to iio_demux_table in subsystem core; except allocated
> + * in a fixed 8-element array.
> + */
> +struct dln2_adc_demux_table {
> +	unsigned int from;
> +	unsigned int to;
> +	unsigned int length;
> +};
> +
> +struct dln2_adc {
> +	struct platform_device *pdev;
> +	struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1];
> +	int port, trigger_chan;
> +	struct iio_trigger *trig;
> +	struct mutex mutex;
> +	/* Cached sample period in milliseconds */
> +	unsigned int sample_period;
> +	/* Demux table */
> +	unsigned int demux_count;
> +	struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS];
> +	/* Precomputed timestamp padding offset and length */
> +	unsigned int ts_pad_offset, ts_pad_length;
> +};
> +
> +struct dln2_adc_port_chan {
> +	u8 port;
> +	u8 chan;
> +};
> +
> +struct dln2_adc_get_all_vals {
> +	__le16 channel_mask;
> +	__le16 values[DLN2_ADC_MAX_CHANNELS];
> +};
> +
> +static void dln2_adc_add_demux(struct dln2_adc *dln2,
> +	unsigned int in_loc, unsigned int out_loc,
> +	unsigned int length)
> +{
Nice to see the cunning version ;)
I always like this little optimization - though I'll admit
I didn't think you'd bother for use within the driver.

Anyhow, it's well tested code, so why not I guess.
> +	struct dln2_adc_demux_table *p = dln2->demux_count ?
> +		&dln2->demux[dln2->demux_count - 1] : NULL;
> +	if (p && p->from + p->length == in_loc &&
> +		p->to + p->length == out_loc) {
> +		p->length += length;
> +	} else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) {
> +		p = &dln2->demux[dln2->demux_count++];
> +		p->from = in_loc;
> +		p->to = out_loc;
> +		p->length = length;
> +	}
> +}
> +
> +static void dln2_adc_update_demux(struct dln2_adc *dln2)
> +{
> +	int in_ind = -1, out_ind;
> +	unsigned int in_loc = 0, out_loc = 0;
> +	struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
> +
> +	/* Clear out any old demux */
> +	dln2->demux_count = 0;
> +
> +	/* Optimize all 8-channels case */
> +	if (indio_dev->masklength &&
> +	    (*indio_dev->active_scan_mask & 0xff) == 0xff) {
> +		dln2_adc_add_demux(dln2, 0, 0, 16);
> +		dln2->ts_pad_offset = 0;
> +		dln2->ts_pad_length = 0;
> +		return;
> +	}
> +
> +	/* Build demux table from fixed 8-channels to active_scan_mask */
> +	for_each_set_bit(out_ind,
> +			 indio_dev->active_scan_mask,
> +			 indio_dev->masklength) {
> +		/* Handle timestamp separately */
> +		if (out_ind == DLN2_ADC_MAX_CHANNELS)
> +			break;
> +		for (++in_ind; in_ind != out_ind; ++in_ind)
> +			in_loc += 2;
> +		dln2_adc_add_demux(dln2, in_loc, out_loc, 2);
> +		out_loc += 2;
> +		in_loc += 2;
> +	}
> +
> +	if (indio_dev->scan_timestamp) {
> +		size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1;
> +
> +		dln2->ts_pad_offset = out_loc;
> +		dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc;
> +	} else {
> +		dln2->ts_pad_offset = 0;
> +		dln2->ts_pad_length = 0;
> +	}
> +}
> +
> +static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
> +{
> +	int ret;
> +	u8 port = dln2->port;
> +	u8 count;
> +	int olen = sizeof(count);
> +
> +	ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
> +			    &port, sizeof(port), &count, &olen);
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +	if (olen < sizeof(count))
> +		return -EPROTO;
> +
> +	return count;
> +}
> +
> +static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
> +{
> +	int ret;
> +	struct dln2_adc_port_chan port_chan = {
> +		.port = dln2->port,
> +		.chan = DLN2_ADC_DATA_BITS,
> +	};
> +
> +	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
> +			       &port_chan, sizeof(port_chan));
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +
> +	return 0;
I'm fairly sure dln2_transfer_tx returns 0 on success (based a skim
read) in which case in all the cases where it is used like this
just return ret at the end of the function.  Chances are one
of the static checkers will pick this up and we'll get patches
shortly after merging anyway!
> +}
> +
> +static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
> +				     int channel, bool enable)
> +{
> +	int ret;
> +	struct dln2_adc_port_chan port_chan = {
> +		.port = dln2->port,
> +		.chan = channel,
> +	};
> +
> +	u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
> +
> +	ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable,
> +				     u16 *conflict_out)
> +{
> +	int ret;
> +	u8 port = dln2->port;
> +	__le16 conflict;
> +	int olen = sizeof(conflict);
> +
> +	u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
> +
> +	if (conflict_out)
> +		*conflict_out = 0;
> +
> +	ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
> +			    &conflict, &olen);
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
> +			__func__, (int)enable);
> +		if (conflict_out && enable && olen >= sizeof(conflict))
> +			*conflict_out = le16_to_cpu(conflict);
> +		return ret;
> +	}
> +	if (enable && olen < sizeof(conflict))
> +		return -EPROTO;
> +
> +	return 0;
> +}
> +
> +static int dln2_adc_set_chan_period(struct dln2_adc *dln2,
> +	unsigned int channel, unsigned int period)
> +{
> +	int ret;
> +	struct {
> +		struct dln2_adc_port_chan port_chan;
> +		__u8 type;
> +		__le16 period;
> +		__le16 low;
> +		__le16 high;
> +	} __packed set_cfg = {
> +		.port_chan.port = dln2->port,
> +		.port_chan.chan = channel,
> +		.type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
> +		.period = cpu_to_le16(period)
> +	};
> +
> +	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
> +			       &set_cfg, sizeof(set_cfg));
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
> +{
> +	int ret, i;
> +	struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
> +	u16 conflict;
> +
> +	if (iio_buffer_enabled(indio_dev)) {
> +		ret = -EBUSY;
> +		goto out;
return -EBUSY;
+ please use the iio_claim_direct_mode etc.  What you have here
doesn't prevent races where the buffer is enabled whilst this
function is running...  Those utility functions are there
to ensure everyone gets the right (we used to get it wrong
'a lot'!)
> +	}
> +
> +	ret = dln2_adc_set_chan_enabled(dln2, channel, true);
> +	if (ret < 0)
return ret;
> +		goto out;
> +
> +	ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
> +	if (ret < 0) {
> +		if (conflict) {
> +			dev_err(&dln2->pdev->dev,
> +				"ADC pins conflict with mask %04X\n",
> +				(int)conflict);
> +			ret = -EBUSY;
> +		}
> +		goto disable_chan;
> +	}
> +
> +	struct dln2_adc_port_chan port_chan = {
> +		.port = dln2->port,
> +		.chan = channel,
> +	};
> +	__le16 value;
> +	int olen = sizeof(value);
Please stick to c style local variables (top of function).  If it makes
sense to have them here, then perhaps a little utility function is
what is needed.
> +
> +	/*
> +	 * Call GET_VAL twice due to initial zero-return immediately after
> +	 * enabling channel.
> +	 */
> +	for (i = 0; i < 2; ++i) {
> +		ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
> +				    &port_chan, sizeof(port_chan),
> +				    &value, &olen);
> +		if (ret < 0) {
> +			dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +			goto disable_port;
> +		}
> +		if (olen < sizeof(value)) {
> +			ret = -EPROTO;
> +			goto disable_port;
> +		}
> +	}
> +
> +	ret = le16_to_cpu(value);
> +
> +disable_port:
> +	dln2_adc_set_port_enabled(dln2, false, NULL);
> +disable_chan:
> +	dln2_adc_set_chan_enabled(dln2, channel, false);
> +out:
> +	return ret;
> +}
> +
> +static int dln2_adc_read_all(struct dln2_adc *dln2,
> +			     struct dln2_adc_get_all_vals *get_all_vals)
> +{
> +	int ret;
> +	__u8 port = dln2->port;
> +	int olen = sizeof(*get_all_vals);
> +
> +	ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
> +			    &port, sizeof(port), get_all_vals, &olen);
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +	if (olen < sizeof(*get_all_vals))
> +		return -EPROTO;
> +
> +	return 0;
> +}
> +
> +static int dln2_adc_read_raw(struct iio_dev *indio_dev,
> +			     struct iio_chan_spec const *chan,
> +			     int *val,
> +			     int *val2,
> +			     long mask)
> +{
> +	int ret;
> +	unsigned int microhertz;
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		mutex_lock(&dln2->mutex);
> +		ret = dln2_adc_read(dln2, chan->channel);
> +		mutex_unlock(&dln2->mutex);
> +
> +		if (ret < 0)
> +			return ret;
> +
> +		*val = ret;
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		/*
> +		 * Voltage reference is fixed at 3.3v
> +		 *  3.3 / (1 << 10) * 1000000000
> +		 */
> +		*val = 0;
> +		*val2 = 3222656;
> +		return IIO_VAL_INT_PLUS_NANO;
> +
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		if (dln2->sample_period) {
> +			microhertz = 1000000000 / dln2->sample_period;
> +			*val = microhertz / 1000000;
> +			*val2 = microhertz % 1000000;

> +		} else {
> +			*val = 0;
> +			*val2 = 0;
> +		}
> +
> +		return IIO_VAL_INT_PLUS_MICRO;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int dln2_adc_write_raw(struct iio_dev *indio_dev,
> +			      struct iio_chan_spec const *chan,
> +			      int val,
> +			      int val2,
> +			      long mask)
> +{
> +	int ret;
> +	unsigned int microhertz;
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		microhertz = 1000000 * val + val2;
> +
> +		mutex_lock(&dln2->mutex);
> +
> +		dln2->sample_period =
> +			microhertz ? 1000000000 / microhertz : UINT_MAX;
> +		if (dln2->sample_period > 65535) {
> +			dln2->sample_period = 65535;
> +			dev_warn(&dln2->pdev->dev,
> +				 "clamping period to 65535ms\n");
> +		}
> +
> +		/*
> +		 * The first requested channel is arbitrated as a shared
> +		 * trigger source, so only one event is registered with the
> +		 * DLN. The event handler will then read all enabled channel
> +		 * values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
> +		 * synchronization between ADC readings.
> +		 */
> +		if (dln2->trigger_chan != -1)
> +			ret = dln2_adc_set_chan_period(dln2,
> +				dln2->trigger_chan, dln2->sample_period);
> +		else
> +			ret = 0;
> +
> +		mutex_unlock(&dln2->mutex);
> +
> +		return ret;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int dln2_update_scan_mode(struct iio_dev *indio_dev,
> +				 const unsigned long *scan_mask)
> +{
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +	int chan_count = indio_dev->num_channels - 1;
> +	int ret, i, j;
> +
> +	mutex_lock(&dln2->mutex);
> +
> +	for (i = 0; i < chan_count; ++i) {
> +		ret = dln2_adc_set_chan_enabled(dln2, i,
> +						test_bit(i, scan_mask));
> +		if (ret < 0) {
> +			for (j = 0; j < i; ++j)
> +				dln2_adc_set_chan_enabled(dln2, j, false);
> +			mutex_unlock(&dln2->mutex);
> +			dev_err(&dln2->pdev->dev,
> +				"Unable to enable ADC channel %d\n", i);
> +			return -EBUSY;
> +		}
> +	}
> +
> +	dln2_adc_update_demux(dln2);
> +
> +	mutex_unlock(&dln2->mutex);
> +
> +	return 0;
> +}
> +
> +#define DLN2_ADC_CHAN(idx) {					\
> +	.type = IIO_VOLTAGE,					\
> +	.channel = idx,						\
> +	.indexed = 1,						\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
> +	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |	\
> +				   BIT(IIO_CHAN_INFO_SAMP_FREQ),\
> +	.scan_index = idx,					\
> +	.scan_type = {						\
> +		.sign = 'u',					\
> +		.realbits = DLN2_ADC_DATA_BITS,			\
> +		.storagebits = 16,				\
> +		.endianness = IIO_LE,				\
> +	}							\
> +}
> +
> +static const struct iio_info dln2_adc_info = {
> +	.read_raw = dln2_adc_read_raw,
> +	.write_raw = dln2_adc_write_raw,
> +	.update_scan_mode = dln2_update_scan_mode,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct {
> +		__le16 values[DLN2_ADC_MAX_CHANNELS];
> +		int64_t timestamp_space;
> +	} data;
> +	struct dln2_adc_get_all_vals dev_data;
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +	const struct dln2_adc_demux_table *t;
> +	int ret, i;
> +
> +	mutex_lock(&dln2->mutex);
> +	ret = dln2_adc_read_all(dln2, &dev_data);
> +	mutex_unlock(&dln2->mutex);
> +	if (ret < 0)
> +		goto done;
> +
> +	/* Demux operation */
> +	for (i = 0; i < dln2->demux_count; ++i) {
> +		t = &dln2->demux[i];
> +		memcpy((void *)data.values + t->to,
> +		       (void *)dev_data.values + t->from, t->length);
> +	}
> +
> +	/* Zero padding space between values and timestamp */
> +	if (dln2->ts_pad_length)
> +		memset((void *)data.values + dln2->ts_pad_offset,
> +		       0, dln2->ts_pad_length);
> +
> +	iio_push_to_buffers_with_timestamp(indio_dev, &data,
> +					   iio_get_time_ns(indio_dev));
> +
> +done:
> +	iio_trigger_notify_done(indio_dev->trig);
> +	return IRQ_HANDLED;
> +}
> +
> +static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
> +{
I think you have struck a good balance here on where
the various elements are (update_scan_mode vs here)
Another device that doesn't quite fit the model :(
> +	int ret;
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +	u16 conflict;
> +
> +	mutex_lock(&dln2->mutex);
> +
> +	/* Enable ADC */
> +	ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
> +	if (ret < 0) {
> +		mutex_unlock(&dln2->mutex);
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		if (conflict) {
> +			dev_err(&dln2->pdev->dev,
> +				"ADC pins conflict with mask %04X\n",
> +				(int)conflict);
> +			ret = -EBUSY;
> +		}
> +		return ret;
> +	}
> +
> +	/* Assign trigger channel based on first enabled channel */
> +	unsigned int trigger_chan =
> +		find_first_bit(indio_dev->active_scan_mask,
> +			       indio_dev->masklength);
> +	if (trigger_chan < DLN2_ADC_MAX_CHANNELS) {
> +		dln2->trigger_chan = trigger_chan;
> +		ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan,
> +					       dln2->sample_period);
> +		mutex_unlock(&dln2->mutex);
> +		if (ret < 0) {
> +			dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +			return ret;
> +		}
> +	} else {
> +		dln2->trigger_chan = -1;
> +		mutex_unlock(&dln2->mutex);
> +	}
> +
> +	return iio_triggered_buffer_postenable(indio_dev);
> +}
> +
> +static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
> +{
> +	int ret;
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +	mutex_lock(&dln2->mutex);
> +
> +	/* Disable trigger channel */
> +	if (dln2->trigger_chan != -1) {
> +		dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0);
> +		dln2->trigger_chan = -1;
> +	}
> +
> +	/* Disable ADC */
> +	ret = dln2_adc_set_port_enabled(dln2, false, NULL);
> +
> +	mutex_unlock(&dln2->mutex);
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +
> +	return iio_triggered_buffer_predisable(indio_dev);
> +}
> +
> +static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
> +	.postenable = dln2_adc_triggered_buffer_postenable,
> +	.predisable = dln2_adc_triggered_buffer_predisable,
> +};
> +
> +static void dln2_adc_event(struct platform_device *pdev, u16 echo,
> +			   const void *data, int len)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +	/* Called via URB completion handler */
> +	iio_trigger_poll(dln2->trig);
> +}
> +
> +static const struct iio_trigger_ops dln2_adc_trigger_ops = {
> +	.owner = THIS_MODULE,
> +};
> +
> +static int dln2_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct dln2_adc *dln2;
> +	struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
> +	struct iio_dev *indio_dev;
> +	int i, ret, chans;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2));
> +	if (!indio_dev) {
> +		dev_err(dev, "failed allocating iio device\n");
> +		return -ENOMEM;
> +	}
> +
> +	dln2 = iio_priv(indio_dev);
> +	dln2->pdev = pdev;
> +	dln2->port = pdata->port;
> +	dln2->trigger_chan = -1;
> +	mutex_init(&dln2->mutex);
> +	dln2->sample_period = 0;
> +	dln2->demux_count = 0;
One might argue these last two are obvious defaults and
they are already true due to kzalloc of the iio_priv region.
If you prefer to keep them as documentation then fair enough.
> +
> +	platform_set_drvdata(pdev, indio_dev);
> +
> +	ret = dln2_adc_set_port_resolution(dln2);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to set ADC resolution to 10 bits\n");
> +		goto dealloc_dev;
> +	}
> +
> +	chans = dln2_adc_get_chan_count(dln2);
> +	if (chans < 0) {
> +		dev_err(dev, "failed to get channel count: %d\n", chans);
> +		ret = chans;
> +		goto dealloc_dev;
If there is no unwinding to be done, it is always preferred to
return directly.

return chans;
> +	}
> +	if (chans > DLN2_ADC_MAX_CHANNELS) {
> +		chans = DLN2_ADC_MAX_CHANNELS;
> +		dev_warn(dev, "clamping channels to %d\n",
> +			 DLN2_ADC_MAX_CHANNELS);
> +	}
> +
> +	for (i = 0; i < chans; ++i) {
> +		struct iio_chan_spec chan_spec = DLN2_ADC_CHAN(i);
> +		dln2->iio_channels[i] = chan_spec;
> +	}
> +	struct iio_chan_spec timestamp_spec = IIO_CHAN_SOFT_TIMESTAMP(i);
> +	dln2->iio_channels[i] = timestamp_spec;
Why the dance with the local variable?  Also, c here, so local
variable should be at the top of the scope.
> +
> +	indio_dev->name = DLN2_ADC_MOD_NAME;
> +	indio_dev->dev.parent = dev;
> +	indio_dev->info = &dln2_adc_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->channels = dln2->iio_channels;
> +	indio_dev->num_channels = chans + 1;
> +	indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
> +
> +	dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
> +					    indio_dev->name, indio_dev->id);
> +	if (!dln2->trig) {
> +		dev_err(dev, "failed to allocate trigger\n");
> +		ret = -ENOMEM;
> +		goto dealloc_dev;
Preferred to return directly as no unwinding to be done.
> +	}
> +	dln2->trig->ops = &dln2_adc_trigger_ops;
> +	iio_trigger_set_drvdata(dln2->trig, dln2);
> +	iio_trigger_register(dln2->trig);
Given we are all devm managed allocs to here, you could use devm_iio_trigger_register
and keep it going a few more lines...
> +	iio_trigger_set_immutable(indio_dev, dln2->trig);
> +
> +	ret = iio_triggered_buffer_setup(indio_dev, NULL, dln2_adc_trigger_h,
> +					 &dln2_adc_buffer_setup_ops);
> +	if (ret) {
> +		dev_err(dev, "failed to allocate triggered buffer: %d\n", ret);
> +		goto dealloc_trigger;
We do have devm_iio_triggered_buffer_setup if you want a really small
simplication of the code.
> +	}
> +
> +	ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
> +				     dln2_adc_event);
> +	if (ret) {
> +		dev_err(dev, "failed to register event cb: %d\n", ret);
> +		goto dealloc_pollfunc;
Error label is a bit missleading as a lot more than that is done on
this condition...
> +	}
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret) {
> +		dev_err(dev, "failed to register iio device: %d\n", ret);
> +		goto dealloc_pollfunc;
Shouldn't an error here be calling:
dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);

> +	}
> +
> +	return 0;
> +
> +dealloc_pollfunc:
> +	iio_triggered_buffer_cleanup(indio_dev);
> +dealloc_trigger:
> +	iio_trigger_unregister(dln2->trig);
> +dealloc_dev:
> +
> +	return ret;
> +}
> +
> +static int dln2_adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +	iio_device_unregister(indio_dev);
> +	dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
> +	iio_trigger_unregister(dln2->trig);
> +	iio_triggered_buffer_cleanup(indio_dev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver dln2_adc_driver = {
> +	.driver.name	= DLN2_ADC_MOD_NAME,
> +	.probe		= dln2_adc_probe,
> +	.remove		= dln2_adc_remove,
> +};
> +
> +module_platform_driver(dln2_adc_driver);
> +
> +MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
> +MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:dln2-adc");
> diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
> index 704e189..a22ab8c 100644
> --- a/drivers/mfd/dln2.c
> +++ b/drivers/mfd/dln2.c
> @@ -53,6 +53,7 @@ enum dln2_handle {
>  	DLN2_HANDLE_GPIO,
>  	DLN2_HANDLE_I2C,
>  	DLN2_HANDLE_SPI,
> +	DLN2_HANDLE_ADC,
>  	DLN2_HANDLES
>  };
>  
> @@ -663,6 +664,12 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
>  	.port = 0,
>  };
>  
> +/* Only one ADC port supported */
> +static struct dln2_platform_data dln2_pdata_adc = {
> +	.handle = DLN2_HANDLE_ADC,
> +	.port = 0,
> +};
> +
>  static const struct mfd_cell dln2_devs[] = {
>  	{
>  		.name = "dln2-gpio",
> @@ -679,6 +686,11 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
>  		.platform_data = &dln2_pdata_spi,
>  		.pdata_size = sizeof(struct dln2_platform_data),
>  	},
> +	{
> +		.name = "dln2-adc",
> +		.platform_data = &dln2_pdata_adc,
> +		.pdata_size = sizeof(struct dln2_platform_data),
> +	},
>  };
>  
>  static void dln2_stop(struct dln2_dev *dln2)

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

* [PATCH v5 1/3] iio: adc: Add support for DLN2 ADC
       [not found] <b5ede0cc2cb36695b86edf44c0dddbc4736a6420>
                   ` (2 preceding siblings ...)
  2017-06-28  1:05 ` [PATCH v4] " Jack Andersen
@ 2017-07-05 21:12 ` Jack Andersen
  2017-07-06 11:37   ` Jonathan Cameron
  2017-07-05 21:12 ` [PATCH v5 2/3] mfd: dln2: Add cell for initializing " Jack Andersen
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Jack Andersen @ 2017-07-05 21:12 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: Jack Andersen, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Lee Jones

This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
ADC is the fourth and final component of the DLN2 for the kernel.

Signed-off-by: Jack Andersen <jackoalan@gmail.com>
---
Changes in v2:
- Address Peter's remarks; bringing consistency to C99 syntax, dev_dbg usage,
  and error handling.
- Remove probe/remove logging.
- Utilize stack-allocated buffer for pushing into IIO.
- Add timestamp channel to IIO spec.

Suggested-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
---
Changes in v3:
- Remove lazy channel enabling; integrating with update_channel_mode instead.
- Implement memcpy demux table similar to subsystem core.
- Use iio_triggered_buffer_setup to create buffer.

Suggested-by: Jonathan Cameron <jic23@kernel.org>
---
Changes in v4:
- Remove unrelated, erronous change to lsgpio.c
---
Changes in v5:
- Use of devm functions wherever possible.
- Use iio_device_claim_direct_mode.
- Variable declaration more consistent with C style.
- MFD modifications in separate patch.
Suggested-by: Jonathan Cameron <jic23@kernel.org>
---
 drivers/iio/adc/Kconfig    |   9 +
 drivers/iio/adc/Makefile   |   1 +
 drivers/iio/adc/dln2-adc.c | 719 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 729 insertions(+)
 create mode 100644 drivers/iio/adc/dln2-adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 614fa41..196d2ce 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -239,6 +239,15 @@ config DA9150_GPADC
 	  To compile this driver as a module, choose M here: the module will be
 	  called berlin2-adc.
 
+config DLN2_ADC
+	tristate "Diolan DLN-2 ADC driver support"
+	depends on MFD_DLN2
+	help
+	  Say yes here to build support for Diolan DLN-2 ADC.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called adc_dln2.
+
 config ENVELOPE_DETECTOR
 	tristate "Envelope detector using a DAC and a comparator"
 	depends on OF
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index b546736a..7ea0bf3 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
 obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
 obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
 obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
+obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
 obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
 obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
new file mode 100644
index 0000000..649bbef9b
--- /dev/null
+++ b/drivers/iio/adc/dln2-adc.c
@@ -0,0 +1,719 @@
+/*
+ * Driver for the Diolan DLN-2 USB-ADC adapter
+ *
+ * Copyright (c) 2017 Jack Andersen
+ *
+ * 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, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/dln2.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+
+#define DLN2_ADC_MOD_NAME "dln2-adc"
+
+#define DLN2_ADC_ID             0x06
+
+#define DLN2_ADC_GET_CHANNEL_COUNT	DLN2_CMD(0x01, DLN2_ADC_ID)
+#define DLN2_ADC_ENABLE			DLN2_CMD(0x02, DLN2_ADC_ID)
+#define DLN2_ADC_DISABLE		DLN2_CMD(0x03, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_ENABLE		DLN2_CMD(0x05, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_DISABLE	DLN2_CMD(0x06, DLN2_ADC_ID)
+#define DLN2_ADC_SET_RESOLUTION		DLN2_CMD(0x08, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_VAL	DLN2_CMD(0x0A, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_ALL_VAL	DLN2_CMD(0x0B, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_SET_CFG	DLN2_CMD(0x0C, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_CFG	DLN2_CMD(0x0D, DLN2_ADC_ID)
+#define DLN2_ADC_CONDITION_MET_EV	DLN2_CMD(0x10, DLN2_ADC_ID)
+
+#define DLN2_ADC_EVENT_NONE		0
+#define DLN2_ADC_EVENT_BELOW		1
+#define DLN2_ADC_EVENT_LEVEL_ABOVE	2
+#define DLN2_ADC_EVENT_OUTSIDE		3
+#define DLN2_ADC_EVENT_INSIDE		4
+#define DLN2_ADC_EVENT_ALWAYS		5
+
+#define DLN2_ADC_MAX_CHANNELS 8
+#define DLN2_ADC_DATA_BITS 10
+
+/*
+ * Plays similar role to iio_demux_table in subsystem core; except allocated
+ * in a fixed 8-element array.
+ */
+struct dln2_adc_demux_table {
+	unsigned int from;
+	unsigned int to;
+	unsigned int length;
+};
+
+struct dln2_adc {
+	struct platform_device *pdev;
+	struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1];
+	int port, trigger_chan;
+	struct iio_trigger *trig;
+	struct mutex mutex;
+	/* Cached sample period in milliseconds */
+	unsigned int sample_period;
+	/* Demux table */
+	unsigned int demux_count;
+	struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS];
+	/* Precomputed timestamp padding offset and length */
+	unsigned int ts_pad_offset, ts_pad_length;
+};
+
+struct dln2_adc_port_chan {
+	u8 port;
+	u8 chan;
+};
+
+struct dln2_adc_get_all_vals {
+	__le16 channel_mask;
+	__le16 values[DLN2_ADC_MAX_CHANNELS];
+};
+
+static void dln2_adc_add_demux(struct dln2_adc *dln2,
+	unsigned int in_loc, unsigned int out_loc,
+	unsigned int length)
+{
+	struct dln2_adc_demux_table *p = dln2->demux_count ?
+		&dln2->demux[dln2->demux_count - 1] : NULL;
+
+	if (p && p->from + p->length == in_loc &&
+		p->to + p->length == out_loc) {
+		p->length += length;
+	} else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) {
+		p = &dln2->demux[dln2->demux_count++];
+		p->from = in_loc;
+		p->to = out_loc;
+		p->length = length;
+	}
+}
+
+static void dln2_adc_update_demux(struct dln2_adc *dln2)
+{
+	int in_ind = -1, out_ind;
+	unsigned int in_loc = 0, out_loc = 0;
+	struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
+
+	/* Clear out any old demux */
+	dln2->demux_count = 0;
+
+	/* Optimize all 8-channels case */
+	if (indio_dev->masklength &&
+	    (*indio_dev->active_scan_mask & 0xff) == 0xff) {
+		dln2_adc_add_demux(dln2, 0, 0, 16);
+		dln2->ts_pad_offset = 0;
+		dln2->ts_pad_length = 0;
+		return;
+	}
+
+	/* Build demux table from fixed 8-channels to active_scan_mask */
+	for_each_set_bit(out_ind,
+			 indio_dev->active_scan_mask,
+			 indio_dev->masklength) {
+		/* Handle timestamp separately */
+		if (out_ind == DLN2_ADC_MAX_CHANNELS)
+			break;
+		for (++in_ind; in_ind != out_ind; ++in_ind)
+			in_loc += 2;
+		dln2_adc_add_demux(dln2, in_loc, out_loc, 2);
+		out_loc += 2;
+		in_loc += 2;
+	}
+
+	if (indio_dev->scan_timestamp) {
+		size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1;
+
+		dln2->ts_pad_offset = out_loc;
+		dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc;
+	} else {
+		dln2->ts_pad_offset = 0;
+		dln2->ts_pad_length = 0;
+	}
+}
+
+static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
+{
+	int ret;
+	u8 port = dln2->port;
+	u8 count;
+	int olen = sizeof(count);
+
+	ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
+			    &port, sizeof(port), &count, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+	if (olen < sizeof(count))
+		return -EPROTO;
+
+	return count;
+}
+
+static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
+{
+	int ret;
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = DLN2_ADC_DATA_BITS,
+	};
+
+	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
+			       &port_chan, sizeof(port_chan));
+	if (ret < 0)
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+
+	return ret;
+}
+
+static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
+				     int channel, bool enable)
+{
+	int ret;
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = channel,
+	};
+	u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
+
+	ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
+	if (ret < 0)
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+
+	return ret;
+}
+
+static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable,
+				     u16 *conflict_out)
+{
+	int ret;
+	u8 port = dln2->port;
+	__le16 conflict;
+	int olen = sizeof(conflict);
+	u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
+
+	if (conflict_out)
+		*conflict_out = 0;
+
+	ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
+			    &conflict, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
+			__func__, (int)enable);
+		if (conflict_out && enable && olen >= sizeof(conflict))
+			*conflict_out = le16_to_cpu(conflict);
+		return ret;
+	}
+	if (enable && olen < sizeof(conflict))
+		return -EPROTO;
+
+	return ret;
+}
+
+static int dln2_adc_set_chan_period(struct dln2_adc *dln2,
+	unsigned int channel, unsigned int period)
+{
+	int ret;
+	struct {
+		struct dln2_adc_port_chan port_chan;
+		__u8 type;
+		__le16 period;
+		__le16 low;
+		__le16 high;
+	} __packed set_cfg = {
+		.port_chan.port = dln2->port,
+		.port_chan.chan = channel,
+		.type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
+		.period = cpu_to_le16(period)
+	};
+
+	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
+			       &set_cfg, sizeof(set_cfg));
+	if (ret < 0)
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+
+	return ret;
+}
+
+static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
+{
+	int ret, i;
+	struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
+	u16 conflict;
+	__le16 value;
+	int olen = sizeof(value);
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = channel,
+	};
+
+	ret = iio_device_claim_direct_mode(indio_dev);
+	if (ret < 0)
+		return ret;
+
+	ret = dln2_adc_set_chan_enabled(dln2, channel, true);
+	if (ret < 0)
+		goto release_direct;
+
+	ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
+	if (ret < 0) {
+		if (conflict) {
+			dev_err(&dln2->pdev->dev,
+				"ADC pins conflict with mask %04X\n",
+				(int)conflict);
+			ret = -EBUSY;
+		}
+		goto disable_chan;
+	}
+
+	/*
+	 * Call GET_VAL twice due to initial zero-return immediately after
+	 * enabling channel.
+	 */
+	for (i = 0; i < 2; ++i) {
+		ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
+				    &port_chan, sizeof(port_chan),
+				    &value, &olen);
+		if (ret < 0) {
+			dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+			goto disable_port;
+		}
+		if (olen < sizeof(value)) {
+			ret = -EPROTO;
+			goto disable_port;
+		}
+	}
+
+	ret = le16_to_cpu(value);
+
+disable_port:
+	dln2_adc_set_port_enabled(dln2, false, NULL);
+disable_chan:
+	dln2_adc_set_chan_enabled(dln2, channel, false);
+release_direct:
+	iio_device_release_direct_mode(indio_dev);
+
+	return ret;
+}
+
+static int dln2_adc_read_all(struct dln2_adc *dln2,
+			     struct dln2_adc_get_all_vals *get_all_vals)
+{
+	int ret;
+	__u8 port = dln2->port;
+	int olen = sizeof(*get_all_vals);
+
+	ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
+			    &port, sizeof(port), get_all_vals, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+	if (olen < sizeof(*get_all_vals))
+		return -EPROTO;
+
+	return ret;
+}
+
+static int dln2_adc_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int *val,
+			     int *val2,
+			     long mask)
+{
+	int ret;
+	unsigned int microhertz;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&dln2->mutex);
+		ret = dln2_adc_read(dln2, chan->channel);
+		mutex_unlock(&dln2->mutex);
+
+		if (ret < 0)
+			return ret;
+
+		*val = ret;
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		/*
+		 * Voltage reference is fixed at 3.3v
+		 *  3.3 / (1 << 10) * 1000000000
+		 */
+		*val = 0;
+		*val2 = 3222656;
+		return IIO_VAL_INT_PLUS_NANO;
+
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (dln2->sample_period) {
+			microhertz = 1000000000 / dln2->sample_period;
+			*val = microhertz / 1000000;
+			*val2 = microhertz % 1000000;
+		} else {
+			*val = 0;
+			*val2 = 0;
+		}
+
+		return IIO_VAL_INT_PLUS_MICRO;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int dln2_adc_write_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int val,
+			      int val2,
+			      long mask)
+{
+	int ret;
+	unsigned int microhertz;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		microhertz = 1000000 * val + val2;
+
+		mutex_lock(&dln2->mutex);
+
+		dln2->sample_period =
+			microhertz ? 1000000000 / microhertz : UINT_MAX;
+		if (dln2->sample_period > 65535) {
+			dln2->sample_period = 65535;
+			dev_warn(&dln2->pdev->dev,
+				 "clamping period to 65535ms\n");
+		}
+
+		/*
+		 * The first requested channel is arbitrated as a shared
+		 * trigger source, so only one event is registered with the
+		 * DLN. The event handler will then read all enabled channel
+		 * values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
+		 * synchronization between ADC readings.
+		 */
+		if (dln2->trigger_chan != -1)
+			ret = dln2_adc_set_chan_period(dln2,
+				dln2->trigger_chan, dln2->sample_period);
+		else
+			ret = 0;
+
+		mutex_unlock(&dln2->mutex);
+
+		return ret;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int dln2_update_scan_mode(struct iio_dev *indio_dev,
+				 const unsigned long *scan_mask)
+{
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+	int chan_count = indio_dev->num_channels - 1;
+	int ret, i, j;
+
+	mutex_lock(&dln2->mutex);
+
+	for (i = 0; i < chan_count; ++i) {
+		ret = dln2_adc_set_chan_enabled(dln2, i,
+						test_bit(i, scan_mask));
+		if (ret < 0) {
+			for (j = 0; j < i; ++j)
+				dln2_adc_set_chan_enabled(dln2, j, false);
+			mutex_unlock(&dln2->mutex);
+			dev_err(&dln2->pdev->dev,
+				"Unable to enable ADC channel %d\n", i);
+			return -EBUSY;
+		}
+	}
+
+	dln2_adc_update_demux(dln2);
+
+	mutex_unlock(&dln2->mutex);
+
+	return 0;
+}
+
+#define DLN2_ADC_CHAN(lval, idx) {					\
+	lval.type = IIO_VOLTAGE;					\
+	lval.channel = idx;						\
+	lval.indexed = 1;						\
+	lval.info_mask_separate = BIT(IIO_CHAN_INFO_RAW);		\
+	lval.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |	\
+				       BIT(IIO_CHAN_INFO_SAMP_FREQ);	\
+	lval.scan_index = idx;						\
+	lval.scan_type.sign = 'u';					\
+	lval.scan_type.realbits = DLN2_ADC_DATA_BITS;			\
+	lval.scan_type.storagebits = 16;				\
+	lval.scan_type.endianness = IIO_LE;				\
+}
+
+/* Assignment version of IIO_CHAN_SOFT_TIMESTAMP */
+#define IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(lval, _si) {	\
+	lval.type = IIO_TIMESTAMP;			\
+	lval.channel = -1;				\
+	lval.scan_index = _si;				\
+	lval.scan_type.sign = 's';			\
+	lval.scan_type.realbits = 64;			\
+	lval.scan_type.storagebits = 64;		\
+}
+
+static const struct iio_info dln2_adc_info = {
+	.read_raw = dln2_adc_read_raw,
+	.write_raw = dln2_adc_write_raw,
+	.update_scan_mode = dln2_update_scan_mode,
+	.driver_module = THIS_MODULE,
+};
+
+static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct {
+		__le16 values[DLN2_ADC_MAX_CHANNELS];
+		int64_t timestamp_space;
+	} data;
+	struct dln2_adc_get_all_vals dev_data;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+	const struct dln2_adc_demux_table *t;
+	int ret, i;
+
+	mutex_lock(&dln2->mutex);
+	ret = dln2_adc_read_all(dln2, &dev_data);
+	mutex_unlock(&dln2->mutex);
+	if (ret < 0)
+		goto done;
+
+	/* Demux operation */
+	for (i = 0; i < dln2->demux_count; ++i) {
+		t = &dln2->demux[i];
+		memcpy((void *)data.values + t->to,
+		       (void *)dev_data.values + t->from, t->length);
+	}
+
+	/* Zero padding space between values and timestamp */
+	if (dln2->ts_pad_length)
+		memset((void *)data.values + dln2->ts_pad_offset,
+		       0, dln2->ts_pad_length);
+
+	iio_push_to_buffers_with_timestamp(indio_dev, &data,
+					   iio_get_time_ns(indio_dev));
+
+done:
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+	u16 conflict;
+	unsigned int trigger_chan;
+
+	mutex_lock(&dln2->mutex);
+
+	/* Enable ADC */
+	ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
+	if (ret < 0) {
+		mutex_unlock(&dln2->mutex);
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		if (conflict) {
+			dev_err(&dln2->pdev->dev,
+				"ADC pins conflict with mask %04X\n",
+				(int)conflict);
+			ret = -EBUSY;
+		}
+		return ret;
+	}
+
+	/* Assign trigger channel based on first enabled channel */
+	trigger_chan = find_first_bit(indio_dev->active_scan_mask,
+				      indio_dev->masklength);
+	if (trigger_chan < DLN2_ADC_MAX_CHANNELS) {
+		dln2->trigger_chan = trigger_chan;
+		ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan,
+					       dln2->sample_period);
+		mutex_unlock(&dln2->mutex);
+		if (ret < 0) {
+			dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+			return ret;
+		}
+	} else {
+		dln2->trigger_chan = -1;
+		mutex_unlock(&dln2->mutex);
+	}
+
+	return iio_triggered_buffer_postenable(indio_dev);
+}
+
+static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	mutex_lock(&dln2->mutex);
+
+	/* Disable trigger channel */
+	if (dln2->trigger_chan != -1) {
+		dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0);
+		dln2->trigger_chan = -1;
+	}
+
+	/* Disable ADC */
+	ret = dln2_adc_set_port_enabled(dln2, false, NULL);
+
+	mutex_unlock(&dln2->mutex);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+
+	return iio_triggered_buffer_predisable(indio_dev);
+}
+
+static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
+	.postenable = dln2_adc_triggered_buffer_postenable,
+	.predisable = dln2_adc_triggered_buffer_predisable,
+};
+
+static void dln2_adc_event(struct platform_device *pdev, u16 echo,
+			   const void *data, int len)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	/* Called via URB completion handler */
+	iio_trigger_poll(dln2->trig);
+}
+
+static const struct iio_trigger_ops dln2_adc_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+static int dln2_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct dln2_adc *dln2;
+	struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	struct iio_dev *indio_dev;
+	int i, ret, chans;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2));
+	if (!indio_dev) {
+		dev_err(dev, "failed allocating iio device\n");
+		return -ENOMEM;
+	}
+
+	dln2 = iio_priv(indio_dev);
+	dln2->pdev = pdev;
+	dln2->port = pdata->port;
+	dln2->trigger_chan = -1;
+	mutex_init(&dln2->mutex);
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	ret = dln2_adc_set_port_resolution(dln2);
+	if (ret < 0) {
+		dev_err(dev, "failed to set ADC resolution to 10 bits\n");
+		return ret;
+	}
+
+	chans = dln2_adc_get_chan_count(dln2);
+	if (chans < 0) {
+		dev_err(dev, "failed to get channel count: %d\n", chans);
+		return chans;
+	}
+	if (chans > DLN2_ADC_MAX_CHANNELS) {
+		chans = DLN2_ADC_MAX_CHANNELS;
+		dev_warn(dev, "clamping channels to %d\n",
+			 DLN2_ADC_MAX_CHANNELS);
+	}
+
+	for (i = 0; i < chans; ++i)
+		DLN2_ADC_CHAN(dln2->iio_channels[i], i)
+	IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(dln2->iio_channels[i], i);
+
+	indio_dev->name = DLN2_ADC_MOD_NAME;
+	indio_dev->dev.parent = dev;
+	indio_dev->info = &dln2_adc_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = dln2->iio_channels;
+	indio_dev->num_channels = chans + 1;
+	indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
+
+	dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
+					    indio_dev->name, indio_dev->id);
+	if (!dln2->trig) {
+		dev_err(dev, "failed to allocate trigger\n");
+		return -ENOMEM;
+	}
+	dln2->trig->ops = &dln2_adc_trigger_ops;
+	iio_trigger_set_drvdata(dln2->trig, dln2);
+	devm_iio_trigger_register(dev, dln2->trig);
+	iio_trigger_set_immutable(indio_dev, dln2->trig);
+
+	ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+					      dln2_adc_trigger_h,
+					      &dln2_adc_buffer_setup_ops);
+	if (ret) {
+		dev_err(dev, "failed to allocate triggered buffer: %d\n", ret);
+		return ret;
+	}
+
+	ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
+				     dln2_adc_event);
+	if (ret) {
+		dev_err(dev, "failed to setup DLN2 periodic event: %d\n", ret);
+		return ret;
+	}
+
+	ret = devm_iio_device_register(dev, indio_dev);
+	if (ret) {
+		dev_err(dev, "failed to register iio device: %d\n", ret);
+		goto unregister_event;
+	}
+
+	return ret;
+
+unregister_event:
+	dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
+
+	return ret;
+}
+
+static int dln2_adc_remove(struct platform_device *pdev)
+{
+	dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
+	return 0;
+}
+
+static struct platform_driver dln2_adc_driver = {
+	.driver.name	= DLN2_ADC_MOD_NAME,
+	.probe		= dln2_adc_probe,
+	.remove		= dln2_adc_remove,
+};
+
+module_platform_driver(dln2_adc_driver);
+
+MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
+MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dln2-adc");
-- 
1.9.1

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

* [PATCH v5 2/3] mfd: dln2: Add cell for initializing DLN2 ADC
       [not found] <b5ede0cc2cb36695b86edf44c0dddbc4736a6420>
                   ` (3 preceding siblings ...)
  2017-07-05 21:12 ` [PATCH v5 1/3] " Jack Andersen
@ 2017-07-05 21:12 ` Jack Andersen
  2017-07-06  7:10   ` Lee Jones
  2017-07-05 21:12 ` [PATCH v5 3/3] mfd: dln2: Send restart command if HW init fails Jack Andersen
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Jack Andersen @ 2017-07-05 21:12 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: Jack Andersen, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Lee Jones

Signed-off-by: Jack Andersen <jackoalan@gmail.com>
---
 drivers/mfd/dln2.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
index 704e189..a22ab8c 100644
--- a/drivers/mfd/dln2.c
+++ b/drivers/mfd/dln2.c
@@ -53,6 +53,7 @@ enum dln2_handle {
 	DLN2_HANDLE_GPIO,
 	DLN2_HANDLE_I2C,
 	DLN2_HANDLE_SPI,
+	DLN2_HANDLE_ADC,
 	DLN2_HANDLES
 };
 
@@ -663,6 +664,12 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
 	.port = 0,
 };
 
+/* Only one ADC port supported */
+static struct dln2_platform_data dln2_pdata_adc = {
+	.handle = DLN2_HANDLE_ADC,
+	.port = 0,
+};
+
 static const struct mfd_cell dln2_devs[] = {
 	{
 		.name = "dln2-gpio",
@@ -679,6 +686,11 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
 		.platform_data = &dln2_pdata_spi,
 		.pdata_size = sizeof(struct dln2_platform_data),
 	},
+	{
+		.name = "dln2-adc",
+		.platform_data = &dln2_pdata_adc,
+		.pdata_size = sizeof(struct dln2_platform_data),
+	},
 };
 
 static void dln2_stop(struct dln2_dev *dln2)
-- 
1.9.1

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

* [PATCH v5 3/3] mfd: dln2: Send restart command if HW init fails
       [not found] <b5ede0cc2cb36695b86edf44c0dddbc4736a6420>
                   ` (4 preceding siblings ...)
  2017-07-05 21:12 ` [PATCH v5 2/3] mfd: dln2: Add cell for initializing " Jack Andersen
@ 2017-07-05 21:12 ` Jack Andersen
  2017-07-05 21:23   ` Jack Andersen
  2017-07-06 22:39 ` [PATCH v6 1/3] iio: adc: Add support for DLN2 ADC Jack Andersen
  2017-07-06 22:39 ` [PATCH v6 2/3] mfd: dln2: Add cell for initializing " Jack Andersen
  7 siblings, 1 reply; 23+ messages in thread
From: Jack Andersen @ 2017-07-05 21:12 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: Jack Andersen, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Lee Jones

There's an issue with reinitializing DLN2 hardware if ADC channel event
configurations aren't reset before suspending the driver. A physical reconnect
of the device solves the problem, but restarting the machine without a power
cycle leaves the device state inconsistent.

This patch transmits a restart command in the event of a failed init.
To the kernel's perspective, this is the rough equivalent of a disconnect and
reconnect, so another probe occurs shortly afterwards.

Signed-off-by: Jack Andersen <jackoalan@gmail.com>
---
 drivers/mfd/dln2.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
index a22ab8c..1eb0056 100644
--- a/drivers/mfd/dln2.c
+++ b/drivers/mfd/dln2.c
@@ -40,6 +40,7 @@ struct dln2_response {
 #define DLN2_GENERIC_CMD(cmd)		DLN2_CMD(cmd, DLN2_GENERIC_MODULE_ID)
 #define CMD_GET_DEVICE_VER		DLN2_GENERIC_CMD(0x30)
 #define CMD_GET_DEVICE_SN		DLN2_GENERIC_CMD(0x31)
+#define CMD_RESTART			DLN2_GENERIC_CMD(0x43)
 
 #define DLN2_HW_ID			0x200
 #define DLN2_USB_TIMEOUT		200	/* in ms */
@@ -531,6 +532,12 @@ int dln2_transfer(struct platform_device *pdev, u16 cmd,
 }
 EXPORT_SYMBOL(dln2_transfer);
 
+static int dln2_restart(struct dln2_dev *dln2)
+{
+	return _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_RESTART,
+			      NULL, 0, NULL, NULL);
+}
+
 static int dln2_check_hw(struct dln2_dev *dln2)
 {
 	int ret;
@@ -782,7 +789,9 @@ static int dln2_probe(struct usb_interface *interface,
 
 	ret = dln2_hw_init(dln2);
 	if (ret < 0) {
-		dev_err(dev, "failed to initialize hardware\n");
+		dev_err(dev,
+			"failed to initialize hardware; attempting restart\n");
+		dln2_restart(dln2);
 		goto out_stop_rx;
 	}
 
-- 
1.9.1

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

* Re: [PATCH v5 3/3] mfd: dln2: Send restart command if HW init fails
  2017-07-05 21:12 ` [PATCH v5 3/3] mfd: dln2: Send restart command if HW init fails Jack Andersen
@ 2017-07-05 21:23   ` Jack Andersen
  2017-07-06  7:09     ` Lee Jones
  0 siblings, 1 reply; 23+ messages in thread
From: Jack Andersen @ 2017-07-05 21:23 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: Jack Andersen, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Lee Jones

I have mixed feelings about making this patch. The device appears to
lose its ability to send command responses when the driver
unexpectedly goes offline (probably overflowing an internal send
buffer).

The restart command is a send-only way to recover the device.
What concerns me is if there's another issue causing init to fail and
a restart doesn't fix, this will trigger an endless probe loop. I'm
not sure if there's a better way to discern this condition.

On 5 July 2017 at 11:12, Jack Andersen <jackoalan@gmail.com> wrote:
> There's an issue with reinitializing DLN2 hardware if ADC channel event
> configurations aren't reset before suspending the driver. A physical reconnect
> of the device solves the problem, but restarting the machine without a power
> cycle leaves the device state inconsistent.
>
> This patch transmits a restart command in the event of a failed init.
> To the kernel's perspective, this is the rough equivalent of a disconnect and
> reconnect, so another probe occurs shortly afterwards.
>
> Signed-off-by: Jack Andersen <jackoalan@gmail.com>
> ---
>  drivers/mfd/dln2.c | 11 ++++++++++-
>  1 file changed, 10 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
> index a22ab8c..1eb0056 100644
> --- a/drivers/mfd/dln2.c
> +++ b/drivers/mfd/dln2.c
> @@ -40,6 +40,7 @@ struct dln2_response {
>  #define DLN2_GENERIC_CMD(cmd)          DLN2_CMD(cmd, DLN2_GENERIC_MODULE_ID)
>  #define CMD_GET_DEVICE_VER             DLN2_GENERIC_CMD(0x30)
>  #define CMD_GET_DEVICE_SN              DLN2_GENERIC_CMD(0x31)
> +#define CMD_RESTART                    DLN2_GENERIC_CMD(0x43)
>
>  #define DLN2_HW_ID                     0x200
>  #define DLN2_USB_TIMEOUT               200     /* in ms */
> @@ -531,6 +532,12 @@ int dln2_transfer(struct platform_device *pdev, u16 cmd,
>  }
>  EXPORT_SYMBOL(dln2_transfer);
>
> +static int dln2_restart(struct dln2_dev *dln2)
> +{
> +       return _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_RESTART,
> +                             NULL, 0, NULL, NULL);
> +}
> +
>  static int dln2_check_hw(struct dln2_dev *dln2)
>  {
>         int ret;
> @@ -782,7 +789,9 @@ static int dln2_probe(struct usb_interface *interface,
>
>         ret = dln2_hw_init(dln2);
>         if (ret < 0) {
> -               dev_err(dev, "failed to initialize hardware\n");
> +               dev_err(dev,
> +                       "failed to initialize hardware; attempting restart\n");
> +               dln2_restart(dln2);
>                 goto out_stop_rx;
>         }
>
> --
> 1.9.1
>

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

* Re: [PATCH v5 3/3] mfd: dln2: Send restart command if HW init fails
  2017-07-05 21:23   ` Jack Andersen
@ 2017-07-06  7:09     ` Lee Jones
  2017-07-06 21:01       ` Jack Andersen
  0 siblings, 1 reply; 23+ messages in thread
From: Lee Jones @ 2017-07-06  7:09 UTC (permalink / raw)
  To: Jack Andersen
  Cc: linux-iio, linux-kernel, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler

On Wed, 05 Jul 2017, Jack Andersen wrote:

> I have mixed feelings about making this patch. The device appears to
> lose its ability to send command responses when the driver
> unexpectedly goes offline (probably overflowing an internal send
> buffer).
> 
> The restart command is a send-only way to recover the device.
> What concerns me is if there's another issue causing init to fail and
> a restart doesn't fix, this will trigger an endless probe loop. I'm
> not sure if there's a better way to discern this condition.

Why does dln2_restart() cause an unbind/bind?

> On 5 July 2017 at 11:12, Jack Andersen <jackoalan@gmail.com> wrote:
> > There's an issue with reinitializing DLN2 hardware if ADC channel event
> > configurations aren't reset before suspending the driver. A physical reconnect
> > of the device solves the problem, but restarting the machine without a power
> > cycle leaves the device state inconsistent.
> >
> > This patch transmits a restart command in the event of a failed init.
> > To the kernel's perspective, this is the rough equivalent of a disconnect and
> > reconnect, so another probe occurs shortly afterwards.
> >
> > Signed-off-by: Jack Andersen <jackoalan@gmail.com>
> > ---
> >  drivers/mfd/dln2.c | 11 ++++++++++-
> >  1 file changed, 10 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
> > index a22ab8c..1eb0056 100644
> > --- a/drivers/mfd/dln2.c
> > +++ b/drivers/mfd/dln2.c
> > @@ -40,6 +40,7 @@ struct dln2_response {
> >  #define DLN2_GENERIC_CMD(cmd)          DLN2_CMD(cmd, DLN2_GENERIC_MODULE_ID)
> >  #define CMD_GET_DEVICE_VER             DLN2_GENERIC_CMD(0x30)
> >  #define CMD_GET_DEVICE_SN              DLN2_GENERIC_CMD(0x31)
> > +#define CMD_RESTART                    DLN2_GENERIC_CMD(0x43)
> >
> >  #define DLN2_HW_ID                     0x200
> >  #define DLN2_USB_TIMEOUT               200     /* in ms */
> > @@ -531,6 +532,12 @@ int dln2_transfer(struct platform_device *pdev, u16 cmd,
> >  }
> >  EXPORT_SYMBOL(dln2_transfer);
> >
> > +static int dln2_restart(struct dln2_dev *dln2)
> > +{
> > +       return _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_RESTART,
> > +                             NULL, 0, NULL, NULL);
> > +}
> > +
> >  static int dln2_check_hw(struct dln2_dev *dln2)
> >  {
> >         int ret;
> > @@ -782,7 +789,9 @@ static int dln2_probe(struct usb_interface *interface,
> >
> >         ret = dln2_hw_init(dln2);
> >         if (ret < 0) {
> > -               dev_err(dev, "failed to initialize hardware\n");
> > +               dev_err(dev,
> > +                       "failed to initialize hardware; attempting restart\n");
> > +               dln2_restart(dln2);
> >                 goto out_stop_rx;
> >         }
> >
> >

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v5 2/3] mfd: dln2: Add cell for initializing DLN2 ADC
  2017-07-05 21:12 ` [PATCH v5 2/3] mfd: dln2: Add cell for initializing " Jack Andersen
@ 2017-07-06  7:10   ` Lee Jones
  0 siblings, 0 replies; 23+ messages in thread
From: Lee Jones @ 2017-07-06  7:10 UTC (permalink / raw)
  To: Jack Andersen
  Cc: linux-iio, linux-kernel, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler

On Wed, 05 Jul 2017, Jack Andersen wrote:

> Signed-off-by: Jack Andersen <jackoalan@gmail.com>
> ---
>  drivers/mfd/dln2.c | 12 ++++++++++++
>  1 file changed, 12 insertions(+)

Patch looks fine, but you need a commit message.

Once you have one, you may apply my:

For my own reference:
  Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>

> diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
> index 704e189..a22ab8c 100644
> --- a/drivers/mfd/dln2.c
> +++ b/drivers/mfd/dln2.c
> @@ -53,6 +53,7 @@ enum dln2_handle {
>  	DLN2_HANDLE_GPIO,
>  	DLN2_HANDLE_I2C,
>  	DLN2_HANDLE_SPI,
> +	DLN2_HANDLE_ADC,
>  	DLN2_HANDLES
>  };
>  
> @@ -663,6 +664,12 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
>  	.port = 0,
>  };
>  
> +/* Only one ADC port supported */
> +static struct dln2_platform_data dln2_pdata_adc = {
> +	.handle = DLN2_HANDLE_ADC,
> +	.port = 0,
> +};
> +
>  static const struct mfd_cell dln2_devs[] = {
>  	{
>  		.name = "dln2-gpio",
> @@ -679,6 +686,11 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
>  		.platform_data = &dln2_pdata_spi,
>  		.pdata_size = sizeof(struct dln2_platform_data),
>  	},
> +	{
> +		.name = "dln2-adc",
> +		.platform_data = &dln2_pdata_adc,
> +		.pdata_size = sizeof(struct dln2_platform_data),
> +	},
>  };
>  
>  static void dln2_stop(struct dln2_dev *dln2)

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v5 1/3] iio: adc: Add support for DLN2 ADC
  2017-07-05 21:12 ` [PATCH v5 1/3] " Jack Andersen
@ 2017-07-06 11:37   ` Jonathan Cameron
  0 siblings, 0 replies; 23+ messages in thread
From: Jonathan Cameron @ 2017-07-06 11:37 UTC (permalink / raw)
  To: Jack Andersen, linux-iio, linux-kernel
  Cc: Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Lee Jones



On 5 July 2017 22:12:46 BST, Jack Andersen <jackoalan@gmail.com> wrote:
>This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
>ADC is the fourth and final component of the DLN2 for the kernel.
>
>Signed-off-by: Jack Andersen <jackoalan@gmail.com>

The remove order differs from opposite of probe due to use of devm after an non managed call.

Result doesn't pass the obviously correct test of the lazy maintainer :)
>---
>Changes in v2:
>- Address Peter's remarks; bringing consistency to C99 syntax, dev_dbg
>usage,
>  and error handling.
>- Remove probe/remove logging.
>- Utilize stack-allocated buffer for pushing into IIO.
>- Add timestamp channel to IIO spec.
>
>Suggested-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
>---
>Changes in v3:
>- Remove lazy channel enabling; integrating with update_channel_mode
>instead.
>- Implement memcpy demux table similar to subsystem core.
>- Use iio_triggered_buffer_setup to create buffer.
>
>Suggested-by: Jonathan Cameron <jic23@kernel.org>
>---
>Changes in v4:
>- Remove unrelated, erronous change to lsgpio.c
>---
>Changes in v5:
>- Use of devm functions wherever possible.
>- Use iio_device_claim_direct_mode.
>- Variable declaration more consistent with C style.
>- MFD modifications in separate patch.
>Suggested-by: Jonathan Cameron <jic23@kernel.org>
>---
> drivers/iio/adc/Kconfig    |   9 +
> drivers/iio/adc/Makefile   |   1 +
>drivers/iio/adc/dln2-adc.c | 719
>+++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 729 insertions(+)
> create mode 100644 drivers/iio/adc/dln2-adc.c
>
>diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>index 614fa41..196d2ce 100644
>--- a/drivers/iio/adc/Kconfig
>+++ b/drivers/iio/adc/Kconfig
>@@ -239,6 +239,15 @@ config DA9150_GPADC
>	  To compile this driver as a module, choose M here: the module will
>be
> 	  called berlin2-adc.
> 
>+config DLN2_ADC
>+	tristate "Diolan DLN-2 ADC driver support"
>+	depends on MFD_DLN2
>+	help
>+	  Say yes here to build support for Diolan DLN-2 ADC.
>+
>+	  This driver can also be built as a module. If so, the module will
>be
>+	  called adc_dln2.
>+
> config ENVELOPE_DETECTOR
> 	tristate "Envelope detector using a DAC and a comparator"
> 	depends on OF
>diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>index b546736a..7ea0bf3 100644
>--- a/drivers/iio/adc/Makefile
>+++ b/drivers/iio/adc/Makefile
>@@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
> obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
> obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
> obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>+obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
> obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
> obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
> obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
>diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
>new file mode 100644
>index 0000000..649bbef9b
>--- /dev/null
>+++ b/drivers/iio/adc/dln2-adc.c
>@@ -0,0 +1,719 @@
>+/*
>+ * Driver for the Diolan DLN-2 USB-ADC adapter
>+ *
>+ * Copyright (c) 2017 Jack Andersen
>+ *
>+ * 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, version 2.
>+ */
>+
>+#include <linux/kernel.h>
>+#include <linux/module.h>
>+#include <linux/types.h>
>+#include <linux/platform_device.h>
>+#include <linux/mfd/dln2.h>
>+
>+#include <linux/iio/iio.h>
>+#include <linux/iio/sysfs.h>
>+#include <linux/iio/trigger.h>
>+#include <linux/iio/trigger_consumer.h>
>+#include <linux/iio/triggered_buffer.h>
>+#include <linux/iio/buffer.h>
>+#include <linux/iio/kfifo_buf.h>
>+
>+#define DLN2_ADC_MOD_NAME "dln2-adc"
>+
>+#define DLN2_ADC_ID             0x06
>+
>+#define DLN2_ADC_GET_CHANNEL_COUNT	DLN2_CMD(0x01, DLN2_ADC_ID)
>+#define DLN2_ADC_ENABLE			DLN2_CMD(0x02, DLN2_ADC_ID)
>+#define DLN2_ADC_DISABLE		DLN2_CMD(0x03, DLN2_ADC_ID)
>+#define DLN2_ADC_CHANNEL_ENABLE		DLN2_CMD(0x05, DLN2_ADC_ID)
>+#define DLN2_ADC_CHANNEL_DISABLE	DLN2_CMD(0x06, DLN2_ADC_ID)
>+#define DLN2_ADC_SET_RESOLUTION		DLN2_CMD(0x08, DLN2_ADC_ID)
>+#define DLN2_ADC_CHANNEL_GET_VAL	DLN2_CMD(0x0A, DLN2_ADC_ID)
>+#define DLN2_ADC_CHANNEL_GET_ALL_VAL	DLN2_CMD(0x0B, DLN2_ADC_ID)
>+#define DLN2_ADC_CHANNEL_SET_CFG	DLN2_CMD(0x0C, DLN2_ADC_ID)
>+#define DLN2_ADC_CHANNEL_GET_CFG	DLN2_CMD(0x0D, DLN2_ADC_ID)
>+#define DLN2_ADC_CONDITION_MET_EV	DLN2_CMD(0x10, DLN2_ADC_ID)
>+
>+#define DLN2_ADC_EVENT_NONE		0
>+#define DLN2_ADC_EVENT_BELOW		1
>+#define DLN2_ADC_EVENT_LEVEL_ABOVE	2
>+#define DLN2_ADC_EVENT_OUTSIDE		3
>+#define DLN2_ADC_EVENT_INSIDE		4
>+#define DLN2_ADC_EVENT_ALWAYS		5
>+
>+#define DLN2_ADC_MAX_CHANNELS 8
>+#define DLN2_ADC_DATA_BITS 10
>+
>+/*
>+ * Plays similar role to iio_demux_table in subsystem core; except
>allocated
>+ * in a fixed 8-element array.
>+ */
>+struct dln2_adc_demux_table {
>+	unsigned int from;
>+	unsigned int to;
>+	unsigned int length;
>+};
>+
>+struct dln2_adc {
>+	struct platform_device *pdev;
>+	struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1];
>+	int port, trigger_chan;
>+	struct iio_trigger *trig;
>+	struct mutex mutex;
>+	/* Cached sample period in milliseconds */
>+	unsigned int sample_period;
>+	/* Demux table */
>+	unsigned int demux_count;
>+	struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS];
>+	/* Precomputed timestamp padding offset and length */
>+	unsigned int ts_pad_offset, ts_pad_length;
>+};
>+
>+struct dln2_adc_port_chan {
>+	u8 port;
>+	u8 chan;
>+};
>+
>+struct dln2_adc_get_all_vals {
>+	__le16 channel_mask;
>+	__le16 values[DLN2_ADC_MAX_CHANNELS];
>+};
>+
>+static void dln2_adc_add_demux(struct dln2_adc *dln2,
>+	unsigned int in_loc, unsigned int out_loc,
>+	unsigned int length)
>+{
>+	struct dln2_adc_demux_table *p = dln2->demux_count ?
>+		&dln2->demux[dln2->demux_count - 1] : NULL;
>+
>+	if (p && p->from + p->length == in_loc &&
>+		p->to + p->length == out_loc) {
>+		p->length += length;
>+	} else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) {
>+		p = &dln2->demux[dln2->demux_count++];
>+		p->from = in_loc;
>+		p->to = out_loc;
>+		p->length = length;
>+	}
>+}
>+
>+static void dln2_adc_update_demux(struct dln2_adc *dln2)
>+{
>+	int in_ind = -1, out_ind;
>+	unsigned int in_loc = 0, out_loc = 0;
>+	struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
>+
>+	/* Clear out any old demux */
>+	dln2->demux_count = 0;
>+
>+	/* Optimize all 8-channels case */
>+	if (indio_dev->masklength &&
>+	    (*indio_dev->active_scan_mask & 0xff) == 0xff) {
>+		dln2_adc_add_demux(dln2, 0, 0, 16);
>+		dln2->ts_pad_offset = 0;
>+		dln2->ts_pad_length = 0;
>+		return;
>+	}
>+
>+	/* Build demux table from fixed 8-channels to active_scan_mask */
>+	for_each_set_bit(out_ind,
>+			 indio_dev->active_scan_mask,
>+			 indio_dev->masklength) {
>+		/* Handle timestamp separately */
>+		if (out_ind == DLN2_ADC_MAX_CHANNELS)
>+			break;
>+		for (++in_ind; in_ind != out_ind; ++in_ind)
>+			in_loc += 2;
>+		dln2_adc_add_demux(dln2, in_loc, out_loc, 2);
>+		out_loc += 2;
>+		in_loc += 2;
>+	}
>+
>+	if (indio_dev->scan_timestamp) {
>+		size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1;
>+
>+		dln2->ts_pad_offset = out_loc;
>+		dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc;
>+	} else {
>+		dln2->ts_pad_offset = 0;
>+		dln2->ts_pad_length = 0;
>+	}
>+}
>+
>+static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
>+{
>+	int ret;
>+	u8 port = dln2->port;
>+	u8 count;
>+	int olen = sizeof(count);
>+
>+	ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
>+			    &port, sizeof(port), &count, &olen);
>+	if (ret < 0) {
>+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>+		return ret;
>+	}
>+	if (olen < sizeof(count))
>+		return -EPROTO;
>+
>+	return count;
>+}
>+
>+static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
>+{
>+	int ret;
>+	struct dln2_adc_port_chan port_chan = {
>+		.port = dln2->port,
>+		.chan = DLN2_ADC_DATA_BITS,
>+	};
>+
>+	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
>+			       &port_chan, sizeof(port_chan));
>+	if (ret < 0)
>+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>+
>+	return ret;
>+}
>+
>+static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
>+				     int channel, bool enable)
>+{
>+	int ret;
>+	struct dln2_adc_port_chan port_chan = {
>+		.port = dln2->port,
>+		.chan = channel,
>+	};
>+	u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE :
>DLN2_ADC_CHANNEL_DISABLE;
>+
>+	ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan,
>sizeof(port_chan));
>+	if (ret < 0)
>+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>+
>+	return ret;
>+}
>+
>+static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool
>enable,
>+				     u16 *conflict_out)
>+{
>+	int ret;
>+	u8 port = dln2->port;
>+	__le16 conflict;
>+	int olen = sizeof(conflict);
>+	u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
>+
>+	if (conflict_out)
>+		*conflict_out = 0;
>+
>+	ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
>+			    &conflict, &olen);
>+	if (ret < 0) {
>+		dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
>+			__func__, (int)enable);
>+		if (conflict_out && enable && olen >= sizeof(conflict))
>+			*conflict_out = le16_to_cpu(conflict);
>+		return ret;
>+	}
>+	if (enable && olen < sizeof(conflict))
>+		return -EPROTO;
>+
>+	return ret;
>+}
>+
>+static int dln2_adc_set_chan_period(struct dln2_adc *dln2,
>+	unsigned int channel, unsigned int period)
>+{
>+	int ret;
>+	struct {
>+		struct dln2_adc_port_chan port_chan;
>+		__u8 type;
>+		__le16 period;
>+		__le16 low;
>+		__le16 high;
>+	} __packed set_cfg = {
>+		.port_chan.port = dln2->port,
>+		.port_chan.chan = channel,
>+		.type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
>+		.period = cpu_to_le16(period)
>+	};
>+
>+	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
>+			       &set_cfg, sizeof(set_cfg));
>+	if (ret < 0)
>+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>+
>+	return ret;
>+}
>+
>+static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
>+{
>+	int ret, i;
>+	struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
>+	u16 conflict;
>+	__le16 value;
>+	int olen = sizeof(value);
>+	struct dln2_adc_port_chan port_chan = {
>+		.port = dln2->port,
>+		.chan = channel,
>+	};
>+
>+	ret = iio_device_claim_direct_mode(indio_dev);
>+	if (ret < 0)
>+		return ret;
>+
>+	ret = dln2_adc_set_chan_enabled(dln2, channel, true);
>+	if (ret < 0)
>+		goto release_direct;
>+
>+	ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
>+	if (ret < 0) {
>+		if (conflict) {
>+			dev_err(&dln2->pdev->dev,
>+				"ADC pins conflict with mask %04X\n",
>+				(int)conflict);
>+			ret = -EBUSY;
>+		}
>+		goto disable_chan;
>+	}
>+
>+	/*
>+	 * Call GET_VAL twice due to initial zero-return immediately after
>+	 * enabling channel.
>+	 */
>+	for (i = 0; i < 2; ++i) {
>+		ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
>+				    &port_chan, sizeof(port_chan),
>+				    &value, &olen);
>+		if (ret < 0) {
>+			dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>+			goto disable_port;
>+		}
>+		if (olen < sizeof(value)) {
>+			ret = -EPROTO;
>+			goto disable_port;
>+		}
>+	}
>+
>+	ret = le16_to_cpu(value);
>+
>+disable_port:
>+	dln2_adc_set_port_enabled(dln2, false, NULL);
>+disable_chan:
>+	dln2_adc_set_chan_enabled(dln2, channel, false);
>+release_direct:
>+	iio_device_release_direct_mode(indio_dev);
>+
>+	return ret;
>+}
>+
>+static int dln2_adc_read_all(struct dln2_adc *dln2,
>+			     struct dln2_adc_get_all_vals *get_all_vals)
>+{
>+	int ret;
>+	__u8 port = dln2->port;
>+	int olen = sizeof(*get_all_vals);
>+
>+	ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
>+			    &port, sizeof(port), get_all_vals, &olen);
>+	if (ret < 0) {
>+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>+		return ret;
>+	}
>+	if (olen < sizeof(*get_all_vals))
>+		return -EPROTO;
>+
>+	return ret;
>+}
>+
>+static int dln2_adc_read_raw(struct iio_dev *indio_dev,
>+			     struct iio_chan_spec const *chan,
>+			     int *val,
>+			     int *val2,
>+			     long mask)
>+{
>+	int ret;
>+	unsigned int microhertz;
>+	struct dln2_adc *dln2 = iio_priv(indio_dev);
>+
>+	switch (mask) {
>+	case IIO_CHAN_INFO_RAW:
>+		mutex_lock(&dln2->mutex);
>+		ret = dln2_adc_read(dln2, chan->channel);
>+		mutex_unlock(&dln2->mutex);
>+
>+		if (ret < 0)
>+			return ret;
>+
>+		*val = ret;
>+		return IIO_VAL_INT;
>+
>+	case IIO_CHAN_INFO_SCALE:
>+		/*
>+		 * Voltage reference is fixed at 3.3v
>+		 *  3.3 / (1 << 10) * 1000000000
>+		 */
>+		*val = 0;
>+		*val2 = 3222656;
>+		return IIO_VAL_INT_PLUS_NANO;
>+
>+	case IIO_CHAN_INFO_SAMP_FREQ:
>+		if (dln2->sample_period) {
>+			microhertz = 1000000000 / dln2->sample_period;
>+			*val = microhertz / 1000000;
>+			*val2 = microhertz % 1000000;
>+		} else {
>+			*val = 0;
>+			*val2 = 0;
>+		}
>+
>+		return IIO_VAL_INT_PLUS_MICRO;
>+
>+	default:
>+		return -EINVAL;
>+	}
>+}
>+
>+static int dln2_adc_write_raw(struct iio_dev *indio_dev,
>+			      struct iio_chan_spec const *chan,
>+			      int val,
>+			      int val2,
>+			      long mask)
>+{
>+	int ret;
>+	unsigned int microhertz;
>+	struct dln2_adc *dln2 = iio_priv(indio_dev);
>+
>+	switch (mask) {
>+	case IIO_CHAN_INFO_SAMP_FREQ:
>+		microhertz = 1000000 * val + val2;
>+
>+		mutex_lock(&dln2->mutex);
>+
>+		dln2->sample_period =
>+			microhertz ? 1000000000 / microhertz : UINT_MAX;
>+		if (dln2->sample_period > 65535) {
>+			dln2->sample_period = 65535;
>+			dev_warn(&dln2->pdev->dev,
>+				 "clamping period to 65535ms\n");
>+		}
>+
>+		/*
>+		 * The first requested channel is arbitrated as a shared
>+		 * trigger source, so only one event is registered with the
>+		 * DLN. The event handler will then read all enabled channel
>+		 * values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
>+		 * synchronization between ADC readings.
>+		 */
>+		if (dln2->trigger_chan != -1)
>+			ret = dln2_adc_set_chan_period(dln2,
>+				dln2->trigger_chan, dln2->sample_period);
>+		else
>+			ret = 0;
>+
>+		mutex_unlock(&dln2->mutex);
>+
>+		return ret;
>+
>+	default:
>+		return -EINVAL;
>+	}
>+}
>+
>+static int dln2_update_scan_mode(struct iio_dev *indio_dev,
>+				 const unsigned long *scan_mask)
>+{
>+	struct dln2_adc *dln2 = iio_priv(indio_dev);
>+	int chan_count = indio_dev->num_channels - 1;
>+	int ret, i, j;
>+
>+	mutex_lock(&dln2->mutex);
>+
>+	for (i = 0; i < chan_count; ++i) {
>+		ret = dln2_adc_set_chan_enabled(dln2, i,
>+						test_bit(i, scan_mask));
>+		if (ret < 0) {
>+			for (j = 0; j < i; ++j)
>+				dln2_adc_set_chan_enabled(dln2, j, false);
>+			mutex_unlock(&dln2->mutex);
>+			dev_err(&dln2->pdev->dev,
>+				"Unable to enable ADC channel %d\n", i);
>+			return -EBUSY;
>+		}
>+	}
>+
>+	dln2_adc_update_demux(dln2);
>+
>+	mutex_unlock(&dln2->mutex);
>+
>+	return 0;
>+}
>+
>+#define DLN2_ADC_CHAN(lval, idx) {					\
>+	lval.type = IIO_VOLTAGE;					\
>+	lval.channel = idx;						\
>+	lval.indexed = 1;						\
>+	lval.info_mask_separate = BIT(IIO_CHAN_INFO_RAW);		\
>+	lval.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |	\
>+				       BIT(IIO_CHAN_INFO_SAMP_FREQ);	\
>+	lval.scan_index = idx;						\
>+	lval.scan_type.sign = 'u';					\
>+	lval.scan_type.realbits = DLN2_ADC_DATA_BITS;			\
>+	lval.scan_type.storagebits = 16;				\
>+	lval.scan_type.endianness = IIO_LE;				\
>+}
>+
>+/* Assignment version of IIO_CHAN_SOFT_TIMESTAMP */
>+#define IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(lval, _si) {	\
>+	lval.type = IIO_TIMESTAMP;			\
>+	lval.channel = -1;				\
>+	lval.scan_index = _si;				\
>+	lval.scan_type.sign = 's';			\
>+	lval.scan_type.realbits = 64;			\
>+	lval.scan_type.storagebits = 64;		\
>+}
>+
>+static const struct iio_info dln2_adc_info = {
>+	.read_raw = dln2_adc_read_raw,
>+	.write_raw = dln2_adc_write_raw,
>+	.update_scan_mode = dln2_update_scan_mode,
>+	.driver_module = THIS_MODULE,
>+};
>+
>+static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
>+{
>+	struct iio_poll_func *pf = p;
>+	struct iio_dev *indio_dev = pf->indio_dev;
>+	struct {
>+		__le16 values[DLN2_ADC_MAX_CHANNELS];
>+		int64_t timestamp_space;
>+	} data;
>+	struct dln2_adc_get_all_vals dev_data;
>+	struct dln2_adc *dln2 = iio_priv(indio_dev);
>+	const struct dln2_adc_demux_table *t;
>+	int ret, i;
>+
>+	mutex_lock(&dln2->mutex);
>+	ret = dln2_adc_read_all(dln2, &dev_data);
>+	mutex_unlock(&dln2->mutex);
>+	if (ret < 0)
>+		goto done;
>+
>+	/* Demux operation */
>+	for (i = 0; i < dln2->demux_count; ++i) {
>+		t = &dln2->demux[i];
>+		memcpy((void *)data.values + t->to,
>+		       (void *)dev_data.values + t->from, t->length);
>+	}
>+
>+	/* Zero padding space between values and timestamp */
>+	if (dln2->ts_pad_length)
>+		memset((void *)data.values + dln2->ts_pad_offset,
>+		       0, dln2->ts_pad_length);
>+
>+	iio_push_to_buffers_with_timestamp(indio_dev, &data,
>+					   iio_get_time_ns(indio_dev));
>+
>+done:
>+	iio_trigger_notify_done(indio_dev->trig);
>+	return IRQ_HANDLED;
>+}
>+
>+static int dln2_adc_triggered_buffer_postenable(struct iio_dev
>*indio_dev)
>+{
>+	int ret;
>+	struct dln2_adc *dln2 = iio_priv(indio_dev);
>+	u16 conflict;
>+	unsigned int trigger_chan;
>+
>+	mutex_lock(&dln2->mutex);
>+
>+	/* Enable ADC */
>+	ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
>+	if (ret < 0) {
>+		mutex_unlock(&dln2->mutex);
>+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>+		if (conflict) {
>+			dev_err(&dln2->pdev->dev,
>+				"ADC pins conflict with mask %04X\n",
>+				(int)conflict);
>+			ret = -EBUSY;
>+		}
>+		return ret;
>+	}
>+
>+	/* Assign trigger channel based on first enabled channel */
>+	trigger_chan = find_first_bit(indio_dev->active_scan_mask,
>+				      indio_dev->masklength);
>+	if (trigger_chan < DLN2_ADC_MAX_CHANNELS) {
>+		dln2->trigger_chan = trigger_chan;
>+		ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan,
>+					       dln2->sample_period);
>+		mutex_unlock(&dln2->mutex);
>+		if (ret < 0) {
>+			dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>+			return ret;
>+		}
>+	} else {
>+		dln2->trigger_chan = -1;
>+		mutex_unlock(&dln2->mutex);
>+	}
>+
>+	return iio_triggered_buffer_postenable(indio_dev);
>+}
>+
>+static int dln2_adc_triggered_buffer_predisable(struct iio_dev
>*indio_dev)
>+{
>+	int ret;
>+	struct dln2_adc *dln2 = iio_priv(indio_dev);
>+
>+	mutex_lock(&dln2->mutex);
>+
>+	/* Disable trigger channel */
>+	if (dln2->trigger_chan != -1) {
>+		dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0);
>+		dln2->trigger_chan = -1;
>+	}
>+
>+	/* Disable ADC */
>+	ret = dln2_adc_set_port_enabled(dln2, false, NULL);
>+
>+	mutex_unlock(&dln2->mutex);
>+	if (ret < 0) {
>+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>+		return ret;
>+	}
>+
>+	return iio_triggered_buffer_predisable(indio_dev);
>+}
>+
>+static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
>+	.postenable = dln2_adc_triggered_buffer_postenable,
>+	.predisable = dln2_adc_triggered_buffer_predisable,
>+};
>+
>+static void dln2_adc_event(struct platform_device *pdev, u16 echo,
>+			   const void *data, int len)
>+{
>+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>+	struct dln2_adc *dln2 = iio_priv(indio_dev);
>+
>+	/* Called via URB completion handler */
>+	iio_trigger_poll(dln2->trig);
>+}
>+
>+static const struct iio_trigger_ops dln2_adc_trigger_ops = {
>+	.owner = THIS_MODULE,
>+};
>+
>+static int dln2_adc_probe(struct platform_device *pdev)
>+{
>+	struct device *dev = &pdev->dev;
>+	struct dln2_adc *dln2;
>+	struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
>+	struct iio_dev *indio_dev;
>+	int i, ret, chans;
>+
>+	indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2));
>+	if (!indio_dev) {
>+		dev_err(dev, "failed allocating iio device\n");
>+		return -ENOMEM;
>+	}
>+
>+	dln2 = iio_priv(indio_dev);
>+	dln2->pdev = pdev;
>+	dln2->port = pdata->port;
>+	dln2->trigger_chan = -1;
>+	mutex_init(&dln2->mutex);
>+
>+	platform_set_drvdata(pdev, indio_dev);
>+
>+	ret = dln2_adc_set_port_resolution(dln2);
>+	if (ret < 0) {
>+		dev_err(dev, "failed to set ADC resolution to 10 bits\n");
>+		return ret;
>+	}
>+
>+	chans = dln2_adc_get_chan_count(dln2);
>+	if (chans < 0) {
>+		dev_err(dev, "failed to get channel count: %d\n", chans);
>+		return chans;
>+	}
>+	if (chans > DLN2_ADC_MAX_CHANNELS) {
>+		chans = DLN2_ADC_MAX_CHANNELS;
>+		dev_warn(dev, "clamping channels to %d\n",
>+			 DLN2_ADC_MAX_CHANNELS);
>+	}
>+
>+	for (i = 0; i < chans; ++i)
>+		DLN2_ADC_CHAN(dln2->iio_channels[i], i)
>+	IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(dln2->iio_channels[i], i);
>+
>+	indio_dev->name = DLN2_ADC_MOD_NAME;
>+	indio_dev->dev.parent = dev;
>+	indio_dev->info = &dln2_adc_info;
>+	indio_dev->modes = INDIO_DIRECT_MODE;
>+	indio_dev->channels = dln2->iio_channels;
>+	indio_dev->num_channels = chans + 1;
>+	indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
>+
>+	dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
>+					    indio_dev->name, indio_dev->id);
>+	if (!dln2->trig) {
>+		dev_err(dev, "failed to allocate trigger\n");
>+		return -ENOMEM;
>+	}
>+	dln2->trig->ops = &dln2_adc_trigger_ops;
>+	iio_trigger_set_drvdata(dln2->trig, dln2);
>+	devm_iio_trigger_register(dev, dln2->trig);
>+	iio_trigger_set_immutable(indio_dev, dln2->trig);
>+
>+	ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
>+					      dln2_adc_trigger_h,
>+					      &dln2_adc_buffer_setup_ops);
>+	if (ret) {
>+		dev_err(dev, "failed to allocate triggered buffer: %d\n", ret);
>+		return ret;
>+	}
>+
>+	ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
>+				     dln2_adc_event);
>+	if (ret) {
>+		dev_err(dev, "failed to setup DLN2 periodic event: %d\n", ret);
>+		return ret;
>+	}
>+
>+	ret = devm_iio_device_register(dev, indio_dev);
>+	if (ret) {
>+		dev_err(dev, "failed to register iio device: %d\n", ret);
>+		goto unregister_event;
>+	}
>+
>+	return ret;
>+
>+unregister_event:
>+	dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
>+
>+	return ret;
>+}
>+
>+static int dln2_adc_remove(struct platform_device *pdev)
>+{
>+	dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
Potential race here as iio_device_register will be unwound after this...  Use non devm form 
to keep ordering.
>+	return 0;
>+}
>+
>+static struct platform_driver dln2_adc_driver = {
>+	.driver.name	= DLN2_ADC_MOD_NAME,
>+	.probe		= dln2_adc_probe,
>+	.remove		= dln2_adc_remove,
>+};
>+
>+module_platform_driver(dln2_adc_driver);
>+
>+MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
>+MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
>+MODULE_LICENSE("GPL v2");
>+MODULE_ALIAS("platform:dln2-adc");

-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.

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

* Re: [PATCH v5 3/3] mfd: dln2: Send restart command if HW init fails
  2017-07-06  7:09     ` Lee Jones
@ 2017-07-06 21:01       ` Jack Andersen
  0 siblings, 0 replies; 23+ messages in thread
From: Jack Andersen @ 2017-07-06 21:01 UTC (permalink / raw)
  To: Lee Jones
  Cc: linux-kernel, linux-iio, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler

[-- Attachment #1: Type: text/plain, Size: 3739 bytes --]

The restart command doesn't behave like other commands. It doesn't
return an error code (which indicates a failed transfer in the dln2
module so the probe fails). Attached is a system log snippet to give
you a better idea.

I'm able to consistently reproduce this behavior on 4.12 by
registering an ADC event on the DLN2 then restarting the host without
powering off. Without the dln2_restart patch, it ends on the `dln2:
probe of 1-2:1.0 failed with error -110' line. Shortly after issuing
the restart, the kernel indicates a device disconnect, and it
reconnects about 400ms later, with a usable device state.

On 5 July 2017 at 21:09, Lee Jones <lee.jones@linaro.org> wrote:
> On Wed, 05 Jul 2017, Jack Andersen wrote:
>
>> I have mixed feelings about making this patch. The device appears to
>> lose its ability to send command responses when the driver
>> unexpectedly goes offline (probably overflowing an internal send
>> buffer).
>>
>> The restart command is a send-only way to recover the device.
>> What concerns me is if there's another issue causing init to fail and
>> a restart doesn't fix, this will trigger an endless probe loop. I'm
>> not sure if there's a better way to discern this condition.
>
> Why does dln2_restart() cause an unbind/bind?
>
>> On 5 July 2017 at 11:12, Jack Andersen <jackoalan@gmail.com> wrote:
>> > There's an issue with reinitializing DLN2 hardware if ADC channel event
>> > configurations aren't reset before suspending the driver. A physical reconnect
>> > of the device solves the problem, but restarting the machine without a power
>> > cycle leaves the device state inconsistent.
>> >
>> > This patch transmits a restart command in the event of a failed init.
>> > To the kernel's perspective, this is the rough equivalent of a disconnect and
>> > reconnect, so another probe occurs shortly afterwards.
>> >
>> > Signed-off-by: Jack Andersen <jackoalan@gmail.com>
>> > ---
>> >  drivers/mfd/dln2.c | 11 ++++++++++-
>> >  1 file changed, 10 insertions(+), 1 deletion(-)
>> >
>> > diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
>> > index a22ab8c..1eb0056 100644
>> > --- a/drivers/mfd/dln2.c
>> > +++ b/drivers/mfd/dln2.c
>> > @@ -40,6 +40,7 @@ struct dln2_response {
>> >  #define DLN2_GENERIC_CMD(cmd)          DLN2_CMD(cmd, DLN2_GENERIC_MODULE_ID)
>> >  #define CMD_GET_DEVICE_VER             DLN2_GENERIC_CMD(0x30)
>> >  #define CMD_GET_DEVICE_SN              DLN2_GENERIC_CMD(0x31)
>> > +#define CMD_RESTART                    DLN2_GENERIC_CMD(0x43)
>> >
>> >  #define DLN2_HW_ID                     0x200
>> >  #define DLN2_USB_TIMEOUT               200     /* in ms */
>> > @@ -531,6 +532,12 @@ int dln2_transfer(struct platform_device *pdev, u16 cmd,
>> >  }
>> >  EXPORT_SYMBOL(dln2_transfer);
>> >
>> > +static int dln2_restart(struct dln2_dev *dln2)
>> > +{
>> > +       return _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_RESTART,
>> > +                             NULL, 0, NULL, NULL);
>> > +}
>> > +
>> >  static int dln2_check_hw(struct dln2_dev *dln2)
>> >  {
>> >         int ret;
>> > @@ -782,7 +789,9 @@ static int dln2_probe(struct usb_interface *interface,
>> >
>> >         ret = dln2_hw_init(dln2);
>> >         if (ret < 0) {
>> > -               dev_err(dev, "failed to initialize hardware\n");
>> > +               dev_err(dev,
>> > +                       "failed to initialize hardware; attempting restart\n");
>> > +               dln2_restart(dln2);
>> >                 goto out_stop_rx;
>> >         }
>> >
>> >
>
> --
> Lee Jones
> Linaro STMicroelectronics Landing Team Lead
> Linaro.org │ Open source software for ARM SoCs
> Follow Linaro: Facebook | Twitter | Blog

[-- Attachment #2: dln2-log.txt --]
[-- Type: text/plain, Size: 3536 bytes --]

[    1.316285] usb 1-2: New USB device found, idVendor=a257, idProduct=2013
[    1.316286] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[    1.316287] usb 1-2: Product: Diolan DLN2
[    1.316288] usb 1-2: Manufacturer: Diolan
[    2.264248] dln2: loading out-of-tree module taints kernel.
[    2.264282] dln2: module verification failed: signature and/or required key missing - tainting kernel
[    2.469951] dln2 1-2:1.0: failed to initialize hardware; attempting restart
[    2.602729] dln2 1-2:1.0: nonzero urb status received -71
[    2.602761] dln2 1-2:1.0: nonzero urb status received -71
[    2.602841] dln2 1-2:1.0: nonzero urb status received -71
[    2.602957] dln2 1-2:1.0: nonzero urb status received -71
[    2.603038] dln2 1-2:1.0: nonzero urb status received -71
[    2.603117] dln2 1-2:1.0: nonzero urb status received -71
[    2.603201] dln2 1-2:1.0: nonzero urb status received -71
[    2.603277] dln2 1-2:1.0: nonzero urb status received -71
[    2.603328] dln2 1-2:1.0: nonzero urb status received -71
[    2.603345] usb 1-2: USB disconnect, device number 2
[    2.603398] dln2 1-2:1.0: nonzero urb status received -71
[    2.603398] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.603437] dln2 1-2:1.0: nonzero urb status received -71
[    2.603438] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.603477] dln2 1-2:1.0: nonzero urb status received -71
[    2.603478] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.603517] dln2 1-2:1.0: nonzero urb status received -71
[    2.603518] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.603561] dln2 1-2:1.0: nonzero urb status received -71
[    2.603561] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.603597] dln2 1-2:1.0: nonzero urb status received -71
[    2.603598] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.603637] dln2 1-2:1.0: nonzero urb status received -71
[    2.603638] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.603677] dln2 1-2:1.0: nonzero urb status received -71
[    2.603678] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.603717] dln2 1-2:1.0: nonzero urb status received -71
[    2.603718] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.603757] dln2 1-2:1.0: nonzero urb status received -71
[    2.603758] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.603797] dln2 1-2:1.0: nonzero urb status received -71
[    2.603798] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.603841] dln2 1-2:1.0: nonzero urb status received -71
[    2.603842] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.603880] dln2 1-2:1.0: nonzero urb status received -71
[    2.603881] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.603917] dln2 1-2:1.0: nonzero urb status received -71
[    2.603918] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.603957] dln2 1-2:1.0: nonzero urb status received -71
[    2.603958] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.603997] dln2 1-2:1.0: nonzero urb status received -71
[    2.603998] dln2 1-2:1.0: failed to resubmit RX URB: -19
[    2.674022] dln2: probe of 1-2:1.0 failed with error -110
[    2.674057] usbcore: registered new interface driver dln2
[    2.977959] usb 1-2: new full-speed USB device number 7 using xhci_hcd
[    3.119108] usb 1-2: New USB device found, idVendor=a257, idProduct=2013
[    3.119109] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[    3.119110] usb 1-2: Product: Diolan DLN2
[    3.119111] usb 1-2: Manufacturer: Diolan
[    3.119774] dln2 1-2:1.0: Diolan DLN2 serial 33628844


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

* [PATCH v6 1/3] iio: adc: Add support for DLN2 ADC
       [not found] <b5ede0cc2cb36695b86edf44c0dddbc4736a6420>
                   ` (5 preceding siblings ...)
  2017-07-05 21:12 ` [PATCH v5 3/3] mfd: dln2: Send restart command if HW init fails Jack Andersen
@ 2017-07-06 22:39 ` Jack Andersen
  2017-07-09 17:12   ` Jonathan Cameron
  2017-07-06 22:39 ` [PATCH v6 2/3] mfd: dln2: Add cell for initializing " Jack Andersen
  7 siblings, 1 reply; 23+ messages in thread
From: Jack Andersen @ 2017-07-06 22:39 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: Jack Andersen, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Lee Jones

This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
ADC is the fourth and final component of the DLN2 for the kernel.

Signed-off-by: Jack Andersen <jackoalan@gmail.com>
---
Changes in v2:
- Address Peter's remarks; bringing consistency to C99 syntax, dev_dbg usage,
  and error handling.
- Remove probe/remove logging.
- Utilize stack-allocated buffer for pushing into IIO.
- Add timestamp channel to IIO spec.

Suggested-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
---
Changes in v3:
- Remove lazy channel enabling; integrating with update_channel_mode instead.
- Implement memcpy demux table similar to subsystem core.
- Use iio_triggered_buffer_setup to create buffer.

Suggested-by: Jonathan Cameron <jic23@kernel.org>
---
Changes in v4:
- Remove unrelated, erronous change to lsgpio.c
---
Changes in v5:
- Use of devm functions wherever possible.
- Use iio_device_claim_direct_mode.
- Variable declaration more consistent with C style.
- MFD modifications in separate patch.
Suggested-by: Jonathan Cameron <jic23@kernel.org>
---
Changes in v6:
- Don't use devm_iio_device_register to ensure correct unwind order.
Suggested-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/iio/adc/Kconfig    |   9 +
 drivers/iio/adc/Makefile   |   1 +
 drivers/iio/adc/dln2-adc.c | 722 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 732 insertions(+)
 create mode 100644 drivers/iio/adc/dln2-adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 614fa41..196d2ce 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -239,6 +239,15 @@ config DA9150_GPADC
 	  To compile this driver as a module, choose M here: the module will be
 	  called berlin2-adc.
 
+config DLN2_ADC
+	tristate "Diolan DLN-2 ADC driver support"
+	depends on MFD_DLN2
+	help
+	  Say yes here to build support for Diolan DLN-2 ADC.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called adc_dln2.
+
 config ENVELOPE_DETECTOR
 	tristate "Envelope detector using a DAC and a comparator"
 	depends on OF
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index b546736a..7ea0bf3 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
 obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
 obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
 obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
+obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
 obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
 obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
new file mode 100644
index 0000000..ab8d6ae
--- /dev/null
+++ b/drivers/iio/adc/dln2-adc.c
@@ -0,0 +1,722 @@
+/*
+ * Driver for the Diolan DLN-2 USB-ADC adapter
+ *
+ * Copyright (c) 2017 Jack Andersen
+ *
+ * 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, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/dln2.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+
+#define DLN2_ADC_MOD_NAME "dln2-adc"
+
+#define DLN2_ADC_ID             0x06
+
+#define DLN2_ADC_GET_CHANNEL_COUNT	DLN2_CMD(0x01, DLN2_ADC_ID)
+#define DLN2_ADC_ENABLE			DLN2_CMD(0x02, DLN2_ADC_ID)
+#define DLN2_ADC_DISABLE		DLN2_CMD(0x03, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_ENABLE		DLN2_CMD(0x05, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_DISABLE	DLN2_CMD(0x06, DLN2_ADC_ID)
+#define DLN2_ADC_SET_RESOLUTION		DLN2_CMD(0x08, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_VAL	DLN2_CMD(0x0A, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_ALL_VAL	DLN2_CMD(0x0B, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_SET_CFG	DLN2_CMD(0x0C, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_CFG	DLN2_CMD(0x0D, DLN2_ADC_ID)
+#define DLN2_ADC_CONDITION_MET_EV	DLN2_CMD(0x10, DLN2_ADC_ID)
+
+#define DLN2_ADC_EVENT_NONE		0
+#define DLN2_ADC_EVENT_BELOW		1
+#define DLN2_ADC_EVENT_LEVEL_ABOVE	2
+#define DLN2_ADC_EVENT_OUTSIDE		3
+#define DLN2_ADC_EVENT_INSIDE		4
+#define DLN2_ADC_EVENT_ALWAYS		5
+
+#define DLN2_ADC_MAX_CHANNELS 8
+#define DLN2_ADC_DATA_BITS 10
+
+/*
+ * Plays similar role to iio_demux_table in subsystem core; except allocated
+ * in a fixed 8-element array.
+ */
+struct dln2_adc_demux_table {
+	unsigned int from;
+	unsigned int to;
+	unsigned int length;
+};
+
+struct dln2_adc {
+	struct platform_device *pdev;
+	struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1];
+	int port, trigger_chan;
+	struct iio_trigger *trig;
+	struct mutex mutex;
+	/* Cached sample period in milliseconds */
+	unsigned int sample_period;
+	/* Demux table */
+	unsigned int demux_count;
+	struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS];
+	/* Precomputed timestamp padding offset and length */
+	unsigned int ts_pad_offset, ts_pad_length;
+};
+
+struct dln2_adc_port_chan {
+	u8 port;
+	u8 chan;
+};
+
+struct dln2_adc_get_all_vals {
+	__le16 channel_mask;
+	__le16 values[DLN2_ADC_MAX_CHANNELS];
+};
+
+static void dln2_adc_add_demux(struct dln2_adc *dln2,
+	unsigned int in_loc, unsigned int out_loc,
+	unsigned int length)
+{
+	struct dln2_adc_demux_table *p = dln2->demux_count ?
+		&dln2->demux[dln2->demux_count - 1] : NULL;
+
+	if (p && p->from + p->length == in_loc &&
+		p->to + p->length == out_loc) {
+		p->length += length;
+	} else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) {
+		p = &dln2->demux[dln2->demux_count++];
+		p->from = in_loc;
+		p->to = out_loc;
+		p->length = length;
+	}
+}
+
+static void dln2_adc_update_demux(struct dln2_adc *dln2)
+{
+	int in_ind = -1, out_ind;
+	unsigned int in_loc = 0, out_loc = 0;
+	struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
+
+	/* Clear out any old demux */
+	dln2->demux_count = 0;
+
+	/* Optimize all 8-channels case */
+	if (indio_dev->masklength &&
+	    (*indio_dev->active_scan_mask & 0xff) == 0xff) {
+		dln2_adc_add_demux(dln2, 0, 0, 16);
+		dln2->ts_pad_offset = 0;
+		dln2->ts_pad_length = 0;
+		return;
+	}
+
+	/* Build demux table from fixed 8-channels to active_scan_mask */
+	for_each_set_bit(out_ind,
+			 indio_dev->active_scan_mask,
+			 indio_dev->masklength) {
+		/* Handle timestamp separately */
+		if (out_ind == DLN2_ADC_MAX_CHANNELS)
+			break;
+		for (++in_ind; in_ind != out_ind; ++in_ind)
+			in_loc += 2;
+		dln2_adc_add_demux(dln2, in_loc, out_loc, 2);
+		out_loc += 2;
+		in_loc += 2;
+	}
+
+	if (indio_dev->scan_timestamp) {
+		size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1;
+
+		dln2->ts_pad_offset = out_loc;
+		dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc;
+	} else {
+		dln2->ts_pad_offset = 0;
+		dln2->ts_pad_length = 0;
+	}
+}
+
+static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
+{
+	int ret;
+	u8 port = dln2->port;
+	u8 count;
+	int olen = sizeof(count);
+
+	ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
+			    &port, sizeof(port), &count, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+	if (olen < sizeof(count))
+		return -EPROTO;
+
+	return count;
+}
+
+static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
+{
+	int ret;
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = DLN2_ADC_DATA_BITS,
+	};
+
+	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
+			       &port_chan, sizeof(port_chan));
+	if (ret < 0)
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+
+	return ret;
+}
+
+static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
+				     int channel, bool enable)
+{
+	int ret;
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = channel,
+	};
+	u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
+
+	ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
+	if (ret < 0)
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+
+	return ret;
+}
+
+static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable,
+				     u16 *conflict_out)
+{
+	int ret;
+	u8 port = dln2->port;
+	__le16 conflict;
+	int olen = sizeof(conflict);
+	u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
+
+	if (conflict_out)
+		*conflict_out = 0;
+
+	ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
+			    &conflict, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
+			__func__, (int)enable);
+		if (conflict_out && enable && olen >= sizeof(conflict))
+			*conflict_out = le16_to_cpu(conflict);
+		return ret;
+	}
+	if (enable && olen < sizeof(conflict))
+		return -EPROTO;
+
+	return ret;
+}
+
+static int dln2_adc_set_chan_period(struct dln2_adc *dln2,
+	unsigned int channel, unsigned int period)
+{
+	int ret;
+	struct {
+		struct dln2_adc_port_chan port_chan;
+		__u8 type;
+		__le16 period;
+		__le16 low;
+		__le16 high;
+	} __packed set_cfg = {
+		.port_chan.port = dln2->port,
+		.port_chan.chan = channel,
+		.type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
+		.period = cpu_to_le16(period)
+	};
+
+	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
+			       &set_cfg, sizeof(set_cfg));
+	if (ret < 0)
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+
+	return ret;
+}
+
+static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
+{
+	int ret, i;
+	struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
+	u16 conflict;
+	__le16 value;
+	int olen = sizeof(value);
+	struct dln2_adc_port_chan port_chan = {
+		.port = dln2->port,
+		.chan = channel,
+	};
+
+	ret = iio_device_claim_direct_mode(indio_dev);
+	if (ret < 0)
+		return ret;
+
+	ret = dln2_adc_set_chan_enabled(dln2, channel, true);
+	if (ret < 0)
+		goto release_direct;
+
+	ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
+	if (ret < 0) {
+		if (conflict) {
+			dev_err(&dln2->pdev->dev,
+				"ADC pins conflict with mask %04X\n",
+				(int)conflict);
+			ret = -EBUSY;
+		}
+		goto disable_chan;
+	}
+
+	/*
+	 * Call GET_VAL twice due to initial zero-return immediately after
+	 * enabling channel.
+	 */
+	for (i = 0; i < 2; ++i) {
+		ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
+				    &port_chan, sizeof(port_chan),
+				    &value, &olen);
+		if (ret < 0) {
+			dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+			goto disable_port;
+		}
+		if (olen < sizeof(value)) {
+			ret = -EPROTO;
+			goto disable_port;
+		}
+	}
+
+	ret = le16_to_cpu(value);
+
+disable_port:
+	dln2_adc_set_port_enabled(dln2, false, NULL);
+disable_chan:
+	dln2_adc_set_chan_enabled(dln2, channel, false);
+release_direct:
+	iio_device_release_direct_mode(indio_dev);
+
+	return ret;
+}
+
+static int dln2_adc_read_all(struct dln2_adc *dln2,
+			     struct dln2_adc_get_all_vals *get_all_vals)
+{
+	int ret;
+	__u8 port = dln2->port;
+	int olen = sizeof(*get_all_vals);
+
+	ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
+			    &port, sizeof(port), get_all_vals, &olen);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+	if (olen < sizeof(*get_all_vals))
+		return -EPROTO;
+
+	return ret;
+}
+
+static int dln2_adc_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int *val,
+			     int *val2,
+			     long mask)
+{
+	int ret;
+	unsigned int microhertz;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&dln2->mutex);
+		ret = dln2_adc_read(dln2, chan->channel);
+		mutex_unlock(&dln2->mutex);
+
+		if (ret < 0)
+			return ret;
+
+		*val = ret;
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		/*
+		 * Voltage reference is fixed at 3.3v
+		 *  3.3 / (1 << 10) * 1000000000
+		 */
+		*val = 0;
+		*val2 = 3222656;
+		return IIO_VAL_INT_PLUS_NANO;
+
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (dln2->sample_period) {
+			microhertz = 1000000000 / dln2->sample_period;
+			*val = microhertz / 1000000;
+			*val2 = microhertz % 1000000;
+		} else {
+			*val = 0;
+			*val2 = 0;
+		}
+
+		return IIO_VAL_INT_PLUS_MICRO;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int dln2_adc_write_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int val,
+			      int val2,
+			      long mask)
+{
+	int ret;
+	unsigned int microhertz;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		microhertz = 1000000 * val + val2;
+
+		mutex_lock(&dln2->mutex);
+
+		dln2->sample_period =
+			microhertz ? 1000000000 / microhertz : UINT_MAX;
+		if (dln2->sample_period > 65535) {
+			dln2->sample_period = 65535;
+			dev_warn(&dln2->pdev->dev,
+				 "clamping period to 65535ms\n");
+		}
+
+		/*
+		 * The first requested channel is arbitrated as a shared
+		 * trigger source, so only one event is registered with the
+		 * DLN. The event handler will then read all enabled channel
+		 * values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
+		 * synchronization between ADC readings.
+		 */
+		if (dln2->trigger_chan != -1)
+			ret = dln2_adc_set_chan_period(dln2,
+				dln2->trigger_chan, dln2->sample_period);
+		else
+			ret = 0;
+
+		mutex_unlock(&dln2->mutex);
+
+		return ret;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int dln2_update_scan_mode(struct iio_dev *indio_dev,
+				 const unsigned long *scan_mask)
+{
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+	int chan_count = indio_dev->num_channels - 1;
+	int ret, i, j;
+
+	mutex_lock(&dln2->mutex);
+
+	for (i = 0; i < chan_count; ++i) {
+		ret = dln2_adc_set_chan_enabled(dln2, i,
+						test_bit(i, scan_mask));
+		if (ret < 0) {
+			for (j = 0; j < i; ++j)
+				dln2_adc_set_chan_enabled(dln2, j, false);
+			mutex_unlock(&dln2->mutex);
+			dev_err(&dln2->pdev->dev,
+				"Unable to enable ADC channel %d\n", i);
+			return -EBUSY;
+		}
+	}
+
+	dln2_adc_update_demux(dln2);
+
+	mutex_unlock(&dln2->mutex);
+
+	return 0;
+}
+
+#define DLN2_ADC_CHAN(lval, idx) {					\
+	lval.type = IIO_VOLTAGE;					\
+	lval.channel = idx;						\
+	lval.indexed = 1;						\
+	lval.info_mask_separate = BIT(IIO_CHAN_INFO_RAW);		\
+	lval.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |	\
+				       BIT(IIO_CHAN_INFO_SAMP_FREQ);	\
+	lval.scan_index = idx;						\
+	lval.scan_type.sign = 'u';					\
+	lval.scan_type.realbits = DLN2_ADC_DATA_BITS;			\
+	lval.scan_type.storagebits = 16;				\
+	lval.scan_type.endianness = IIO_LE;				\
+}
+
+/* Assignment version of IIO_CHAN_SOFT_TIMESTAMP */
+#define IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(lval, _si) {	\
+	lval.type = IIO_TIMESTAMP;			\
+	lval.channel = -1;				\
+	lval.scan_index = _si;				\
+	lval.scan_type.sign = 's';			\
+	lval.scan_type.realbits = 64;			\
+	lval.scan_type.storagebits = 64;		\
+}
+
+static const struct iio_info dln2_adc_info = {
+	.read_raw = dln2_adc_read_raw,
+	.write_raw = dln2_adc_write_raw,
+	.update_scan_mode = dln2_update_scan_mode,
+	.driver_module = THIS_MODULE,
+};
+
+static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct {
+		__le16 values[DLN2_ADC_MAX_CHANNELS];
+		int64_t timestamp_space;
+	} data;
+	struct dln2_adc_get_all_vals dev_data;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+	const struct dln2_adc_demux_table *t;
+	int ret, i;
+
+	mutex_lock(&dln2->mutex);
+	ret = dln2_adc_read_all(dln2, &dev_data);
+	mutex_unlock(&dln2->mutex);
+	if (ret < 0)
+		goto done;
+
+	/* Demux operation */
+	for (i = 0; i < dln2->demux_count; ++i) {
+		t = &dln2->demux[i];
+		memcpy((void *)data.values + t->to,
+		       (void *)dev_data.values + t->from, t->length);
+	}
+
+	/* Zero padding space between values and timestamp */
+	if (dln2->ts_pad_length)
+		memset((void *)data.values + dln2->ts_pad_offset,
+		       0, dln2->ts_pad_length);
+
+	iio_push_to_buffers_with_timestamp(indio_dev, &data,
+					   iio_get_time_ns(indio_dev));
+
+done:
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+	u16 conflict;
+	unsigned int trigger_chan;
+
+	mutex_lock(&dln2->mutex);
+
+	/* Enable ADC */
+	ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
+	if (ret < 0) {
+		mutex_unlock(&dln2->mutex);
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		if (conflict) {
+			dev_err(&dln2->pdev->dev,
+				"ADC pins conflict with mask %04X\n",
+				(int)conflict);
+			ret = -EBUSY;
+		}
+		return ret;
+	}
+
+	/* Assign trigger channel based on first enabled channel */
+	trigger_chan = find_first_bit(indio_dev->active_scan_mask,
+				      indio_dev->masklength);
+	if (trigger_chan < DLN2_ADC_MAX_CHANNELS) {
+		dln2->trigger_chan = trigger_chan;
+		ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan,
+					       dln2->sample_period);
+		mutex_unlock(&dln2->mutex);
+		if (ret < 0) {
+			dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+			return ret;
+		}
+	} else {
+		dln2->trigger_chan = -1;
+		mutex_unlock(&dln2->mutex);
+	}
+
+	return iio_triggered_buffer_postenable(indio_dev);
+}
+
+static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	mutex_lock(&dln2->mutex);
+
+	/* Disable trigger channel */
+	if (dln2->trigger_chan != -1) {
+		dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0);
+		dln2->trigger_chan = -1;
+	}
+
+	/* Disable ADC */
+	ret = dln2_adc_set_port_enabled(dln2, false, NULL);
+
+	mutex_unlock(&dln2->mutex);
+	if (ret < 0) {
+		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+		return ret;
+	}
+
+	return iio_triggered_buffer_predisable(indio_dev);
+}
+
+static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
+	.postenable = dln2_adc_triggered_buffer_postenable,
+	.predisable = dln2_adc_triggered_buffer_predisable,
+};
+
+static void dln2_adc_event(struct platform_device *pdev, u16 echo,
+			   const void *data, int len)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+	/* Called via URB completion handler */
+	iio_trigger_poll(dln2->trig);
+}
+
+static const struct iio_trigger_ops dln2_adc_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+static int dln2_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct dln2_adc *dln2;
+	struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	struct iio_dev *indio_dev;
+	int i, ret, chans;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2));
+	if (!indio_dev) {
+		dev_err(dev, "failed allocating iio device\n");
+		return -ENOMEM;
+	}
+
+	dln2 = iio_priv(indio_dev);
+	dln2->pdev = pdev;
+	dln2->port = pdata->port;
+	dln2->trigger_chan = -1;
+	mutex_init(&dln2->mutex);
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	ret = dln2_adc_set_port_resolution(dln2);
+	if (ret < 0) {
+		dev_err(dev, "failed to set ADC resolution to 10 bits\n");
+		return ret;
+	}
+
+	chans = dln2_adc_get_chan_count(dln2);
+	if (chans < 0) {
+		dev_err(dev, "failed to get channel count: %d\n", chans);
+		return chans;
+	}
+	if (chans > DLN2_ADC_MAX_CHANNELS) {
+		chans = DLN2_ADC_MAX_CHANNELS;
+		dev_warn(dev, "clamping channels to %d\n",
+			 DLN2_ADC_MAX_CHANNELS);
+	}
+
+	for (i = 0; i < chans; ++i)
+		DLN2_ADC_CHAN(dln2->iio_channels[i], i)
+	IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(dln2->iio_channels[i], i);
+
+	indio_dev->name = DLN2_ADC_MOD_NAME;
+	indio_dev->dev.parent = dev;
+	indio_dev->info = &dln2_adc_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = dln2->iio_channels;
+	indio_dev->num_channels = chans + 1;
+	indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
+
+	dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
+					    indio_dev->name, indio_dev->id);
+	if (!dln2->trig) {
+		dev_err(dev, "failed to allocate trigger\n");
+		return -ENOMEM;
+	}
+	dln2->trig->ops = &dln2_adc_trigger_ops;
+	iio_trigger_set_drvdata(dln2->trig, dln2);
+	devm_iio_trigger_register(dev, dln2->trig);
+	iio_trigger_set_immutable(indio_dev, dln2->trig);
+
+	ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+					      dln2_adc_trigger_h,
+					      &dln2_adc_buffer_setup_ops);
+	if (ret) {
+		dev_err(dev, "failed to allocate triggered buffer: %d\n", ret);
+		return ret;
+	}
+
+	ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
+				     dln2_adc_event);
+	if (ret) {
+		dev_err(dev, "failed to setup DLN2 periodic event: %d\n", ret);
+		return ret;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(dev, "failed to register iio device: %d\n", ret);
+		goto unregister_event;
+	}
+
+	return ret;
+
+unregister_event:
+	dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
+
+	return ret;
+}
+
+static int dln2_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+	iio_device_unregister(indio_dev);
+	dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
+	return 0;
+}
+
+static struct platform_driver dln2_adc_driver = {
+	.driver.name	= DLN2_ADC_MOD_NAME,
+	.probe		= dln2_adc_probe,
+	.remove		= dln2_adc_remove,
+};
+
+module_platform_driver(dln2_adc_driver);
+
+MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
+MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dln2-adc");
-- 
1.9.1

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

* [PATCH v6 2/3] mfd: dln2: Add cell for initializing DLN2 ADC
       [not found] <b5ede0cc2cb36695b86edf44c0dddbc4736a6420>
                   ` (6 preceding siblings ...)
  2017-07-06 22:39 ` [PATCH v6 1/3] iio: adc: Add support for DLN2 ADC Jack Andersen
@ 2017-07-06 22:39 ` Jack Andersen
  7 siblings, 0 replies; 23+ messages in thread
From: Jack Andersen @ 2017-07-06 22:39 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: Jack Andersen, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Lee Jones

This patch extends the DLN2 driver; adding cell for adc_dln2 module.

Signed-off-by: Jack Andersen <jackoalan@gmail.com>
Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
---
Changes in v6:
- Add commit message

For my own reference:
  Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mfd/dln2.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
index 704e189..a22ab8c 100644
--- a/drivers/mfd/dln2.c
+++ b/drivers/mfd/dln2.c
@@ -53,6 +53,7 @@ enum dln2_handle {
 	DLN2_HANDLE_GPIO,
 	DLN2_HANDLE_I2C,
 	DLN2_HANDLE_SPI,
+	DLN2_HANDLE_ADC,
 	DLN2_HANDLES
 };
 
@@ -663,6 +664,12 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
 	.port = 0,
 };
 
+/* Only one ADC port supported */
+static struct dln2_platform_data dln2_pdata_adc = {
+	.handle = DLN2_HANDLE_ADC,
+	.port = 0,
+};
+
 static const struct mfd_cell dln2_devs[] = {
 	{
 		.name = "dln2-gpio",
@@ -679,6 +686,11 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
 		.platform_data = &dln2_pdata_spi,
 		.pdata_size = sizeof(struct dln2_platform_data),
 	},
+	{
+		.name = "dln2-adc",
+		.platform_data = &dln2_pdata_adc,
+		.pdata_size = sizeof(struct dln2_platform_data),
+	},
 };
 
 static void dln2_stop(struct dln2_dev *dln2)
-- 
1.9.1

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

* Re: [PATCH v6 1/3] iio: adc: Add support for DLN2 ADC
  2017-07-06 22:39 ` [PATCH v6 1/3] iio: adc: Add support for DLN2 ADC Jack Andersen
@ 2017-07-09 17:12   ` Jonathan Cameron
  2017-07-10 20:04     ` Jack Andersen
  0 siblings, 1 reply; 23+ messages in thread
From: Jonathan Cameron @ 2017-07-09 17:12 UTC (permalink / raw)
  To: Jack Andersen
  Cc: linux-iio, linux-kernel, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Lee Jones

On Thu,  6 Jul 2017 12:39:33 -1000
Jack Andersen <jackoalan@gmail.com> wrote:

> This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
> ADC is the fourth and final component of the DLN2 for the kernel.
> 
> Signed-off-by: Jack Andersen <jackoalan@gmail.com>
Applied to the togreg branch of iio.git.  Initially pushed out as
testing for the autobuilders to play with it.

Thanks for your persistence with this.  Nice to have a driver
covering this element of what is a very handy bit of kit!

Jonathan
> ---
> Changes in v2:
> - Address Peter's remarks; bringing consistency to C99 syntax, dev_dbg usage,
>   and error handling.
> - Remove probe/remove logging.
> - Utilize stack-allocated buffer for pushing into IIO.
> - Add timestamp channel to IIO spec.
> 
> Suggested-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
> ---
> Changes in v3:
> - Remove lazy channel enabling; integrating with update_channel_mode instead.
> - Implement memcpy demux table similar to subsystem core.
> - Use iio_triggered_buffer_setup to create buffer.
> 
> Suggested-by: Jonathan Cameron <jic23@kernel.org>
> ---
> Changes in v4:
> - Remove unrelated, erronous change to lsgpio.c
> ---
> Changes in v5:
> - Use of devm functions wherever possible.
> - Use iio_device_claim_direct_mode.
> - Variable declaration more consistent with C style.
> - MFD modifications in separate patch.
> Suggested-by: Jonathan Cameron <jic23@kernel.org>
> ---
> Changes in v6:
> - Don't use devm_iio_device_register to ensure correct unwind order.
> Suggested-by: Lee Jones <lee.jones@linaro.org>
> ---
>  drivers/iio/adc/Kconfig    |   9 +
>  drivers/iio/adc/Makefile   |   1 +
>  drivers/iio/adc/dln2-adc.c | 722 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 732 insertions(+)
>  create mode 100644 drivers/iio/adc/dln2-adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 614fa41..196d2ce 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -239,6 +239,15 @@ config DA9150_GPADC
>  	  To compile this driver as a module, choose M here: the module will be
>  	  called berlin2-adc.
>  
> +config DLN2_ADC
> +	tristate "Diolan DLN-2 ADC driver support"
> +	depends on MFD_DLN2
> +	help
> +	  Say yes here to build support for Diolan DLN-2 ADC.
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called adc_dln2.
> +
>  config ENVELOPE_DETECTOR
>  	tristate "Envelope detector using a DAC and a comparator"
>  	depends on OF
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index b546736a..7ea0bf3 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>  obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>  obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
>  obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
> +obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
>  obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
> diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
> new file mode 100644
> index 0000000..ab8d6ae
> --- /dev/null
> +++ b/drivers/iio/adc/dln2-adc.c
> @@ -0,0 +1,722 @@
> +/*
> + * Driver for the Diolan DLN-2 USB-ADC adapter
> + *
> + * Copyright (c) 2017 Jack Andersen
> + *
> + * 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, version 2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/platform_device.h>
> +#include <linux/mfd/dln2.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/kfifo_buf.h>
> +
> +#define DLN2_ADC_MOD_NAME "dln2-adc"
> +
> +#define DLN2_ADC_ID             0x06
> +
> +#define DLN2_ADC_GET_CHANNEL_COUNT	DLN2_CMD(0x01, DLN2_ADC_ID)
> +#define DLN2_ADC_ENABLE			DLN2_CMD(0x02, DLN2_ADC_ID)
> +#define DLN2_ADC_DISABLE		DLN2_CMD(0x03, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_ENABLE		DLN2_CMD(0x05, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_DISABLE	DLN2_CMD(0x06, DLN2_ADC_ID)
> +#define DLN2_ADC_SET_RESOLUTION		DLN2_CMD(0x08, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_GET_VAL	DLN2_CMD(0x0A, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_GET_ALL_VAL	DLN2_CMD(0x0B, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_SET_CFG	DLN2_CMD(0x0C, DLN2_ADC_ID)
> +#define DLN2_ADC_CHANNEL_GET_CFG	DLN2_CMD(0x0D, DLN2_ADC_ID)
> +#define DLN2_ADC_CONDITION_MET_EV	DLN2_CMD(0x10, DLN2_ADC_ID)
> +
> +#define DLN2_ADC_EVENT_NONE		0
> +#define DLN2_ADC_EVENT_BELOW		1
> +#define DLN2_ADC_EVENT_LEVEL_ABOVE	2
> +#define DLN2_ADC_EVENT_OUTSIDE		3
> +#define DLN2_ADC_EVENT_INSIDE		4
> +#define DLN2_ADC_EVENT_ALWAYS		5
> +
> +#define DLN2_ADC_MAX_CHANNELS 8
> +#define DLN2_ADC_DATA_BITS 10
> +
> +/*
> + * Plays similar role to iio_demux_table in subsystem core; except allocated
> + * in a fixed 8-element array.
> + */
> +struct dln2_adc_demux_table {
> +	unsigned int from;
> +	unsigned int to;
> +	unsigned int length;
> +};
> +
> +struct dln2_adc {
> +	struct platform_device *pdev;
> +	struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1];
> +	int port, trigger_chan;
> +	struct iio_trigger *trig;
> +	struct mutex mutex;
> +	/* Cached sample period in milliseconds */
> +	unsigned int sample_period;
> +	/* Demux table */
> +	unsigned int demux_count;
> +	struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS];
> +	/* Precomputed timestamp padding offset and length */
> +	unsigned int ts_pad_offset, ts_pad_length;
> +};
> +
> +struct dln2_adc_port_chan {
> +	u8 port;
> +	u8 chan;
> +};
> +
> +struct dln2_adc_get_all_vals {
> +	__le16 channel_mask;
> +	__le16 values[DLN2_ADC_MAX_CHANNELS];
> +};
> +
> +static void dln2_adc_add_demux(struct dln2_adc *dln2,
> +	unsigned int in_loc, unsigned int out_loc,
> +	unsigned int length)
> +{
> +	struct dln2_adc_demux_table *p = dln2->demux_count ?
> +		&dln2->demux[dln2->demux_count - 1] : NULL;
> +
> +	if (p && p->from + p->length == in_loc &&
> +		p->to + p->length == out_loc) {
> +		p->length += length;
> +	} else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) {
> +		p = &dln2->demux[dln2->demux_count++];
> +		p->from = in_loc;
> +		p->to = out_loc;
> +		p->length = length;
> +	}
> +}
> +
> +static void dln2_adc_update_demux(struct dln2_adc *dln2)
> +{
> +	int in_ind = -1, out_ind;
> +	unsigned int in_loc = 0, out_loc = 0;
> +	struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
> +
> +	/* Clear out any old demux */
> +	dln2->demux_count = 0;
> +
> +	/* Optimize all 8-channels case */
> +	if (indio_dev->masklength &&
> +	    (*indio_dev->active_scan_mask & 0xff) == 0xff) {
> +		dln2_adc_add_demux(dln2, 0, 0, 16);
> +		dln2->ts_pad_offset = 0;
> +		dln2->ts_pad_length = 0;
> +		return;
> +	}
> +
> +	/* Build demux table from fixed 8-channels to active_scan_mask */
> +	for_each_set_bit(out_ind,
> +			 indio_dev->active_scan_mask,
> +			 indio_dev->masklength) {
> +		/* Handle timestamp separately */
> +		if (out_ind == DLN2_ADC_MAX_CHANNELS)
> +			break;
> +		for (++in_ind; in_ind != out_ind; ++in_ind)
> +			in_loc += 2;
> +		dln2_adc_add_demux(dln2, in_loc, out_loc, 2);
> +		out_loc += 2;
> +		in_loc += 2;
> +	}
> +
> +	if (indio_dev->scan_timestamp) {
> +		size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1;
> +
> +		dln2->ts_pad_offset = out_loc;
> +		dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc;
> +	} else {
> +		dln2->ts_pad_offset = 0;
> +		dln2->ts_pad_length = 0;
> +	}
> +}
> +
> +static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
> +{
> +	int ret;
> +	u8 port = dln2->port;
> +	u8 count;
> +	int olen = sizeof(count);
> +
> +	ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
> +			    &port, sizeof(port), &count, &olen);
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +	if (olen < sizeof(count))
> +		return -EPROTO;
> +
> +	return count;
> +}
> +
> +static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
> +{
> +	int ret;
> +	struct dln2_adc_port_chan port_chan = {
> +		.port = dln2->port,
> +		.chan = DLN2_ADC_DATA_BITS,
> +	};
> +
> +	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
> +			       &port_chan, sizeof(port_chan));
> +	if (ret < 0)
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +
> +	return ret;
> +}
> +
> +static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
> +				     int channel, bool enable)
> +{
> +	int ret;
> +	struct dln2_adc_port_chan port_chan = {
> +		.port = dln2->port,
> +		.chan = channel,
> +	};
> +	u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
> +
> +	ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
> +	if (ret < 0)
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +
> +	return ret;
> +}
> +
> +static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable,
> +				     u16 *conflict_out)
> +{
> +	int ret;
> +	u8 port = dln2->port;
> +	__le16 conflict;
> +	int olen = sizeof(conflict);
> +	u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
> +
> +	if (conflict_out)
> +		*conflict_out = 0;
> +
> +	ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
> +			    &conflict, &olen);
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
> +			__func__, (int)enable);
> +		if (conflict_out && enable && olen >= sizeof(conflict))
> +			*conflict_out = le16_to_cpu(conflict);
> +		return ret;
> +	}
> +	if (enable && olen < sizeof(conflict))
> +		return -EPROTO;
> +
> +	return ret;
> +}
> +
> +static int dln2_adc_set_chan_period(struct dln2_adc *dln2,
> +	unsigned int channel, unsigned int period)
> +{
> +	int ret;
> +	struct {
> +		struct dln2_adc_port_chan port_chan;
> +		__u8 type;
> +		__le16 period;
> +		__le16 low;
> +		__le16 high;
> +	} __packed set_cfg = {
> +		.port_chan.port = dln2->port,
> +		.port_chan.chan = channel,
> +		.type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
> +		.period = cpu_to_le16(period)
> +	};
> +
> +	ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
> +			       &set_cfg, sizeof(set_cfg));
> +	if (ret < 0)
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +
> +	return ret;
> +}
> +
> +static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
> +{
> +	int ret, i;
> +	struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
> +	u16 conflict;
> +	__le16 value;
> +	int olen = sizeof(value);
> +	struct dln2_adc_port_chan port_chan = {
> +		.port = dln2->port,
> +		.chan = channel,
> +	};
> +
> +	ret = iio_device_claim_direct_mode(indio_dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = dln2_adc_set_chan_enabled(dln2, channel, true);
> +	if (ret < 0)
> +		goto release_direct;
> +
> +	ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
> +	if (ret < 0) {
> +		if (conflict) {
> +			dev_err(&dln2->pdev->dev,
> +				"ADC pins conflict with mask %04X\n",
> +				(int)conflict);
> +			ret = -EBUSY;
> +		}
> +		goto disable_chan;
> +	}
> +
> +	/*
> +	 * Call GET_VAL twice due to initial zero-return immediately after
> +	 * enabling channel.
> +	 */
> +	for (i = 0; i < 2; ++i) {
> +		ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
> +				    &port_chan, sizeof(port_chan),
> +				    &value, &olen);
> +		if (ret < 0) {
> +			dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +			goto disable_port;
> +		}
> +		if (olen < sizeof(value)) {
> +			ret = -EPROTO;
> +			goto disable_port;
> +		}
> +	}
> +
> +	ret = le16_to_cpu(value);
> +
> +disable_port:
> +	dln2_adc_set_port_enabled(dln2, false, NULL);
> +disable_chan:
> +	dln2_adc_set_chan_enabled(dln2, channel, false);
> +release_direct:
> +	iio_device_release_direct_mode(indio_dev);
> +
> +	return ret;
> +}
> +
> +static int dln2_adc_read_all(struct dln2_adc *dln2,
> +			     struct dln2_adc_get_all_vals *get_all_vals)
> +{
> +	int ret;
> +	__u8 port = dln2->port;
> +	int olen = sizeof(*get_all_vals);
> +
> +	ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
> +			    &port, sizeof(port), get_all_vals, &olen);
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +	if (olen < sizeof(*get_all_vals))
> +		return -EPROTO;
> +
> +	return ret;
> +}
> +
> +static int dln2_adc_read_raw(struct iio_dev *indio_dev,
> +			     struct iio_chan_spec const *chan,
> +			     int *val,
> +			     int *val2,
> +			     long mask)
> +{
> +	int ret;
> +	unsigned int microhertz;
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		mutex_lock(&dln2->mutex);
> +		ret = dln2_adc_read(dln2, chan->channel);
> +		mutex_unlock(&dln2->mutex);
> +
> +		if (ret < 0)
> +			return ret;
> +
> +		*val = ret;
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		/*
> +		 * Voltage reference is fixed at 3.3v
> +		 *  3.3 / (1 << 10) * 1000000000
> +		 */
> +		*val = 0;
> +		*val2 = 3222656;
> +		return IIO_VAL_INT_PLUS_NANO;
> +
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		if (dln2->sample_period) {
> +			microhertz = 1000000000 / dln2->sample_period;
> +			*val = microhertz / 1000000;
> +			*val2 = microhertz % 1000000;
> +		} else {
> +			*val = 0;
> +			*val2 = 0;
> +		}
> +
> +		return IIO_VAL_INT_PLUS_MICRO;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int dln2_adc_write_raw(struct iio_dev *indio_dev,
> +			      struct iio_chan_spec const *chan,
> +			      int val,
> +			      int val2,
> +			      long mask)
> +{
> +	int ret;
> +	unsigned int microhertz;
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		microhertz = 1000000 * val + val2;
> +
> +		mutex_lock(&dln2->mutex);
> +
> +		dln2->sample_period =
> +			microhertz ? 1000000000 / microhertz : UINT_MAX;
> +		if (dln2->sample_period > 65535) {
> +			dln2->sample_period = 65535;
> +			dev_warn(&dln2->pdev->dev,
> +				 "clamping period to 65535ms\n");
> +		}
> +
> +		/*
> +		 * The first requested channel is arbitrated as a shared
> +		 * trigger source, so only one event is registered with the
> +		 * DLN. The event handler will then read all enabled channel
> +		 * values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
> +		 * synchronization between ADC readings.
> +		 */
> +		if (dln2->trigger_chan != -1)
> +			ret = dln2_adc_set_chan_period(dln2,
> +				dln2->trigger_chan, dln2->sample_period);
> +		else
> +			ret = 0;
> +
> +		mutex_unlock(&dln2->mutex);
> +
> +		return ret;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int dln2_update_scan_mode(struct iio_dev *indio_dev,
> +				 const unsigned long *scan_mask)
> +{
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +	int chan_count = indio_dev->num_channels - 1;
> +	int ret, i, j;
> +
> +	mutex_lock(&dln2->mutex);
> +
> +	for (i = 0; i < chan_count; ++i) {
> +		ret = dln2_adc_set_chan_enabled(dln2, i,
> +						test_bit(i, scan_mask));
> +		if (ret < 0) {
> +			for (j = 0; j < i; ++j)
> +				dln2_adc_set_chan_enabled(dln2, j, false);
> +			mutex_unlock(&dln2->mutex);
> +			dev_err(&dln2->pdev->dev,
> +				"Unable to enable ADC channel %d\n", i);
> +			return -EBUSY;
> +		}
> +	}
> +
> +	dln2_adc_update_demux(dln2);
> +
> +	mutex_unlock(&dln2->mutex);
> +
> +	return 0;
> +}
> +
> +#define DLN2_ADC_CHAN(lval, idx) {					\
> +	lval.type = IIO_VOLTAGE;					\
> +	lval.channel = idx;						\
> +	lval.indexed = 1;						\
> +	lval.info_mask_separate = BIT(IIO_CHAN_INFO_RAW);		\
> +	lval.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |	\
> +				       BIT(IIO_CHAN_INFO_SAMP_FREQ);	\
> +	lval.scan_index = idx;						\
> +	lval.scan_type.sign = 'u';					\
> +	lval.scan_type.realbits = DLN2_ADC_DATA_BITS;			\
> +	lval.scan_type.storagebits = 16;				\
> +	lval.scan_type.endianness = IIO_LE;				\
> +}
> +
> +/* Assignment version of IIO_CHAN_SOFT_TIMESTAMP */
> +#define IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(lval, _si) {	\
> +	lval.type = IIO_TIMESTAMP;			\
> +	lval.channel = -1;				\
> +	lval.scan_index = _si;				\
> +	lval.scan_type.sign = 's';			\
> +	lval.scan_type.realbits = 64;			\
> +	lval.scan_type.storagebits = 64;		\
> +}
> +
> +static const struct iio_info dln2_adc_info = {
> +	.read_raw = dln2_adc_read_raw,
> +	.write_raw = dln2_adc_write_raw,
> +	.update_scan_mode = dln2_update_scan_mode,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct {
> +		__le16 values[DLN2_ADC_MAX_CHANNELS];
> +		int64_t timestamp_space;
> +	} data;
> +	struct dln2_adc_get_all_vals dev_data;
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +	const struct dln2_adc_demux_table *t;
> +	int ret, i;
> +
> +	mutex_lock(&dln2->mutex);
> +	ret = dln2_adc_read_all(dln2, &dev_data);
> +	mutex_unlock(&dln2->mutex);
> +	if (ret < 0)
> +		goto done;
> +
> +	/* Demux operation */
> +	for (i = 0; i < dln2->demux_count; ++i) {
> +		t = &dln2->demux[i];
> +		memcpy((void *)data.values + t->to,
> +		       (void *)dev_data.values + t->from, t->length);
> +	}
> +
> +	/* Zero padding space between values and timestamp */
> +	if (dln2->ts_pad_length)
> +		memset((void *)data.values + dln2->ts_pad_offset,
> +		       0, dln2->ts_pad_length);
> +
> +	iio_push_to_buffers_with_timestamp(indio_dev, &data,
> +					   iio_get_time_ns(indio_dev));
> +
> +done:
> +	iio_trigger_notify_done(indio_dev->trig);
> +	return IRQ_HANDLED;
> +}
> +
> +static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
> +{
> +	int ret;
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +	u16 conflict;
> +	unsigned int trigger_chan;
> +
> +	mutex_lock(&dln2->mutex);
> +
> +	/* Enable ADC */
> +	ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
> +	if (ret < 0) {
> +		mutex_unlock(&dln2->mutex);
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		if (conflict) {
> +			dev_err(&dln2->pdev->dev,
> +				"ADC pins conflict with mask %04X\n",
> +				(int)conflict);
> +			ret = -EBUSY;
> +		}
> +		return ret;
> +	}
> +
> +	/* Assign trigger channel based on first enabled channel */
> +	trigger_chan = find_first_bit(indio_dev->active_scan_mask,
> +				      indio_dev->masklength);
> +	if (trigger_chan < DLN2_ADC_MAX_CHANNELS) {
> +		dln2->trigger_chan = trigger_chan;
> +		ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan,
> +					       dln2->sample_period);
> +		mutex_unlock(&dln2->mutex);
> +		if (ret < 0) {
> +			dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +			return ret;
> +		}
> +	} else {
> +		dln2->trigger_chan = -1;
> +		mutex_unlock(&dln2->mutex);
> +	}
> +
> +	return iio_triggered_buffer_postenable(indio_dev);
> +}
> +
> +static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
> +{
> +	int ret;
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +	mutex_lock(&dln2->mutex);
> +
> +	/* Disable trigger channel */
> +	if (dln2->trigger_chan != -1) {
> +		dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0);
> +		dln2->trigger_chan = -1;
> +	}
> +
> +	/* Disable ADC */
> +	ret = dln2_adc_set_port_enabled(dln2, false, NULL);
> +
> +	mutex_unlock(&dln2->mutex);
> +	if (ret < 0) {
> +		dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
> +		return ret;
> +	}
> +
> +	return iio_triggered_buffer_predisable(indio_dev);
> +}
> +
> +static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
> +	.postenable = dln2_adc_triggered_buffer_postenable,
> +	.predisable = dln2_adc_triggered_buffer_predisable,
> +};
> +
> +static void dln2_adc_event(struct platform_device *pdev, u16 echo,
> +			   const void *data, int len)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +	struct dln2_adc *dln2 = iio_priv(indio_dev);
> +
> +	/* Called via URB completion handler */
> +	iio_trigger_poll(dln2->trig);
> +}
> +
> +static const struct iio_trigger_ops dln2_adc_trigger_ops = {
> +	.owner = THIS_MODULE,
> +};
> +
> +static int dln2_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct dln2_adc *dln2;
> +	struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
> +	struct iio_dev *indio_dev;
> +	int i, ret, chans;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2));
> +	if (!indio_dev) {
> +		dev_err(dev, "failed allocating iio device\n");
> +		return -ENOMEM;
> +	}
> +
> +	dln2 = iio_priv(indio_dev);
> +	dln2->pdev = pdev;
> +	dln2->port = pdata->port;
> +	dln2->trigger_chan = -1;
> +	mutex_init(&dln2->mutex);
> +
> +	platform_set_drvdata(pdev, indio_dev);
> +
> +	ret = dln2_adc_set_port_resolution(dln2);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to set ADC resolution to 10 bits\n");
> +		return ret;
> +	}
> +
> +	chans = dln2_adc_get_chan_count(dln2);
> +	if (chans < 0) {
> +		dev_err(dev, "failed to get channel count: %d\n", chans);
> +		return chans;
> +	}
> +	if (chans > DLN2_ADC_MAX_CHANNELS) {
> +		chans = DLN2_ADC_MAX_CHANNELS;
> +		dev_warn(dev, "clamping channels to %d\n",
> +			 DLN2_ADC_MAX_CHANNELS);
> +	}
> +
> +	for (i = 0; i < chans; ++i)
> +		DLN2_ADC_CHAN(dln2->iio_channels[i], i)
> +	IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(dln2->iio_channels[i], i);
> +
> +	indio_dev->name = DLN2_ADC_MOD_NAME;
> +	indio_dev->dev.parent = dev;
> +	indio_dev->info = &dln2_adc_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->channels = dln2->iio_channels;
> +	indio_dev->num_channels = chans + 1;
> +	indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
> +
> +	dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
> +					    indio_dev->name, indio_dev->id);
> +	if (!dln2->trig) {
> +		dev_err(dev, "failed to allocate trigger\n");
> +		return -ENOMEM;
> +	}
> +	dln2->trig->ops = &dln2_adc_trigger_ops;
> +	iio_trigger_set_drvdata(dln2->trig, dln2);
> +	devm_iio_trigger_register(dev, dln2->trig);
> +	iio_trigger_set_immutable(indio_dev, dln2->trig);
> +
> +	ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> +					      dln2_adc_trigger_h,
> +					      &dln2_adc_buffer_setup_ops);
> +	if (ret) {
> +		dev_err(dev, "failed to allocate triggered buffer: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
> +				     dln2_adc_event);
> +	if (ret) {
> +		dev_err(dev, "failed to setup DLN2 periodic event: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret) {
> +		dev_err(dev, "failed to register iio device: %d\n", ret);
> +		goto unregister_event;
> +	}
> +
> +	return ret;
> +
> +unregister_event:
> +	dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
> +
> +	return ret;
> +}
> +
> +static int dln2_adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +
> +	iio_device_unregister(indio_dev);
> +	dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
> +	return 0;
> +}
> +
> +static struct platform_driver dln2_adc_driver = {
> +	.driver.name	= DLN2_ADC_MOD_NAME,
> +	.probe		= dln2_adc_probe,
> +	.remove		= dln2_adc_remove,
> +};
> +
> +module_platform_driver(dln2_adc_driver);
> +
> +MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
> +MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:dln2-adc");

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

* Re: [PATCH v6 1/3] iio: adc: Add support for DLN2 ADC
  2017-07-09 17:12   ` Jonathan Cameron
@ 2017-07-10 20:04     ` Jack Andersen
  0 siblings, 0 replies; 23+ messages in thread
From: Jack Andersen @ 2017-07-10 20:04 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-iio, linux-kernel, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Lee Jones

It's a pleasure to contribute! The iio interface has simplified our
robotics telemetry solution considerably; no more awkward setup. A
simple udev script is ideal for bringing the buffer online.

On 9 July 2017 at 07:12, Jonathan Cameron <jic23@kernel.org> wrote:
> On Thu,  6 Jul 2017 12:39:33 -1000
> Jack Andersen <jackoalan@gmail.com> wrote:
>
>> This patch adds support for Diolan DLN2 ADC via IIO's ADC interface.
>> ADC is the fourth and final component of the DLN2 for the kernel.
>>
>> Signed-off-by: Jack Andersen <jackoalan@gmail.com>
> Applied to the togreg branch of iio.git.  Initially pushed out as
> testing for the autobuilders to play with it.
>
> Thanks for your persistence with this.  Nice to have a driver
> covering this element of what is a very handy bit of kit!
>
> Jonathan
>> ---
>> Changes in v2:
>> - Address Peter's remarks; bringing consistency to C99 syntax, dev_dbg usage,
>>   and error handling.
>> - Remove probe/remove logging.
>> - Utilize stack-allocated buffer for pushing into IIO.
>> - Add timestamp channel to IIO spec.
>>
>> Suggested-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
>> ---
>> Changes in v3:
>> - Remove lazy channel enabling; integrating with update_channel_mode instead.
>> - Implement memcpy demux table similar to subsystem core.
>> - Use iio_triggered_buffer_setup to create buffer.
>>
>> Suggested-by: Jonathan Cameron <jic23@kernel.org>
>> ---
>> Changes in v4:
>> - Remove unrelated, erronous change to lsgpio.c
>> ---
>> Changes in v5:
>> - Use of devm functions wherever possible.
>> - Use iio_device_claim_direct_mode.
>> - Variable declaration more consistent with C style.
>> - MFD modifications in separate patch.
>> Suggested-by: Jonathan Cameron <jic23@kernel.org>
>> ---
>> Changes in v6:
>> - Don't use devm_iio_device_register to ensure correct unwind order.
>> Suggested-by: Lee Jones <lee.jones@linaro.org>
>> ---
>>  drivers/iio/adc/Kconfig    |   9 +
>>  drivers/iio/adc/Makefile   |   1 +
>>  drivers/iio/adc/dln2-adc.c | 722 +++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 732 insertions(+)
>>  create mode 100644 drivers/iio/adc/dln2-adc.c
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 614fa41..196d2ce 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -239,6 +239,15 @@ config DA9150_GPADC
>>         To compile this driver as a module, choose M here: the module will be
>>         called berlin2-adc.
>>
>> +config DLN2_ADC
>> +     tristate "Diolan DLN-2 ADC driver support"
>> +     depends on MFD_DLN2
>> +     help
>> +       Say yes here to build support for Diolan DLN-2 ADC.
>> +
>> +       This driver can also be built as a module. If so, the module will be
>> +       called adc_dln2.
>> +
>>  config ENVELOPE_DETECTOR
>>       tristate "Envelope detector using a DAC and a comparator"
>>       depends on OF
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index b546736a..7ea0bf3 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -24,6 +24,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>>  obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>>  obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
>>  obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>> +obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
>>  obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
>>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>>  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
>> diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
>> new file mode 100644
>> index 0000000..ab8d6ae
>> --- /dev/null
>> +++ b/drivers/iio/adc/dln2-adc.c
>> @@ -0,0 +1,722 @@
>> +/*
>> + * Driver for the Diolan DLN-2 USB-ADC adapter
>> + *
>> + * Copyright (c) 2017 Jack Andersen
>> + *
>> + * 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, version 2.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/types.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/mfd/dln2.h>
>> +
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/iio/trigger.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/kfifo_buf.h>
>> +
>> +#define DLN2_ADC_MOD_NAME "dln2-adc"
>> +
>> +#define DLN2_ADC_ID             0x06
>> +
>> +#define DLN2_ADC_GET_CHANNEL_COUNT   DLN2_CMD(0x01, DLN2_ADC_ID)
>> +#define DLN2_ADC_ENABLE                      DLN2_CMD(0x02, DLN2_ADC_ID)
>> +#define DLN2_ADC_DISABLE             DLN2_CMD(0x03, DLN2_ADC_ID)
>> +#define DLN2_ADC_CHANNEL_ENABLE              DLN2_CMD(0x05, DLN2_ADC_ID)
>> +#define DLN2_ADC_CHANNEL_DISABLE     DLN2_CMD(0x06, DLN2_ADC_ID)
>> +#define DLN2_ADC_SET_RESOLUTION              DLN2_CMD(0x08, DLN2_ADC_ID)
>> +#define DLN2_ADC_CHANNEL_GET_VAL     DLN2_CMD(0x0A, DLN2_ADC_ID)
>> +#define DLN2_ADC_CHANNEL_GET_ALL_VAL DLN2_CMD(0x0B, DLN2_ADC_ID)
>> +#define DLN2_ADC_CHANNEL_SET_CFG     DLN2_CMD(0x0C, DLN2_ADC_ID)
>> +#define DLN2_ADC_CHANNEL_GET_CFG     DLN2_CMD(0x0D, DLN2_ADC_ID)
>> +#define DLN2_ADC_CONDITION_MET_EV    DLN2_CMD(0x10, DLN2_ADC_ID)
>> +
>> +#define DLN2_ADC_EVENT_NONE          0
>> +#define DLN2_ADC_EVENT_BELOW         1
>> +#define DLN2_ADC_EVENT_LEVEL_ABOVE   2
>> +#define DLN2_ADC_EVENT_OUTSIDE               3
>> +#define DLN2_ADC_EVENT_INSIDE                4
>> +#define DLN2_ADC_EVENT_ALWAYS                5
>> +
>> +#define DLN2_ADC_MAX_CHANNELS 8
>> +#define DLN2_ADC_DATA_BITS 10
>> +
>> +/*
>> + * Plays similar role to iio_demux_table in subsystem core; except allocated
>> + * in a fixed 8-element array.
>> + */
>> +struct dln2_adc_demux_table {
>> +     unsigned int from;
>> +     unsigned int to;
>> +     unsigned int length;
>> +};
>> +
>> +struct dln2_adc {
>> +     struct platform_device *pdev;
>> +     struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1];
>> +     int port, trigger_chan;
>> +     struct iio_trigger *trig;
>> +     struct mutex mutex;
>> +     /* Cached sample period in milliseconds */
>> +     unsigned int sample_period;
>> +     /* Demux table */
>> +     unsigned int demux_count;
>> +     struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS];
>> +     /* Precomputed timestamp padding offset and length */
>> +     unsigned int ts_pad_offset, ts_pad_length;
>> +};
>> +
>> +struct dln2_adc_port_chan {
>> +     u8 port;
>> +     u8 chan;
>> +};
>> +
>> +struct dln2_adc_get_all_vals {
>> +     __le16 channel_mask;
>> +     __le16 values[DLN2_ADC_MAX_CHANNELS];
>> +};
>> +
>> +static void dln2_adc_add_demux(struct dln2_adc *dln2,
>> +     unsigned int in_loc, unsigned int out_loc,
>> +     unsigned int length)
>> +{
>> +     struct dln2_adc_demux_table *p = dln2->demux_count ?
>> +             &dln2->demux[dln2->demux_count - 1] : NULL;
>> +
>> +     if (p && p->from + p->length == in_loc &&
>> +             p->to + p->length == out_loc) {
>> +             p->length += length;
>> +     } else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) {
>> +             p = &dln2->demux[dln2->demux_count++];
>> +             p->from = in_loc;
>> +             p->to = out_loc;
>> +             p->length = length;
>> +     }
>> +}
>> +
>> +static void dln2_adc_update_demux(struct dln2_adc *dln2)
>> +{
>> +     int in_ind = -1, out_ind;
>> +     unsigned int in_loc = 0, out_loc = 0;
>> +     struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
>> +
>> +     /* Clear out any old demux */
>> +     dln2->demux_count = 0;
>> +
>> +     /* Optimize all 8-channels case */
>> +     if (indio_dev->masklength &&
>> +         (*indio_dev->active_scan_mask & 0xff) == 0xff) {
>> +             dln2_adc_add_demux(dln2, 0, 0, 16);
>> +             dln2->ts_pad_offset = 0;
>> +             dln2->ts_pad_length = 0;
>> +             return;
>> +     }
>> +
>> +     /* Build demux table from fixed 8-channels to active_scan_mask */
>> +     for_each_set_bit(out_ind,
>> +                      indio_dev->active_scan_mask,
>> +                      indio_dev->masklength) {
>> +             /* Handle timestamp separately */
>> +             if (out_ind == DLN2_ADC_MAX_CHANNELS)
>> +                     break;
>> +             for (++in_ind; in_ind != out_ind; ++in_ind)
>> +                     in_loc += 2;
>> +             dln2_adc_add_demux(dln2, in_loc, out_loc, 2);
>> +             out_loc += 2;
>> +             in_loc += 2;
>> +     }
>> +
>> +     if (indio_dev->scan_timestamp) {
>> +             size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1;
>> +
>> +             dln2->ts_pad_offset = out_loc;
>> +             dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc;
>> +     } else {
>> +             dln2->ts_pad_offset = 0;
>> +             dln2->ts_pad_length = 0;
>> +     }
>> +}
>> +
>> +static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
>> +{
>> +     int ret;
>> +     u8 port = dln2->port;
>> +     u8 count;
>> +     int olen = sizeof(count);
>> +
>> +     ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
>> +                         &port, sizeof(port), &count, &olen);
>> +     if (ret < 0) {
>> +             dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> +             return ret;
>> +     }
>> +     if (olen < sizeof(count))
>> +             return -EPROTO;
>> +
>> +     return count;
>> +}
>> +
>> +static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
>> +{
>> +     int ret;
>> +     struct dln2_adc_port_chan port_chan = {
>> +             .port = dln2->port,
>> +             .chan = DLN2_ADC_DATA_BITS,
>> +     };
>> +
>> +     ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
>> +                            &port_chan, sizeof(port_chan));
>> +     if (ret < 0)
>> +             dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> +
>> +     return ret;
>> +}
>> +
>> +static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
>> +                                  int channel, bool enable)
>> +{
>> +     int ret;
>> +     struct dln2_adc_port_chan port_chan = {
>> +             .port = dln2->port,
>> +             .chan = channel,
>> +     };
>> +     u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
>> +
>> +     ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
>> +     if (ret < 0)
>> +             dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> +
>> +     return ret;
>> +}
>> +
>> +static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable,
>> +                                  u16 *conflict_out)
>> +{
>> +     int ret;
>> +     u8 port = dln2->port;
>> +     __le16 conflict;
>> +     int olen = sizeof(conflict);
>> +     u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
>> +
>> +     if (conflict_out)
>> +             *conflict_out = 0;
>> +
>> +     ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
>> +                         &conflict, &olen);
>> +     if (ret < 0) {
>> +             dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
>> +                     __func__, (int)enable);
>> +             if (conflict_out && enable && olen >= sizeof(conflict))
>> +                     *conflict_out = le16_to_cpu(conflict);
>> +             return ret;
>> +     }
>> +     if (enable && olen < sizeof(conflict))
>> +             return -EPROTO;
>> +
>> +     return ret;
>> +}
>> +
>> +static int dln2_adc_set_chan_period(struct dln2_adc *dln2,
>> +     unsigned int channel, unsigned int period)
>> +{
>> +     int ret;
>> +     struct {
>> +             struct dln2_adc_port_chan port_chan;
>> +             __u8 type;
>> +             __le16 period;
>> +             __le16 low;
>> +             __le16 high;
>> +     } __packed set_cfg = {
>> +             .port_chan.port = dln2->port,
>> +             .port_chan.chan = channel,
>> +             .type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
>> +             .period = cpu_to_le16(period)
>> +     };
>> +
>> +     ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
>> +                            &set_cfg, sizeof(set_cfg));
>> +     if (ret < 0)
>> +             dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> +
>> +     return ret;
>> +}
>> +
>> +static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
>> +{
>> +     int ret, i;
>> +     struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
>> +     u16 conflict;
>> +     __le16 value;
>> +     int olen = sizeof(value);
>> +     struct dln2_adc_port_chan port_chan = {
>> +             .port = dln2->port,
>> +             .chan = channel,
>> +     };
>> +
>> +     ret = iio_device_claim_direct_mode(indio_dev);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     ret = dln2_adc_set_chan_enabled(dln2, channel, true);
>> +     if (ret < 0)
>> +             goto release_direct;
>> +
>> +     ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
>> +     if (ret < 0) {
>> +             if (conflict) {
>> +                     dev_err(&dln2->pdev->dev,
>> +                             "ADC pins conflict with mask %04X\n",
>> +                             (int)conflict);
>> +                     ret = -EBUSY;
>> +             }
>> +             goto disable_chan;
>> +     }
>> +
>> +     /*
>> +      * Call GET_VAL twice due to initial zero-return immediately after
>> +      * enabling channel.
>> +      */
>> +     for (i = 0; i < 2; ++i) {
>> +             ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
>> +                                 &port_chan, sizeof(port_chan),
>> +                                 &value, &olen);
>> +             if (ret < 0) {
>> +                     dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> +                     goto disable_port;
>> +             }
>> +             if (olen < sizeof(value)) {
>> +                     ret = -EPROTO;
>> +                     goto disable_port;
>> +             }
>> +     }
>> +
>> +     ret = le16_to_cpu(value);
>> +
>> +disable_port:
>> +     dln2_adc_set_port_enabled(dln2, false, NULL);
>> +disable_chan:
>> +     dln2_adc_set_chan_enabled(dln2, channel, false);
>> +release_direct:
>> +     iio_device_release_direct_mode(indio_dev);
>> +
>> +     return ret;
>> +}
>> +
>> +static int dln2_adc_read_all(struct dln2_adc *dln2,
>> +                          struct dln2_adc_get_all_vals *get_all_vals)
>> +{
>> +     int ret;
>> +     __u8 port = dln2->port;
>> +     int olen = sizeof(*get_all_vals);
>> +
>> +     ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
>> +                         &port, sizeof(port), get_all_vals, &olen);
>> +     if (ret < 0) {
>> +             dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> +             return ret;
>> +     }
>> +     if (olen < sizeof(*get_all_vals))
>> +             return -EPROTO;
>> +
>> +     return ret;
>> +}
>> +
>> +static int dln2_adc_read_raw(struct iio_dev *indio_dev,
>> +                          struct iio_chan_spec const *chan,
>> +                          int *val,
>> +                          int *val2,
>> +                          long mask)
>> +{
>> +     int ret;
>> +     unsigned int microhertz;
>> +     struct dln2_adc *dln2 = iio_priv(indio_dev);
>> +
>> +     switch (mask) {
>> +     case IIO_CHAN_INFO_RAW:
>> +             mutex_lock(&dln2->mutex);
>> +             ret = dln2_adc_read(dln2, chan->channel);
>> +             mutex_unlock(&dln2->mutex);
>> +
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             *val = ret;
>> +             return IIO_VAL_INT;
>> +
>> +     case IIO_CHAN_INFO_SCALE:
>> +             /*
>> +              * Voltage reference is fixed at 3.3v
>> +              *  3.3 / (1 << 10) * 1000000000
>> +              */
>> +             *val = 0;
>> +             *val2 = 3222656;
>> +             return IIO_VAL_INT_PLUS_NANO;
>> +
>> +     case IIO_CHAN_INFO_SAMP_FREQ:
>> +             if (dln2->sample_period) {
>> +                     microhertz = 1000000000 / dln2->sample_period;
>> +                     *val = microhertz / 1000000;
>> +                     *val2 = microhertz % 1000000;
>> +             } else {
>> +                     *val = 0;
>> +                     *val2 = 0;
>> +             }
>> +
>> +             return IIO_VAL_INT_PLUS_MICRO;
>> +
>> +     default:
>> +             return -EINVAL;
>> +     }
>> +}
>> +
>> +static int dln2_adc_write_raw(struct iio_dev *indio_dev,
>> +                           struct iio_chan_spec const *chan,
>> +                           int val,
>> +                           int val2,
>> +                           long mask)
>> +{
>> +     int ret;
>> +     unsigned int microhertz;
>> +     struct dln2_adc *dln2 = iio_priv(indio_dev);
>> +
>> +     switch (mask) {
>> +     case IIO_CHAN_INFO_SAMP_FREQ:
>> +             microhertz = 1000000 * val + val2;
>> +
>> +             mutex_lock(&dln2->mutex);
>> +
>> +             dln2->sample_period =
>> +                     microhertz ? 1000000000 / microhertz : UINT_MAX;
>> +             if (dln2->sample_period > 65535) {
>> +                     dln2->sample_period = 65535;
>> +                     dev_warn(&dln2->pdev->dev,
>> +                              "clamping period to 65535ms\n");
>> +             }
>> +
>> +             /*
>> +              * The first requested channel is arbitrated as a shared
>> +              * trigger source, so only one event is registered with the
>> +              * DLN. The event handler will then read all enabled channel
>> +              * values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
>> +              * synchronization between ADC readings.
>> +              */
>> +             if (dln2->trigger_chan != -1)
>> +                     ret = dln2_adc_set_chan_period(dln2,
>> +                             dln2->trigger_chan, dln2->sample_period);
>> +             else
>> +                     ret = 0;
>> +
>> +             mutex_unlock(&dln2->mutex);
>> +
>> +             return ret;
>> +
>> +     default:
>> +             return -EINVAL;
>> +     }
>> +}
>> +
>> +static int dln2_update_scan_mode(struct iio_dev *indio_dev,
>> +                              const unsigned long *scan_mask)
>> +{
>> +     struct dln2_adc *dln2 = iio_priv(indio_dev);
>> +     int chan_count = indio_dev->num_channels - 1;
>> +     int ret, i, j;
>> +
>> +     mutex_lock(&dln2->mutex);
>> +
>> +     for (i = 0; i < chan_count; ++i) {
>> +             ret = dln2_adc_set_chan_enabled(dln2, i,
>> +                                             test_bit(i, scan_mask));
>> +             if (ret < 0) {
>> +                     for (j = 0; j < i; ++j)
>> +                             dln2_adc_set_chan_enabled(dln2, j, false);
>> +                     mutex_unlock(&dln2->mutex);
>> +                     dev_err(&dln2->pdev->dev,
>> +                             "Unable to enable ADC channel %d\n", i);
>> +                     return -EBUSY;
>> +             }
>> +     }
>> +
>> +     dln2_adc_update_demux(dln2);
>> +
>> +     mutex_unlock(&dln2->mutex);
>> +
>> +     return 0;
>> +}
>> +
>> +#define DLN2_ADC_CHAN(lval, idx) {                                   \
>> +     lval.type = IIO_VOLTAGE;                                        \
>> +     lval.channel = idx;                                             \
>> +     lval.indexed = 1;                                               \
>> +     lval.info_mask_separate = BIT(IIO_CHAN_INFO_RAW);               \
>> +     lval.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |       \
>> +                                    BIT(IIO_CHAN_INFO_SAMP_FREQ);    \
>> +     lval.scan_index = idx;                                          \
>> +     lval.scan_type.sign = 'u';                                      \
>> +     lval.scan_type.realbits = DLN2_ADC_DATA_BITS;                   \
>> +     lval.scan_type.storagebits = 16;                                \
>> +     lval.scan_type.endianness = IIO_LE;                             \
>> +}
>> +
>> +/* Assignment version of IIO_CHAN_SOFT_TIMESTAMP */
>> +#define IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(lval, _si) {  \
>> +     lval.type = IIO_TIMESTAMP;                      \
>> +     lval.channel = -1;                              \
>> +     lval.scan_index = _si;                          \
>> +     lval.scan_type.sign = 's';                      \
>> +     lval.scan_type.realbits = 64;                   \
>> +     lval.scan_type.storagebits = 64;                \
>> +}
>> +
>> +static const struct iio_info dln2_adc_info = {
>> +     .read_raw = dln2_adc_read_raw,
>> +     .write_raw = dln2_adc_write_raw,
>> +     .update_scan_mode = dln2_update_scan_mode,
>> +     .driver_module = THIS_MODULE,
>> +};
>> +
>> +static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
>> +{
>> +     struct iio_poll_func *pf = p;
>> +     struct iio_dev *indio_dev = pf->indio_dev;
>> +     struct {
>> +             __le16 values[DLN2_ADC_MAX_CHANNELS];
>> +             int64_t timestamp_space;
>> +     } data;
>> +     struct dln2_adc_get_all_vals dev_data;
>> +     struct dln2_adc *dln2 = iio_priv(indio_dev);
>> +     const struct dln2_adc_demux_table *t;
>> +     int ret, i;
>> +
>> +     mutex_lock(&dln2->mutex);
>> +     ret = dln2_adc_read_all(dln2, &dev_data);
>> +     mutex_unlock(&dln2->mutex);
>> +     if (ret < 0)
>> +             goto done;
>> +
>> +     /* Demux operation */
>> +     for (i = 0; i < dln2->demux_count; ++i) {
>> +             t = &dln2->demux[i];
>> +             memcpy((void *)data.values + t->to,
>> +                    (void *)dev_data.values + t->from, t->length);
>> +     }
>> +
>> +     /* Zero padding space between values and timestamp */
>> +     if (dln2->ts_pad_length)
>> +             memset((void *)data.values + dln2->ts_pad_offset,
>> +                    0, dln2->ts_pad_length);
>> +
>> +     iio_push_to_buffers_with_timestamp(indio_dev, &data,
>> +                                        iio_get_time_ns(indio_dev));
>> +
>> +done:
>> +     iio_trigger_notify_done(indio_dev->trig);
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
>> +{
>> +     int ret;
>> +     struct dln2_adc *dln2 = iio_priv(indio_dev);
>> +     u16 conflict;
>> +     unsigned int trigger_chan;
>> +
>> +     mutex_lock(&dln2->mutex);
>> +
>> +     /* Enable ADC */
>> +     ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
>> +     if (ret < 0) {
>> +             mutex_unlock(&dln2->mutex);
>> +             dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> +             if (conflict) {
>> +                     dev_err(&dln2->pdev->dev,
>> +                             "ADC pins conflict with mask %04X\n",
>> +                             (int)conflict);
>> +                     ret = -EBUSY;
>> +             }
>> +             return ret;
>> +     }
>> +
>> +     /* Assign trigger channel based on first enabled channel */
>> +     trigger_chan = find_first_bit(indio_dev->active_scan_mask,
>> +                                   indio_dev->masklength);
>> +     if (trigger_chan < DLN2_ADC_MAX_CHANNELS) {
>> +             dln2->trigger_chan = trigger_chan;
>> +             ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan,
>> +                                            dln2->sample_period);
>> +             mutex_unlock(&dln2->mutex);
>> +             if (ret < 0) {
>> +                     dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> +                     return ret;
>> +             }
>> +     } else {
>> +             dln2->trigger_chan = -1;
>> +             mutex_unlock(&dln2->mutex);
>> +     }
>> +
>> +     return iio_triggered_buffer_postenable(indio_dev);
>> +}
>> +
>> +static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
>> +{
>> +     int ret;
>> +     struct dln2_adc *dln2 = iio_priv(indio_dev);
>> +
>> +     mutex_lock(&dln2->mutex);
>> +
>> +     /* Disable trigger channel */
>> +     if (dln2->trigger_chan != -1) {
>> +             dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0);
>> +             dln2->trigger_chan = -1;
>> +     }
>> +
>> +     /* Disable ADC */
>> +     ret = dln2_adc_set_port_enabled(dln2, false, NULL);
>> +
>> +     mutex_unlock(&dln2->mutex);
>> +     if (ret < 0) {
>> +             dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
>> +             return ret;
>> +     }
>> +
>> +     return iio_triggered_buffer_predisable(indio_dev);
>> +}
>> +
>> +static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
>> +     .postenable = dln2_adc_triggered_buffer_postenable,
>> +     .predisable = dln2_adc_triggered_buffer_predisable,
>> +};
>> +
>> +static void dln2_adc_event(struct platform_device *pdev, u16 echo,
>> +                        const void *data, int len)
>> +{
>> +     struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>> +     struct dln2_adc *dln2 = iio_priv(indio_dev);
>> +
>> +     /* Called via URB completion handler */
>> +     iio_trigger_poll(dln2->trig);
>> +}
>> +
>> +static const struct iio_trigger_ops dln2_adc_trigger_ops = {
>> +     .owner = THIS_MODULE,
>> +};
>> +
>> +static int dln2_adc_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct dln2_adc *dln2;
>> +     struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
>> +     struct iio_dev *indio_dev;
>> +     int i, ret, chans;
>> +
>> +     indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2));
>> +     if (!indio_dev) {
>> +             dev_err(dev, "failed allocating iio device\n");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     dln2 = iio_priv(indio_dev);
>> +     dln2->pdev = pdev;
>> +     dln2->port = pdata->port;
>> +     dln2->trigger_chan = -1;
>> +     mutex_init(&dln2->mutex);
>> +
>> +     platform_set_drvdata(pdev, indio_dev);
>> +
>> +     ret = dln2_adc_set_port_resolution(dln2);
>> +     if (ret < 0) {
>> +             dev_err(dev, "failed to set ADC resolution to 10 bits\n");
>> +             return ret;
>> +     }
>> +
>> +     chans = dln2_adc_get_chan_count(dln2);
>> +     if (chans < 0) {
>> +             dev_err(dev, "failed to get channel count: %d\n", chans);
>> +             return chans;
>> +     }
>> +     if (chans > DLN2_ADC_MAX_CHANNELS) {
>> +             chans = DLN2_ADC_MAX_CHANNELS;
>> +             dev_warn(dev, "clamping channels to %d\n",
>> +                      DLN2_ADC_MAX_CHANNELS);
>> +     }
>> +
>> +     for (i = 0; i < chans; ++i)
>> +             DLN2_ADC_CHAN(dln2->iio_channels[i], i)
>> +     IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(dln2->iio_channels[i], i);
>> +
>> +     indio_dev->name = DLN2_ADC_MOD_NAME;
>> +     indio_dev->dev.parent = dev;
>> +     indio_dev->info = &dln2_adc_info;
>> +     indio_dev->modes = INDIO_DIRECT_MODE;
>> +     indio_dev->channels = dln2->iio_channels;
>> +     indio_dev->num_channels = chans + 1;
>> +     indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
>> +
>> +     dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
>> +                                         indio_dev->name, indio_dev->id);
>> +     if (!dln2->trig) {
>> +             dev_err(dev, "failed to allocate trigger\n");
>> +             return -ENOMEM;
>> +     }
>> +     dln2->trig->ops = &dln2_adc_trigger_ops;
>> +     iio_trigger_set_drvdata(dln2->trig, dln2);
>> +     devm_iio_trigger_register(dev, dln2->trig);
>> +     iio_trigger_set_immutable(indio_dev, dln2->trig);
>> +
>> +     ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
>> +                                           dln2_adc_trigger_h,
>> +                                           &dln2_adc_buffer_setup_ops);
>> +     if (ret) {
>> +             dev_err(dev, "failed to allocate triggered buffer: %d\n", ret);
>> +             return ret;
>> +     }
>> +
>> +     ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
>> +                                  dln2_adc_event);
>> +     if (ret) {
>> +             dev_err(dev, "failed to setup DLN2 periodic event: %d\n", ret);
>> +             return ret;
>> +     }
>> +
>> +     ret = iio_device_register(indio_dev);
>> +     if (ret) {
>> +             dev_err(dev, "failed to register iio device: %d\n", ret);
>> +             goto unregister_event;
>> +     }
>> +
>> +     return ret;
>> +
>> +unregister_event:
>> +     dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
>> +
>> +     return ret;
>> +}
>> +
>> +static int dln2_adc_remove(struct platform_device *pdev)
>> +{
>> +     struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>> +
>> +     iio_device_unregister(indio_dev);
>> +     dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
>> +     return 0;
>> +}
>> +
>> +static struct platform_driver dln2_adc_driver = {
>> +     .driver.name    = DLN2_ADC_MOD_NAME,
>> +     .probe          = dln2_adc_probe,
>> +     .remove         = dln2_adc_remove,
>> +};
>> +
>> +module_platform_driver(dln2_adc_driver);
>> +
>> +MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
>> +MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:dln2-adc");
>

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

end of thread, other threads:[~2017-07-10 20:04 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <b5ede0cc2cb36695b86edf44c0dddbc4736a6420>
2017-06-22 22:31 ` [PATCH v2] iio: adc: Add support for DLN2 ADC Jack Andersen
2017-06-23  7:25   ` Peter Meerwald-Stadler
     [not found]     ` <20170624203801.7e982ad0@kernel.org>
     [not found]       ` <CAPHBK3ZeVFVtJgsNQhqhGPkiKTL4mi6h0nLi4Jjm6BUHM5u96Q@mail.gmail.com>
2017-06-25 15:58         ` Jonathan Cameron
2017-06-25 17:06           ` Jack Andersen
2017-06-26 16:51             ` Jonathan Cameron
2017-06-28  0:53 ` [PATCH v3] " Jack Andersen
2017-06-28  0:57   ` Jack Andersen
2017-06-28  1:05 ` [PATCH v4] " Jack Andersen
2017-06-28  1:18   ` Jack Andersen
2017-07-01 10:22     ` Jonathan Cameron
2017-07-01 11:57   ` Jonathan Cameron
2017-07-05 21:12 ` [PATCH v5 1/3] " Jack Andersen
2017-07-06 11:37   ` Jonathan Cameron
2017-07-05 21:12 ` [PATCH v5 2/3] mfd: dln2: Add cell for initializing " Jack Andersen
2017-07-06  7:10   ` Lee Jones
2017-07-05 21:12 ` [PATCH v5 3/3] mfd: dln2: Send restart command if HW init fails Jack Andersen
2017-07-05 21:23   ` Jack Andersen
2017-07-06  7:09     ` Lee Jones
2017-07-06 21:01       ` Jack Andersen
2017-07-06 22:39 ` [PATCH v6 1/3] iio: adc: Add support for DLN2 ADC Jack Andersen
2017-07-09 17:12   ` Jonathan Cameron
2017-07-10 20:04     ` Jack Andersen
2017-07-06 22:39 ` [PATCH v6 2/3] mfd: dln2: Add cell for initializing " Jack Andersen

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.