Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / comedi / drivers / aio_iiro_16.c
diff --git a/kernel/drivers/staging/comedi/drivers/aio_iiro_16.c b/kernel/drivers/staging/comedi/drivers/aio_iiro_16.c
new file mode 100644 (file)
index 0000000..35b2f98
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * aio_iiro_16.c
+ * Comedi driver for Access I/O Products 104-IIRO-16 board
+ * Copyright (C) 2006 C&C Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: aio_iiro_16
+ * Description: Access I/O Products PC/104 Isolated Input/Relay Output Board
+ * Author: Zachary Ware <zach.ware@cctechnol.com>
+ * Devices: [Access I/O] 104-IIRO-16 (aio_iiro_16)
+ * Status: experimental
+ *
+ * Configuration Options:
+ *   [0] - I/O port base address
+ *   [1] - IRQ (optional)
+ *
+ * The board supports interrupts on change of state of the digital inputs.
+ * The sample data returned by the async command indicates which inputs
+ * changed state and the current state of the inputs:
+ *
+ *     Bit 23 - IRQ Enable (1) / Disable (0)
+ *     Bit 17 - Input 8-15 Changed State (1 = Changed, 0 = No Change)
+ *     Bit 16 - Input 0-7 Changed State (1 = Changed, 0 = No Change)
+ *     Bit 15 - Digital input 15
+ *     ...
+ *     Bit 0  - Digital input 0
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedidev.h"
+
+#define AIO_IIRO_16_RELAY_0_7          0x00
+#define AIO_IIRO_16_INPUT_0_7          0x01
+#define AIO_IIRO_16_IRQ                        0x02
+#define AIO_IIRO_16_RELAY_8_15         0x04
+#define AIO_IIRO_16_INPUT_8_15         0x05
+#define AIO_IIRO_16_STATUS             0x07
+#define AIO_IIRO_16_STATUS_IRQE                BIT(7)
+#define AIO_IIRO_16_STATUS_INPUT_8_15  BIT(1)
+#define AIO_IIRO_16_STATUS_INPUT_0_7   BIT(0)
+
+static unsigned int aio_iiro_16_read_inputs(struct comedi_device *dev)
+{
+       unsigned int val;
+
+       val = inb(dev->iobase + AIO_IIRO_16_INPUT_0_7);
+       val |= inb(dev->iobase + AIO_IIRO_16_INPUT_8_15) << 8;
+
+       return val;
+}
+
+static irqreturn_t aio_iiro_16_cos(int irq, void *d)
+{
+       struct comedi_device *dev = d;
+       struct comedi_subdevice *s = dev->read_subdev;
+       unsigned int status;
+       unsigned int val;
+
+       status = inb(dev->iobase + AIO_IIRO_16_STATUS);
+       if (!(status & AIO_IIRO_16_STATUS_IRQE))
+               return IRQ_NONE;
+
+       val = aio_iiro_16_read_inputs(dev);
+       val |= (status << 16);
+
+       comedi_buf_write_samples(s, &val, 1);
+       comedi_handle_events(dev, s);
+
+       return IRQ_HANDLED;
+}
+
+static void aio_iiro_enable_irq(struct comedi_device *dev, bool enable)
+{
+       if (enable)
+               inb(dev->iobase + AIO_IIRO_16_IRQ);
+       else
+               outb(0, dev->iobase + AIO_IIRO_16_IRQ);
+}
+
+static int aio_iiro_16_cos_cancel(struct comedi_device *dev,
+                                 struct comedi_subdevice *s)
+{
+       aio_iiro_enable_irq(dev, false);
+
+       return 0;
+}
+
+static int aio_iiro_16_cos_cmd(struct comedi_device *dev,
+                              struct comedi_subdevice *s)
+{
+       aio_iiro_enable_irq(dev, true);
+
+       return 0;
+}
+
+static int aio_iiro_16_cos_cmdtest(struct comedi_device *dev,
+                                  struct comedi_subdevice *s,
+                                  struct comedi_cmd *cmd)
+{
+       int err = 0;
+
+       /* Step 1 : check if triggers are trivially valid */
+
+       err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+       err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+       err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+       err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+       err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
+
+       if (err)
+               return 1;
+
+       /* Step 2a : make sure trigger sources are unique */
+       /* Step 2b : and mutually compatible */
+
+       /* Step 3: check if arguments are trivially valid */
+
+       err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+       err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+       err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+       err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+                                          cmd->chanlist_len);
+       err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+       if (err)
+               return 3;
+
+       /* Step 4: fix up any arguments */
+
+       /* Step 5: check channel list if it exists */
+
+       return 0;
+}
+
+static int aio_iiro_16_do_insn_bits(struct comedi_device *dev,
+                                   struct comedi_subdevice *s,
+                                   struct comedi_insn *insn,
+                                   unsigned int *data)
+{
+       if (comedi_dio_update_state(s, data)) {
+               outb(s->state & 0xff, dev->iobase + AIO_IIRO_16_RELAY_0_7);
+               outb((s->state >> 8) & 0xff,
+                    dev->iobase + AIO_IIRO_16_RELAY_8_15);
+       }
+
+       data[1] = s->state;
+
+       return insn->n;
+}
+
+static int aio_iiro_16_di_insn_bits(struct comedi_device *dev,
+                                   struct comedi_subdevice *s,
+                                   struct comedi_insn *insn,
+                                   unsigned int *data)
+{
+       data[1] = aio_iiro_16_read_inputs(dev);
+
+       return insn->n;
+}
+
+static int aio_iiro_16_attach(struct comedi_device *dev,
+                             struct comedi_devconfig *it)
+{
+       struct comedi_subdevice *s;
+       int ret;
+
+       ret = comedi_request_region(dev, it->options[0], 0x8);
+       if (ret)
+               return ret;
+
+       aio_iiro_enable_irq(dev, false);
+
+       /*
+        * Digital input change of state interrupts are optionally supported
+        * using IRQ 2-7, 10-12, 14, or 15.
+        */
+       if ((1 << it->options[1]) & 0xdcfc) {
+               ret = request_irq(it->options[1], aio_iiro_16_cos, 0,
+                                 dev->board_name, dev);
+               if (ret == 0)
+                       dev->irq = it->options[1];
+       }
+
+       ret = comedi_alloc_subdevices(dev, 2);
+       if (ret)
+               return ret;
+
+       /* Digital Output subdevice */
+       s = &dev->subdevices[0];
+       s->type         = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITABLE;
+       s->n_chan       = 16;
+       s->maxdata      = 1;
+       s->range_table  = &range_digital;
+       s->insn_bits    = aio_iiro_16_do_insn_bits;
+
+       /* get the initial state of the relays */
+       s->state = inb(dev->iobase + AIO_IIRO_16_RELAY_0_7) |
+                  (inb(dev->iobase + AIO_IIRO_16_RELAY_8_15) << 8);
+
+       /* Digital Input subdevice */
+       s = &dev->subdevices[1];
+       s->type         = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan       = 16;
+       s->maxdata      = 1;
+       s->range_table  = &range_digital;
+       s->insn_bits    = aio_iiro_16_di_insn_bits;
+       if (dev->irq) {
+               dev->read_subdev = s;
+               s->subdev_flags |= SDF_CMD_READ | SDF_LSAMPL;
+               s->len_chanlist = 1;
+               s->do_cmdtest   = aio_iiro_16_cos_cmdtest;
+               s->do_cmd       = aio_iiro_16_cos_cmd;
+               s->cancel       = aio_iiro_16_cos_cancel;
+       }
+
+       return 0;
+}
+
+static struct comedi_driver aio_iiro_16_driver = {
+       .driver_name    = "aio_iiro_16",
+       .module         = THIS_MODULE,
+       .attach         = aio_iiro_16_attach,
+       .detach         = comedi_legacy_detach,
+};
+module_comedi_driver(aio_iiro_16_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Access I/O Products 104-IIRO-16 board");
+MODULE_LICENSE("GPL");