linux/drivers/comedi/drivers/aio_iiro_16.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * aio_iiro_16.c
   4 * Comedi driver for Access I/O Products 104-IIRO-16 board
   5 * Copyright (C) 2006 C&C Technologies, Inc.
   6 */
   7
   8/*
   9 * Driver: aio_iiro_16
  10 * Description: Access I/O Products PC/104 Isolated Input/Relay Output Board
  11 * Author: Zachary Ware <zach.ware@cctechnol.com>
  12 * Devices: [Access I/O] 104-IIRO-16 (aio_iiro_16)
  13 * Status: experimental
  14 *
  15 * Configuration Options:
  16 *   [0] - I/O port base address
  17 *   [1] - IRQ (optional)
  18 *
  19 * The board supports interrupts on change of state of the digital inputs.
  20 * The sample data returned by the async command indicates which inputs
  21 * changed state and the current state of the inputs:
  22 *
  23 *      Bit 23 - IRQ Enable (1) / Disable (0)
  24 *      Bit 17 - Input 8-15 Changed State (1 = Changed, 0 = No Change)
  25 *      Bit 16 - Input 0-7 Changed State (1 = Changed, 0 = No Change)
  26 *      Bit 15 - Digital input 15
  27 *      ...
  28 *      Bit 0  - Digital input 0
  29 */
  30
  31#include <linux/module.h>
  32#include <linux/interrupt.h>
  33
  34#include "../comedidev.h"
  35
  36#define AIO_IIRO_16_RELAY_0_7           0x00
  37#define AIO_IIRO_16_INPUT_0_7           0x01
  38#define AIO_IIRO_16_IRQ                 0x02
  39#define AIO_IIRO_16_RELAY_8_15          0x04
  40#define AIO_IIRO_16_INPUT_8_15          0x05
  41#define AIO_IIRO_16_STATUS              0x07
  42#define AIO_IIRO_16_STATUS_IRQE         BIT(7)
  43#define AIO_IIRO_16_STATUS_INPUT_8_15   BIT(1)
  44#define AIO_IIRO_16_STATUS_INPUT_0_7    BIT(0)
  45
  46static unsigned int aio_iiro_16_read_inputs(struct comedi_device *dev)
  47{
  48        unsigned int val;
  49
  50        val = inb(dev->iobase + AIO_IIRO_16_INPUT_0_7);
  51        val |= inb(dev->iobase + AIO_IIRO_16_INPUT_8_15) << 8;
  52
  53        return val;
  54}
  55
  56static irqreturn_t aio_iiro_16_cos(int irq, void *d)
  57{
  58        struct comedi_device *dev = d;
  59        struct comedi_subdevice *s = dev->read_subdev;
  60        unsigned int status;
  61        unsigned int val;
  62
  63        status = inb(dev->iobase + AIO_IIRO_16_STATUS);
  64        if (!(status & AIO_IIRO_16_STATUS_IRQE))
  65                return IRQ_NONE;
  66
  67        val = aio_iiro_16_read_inputs(dev);
  68        val |= (status << 16);
  69
  70        comedi_buf_write_samples(s, &val, 1);
  71        comedi_handle_events(dev, s);
  72
  73        return IRQ_HANDLED;
  74}
  75
  76static void aio_iiro_enable_irq(struct comedi_device *dev, bool enable)
  77{
  78        if (enable)
  79                inb(dev->iobase + AIO_IIRO_16_IRQ);
  80        else
  81                outb(0, dev->iobase + AIO_IIRO_16_IRQ);
  82}
  83
  84static int aio_iiro_16_cos_cancel(struct comedi_device *dev,
  85                                  struct comedi_subdevice *s)
  86{
  87        aio_iiro_enable_irq(dev, false);
  88
  89        return 0;
  90}
  91
  92static int aio_iiro_16_cos_cmd(struct comedi_device *dev,
  93                               struct comedi_subdevice *s)
  94{
  95        aio_iiro_enable_irq(dev, true);
  96
  97        return 0;
  98}
  99
 100static int aio_iiro_16_cos_cmdtest(struct comedi_device *dev,
 101                                   struct comedi_subdevice *s,
 102                                   struct comedi_cmd *cmd)
 103{
 104        int err = 0;
 105
 106        /* Step 1 : check if triggers are trivially valid */
 107
 108        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 109        err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
 110        err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
 111        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 112        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
 113
 114        if (err)
 115                return 1;
 116
 117        /* Step 2a : make sure trigger sources are unique */
 118        /* Step 2b : and mutually compatible */
 119
 120        /* Step 3: check if arguments are trivially valid */
 121
 122        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 123        err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 124        err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 125        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 126                                           cmd->chanlist_len);
 127        err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 128
 129        if (err)
 130                return 3;
 131
 132        /* Step 4: fix up any arguments */
 133
 134        /* Step 5: check channel list if it exists */
 135
 136        return 0;
 137}
 138
 139static int aio_iiro_16_do_insn_bits(struct comedi_device *dev,
 140                                    struct comedi_subdevice *s,
 141                                    struct comedi_insn *insn,
 142                                    unsigned int *data)
 143{
 144        if (comedi_dio_update_state(s, data)) {
 145                outb(s->state & 0xff, dev->iobase + AIO_IIRO_16_RELAY_0_7);
 146                outb((s->state >> 8) & 0xff,
 147                     dev->iobase + AIO_IIRO_16_RELAY_8_15);
 148        }
 149
 150        data[1] = s->state;
 151
 152        return insn->n;
 153}
 154
 155static int aio_iiro_16_di_insn_bits(struct comedi_device *dev,
 156                                    struct comedi_subdevice *s,
 157                                    struct comedi_insn *insn,
 158                                    unsigned int *data)
 159{
 160        data[1] = aio_iiro_16_read_inputs(dev);
 161
 162        return insn->n;
 163}
 164
 165static int aio_iiro_16_attach(struct comedi_device *dev,
 166                              struct comedi_devconfig *it)
 167{
 168        struct comedi_subdevice *s;
 169        int ret;
 170
 171        ret = comedi_request_region(dev, it->options[0], 0x8);
 172        if (ret)
 173                return ret;
 174
 175        aio_iiro_enable_irq(dev, false);
 176
 177        /*
 178         * Digital input change of state interrupts are optionally supported
 179         * using IRQ 2-7, 10-12, 14, or 15.
 180         */
 181        if ((1 << it->options[1]) & 0xdcfc) {
 182                ret = request_irq(it->options[1], aio_iiro_16_cos, 0,
 183                                  dev->board_name, dev);
 184                if (ret == 0)
 185                        dev->irq = it->options[1];
 186        }
 187
 188        ret = comedi_alloc_subdevices(dev, 2);
 189        if (ret)
 190                return ret;
 191
 192        /* Digital Output subdevice */
 193        s = &dev->subdevices[0];
 194        s->type         = COMEDI_SUBD_DO;
 195        s->subdev_flags = SDF_WRITABLE;
 196        s->n_chan       = 16;
 197        s->maxdata      = 1;
 198        s->range_table  = &range_digital;
 199        s->insn_bits    = aio_iiro_16_do_insn_bits;
 200
 201        /* get the initial state of the relays */
 202        s->state = inb(dev->iobase + AIO_IIRO_16_RELAY_0_7) |
 203                   (inb(dev->iobase + AIO_IIRO_16_RELAY_8_15) << 8);
 204
 205        /* Digital Input subdevice */
 206        s = &dev->subdevices[1];
 207        s->type         = COMEDI_SUBD_DI;
 208        s->subdev_flags = SDF_READABLE;
 209        s->n_chan       = 16;
 210        s->maxdata      = 1;
 211        s->range_table  = &range_digital;
 212        s->insn_bits    = aio_iiro_16_di_insn_bits;
 213        if (dev->irq) {
 214                dev->read_subdev = s;
 215                s->subdev_flags |= SDF_CMD_READ | SDF_LSAMPL;
 216                s->len_chanlist = 1;
 217                s->do_cmdtest   = aio_iiro_16_cos_cmdtest;
 218                s->do_cmd       = aio_iiro_16_cos_cmd;
 219                s->cancel       = aio_iiro_16_cos_cancel;
 220        }
 221
 222        return 0;
 223}
 224
 225static struct comedi_driver aio_iiro_16_driver = {
 226        .driver_name    = "aio_iiro_16",
 227        .module         = THIS_MODULE,
 228        .attach         = aio_iiro_16_attach,
 229        .detach         = comedi_legacy_detach,
 230};
 231module_comedi_driver(aio_iiro_16_driver);
 232
 233MODULE_AUTHOR("Comedi https://www.comedi.org");
 234MODULE_DESCRIPTION("Comedi driver for Access I/O Products 104-IIRO-16 board");
 235MODULE_LICENSE("GPL");
 236