staging: comedi: das800: cleanup range table declarations
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / staging / comedi / drivers / das800.c
index 1cf9ddd..a71a290 100644 (file)
@@ -115,62 +115,58 @@ struct das800_board {
 };
 
 static const struct comedi_lrange range_das801_ai = {
-       9,
-       {
-        RANGE(-5, 5),
-        RANGE(-10, 10),
-        RANGE(0, 10),
-        RANGE(-0.5, 0.5),
-        RANGE(0, 1),
-        RANGE(-0.05, 0.05),
-        RANGE(0, 0.1),
-        RANGE(-0.01, 0.01),
-        RANGE(0, 0.02),
-        }
+       9, {
+               BIP_RANGE(5),
+               BIP_RANGE(10),
+               UNI_RANGE(10),
+               BIP_RANGE(0.5),
+               UNI_RANGE(1),
+               BIP_RANGE(0.05),
+               UNI_RANGE(0.1),
+               BIP_RANGE(0.01),
+               UNI_RANGE(0.02)
+       }
 };
 
 static const struct comedi_lrange range_cio_das801_ai = {
-       9,
-       {
-        RANGE(-5, 5),
-        RANGE(-10, 10),
-        RANGE(0, 10),
-        RANGE(-0.5, 0.5),
-        RANGE(0, 1),
-        RANGE(-0.05, 0.05),
-        RANGE(0, 0.1),
-        RANGE(-0.005, 0.005),
-        RANGE(0, 0.01),
-        }
+       9, {
+               BIP_RANGE(5),
+               BIP_RANGE(10),
+               UNI_RANGE(10),
+               BIP_RANGE(0.5),
+               UNI_RANGE(1),
+               BIP_RANGE(0.05),
+               UNI_RANGE(0.1),
+               BIP_RANGE(0.005),
+               UNI_RANGE(0.01)
+       }
 };
 
 static const struct comedi_lrange range_das802_ai = {
-       9,
-       {
-        RANGE(-5, 5),
-        RANGE(-10, 10),
-        RANGE(0, 10),
-        RANGE(-2.5, 2.5),
-        RANGE(0, 5),
-        RANGE(-1.25, 1.25),
-        RANGE(0, 2.5),
-        RANGE(-0.625, 0.625),
-        RANGE(0, 1.25),
-        }
+       9, {
+               BIP_RANGE(5),
+               BIP_RANGE(10),
+               UNI_RANGE(10),
+               BIP_RANGE(2.5),
+               UNI_RANGE(5),
+               BIP_RANGE(1.25),
+               UNI_RANGE(2.5),
+               BIP_RANGE(0.625),
+               UNI_RANGE(1.25)
+       }
 };
 
 static const struct comedi_lrange range_das80216_ai = {
-       8,
-       {
-        RANGE(-10, 10),
-        RANGE(0, 10),
-        RANGE(-5, 5),
-        RANGE(0, 5),
-        RANGE(-2.5, 2.5),
-        RANGE(0, 2.5),
-        RANGE(-1.25, 1.25),
-        RANGE(0, 1.25),
-        }
+       8, {
+               BIP_RANGE(10),
+               UNI_RANGE(10),
+               BIP_RANGE(5),
+               UNI_RANGE(5),
+               BIP_RANGE(2.5),
+               UNI_RANGE(2.5),
+               BIP_RANGE(1.25),
+               UNI_RANGE(1.25)
+       }
 };
 
 enum { das800, ciodas800, das801, ciodas801, das802, ciodas802, ciodas80216 };
@@ -220,11 +216,6 @@ static const struct das800_board das800_boards[] = {
         },
 };
 
-/*
- * Useful for shorthand access to the particular board structure
- */
-#define thisboard ((const struct das800_board *)dev->board_ptr)
-
 struct das800_private {
        volatile unsigned int count;    /* number of data points left to be taken */
        volatile int forever;   /* flag indicating whether we should take data forever */
@@ -233,331 +224,31 @@ struct das800_private {
        volatile int do_bits;   /* digital output bits */
 };
 
-static int das800_attach(struct comedi_device *dev,
-                        struct comedi_devconfig *it);
-static void das800_detach(struct comedi_device *dev);
-static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
-
-static struct comedi_driver driver_das800 = {
-       .driver_name = "das800",
-       .module = THIS_MODULE,
-       .attach = das800_attach,
-       .detach = das800_detach,
-       .num_names = ARRAY_SIZE(das800_boards),
-       .board_name = &das800_boards[0].name,
-       .offset = sizeof(struct das800_board),
-};
-
-static irqreturn_t das800_interrupt(int irq, void *d);
-static void enable_das800(struct comedi_device *dev);
-static void disable_das800(struct comedi_device *dev);
-static int das800_ai_do_cmdtest(struct comedi_device *dev,
-                               struct comedi_subdevice *s,
-                               struct comedi_cmd *cmd);
-static int das800_ai_do_cmd(struct comedi_device *dev,
-                           struct comedi_subdevice *s);
-static int das800_ai_rinsn(struct comedi_device *dev,
-                          struct comedi_subdevice *s, struct comedi_insn *insn,
-                          unsigned int *data);
-static int das800_di_rbits(struct comedi_device *dev,
-                          struct comedi_subdevice *s, struct comedi_insn *insn,
-                          unsigned int *data);
-static int das800_do_wbits(struct comedi_device *dev,
-                          struct comedi_subdevice *s, struct comedi_insn *insn,
-                          unsigned int *data);
-static int das800_probe(struct comedi_device *dev);
-static int das800_set_frequency(struct comedi_device *dev);
-
-/* checks and probes das-800 series board type */
-static int das800_probe(struct comedi_device *dev)
-{
-       int id_bits;
-       unsigned long irq_flags;
-       int board;
-
-       /*  'comedi spin lock irqsave' disables even rt interrupts, we use them to protect indirect addressing */
-       spin_lock_irqsave(&dev->spinlock, irq_flags);
-       outb(ID, dev->iobase + DAS800_GAIN);    /* select base address + 7 to be ID register */
-       id_bits = inb(dev->iobase + DAS800_ID) & 0x3;   /* get id bits */
-       spin_unlock_irqrestore(&dev->spinlock, irq_flags);
-
-       board = thisboard - das800_boards;
-
-       switch (id_bits) {
-       case 0x0:
-               if (board == das800) {
-                       dev_dbg(dev->class_dev, "Board model: DAS-800\n");
-                       return board;
-               }
-               if (board == ciodas800) {
-                       dev_dbg(dev->class_dev, "Board model: CIO-DAS800\n");
-                       return board;
-               }
-               dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
-               return das800;
-               break;
-       case 0x2:
-               if (board == das801) {
-                       dev_dbg(dev->class_dev, "Board model: DAS-801\n");
-                       return board;
-               }
-               if (board == ciodas801) {
-                       dev_dbg(dev->class_dev, "Board model: CIO-DAS801\n");
-                       return board;
-               }
-               dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
-               return das801;
-               break;
-       case 0x3:
-               if (board == das802) {
-                       dev_dbg(dev->class_dev, "Board model: DAS-802\n");
-                       return board;
-               }
-               if (board == ciodas802) {
-                       dev_dbg(dev->class_dev, "Board model: CIO-DAS802\n");
-                       return board;
-               }
-               if (board == ciodas80216) {
-                       dev_dbg(dev->class_dev, "Board model: CIO-DAS802/16\n");
-                       return board;
-               }
-               dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
-               return das802;
-               break;
-       default:
-               dev_dbg(dev->class_dev,
-                       "Board model: probe returned 0x%x (unknown)\n",
-                       id_bits);
-               return board;
-               break;
-       }
-       return -1;
-}
-
-module_comedi_driver(driver_das800);
-
-/* interrupt service routine */
-static irqreturn_t das800_interrupt(int irq, void *d)
+static void das800_ind_write(struct comedi_device *dev,
+                            unsigned val, unsigned reg)
 {
-       short i;                /* loop index */
-       short dataPoint = 0;
-       struct comedi_device *dev = d;
-       struct das800_private *devpriv = dev->private;
-       struct comedi_subdevice *s = dev->read_subdev;  /* analog input subdevice */
-       struct comedi_async *async;
-       int status;
-       unsigned long irq_flags;
-       static const int max_loops = 128;       /*  half-fifo size for cio-das802/16 */
-       /*  flags */
-       int fifo_empty = 0;
-       int fifo_overflow = 0;
-
-       status = inb(dev->iobase + DAS800_STATUS);
-       /* if interrupt was not generated by board or driver not attached, quit */
-       if (!(status & IRQ))
-               return IRQ_NONE;
-       if (!(dev->attached))
-               return IRQ_HANDLED;
-
-       /* wait until here to initialize async, since we will get null dereference
-        * if interrupt occurs before driver is fully attached!
+       /*
+        * Select dev->iobase + 2 to be desired register
+        * then write to that register.
         */
-       async = s->async;
-
-       /*  if hardware conversions are not enabled, then quit */
-       spin_lock_irqsave(&dev->spinlock, irq_flags);
-       outb(CONTROL1, dev->iobase + DAS800_GAIN);      /* select base address + 7 to be STATUS2 register */
-       status = inb(dev->iobase + DAS800_STATUS2) & STATUS2_HCEN;
-       /* don't release spinlock yet since we want to make sure no one else disables hardware conversions */
-       if (status == 0) {
-               spin_unlock_irqrestore(&dev->spinlock, irq_flags);
-               return IRQ_HANDLED;
-       }
-
-       /* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */
-       for (i = 0; i < max_loops; i++) {
-               /* read 16 bits from dev->iobase and dev->iobase + 1 */
-               dataPoint = inb(dev->iobase + DAS800_LSB);
-               dataPoint += inb(dev->iobase + DAS800_MSB) << 8;
-               if (thisboard->resolution == 12) {
-                       fifo_empty = dataPoint & FIFO_EMPTY;
-                       fifo_overflow = dataPoint & FIFO_OVF;
-                       if (fifo_overflow)
-                               break;
-               } else {
-                       fifo_empty = 0; /*  cio-das802/16 has no fifo empty status bit */
-               }
-               if (fifo_empty)
-                       break;
-               /* strip off extraneous bits for 12 bit cards */
-               if (thisboard->resolution == 12)
-                       dataPoint = (dataPoint >> 4) & 0xfff;
-               /* if there are more data points to collect */
-               if (devpriv->count > 0 || devpriv->forever == 1) {
-                       /* write data point to buffer */
-                       cfc_write_to_buffer(s, dataPoint);
-                       if (devpriv->count > 0)
-                               devpriv->count--;
-               }
-       }
-       async->events |= COMEDI_CB_BLOCK;
-       /* check for fifo overflow */
-       if (thisboard->resolution == 12) {
-               fifo_overflow = dataPoint & FIFO_OVF;
-               /*  else cio-das802/16 */
-       } else {
-               fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
-       }
-       if (fifo_overflow) {
-               spin_unlock_irqrestore(&dev->spinlock, irq_flags);
-               comedi_error(dev, "DAS800 FIFO overflow");
-               das800_cancel(dev, s);
-               async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
-               comedi_event(dev, s);
-               async->events = 0;
-               return IRQ_HANDLED;
-       }
-       if (devpriv->count > 0 || devpriv->forever == 1) {
-               /* Re-enable card's interrupt.
-                * We already have spinlock, so indirect addressing is safe */
-               outb(CONTROL1, dev->iobase + DAS800_GAIN);      /* select dev->iobase + 2 to be control register 1 */
-               outb(CONTROL1_INTE | devpriv->do_bits,
-                    dev->iobase + DAS800_CONTROL1);
-               spin_unlock_irqrestore(&dev->spinlock, irq_flags);
-               /* otherwise, stop taking data */
-       } else {
-               spin_unlock_irqrestore(&dev->spinlock, irq_flags);
-               disable_das800(dev);    /* disable hardware triggered conversions */
-               async->events |= COMEDI_CB_EOA;
-       }
-       comedi_event(dev, s);
-       async->events = 0;
-       return IRQ_HANDLED;
+       outb(reg, dev->iobase + DAS800_GAIN);
+       outb(val, dev->iobase + 2);
 }
 
-static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
-{
-       struct das800_private *devpriv;
-       struct comedi_subdevice *s;
-       unsigned long iobase = it->options[0];
-       unsigned int irq = it->options[1];
-       unsigned long irq_flags;
-       int board;
-       int ret;
-
-       dev_info(dev->class_dev, "das800: io 0x%lx\n", iobase);
-       if (irq)
-               dev_dbg(dev->class_dev, "irq %u\n", irq);
-
-       devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
-       if (!devpriv)
-               return -ENOMEM;
-       dev->private = devpriv;
-
-       if (iobase == 0) {
-               dev_err(dev->class_dev,
-                       "io base address required for das800\n");
-               return -EINVAL;
-       }
-
-       /* check if io addresses are available */
-       if (!request_region(iobase, DAS800_SIZE, "das800")) {
-               dev_err(dev->class_dev, "I/O port conflict\n");
-               return -EIO;
-       }
-       dev->iobase = iobase;
-
-       board = das800_probe(dev);
-       if (board < 0) {
-               dev_dbg(dev->class_dev, "unable to determine board type\n");
-               return -ENODEV;
-       }
-       dev->board_ptr = das800_boards + board;
-
-       /* grab our IRQ */
-       if (irq == 1 || irq > 7) {
-               dev_err(dev->class_dev, "irq out of range\n");
-               return -EINVAL;
-       }
-       if (irq) {
-               if (request_irq(irq, das800_interrupt, 0, "das800", dev)) {
-                       dev_err(dev->class_dev, "unable to allocate irq %u\n",
-                               irq);
-                       return -EINVAL;
-               }
-       }
-       dev->irq = irq;
-
-       dev->board_name = thisboard->name;
-
-       ret = comedi_alloc_subdevices(dev, 3);
-       if (ret)
-               return ret;
-
-       /* analog input subdevice */
-       s = &dev->subdevices[0];
-       dev->read_subdev = s;
-       s->type = COMEDI_SUBD_AI;
-       s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
-       s->n_chan = 8;
-       s->len_chanlist = 8;
-       s->maxdata = (1 << thisboard->resolution) - 1;
-       s->range_table = thisboard->ai_range;
-       s->do_cmd = das800_ai_do_cmd;
-       s->do_cmdtest = das800_ai_do_cmdtest;
-       s->insn_read = das800_ai_rinsn;
-       s->cancel = das800_cancel;
-
-       /* di */
-       s = &dev->subdevices[1];
-       s->type = COMEDI_SUBD_DI;
-       s->subdev_flags = SDF_READABLE;
-       s->n_chan = 3;
-       s->maxdata = 1;
-       s->range_table = &range_digital;
-       s->insn_bits = das800_di_rbits;
-
-       /* do */
-       s = &dev->subdevices[2];
-       s->type = COMEDI_SUBD_DO;
-       s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
-       s->n_chan = 4;
-       s->maxdata = 1;
-       s->range_table = &range_digital;
-       s->insn_bits = das800_do_wbits;
-
-       disable_das800(dev);
-
-       /* initialize digital out channels */
-       spin_lock_irqsave(&dev->spinlock, irq_flags);
-       outb(CONTROL1, dev->iobase + DAS800_GAIN);      /* select dev->iobase + 2 to be control register 1 */
-       outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
-       spin_unlock_irqrestore(&dev->spinlock, irq_flags);
-
-       return 0;
-};
-
-static void das800_detach(struct comedi_device *dev)
-{
-       if (dev->iobase)
-               release_region(dev->iobase, DAS800_SIZE);
-       if (dev->irq)
-               free_irq(dev->irq, dev);
-};
-
-static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
 {
-       struct das800_private *devpriv = dev->private;
-
-       devpriv->forever = 0;
-       devpriv->count = 0;
-       disable_das800(dev);
-       return 0;
+       /*
+        * Select dev->iobase + 7 to be desired register
+        * then read from that register.
+        */
+       outb(reg, dev->iobase + DAS800_GAIN);
+       return inb(dev->iobase + 7);
 }
 
 /* enable_das800 makes the card start taking hardware triggered conversions */
 static void enable_das800(struct comedi_device *dev)
 {
+       const struct das800_board *thisboard = comedi_board(dev);
        struct das800_private *devpriv = dev->private;
        unsigned long irq_flags;
 
@@ -565,10 +256,10 @@ static void enable_das800(struct comedi_device *dev)
        /*  enable fifo-half full interrupts for cio-das802/16 */
        if (thisboard->resolution == 16)
                outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
-       outb(CONV_CONTROL, dev->iobase + DAS800_GAIN);  /* select dev->iobase + 2 to be conversion control register */
-       outb(CONV_HCEN, dev->iobase + DAS800_CONV_CONTROL);     /* enable hardware triggering */
-       outb(CONTROL1, dev->iobase + DAS800_GAIN);      /* select dev->iobase + 2 to be control register 1 */
-       outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);  /* enable card's interrupt */
+       /* enable hardware triggering */
+       das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
+       /* enable card's interrupt */
+       das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 }
 
@@ -576,16 +267,43 @@ static void enable_das800(struct comedi_device *dev)
 static void disable_das800(struct comedi_device *dev)
 {
        unsigned long irq_flags;
+
        spin_lock_irqsave(&dev->spinlock, irq_flags);
-       outb(CONV_CONTROL, dev->iobase + DAS800_GAIN);  /* select dev->iobase + 2 to be conversion control register */
-       outb(0x0, dev->iobase + DAS800_CONV_CONTROL);   /* disable hardware triggering of conversions */
+       /* disable hardware triggering of conversions */
+       das800_ind_write(dev, 0x0, CONV_CONTROL);
        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 }
 
+static int das800_set_frequency(struct comedi_device *dev)
+{
+       struct das800_private *devpriv = dev->private;
+       int err = 0;
+
+       if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
+               err++;
+       if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
+               err++;
+       if (err)
+               return -1;
+
+       return 0;
+}
+
+static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+       struct das800_private *devpriv = dev->private;
+
+       devpriv->forever = 0;
+       devpriv->count = 0;
+       disable_das800(dev);
+       return 0;
+}
+
 static int das800_ai_do_cmdtest(struct comedi_device *dev,
                                struct comedi_subdevice *s,
                                struct comedi_cmd *cmd)
 {
+       const struct das800_board *thisboard = comedi_board(dev);
        struct das800_private *devpriv = dev->private;
        int err = 0;
        int tmp;
@@ -677,6 +395,7 @@ static int das800_ai_do_cmdtest(struct comedi_device *dev,
 static int das800_ai_do_cmd(struct comedi_device *dev,
                            struct comedi_subdevice *s)
 {
+       const struct das800_board *thisboard = comedi_board(dev);
        struct das800_private *devpriv = dev->private;
        int startChan, endChan, scan, gain;
        int conv_bits;
@@ -697,8 +416,8 @@ static int das800_ai_do_cmd(struct comedi_device *dev,
        scan = (endChan << 3) | startChan;
 
        spin_lock_irqsave(&dev->spinlock, irq_flags);
-       outb(SCAN_LIMITS, dev->iobase + DAS800_GAIN);   /* select base address + 2 to be scan limits register */
-       outb(scan, dev->iobase + DAS800_SCAN_LIMITS);   /* set scan limits */
+       /* set scan limits */
+       das800_ind_write(dev, scan, SCAN_LIMITS);
        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 
        /* set gain */
@@ -749,18 +468,116 @@ static int das800_ai_do_cmd(struct comedi_device *dev,
        }
 
        spin_lock_irqsave(&dev->spinlock, irq_flags);
-       outb(CONV_CONTROL, dev->iobase + DAS800_GAIN);  /* select dev->iobase + 2 to be conversion control register */
-       outb(conv_bits, dev->iobase + DAS800_CONV_CONTROL);
+       das800_ind_write(dev, conv_bits, CONV_CONTROL);
        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
        async->events = 0;
        enable_das800(dev);
        return 0;
 }
 
+static irqreturn_t das800_interrupt(int irq, void *d)
+{
+       short i;                /* loop index */
+       short dataPoint = 0;
+       struct comedi_device *dev = d;
+       const struct das800_board *thisboard = comedi_board(dev);
+       struct das800_private *devpriv = dev->private;
+       struct comedi_subdevice *s = dev->read_subdev;  /* analog input subdevice */
+       struct comedi_async *async;
+       int status;
+       unsigned long irq_flags;
+       static const int max_loops = 128;       /*  half-fifo size for cio-das802/16 */
+       /*  flags */
+       int fifo_empty = 0;
+       int fifo_overflow = 0;
+
+       status = inb(dev->iobase + DAS800_STATUS);
+       /* if interrupt was not generated by board or driver not attached, quit */
+       if (!(status & IRQ))
+               return IRQ_NONE;
+       if (!(dev->attached))
+               return IRQ_HANDLED;
+
+       /* wait until here to initialize async, since we will get null dereference
+        * if interrupt occurs before driver is fully attached!
+        */
+       async = s->async;
+
+       /*  if hardware conversions are not enabled, then quit */
+       spin_lock_irqsave(&dev->spinlock, irq_flags);
+       status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
+       /* don't release spinlock yet since we want to make sure no one else disables hardware conversions */
+       if (status == 0) {
+               spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+               return IRQ_HANDLED;
+       }
+
+       /* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */
+       for (i = 0; i < max_loops; i++) {
+               /* read 16 bits from dev->iobase and dev->iobase + 1 */
+               dataPoint = inb(dev->iobase + DAS800_LSB);
+               dataPoint += inb(dev->iobase + DAS800_MSB) << 8;
+               if (thisboard->resolution == 12) {
+                       fifo_empty = dataPoint & FIFO_EMPTY;
+                       fifo_overflow = dataPoint & FIFO_OVF;
+                       if (fifo_overflow)
+                               break;
+               } else {
+                       fifo_empty = 0; /*  cio-das802/16 has no fifo empty status bit */
+               }
+               if (fifo_empty)
+                       break;
+               /* strip off extraneous bits for 12 bit cards */
+               if (thisboard->resolution == 12)
+                       dataPoint = (dataPoint >> 4) & 0xfff;
+               /* if there are more data points to collect */
+               if (devpriv->count > 0 || devpriv->forever == 1) {
+                       /* write data point to buffer */
+                       cfc_write_to_buffer(s, dataPoint);
+                       if (devpriv->count > 0)
+                               devpriv->count--;
+               }
+       }
+       async->events |= COMEDI_CB_BLOCK;
+       /* check for fifo overflow */
+       if (thisboard->resolution == 12) {
+               fifo_overflow = dataPoint & FIFO_OVF;
+               /*  else cio-das802/16 */
+       } else {
+               fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
+       }
+       if (fifo_overflow) {
+               spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+               comedi_error(dev, "DAS800 FIFO overflow");
+               das800_cancel(dev, s);
+               async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+               comedi_event(dev, s);
+               async->events = 0;
+               return IRQ_HANDLED;
+       }
+       if (devpriv->count > 0 || devpriv->forever == 1) {
+               /* Re-enable card's interrupt.
+                * We already have spinlock, so indirect addressing is safe */
+               das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
+                                CONTROL1);
+               spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+               /* otherwise, stop taking data */
+       } else {
+               spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+               disable_das800(dev);    /* disable hardware triggered conversions */
+               async->events |= COMEDI_CB_EOA;
+       }
+       comedi_event(dev, s);
+       async->events = 0;
+       return IRQ_HANDLED;
+}
+
 static int das800_ai_rinsn(struct comedi_device *dev,
                           struct comedi_subdevice *s, struct comedi_insn *insn,
                           unsigned int *data)
 {
+       const struct das800_board *thisboard = comedi_board(dev);
        struct das800_private *devpriv = dev->private;
        int i, n;
        int chan;
@@ -775,8 +592,7 @@ static int das800_ai_rinsn(struct comedi_device *dev,
        chan = CR_CHAN(insn->chanspec);
 
        spin_lock_irqsave(&dev->spinlock, irq_flags);
-       outb(CONTROL1, dev->iobase + DAS800_GAIN);      /* select dev->iobase + 2 to be control register 1 */
-       outb(chan | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
+       das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 
        /* set gain / range */
@@ -843,8 +659,7 @@ static int das800_do_wbits(struct comedi_device *dev,
        devpriv->do_bits = wbits << 4;
 
        spin_lock_irqsave(&dev->spinlock, irq_flags);
-       outb(CONTROL1, dev->iobase + DAS800_GAIN);      /* select dev->iobase + 2 to be control register 1 */
-       outb(devpriv->do_bits | CONTROL1_INTE, dev->iobase + DAS800_CONTROL1);
+       das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 
        data[1] = wbits;
@@ -852,22 +667,170 @@ static int das800_do_wbits(struct comedi_device *dev,
        return insn->n;
 }
 
-/* loads counters with divisor1, divisor2 from private structure */
-static int das800_set_frequency(struct comedi_device *dev)
+static int das800_probe(struct comedi_device *dev)
 {
-       struct das800_private *devpriv = dev->private;
-       int err = 0;
+       const struct das800_board *thisboard = comedi_board(dev);
+       int id_bits;
+       unsigned long irq_flags;
+       int board;
 
-       if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
-               err++;
-       if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
-               err++;
-       if (err)
-               return -1;
+       spin_lock_irqsave(&dev->spinlock, irq_flags);
+       id_bits = das800_ind_read(dev, ID) & 0x3;
+       spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 
-       return 0;
+       board = thisboard - das800_boards;
+
+       switch (id_bits) {
+       case 0x0:
+               if (board == das800) {
+                       dev_dbg(dev->class_dev, "Board model: DAS-800\n");
+                       return board;
+               }
+               if (board == ciodas800) {
+                       dev_dbg(dev->class_dev, "Board model: CIO-DAS800\n");
+                       return board;
+               }
+               dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
+               return das800;
+               break;
+       case 0x2:
+               if (board == das801) {
+                       dev_dbg(dev->class_dev, "Board model: DAS-801\n");
+                       return board;
+               }
+               if (board == ciodas801) {
+                       dev_dbg(dev->class_dev, "Board model: CIO-DAS801\n");
+                       return board;
+               }
+               dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
+               return das801;
+               break;
+       case 0x3:
+               if (board == das802) {
+                       dev_dbg(dev->class_dev, "Board model: DAS-802\n");
+                       return board;
+               }
+               if (board == ciodas802) {
+                       dev_dbg(dev->class_dev, "Board model: CIO-DAS802\n");
+                       return board;
+               }
+               if (board == ciodas80216) {
+                       dev_dbg(dev->class_dev, "Board model: CIO-DAS802/16\n");
+                       return board;
+               }
+               dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
+               return das802;
+               break;
+       default:
+               dev_dbg(dev->class_dev,
+                       "Board model: probe returned 0x%x (unknown)\n",
+                       id_bits);
+               return board;
+               break;
+       }
+       return -1;
 }
 
+static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+       const struct das800_board *thisboard = comedi_board(dev);
+       struct das800_private *devpriv;
+       struct comedi_subdevice *s;
+       unsigned int irq = it->options[1];
+       unsigned long irq_flags;
+       int board;
+       int ret;
+
+       devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
+       if (!devpriv)
+               return -ENOMEM;
+       dev->private = devpriv;
+
+       ret = comedi_request_region(dev, it->options[0], DAS800_SIZE);
+       if (ret)
+               return ret;
+
+       board = das800_probe(dev);
+       if (board < 0) {
+               dev_dbg(dev->class_dev, "unable to determine board type\n");
+               return -ENODEV;
+       }
+       dev->board_ptr = das800_boards + board;
+       thisboard = comedi_board(dev);
+
+       /* grab our IRQ */
+       if (irq == 1 || irq > 7) {
+               dev_err(dev->class_dev, "irq out of range\n");
+               return -EINVAL;
+       }
+       if (irq) {
+               if (request_irq(irq, das800_interrupt, 0, "das800", dev)) {
+                       dev_err(dev->class_dev, "unable to allocate irq %u\n",
+                               irq);
+                       return -EINVAL;
+               }
+       }
+       dev->irq = irq;
+
+       dev->board_name = thisboard->name;
+
+       ret = comedi_alloc_subdevices(dev, 3);
+       if (ret)
+               return ret;
+
+       /* analog input subdevice */
+       s = &dev->subdevices[0];
+       dev->read_subdev = s;
+       s->type = COMEDI_SUBD_AI;
+       s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
+       s->n_chan = 8;
+       s->len_chanlist = 8;
+       s->maxdata = (1 << thisboard->resolution) - 1;
+       s->range_table = thisboard->ai_range;
+       s->do_cmd = das800_ai_do_cmd;
+       s->do_cmdtest = das800_ai_do_cmdtest;
+       s->insn_read = das800_ai_rinsn;
+       s->cancel = das800_cancel;
+
+       /* di */
+       s = &dev->subdevices[1];
+       s->type = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan = 3;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+       s->insn_bits = das800_di_rbits;
+
+       /* do */
+       s = &dev->subdevices[2];
+       s->type = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+       s->n_chan = 4;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+       s->insn_bits = das800_do_wbits;
+
+       disable_das800(dev);
+
+       /* initialize digital out channels */
+       spin_lock_irqsave(&dev->spinlock, irq_flags);
+       das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
+       spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+       return 0;
+};
+
+static struct comedi_driver driver_das800 = {
+       .driver_name    = "das800",
+       .module         = THIS_MODULE,
+       .attach         = das800_attach,
+       .detach         = comedi_legacy_detach,
+       .num_names      = ARRAY_SIZE(das800_boards),
+       .board_name     = &das800_boards[0].name,
+       .offset         = sizeof(struct das800_board),
+};
+module_comedi_driver(driver_das800);
+
 MODULE_AUTHOR("Comedi http://www.comedi.org");
 MODULE_DESCRIPTION("Comedi low-level driver");
 MODULE_LICENSE("GPL");