On Tue, 27 Jun 2017 15:18:43 -1000 Jack Andersen <jackoalan@xxxxxxxxx> 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@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> > > --- > > 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 > > --- > > 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@xxxxxxxxx"); > > +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@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- 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