Re: [PATCH v6] iio: stx104: Add IIO support for the ADC channels
From: Jonathan Cameron
Date: Sat Sep 03 2016 - 12:18:28 EST
On 29/08/16 21:22, William Breathitt Gray wrote:
> The Apex Embedded Systems STX104 features 16 channels of single-ended (8
> channels of true differential) 16-bit analog input. Differential input
> configuration may be selected via a physical jumper on the device.
> Similarly, input polarity (unipolar/bipolar) is configured via a
> physical jumper on the device.
>
> Input gain selection is available to the user via software, thus
> allowing eight possible input ranges: +-10V, +-5V, +-2.5V, +-1.25V,
> 0 to 10V, 0 to 5V, 0 to 2.5V, and 0 to 1.25V. Four input gain
> configurations are supported: x1, x2, x4, and x8.
>
> This ADC resolution is 16-bits (1/65536 of full scale). Analog input
> samples are taken on software trigger; neither FIFO sampling nor
> interrupt triggering is supported by this driver.
>
> The Apex Embedded Systems STX104 is primarily an analog-to-digital
> converter device. The STX104 IIO driver was initially placed in the DAC
> directory because only the DAC portion of the STX104 was supported at
> the time. Now that ADC support has been added to the STX104 IIO driver,
> the driver should be moved to the more appropriate ADC directory.
>
> Signed-off-by: William Breathitt Gray <vilhelm.gray@xxxxxxxxx>
Very nice.
Applied to the togreg branch of iio.git - initially pushed out
as testing for the autobuilders to play with it.
Thanks,
Jonathan
> ---
> Changes in v6:
> - Use switch-case to branch logic for read_raw/write_raw mask evaluation; this
> should make it clearer when evaluating the various possible code paths
> - IIO_CHAN_INFO_HARDWAREGAIN is used to expose the device gain setting
> - Raw analog input value is returned by IIO_CHAN_INFO_RAW; constants and
> values should be exposed to userspace for processing rather than doing all
> the math within the driver, so IIO_CHAN_INFO_OFFSET and IIO_CHAN_INFO_SCALE
> now expose the relevant offset and scale values necessary for processing
>
> MAINTAINERS | 4 +-
> drivers/iio/adc/Kconfig | 15 ++++
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/{dac => adc}/stx104.c | 153 ++++++++++++++++++++++++++++++++------
> drivers/iio/dac/Kconfig | 10 ---
> drivers/iio/dac/Makefile | 1 -
> 6 files changed, 147 insertions(+), 37 deletions(-)
> rename drivers/iio/{dac => adc}/stx104.c (60%)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ae09eb4..6f0ff72 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -809,11 +809,11 @@ L: alsa-devel@xxxxxxxxxxxxxxxx (moderated for non-subscribers)
> S: Maintained
> F: sound/aoa/
>
> -APEX EMBEDDED SYSTEMS STX104 DAC DRIVER
> +APEX EMBEDDED SYSTEMS STX104 IIO DRIVER
> M: William Breathitt Gray <vilhelm.gray@xxxxxxxxx>
> L: linux-iio@xxxxxxxxxxxxxxx
> S: Maintained
> -F: drivers/iio/dac/stx104.c
> +F: drivers/iio/adc/stx104.c
>
> APM DRIVER
> M: Jiri Kosina <jikos@xxxxxxxxxx>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index e4022fd..36695e8 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -409,6 +409,21 @@ config ROCKCHIP_SARADC
> To compile this driver as a module, choose M here: the
> module will be called rockchip_saradc.
>
> +config STX104
> + tristate "Apex Embedded Systems STX104 driver"
> + depends on X86 && ISA_BUS_API
> + select GPIOLIB
> + help
> + Say yes here to build support for the Apex Embedded Systems STX104
> + integrated analog PC/104 card.
> +
> + This driver supports the 16 channels of single-ended (8 channels of
> + differential) analog inputs, 2 channels of analog output, 4 digital
> + inputs, and 4 digital outputs provided by the STX104.
> +
> + The base port addresses for the devices may be configured via the base
> + array module parameter.
> +
> config TI_ADC081C
> tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
> depends on I2C
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 33254eb..bdd69d6 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -39,6 +39,7 @@ obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
> obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
> obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
> obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
> +obj-$(CONFIG_STX104) += stx104.o
> obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
> obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
> obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
> diff --git a/drivers/iio/dac/stx104.c b/drivers/iio/adc/stx104.c
> similarity index 60%
> rename from drivers/iio/dac/stx104.c
> rename to drivers/iio/adc/stx104.c
> index 792a971..7ca12d5 100644
> --- a/drivers/iio/dac/stx104.c
> +++ b/drivers/iio/adc/stx104.c
> @@ -1,5 +1,5 @@
> /*
> - * DAC driver for the Apex Embedded Systems STX104
> + * IIO driver for the Apex Embedded Systems STX104
> * Copyright (C) 2016 William Breathitt Gray
> *
> * This program is free software; you can redistribute it and/or modify
> @@ -20,19 +20,30 @@
> #include <linux/io.h>
> #include <linux/ioport.h>
> #include <linux/isa.h>
> +#include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/moduleparam.h>
> #include <linux/spinlock.h>
>
> -#define STX104_NUM_CHAN 2
> -
> -#define STX104_CHAN(chan) { \
> +#define STX104_OUT_CHAN(chan) { \
> .type = IIO_VOLTAGE, \
> .channel = chan, \
> .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> .indexed = 1, \
> .output = 1 \
> }
> +#define STX104_IN_CHAN(chan, diff) { \
> + .type = IIO_VOLTAGE, \
> + .channel = chan, \
> + .channel2 = chan, \
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \
> + BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> + .indexed = 1, \
> + .differential = diff \
> +}
> +
> +#define STX104_NUM_OUT_CHAN 2
>
> #define STX104_EXTENT 16
>
> @@ -47,8 +58,8 @@ MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses");
> * @base: base port address of the IIO device
> */
> struct stx104_iio {
> - unsigned chan_out_states[STX104_NUM_CHAN];
> - unsigned base;
> + unsigned int chan_out_states[STX104_NUM_OUT_CHAN];
> + unsigned int base;
> };
>
> /**
> @@ -69,28 +80,95 @@ static int stx104_read_raw(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan, int *val, int *val2, long mask)
> {
> struct stx104_iio *const priv = iio_priv(indio_dev);
> + unsigned int adc_config;
> + int adbu;
> + int gain;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_HARDWAREGAIN:
> + /* get gain configuration */
> + adc_config = inb(priv->base + 11);
> + gain = adc_config & 0x3;
> +
> + *val = 1 << gain;
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_RAW:
> + if (chan->output) {
> + *val = priv->chan_out_states[chan->channel];
> + return IIO_VAL_INT;
> + }
> +
> + /* select ADC channel */
> + outb(chan->channel | (chan->channel << 4), priv->base + 2);
> +
> + /* trigger ADC sample capture and wait for completion */
> + outb(0, priv->base);
> + while (inb(priv->base + 8) & BIT(7));
> +
> + *val = inw(priv->base);
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_OFFSET:
> + /* get ADC bipolar/unipolar configuration */
> + adc_config = inb(priv->base + 11);
> + adbu = !(adc_config & BIT(2));
> +
> + *val = -32768 * adbu;
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_SCALE:
> + /* get ADC bipolar/unipolar and gain configuration */
> + adc_config = inb(priv->base + 11);
> + adbu = !(adc_config & BIT(2));
> + gain = adc_config & 0x3;
> +
> + *val = 5;
> + *val2 = 15 - adbu + gain;
> + return IIO_VAL_FRACTIONAL_LOG2;
> + }
>
> - if (mask != IIO_CHAN_INFO_RAW)
> - return -EINVAL;
> -
> - *val = priv->chan_out_states[chan->channel];
> -
> - return IIO_VAL_INT;
> + return -EINVAL;
> }
>
> static int stx104_write_raw(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan, int val, int val2, long mask)
> {
> struct stx104_iio *const priv = iio_priv(indio_dev);
> - const unsigned chan_addr_offset = 2 * chan->channel;
>
> - if (mask != IIO_CHAN_INFO_RAW)
> + switch (mask) {
> + case IIO_CHAN_INFO_HARDWAREGAIN:
> + /* Only four gain states (x1, x2, x4, x8) */
> + switch (val) {
> + case 1:
> + outb(0, priv->base + 11);
> + break;
> + case 2:
> + outb(1, priv->base + 11);
> + break;
> + case 4:
> + outb(2, priv->base + 11);
> + break;
> + case 8:
> + outb(3, priv->base + 11);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> + case IIO_CHAN_INFO_RAW:
> + if (chan->output) {
> + /* DAC can only accept up to a 16-bit value */
> + if ((unsigned int)val > 65535)
> + return -EINVAL;
> +
> + priv->chan_out_states[chan->channel] = val;
> + outw(val, priv->base + 4 + 2 * chan->channel);
> +
> + return 0;
> + }
> return -EINVAL;
> + }
>
> - priv->chan_out_states[chan->channel] = val;
> - outw(val, priv->base + 4 + chan_addr_offset);
> -
> - return 0;
> + return -EINVAL;
> }
>
> static const struct iio_info stx104_info = {
> @@ -99,9 +177,22 @@ static const struct iio_info stx104_info = {
> .write_raw = stx104_write_raw
> };
>
> -static const struct iio_chan_spec stx104_channels[STX104_NUM_CHAN] = {
> - STX104_CHAN(0),
> - STX104_CHAN(1)
> +/* single-ended input channels configuration */
> +static const struct iio_chan_spec stx104_channels_sing[] = {
> + STX104_OUT_CHAN(0), STX104_OUT_CHAN(1),
> + STX104_IN_CHAN(0, 0), STX104_IN_CHAN(1, 0), STX104_IN_CHAN(2, 0),
> + STX104_IN_CHAN(3, 0), STX104_IN_CHAN(4, 0), STX104_IN_CHAN(5, 0),
> + STX104_IN_CHAN(6, 0), STX104_IN_CHAN(7, 0), STX104_IN_CHAN(8, 0),
> + STX104_IN_CHAN(9, 0), STX104_IN_CHAN(10, 0), STX104_IN_CHAN(11, 0),
> + STX104_IN_CHAN(12, 0), STX104_IN_CHAN(13, 0), STX104_IN_CHAN(14, 0),
> + STX104_IN_CHAN(15, 0)
> +};
> +/* differential input channels configuration */
> +static const struct iio_chan_spec stx104_channels_diff[] = {
> + STX104_OUT_CHAN(0), STX104_OUT_CHAN(1),
> + STX104_IN_CHAN(0, 1), STX104_IN_CHAN(1, 1), STX104_IN_CHAN(2, 1),
> + STX104_IN_CHAN(3, 1), STX104_IN_CHAN(4, 1), STX104_IN_CHAN(5, 1),
> + STX104_IN_CHAN(6, 1), STX104_IN_CHAN(7, 1)
> };
>
> static int stx104_gpio_get_direction(struct gpio_chip *chip,
> @@ -188,13 +279,27 @@ static int stx104_probe(struct device *dev, unsigned int id)
>
> indio_dev->info = &stx104_info;
> indio_dev->modes = INDIO_DIRECT_MODE;
> - indio_dev->channels = stx104_channels;
> - indio_dev->num_channels = STX104_NUM_CHAN;
> +
> + /* determine if differential inputs */
> + if (inb(base[id] + 8) & BIT(5)) {
> + indio_dev->num_channels = ARRAY_SIZE(stx104_channels_diff);
> + indio_dev->channels = stx104_channels_diff;
> + } else {
> + indio_dev->num_channels = ARRAY_SIZE(stx104_channels_sing);
> + indio_dev->channels = stx104_channels_sing;
> + }
> +
> indio_dev->name = dev_name(dev);
>
> priv = iio_priv(indio_dev);
> priv->base = base[id];
>
> + /* configure device for software trigger operation */
> + outb(0, base[id] + 9);
> +
> + /* initialize gain setting to x1 */
> + outb(0, base[id] + 11);
> +
> /* initialize DAC output to 0V */
> outw(0, base[id] + 4);
> outw(0, base[id] + 6);
> @@ -251,5 +356,5 @@ static struct isa_driver stx104_driver = {
> module_isa_driver(stx104_driver, num_stx104);
>
> MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@xxxxxxxxx>");
> -MODULE_DESCRIPTION("Apex Embedded Systems STX104 DAC driver");
> +MODULE_DESCRIPTION("Apex Embedded Systems STX104 IIO driver");
> MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
> index b9f0442..6048aed 100644
> --- a/drivers/iio/dac/Kconfig
> +++ b/drivers/iio/dac/Kconfig
> @@ -254,16 +254,6 @@ config MCP4922
> To compile this driver as a module, choose M here: the module
> will be called mcp4922.
>
> -config STX104
> - tristate "Apex Embedded Systems STX104 DAC driver"
> - depends on X86 && ISA_BUS_API
> - select GPIOLIB
> - help
> - Say yes here to build support for the 2-channel DAC and GPIO on the
> - Apex Embedded Systems STX104 integrated analog PC/104 card. The base
> - port addresses for the devices may be configured via the base array
> - module parameter.
> -
> config VF610_DAC
> tristate "Vybrid vf610 DAC driver"
> depends on OF
> diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
> index b1a1206..e6abef9 100644
> --- a/drivers/iio/dac/Makefile
> +++ b/drivers/iio/dac/Makefile
> @@ -27,5 +27,4 @@ obj-$(CONFIG_MAX517) += max517.o
> obj-$(CONFIG_MAX5821) += max5821.o
> obj-$(CONFIG_MCP4725) += mcp4725.o
> obj-$(CONFIG_MCP4922) += mcp4922.o
> -obj-$(CONFIG_STX104) += stx104.o
> obj-$(CONFIG_VF610_DAC) += vf610_dac.o
>