linux/drivers/iio/adc/stx104.c
<<
>>
Prefs
   1/*
   2 * IIO driver for the Apex Embedded Systems STX104
   3 * Copyright (C) 2016 William Breathitt Gray
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License, version 2, as
   7 * published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful, but
  10 * WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12 * General Public License for more details.
  13 */
  14#include <linux/bitops.h>
  15#include <linux/device.h>
  16#include <linux/errno.h>
  17#include <linux/gpio/driver.h>
  18#include <linux/iio/iio.h>
  19#include <linux/iio/types.h>
  20#include <linux/io.h>
  21#include <linux/ioport.h>
  22#include <linux/isa.h>
  23#include <linux/kernel.h>
  24#include <linux/module.h>
  25#include <linux/moduleparam.h>
  26#include <linux/spinlock.h>
  27
  28#define STX104_OUT_CHAN(chan) {                         \
  29        .type = IIO_VOLTAGE,                            \
  30        .channel = chan,                                \
  31        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
  32        .indexed = 1,                                   \
  33        .output = 1                                     \
  34}
  35#define STX104_IN_CHAN(chan, diff) {                                    \
  36        .type = IIO_VOLTAGE,                                            \
  37        .channel = chan,                                                \
  38        .channel2 = chan,                                               \
  39        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN) |   \
  40                BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),   \
  41        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),                   \
  42        .indexed = 1,                                                   \
  43        .differential = diff                                            \
  44}
  45
  46#define STX104_NUM_OUT_CHAN 2
  47
  48#define STX104_EXTENT 16
  49
  50static unsigned int base[max_num_isa_dev(STX104_EXTENT)];
  51static unsigned int num_stx104;
  52module_param_hw_array(base, uint, ioport, &num_stx104, 0);
  53MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses");
  54
  55/**
  56 * struct stx104_iio - IIO device private data structure
  57 * @chan_out_states:    channels' output states
  58 * @base:               base port address of the IIO device
  59 */
  60struct stx104_iio {
  61        unsigned int chan_out_states[STX104_NUM_OUT_CHAN];
  62        unsigned int base;
  63};
  64
  65/**
  66 * struct stx104_gpio - GPIO device private data structure
  67 * @chip:       instance of the gpio_chip
  68 * @lock:       synchronization lock to prevent I/O race conditions
  69 * @base:       base port address of the GPIO device
  70 * @out_state:  output bits state
  71 */
  72struct stx104_gpio {
  73        struct gpio_chip chip;
  74        spinlock_t lock;
  75        unsigned int base;
  76        unsigned int out_state;
  77};
  78
  79static int stx104_read_raw(struct iio_dev *indio_dev,
  80        struct iio_chan_spec const *chan, int *val, int *val2, long mask)
  81{
  82        struct stx104_iio *const priv = iio_priv(indio_dev);
  83        unsigned int adc_config;
  84        int adbu;
  85        int gain;
  86
  87        switch (mask) {
  88        case IIO_CHAN_INFO_HARDWAREGAIN:
  89                /* get gain configuration */
  90                adc_config = inb(priv->base + 11);
  91                gain = adc_config & 0x3;
  92
  93                *val = 1 << gain;
  94                return IIO_VAL_INT;
  95        case IIO_CHAN_INFO_RAW:
  96                if (chan->output) {
  97                        *val = priv->chan_out_states[chan->channel];
  98                        return IIO_VAL_INT;
  99                }
 100
 101                /* select ADC channel */
 102                outb(chan->channel | (chan->channel << 4), priv->base + 2);
 103
 104                /* trigger ADC sample capture and wait for completion */
 105                outb(0, priv->base);
 106                while (inb(priv->base + 8) & BIT(7));
 107
 108                *val = inw(priv->base);
 109                return IIO_VAL_INT;
 110        case IIO_CHAN_INFO_OFFSET:
 111                /* get ADC bipolar/unipolar configuration */
 112                adc_config = inb(priv->base + 11);
 113                adbu = !(adc_config & BIT(2));
 114
 115                *val = -32768 * adbu;
 116                return IIO_VAL_INT;
 117        case IIO_CHAN_INFO_SCALE:
 118                /* get ADC bipolar/unipolar and gain configuration */
 119                adc_config = inb(priv->base + 11);
 120                adbu = !(adc_config & BIT(2));
 121                gain = adc_config & 0x3;
 122
 123                *val = 5;
 124                *val2 = 15 - adbu + gain;
 125                return IIO_VAL_FRACTIONAL_LOG2;
 126        }
 127
 128        return -EINVAL;
 129}
 130
 131static int stx104_write_raw(struct iio_dev *indio_dev,
 132        struct iio_chan_spec const *chan, int val, int val2, long mask)
 133{
 134        struct stx104_iio *const priv = iio_priv(indio_dev);
 135
 136        switch (mask) {
 137        case IIO_CHAN_INFO_HARDWAREGAIN:
 138                /* Only four gain states (x1, x2, x4, x8) */
 139                switch (val) {
 140                case 1:
 141                        outb(0, priv->base + 11);
 142                        break;
 143                case 2:
 144                        outb(1, priv->base + 11);
 145                        break;
 146                case 4:
 147                        outb(2, priv->base + 11);
 148                        break;
 149                case 8:
 150                        outb(3, priv->base + 11);
 151                        break;
 152                default:
 153                        return -EINVAL;
 154                }
 155
 156                return 0;
 157        case IIO_CHAN_INFO_RAW:
 158                if (chan->output) {
 159                        /* DAC can only accept up to a 16-bit value */
 160                        if ((unsigned int)val > 65535)
 161                                return -EINVAL;
 162
 163                        priv->chan_out_states[chan->channel] = val;
 164                        outw(val, priv->base + 4 + 2 * chan->channel);
 165
 166                        return 0;
 167                }
 168                return -EINVAL;
 169        }
 170
 171        return -EINVAL;
 172}
 173
 174static const struct iio_info stx104_info = {
 175        .driver_module = THIS_MODULE,
 176        .read_raw = stx104_read_raw,
 177        .write_raw = stx104_write_raw
 178};
 179
 180/* single-ended input channels configuration */
 181static const struct iio_chan_spec stx104_channels_sing[] = {
 182        STX104_OUT_CHAN(0), STX104_OUT_CHAN(1),
 183        STX104_IN_CHAN(0, 0), STX104_IN_CHAN(1, 0), STX104_IN_CHAN(2, 0),
 184        STX104_IN_CHAN(3, 0), STX104_IN_CHAN(4, 0), STX104_IN_CHAN(5, 0),
 185        STX104_IN_CHAN(6, 0), STX104_IN_CHAN(7, 0), STX104_IN_CHAN(8, 0),
 186        STX104_IN_CHAN(9, 0), STX104_IN_CHAN(10, 0), STX104_IN_CHAN(11, 0),
 187        STX104_IN_CHAN(12, 0), STX104_IN_CHAN(13, 0), STX104_IN_CHAN(14, 0),
 188        STX104_IN_CHAN(15, 0)
 189};
 190/* differential input channels configuration */
 191static const struct iio_chan_spec stx104_channels_diff[] = {
 192        STX104_OUT_CHAN(0), STX104_OUT_CHAN(1),
 193        STX104_IN_CHAN(0, 1), STX104_IN_CHAN(1, 1), STX104_IN_CHAN(2, 1),
 194        STX104_IN_CHAN(3, 1), STX104_IN_CHAN(4, 1), STX104_IN_CHAN(5, 1),
 195        STX104_IN_CHAN(6, 1), STX104_IN_CHAN(7, 1)
 196};
 197
 198static int stx104_gpio_get_direction(struct gpio_chip *chip,
 199        unsigned int offset)
 200{
 201        /* GPIO 0-3 are input only, while the rest are output only */
 202        if (offset < 4)
 203                return 1;
 204
 205        return 0;
 206}
 207
 208static int stx104_gpio_direction_input(struct gpio_chip *chip,
 209        unsigned int offset)
 210{
 211        if (offset >= 4)
 212                return -EINVAL;
 213
 214        return 0;
 215}
 216
 217static int stx104_gpio_direction_output(struct gpio_chip *chip,
 218        unsigned int offset, int value)
 219{
 220        if (offset < 4)
 221                return -EINVAL;
 222
 223        chip->set(chip, offset, value);
 224        return 0;
 225}
 226
 227static int stx104_gpio_get(struct gpio_chip *chip, unsigned int offset)
 228{
 229        struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip);
 230
 231        if (offset >= 4)
 232                return -EINVAL;
 233
 234        return !!(inb(stx104gpio->base) & BIT(offset));
 235}
 236
 237static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset,
 238        int value)
 239{
 240        struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip);
 241        const unsigned int mask = BIT(offset) >> 4;
 242        unsigned long flags;
 243
 244        if (offset < 4)
 245                return;
 246
 247        spin_lock_irqsave(&stx104gpio->lock, flags);
 248
 249        if (value)
 250                stx104gpio->out_state |= mask;
 251        else
 252                stx104gpio->out_state &= ~mask;
 253
 254        outb(stx104gpio->out_state, stx104gpio->base);
 255
 256        spin_unlock_irqrestore(&stx104gpio->lock, flags);
 257}
 258
 259#define STX104_NGPIO 8
 260static const char *stx104_names[STX104_NGPIO] = {
 261        "DIN0", "DIN1", "DIN2", "DIN3", "DOUT0", "DOUT1", "DOUT2", "DOUT3"
 262};
 263
 264static void stx104_gpio_set_multiple(struct gpio_chip *chip,
 265        unsigned long *mask, unsigned long *bits)
 266{
 267        struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip);
 268        unsigned long flags;
 269
 270        /* verify masked GPIO are output */
 271        if (!(*mask & 0xF0))
 272                return;
 273
 274        *mask >>= 4;
 275        *bits >>= 4;
 276
 277        spin_lock_irqsave(&stx104gpio->lock, flags);
 278
 279        stx104gpio->out_state &= ~*mask;
 280        stx104gpio->out_state |= *mask & *bits;
 281        outb(stx104gpio->out_state, stx104gpio->base);
 282
 283        spin_unlock_irqrestore(&stx104gpio->lock, flags);
 284}
 285
 286static int stx104_probe(struct device *dev, unsigned int id)
 287{
 288        struct iio_dev *indio_dev;
 289        struct stx104_iio *priv;
 290        struct stx104_gpio *stx104gpio;
 291        int err;
 292
 293        indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
 294        if (!indio_dev)
 295                return -ENOMEM;
 296
 297        stx104gpio = devm_kzalloc(dev, sizeof(*stx104gpio), GFP_KERNEL);
 298        if (!stx104gpio)
 299                return -ENOMEM;
 300
 301        if (!devm_request_region(dev, base[id], STX104_EXTENT,
 302                dev_name(dev))) {
 303                dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
 304                        base[id], base[id] + STX104_EXTENT);
 305                return -EBUSY;
 306        }
 307
 308        indio_dev->info = &stx104_info;
 309        indio_dev->modes = INDIO_DIRECT_MODE;
 310
 311        /* determine if differential inputs */
 312        if (inb(base[id] + 8) & BIT(5)) {
 313                indio_dev->num_channels = ARRAY_SIZE(stx104_channels_diff);
 314                indio_dev->channels = stx104_channels_diff;
 315        } else {
 316                indio_dev->num_channels = ARRAY_SIZE(stx104_channels_sing);
 317                indio_dev->channels = stx104_channels_sing;
 318        }
 319
 320        indio_dev->name = dev_name(dev);
 321        indio_dev->dev.parent = dev;
 322
 323        priv = iio_priv(indio_dev);
 324        priv->base = base[id];
 325
 326        /* configure device for software trigger operation */
 327        outb(0, base[id] + 9);
 328
 329        /* initialize gain setting to x1 */
 330        outb(0, base[id] + 11);
 331
 332        /* initialize DAC output to 0V */
 333        outw(0, base[id] + 4);
 334        outw(0, base[id] + 6);
 335
 336        stx104gpio->chip.label = dev_name(dev);
 337        stx104gpio->chip.parent = dev;
 338        stx104gpio->chip.owner = THIS_MODULE;
 339        stx104gpio->chip.base = -1;
 340        stx104gpio->chip.ngpio = STX104_NGPIO;
 341        stx104gpio->chip.names = stx104_names;
 342        stx104gpio->chip.get_direction = stx104_gpio_get_direction;
 343        stx104gpio->chip.direction_input = stx104_gpio_direction_input;
 344        stx104gpio->chip.direction_output = stx104_gpio_direction_output;
 345        stx104gpio->chip.get = stx104_gpio_get;
 346        stx104gpio->chip.set = stx104_gpio_set;
 347        stx104gpio->chip.set_multiple = stx104_gpio_set_multiple;
 348        stx104gpio->base = base[id] + 3;
 349        stx104gpio->out_state = 0x0;
 350
 351        spin_lock_init(&stx104gpio->lock);
 352
 353        err = devm_gpiochip_add_data(dev, &stx104gpio->chip, stx104gpio);
 354        if (err) {
 355                dev_err(dev, "GPIO registering failed (%d)\n", err);
 356                return err;
 357        }
 358
 359        return devm_iio_device_register(dev, indio_dev);
 360}
 361
 362static struct isa_driver stx104_driver = {
 363        .probe = stx104_probe,
 364        .driver = {
 365                .name = "stx104"
 366        },
 367};
 368
 369module_isa_driver(stx104_driver, num_stx104);
 370
 371MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
 372MODULE_DESCRIPTION("Apex Embedded Systems STX104 IIO driver");
 373MODULE_LICENSE("GPL v2");
 374