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. Signed-off-by: William Breathitt Gray <vilhelm.gray@xxxxxxxxx> --- drivers/iio/dac/Kconfig | 9 ++- drivers/iio/dac/stx104.c | 154 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 139 insertions(+), 24 deletions(-) diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index ca81447..f7f896e 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -246,14 +246,13 @@ config MCP4922 will be called mcp4922. config STX104 - tristate "Apex Embedded Systems STX104 DAC driver" + tristate "Apex Embedded Systems STX104 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. + Say yes here to build support for 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" diff --git a/drivers/iio/dac/stx104.c b/drivers/iio/dac/stx104.c index 792a971..4986e9a 100644 --- a/drivers/iio/dac/stx104.c +++ b/drivers/iio/dac/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 @@ -24,17 +24,32 @@ #include <linux/moduleparam.h> #include <linux/spinlock.h> -#define STX104_NUM_CHAN 2 +#define STX104_EXTENT 16 -#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_GAIN_CHAN { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ + .output = 1 \ +} +#define STX104_IN_CHAN(chan) { \ + .type = IIO_VOLTAGE, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1 \ +} -#define STX104_EXTENT 16 +#define STX104_NUM_OUT_CHAN 2 +#define STX104_NUM_GAIN_CHAN 1 +#define STX104_NUM_IN_CHAN 16 +#define IN_CHAN_OFFSET (STX104_NUM_OUT_CHAN + STX104_NUM_GAIN_CHAN) +#define STX104_MAX_NUM_CHAN (IN_CHAN_OFFSET + STX104_NUM_IN_CHAN) static unsigned int base[max_num_isa_dev(STX104_EXTENT)]; static unsigned int num_stx104; @@ -47,7 +62,7 @@ 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 int chan_out_states[STX104_NUM_OUT_CHAN]; unsigned base; }; @@ -69,26 +84,103 @@ 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); + long adc_sample; + unsigned int adc_config; + long adbu; + unsigned int gain; + + /* handle output channels */ + if (chan->output) { + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = priv->chan_out_states[chan->channel]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_HARDWAREGAIN: + *val = 1 << (inb(priv->base + 11) & 0x3); + return IIO_VAL_INT; + default: + return -EINVAL; + } + } - if (mask != IIO_CHAN_INFO_RAW) + if (mask != IIO_CHAN_INFO_SCALE) return -EINVAL; - *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)); + + adc_sample = inw(priv->base); + + /* get ADC bipolar/unipolar and gain configuration */ + adc_config = inb(priv->base + 11); + adbu = !(adc_config & BIT(2)); + gain = adc_config & 0x3; + + /* Value conversion math: + * ---------------------- + * scale = adc_sample / 65536 + * range = 10 / (1 << gain) + * voltage = scale * (range + adbu * range) - adbu * range + * + * Simplified: + * ----------- + * voltage = 5 * (adc_sample * (1 + adbu) - adbu * 65536) / + * (1 << (15 + gain)) + * + * Portability Caution: + * -------------------- + * *val will be set to a value between -327680 and 327675; in order to + * prevent integer underflow/overflow, the int data type of the + * implementation should be capable of representing this value range. + */ + *val = 5 * (adc_sample * (1 + adbu) - adbu * 65536); + *val2 = 15 + gain; + + return IIO_VAL_FRACTIONAL_LOG2; } 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_RAW: + /* 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); + + break; + 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; + } + + break; + default: return -EINVAL; - - priv->chan_out_states[chan->channel] = val; - outw(val, priv->base + 4 + chan_addr_offset); + } return 0; } @@ -99,9 +191,15 @@ 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) +static struct iio_chan_spec stx104_channels[STX104_MAX_NUM_CHAN] = { + STX104_OUT_CHAN(0), STX104_OUT_CHAN(1), + STX104_GAIN_CHAN, + STX104_IN_CHAN(0), STX104_IN_CHAN(1), STX104_IN_CHAN(2), + STX104_IN_CHAN(3), STX104_IN_CHAN(4), STX104_IN_CHAN(5), + STX104_IN_CHAN(6), STX104_IN_CHAN(7), STX104_IN_CHAN(8), + STX104_IN_CHAN(9), STX104_IN_CHAN(10), STX104_IN_CHAN(11), + STX104_IN_CHAN(12), STX104_IN_CHAN(13), STX104_IN_CHAN(14), + STX104_IN_CHAN(15) }; static int stx104_gpio_get_direction(struct gpio_chip *chip, @@ -169,6 +267,7 @@ static int stx104_probe(struct device *dev, unsigned int id) struct iio_dev *indio_dev; struct stx104_iio *priv; struct stx104_gpio *stx104gpio; + int i; int err; indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); @@ -188,13 +287,30 @@ static int stx104_probe(struct device *dev, unsigned int id) indio_dev->info = &stx104_info; indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = IN_CHAN_OFFSET + STX104_NUM_IN_CHAN; + + /* determine if differential inputs */ + if (inb(base[id] + 8) & BIT(5)) { + indio_dev->num_channels -= STX104_NUM_IN_CHAN / 2; + + for (i = 0; i < STX104_NUM_IN_CHAN / 2; i++) { + stx104_channels[i + IN_CHAN_OFFSET].differential = 1; + stx104_channels[i + IN_CHAN_OFFSET].channel2 = i; + } + } + indio_dev->channels = stx104_channels; - indio_dev->num_channels = STX104_NUM_CHAN; 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 +367,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"); -- 2.7.3 -- 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