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        .read_raw = stx104_read_raw,
 176        .write_raw = stx104_write_raw
 177};
 178
 179/* single-ended input channels configuration */
 180static const struct iio_chan_spec stx104_channels_sing[] = {
 181        STX104_OUT_CHAN(0), STX104_OUT_CHAN(1),
 182        STX104_IN_CHAN(0, 0), STX104_IN_CHAN(1, 0), STX104_IN_CHAN(2, 0),
 183        STX104_IN_CHAN(3, 0), STX104_IN_CHAN(4, 0), STX104_IN_CHAN(5, 0),
 184        STX104_IN_CHAN(6, 0), STX104_IN_CHAN(7, 0), STX104_IN_CHAN(8, 0),
 185        STX104_IN_CHAN(9, 0), STX104_IN_CHAN(10, 0), STX104_IN_CHAN(11, 0),
 186        STX104_IN_CHAN(12, 0), STX104_IN_CHAN(13, 0), STX104_IN_CHAN(14, 0),
 187        STX104_IN_CHAN(15, 0)
 188};
 189/* differential input channels configuration */
 190static const struct iio_chan_spec stx104_channels_diff[] = {
 191        STX104_OUT_CHAN(0), STX104_OUT_CHAN(1),
 192        STX104_IN_CHAN(0, 1), STX104_IN_CHAN(1, 1), STX104_IN_CHAN(2, 1),
 193        STX104_IN_CHAN(3, 1), STX104_IN_CHAN(4, 1), STX104_IN_CHAN(5, 1),
 194        STX104_IN_CHAN(6, 1), STX104_IN_CHAN(7, 1)
 195};
 196
 197static int stx104_gpio_get_direction(struct gpio_chip *chip,
 198        unsigned int offset)
 199{
 200        /* GPIO 0-3 are input only, while the rest are output only */
 201        if (offset < 4)
 202                return 1;
 203
 204        return 0;
 205}
 206
 207static int stx104_gpio_direction_input(struct gpio_chip *chip,
 208        unsigned int offset)
 209{
 210        if (offset >= 4)
 211                return -EINVAL;
 212
 213        return 0;
 214}
 215
 216static int stx104_gpio_direction_output(struct gpio_chip *chip,
 217        unsigned int offset, int value)
 218{
 219        if (offset < 4)
 220                return -EINVAL;
 221
 222        chip->set(chip, offset, value);
 223        return 0;
 224}
 225
 226static int stx104_gpio_get(struct gpio_chip *chip, unsigned int offset)
 227{
 228        struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip);
 229
 230        if (offset >= 4)
 231                return -EINVAL;
 232
 233        return !!(inb(stx104gpio->base) & BIT(offset));
 234}
 235
 236static int stx104_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
 237        unsigned long *bits)
 238{
 239        struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip);
 240
 241        *bits = inb(stx104gpio->base);
 242
 243        return 0;
 244}
 245
 246static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset,
 247        int value)
 248{
 249        struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip);
 250        const unsigned int mask = BIT(offset) >> 4;
 251        unsigned long flags;
 252
 253        if (offset < 4)
 254                return;
 255
 256        spin_lock_irqsave(&stx104gpio->lock, flags);
 257
 258        if (value)
 259                stx104gpio->out_state |= mask;
 260        else
 261                stx104gpio->out_state &= ~mask;
 262
 263        outb(stx104gpio->out_state, stx104gpio->base);
 264
 265        spin_unlock_irqrestore(&stx104gpio->lock, flags);
 266}
 267
 268#define STX104_NGPIO 8
 269static const char *stx104_names[STX104_NGPIO] = {
 270        "DIN0", "DIN1", "DIN2", "DIN3", "DOUT0", "DOUT1", "DOUT2", "DOUT3"
 271};
 272
 273static void stx104_gpio_set_multiple(struct gpio_chip *chip,
 274        unsigned long *mask, unsigned long *bits)
 275{
 276        struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip);
 277        unsigned long flags;
 278
 279        /* verify masked GPIO are output */
 280        if (!(*mask & 0xF0))
 281                return;
 282
 283        *mask >>= 4;
 284        *bits >>= 4;
 285
 286        spin_lock_irqsave(&stx104gpio->lock, flags);
 287
 288        stx104gpio->out_state &= ~*mask;
 289        stx104gpio->out_state |= *mask & *bits;
 290        outb(stx104gpio->out_state, stx104gpio->base);
 291
 292        spin_unlock_irqrestore(&stx104gpio->lock, flags);
 293}
 294
 295static int stx104_probe(struct device *dev, unsigned int id)
 296{
 297        struct iio_dev *indio_dev;
 298        struct stx104_iio *priv;
 299        struct stx104_gpio *stx104gpio;
 300        int err;
 301
 302        indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
 303        if (!indio_dev)
 304                return -ENOMEM;
 305
 306        stx104gpio = devm_kzalloc(dev, sizeof(*stx104gpio), GFP_KERNEL);
 307        if (!stx104gpio)
 308                return -ENOMEM;
 309
 310        if (!devm_request_region(dev, base[id], STX104_EXTENT,
 311                dev_name(dev))) {
 312                dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
 313                        base[id], base[id] + STX104_EXTENT);
 314                return -EBUSY;
 315        }
 316
 317        indio_dev->info = &stx104_info;
 318        indio_dev->modes = INDIO_DIRECT_MODE;
 319
 320        /* determine if differential inputs */
 321        if (inb(base[id] + 8) & BIT(5)) {
 322                indio_dev->num_channels = ARRAY_SIZE(stx104_channels_diff);
 323                indio_dev->channels = stx104_channels_diff;
 324        } else {
 325                indio_dev->num_channels = ARRAY_SIZE(stx104_channels_sing);
 326                indio_dev->channels = stx104_channels_sing;
 327        }
 328
 329        indio_dev->name = dev_name(dev);
 330        indio_dev->dev.parent = dev;
 331
 332        priv = iio_priv(indio_dev);
 333        priv->base = base[id];
 334
 335        /* configure device for software trigger operation */
 336        outb(0, base[id] + 9);
 337
 338        /* initialize gain setting to x1 */
 339        outb(0, base[id] + 11);
 340
 341        /* initialize DAC output to 0V */
 342        outw(0, base[id] + 4);
 343        outw(0, base[id] + 6);
 344
 345        stx104gpio->chip.label = dev_name(dev);
 346        stx104gpio->chip.parent = dev;
 347        stx104gpio->chip.owner = THIS_MODULE;
 348        stx104gpio->chip.base = -1;
 349        stx104gpio->chip.ngpio = STX104_NGPIO;
 350        stx104gpio->chip.names = stx104_names;
 351        stx104gpio->chip.get_direction = stx104_gpio_get_direction;
 352        stx104gpio->chip.direction_input = stx104_gpio_direction_input;
 353        stx104gpio->chip.direction_output = stx104_gpio_direction_output;
 354        stx104gpio->chip.get = stx104_gpio_get;
 355        stx104gpio->chip.get_multiple = stx104_gpio_get_multiple;
 356        stx104gpio->chip.set = stx104_gpio_set;
 357        stx104gpio->chip.set_multiple = stx104_gpio_set_multiple;
 358        stx104gpio->base = base[id] + 3;
 359        stx104gpio->out_state = 0x0;
 360
 361        spin_lock_init(&stx104gpio->lock);
 362
 363        err = devm_gpiochip_add_data(dev, &stx104gpio->chip, stx104gpio);
 364        if (err) {
 365                dev_err(dev, "GPIO registering failed (%d)\n", err);
 366                return err;
 367        }
 368
 369        return devm_iio_device_register(dev, indio_dev);
 370}
 371
 372static struct isa_driver stx104_driver = {
 373        .probe = stx104_probe,
 374        .driver = {
 375                .name = "stx104"
 376        },
 377};
 378
 379module_isa_driver(stx104_driver, num_stx104);
 380
 381MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
 382MODULE_DESCRIPTION("Apex Embedded Systems STX104 IIO driver");
 383MODULE_LICENSE("GPL v2");
 384