GNU Linux-libre 6.8.7-gnu
[releases.git] / drivers / comedi / drivers / pcl818.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * comedi/drivers/pcl818.c
4  *
5  * Driver: pcl818
6  * Description: Advantech PCL-818 cards, PCL-718
7  * Author: Michal Dobes <dobes@tesnet.cz>
8  * Devices: [Advantech] PCL-818L (pcl818l), PCL-818H (pcl818h),
9  *   PCL-818HD (pcl818hd), PCL-818HG (pcl818hg), PCL-818 (pcl818),
10  *   PCL-718 (pcl718)
11  * Status: works
12  *
13  * All cards have 16 SE/8 DIFF ADCs, one or two DACs, 16 DI and 16 DO.
14  * Differences are only at maximal sample speed, range list and FIFO
15  * support.
16  * The driver support AI mode 0, 1, 3 other subdevices (AO, DI, DO) support
17  * only mode 0. If DMA/FIFO/INT are disabled then AI support only mode 0.
18  * PCL-818HD and PCL-818HG support 1kword FIFO. Driver support this FIFO
19  * but this code is untested.
20  * A word or two about DMA. Driver support DMA operations at two ways:
21  * 1) DMA uses two buffers and after one is filled then is generated
22  *    INT and DMA restart with second buffer. With this mode I'm unable run
23  *    more that 80Ksamples/secs without data dropouts on K6/233.
24  * 2) DMA uses one buffer and run in autoinit mode and the data are
25  *    from DMA buffer moved on the fly with 2kHz interrupts from RTC.
26  *    This mode is used if the interrupt 8 is available for allocation.
27  *    If not, then first DMA mode is used. With this I can run at
28  *    full speed one card (100ksamples/secs) or two cards with
29  *    60ksamples/secs each (more is problem on account of ISA limitations).
30  *    To use this mode you must have compiled  kernel with disabled
31  *    "Enhanced Real Time Clock Support".
32  *    Maybe you can have problems if you use xntpd or similar.
33  *    If you've data dropouts with DMA mode 2 then:
34  *     a) disable IDE DMA
35  *     b) switch text mode console to fb.
36  *
37  *  Options for PCL-818L:
38  *  [0] - IO Base
39  *  [1] - IRQ        (0=disable, 2, 3, 4, 5, 6, 7)
40  *  [2] - DMA        (0=disable, 1, 3)
41  *  [3] - 0, 10=10MHz clock for 8254
42  *            1= 1MHz clock for 8254
43  *  [4] - 0,  5=A/D input  -5V.. +5V
44  *        1, 10=A/D input -10V..+10V
45  *  [5] - 0,  5=D/A output 0-5V  (internal reference -5V)
46  *        1, 10=D/A output 0-10V (internal reference -10V)
47  *        2    =D/A output unknown (external reference)
48  *
49  *  Options for PCL-818, PCL-818H:
50  *  [0] - IO Base
51  *  [1] - IRQ        (0=disable, 2, 3, 4, 5, 6, 7)
52  *  [2] - DMA        (0=disable, 1, 3)
53  *  [3] - 0, 10=10MHz clock for 8254
54  *            1= 1MHz clock for 8254
55  *  [4] - 0,  5=D/A output 0-5V  (internal reference -5V)
56  *        1, 10=D/A output 0-10V (internal reference -10V)
57  *        2    =D/A output unknown (external reference)
58  *
59  *  Options for PCL-818HD, PCL-818HG:
60  *  [0] - IO Base
61  *  [1] - IRQ        (0=disable, 2, 3, 4, 5, 6, 7)
62  *  [2] - DMA/FIFO  (-1=use FIFO, 0=disable both FIFO and DMA,
63  *                    1=use DMA ch 1, 3=use DMA ch 3)
64  *  [3] - 0, 10=10MHz clock for 8254
65  *            1= 1MHz clock for 8254
66  *  [4] - 0,  5=D/A output 0-5V  (internal reference -5V)
67  *        1, 10=D/A output 0-10V (internal reference -10V)
68  *        2    =D/A output unknown (external reference)
69  *
70  *  Options for PCL-718:
71  *  [0] - IO Base
72  *  [1] - IRQ        (0=disable, 2, 3, 4, 5, 6, 7)
73  *  [2] - DMA        (0=disable, 1, 3)
74  *  [3] - 0, 10=10MHz clock for 8254
75  *            1= 1MHz clock for 8254
76  *  [4] -     0=A/D Range is +/-10V
77  *            1=             +/-5V
78  *            2=             +/-2.5V
79  *            3=             +/-1V
80  *            4=             +/-0.5V
81  *            5=             user defined bipolar
82  *            6=             0-10V
83  *            7=             0-5V
84  *            8=             0-2V
85  *            9=             0-1V
86  *           10=             user defined unipolar
87  *  [5] - 0,  5=D/A outputs 0-5V  (internal reference -5V)
88  *        1, 10=D/A outputs 0-10V (internal reference -10V)
89  *            2=D/A outputs unknown (external reference)
90  *  [6] - 0, 60=max  60kHz A/D sampling
91  *        1,100=max 100kHz A/D sampling (PCL-718 with Option 001 installed)
92  *
93  */
94
95 #include <linux/module.h>
96 #include <linux/gfp.h>
97 #include <linux/delay.h>
98 #include <linux/io.h>
99 #include <linux/interrupt.h>
100 #include <linux/comedi/comedidev.h>
101 #include <linux/comedi/comedi_8254.h>
102 #include <linux/comedi/comedi_isadma.h>
103
104 /*
105  * Register I/O map
106  */
107 #define PCL818_AI_LSB_REG                       0x00
108 #define PCL818_AI_MSB_REG                       0x01
109 #define PCL818_RANGE_REG                        0x01
110 #define PCL818_MUX_REG                          0x02
111 #define PCL818_MUX_SCAN(_first, _last)          (((_last) << 4) | (_first))
112 #define PCL818_DO_DI_LSB_REG                    0x03
113 #define PCL818_AO_LSB_REG(x)                    (0x04 + ((x) * 2))
114 #define PCL818_AO_MSB_REG(x)                    (0x05 + ((x) * 2))
115 #define PCL818_STATUS_REG                       0x08
116 #define PCL818_STATUS_NEXT_CHAN_MASK            (0xf << 0)
117 #define PCL818_STATUS_INT                       BIT(4)
118 #define PCL818_STATUS_MUX                       BIT(5)
119 #define PCL818_STATUS_UNI                       BIT(6)
120 #define PCL818_STATUS_EOC                       BIT(7)
121 #define PCL818_CTRL_REG                         0x09
122 #define PCL818_CTRL_TRIG(x)                     (((x) & 0x3) << 0)
123 #define PCL818_CTRL_DISABLE_TRIG                PCL818_CTRL_TRIG(0)
124 #define PCL818_CTRL_SOFT_TRIG                   PCL818_CTRL_TRIG(1)
125 #define PCL818_CTRL_EXT_TRIG                    PCL818_CTRL_TRIG(2)
126 #define PCL818_CTRL_PACER_TRIG                  PCL818_CTRL_TRIG(3)
127 #define PCL818_CTRL_DMAE                        BIT(2)
128 #define PCL818_CTRL_IRQ(x)                      ((x) << 4)
129 #define PCL818_CTRL_INTE                        BIT(7)
130 #define PCL818_CNTENABLE_REG                    0x0a
131 #define PCL818_CNTENABLE_PACER_TRIG0            BIT(0)
132 #define PCL818_CNTENABLE_CNT0_INT_CLK           BIT(1)  /* 0=ext clk */
133 #define PCL818_DO_DI_MSB_REG                    0x0b
134 #define PCL818_TIMER_BASE                       0x0c
135
136 /* W: fifo enable/disable */
137 #define PCL818_FI_ENABLE 6
138 /* W: fifo interrupt clear */
139 #define PCL818_FI_INTCLR 20
140 /* W: fifo interrupt clear */
141 #define PCL818_FI_FLUSH 25
142 /* R: fifo status */
143 #define PCL818_FI_STATUS 25
144 /* R: one record from FIFO */
145 #define PCL818_FI_DATALO 23
146 #define PCL818_FI_DATAHI 24
147
148 #define MAGIC_DMA_WORD 0x5a5a
149
150 static const struct comedi_lrange range_pcl818h_ai = {
151         9, {
152                 BIP_RANGE(5),
153                 BIP_RANGE(2.5),
154                 BIP_RANGE(1.25),
155                 BIP_RANGE(0.625),
156                 UNI_RANGE(10),
157                 UNI_RANGE(5),
158                 UNI_RANGE(2.5),
159                 UNI_RANGE(1.25),
160                 BIP_RANGE(10)
161         }
162 };
163
164 static const struct comedi_lrange range_pcl818hg_ai = {
165         10, {
166                 BIP_RANGE(5),
167                 BIP_RANGE(0.5),
168                 BIP_RANGE(0.05),
169                 BIP_RANGE(0.005),
170                 UNI_RANGE(10),
171                 UNI_RANGE(1),
172                 UNI_RANGE(0.1),
173                 UNI_RANGE(0.01),
174                 BIP_RANGE(10),
175                 BIP_RANGE(1),
176                 BIP_RANGE(0.1),
177                 BIP_RANGE(0.01)
178         }
179 };
180
181 static const struct comedi_lrange range_pcl818l_l_ai = {
182         4, {
183                 BIP_RANGE(5),
184                 BIP_RANGE(2.5),
185                 BIP_RANGE(1.25),
186                 BIP_RANGE(0.625)
187         }
188 };
189
190 static const struct comedi_lrange range_pcl818l_h_ai = {
191         4, {
192                 BIP_RANGE(10),
193                 BIP_RANGE(5),
194                 BIP_RANGE(2.5),
195                 BIP_RANGE(1.25)
196         }
197 };
198
199 static const struct comedi_lrange range718_bipolar1 = {
200         1, {
201                 BIP_RANGE(1)
202         }
203 };
204
205 static const struct comedi_lrange range718_bipolar0_5 = {
206         1, {
207                 BIP_RANGE(0.5)
208         }
209 };
210
211 static const struct comedi_lrange range718_unipolar2 = {
212         1, {
213                 UNI_RANGE(2)
214         }
215 };
216
217 static const struct comedi_lrange range718_unipolar1 = {
218         1, {
219                 BIP_RANGE(1)
220         }
221 };
222
223 struct pcl818_board {
224         const char *name;
225         unsigned int ns_min;
226         int n_aochan;
227         const struct comedi_lrange *ai_range_type;
228         unsigned int has_dma:1;
229         unsigned int has_fifo:1;
230         unsigned int is_818:1;
231 };
232
233 static const struct pcl818_board boardtypes[] = {
234         {
235                 .name           = "pcl818l",
236                 .ns_min         = 25000,
237                 .n_aochan       = 1,
238                 .ai_range_type  = &range_pcl818l_l_ai,
239                 .has_dma        = 1,
240                 .is_818         = 1,
241         }, {
242                 .name           = "pcl818h",
243                 .ns_min         = 10000,
244                 .n_aochan       = 1,
245                 .ai_range_type  = &range_pcl818h_ai,
246                 .has_dma        = 1,
247                 .is_818         = 1,
248         }, {
249                 .name           = "pcl818hd",
250                 .ns_min         = 10000,
251                 .n_aochan       = 1,
252                 .ai_range_type  = &range_pcl818h_ai,
253                 .has_dma        = 1,
254                 .has_fifo       = 1,
255                 .is_818         = 1,
256         }, {
257                 .name           = "pcl818hg",
258                 .ns_min         = 10000,
259                 .n_aochan       = 1,
260                 .ai_range_type  = &range_pcl818hg_ai,
261                 .has_dma        = 1,
262                 .has_fifo       = 1,
263                 .is_818         = 1,
264         }, {
265                 .name           = "pcl818",
266                 .ns_min         = 10000,
267                 .n_aochan       = 2,
268                 .ai_range_type  = &range_pcl818h_ai,
269                 .has_dma        = 1,
270                 .is_818         = 1,
271         }, {
272                 .name           = "pcl718",
273                 .ns_min         = 16000,
274                 .n_aochan       = 2,
275                 .ai_range_type  = &range_unipolar5,
276                 .has_dma        = 1,
277         }, {
278                 .name           = "pcm3718",
279                 .ns_min         = 10000,
280                 .ai_range_type  = &range_pcl818h_ai,
281                 .has_dma        = 1,
282                 .is_818         = 1,
283         },
284 };
285
286 struct pcl818_private {
287         struct comedi_isadma *dma;
288         /*  manimal allowed delay between samples (in us) for actual card */
289         unsigned int ns_min;
290         /*  MUX setting for actual AI operations */
291         unsigned int act_chanlist[16];
292         unsigned int act_chanlist_len;  /*  how long is actual MUX list */
293         unsigned int act_chanlist_pos;  /*  actual position in MUX list */
294         unsigned int usefifo:1;
295         unsigned int ai_cmd_running:1;
296         unsigned int ai_cmd_canceled:1;
297 };
298
299 static void pcl818_ai_setup_dma(struct comedi_device *dev,
300                                 struct comedi_subdevice *s,
301                                 unsigned int unread_samples)
302 {
303         struct pcl818_private *devpriv = dev->private;
304         struct comedi_isadma *dma = devpriv->dma;
305         struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
306         unsigned int max_samples = comedi_bytes_to_samples(s, desc->maxsize);
307         unsigned int nsamples;
308
309         comedi_isadma_disable(dma->chan);
310
311         /*
312          * Determine dma size based on the buffer maxsize plus the number of
313          * unread samples and the number of samples remaining in the command.
314          */
315         nsamples = comedi_nsamples_left(s, max_samples + unread_samples);
316         if (nsamples > unread_samples) {
317                 nsamples -= unread_samples;
318                 desc->size = comedi_samples_to_bytes(s, nsamples);
319                 comedi_isadma_program(desc);
320         }
321 }
322
323 static void pcl818_ai_set_chan_range(struct comedi_device *dev,
324                                      unsigned int chan,
325                                      unsigned int range)
326 {
327         outb(chan, dev->iobase + PCL818_MUX_REG);
328         outb(range, dev->iobase + PCL818_RANGE_REG);
329 }
330
331 static void pcl818_ai_set_chan_scan(struct comedi_device *dev,
332                                     unsigned int first_chan,
333                                     unsigned int last_chan)
334 {
335         outb(PCL818_MUX_SCAN(first_chan, last_chan),
336              dev->iobase + PCL818_MUX_REG);
337 }
338
339 static void pcl818_ai_setup_chanlist(struct comedi_device *dev,
340                                      unsigned int *chanlist,
341                                      unsigned int seglen)
342 {
343         struct pcl818_private *devpriv = dev->private;
344         unsigned int first_chan = CR_CHAN(chanlist[0]);
345         unsigned int last_chan;
346         unsigned int range;
347         int i;
348
349         devpriv->act_chanlist_len = seglen;
350         devpriv->act_chanlist_pos = 0;
351
352         /* store range list to card */
353         for (i = 0; i < seglen; i++) {
354                 last_chan = CR_CHAN(chanlist[i]);
355                 range = CR_RANGE(chanlist[i]);
356
357                 devpriv->act_chanlist[i] = last_chan;
358
359                 pcl818_ai_set_chan_range(dev, last_chan, range);
360         }
361
362         udelay(1);
363
364         pcl818_ai_set_chan_scan(dev, first_chan, last_chan);
365 }
366
367 static void pcl818_ai_clear_eoc(struct comedi_device *dev)
368 {
369         /* writing any value clears the interrupt request */
370         outb(0, dev->iobase + PCL818_STATUS_REG);
371 }
372
373 static void pcl818_ai_soft_trig(struct comedi_device *dev)
374 {
375         /* writing any value triggers a software conversion */
376         outb(0, dev->iobase + PCL818_AI_LSB_REG);
377 }
378
379 static unsigned int pcl818_ai_get_fifo_sample(struct comedi_device *dev,
380                                               struct comedi_subdevice *s,
381                                               unsigned int *chan)
382 {
383         unsigned int val;
384
385         val = inb(dev->iobase + PCL818_FI_DATALO);
386         val |= (inb(dev->iobase + PCL818_FI_DATAHI) << 8);
387
388         if (chan)
389                 *chan = val & 0xf;
390
391         return (val >> 4) & s->maxdata;
392 }
393
394 static unsigned int pcl818_ai_get_sample(struct comedi_device *dev,
395                                          struct comedi_subdevice *s,
396                                          unsigned int *chan)
397 {
398         unsigned int val;
399
400         val = inb(dev->iobase + PCL818_AI_MSB_REG) << 8;
401         val |= inb(dev->iobase + PCL818_AI_LSB_REG);
402
403         if (chan)
404                 *chan = val & 0xf;
405
406         return (val >> 4) & s->maxdata;
407 }
408
409 static int pcl818_ai_eoc(struct comedi_device *dev,
410                          struct comedi_subdevice *s,
411                          struct comedi_insn *insn,
412                          unsigned long context)
413 {
414         unsigned int status;
415
416         status = inb(dev->iobase + PCL818_STATUS_REG);
417         if (status & PCL818_STATUS_INT)
418                 return 0;
419         return -EBUSY;
420 }
421
422 static bool pcl818_ai_write_sample(struct comedi_device *dev,
423                                    struct comedi_subdevice *s,
424                                    unsigned int chan, unsigned short val)
425 {
426         struct pcl818_private *devpriv = dev->private;
427         struct comedi_cmd *cmd = &s->async->cmd;
428         unsigned int expected_chan;
429
430         expected_chan = devpriv->act_chanlist[devpriv->act_chanlist_pos];
431         if (chan != expected_chan) {
432                 dev_dbg(dev->class_dev,
433                         "A/D mode1/3 %s - channel dropout %d!=%d !\n",
434                         (devpriv->dma) ? "DMA" :
435                         (devpriv->usefifo) ? "FIFO" : "IRQ",
436                         chan, expected_chan);
437                 s->async->events |= COMEDI_CB_ERROR;
438                 return false;
439         }
440
441         comedi_buf_write_samples(s, &val, 1);
442
443         devpriv->act_chanlist_pos++;
444         if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
445                 devpriv->act_chanlist_pos = 0;
446
447         if (cmd->stop_src == TRIG_COUNT &&
448             s->async->scans_done >= cmd->stop_arg) {
449                 s->async->events |= COMEDI_CB_EOA;
450                 return false;
451         }
452
453         return true;
454 }
455
456 static void pcl818_handle_eoc(struct comedi_device *dev,
457                               struct comedi_subdevice *s)
458 {
459         unsigned int chan;
460         unsigned int val;
461
462         if (pcl818_ai_eoc(dev, s, NULL, 0)) {
463                 dev_err(dev->class_dev, "A/D mode1/3 IRQ without DRDY!\n");
464                 s->async->events |= COMEDI_CB_ERROR;
465                 return;
466         }
467
468         val = pcl818_ai_get_sample(dev, s, &chan);
469         pcl818_ai_write_sample(dev, s, chan, val);
470 }
471
472 static void pcl818_handle_dma(struct comedi_device *dev,
473                               struct comedi_subdevice *s)
474 {
475         struct pcl818_private *devpriv = dev->private;
476         struct comedi_isadma *dma = devpriv->dma;
477         struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
478         unsigned short *ptr = desc->virt_addr;
479         unsigned int nsamples = comedi_bytes_to_samples(s, desc->size);
480         unsigned int chan;
481         unsigned int val;
482         int i;
483
484         /* restart dma with the next buffer */
485         dma->cur_dma = 1 - dma->cur_dma;
486         pcl818_ai_setup_dma(dev, s, nsamples);
487
488         for (i = 0; i < nsamples; i++) {
489                 val = ptr[i];
490                 chan = val & 0xf;
491                 val = (val >> 4) & s->maxdata;
492                 if (!pcl818_ai_write_sample(dev, s, chan, val))
493                         break;
494         }
495 }
496
497 static void pcl818_handle_fifo(struct comedi_device *dev,
498                                struct comedi_subdevice *s)
499 {
500         unsigned int status;
501         unsigned int chan;
502         unsigned int val;
503         int i, len;
504
505         status = inb(dev->iobase + PCL818_FI_STATUS);
506
507         if (status & 4) {
508                 dev_err(dev->class_dev, "A/D mode1/3 FIFO overflow!\n");
509                 s->async->events |= COMEDI_CB_ERROR;
510                 return;
511         }
512
513         if (status & 1) {
514                 dev_err(dev->class_dev,
515                         "A/D mode1/3 FIFO interrupt without data!\n");
516                 s->async->events |= COMEDI_CB_ERROR;
517                 return;
518         }
519
520         if (status & 2)
521                 len = 512;
522         else
523                 len = 0;
524
525         for (i = 0; i < len; i++) {
526                 val = pcl818_ai_get_fifo_sample(dev, s, &chan);
527                 if (!pcl818_ai_write_sample(dev, s, chan, val))
528                         break;
529         }
530 }
531
532 static irqreturn_t pcl818_interrupt(int irq, void *d)
533 {
534         struct comedi_device *dev = d;
535         struct pcl818_private *devpriv = dev->private;
536         struct comedi_subdevice *s = dev->read_subdev;
537         struct comedi_cmd *cmd = &s->async->cmd;
538
539         if (!dev->attached || !devpriv->ai_cmd_running) {
540                 pcl818_ai_clear_eoc(dev);
541                 return IRQ_HANDLED;
542         }
543
544         if (devpriv->ai_cmd_canceled) {
545                 /*
546                  * The cleanup from ai_cancel() has been delayed
547                  * until now because the card doesn't seem to like
548                  * being reprogrammed while a DMA transfer is in
549                  * progress.
550                  */
551                 s->async->scans_done = cmd->stop_arg;
552                 s->cancel(dev, s);
553                 return IRQ_HANDLED;
554         }
555
556         if (devpriv->dma)
557                 pcl818_handle_dma(dev, s);
558         else if (devpriv->usefifo)
559                 pcl818_handle_fifo(dev, s);
560         else
561                 pcl818_handle_eoc(dev, s);
562
563         pcl818_ai_clear_eoc(dev);
564
565         comedi_handle_events(dev, s);
566         return IRQ_HANDLED;
567 }
568
569 static int check_channel_list(struct comedi_device *dev,
570                               struct comedi_subdevice *s,
571                               unsigned int *chanlist, unsigned int n_chan)
572 {
573         unsigned int chansegment[16];
574         unsigned int i, nowmustbechan, seglen;
575
576         /* correct channel and range number check itself comedi/range.c */
577         if (n_chan < 1) {
578                 dev_err(dev->class_dev, "range/channel list is empty!\n");
579                 return 0;
580         }
581
582         if (n_chan > 1) {
583                 /*  first channel is every time ok */
584                 chansegment[0] = chanlist[0];
585                 /*  build part of chanlist */
586                 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
587                         /* we detect loop, this must by finish */
588
589                         if (chanlist[0] == chanlist[i])
590                                 break;
591                         nowmustbechan =
592                             (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
593                         if (nowmustbechan != CR_CHAN(chanlist[i])) {
594                                 /*  channel list isn't continuous :-( */
595                                 dev_dbg(dev->class_dev,
596                                         "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
597                                         i, CR_CHAN(chanlist[i]), nowmustbechan,
598                                         CR_CHAN(chanlist[0]));
599                                 return 0;
600                         }
601                         /*  well, this is next correct channel in list */
602                         chansegment[i] = chanlist[i];
603                 }
604
605                 /*  check whole chanlist */
606                 for (i = 0; i < n_chan; i++) {
607                         if (chanlist[i] != chansegment[i % seglen]) {
608                                 dev_dbg(dev->class_dev,
609                                         "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
610                                         i, CR_CHAN(chansegment[i]),
611                                         CR_RANGE(chansegment[i]),
612                                         CR_AREF(chansegment[i]),
613                                         CR_CHAN(chanlist[i % seglen]),
614                                         CR_RANGE(chanlist[i % seglen]),
615                                         CR_AREF(chansegment[i % seglen]));
616                                 return 0;       /*  chan/gain list is strange */
617                         }
618                 }
619         } else {
620                 seglen = 1;
621         }
622         return seglen;
623 }
624
625 static int check_single_ended(unsigned int port)
626 {
627         if (inb(port + PCL818_STATUS_REG) & PCL818_STATUS_MUX)
628                 return 1;
629         return 0;
630 }
631
632 static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
633                       struct comedi_cmd *cmd)
634 {
635         const struct pcl818_board *board = dev->board_ptr;
636         int err = 0;
637
638         /* Step 1 : check if triggers are trivially valid */
639
640         err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
641         err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
642         err |= comedi_check_trigger_src(&cmd->convert_src,
643                                         TRIG_TIMER | TRIG_EXT);
644         err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
645         err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
646
647         if (err)
648                 return 1;
649
650         /* Step 2a : make sure trigger sources are unique */
651
652         err |= comedi_check_trigger_is_unique(cmd->convert_src);
653         err |= comedi_check_trigger_is_unique(cmd->stop_src);
654
655         /* Step 2b : and mutually compatible */
656
657         if (err)
658                 return 2;
659
660         /* Step 3: check if arguments are trivially valid */
661
662         err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
663         err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
664
665         if (cmd->convert_src == TRIG_TIMER) {
666                 err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
667                                                     board->ns_min);
668         } else {        /* TRIG_EXT */
669                 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
670         }
671
672         err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
673                                            cmd->chanlist_len);
674
675         if (cmd->stop_src == TRIG_COUNT)
676                 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
677         else    /* TRIG_NONE */
678                 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
679
680         if (err)
681                 return 3;
682
683         /* step 4: fix up any arguments */
684
685         if (cmd->convert_src == TRIG_TIMER) {
686                 unsigned int arg = cmd->convert_arg;
687
688                 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
689                 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
690         }
691
692         if (err)
693                 return 4;
694
695         /* step 5: complain about special chanlist considerations */
696
697         if (cmd->chanlist) {
698                 if (!check_channel_list(dev, s, cmd->chanlist,
699                                         cmd->chanlist_len))
700                         return 5;       /*  incorrect channels list */
701         }
702
703         return 0;
704 }
705
706 static int pcl818_ai_cmd(struct comedi_device *dev,
707                          struct comedi_subdevice *s)
708 {
709         struct pcl818_private *devpriv = dev->private;
710         struct comedi_isadma *dma = devpriv->dma;
711         struct comedi_cmd *cmd = &s->async->cmd;
712         unsigned int ctrl = 0;
713         unsigned int seglen;
714
715         if (devpriv->ai_cmd_running)
716                 return -EBUSY;
717
718         seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
719         if (seglen < 1)
720                 return -EINVAL;
721         pcl818_ai_setup_chanlist(dev, cmd->chanlist, seglen);
722
723         devpriv->ai_cmd_running = 1;
724         devpriv->ai_cmd_canceled = 0;
725         devpriv->act_chanlist_pos = 0;
726
727         if (cmd->convert_src == TRIG_TIMER)
728                 ctrl |= PCL818_CTRL_PACER_TRIG;
729         else
730                 ctrl |= PCL818_CTRL_EXT_TRIG;
731
732         outb(0, dev->iobase + PCL818_CNTENABLE_REG);
733
734         if (dma) {
735                 /* setup and enable dma for the first buffer */
736                 dma->cur_dma = 0;
737                 pcl818_ai_setup_dma(dev, s, 0);
738
739                 ctrl |= PCL818_CTRL_INTE | PCL818_CTRL_IRQ(dev->irq) |
740                         PCL818_CTRL_DMAE;
741         } else if (devpriv->usefifo) {
742                 /* enable FIFO */
743                 outb(1, dev->iobase + PCL818_FI_ENABLE);
744         } else {
745                 ctrl |= PCL818_CTRL_INTE | PCL818_CTRL_IRQ(dev->irq);
746         }
747         outb(ctrl, dev->iobase + PCL818_CTRL_REG);
748
749         if (cmd->convert_src == TRIG_TIMER) {
750                 comedi_8254_update_divisors(dev->pacer);
751                 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
752         }
753
754         return 0;
755 }
756
757 static int pcl818_ai_cancel(struct comedi_device *dev,
758                             struct comedi_subdevice *s)
759 {
760         struct pcl818_private *devpriv = dev->private;
761         struct comedi_isadma *dma = devpriv->dma;
762         struct comedi_cmd *cmd = &s->async->cmd;
763
764         if (!devpriv->ai_cmd_running)
765                 return 0;
766
767         if (dma) {
768                 if (cmd->stop_src == TRIG_NONE ||
769                     (cmd->stop_src == TRIG_COUNT &&
770                      s->async->scans_done < cmd->stop_arg)) {
771                         if (!devpriv->ai_cmd_canceled) {
772                                 /*
773                                  * Wait for running dma transfer to end,
774                                  * do cleanup in interrupt.
775                                  */
776                                 devpriv->ai_cmd_canceled = 1;
777                                 return 0;
778                         }
779                 }
780                 comedi_isadma_disable(dma->chan);
781         }
782
783         outb(PCL818_CTRL_DISABLE_TRIG, dev->iobase + PCL818_CTRL_REG);
784         comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
785         pcl818_ai_clear_eoc(dev);
786
787         if (devpriv->usefifo) { /*  FIFO shutdown */
788                 outb(0, dev->iobase + PCL818_FI_INTCLR);
789                 outb(0, dev->iobase + PCL818_FI_FLUSH);
790                 outb(0, dev->iobase + PCL818_FI_ENABLE);
791         }
792         devpriv->ai_cmd_running = 0;
793         devpriv->ai_cmd_canceled = 0;
794
795         return 0;
796 }
797
798 static int pcl818_ai_insn_read(struct comedi_device *dev,
799                                struct comedi_subdevice *s,
800                                struct comedi_insn *insn,
801                                unsigned int *data)
802 {
803         unsigned int chan = CR_CHAN(insn->chanspec);
804         unsigned int range = CR_RANGE(insn->chanspec);
805         int ret = 0;
806         int i;
807
808         outb(PCL818_CTRL_SOFT_TRIG, dev->iobase + PCL818_CTRL_REG);
809
810         pcl818_ai_set_chan_range(dev, chan, range);
811         pcl818_ai_set_chan_scan(dev, chan, chan);
812
813         for (i = 0; i < insn->n; i++) {
814                 pcl818_ai_clear_eoc(dev);
815                 pcl818_ai_soft_trig(dev);
816
817                 ret = comedi_timeout(dev, s, insn, pcl818_ai_eoc, 0);
818                 if (ret)
819                         break;
820
821                 data[i] = pcl818_ai_get_sample(dev, s, NULL);
822         }
823         pcl818_ai_clear_eoc(dev);
824
825         return ret ? ret : insn->n;
826 }
827
828 static int pcl818_ao_insn_write(struct comedi_device *dev,
829                                 struct comedi_subdevice *s,
830                                 struct comedi_insn *insn,
831                                 unsigned int *data)
832 {
833         unsigned int chan = CR_CHAN(insn->chanspec);
834         unsigned int val = s->readback[chan];
835         int i;
836
837         for (i = 0; i < insn->n; i++) {
838                 val = data[i];
839                 outb((val & 0x000f) << 4,
840                      dev->iobase + PCL818_AO_LSB_REG(chan));
841                 outb((val & 0x0ff0) >> 4,
842                      dev->iobase + PCL818_AO_MSB_REG(chan));
843         }
844         s->readback[chan] = val;
845
846         return insn->n;
847 }
848
849 static int pcl818_di_insn_bits(struct comedi_device *dev,
850                                struct comedi_subdevice *s,
851                                struct comedi_insn *insn,
852                                unsigned int *data)
853 {
854         data[1] = inb(dev->iobase + PCL818_DO_DI_LSB_REG) |
855                   (inb(dev->iobase + PCL818_DO_DI_MSB_REG) << 8);
856
857         return insn->n;
858 }
859
860 static int pcl818_do_insn_bits(struct comedi_device *dev,
861                                struct comedi_subdevice *s,
862                                struct comedi_insn *insn,
863                                unsigned int *data)
864 {
865         if (comedi_dio_update_state(s, data)) {
866                 outb(s->state & 0xff, dev->iobase + PCL818_DO_DI_LSB_REG);
867                 outb((s->state >> 8), dev->iobase + PCL818_DO_DI_MSB_REG);
868         }
869
870         data[1] = s->state;
871
872         return insn->n;
873 }
874
875 static void pcl818_reset(struct comedi_device *dev)
876 {
877         const struct pcl818_board *board = dev->board_ptr;
878         unsigned int chan;
879
880         /* flush and disable the FIFO */
881         if (board->has_fifo) {
882                 outb(0, dev->iobase + PCL818_FI_INTCLR);
883                 outb(0, dev->iobase + PCL818_FI_FLUSH);
884                 outb(0, dev->iobase + PCL818_FI_ENABLE);
885         }
886
887         /* disable analog input trigger */
888         outb(PCL818_CTRL_DISABLE_TRIG, dev->iobase + PCL818_CTRL_REG);
889         pcl818_ai_clear_eoc(dev);
890
891         pcl818_ai_set_chan_range(dev, 0, 0);
892
893         /* stop pacer */
894         outb(0, dev->iobase + PCL818_CNTENABLE_REG);
895
896         /* set analog output channels to 0V */
897         for (chan = 0; chan < board->n_aochan; chan++) {
898                 outb(0, dev->iobase + PCL818_AO_LSB_REG(chan));
899                 outb(0, dev->iobase + PCL818_AO_MSB_REG(chan));
900         }
901
902         /* set all digital outputs low */
903         outb(0, dev->iobase + PCL818_DO_DI_MSB_REG);
904         outb(0, dev->iobase + PCL818_DO_DI_LSB_REG);
905 }
906
907 static void pcl818_set_ai_range_table(struct comedi_device *dev,
908                                       struct comedi_subdevice *s,
909                                       struct comedi_devconfig *it)
910 {
911         const struct pcl818_board *board = dev->board_ptr;
912
913         /* default to the range table from the boardinfo */
914         s->range_table = board->ai_range_type;
915
916         /* now check the user config option based on the boardtype */
917         if (board->is_818) {
918                 if (it->options[4] == 1 || it->options[4] == 10) {
919                         /* secondary range list jumper selectable */
920                         s->range_table = &range_pcl818l_h_ai;
921                 }
922         } else {
923                 switch (it->options[4]) {
924                 case 0:
925                         s->range_table = &range_bipolar10;
926                         break;
927                 case 1:
928                         s->range_table = &range_bipolar5;
929                         break;
930                 case 2:
931                         s->range_table = &range_bipolar2_5;
932                         break;
933                 case 3:
934                         s->range_table = &range718_bipolar1;
935                         break;
936                 case 4:
937                         s->range_table = &range718_bipolar0_5;
938                         break;
939                 case 6:
940                         s->range_table = &range_unipolar10;
941                         break;
942                 case 7:
943                         s->range_table = &range_unipolar5;
944                         break;
945                 case 8:
946                         s->range_table = &range718_unipolar2;
947                         break;
948                 case 9:
949                         s->range_table = &range718_unipolar1;
950                         break;
951                 default:
952                         s->range_table = &range_unknown;
953                         break;
954                 }
955         }
956 }
957
958 static void pcl818_alloc_dma(struct comedi_device *dev, unsigned int dma_chan)
959 {
960         struct pcl818_private *devpriv = dev->private;
961
962         /* only DMA channels 3 and 1 are valid */
963         if (!(dma_chan == 3 || dma_chan == 1))
964                 return;
965
966         /* DMA uses two 16K buffers */
967         devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan,
968                                            PAGE_SIZE * 4, COMEDI_ISADMA_READ);
969 }
970
971 static void pcl818_free_dma(struct comedi_device *dev)
972 {
973         struct pcl818_private *devpriv = dev->private;
974
975         if (devpriv)
976                 comedi_isadma_free(devpriv->dma);
977 }
978
979 static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it)
980 {
981         const struct pcl818_board *board = dev->board_ptr;
982         struct pcl818_private *devpriv;
983         struct comedi_subdevice *s;
984         unsigned int osc_base;
985         int ret;
986
987         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
988         if (!devpriv)
989                 return -ENOMEM;
990
991         ret = comedi_request_region(dev, it->options[0],
992                                     board->has_fifo ? 0x20 : 0x10);
993         if (ret)
994                 return ret;
995
996         /* we can use IRQ 2-7 for async command support */
997         if (it->options[1] >= 2 && it->options[1] <= 7) {
998                 ret = request_irq(it->options[1], pcl818_interrupt, 0,
999                                   dev->board_name, dev);
1000                 if (ret == 0)
1001                         dev->irq = it->options[1];
1002         }
1003
1004         /* should we use the FIFO? */
1005         if (dev->irq && board->has_fifo && it->options[2] == -1)
1006                 devpriv->usefifo = 1;
1007
1008         /* we need an IRQ to do DMA on channel 3 or 1 */
1009         if (dev->irq && board->has_dma)
1010                 pcl818_alloc_dma(dev, it->options[2]);
1011
1012         /* use 1MHz or 10MHz oscilator */
1013         if ((it->options[3] == 0) || (it->options[3] == 10))
1014                 osc_base = I8254_OSC_BASE_10MHZ;
1015         else
1016                 osc_base = I8254_OSC_BASE_1MHZ;
1017
1018         dev->pacer = comedi_8254_io_alloc(dev->iobase + PCL818_TIMER_BASE,
1019                                           osc_base, I8254_IO8, 0);
1020         if (IS_ERR(dev->pacer))
1021                 return PTR_ERR(dev->pacer);
1022
1023         /* max sampling speed */
1024         devpriv->ns_min = board->ns_min;
1025         if (!board->is_818) {
1026                 /* extended PCL718 to 100kHz DAC */
1027                 if ((it->options[6] == 1) || (it->options[6] == 100))
1028                         devpriv->ns_min = 10000;
1029         }
1030
1031         ret = comedi_alloc_subdevices(dev, 4);
1032         if (ret)
1033                 return ret;
1034
1035         s = &dev->subdevices[0];
1036         s->type         = COMEDI_SUBD_AI;
1037         s->subdev_flags = SDF_READABLE;
1038         if (check_single_ended(dev->iobase)) {
1039                 s->n_chan       = 16;
1040                 s->subdev_flags |= SDF_COMMON | SDF_GROUND;
1041         } else {
1042                 s->n_chan       = 8;
1043                 s->subdev_flags |= SDF_DIFF;
1044         }
1045         s->maxdata      = 0x0fff;
1046
1047         pcl818_set_ai_range_table(dev, s, it);
1048
1049         s->insn_read    = pcl818_ai_insn_read;
1050         if (dev->irq) {
1051                 dev->read_subdev = s;
1052                 s->subdev_flags |= SDF_CMD_READ;
1053                 s->len_chanlist = s->n_chan;
1054                 s->do_cmdtest   = ai_cmdtest;
1055                 s->do_cmd       = pcl818_ai_cmd;
1056                 s->cancel       = pcl818_ai_cancel;
1057         }
1058
1059         /* Analog Output subdevice */
1060         s = &dev->subdevices[1];
1061         if (board->n_aochan) {
1062                 s->type         = COMEDI_SUBD_AO;
1063                 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1064                 s->n_chan       = board->n_aochan;
1065                 s->maxdata      = 0x0fff;
1066                 s->range_table  = &range_unipolar5;
1067                 if (board->is_818) {
1068                         if ((it->options[4] == 1) || (it->options[4] == 10))
1069                                 s->range_table = &range_unipolar10;
1070                         if (it->options[4] == 2)
1071                                 s->range_table = &range_unknown;
1072                 } else {
1073                         if ((it->options[5] == 1) || (it->options[5] == 10))
1074                                 s->range_table = &range_unipolar10;
1075                         if (it->options[5] == 2)
1076                                 s->range_table = &range_unknown;
1077                 }
1078                 s->insn_write   = pcl818_ao_insn_write;
1079
1080                 ret = comedi_alloc_subdev_readback(s);
1081                 if (ret)
1082                         return ret;
1083         } else {
1084                 s->type         = COMEDI_SUBD_UNUSED;
1085         }
1086
1087         /* Digital Input subdevice */
1088         s = &dev->subdevices[2];
1089         s->type         = COMEDI_SUBD_DI;
1090         s->subdev_flags = SDF_READABLE;
1091         s->n_chan       = 16;
1092         s->maxdata      = 1;
1093         s->range_table  = &range_digital;
1094         s->insn_bits    = pcl818_di_insn_bits;
1095
1096         /* Digital Output subdevice */
1097         s = &dev->subdevices[3];
1098         s->type         = COMEDI_SUBD_DO;
1099         s->subdev_flags = SDF_WRITABLE;
1100         s->n_chan       = 16;
1101         s->maxdata      = 1;
1102         s->range_table  = &range_digital;
1103         s->insn_bits    = pcl818_do_insn_bits;
1104
1105         pcl818_reset(dev);
1106
1107         return 0;
1108 }
1109
1110 static void pcl818_detach(struct comedi_device *dev)
1111 {
1112         struct pcl818_private *devpriv = dev->private;
1113
1114         if (devpriv) {
1115                 pcl818_ai_cancel(dev, dev->read_subdev);
1116                 pcl818_reset(dev);
1117         }
1118         pcl818_free_dma(dev);
1119         comedi_legacy_detach(dev);
1120 }
1121
1122 static struct comedi_driver pcl818_driver = {
1123         .driver_name    = "pcl818",
1124         .module         = THIS_MODULE,
1125         .attach         = pcl818_attach,
1126         .detach         = pcl818_detach,
1127         .board_name     = &boardtypes[0].name,
1128         .num_names      = ARRAY_SIZE(boardtypes),
1129         .offset         = sizeof(struct pcl818_board),
1130 };
1131 module_comedi_driver(pcl818_driver);
1132
1133 MODULE_AUTHOR("Comedi https://www.comedi.org");
1134 MODULE_DESCRIPTION("Comedi low-level driver");
1135 MODULE_LICENSE("GPL");