]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/staging/comedi/drivers/pcl726.c
staging: comedi: pcl726: remove digital i/o register offsets from boardinfo
[karo-tx-linux.git] / drivers / staging / comedi / drivers / pcl726.c
index a4d0bcc31e52df22290fbfb174f08eac5f89f3c5..da5df5a79b676b40af0bfac006d266934a0dce61 100644 (file)
@@ -63,26 +63,25 @@ Interrupts are not supported.
 */
 
 #include <linux/module.h>
-#include "../comedidev.h"
+#include <linux/interrupt.h>
 
-#undef ACL6126_IRQ             /* no interrupt support (yet) */
+#include "../comedidev.h"
 
 #define PCL726_SIZE 16
 #define PCL727_SIZE 32
 #define PCL728_SIZE 8
 
-#define PCL726_DAC0_HI 0
-#define PCL726_DAC0_LO 1
+#define PCL726_AO_MSB_REG(x)   (0x00 + ((x) * 2))
+#define PCL726_AO_LSB_REG(x)   (0x01 + ((x) * 2))
+#define PCL726_DO_MSB_REG      0x0c
+#define PCL726_DO_LSB_REG      0x0d
+#define PCL726_DI_MSB_REG      0x0e
+#define PCL726_DI_LSB_REG      0x0f
 
-#define PCL726_DO_HI 12
-#define PCL726_DO_LO 13
-#define PCL726_DI_HI 14
-#define PCL726_DI_LO 15
-
-#define PCL727_DO_HI 24
-#define PCL727_DO_LO 25
-#define PCL727_DI_HI  0
-#define PCL727_DI_LO  1
+#define PCL727_DI_MSB_REG      0x00
+#define PCL727_DI_LSB_REG      0x01
+#define PCL727_DO_MSB_REG      0x18
+#define PCL727_DO_LSB_REG      0x19
 
 static const struct comedi_lrange *const rangelist_726[] = {
        &range_unipolar5, &range_unipolar10,
@@ -109,120 +108,161 @@ struct pcl726_board {
        int num_of_ranges;      /*  num of ranges */
        unsigned int IRQbits;   /*  allowed interrupts */
        unsigned int io_range;  /*  len of IO space */
-       char have_dio;          /*  1=card have DI/DO ports */
-       int di_hi;              /*  ports for DI/DO operations */
-       int di_lo;
-       int do_hi;
-       int do_lo;
+       unsigned int have_dio:1;
+       unsigned int is_pcl727:1;
        const struct comedi_lrange *const *range_type_list;
        /*  list of supported ranges */
 };
 
 static const struct pcl726_board boardtypes[] = {
-       {"pcl726", 6, 6, 0x0000, PCL726_SIZE, 1,
-        PCL726_DI_HI, PCL726_DI_LO, PCL726_DO_HI, PCL726_DO_LO,
-        &rangelist_726[0],},
-       {"pcl727", 12, 4, 0x0000, PCL727_SIZE, 1,
-        PCL727_DI_HI, PCL727_DI_LO, PCL727_DO_HI, PCL727_DO_LO,
-        &rangelist_727[0],},
-       {"pcl728", 2, 6, 0x0000, PCL728_SIZE, 0,
-        0, 0, 0, 0,
-        &rangelist_728[0],},
-       {"acl6126", 6, 5, 0x96e8, PCL726_SIZE, 1,
-        PCL726_DI_HI, PCL726_DI_LO, PCL726_DO_HI, PCL726_DO_LO,
-        &rangelist_726[0],},
-       {"acl6128", 2, 6, 0x0000, PCL728_SIZE, 0,
-        0, 0, 0, 0,
-        &rangelist_728[0],},
+       {
+               .name           = "pcl726",
+               .n_aochan       = 6,
+               .num_of_ranges  = 6,
+               .io_range       = PCL726_SIZE,
+               .have_dio       = 1,
+               .range_type_list = &rangelist_726[0],
+       }, {
+               .name           = "pcl727",
+               .n_aochan       = 12,
+               .num_of_ranges  = 4,
+               .io_range       = PCL727_SIZE,
+               .have_dio       = 1,
+               .is_pcl727      = 1,
+               .range_type_list = &rangelist_727[0],
+       }, {
+               .name           = "pcl728",
+               .n_aochan       = 2,
+               .num_of_ranges  = 6,
+               .io_range       = PCL728_SIZE,
+               .range_type_list = &rangelist_728[0],
+       }, {
+               .name           = "acl6126",
+               .n_aochan       = 6,
+               .num_of_ranges  = 5,
+               .IRQbits        = 0x96e8,
+               .io_range       = PCL726_SIZE,
+               .have_dio       = 1,
+               .range_type_list = &rangelist_726[0],
+       }, {
+               .name           = "acl6128",
+               .n_aochan       = 2,
+               .num_of_ranges  = 6,
+               .io_range       = PCL728_SIZE,
+               .range_type_list = &rangelist_728[0],
+       },
 };
 
 struct pcl726_private {
-
-       int bipolar[12];
        const struct comedi_lrange *rangelist[12];
        unsigned int ao_readback[12];
 };
 
-static int pcl726_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
-                         struct comedi_insn *insn, unsigned int *data)
+static irqreturn_t pcl818_interrupt(int irq, void *d)
+{
+       return IRQ_HANDLED;
+}
+
+static int pcl726_ao_insn_write(struct comedi_device *dev,
+                               struct comedi_subdevice *s,
+                               struct comedi_insn *insn,
+                               unsigned int *data)
 {
        struct pcl726_private *devpriv = dev->private;
-       int hi, lo;
-       int n;
-       int chan = CR_CHAN(insn->chanspec);
-
-       for (n = 0; n < insn->n; n++) {
-               lo = data[n] & 0xff;
-               hi = (data[n] >> 8) & 0xf;
-               if (devpriv->bipolar[chan])
-                       hi ^= 0x8;
-               /*
-                * the programming info did not say which order
-                * to write bytes.  switch the order of the next
-                * two lines if you get glitches.
-                */
-               outb(hi, dev->iobase + PCL726_DAC0_HI + 2 * chan);
-               outb(lo, dev->iobase + PCL726_DAC0_LO + 2 * chan);
-               devpriv->ao_readback[chan] = data[n];
+       unsigned int chan = CR_CHAN(insn->chanspec);
+       unsigned int range = CR_RANGE(insn->chanspec);
+       unsigned int val;
+       int i;
+
+       for (i = 0; i < insn->n; i++) {
+               val = data[i];
+               devpriv->ao_readback[chan] = val;
+
+               /* bipolar data to the DAC is two's complement */
+               if (comedi_chan_range_is_bipolar(s, chan, range))
+                       val = comedi_offset_munge(s, val);
+
+               /* order is important, MSB then LSB */
+               outb((val >> 8) & 0xff, dev->iobase + PCL726_AO_MSB_REG(chan));
+               outb(val & 0xff, dev->iobase + PCL726_AO_LSB_REG(chan));
        }
 
-       return n;
+       return insn->n;
 }
 
 static int pcl726_ao_insn_read(struct comedi_device *dev,
                               struct comedi_subdevice *s,
-                              struct comedi_insn *insn, unsigned int *data)
+                              struct comedi_insn *insn,
+                              unsigned int *data)
 {
        struct pcl726_private *devpriv = dev->private;
-       int chan = CR_CHAN(insn->chanspec);
-       int n;
+       unsigned int chan = CR_CHAN(insn->chanspec);
+       int i;
+
+       for (i = 0; i < insn->n; i++)
+               data[i] = devpriv->ao_readback[chan];
 
-       for (n = 0; n < insn->n; n++)
-               data[n] = devpriv->ao_readback[chan];
-       return n;
+       return insn->n;
 }
 
 static int pcl726_di_insn_bits(struct comedi_device *dev,
                               struct comedi_subdevice *s,
-                              struct comedi_insn *insn, unsigned int *data)
+                              struct comedi_insn *insn,
+                              unsigned int *data)
 {
        const struct pcl726_board *board = comedi_board(dev);
+       unsigned int val;
 
-       data[1] = inb(dev->iobase + board->di_lo) |
-           (inb(dev->iobase + board->di_hi) << 8);
+       if (board->is_pcl727) {
+               val = inb(dev->iobase + PCL727_DI_LSB_REG);
+               val |= (inb(dev->iobase + PCL727_DI_MSB_REG) << 8);
+       } else {
+               val = inb(dev->iobase + PCL726_DI_LSB_REG);
+               val |= (inb(dev->iobase + PCL726_DI_MSB_REG) << 8);
+       }
+
+       data[1] = val;
 
        return insn->n;
 }
 
 static int pcl726_do_insn_bits(struct comedi_device *dev,
                               struct comedi_subdevice *s,
-                              struct comedi_insn *insn, unsigned int *data)
+                              struct comedi_insn *insn,
+                              unsigned int *data)
 {
        const struct pcl726_board *board = comedi_board(dev);
-
-       if (data[0]) {
-               s->state &= ~data[0];
-               s->state |= data[0] & data[1];
+       unsigned long io = dev->iobase;
+       unsigned int mask;
+
+       mask = comedi_dio_update_state(s, data);
+       if (mask) {
+               if (board->is_pcl727) {
+                       if (mask & 0x00ff)
+                               outb(s->state & 0xff, io + PCL727_DO_LSB_REG);
+                       if (mask & 0xff00)
+                               outb((s->state >> 8), io + PCL727_DO_MSB_REG);
+               } else {
+                       if (mask & 0x00ff)
+                               outb(s->state & 0xff, io + PCL726_DO_LSB_REG);
+                       if (mask & 0xff00)
+                               outb((s->state >> 8), io + PCL726_DO_MSB_REG);
+               }
        }
-       if (data[1] & 0x00ff)
-               outb(s->state & 0xff, dev->iobase + board->do_lo);
-       if (data[1] & 0xff00)
-               outb((s->state >> 8), dev->iobase + board->do_hi);
 
        data[1] = s->state;
 
        return insn->n;
 }
 
-static int pcl726_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static int pcl726_attach(struct comedi_device *dev,
+                        struct comedi_devconfig *it)
 {
        const struct pcl726_board *board = comedi_board(dev);
        struct pcl726_private *devpriv;
        struct comedi_subdevice *s;
-       int ret, i;
-#ifdef ACL6126_IRQ
-       unsigned int irq;
-#endif
+       int ret;
+       int i;
 
        ret = comedi_request_region(dev, it->options[0], board->io_range);
        if (ret)
@@ -232,97 +272,61 @@ static int pcl726_attach(struct comedi_device *dev, struct comedi_devconfig *it)
        if (!devpriv)
                return -ENOMEM;
 
-       for (i = 0; i < 12; i++) {
-               devpriv->bipolar[i] = 0;
-               devpriv->rangelist[i] = &range_unknown;
-       }
-
-#ifdef ACL6126_IRQ
-       irq = 0;
-       if (boardtypes[board].IRQbits != 0) {   /* board support IRQ */
-               irq = it->options[1];
-               devpriv->first_chan = 2;
-               if (irq) {      /* we want to use IRQ */
-                       if (((1 << irq) & boardtypes[board].IRQbits) == 0) {
-                               printk(KERN_WARNING
-                                       ", IRQ %d is out of allowed range,"
-                                       " DISABLING IT", irq);
-                               irq = 0;        /* Bad IRQ */
-                       } else {
-                               if (request_irq(irq, interrupt_pcl818, 0,
-                                               dev->board_name, dev)) {
-                                       printk(KERN_WARNING
-                                               ", unable to allocate IRQ %d,"
-                                               " DISABLING IT", irq);
-                                       irq = 0;        /* Can't use IRQ */
-                               } else {
-                                       printk(", irq=%d", irq);
-                               }
-                       }
+       /*
+        * Hook up the external trigger source interrupt only if the
+        * user config option is valid and the board supports interrupts.
+        */
+       if (it->options[1] && (board->IRQbits & (1 << it->options[1]))) {
+               ret = request_irq(it->options[1], pcl818_interrupt, 0,
+                                 dev->board_name, dev);
+               if (ret == 0) {
+                       /* External trigger source is from Pin-17 of CN3 */
+                       dev->irq = it->options[1];
                }
        }
 
-       dev->irq = irq;
-#endif
+       /* setup the per-channel analog output range_table_list */
+       for (i = 0; i < 12; i++) {
+               unsigned int opt = it->options[2 + i];
 
-       printk("\n");
+               if (opt < board->num_of_ranges && i < board->n_aochan)
+                       devpriv->rangelist[i] = board->range_type_list[opt];
+               else
+                       devpriv->rangelist[i] = &range_unknown;
+       }
 
-       ret = comedi_alloc_subdevices(dev, 3);
+       ret = comedi_alloc_subdevices(dev, board->have_dio ? 3 : 1);
        if (ret)
                return ret;
 
+       /* Analog Output subdevice */
        s = &dev->subdevices[0];
-       /* ao */
-       s->type = COMEDI_SUBD_AO;
-       s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
-       s->n_chan = board->n_aochan;
-       s->maxdata = 0xfff;
-       s->len_chanlist = 1;
-       s->insn_write = pcl726_ao_insn;
-       s->insn_read = pcl726_ao_insn_read;
+       s->type         = COMEDI_SUBD_AO;
+       s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
+       s->n_chan       = board->n_aochan;
+       s->maxdata      = 0x0fff;
        s->range_table_list = devpriv->rangelist;
-       for (i = 0; i < board->n_aochan; i++) {
-               int j;
-
-               j = it->options[2 + 1];
-               if ((j < 0) || (j >= board->num_of_ranges)) {
-                       printk
-                           ("Invalid range for channel %d! Must be 0<=%d<%d\n",
-                            i, j, board->num_of_ranges - 1);
-                       j = 0;
-               }
-               devpriv->rangelist[i] = board->range_type_list[j];
-               if (devpriv->rangelist[i]->range[0].min ==
-                   -devpriv->rangelist[i]->range[0].max)
-                       devpriv->bipolar[i] = 1;        /* bipolar range */
-       }
-
-       s = &dev->subdevices[1];
-       /* di */
-       if (!board->have_dio) {
-               s->type = COMEDI_SUBD_UNUSED;
-       } else {
-               s->type = COMEDI_SUBD_DI;
-               s->subdev_flags = SDF_READABLE | SDF_GROUND;
-               s->n_chan = 16;
-               s->maxdata = 1;
-               s->len_chanlist = 1;
-               s->insn_bits = pcl726_di_insn_bits;
-               s->range_table = &range_digital;
-       }
-
-       s = &dev->subdevices[2];
-       /* do */
-       if (!board->have_dio) {
-               s->type = COMEDI_SUBD_UNUSED;
-       } else {
-               s->type = COMEDI_SUBD_DO;
-               s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
-               s->n_chan = 16;
-               s->maxdata = 1;
-               s->len_chanlist = 1;
-               s->insn_bits = pcl726_do_insn_bits;
-               s->range_table = &range_digital;
+       s->insn_write   = pcl726_ao_insn_write;
+       s->insn_read    = pcl726_ao_insn_read;
+
+       if (board->have_dio) {
+               /* 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->insn_bits    = pcl726_di_insn_bits;
+               s->range_table  = &range_digital;
+
+               /* Digital Output subdevice */
+               s = &dev->subdevices[2];
+               s->type         = COMEDI_SUBD_DO;
+               s->subdev_flags = SDF_WRITABLE;
+               s->n_chan       = 16;
+               s->maxdata      = 1;
+               s->insn_bits    = pcl726_do_insn_bits;
+               s->range_table  = &range_digital;
        }
 
        return 0;