These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / staging / comedi / drivers / icp_multi.c
index 1e104eb..28cf53e 100644 (file)
@@ -1,94 +1,89 @@
 /*
-    comedi/drivers/icp_multi.c
-
-    COMEDI - Linux Control and Measurement Device Interface
-    Copyright (C) 1997-2002 David A. Schleef <ds@schleef.org>
-
-    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.
-*/
+ * icp_multi.c
+ * Comedi driver for Inova ICP_MULTI board
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-2002 David A. Schleef <ds@schleef.org>
+ *
+ * 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: icp_multi
-Description: Inova ICP_MULTI
-Author: Anne Smorthit <anne.smorthit@sfwte.ch>
-Devices: [Inova] ICP_MULTI (icp_multi)
-Status: works
-
-The driver works for analog input and output and digital input and output.
-It does not work with interrupts or with the counters.  Currently no support
-for DMA.
-
-It has 16 single-ended or 8 differential Analogue Input channels with 12-bit
-resolution.  Ranges : 5V, 10V, +/-5V, +/-10V, 0..20mA and 4..20mA.  Input
-ranges can be individually programmed for each channel.  Voltage or current
-measurement is selected by jumper.
-
-There are 4 x 12-bit Analogue Outputs.  Ranges : 5V, 10V, +/-5V, +/-10V
-
-16 x Digital Inputs, 24V
-
-8 x Digital Outputs, 24V, 1A
-
-4 x 16-bit counters
-
-Configuration options: not applicable, uses PCI auto config
-*/
+ * Driver: icp_multi
+ * Description: Inova ICP_MULTI
+ * Devices: [Inova] ICP_MULTI (icp_multi)
+ * Author: Anne Smorthit <anne.smorthit@sfwte.ch>
+ * Status: works
+ *
+ * Configuration options: not applicable, uses PCI auto config
+ *
+ * The driver works for analog input and output and digital input and
+ * output. It does not work with interrupts or with the counters. Currently
+ * no support for DMA.
+ *
+ * It has 16 single-ended or 8 differential Analogue Input channels with
+ * 12-bit resolution.  Ranges : 5V, 10V, +/-5V, +/-10V, 0..20mA and 4..20mA.
+ * Input ranges can be individually programmed for each channel.  Voltage or
+ * current measurement is selected by jumper.
+ *
+ * There are 4 x 12-bit Analogue Outputs.  Ranges : 5V, 10V, +/-5V, +/-10V
+ *
+ * 16 x Digital Inputs, 24V
+ *
+ * 8 x Digital Outputs, 24V, 1A
+ *
+ * 4 x 16-bit counters - not implemented
+ */
 
 #include <linux/module.h>
 #include <linux/delay.h>
-#include <linux/interrupt.h>
 
 #include "../comedi_pci.h"
 
-#define ICP_MULTI_ADC_CSR      0       /* R/W: ADC command/status register */
+#define ICP_MULTI_ADC_CSR      0x00    /* R/W: ADC command/status register */
+#define ICP_MULTI_ADC_CSR_ST   BIT(0)  /* Start ADC */
+#define ICP_MULTI_ADC_CSR_BSY  BIT(0)  /* ADC busy */
+#define ICP_MULTI_ADC_CSR_BI   BIT(4)  /* Bipolar input range */
+#define ICP_MULTI_ADC_CSR_RA   BIT(5)  /* Input range 0 = 5V, 1 = 10V */
+#define ICP_MULTI_ADC_CSR_DI   BIT(6)  /* Input mode 1 = differential */
+#define ICP_MULTI_ADC_CSR_DI_CHAN(x) (((x) & 0x7) << 9)
+#define ICP_MULTI_ADC_CSR_SE_CHAN(x) (((x) & 0xf) << 8)
 #define ICP_MULTI_AI           2       /* R:   Analogue input data */
-#define ICP_MULTI_DAC_CSR      4       /* R/W: DAC command/status register */
+#define ICP_MULTI_DAC_CSR      0x04    /* R/W: DAC command/status register */
+#define ICP_MULTI_DAC_CSR_ST   BIT(0)  /* Start DAC */
+#define ICP_MULTI_DAC_CSR_BSY  BIT(0)  /* DAC busy */
+#define ICP_MULTI_DAC_CSR_BI   BIT(4)  /* Bipolar output range */
+#define ICP_MULTI_DAC_CSR_RA   BIT(5)  /* Output range 0 = 5V, 1 = 10V */
+#define ICP_MULTI_DAC_CSR_CHAN(x) (((x) & 0x3) << 8)
 #define ICP_MULTI_AO           6       /* R/W: Analogue output data */
 #define ICP_MULTI_DI           8       /* R/W: Digital inputs */
 #define ICP_MULTI_DO           0x0A    /* R/W: Digital outputs */
-#define ICP_MULTI_INT_EN       0x0C    /* R/W: Interrupt enable register */
-#define ICP_MULTI_INT_STAT     0x0E    /* R/W: Interrupt status register */
+#define ICP_MULTI_INT_EN       0x0c    /* R/W: Interrupt enable register */
+#define ICP_MULTI_INT_STAT     0x0e    /* R/W: Interrupt status register */
+#define ICP_MULTI_INT_ADC_RDY  BIT(0)  /* A/D conversion ready interrupt */
+#define ICP_MULTI_INT_DAC_RDY  BIT(1)  /* D/A conversion ready interrupt */
+#define ICP_MULTI_INT_DOUT_ERR BIT(2)  /* Digital output error interrupt */
+#define ICP_MULTI_INT_DIN_STAT BIT(3)  /* Digital input status change int. */
+#define ICP_MULTI_INT_CIE0     BIT(4)  /* Counter 0 overrun interrupt */
+#define ICP_MULTI_INT_CIE1     BIT(5)  /* Counter 1 overrun interrupt */
+#define ICP_MULTI_INT_CIE2     BIT(6)  /* Counter 2 overrun interrupt */
+#define ICP_MULTI_INT_CIE3     BIT(7)  /* Counter 3 overrun interrupt */
+#define ICP_MULTI_INT_MASK     0xff    /* All interrupts */
 #define ICP_MULTI_CNTR0                0x10    /* R/W: Counter 0 */
 #define ICP_MULTI_CNTR1                0x12    /* R/W: counter 1 */
 #define ICP_MULTI_CNTR2                0x14    /* R/W: Counter 2 */
 #define ICP_MULTI_CNTR3                0x16    /* R/W: Counter 3 */
 
-/*  Define bits from ADC command/status register */
-#define        ADC_ST          0x0001  /* Start ADC */
-#define        ADC_BSY         0x0001  /* ADC busy */
-#define ADC_BI         0x0010  /* Bipolar input range 1 = bipolar */
-#define ADC_RA         0x0020  /* Input range 0 = 5V, 1 = 10V */
-#define        ADC_DI          0x0040  /* Differential input mode 1 = differential */
-
-/*  Define bits from DAC command/status register */
-#define        DAC_ST          0x0001  /* Start DAC */
-#define DAC_BSY                0x0001  /* DAC busy */
-#define        DAC_BI          0x0010  /* Bipolar input range 1 = bipolar */
-#define        DAC_RA          0x0020  /* Input range 0 = 5V, 1 = 10V */
-
-/*  Define bits from interrupt enable/status registers */
-#define        ADC_READY       0x0001  /* A/d conversion ready interrupt */
-#define        DAC_READY       0x0002  /* D/a conversion ready interrupt */
-#define        DOUT_ERROR      0x0004  /* Digital output error interrupt */
-#define        DIN_STATUS      0x0008  /* Digital input status change interrupt */
-#define        CIE0            0x0010  /* Counter 0 overrun interrupt */
-#define        CIE1            0x0020  /* Counter 1 overrun interrupt */
-#define        CIE2            0x0040  /* Counter 2 overrun interrupt */
-#define        CIE3            0x0080  /* Counter 3 overrun interrupt */
-
-/*  Useful definitions */
-#define        Status_IRQ      0x00ff  /*  All interrupts */
-
-/*  Define analogue range */
-static const struct comedi_lrange range_analog = {
+/* analog input and output have the same range options */
+static const struct comedi_lrange icp_multi_ranges = {
        4, {
                UNI_RANGE(5),
                UNI_RANGE(10),
@@ -99,71 +94,6 @@ static const struct comedi_lrange range_analog = {
 
 static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
 
-/*
-==============================================================================
-       Data & Structure declarations
-==============================================================================
-*/
-
-struct icp_multi_private {
-       unsigned int AdcCmdStatus;      /*  ADC Command/Status register */
-       unsigned int DacCmdStatus;      /*  DAC Command/Status register */
-       unsigned int IntEnable; /*  Interrupt Enable register */
-       unsigned int IntStatus; /*  Interrupt Status register */
-       unsigned int act_chanlist[32];  /*  list of scanned channel */
-       unsigned char act_chanlist_len; /*  len of scanlist */
-       unsigned char act_chanlist_pos; /*  actual position in MUX list */
-       unsigned int *ai_chanlist;      /*  actaul chanlist */
-       unsigned int do_data;   /*  Remember digital output data */
-};
-
-static void setup_channel_list(struct comedi_device *dev,
-                              struct comedi_subdevice *s,
-                              unsigned int *chanlist, unsigned int n_chan)
-{
-       struct icp_multi_private *devpriv = dev->private;
-       unsigned int i, range, chanprog;
-       unsigned int diff;
-
-       devpriv->act_chanlist_len = n_chan;
-       devpriv->act_chanlist_pos = 0;
-
-       for (i = 0; i < n_chan; i++) {
-               /*  Get channel */
-               chanprog = CR_CHAN(chanlist[i]);
-
-               /*  Determine if it is a differential channel (Bit 15  = 1) */
-               if (CR_AREF(chanlist[i]) == AREF_DIFF) {
-                       diff = 1;
-                       chanprog &= 0x0007;
-               } else {
-                       diff = 0;
-                       chanprog &= 0x000f;
-               }
-
-               /*  Clear channel, range and input mode bits
-                *  in A/D command/status register */
-               devpriv->AdcCmdStatus &= 0xf00f;
-
-               /*  Set channel number and differential mode status bit */
-               if (diff) {
-                       /*  Set channel number, bits 9-11 & mode, bit 6 */
-                       devpriv->AdcCmdStatus |= (chanprog << 9);
-                       devpriv->AdcCmdStatus |= ADC_DI;
-               } else
-                       /*  Set channel number, bits 8-11 */
-                       devpriv->AdcCmdStatus |= (chanprog << 8);
-
-               /*  Get range for current channel */
-               range = range_codes_analog[CR_RANGE(chanlist[i])];
-               /*  Set range. bits 4-5 */
-               devpriv->AdcCmdStatus |= range;
-
-               /* Output channel, range, mode to ICP Multi */
-               writew(devpriv->AdcCmdStatus, dev->mmio + ICP_MULTI_ADC_CSR);
-       }
-}
-
 static int icp_multi_ai_eoc(struct comedi_device *dev,
                            struct comedi_subdevice *s,
                            struct comedi_insn *insn,
@@ -172,36 +102,37 @@ static int icp_multi_ai_eoc(struct comedi_device *dev,
        unsigned int status;
 
        status = readw(dev->mmio + ICP_MULTI_ADC_CSR);
-       if ((status & ADC_BSY) == 0)
+       if ((status & ICP_MULTI_ADC_CSR_BSY) == 0)
                return 0;
        return -EBUSY;
 }
 
-static int icp_multi_insn_read_ai(struct comedi_device *dev,
+static int icp_multi_ai_insn_read(struct comedi_device *dev,
                                  struct comedi_subdevice *s,
                                  struct comedi_insn *insn,
                                  unsigned int *data)
 {
-       struct icp_multi_private *devpriv = dev->private;
+       unsigned int chan = CR_CHAN(insn->chanspec);
+       unsigned int range = CR_RANGE(insn->chanspec);
+       unsigned int aref = CR_AREF(insn->chanspec);
+       unsigned int adc_csr;
        int ret = 0;
        int n;
 
-       /*  Disable A/D conversion ready interrupt */
-       devpriv->IntEnable &= ~ADC_READY;
-       writew(devpriv->IntEnable, dev->mmio + ICP_MULTI_INT_EN);
-
-       /*  Clear interrupt status */
-       devpriv->IntStatus |= ADC_READY;
-       writew(devpriv->IntStatus, dev->mmio + ICP_MULTI_INT_STAT);
-
-       /*  Set up appropriate channel, mode and range data, for specified ch */
-       setup_channel_list(dev, s, &insn->chanspec, 1);
+       /* Set mode and range data for specified channel */
+       if (aref == AREF_DIFF) {
+               adc_csr = ICP_MULTI_ADC_CSR_DI_CHAN(chan) |
+                         ICP_MULTI_ADC_CSR_DI;
+       } else {
+               adc_csr = ICP_MULTI_ADC_CSR_SE_CHAN(chan);
+       }
+       adc_csr |= range_codes_analog[range];
+       writew(adc_csr, dev->mmio + ICP_MULTI_ADC_CSR);
 
        for (n = 0; n < insn->n; n++) {
                /*  Set start ADC bit */
-               devpriv->AdcCmdStatus |= ADC_ST;
-               writew(devpriv->AdcCmdStatus, dev->mmio + ICP_MULTI_ADC_CSR);
-               devpriv->AdcCmdStatus &= ~ADC_ST;
+               writew(adc_csr | ICP_MULTI_ADC_CSR_ST,
+                      dev->mmio + ICP_MULTI_ADC_CSR);
 
                udelay(1);
 
@@ -213,26 +144,18 @@ static int icp_multi_insn_read_ai(struct comedi_device *dev,
                data[n] = (readw(dev->mmio + ICP_MULTI_AI) >> 4) & 0x0fff;
        }
 
-       /*  Disable interrupt */
-       devpriv->IntEnable &= ~ADC_READY;
-       writew(devpriv->IntEnable, dev->mmio + ICP_MULTI_INT_EN);
-
-       /*  Clear interrupt status */
-       devpriv->IntStatus |= ADC_READY;
-       writew(devpriv->IntStatus, dev->mmio + ICP_MULTI_INT_STAT);
-
        return ret ? ret : n;
 }
 
-static int icp_multi_ao_eoc(struct comedi_device *dev,
-                           struct comedi_subdevice *s,
-                           struct comedi_insn *insn,
-                           unsigned long context)
+static int icp_multi_ao_ready(struct comedi_device *dev,
+                             struct comedi_subdevice *s,
+                             struct comedi_insn *insn,
+                             unsigned long context)
 {
        unsigned int status;
 
        status = readw(dev->mmio + ICP_MULTI_DAC_CSR);
-       if ((status & DAC_BSY) == 0)
+       if ((status & ICP_MULTI_DAC_CSR_BSY) == 0)
                return 0;
        return -EBUSY;
 }
@@ -242,57 +165,30 @@ static int icp_multi_ao_insn_write(struct comedi_device *dev,
                                   struct comedi_insn *insn,
                                   unsigned int *data)
 {
-       struct icp_multi_private *devpriv = dev->private;
        unsigned int chan = CR_CHAN(insn->chanspec);
        unsigned int range = CR_RANGE(insn->chanspec);
+       unsigned int dac_csr;
        int i;
 
-       /*  Disable D/A conversion ready interrupt */
-       devpriv->IntEnable &= ~DAC_READY;
-       writew(devpriv->IntEnable, dev->mmio + ICP_MULTI_INT_EN);
-
-       /*  Clear interrupt status */
-       devpriv->IntStatus |= DAC_READY;
-       writew(devpriv->IntStatus, dev->mmio + ICP_MULTI_INT_STAT);
-
-       /*  Set up range and channel data */
-       /*  Bit 4 = 1 : Bipolar */
-       /*  Bit 5 = 0 : 5V */
-       /*  Bit 5 = 1 : 10V */
-       /*  Bits 8-9 : Channel number */
-       devpriv->DacCmdStatus &= 0xfccf;
-       devpriv->DacCmdStatus |= range_codes_analog[range];
-       devpriv->DacCmdStatus |= (chan << 8);
-
-       writew(devpriv->DacCmdStatus, dev->mmio + ICP_MULTI_DAC_CSR);
+       /* Select channel and range */
+       dac_csr = ICP_MULTI_DAC_CSR_CHAN(chan);
+       dac_csr |= range_codes_analog[range];
+       writew(dac_csr, dev->mmio + ICP_MULTI_DAC_CSR);
 
        for (i = 0; i < insn->n; i++) {
                unsigned int val = data[i];
                int ret;
 
-               /*  Wait for analogue output data register to be
-                *  ready for new data, or get fed up waiting */
-               ret = comedi_timeout(dev, s, insn, icp_multi_ao_eoc, 0);
-               if (ret) {
-                       /*  Disable interrupt */
-                       devpriv->IntEnable &= ~DAC_READY;
-                       writew(devpriv->IntEnable,
-                              dev->mmio + ICP_MULTI_INT_EN);
-
-                       /*  Clear interrupt status */
-                       devpriv->IntStatus |= DAC_READY;
-                       writew(devpriv->IntStatus,
-                              dev->mmio + ICP_MULTI_INT_STAT);
-
+               /* Wait for analog output to be ready for new data */
+               ret = comedi_timeout(dev, s, insn, icp_multi_ao_ready, 0);
+               if (ret)
                        return ret;
-               }
 
                writew(val, dev->mmio + ICP_MULTI_AO);
 
-               /*  Set DAC_ST bit to write the data to selected channel */
-               devpriv->DacCmdStatus |= DAC_ST;
-               writew(devpriv->DacCmdStatus, dev->mmio + ICP_MULTI_DAC_CSR);
-               devpriv->DacCmdStatus &= ~DAC_ST;
+               /* Set start conversion bit to write data to channel */
+               writew(dac_csr | ICP_MULTI_DAC_CSR_ST,
+                      dev->mmio + ICP_MULTI_DAC_CSR);
 
                s->readback[chan] = val;
        }
@@ -300,7 +196,7 @@ static int icp_multi_ao_insn_write(struct comedi_device *dev,
        return insn->n;
 }
 
-static int icp_multi_insn_bits_di(struct comedi_device *dev,
+static int icp_multi_di_insn_bits(struct comedi_device *dev,
                                  struct comedi_subdevice *s,
                                  struct comedi_insn *insn,
                                  unsigned int *data)
@@ -310,7 +206,7 @@ static int icp_multi_insn_bits_di(struct comedi_device *dev,
        return insn->n;
 }
 
-static int icp_multi_insn_bits_do(struct comedi_device *dev,
+static int icp_multi_do_insn_bits(struct comedi_device *dev,
                                  struct comedi_subdevice *s,
                                  struct comedi_insn *insn,
                                  unsigned int *data)
@@ -323,116 +219,27 @@ static int icp_multi_insn_bits_do(struct comedi_device *dev,
        return insn->n;
 }
 
-static int icp_multi_insn_read_ctr(struct comedi_device *dev,
-                                  struct comedi_subdevice *s,
-                                  struct comedi_insn *insn, unsigned int *data)
-{
-       return 0;
-}
-
-static int icp_multi_insn_write_ctr(struct comedi_device *dev,
-                                   struct comedi_subdevice *s,
-                                   struct comedi_insn *insn,
-                                   unsigned int *data)
-{
-       return 0;
-}
-
-static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
-{
-       struct comedi_device *dev = d;
-       int int_no;
-
-       /*  Is this interrupt from our board? */
-       int_no = readw(dev->mmio + ICP_MULTI_INT_STAT) & Status_IRQ;
-       if (!int_no)
-               /*  No, exit */
-               return IRQ_NONE;
-
-       /*  Determine which interrupt is active & handle it */
-       switch (int_no) {
-       case ADC_READY:
-               break;
-       case DAC_READY:
-               break;
-       case DOUT_ERROR:
-               break;
-       case DIN_STATUS:
-               break;
-       case CIE0:
-               break;
-       case CIE1:
-               break;
-       case CIE2:
-               break;
-       case CIE3:
-               break;
-       default:
-               break;
-       }
-
-       return IRQ_HANDLED;
-}
-
-#if 0
-static int check_channel_list(struct comedi_device *dev,
-                             struct comedi_subdevice *s,
-                             unsigned int *chanlist, unsigned int n_chan)
-{
-       unsigned int i;
-
-       /*  Check that we at least have one channel to check */
-       if (n_chan < 1) {
-               dev_err(dev->class_dev, "range/channel list is empty!\n");
-               return 0;
-       }
-       /*  Check all channels */
-       for (i = 0; i < n_chan; i++) {
-               /*  Check that channel number is < maximum */
-               if (CR_AREF(chanlist[i]) == AREF_DIFF) {
-                       if (CR_CHAN(chanlist[i]) > (s->nchan / 2)) {
-                               dev_err(dev->class_dev,
-                                       "Incorrect differential ai ch-nr\n");
-                               return 0;
-                       }
-               } else {
-                       if (CR_CHAN(chanlist[i]) > s->n_chan) {
-                               dev_err(dev->class_dev,
-                                       "Incorrect ai channel number\n");
-                               return 0;
-                       }
-               }
-       }
-       return 1;
-}
-#endif
-
 static int icp_multi_reset(struct comedi_device *dev)
 {
-       struct icp_multi_private *devpriv = dev->private;
-       unsigned int i;
+       int i;
 
-       /*  Clear INT enables and requests */
+       /* Disable all interrupts and clear any requests */
        writew(0, dev->mmio + ICP_MULTI_INT_EN);
-       writew(0x00ff, dev->mmio + ICP_MULTI_INT_STAT);
+       writew(ICP_MULTI_INT_MASK, dev->mmio + ICP_MULTI_INT_STAT);
 
-       /* Set DACs to 0..5V range and 0V output */
+       /* Reset the analog output channels to 0V */
        for (i = 0; i < 4; i++) {
-               devpriv->DacCmdStatus &= 0xfcce;
+               unsigned int dac_csr = ICP_MULTI_DAC_CSR_CHAN(i);
 
-               /*  Set channel number */
-               devpriv->DacCmdStatus |= (i << 8);
+               /* Select channel and 0..5V range */
+               writew(dac_csr, dev->mmio + ICP_MULTI_DAC_CSR);
 
-               /*  Output 0V */
+               /* Output 0V */
                writew(0, dev->mmio + ICP_MULTI_AO);
 
-               /*  Set start conversion bit */
-               devpriv->DacCmdStatus |= DAC_ST;
-
-               /*  Output to command / status register */
-               writew(devpriv->DacCmdStatus, dev->mmio + ICP_MULTI_DAC_CSR);
-
-               /*  Delay to allow DAC time to recover */
+               /* Set start conversion bit to write data to channel */
+               writew(dac_csr | ICP_MULTI_DAC_CSR_ST,
+                      dev->mmio + ICP_MULTI_DAC_CSR);
                udelay(1);
        }
 
@@ -446,14 +253,9 @@ static int icp_multi_auto_attach(struct comedi_device *dev,
                                 unsigned long context_unused)
 {
        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
-       struct icp_multi_private *devpriv;
        struct comedi_subdevice *s;
        int ret;
 
-       devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
-       if (!devpriv)
-               return -ENOMEM;
-
        ret = comedi_pci_enable(dev);
        if (ret)
                return ret;
@@ -462,85 +264,60 @@ static int icp_multi_auto_attach(struct comedi_device *dev,
        if (!dev->mmio)
                return -ENOMEM;
 
-       ret = comedi_alloc_subdevices(dev, 5);
+       ret = comedi_alloc_subdevices(dev, 4);
        if (ret)
                return ret;
 
        icp_multi_reset(dev);
 
-       if (pcidev->irq) {
-               ret = request_irq(pcidev->irq, interrupt_service_icp_multi,
-                                 IRQF_SHARED, dev->board_name, dev);
-               if (ret == 0)
-                       dev->irq = pcidev->irq;
-       }
-
+       /* Analog Input subdevice */
        s = &dev->subdevices[0];
-       dev->read_subdev = s;
-       s->type = COMEDI_SUBD_AI;
-       s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
-       s->n_chan = 16;
-       s->maxdata = 0x0fff;
-       s->len_chanlist = 16;
-       s->range_table = &range_analog;
-       s->insn_read = icp_multi_insn_read_ai;
-
+       s->type         = COMEDI_SUBD_AI;
+       s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
+       s->n_chan       = 16;
+       s->maxdata      = 0x0fff;
+       s->range_table  = &icp_multi_ranges;
+       s->insn_read    = icp_multi_ai_insn_read;
+
+       /* Analog Output subdevice */
        s = &dev->subdevices[1];
-       s->type = COMEDI_SUBD_AO;
-       s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
-       s->n_chan = 4;
-       s->maxdata = 0x0fff;
-       s->len_chanlist = 4;
-       s->range_table = &range_analog;
-       s->insn_write = icp_multi_ao_insn_write;
+       s->type         = COMEDI_SUBD_AO;
+       s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+       s->n_chan       = 4;
+       s->maxdata      = 0x0fff;
+       s->range_table  = &icp_multi_ranges;
+       s->insn_write   = icp_multi_ao_insn_write;
 
        ret = comedi_alloc_subdev_readback(s);
        if (ret)
                return ret;
 
+       /* Digital Input subdevice */
        s = &dev->subdevices[2];
-       s->type = COMEDI_SUBD_DI;
-       s->subdev_flags = SDF_READABLE;
-       s->n_chan = 16;
-       s->maxdata = 1;
-       s->len_chanlist = 16;
-       s->range_table = &range_digital;
-       s->insn_bits = icp_multi_insn_bits_di;
-
+       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    = icp_multi_di_insn_bits;
+
+       /* Digital Output subdevice */
        s = &dev->subdevices[3];
-       s->type = COMEDI_SUBD_DO;
-       s->subdev_flags = SDF_WRITABLE;
-       s->n_chan = 8;
-       s->maxdata = 1;
-       s->len_chanlist = 8;
-       s->range_table = &range_digital;
-       s->insn_bits = icp_multi_insn_bits_do;
-
-       s = &dev->subdevices[4];
-       s->type = COMEDI_SUBD_COUNTER;
-       s->subdev_flags = SDF_WRITABLE;
-       s->n_chan = 4;
-       s->maxdata = 0xffff;
-       s->len_chanlist = 4;
-       s->state = 0;
-       s->insn_read = icp_multi_insn_read_ctr;
-       s->insn_write = icp_multi_insn_write_ctr;
+       s->type         = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITABLE;
+       s->n_chan       = 8;
+       s->maxdata      = 1;
+       s->range_table  = &range_digital;
+       s->insn_bits    = icp_multi_do_insn_bits;
 
        return 0;
 }
 
-static void icp_multi_detach(struct comedi_device *dev)
-{
-       if (dev->mmio)
-               icp_multi_reset(dev);
-       comedi_pci_detach(dev);
-}
-
 static struct comedi_driver icp_multi_driver = {
        .driver_name    = "icp_multi",
        .module         = THIS_MODULE,
        .auto_attach    = icp_multi_auto_attach,
-       .detach         = icp_multi_detach,
+       .detach         = comedi_pci_detach,
 };
 
 static int icp_multi_pci_probe(struct pci_dev *dev,
@@ -564,5 +341,5 @@ static struct pci_driver icp_multi_pci_driver = {
 module_comedi_pci_driver(icp_multi_driver, icp_multi_pci_driver);
 
 MODULE_AUTHOR("Comedi http://www.comedi.org");
-MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_DESCRIPTION("Comedi driver for Inova ICP_MULTI board");
 MODULE_LICENSE("GPL");