On 5 July 2017 22:12:46 BST, Jack Andersen <jackoalan@xxxxxxxxx> 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@xxxxxxxxx> 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@xxxxxxxxxx> >--- >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@xxxxxxxxxx> >--- >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@xxxxxxxxxx> >--- > 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@xxxxxxxxx"); >+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. -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html