staging: comedi: adv_pci1710: tidy up digital input and output subdevice init
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / adv_pci1710.c
index d02df7d0c629cdf8198f01439e8b04d9a2e0d601..297a560c26f0df6ad002fa4b85c026c161d4cd67 100644 (file)
@@ -191,112 +191,89 @@ enum pci1710_boardid {
 
 struct boardtype {
        const char *name;       /*  board name */
-       char have_irq;          /*  1=card support IRQ */
        char cardtype;          /*  0=1710& co. 2=1713, ... */
        int n_aichan;           /*  num of A/D chans */
-       int n_aichand;          /*  num of A/D chans in diff mode */
-       int n_aochan;           /*  num of D/A chans */
-       int n_dichan;           /*  num of DI chans */
-       int n_dochan;           /*  num of DO chans */
-       int n_counter;          /*  num of counters */
-       int ai_maxdata;         /*  resolution of A/D */
-       int ao_maxdata;         /*  resolution of D/A */
        const struct comedi_lrange *rangelist_ai;       /*  rangelist for A/D */
        const char *rangecode_ai;       /*  range codes for programming */
        const struct comedi_lrange *rangelist_ao;       /*  rangelist for D/A */
-       unsigned int ai_ns_min; /*  max sample speed of card v ns */
-       unsigned int fifo_half_size;    /*  size of FIFO/2 */
+       unsigned int has_irq:1;
+       unsigned int has_large_fifo:1;  /* 4K or 1K FIFO */
+       unsigned int has_diff_ai:1;
+       unsigned int has_ao:1;
+       unsigned int has_di_do:1;
+       unsigned int has_counter:1;
 };
 
 static const struct boardtype boardtypes[] = {
        [BOARD_PCI1710] = {
                .name           = "pci1710",
-               .have_irq       = 1,
                .cardtype       = TYPE_PCI171X,
                .n_aichan       = 16,
-               .n_aichand      = 8,
-               .n_aochan       = 2,
-               .n_dichan       = 16,
-               .n_dochan       = 16,
-               .n_counter      = 1,
-               .ai_maxdata     = 0x0fff,
-               .ao_maxdata     = 0x0fff,
                .rangelist_ai   = &range_pci1710_3,
                .rangecode_ai   = range_codes_pci1710_3,
                .rangelist_ao   = &range_pci171x_da,
-               .ai_ns_min      = 10000,
-               .fifo_half_size = 2048,
+               .has_irq        = 1,
+               .has_large_fifo = 1,
+               .has_diff_ai    = 1,
+               .has_ao         = 1,
+               .has_di_do      = 1,
+               .has_counter    = 1,
        },
        [BOARD_PCI1710HG] = {
                .name           = "pci1710hg",
-               .have_irq       = 1,
                .cardtype       = TYPE_PCI171X,
                .n_aichan       = 16,
-               .n_aichand      = 8,
-               .n_aochan       = 2,
-               .n_dichan       = 16,
-               .n_dochan       = 16,
-               .n_counter      = 1,
-               .ai_maxdata     = 0x0fff,
-               .ao_maxdata     = 0x0fff,
                .rangelist_ai   = &range_pci1710hg,
                .rangecode_ai   = range_codes_pci1710hg,
                .rangelist_ao   = &range_pci171x_da,
-               .ai_ns_min      = 10000,
-               .fifo_half_size = 2048,
+               .has_irq        = 1,
+               .has_large_fifo = 1,
+               .has_diff_ai    = 1,
+               .has_ao         = 1,
+               .has_di_do      = 1,
+               .has_counter    = 1,
        },
        [BOARD_PCI1711] = {
                .name           = "pci1711",
-               .have_irq       = 1,
                .cardtype       = TYPE_PCI171X,
                .n_aichan       = 16,
-               .n_aochan       = 2,
-               .n_dichan       = 16,
-               .n_dochan       = 16,
-               .n_counter      = 1,
-               .ai_maxdata     = 0x0fff,
-               .ao_maxdata     = 0x0fff,
                .rangelist_ai   = &range_pci17x1,
                .rangecode_ai   = range_codes_pci17x1,
                .rangelist_ao   = &range_pci171x_da,
-               .ai_ns_min      = 10000,
-               .fifo_half_size = 512,
+               .has_irq        = 1,
+               .has_ao         = 1,
+               .has_di_do      = 1,
+               .has_counter    = 1,
        },
        [BOARD_PCI1713] = {
                .name           = "pci1713",
-               .have_irq       = 1,
                .cardtype       = TYPE_PCI1713,
                .n_aichan       = 32,
-               .n_aichand      = 16,
-               .ai_maxdata     = 0x0fff,
                .rangelist_ai   = &range_pci1710_3,
                .rangecode_ai   = range_codes_pci1710_3,
-               .ai_ns_min      = 10000,
-               .fifo_half_size = 2048,
+               .has_irq        = 1,
+               .has_large_fifo = 1,
+               .has_diff_ai    = 1,
        },
        [BOARD_PCI1720] = {
                .name           = "pci1720",
                .cardtype       = TYPE_PCI1720,
-               .n_aochan       = 4,
-               .ao_maxdata     = 0x0fff,
                .rangelist_ao   = &range_pci1720,
+               .has_ao         = 1,
        },
        [BOARD_PCI1731] = {
                .name           = "pci1731",
-               .have_irq       = 1,
                .cardtype       = TYPE_PCI171X,
                .n_aichan       = 16,
-               .n_dichan       = 16,
-               .n_dochan       = 16,
-               .ai_maxdata     = 0x0fff,
                .rangelist_ai   = &range_pci17x1,
                .rangecode_ai   = range_codes_pci17x1,
-               .ai_ns_min      = 10000,
-               .fifo_half_size = 512,
+               .has_irq        = 1,
+               .has_di_do      = 1,
        },
 };
 
 struct pci1710_private {
+       unsigned int max_samples;
        unsigned int CntrlReg;  /*  Control register */
        unsigned char ai_et;
        unsigned int ai_et_CntrlReg;
@@ -308,7 +285,6 @@ struct pci1710_private {
        unsigned int act_chanlist[32];  /*  list of scanned channel */
        unsigned char saved_seglen;     /* len of the non-repeating chanlist */
        unsigned char da_ranges;        /*  copy of D/A outpit range register */
-       unsigned short ao_data[4];      /*  data output buffer */
        unsigned int cnt0_write_wait;   /* after a write, wait for update of the
                                         * internal state */
 };
@@ -490,73 +466,43 @@ static int pci171x_insn_read_ai(struct comedi_device *dev,
        return ret ? ret : insn->n;
 }
 
-/*
-==============================================================================
-*/
-static int pci171x_insn_write_ao(struct comedi_device *dev,
+static int pci171x_ao_insn_write(struct comedi_device *dev,
                                 struct comedi_subdevice *s,
-                                struct comedi_insn *insn, unsigned int *data)
+                                struct comedi_insn *insn,
+                                unsigned int *data)
 {
        struct pci1710_private *devpriv = dev->private;
-       unsigned int val;
-       int n, chan, range, ofs;
-
-       chan = CR_CHAN(insn->chanspec);
-       range = CR_RANGE(insn->chanspec);
-       if (chan) {
-               devpriv->da_ranges &= 0xfb;
-               devpriv->da_ranges |= (range << 2);
-               outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
-               ofs = PCI171x_DA2;
-       } else {
-               devpriv->da_ranges &= 0xfe;
-               devpriv->da_ranges |= range;
-               outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
-               ofs = PCI171x_DA1;
-       }
-       val = devpriv->ao_data[chan];
-
-       for (n = 0; n < insn->n; n++) {
-               val = data[n];
-               outw(val, dev->iobase + ofs);
-       }
-
-       devpriv->ao_data[chan] = val;
-
-       return n;
+       unsigned int chan = CR_CHAN(insn->chanspec);
+       unsigned int range = CR_RANGE(insn->chanspec);
+       unsigned int reg = chan ? PCI171x_DA2 : PCI171x_DA1;
+       unsigned int val = s->readback[chan];
+       int i;
 
-}
+       devpriv->da_ranges &= ~(1 << (chan << 1));
+       devpriv->da_ranges |= (range << (chan << 1));
+       outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
 
-/*
-==============================================================================
-*/
-static int pci171x_insn_read_ao(struct comedi_device *dev,
-                               struct comedi_subdevice *s,
-                               struct comedi_insn *insn, unsigned int *data)
-{
-       struct pci1710_private *devpriv = dev->private;
-       int n, chan;
+       for (i = 0; i < insn->n; i++) {
+               val = data[i];
+               outw(val, dev->iobase + reg);
+       }
 
-       chan = CR_CHAN(insn->chanspec);
-       for (n = 0; n < insn->n; n++)
-               data[n] = devpriv->ao_data[chan];
+       s->readback[chan] = val;
 
-       return n;
+       return insn->n;
 }
 
-/*
-==============================================================================
-*/
-static int pci171x_insn_bits_di(struct comedi_device *dev,
+static int pci171x_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)
 {
        data[1] = inw(dev->iobase + PCI171x_DI);
 
        return insn->n;
 }
 
-static int pci171x_insn_bits_do(struct comedi_device *dev,
+static int pci171x_do_insn_bits(struct comedi_device *dev,
                                struct comedi_subdevice *s,
                                struct comedi_insn *insn,
                                unsigned int *data)
@@ -584,10 +530,7 @@ static void pci171x_start_pacer(struct comedi_device *dev,
        }
 }
 
-/*
-==============================================================================
-*/
-static int pci171x_insn_counter_read(struct comedi_device *dev,
+static int pci171x_counter_insn_read(struct comedi_device *dev,
                                     struct comedi_subdevice *s,
                                     struct comedi_insn *insn,
                                     unsigned int *data)
@@ -608,10 +551,7 @@ static int pci171x_insn_counter_read(struct comedi_device *dev,
        return insn->n;
 }
 
-/*
-==============================================================================
-*/
-static int pci171x_insn_counter_write(struct comedi_device *dev,
+static int pci171x_counter_insn_write(struct comedi_device *dev,
                                      struct comedi_subdevice *s,
                                      struct comedi_insn *insn,
                                      unsigned int *data)
@@ -638,10 +578,7 @@ static int pci171x_insn_counter_write(struct comedi_device *dev,
        return insn->n;
 }
 
-/*
-==============================================================================
-*/
-static int pci171x_insn_counter_config(struct comedi_device *dev,
+static int pci171x_counter_insn_config(struct comedi_device *dev,
                                       struct comedi_subdevice *s,
                                       struct comedi_insn *insn,
                                       unsigned int *data)
@@ -677,35 +614,34 @@ static int pci171x_insn_counter_config(struct comedi_device *dev,
        return 1;
 }
 
-/*
-==============================================================================
-*/
-static int pci1720_insn_write_ao(struct comedi_device *dev,
+static int pci1720_ao_insn_write(struct comedi_device *dev,
                                 struct comedi_subdevice *s,
-                                struct comedi_insn *insn, unsigned int *data)
+                                struct comedi_insn *insn,
+                                unsigned int *data)
 {
        struct pci1710_private *devpriv = dev->private;
+       unsigned int chan = CR_CHAN(insn->chanspec);
+       unsigned int range = CR_RANGE(insn->chanspec);
        unsigned int val;
-       int n, rangereg, chan;
-
-       chan = CR_CHAN(insn->chanspec);
-       rangereg = devpriv->da_ranges & (~(0x03 << (chan << 1)));
-       rangereg |= (CR_RANGE(insn->chanspec) << (chan << 1));
-       if (rangereg != devpriv->da_ranges) {
-               outb(rangereg, dev->iobase + PCI1720_RANGE);
-               devpriv->da_ranges = rangereg;
+       int i;
+
+       val = devpriv->da_ranges & (~(0x03 << (chan << 1)));
+       val |= (range << (chan << 1));
+       if (val != devpriv->da_ranges) {
+               outb(val, dev->iobase + PCI1720_RANGE);
+               devpriv->da_ranges = val;
        }
-       val = devpriv->ao_data[chan];
 
-       for (n = 0; n < insn->n; n++) {
-               val = data[n];
+       val = s->readback[chan];
+       for (i = 0; i < insn->n; i++) {
+               val = data[i];
                outw(val, dev->iobase + PCI1720_DA0 + (chan << 1));
-               outb(0, dev->iobase + PCI1720_SYNCOUT); /*  update outputs */
+               outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
        }
 
-       devpriv->ao_data[chan] = val;
+       s->readback[chan] = val;
 
-       return n;
+       return insn->n;
 }
 
 /*
@@ -743,14 +679,14 @@ static void pci1710_handle_every_sample(struct comedi_device *dev,
        status = inw(dev->iobase + PCI171x_STATUS);
        if (status & Status_FE) {
                dev_dbg(dev->class_dev, "A/D FIFO empty (%4x)\n", status);
-               s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+               s->async->events |= COMEDI_CB_ERROR;
                comedi_handle_events(dev, s);
                return;
        }
        if (status & Status_FF) {
                dev_dbg(dev->class_dev,
                        "A/D FIFO Full status (Fatal Error!) (%4x)\n", status);
-               s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+               s->async->events |= COMEDI_CB_ERROR;
                comedi_handle_events(dev, s);
                return;
        }
@@ -761,7 +697,7 @@ static void pci1710_handle_every_sample(struct comedi_device *dev,
                val = inw(dev->iobase + PCI171x_AD_DATA);
                ret = pci171x_ai_dropout(dev, s, s->async->cur_chan, val);
                if (ret) {
-                       s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+                       s->async->events |= COMEDI_CB_ERROR;
                        break;
                }
 
@@ -795,7 +731,7 @@ static int move_block_from_fifo(struct comedi_device *dev,
 
                ret = pci171x_ai_dropout(dev, s, s->async->cur_chan, val);
                if (ret) {
-                       s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+                       s->async->events |= COMEDI_CB_ERROR;
                        return ret;
                }
 
@@ -808,7 +744,7 @@ static int move_block_from_fifo(struct comedi_device *dev,
 static void pci1710_handle_fifo(struct comedi_device *dev,
                                struct comedi_subdevice *s)
 {
-       const struct boardtype *this_board = dev->board_ptr;
+       struct pci1710_private *devpriv = dev->private;
        struct comedi_cmd *cmd = &s->async->cmd;
        unsigned int nsamples;
        unsigned int m;
@@ -816,19 +752,19 @@ static void pci1710_handle_fifo(struct comedi_device *dev,
        m = inw(dev->iobase + PCI171x_STATUS);
        if (!(m & Status_FH)) {
                dev_dbg(dev->class_dev, "A/D FIFO not half full! (%4x)\n", m);
-               s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+               s->async->events |= COMEDI_CB_ERROR;
                comedi_handle_events(dev, s);
                return;
        }
        if (m & Status_FF) {
                dev_dbg(dev->class_dev,
                        "A/D FIFO Full status (Fatal Error!) (%4x)\n", m);
-               s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+               s->async->events |= COMEDI_CB_ERROR;
                comedi_handle_events(dev, s);
                return;
        }
 
-       nsamples = this_board->fifo_half_size;
+       nsamples = devpriv->max_samples;
        if (comedi_samples_to_bytes(s, nsamples) >= s->async->prealloc_bufsz) {
                m = comedi_bytes_to_samples(s, s->async->prealloc_bufsz);
                if (move_block_from_fifo(dev, s, m, 0))
@@ -944,7 +880,6 @@ static int pci171x_ai_cmdtest(struct comedi_device *dev,
                              struct comedi_subdevice *s,
                              struct comedi_cmd *cmd)
 {
-       const struct boardtype *this_board = dev->board_ptr;
        struct pci1710_private *devpriv = dev->private;
        int err = 0;
        unsigned int arg;
@@ -977,8 +912,7 @@ static int pci171x_ai_cmdtest(struct comedi_device *dev,
        err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 
        if (cmd->convert_src == TRIG_TIMER)
-               err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
-                                                this_board->ai_ns_min);
+               err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 10000);
        else    /* TRIG_FOLLOW */
                err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
 
@@ -1033,15 +967,11 @@ static int pci171x_reset(struct comedi_device *dev)
        outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear INT request */
        pci171x_start_pacer(dev, false);
        devpriv->da_ranges = 0;
-       if (this_board->n_aochan) {
+       if (this_board->has_ao) {
                /* set DACs to 0..5V */
                outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
                outw(0, dev->iobase + PCI171x_DA1); /* set DA outputs to 0V */
-               devpriv->ao_data[0] = 0x0000;
-               if (this_board->n_aochan > 1) {
-                       outw(0, dev->iobase + PCI171x_DA2);
-                       devpriv->ao_data[1] = 0x0000;
-               }
+               outw(0, dev->iobase + PCI171x_DA2);
        }
        outw(0, dev->iobase + PCI171x_DO);      /*  digital outputs to 0 */
        outb(0, dev->iobase + PCI171x_CLRFIFO); /*  clear FIFO */
@@ -1066,10 +996,7 @@ static int pci1720_reset(struct comedi_device *dev)
        outw(0x0800, dev->iobase + PCI1720_DA2);
        outw(0x0800, dev->iobase + PCI1720_DA3);
        outb(0, dev->iobase + PCI1720_SYNCOUT); /*  update outputs */
-       devpriv->ao_data[0] = 0x0800;
-       devpriv->ao_data[1] = 0x0800;
-       devpriv->ao_data[2] = 0x0800;
-       devpriv->ao_data[3] = 0x0800;
+
        return 0;
 }
 
@@ -1116,13 +1043,11 @@ static int pci1710_auto_attach(struct comedi_device *dev,
        n_subdevices = 0;
        if (this_board->n_aichan)
                n_subdevices++;
-       if (this_board->n_aochan)
+       if (this_board->has_ao)
                n_subdevices++;
-       if (this_board->n_dichan)
-               n_subdevices++;
-       if (this_board->n_dochan)
-               n_subdevices++;
-       if (this_board->n_counter)
+       if (this_board->has_di_do)
+               n_subdevices += 2;
+       if (this_board->has_counter)
                n_subdevices++;
 
        ret = comedi_alloc_subdevices(dev, n_subdevices);
@@ -1131,7 +1056,7 @@ static int pci1710_auto_attach(struct comedi_device *dev,
 
        pci1710_reset(dev);
 
-       if (this_board->have_irq && pcidev->irq) {
+       if (this_board->has_irq && pcidev->irq) {
                ret = request_irq(pcidev->irq, interrupt_service_pci1710,
                                  IRQF_SHARED, dev->board_name, dev);
                if (ret == 0)
@@ -1144,10 +1069,10 @@ static int pci1710_auto_attach(struct comedi_device *dev,
                s = &dev->subdevices[subdev];
                s->type = COMEDI_SUBD_AI;
                s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
-               if (this_board->n_aichand)
+               if (this_board->has_diff_ai)
                        s->subdev_flags |= SDF_DIFF;
                s->n_chan = this_board->n_aichan;
-               s->maxdata = this_board->ai_maxdata;
+               s->maxdata = 0x0fff;
                s->range_table = this_board->rangelist_ai;
                s->insn_read = pci171x_insn_read_ai;
                if (dev->irq) {
@@ -1161,64 +1086,74 @@ static int pci1710_auto_attach(struct comedi_device *dev,
                subdev++;
        }
 
-       if (this_board->n_aochan) {
+       if (this_board->has_ao) {
                s = &dev->subdevices[subdev];
                s->type = COMEDI_SUBD_AO;
                s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
-               s->n_chan = this_board->n_aochan;
-               s->maxdata = this_board->ao_maxdata;
-               s->len_chanlist = this_board->n_aochan;
+               s->maxdata = 0x0fff;
                s->range_table = this_board->rangelist_ao;
                switch (this_board->cardtype) {
                case TYPE_PCI1720:
-                       s->insn_write = pci1720_insn_write_ao;
+                       s->n_chan = 4;
+                       s->insn_write = pci1720_ao_insn_write;
                        break;
                default:
-                       s->insn_write = pci171x_insn_write_ao;
+                       s->n_chan = 2;
+                       s->insn_write = pci171x_ao_insn_write;
                        break;
                }
-               s->insn_read = pci171x_insn_read_ao;
+
+               ret = comedi_alloc_subdev_readback(s);
+               if (ret)
+                       return ret;
+
+               /* initialize the readback values to match the board reset */
+               if (this_board->cardtype == TYPE_PCI1720) {
+                       int i;
+
+                       for (i = 0; i < s->n_chan; i++)
+                               s->readback[i] = 0x0800;
+               }
+
                subdev++;
        }
 
-       if (this_board->n_dichan) {
+       if (this_board->has_di_do) {
                s = &dev->subdevices[subdev];
-               s->type = COMEDI_SUBD_DI;
-               s->subdev_flags = SDF_READABLE;
-               s->n_chan = this_board->n_dichan;
-               s->maxdata = 1;
-               s->len_chanlist = this_board->n_dichan;
-               s->range_table = &range_digital;
-               s->insn_bits = pci171x_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    = pci171x_di_insn_bits;
                subdev++;
-       }
 
-       if (this_board->n_dochan) {
                s = &dev->subdevices[subdev];
-               s->type = COMEDI_SUBD_DO;
-               s->subdev_flags = SDF_WRITABLE;
-               s->n_chan = this_board->n_dochan;
-               s->maxdata = 1;
-               s->len_chanlist = this_board->n_dochan;
-               s->range_table = &range_digital;
-               s->insn_bits = pci171x_insn_bits_do;
+               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    = pci171x_do_insn_bits;
                subdev++;
        }
 
-       if (this_board->n_counter) {
+       if (this_board->has_counter) {
                s = &dev->subdevices[subdev];
-               s->type = COMEDI_SUBD_COUNTER;
-               s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
-               s->n_chan = this_board->n_counter;
-               s->len_chanlist = this_board->n_counter;
-               s->maxdata = 0xffff;
-               s->range_table = &range_unknown;
-               s->insn_read = pci171x_insn_counter_read;
-               s->insn_write = pci171x_insn_counter_write;
-               s->insn_config = pci171x_insn_counter_config;
+               s->type         = COMEDI_SUBD_COUNTER;
+               s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+               s->n_chan       = 1;
+               s->maxdata      = 0xffff;
+               s->range_table  = &range_unknown;
+               s->insn_read    = pci171x_counter_insn_read;
+               s->insn_write   = pci171x_counter_insn_write;
+               s->insn_config  = pci171x_counter_insn_config;
                subdev++;
        }
 
+       /* max_samples is half the FIFO size (2 bytes/sample) */
+       devpriv->max_samples = (this_board->has_large_fifo) ? 2048 : 512;
+
        return 0;
 }