Please ignore this patch, a stray modification to lsgpio.c got in. On 27 June 2017 at 14:53, 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> > --- > 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@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) > 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 > -- 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