staging: comedi: ii_pci20kc: cleanup the ai subdevice
authorH Hartley Sweeten <hsweeten@visionengravers.com>
Wed, 24 Jul 2013 19:13:51 +0000 (12:13 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 25 Jul 2013 20:24:13 +0000 (13:24 -0700)
The PCI-200341M-1 module can both provide an analog input subdevice
in this driver. The module provides four differential input channels
with four programmable gains.

Currently the gain is configured as an option passed when the board
is attached. Rewrite the ai subdevice support functions to properly
handle the programmable gain.

For aesthetics, redefine the memory map for the module.

Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/comedi/drivers/ii_pci20kc.c

index 1119a1a33135ce311370e30a48ca33d3a7247c3b..069cd959ba4b17e84be4a4190df06ce5e38e7d57 100644 (file)
@@ -55,21 +55,6 @@ are:
 Options:
   0   Board base address
   1   IRQ
-  2   first option for module 1
-  3   second option for module 1
-  4   first option for module 2
-  5   second option for module 2
-  6   first option for module 3
-  7   second option for module 3
-
-options for PCI-20006M:
-
-options for PCI-20341M:
-  first:   Analog input gain configuration
-            0  1
-            1  10
-            2  100
-            3  200
 */
 
 #include <linux/module.h>
@@ -119,26 +104,44 @@ options for PCI-20341M:
 #define II20K_AO_MSB_REG(x)            (0x0e + ((x) * 0x08))
 #define II20K_AO_STRB_BOTH_REG         0x1b
 
-#define PCI20341_ID                    0x77
-#define PCI20xxx_EMPTY_ID              0xff
-
-#define PCI20341_INIT                  0x04
-#define PCI20341_REPMODE               0x00    /* single shot mode */
-#define PCI20341_PACER                 0x00    /* Hardware Pacer disabled */
-#define PCI20341_CONFIG_REG            0x10
-#define PCI20341_MOD_STATUS            0x01
-#define PCI20341_OPT_REG               0x11
-#define PCI20341_SET_TIME_REG          0x15
-#define PCI20341_LCHAN_ADDR_REG                0x13
-#define PCI20341_CHAN_LIST             0x80
-#define PCI20341_CC_RESET              0x1b
-#define PCI20341_CHAN_RESET            0x19
-#define PCI20341_SOFT_PACER            0x04
-#define PCI20341_STATUS_REG            0x12
-#define PCI20341_LDATA                 0x02
-#define PCI20341_DAISY_CHAIN           0x20    /* On-board inputs only */
-#define PCI20341_MUX                   0x04    /* Enable on-board MUX */
-#define PCI20341_SCANLIST              0x80    /* Channel/Gain Scan List */
+#define II20K_ID_PCI200341M_1          0x77    /* 4 AI channels */
+#define II20K_AI_STATUS_CMD_REG                0x01
+#define II20K_AI_STATUS_CMD_BUSY       (1 << 7)
+#define II20K_AI_STATUS_CMD_HW_ENA     (1 << 1)
+#define II20K_AI_STATUS_CMD_EXT_START  (1 << 0)
+#define II20K_AI_LSB_REG               0x02
+#define II20K_AI_MSB_REG               0x03
+#define II20K_AI_PACER_RESET_REG       0x04
+#define II20K_AI_16BIT_DATA_REG                0x06
+#define II20K_AI_CONF_REG              0x10
+#define II20K_AI_CONF_ENA              (1 << 2)
+#define II20K_AI_OPT_REG               0x11
+#define II20K_AI_OPT_TRIG_ENA          (1 << 5)
+#define II20K_AI_OPT_TRIG_INV          (1 << 4)
+#define II20K_AI_OPT_TIMEBASE(x)       (((x) & 0x3) << 1)
+#define II20K_AI_OPT_BURST_MODE                (1 << 0)
+#define II20K_AI_STATUS_REG            0x12
+#define II20K_AI_STATUS_INT            (1 << 7)
+#define II20K_AI_STATUS_TRIG           (1 << 6)
+#define II20K_AI_STATUS_TRIG_ENA       (1 << 5)
+#define II20K_AI_STATUS_PACER_ERR      (1 << 2)
+#define II20K_AI_STATUS_DATA_ERR       (1 << 1)
+#define II20K_AI_STATUS_SET_TIME_ERR   (1 << 0)
+#define II20K_AI_LAST_CHAN_ADDR_REG    0x13
+#define II20K_AI_CUR_ADDR_REG          0x14
+#define II20K_AI_SET_TIME_REG          0x15
+#define II20K_AI_DELAY_LSB_REG         0x16
+#define II20K_AI_DELAY_MSB_REG         0x17
+#define II20K_AI_CHAN_ADV_REG          0x18
+#define II20K_AI_CHAN_RESET_REG                0x19
+#define II20K_AI_START_TRIG_REG                0x1a
+#define II20K_AI_COUNT_RESET_REG       0x1b
+#define II20K_AI_CHANLIST_REG          0x80
+#define II20K_AI_CHANLIST_ONBOARD_ONLY (1 << 5)
+#define II20K_AI_CHANLIST_GAIN(x)      (((x) & 0x3) << 3)
+#define II20K_AI_CHANLIST_MUX_ENA      (1 << 2)
+#define II20K_AI_CHANLIST_CHAN(x)      (((x) & 0x3) << 0)
+#define II20K_AI_CHANLIST_LEN          0x80
 
 /* the AO range is set by jumpers on the 20006M module */
 static const struct comedi_lrange ii20k_ao_ranges = {
@@ -148,44 +151,20 @@ static const struct comedi_lrange ii20k_ao_ranges = {
                BIP_RANGE(10)   /* Chan 0 - W1/W3 in   Chan 1 - W2/W4 out */
        }
 };
-static const struct comedi_lrange range_bipolar0_5 = {
-       1, {
-               BIP_RANGE(0.5)
-       }
-};
-
-static const struct comedi_lrange range_bipolar0_05 = {
-       1, {
-               BIP_RANGE(0.05)
-       }
-};
-
-static const struct comedi_lrange range_bipolar0_025 = {
-       1, {
-               BIP_RANGE(0.025)
-       }
-};
 
-static const struct comedi_lrange *const ii20k_ai_ranges[] = {
-       &range_bipolar5,
-       &range_bipolar0_5,
-       &range_bipolar0_05,
-       &range_bipolar0_025,
+static const struct comedi_lrange ii20k_ai_ranges = {
+       4, {
+               BIP_RANGE(5),           /* gain 1 */
+               BIP_RANGE(0.5),         /* gain 10 */
+               BIP_RANGE(0.05),        /* gain 100 */
+               BIP_RANGE(0.025)        /* gain 200 */
+       },
 };
 
-static const int pci20341_timebase[] = { 0x00, 0x00, 0x00, 0x04 };
-static const int pci20341_settling_time[] = { 0x58, 0x58, 0x93, 0x99 };
-
 struct ii20k_ao_private {
        unsigned int last_data[2];
 };
 
-struct ii20k_ai_private {
-       int timebase;
-       int settling_time;
-       int ai_gain;
-};
-
 struct pci20xxx_private {
        void __iomem *ioaddr;
 };
@@ -241,82 +220,94 @@ static int ii20k_ao_insn_write(struct comedi_device *dev,
        return insn->n;
 }
 
+static int ii20k_ai_wait_eoc(struct comedi_device *dev,
+                            struct comedi_subdevice *s,
+                            int timeout)
+{
+       void __iomem *iobase = ii20k_module_iobase(dev, s);
+       unsigned char status;
+
+       do {
+               status = readb(iobase + II20K_AI_STATUS_REG);
+               if ((status & II20K_AI_STATUS_INT) == 0)
+                       return 0;
+       } while (timeout--);
+
+       return -ETIME;
+}
+
+static void ii20k_ai_setup(struct comedi_device *dev,
+                          struct comedi_subdevice *s,
+                          unsigned int chanspec)
+{
+       void __iomem *iobase = ii20k_module_iobase(dev, s);
+       unsigned int chan = CR_CHAN(chanspec);
+       unsigned int range = CR_RANGE(chanspec);
+       unsigned char val;
+
+       /* initialize module */
+       writeb(II20K_AI_CONF_ENA, iobase + II20K_AI_CONF_REG);
+
+       /* software conversion */
+       writeb(0, iobase + II20K_AI_STATUS_CMD_REG);
+
+       /* set the time base for the settling time counter based on the gain */
+       val = (range < 3) ? II20K_AI_OPT_TIMEBASE(0) : II20K_AI_OPT_TIMEBASE(2);
+       writeb(val, iobase + II20K_AI_OPT_REG);
+
+       /* set the settling time counter based on the gain */
+       val = (range < 2) ? 0x58 : (range < 3) ? 0x93 : 0x99;
+       writeb(val, iobase + II20K_AI_SET_TIME_REG);
+
+       /* set number of input channels */
+       writeb(1, iobase + II20K_AI_LAST_CHAN_ADDR_REG);
+
+       /* set the channel list byte */
+       val = II20K_AI_CHANLIST_ONBOARD_ONLY |
+             II20K_AI_CHANLIST_MUX_ENA |
+             II20K_AI_CHANLIST_GAIN(range) |
+             II20K_AI_CHANLIST_CHAN(chan);
+       writeb(val, iobase + II20K_AI_CHANLIST_REG);
+
+       /* reset settling time counter and trigger delay counter */
+       writeb(0, iobase + II20K_AI_COUNT_RESET_REG);
+
+       /* reset channel scanner */
+       writeb(0, iobase + II20K_AI_CHAN_RESET_REG);
+}
+
 static int ii20k_ai_insn_read(struct comedi_device *dev,
                              struct comedi_subdevice *s,
                              struct comedi_insn *insn,
                              unsigned int *data)
 {
-       struct ii20k_ai_private *ai_spriv = s->private;
        void __iomem *iobase = ii20k_module_iobase(dev, s);
-       unsigned int i = 0, j = 0;
-       int lo, hi;
-       unsigned char eoc;      /* end of conversion */
-       unsigned int clb;       /* channel list byte */
-       unsigned int boarddata;
-
-       /* write number of input channels */
-       writeb(1, iobase + PCI20341_LCHAN_ADDR_REG);
-       clb = PCI20341_DAISY_CHAIN | PCI20341_MUX | (ai_spriv->ai_gain << 3)
-           | CR_CHAN(insn->chanspec);
-       writeb(clb, iobase + PCI20341_CHAN_LIST);
+       int ret;
+       int i;
 
-       /* reset settling time counter and trigger delay counter */
-       writeb(0x00, iobase + PCI20341_CC_RESET);
+       ii20k_ai_setup(dev, s, insn->chanspec);
 
-       writeb(0x00, iobase + PCI20341_CHAN_RESET);
+       for (i = 0; i < insn->n; i++) {
+               unsigned int val;
 
-       /* generate Pacer */
+               /* generate a software start convert signal */
+               readb(iobase + II20K_AI_PACER_RESET_REG);
 
-       for (i = 0; i < insn->n; i++) {
-               /* data polling isn't the niciest way to get the data, I know,
-                * but there are only 6 cycles (mean) and it is easier than
-                * the whole interrupt stuff
-                */
-               j = 0;
-               /* generate Pacer */
-               readb(iobase + PCI20341_SOFT_PACER);
-
-               eoc = readb(iobase + PCI20341_STATUS_REG);
-               /* poll Interrupt Flag */
-               while ((eoc < 0x80) && j < 100) {
-                       j++;
-                       eoc = readb(iobase + PCI20341_STATUS_REG);
-               }
-               if (j >= 100) {
-                       dev_warn(dev->class_dev,
-                                "AI interrupt channel %i polling exit !\n", i);
-                       return -EINVAL;
-               }
-               lo = readb(iobase + PCI20341_LDATA);
-               hi = readb(iobase + PCI20341_LDATA + 1);
-               boarddata = lo + 0x100 * hi;
-
-               /* board-data -> comedi-data */
-               data[i] = (short)((boarddata + 0x8000) & 0xffff);
-       }
+               ret = ii20k_ai_wait_eoc(dev, s, 100);
+               if (ret)
+                       return ret;
 
-       return i;
-}
+               val = readb(iobase + II20K_AI_LSB_REG);
+               val |= (readb(iobase + II20K_AI_MSB_REG) << 8);
 
-static void ii20k_ai_init(struct comedi_device *dev,
-                         struct comedi_subdevice *s)
-{
-       struct ii20k_ai_private *ai_spriv = s->private;
-       void __iomem *iobase = ii20k_module_iobase(dev, s);
-       unsigned char option;
-
-       /* depends on gain, trigger, repetition mode */
-       option = ai_spriv->timebase | PCI20341_REPMODE;
-
-       /* initialize Module */
-       writeb(PCI20341_INIT, iobase + PCI20341_CONFIG_REG);
-       /* set Pacer */
-       writeb(PCI20341_PACER, iobase + PCI20341_MOD_STATUS);
-       /* option register */
-       writeb(option, iobase + PCI20341_OPT_REG);
-       /* settling time counter */
-       writeb(ai_spriv->settling_time, iobase + PCI20341_SET_TIME_REG);
-       /* trigger not implemented */
+               /* munge two's complement data */
+               val += ((s->maxdata + 1) >> 1);
+               val &= s->maxdata;
+
+               data[i] = val;
+       }
+
+       return insn->n;
 }
 
 static void ii20k_dio_config(struct comedi_device *dev,
@@ -456,13 +447,10 @@ static int ii20k_dio_insn_bits(struct comedi_device *dev,
 }
 
 static int ii20k_init_module(struct comedi_device *dev,
-                            struct comedi_subdevice *s,
-                            struct comedi_devconfig *it)
+                            struct comedi_subdevice *s)
 {
        struct ii20k_ao_private *ao_spriv;
-       struct ii20k_ai_private *ai_spriv;
        void __iomem *iobase = ii20k_module_iobase(dev, s);
-       unsigned int opt0 = it->options[(2 * s->index) + 2];
        unsigned char id;
 
        id = readb(iobase + II20K_ID_REG);
@@ -482,28 +470,15 @@ static int ii20k_init_module(struct comedi_device *dev,
                s->insn_read    = ii20k_ao_insn_read;
                s->insn_write   = ii20k_ao_insn_write;
                break;
-       case PCI20341_ID:
-               if (opt0 < 0 || opt0 > 3)
-                       opt0 = 0;
-
-               ai_spriv = comedi_alloc_spriv(s, sizeof(*ai_spriv));
-               if (!ai_spriv)
-                       return -ENOMEM;
-
-               ai_spriv->timebase = pci20341_timebase[opt0];
-               ai_spriv->settling_time = pci20341_settling_time[opt0];
-
+       case II20K_ID_PCI200341M_1:
                /* Analog Input subdevice */
                s->type         = COMEDI_SUBD_AI;
-               s->subdev_flags = SDF_READABLE;
+               s->subdev_flags = SDF_READABLE | SDF_DIFF;
                s->n_chan       = 4;
                s->maxdata      = 0xffff;
-               s->range_table  = ii20k_ai_ranges[opt0];
+               s->range_table  = &ii20k_ai_ranges;
                s->insn_read    = ii20k_ai_insn_read;
-
-               ii20k_ai_init(dev, s);
                break;
-       case PCI20xxx_EMPTY_ID:
        default:
                s->type = COMEDI_SUBD_UNUSED;
                break;
@@ -546,7 +521,7 @@ static int pci20xxx_attach(struct comedi_device *dev,
        if (id & II20K_ID_MOD1_EMPTY) {
                s->type = COMEDI_SUBD_UNUSED;
        } else {
-               ret = ii20k_init_module(dev, s, it);
+               ret = ii20k_init_module(dev, s);
                if (ret)
                        return ret;
        }
@@ -555,7 +530,7 @@ static int pci20xxx_attach(struct comedi_device *dev,
        if (id & II20K_ID_MOD2_EMPTY) {
                s->type = COMEDI_SUBD_UNUSED;
        } else {
-               ret = ii20k_init_module(dev, s, it);
+               ret = ii20k_init_module(dev, s);
                if (ret)
                        return ret;
        }
@@ -564,7 +539,7 @@ static int pci20xxx_attach(struct comedi_device *dev,
        if (id & II20K_ID_MOD3_EMPTY) {
                s->type = COMEDI_SUBD_UNUSED;
        } else {
-               ret = ii20k_init_module(dev, s, it);
+               ret = ii20k_init_module(dev, s);
                if (ret)
                        return ret;
        }