staging: comedi: pcl818: remove pcl818_check()
[linux-2.6-block.git] / drivers / staging / comedi / drivers / pcl818.c
1 /*
2    comedi/drivers/pcl818.c
3
4    Author:  Michal Dobes <dobes@tesnet.cz>
5
6    hardware driver for Advantech cards:
7     card:   PCL-818L, PCL-818H, PCL-818HD, PCL-818HG, PCL-818, PCL-718
8     driver: pcl818l,  pcl818h,  pcl818hd,  pcl818hg,  pcl818,  pcl718
9 */
10 /*
11 Driver: pcl818
12 Description: Advantech PCL-818 cards, PCL-718
13 Author: Michal Dobes <dobes@tesnet.cz>
14 Devices: [Advantech] PCL-818L (pcl818l), PCL-818H (pcl818h),
15   PCL-818HD (pcl818hd), PCL-818HG (pcl818hg), PCL-818 (pcl818),
16   PCL-718 (pcl718)
17 Status: works
18
19 All cards have 16 SE/8 DIFF ADCs, one or two DACs, 16 DI and 16 DO.
20 Differences are only at maximal sample speed, range list and FIFO
21 support.
22 The driver support AI mode 0, 1, 3 other subdevices (AO, DI, DO) support
23 only mode 0. If DMA/FIFO/INT are disabled then AI support only mode 0.
24 PCL-818HD and PCL-818HG support 1kword FIFO. Driver support this FIFO
25 but this code is untested.
26 A word or two about DMA. Driver support DMA operations at two ways:
27 1) DMA uses two buffers and after one is filled then is generated
28    INT and DMA restart with second buffer. With this mode I'm unable run
29    more that 80Ksamples/secs without data dropouts on K6/233.
30 2) DMA uses one buffer and run in autoinit mode and the data are
31    from DMA buffer moved on the fly with 2kHz interrupts from RTC.
32    This mode is used if the interrupt 8 is available for allocation.
33    If not, then first DMA mode is used. With this I can run at
34    full speed one card (100ksamples/secs) or two cards with
35    60ksamples/secs each (more is problem on account of ISA limitations).
36    To use this mode you must have compiled  kernel with disabled
37    "Enhanced Real Time Clock Support".
38    Maybe you can have problems if you use xntpd or similar.
39    If you've data dropouts with DMA mode 2 then:
40     a) disable IDE DMA
41     b) switch text mode console to fb.
42
43    Options for PCL-818L:
44     [0] - IO Base
45     [1] - IRQ   (0=disable, 2, 3, 4, 5, 6, 7)
46     [2] - DMA   (0=disable, 1, 3)
47     [3] - 0, 10=10MHz clock for 8254
48               1= 1MHz clock for 8254
49     [4] - 0,  5=A/D input  -5V.. +5V
50           1, 10=A/D input -10V..+10V
51     [5] - 0,  5=D/A output 0-5V  (internal reference -5V)
52           1, 10=D/A output 0-10V (internal reference -10V)
53           2    =D/A output unknown (external reference)
54
55    Options for PCL-818, PCL-818H:
56     [0] - IO Base
57     [1] - IRQ   (0=disable, 2, 3, 4, 5, 6, 7)
58     [2] - DMA   (0=disable, 1, 3)
59     [3] - 0, 10=10MHz clock for 8254
60               1= 1MHz clock for 8254
61     [4] - 0,  5=D/A output 0-5V  (internal reference -5V)
62           1, 10=D/A output 0-10V (internal reference -10V)
63           2    =D/A output unknown (external reference)
64
65    Options for PCL-818HD, PCL-818HG:
66     [0] - IO Base
67     [1] - IRQ   (0=disable, 2, 3, 4, 5, 6, 7)
68     [2] - DMA/FIFO  (-1=use FIFO, 0=disable both FIFO and DMA,
69                       1=use DMA ch 1, 3=use DMA ch 3)
70     [3] - 0, 10=10MHz clock for 8254
71               1= 1MHz clock for 8254
72     [4] - 0,  5=D/A output 0-5V  (internal reference -5V)
73           1, 10=D/A output 0-10V (internal reference -10V)
74           2    =D/A output unknown (external reference)
75
76    Options for PCL-718:
77     [0] - IO Base
78     [1] - IRQ   (0=disable, 2, 3, 4, 5, 6, 7)
79     [2] - DMA   (0=disable, 1, 3)
80     [3] - 0, 10=10MHz clock for 8254
81               1= 1MHz clock for 8254
82     [4] -     0=A/D Range is +/-10V
83               1=             +/-5V
84               2=             +/-2.5V
85               3=             +/-1V
86               4=             +/-0.5V
87               5=             user defined bipolar
88               6=             0-10V
89               7=             0-5V
90               8=             0-2V
91               9=             0-1V
92              10=             user defined unipolar
93     [5] - 0,  5=D/A outputs 0-5V  (internal reference -5V)
94           1, 10=D/A outputs 0-10V (internal reference -10V)
95               2=D/A outputs unknown (external reference)
96     [6] - 0, 60=max  60kHz A/D sampling
97           1,100=max 100kHz A/D sampling (PCL-718 with Option 001 installed)
98
99 */
100
101 #include <linux/module.h>
102 #include <linux/gfp.h>
103 #include <linux/delay.h>
104 #include <linux/io.h>
105 #include <linux/interrupt.h>
106 #include <asm/dma.h>
107
108 #include "../comedidev.h"
109
110 #include "comedi_fc.h"
111 #include "8253.h"
112
113 /* boards constants */
114
115 #define boardPCL818L 0
116 #define boardPCL818H 1
117 #define boardPCL818HD 2
118 #define boardPCL818HG 3
119 #define boardPCL818 4
120 #define boardPCL718 5
121
122 /* W: clear INT request */
123 #define PCL818_CLRINT 8
124 /* R: return status byte */
125 #define PCL818_STATUS 8
126 /* R: A/D high byte W: A/D range control */
127 #define PCL818_RANGE 1
128 /* R: next mux scan channel W: mux scan channel & range control pointer */
129 #define PCL818_MUX 2
130 /* R/W: operation control register */
131 #define PCL818_CONTROL 9
132 /* W: counter enable */
133 #define PCL818_CNTENABLE 10
134
135 /* R: low byte of A/D W: soft A/D trigger */
136 #define PCL818_AD_LO 0
137 /* R: high byte of A/D W: A/D range control */
138 #define PCL818_AD_HI 1
139 /* W: D/A low&high byte */
140 #define PCL818_DA_LO 4
141 #define PCL818_DA_HI 5
142 /* R: low&high byte of DI */
143 #define PCL818_DI_LO 3
144 #define PCL818_DI_HI 11
145 /* W: low&high byte of DO */
146 #define PCL818_DO_LO 3
147 #define PCL818_DO_HI 11
148 /* W: PCL718 second D/A */
149 #define PCL718_DA2_LO 6
150 #define PCL718_DA2_HI 7
151
152 #define PCL818_TIMER_BASE                       0x0c
153
154 /* W: fifo enable/disable */
155 #define PCL818_FI_ENABLE 6
156 /* W: fifo interrupt clear */
157 #define PCL818_FI_INTCLR 20
158 /* W: fifo interrupt clear */
159 #define PCL818_FI_FLUSH 25
160 /* R: fifo status */
161 #define PCL818_FI_STATUS 25
162 /* R: one record from FIFO */
163 #define PCL818_FI_DATALO 23
164 #define PCL818_FI_DATAHI 23
165
166 /* type of interrupt handler */
167 #define INT_TYPE_AI1_INT 1
168 #define INT_TYPE_AI1_DMA 2
169 #define INT_TYPE_AI1_FIFO 3
170 #define INT_TYPE_AI3_INT 4
171 #define INT_TYPE_AI3_DMA 5
172 #define INT_TYPE_AI3_FIFO 6
173
174 #define MAGIC_DMA_WORD 0x5a5a
175
176 static const struct comedi_lrange range_pcl818h_ai = {
177         9, {
178                 BIP_RANGE(5),
179                 BIP_RANGE(2.5),
180                 BIP_RANGE(1.25),
181                 BIP_RANGE(0.625),
182                 UNI_RANGE(10),
183                 UNI_RANGE(5),
184                 UNI_RANGE(2.5),
185                 UNI_RANGE(1.25),
186                 BIP_RANGE(10)
187         }
188 };
189
190 static const struct comedi_lrange range_pcl818hg_ai = {
191         10, {
192                 BIP_RANGE(5),
193                 BIP_RANGE(0.5),
194                 BIP_RANGE(0.05),
195                 BIP_RANGE(0.005),
196                 UNI_RANGE(10),
197                 UNI_RANGE(1),
198                 UNI_RANGE(0.1),
199                 UNI_RANGE(0.01),
200                 BIP_RANGE(10),
201                 BIP_RANGE(1),
202                 BIP_RANGE(0.1),
203                 BIP_RANGE(0.01)
204         }
205 };
206
207 static const struct comedi_lrange range_pcl818l_l_ai = {
208         4, {
209                 BIP_RANGE(5),
210                 BIP_RANGE(2.5),
211                 BIP_RANGE(1.25),
212                 BIP_RANGE(0.625)
213         }
214 };
215
216 static const struct comedi_lrange range_pcl818l_h_ai = {
217         4, {
218                 BIP_RANGE(10),
219                 BIP_RANGE(5),
220                 BIP_RANGE(2.5),
221                 BIP_RANGE(1.25)
222         }
223 };
224
225 static const struct comedi_lrange range718_bipolar1 = {
226         1, {
227                 BIP_RANGE(1)
228         }
229 };
230
231 static const struct comedi_lrange range718_bipolar0_5 = {
232         1, {
233                 BIP_RANGE(0.5)
234         }
235 };
236
237 static const struct comedi_lrange range718_unipolar2 = {
238         1, {
239                 UNI_RANGE(2)
240         }
241 };
242
243 static const struct comedi_lrange range718_unipolar1 = {
244         1, {
245                 BIP_RANGE(1)
246         }
247 };
248
249 struct pcl818_board {
250         const char *name;
251         unsigned int ns_min;
252         int n_aochan;
253         const struct comedi_lrange *ai_range_type;
254         unsigned int has_dma:1;
255         unsigned int has_fifo:1;
256         unsigned int is_818:1;
257 };
258
259 static const struct pcl818_board boardtypes[] = {
260         {
261                 .name           = "pcl818l",
262                 .ns_min         = 25000,
263                 .n_aochan       = 1,
264                 .ai_range_type  = &range_pcl818l_l_ai,
265                 .has_dma        = 1,
266                 .is_818         = 1,
267         }, {
268                 .name           = "pcl818h",
269                 .ns_min         = 10000,
270                 .n_aochan       = 1,
271                 .ai_range_type  = &range_pcl818h_ai,
272                 .has_dma        = 1,
273                 .is_818         = 1,
274         }, {
275                 .name           = "pcl818hd",
276                 .ns_min         = 10000,
277                 .n_aochan       = 1,
278                 .ai_range_type  = &range_pcl818h_ai,
279                 .has_dma        = 1,
280                 .has_fifo       = 1,
281                 .is_818         = 1,
282         }, {
283                 .name           = "pcl818hg",
284                 .ns_min         = 10000,
285                 .n_aochan       = 1,
286                 .ai_range_type  = &range_pcl818hg_ai,
287                 .has_dma        = 1,
288                 .has_fifo       = 1,
289                 .is_818         = 1,
290         }, {
291                 .name           = "pcl818",
292                 .ns_min         = 10000,
293                 .n_aochan       = 2,
294                 .ai_range_type  = &range_pcl818h_ai,
295                 .has_dma        = 1,
296                 .is_818         = 1,
297         }, {
298                 .name           = "pcl718",
299                 .ns_min         = 16000,
300                 .n_aochan       = 2,
301                 .ai_range_type  = &range_unipolar5,
302                 .has_dma        = 1,
303         }, {
304                 .name           = "pcm3718",
305                 .ns_min         = 10000,
306                 .ai_range_type  = &range_pcl818h_ai,
307                 .has_dma        = 1,
308                 .is_818         = 1,
309         },
310 };
311
312 struct pcl818_private {
313         unsigned int dma;       /*  used DMA, 0=don't use DMA */
314         unsigned int dmapages;
315         unsigned int hwdmasize;
316         unsigned long dmabuf[2];        /*  pointers to begin of DMA buffers */
317         unsigned int hwdmaptr[2];       /*  hardware address of DMA buffers */
318         int next_dma_buf;       /*  which DMA buffer will be used next round */
319         long dma_runs_to_end;   /*  how many we must permorm DMA transfer to end of record */
320         unsigned long last_dma_run;     /*  how many bytes we must transfer on last DMA page */
321         unsigned int ns_min;    /*  manimal allowed delay between samples (in us) for actual card */
322         int i8253_osc_base;     /*  1/frequency of on board oscilator in ns */
323         int ai_mode;            /*  who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
324         int ai_act_scan;        /*  how many scans we finished */
325         int ai_act_chan;        /*  actual position in actual scan */
326         unsigned int act_chanlist[16];  /*  MUX setting for actual AI operations */
327         unsigned int act_chanlist_len;  /*  how long is actual MUX list */
328         unsigned int act_chanlist_pos;  /*  actual position in MUX list */
329         unsigned int ai_data_len;       /*  len of data buffer */
330         unsigned int ao_readback[2];
331         unsigned int divisor1;
332         unsigned int divisor2;
333         unsigned int usefifo:1;
334         unsigned int ai_cmd_running:1;
335         unsigned int irq_was_now_closed:1;
336         unsigned int neverending_ai:1;
337 };
338
339 static const unsigned int muxonechan[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,      /*  used for gain list programming */
340         0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
341 };
342
343 static void setup_channel_list(struct comedi_device *dev,
344                                struct comedi_subdevice *s,
345                                unsigned int *chanlist, unsigned int n_chan,
346                                unsigned int seglen);
347 static int check_channel_list(struct comedi_device *dev,
348                               struct comedi_subdevice *s,
349                               unsigned int *chanlist, unsigned int n_chan);
350
351 static void pcl818_start_pacer(struct comedi_device *dev, bool load_counters)
352 {
353         struct pcl818_private *devpriv = dev->private;
354         unsigned long timer_base = dev->iobase + PCL818_TIMER_BASE;
355
356         i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
357         i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
358         udelay(1);
359
360         if (load_counters) {
361                 i8254_write(timer_base, 0, 2, devpriv->divisor2);
362                 i8254_write(timer_base, 0, 1, devpriv->divisor1);
363         }
364 }
365
366 static unsigned int pcl818_ai_get_sample(struct comedi_device *dev,
367                                          struct comedi_subdevice *s,
368                                          unsigned int *chan)
369 {
370         unsigned int val;
371
372         val = inb(dev->iobase + PCL818_AD_HI) << 8;
373         val |= inb(dev->iobase + PCL818_AD_LO);
374
375         if (chan)
376                 *chan = val & 0xf;
377
378         return (val >> 4) & s->maxdata;
379 }
380
381 static int pcl818_ai_eoc(struct comedi_device *dev,
382                          struct comedi_subdevice *s,
383                          struct comedi_insn *insn,
384                          unsigned long context)
385 {
386         unsigned int status;
387
388         status = inb(dev->iobase + PCL818_STATUS);
389         if (status & 0x10)
390                 return 0;
391         return -EBUSY;
392 }
393
394 static int pcl818_ai_insn_read(struct comedi_device *dev,
395                                struct comedi_subdevice *s,
396                                struct comedi_insn *insn, unsigned int *data)
397 {
398         int ret;
399         int n;
400
401         /* software trigger, DMA and INT off */
402         outb(0, dev->iobase + PCL818_CONTROL);
403
404         /* select channel */
405         outb(muxonechan[CR_CHAN(insn->chanspec)], dev->iobase + PCL818_MUX);
406
407         /* select gain */
408         outb(CR_RANGE(insn->chanspec), dev->iobase + PCL818_RANGE);
409
410         for (n = 0; n < insn->n; n++) {
411
412                 /* clear INT (conversion end) flag */
413                 outb(0, dev->iobase + PCL818_CLRINT);
414
415                 /* start conversion */
416                 outb(0, dev->iobase + PCL818_AD_LO);
417
418                 ret = comedi_timeout(dev, s, insn, pcl818_ai_eoc, 0);
419                 if (ret) {
420                         /* clear INT (conversion end) flag */
421                         outb(0, dev->iobase + PCL818_CLRINT);
422                         return ret;
423                 }
424
425                 data[n] = pcl818_ai_get_sample(dev, s, NULL);
426         }
427
428         return n;
429 }
430
431 static int pcl818_ao_insn_read(struct comedi_device *dev,
432                                struct comedi_subdevice *s,
433                                struct comedi_insn *insn, unsigned int *data)
434 {
435         struct pcl818_private *devpriv = dev->private;
436         int n;
437         int chan = CR_CHAN(insn->chanspec);
438
439         for (n = 0; n < insn->n; n++)
440                 data[n] = devpriv->ao_readback[chan];
441
442         return n;
443 }
444
445 static int pcl818_ao_insn_write(struct comedi_device *dev,
446                                 struct comedi_subdevice *s,
447                                 struct comedi_insn *insn, unsigned int *data)
448 {
449         struct pcl818_private *devpriv = dev->private;
450         int n;
451         int chan = CR_CHAN(insn->chanspec);
452
453         for (n = 0; n < insn->n; n++) {
454                 devpriv->ao_readback[chan] = data[n];
455                 outb((data[n] & 0x000f) << 4, dev->iobase +
456                      (chan ? PCL718_DA2_LO : PCL818_DA_LO));
457                 outb((data[n] & 0x0ff0) >> 4, dev->iobase +
458                      (chan ? PCL718_DA2_HI : PCL818_DA_HI));
459         }
460
461         return n;
462 }
463
464 static int pcl818_di_insn_bits(struct comedi_device *dev,
465                                struct comedi_subdevice *s,
466                                struct comedi_insn *insn, unsigned int *data)
467 {
468         data[1] = inb(dev->iobase + PCL818_DI_LO) |
469             (inb(dev->iobase + PCL818_DI_HI) << 8);
470
471         return insn->n;
472 }
473
474 static int pcl818_do_insn_bits(struct comedi_device *dev,
475                                struct comedi_subdevice *s,
476                                struct comedi_insn *insn,
477                                unsigned int *data)
478 {
479         if (comedi_dio_update_state(s, data)) {
480                 outb(s->state & 0xff, dev->iobase + PCL818_DO_LO);
481                 outb((s->state >> 8), dev->iobase + PCL818_DO_HI);
482         }
483
484         data[1] = s->state;
485
486         return insn->n;
487 }
488
489 static irqreturn_t interrupt_pcl818_ai_mode13_int(int irq, void *d)
490 {
491         struct comedi_device *dev = d;
492         struct pcl818_private *devpriv = dev->private;
493         struct comedi_subdevice *s = dev->read_subdev;
494         struct comedi_cmd *cmd = &s->async->cmd;
495         unsigned int chan;
496         int timeout = 50;       /* wait max 50us */
497
498         while (timeout--) {
499                 if (inb(dev->iobase + PCL818_STATUS) & 0x10)
500                         goto conv_finish;
501                 udelay(1);
502         }
503         outb(0, dev->iobase + PCL818_STATUS);   /* clear INT request */
504         comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
505         s->cancel(dev, s);
506         s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
507         comedi_event(dev, s);
508         return IRQ_HANDLED;
509
510 conv_finish:
511         comedi_buf_put(s->async, pcl818_ai_get_sample(dev, s, &chan));
512         outb(0, dev->iobase + PCL818_CLRINT);   /* clear INT request */
513
514         if (chan != devpriv->act_chanlist[devpriv->act_chanlist_pos]) {
515                 dev_dbg(dev->class_dev,
516                         "A/D mode1/3 IRQ - channel dropout %x!=%x !\n",
517                         chan,
518                         devpriv->act_chanlist[devpriv->act_chanlist_pos]);
519                 s->cancel(dev, s);
520                 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
521                 comedi_event(dev, s);
522                 return IRQ_HANDLED;
523         }
524         devpriv->act_chanlist_pos++;
525         if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
526                 devpriv->act_chanlist_pos = 0;
527
528         s->async->cur_chan++;
529         if (s->async->cur_chan >= cmd->chanlist_len) {
530                 s->async->cur_chan = 0;
531                 devpriv->ai_act_scan--;
532         }
533
534         if (!devpriv->neverending_ai) {
535                 if (devpriv->ai_act_scan == 0) {        /* all data sampled */
536                         s->cancel(dev, s);
537                         s->async->events |= COMEDI_CB_EOA;
538                 }
539         }
540         comedi_event(dev, s);
541         return IRQ_HANDLED;
542 }
543
544 static irqreturn_t interrupt_pcl818_ai_mode13_dma(int irq, void *d)
545 {
546         struct comedi_device *dev = d;
547         struct pcl818_private *devpriv = dev->private;
548         struct comedi_subdevice *s = dev->read_subdev;
549         struct comedi_cmd *cmd = &s->async->cmd;
550         int i, len, bufptr;
551         unsigned long flags;
552         unsigned short *ptr;
553
554         disable_dma(devpriv->dma);
555         devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
556         if ((devpriv->dma_runs_to_end) > -1 || devpriv->neverending_ai) {       /*  switch dma bufs */
557                 set_dma_mode(devpriv->dma, DMA_MODE_READ);
558                 flags = claim_dma_lock();
559                 set_dma_addr(devpriv->dma,
560                              devpriv->hwdmaptr[devpriv->next_dma_buf]);
561                 if (devpriv->dma_runs_to_end || devpriv->neverending_ai)
562                         set_dma_count(devpriv->dma, devpriv->hwdmasize);
563                 else
564                         set_dma_count(devpriv->dma, devpriv->last_dma_run);
565                 release_dma_lock(flags);
566                 enable_dma(devpriv->dma);
567         }
568
569         devpriv->dma_runs_to_end--;
570         outb(0, dev->iobase + PCL818_CLRINT);   /* clear INT request */
571         ptr = (unsigned short *)devpriv->dmabuf[1 - devpriv->next_dma_buf];
572
573         len = devpriv->hwdmasize >> 1;
574         bufptr = 0;
575
576         for (i = 0; i < len; i++) {
577                 if ((ptr[bufptr] & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) {  /*  dropout! */
578                         dev_dbg(dev->class_dev,
579                                 "A/D mode1/3 DMA - channel dropout %d(card)!=%d(chanlist) at %d !\n",
580                                 (ptr[bufptr] & 0xf),
581                                 devpriv->act_chanlist[devpriv->act_chanlist_pos],
582                                 devpriv->act_chanlist_pos);
583                         s->cancel(dev, s);
584                         s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
585                         comedi_event(dev, s);
586                         return IRQ_HANDLED;
587                 }
588
589                 comedi_buf_put(s->async, ptr[bufptr++] >> 4);   /*  get one sample */
590
591                 devpriv->act_chanlist_pos++;
592                 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
593                         devpriv->act_chanlist_pos = 0;
594
595                 s->async->cur_chan++;
596                 if (s->async->cur_chan >= cmd->chanlist_len) {
597                         s->async->cur_chan = 0;
598                         devpriv->ai_act_scan--;
599                 }
600
601                 if (!devpriv->neverending_ai)
602                         if (devpriv->ai_act_scan == 0) {        /* all data sampled */
603                                 s->cancel(dev, s);
604                                 s->async->events |= COMEDI_CB_EOA;
605                                 comedi_event(dev, s);
606                                 return IRQ_HANDLED;
607                         }
608         }
609
610         if (len > 0)
611                 comedi_event(dev, s);
612         return IRQ_HANDLED;
613 }
614
615 static irqreturn_t interrupt_pcl818_ai_mode13_fifo(int irq, void *d)
616 {
617         struct comedi_device *dev = d;
618         struct pcl818_private *devpriv = dev->private;
619         struct comedi_subdevice *s = dev->read_subdev;
620         struct comedi_cmd *cmd = &s->async->cmd;
621         int i, len;
622         unsigned char lo;
623
624         outb(0, dev->iobase + PCL818_FI_INTCLR);        /*  clear fifo int request */
625
626         lo = inb(dev->iobase + PCL818_FI_STATUS);
627
628         if (lo & 4) {
629                 comedi_error(dev, "A/D mode1/3 FIFO overflow!");
630                 s->cancel(dev, s);
631                 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
632                 comedi_event(dev, s);
633                 return IRQ_HANDLED;
634         }
635
636         if (lo & 1) {
637                 comedi_error(dev, "A/D mode1/3 FIFO interrupt without data!");
638                 s->cancel(dev, s);
639                 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
640                 comedi_event(dev, s);
641                 return IRQ_HANDLED;
642         }
643
644         if (lo & 2)
645                 len = 512;
646         else
647                 len = 0;
648
649         for (i = 0; i < len; i++) {
650                 lo = inb(dev->iobase + PCL818_FI_DATALO);
651                 if ((lo & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) {   /*  dropout! */
652                         dev_dbg(dev->class_dev,
653                                 "A/D mode1/3 FIFO - channel dropout %d!=%d !\n",
654                                 (lo & 0xf),
655                                 devpriv->act_chanlist[devpriv->act_chanlist_pos]);
656                         s->cancel(dev, s);
657                         s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
658                         comedi_event(dev, s);
659                         return IRQ_HANDLED;
660                 }
661
662                 comedi_buf_put(s->async, (lo >> 4) | (inb(dev->iobase + PCL818_FI_DATAHI) << 4));       /*  get one sample */
663
664                 devpriv->act_chanlist_pos++;
665                 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
666                         devpriv->act_chanlist_pos = 0;
667
668                 s->async->cur_chan++;
669                 if (s->async->cur_chan >= cmd->chanlist_len) {
670                         s->async->cur_chan = 0;
671                         devpriv->ai_act_scan--;
672                 }
673
674                 if (!devpriv->neverending_ai)
675                         if (devpriv->ai_act_scan == 0) {        /* all data sampled */
676                                 s->cancel(dev, s);
677                                 s->async->events |= COMEDI_CB_EOA;
678                                 comedi_event(dev, s);
679                                 return IRQ_HANDLED;
680                         }
681         }
682
683         if (len > 0)
684                 comedi_event(dev, s);
685         return IRQ_HANDLED;
686 }
687
688 static irqreturn_t interrupt_pcl818(int irq, void *d)
689 {
690         struct comedi_device *dev = d;
691         struct pcl818_private *devpriv = dev->private;
692         struct comedi_subdevice *s = dev->read_subdev;
693
694         if (!dev->attached) {
695                 comedi_error(dev, "premature interrupt");
696                 return IRQ_HANDLED;
697         }
698
699         if (devpriv->ai_cmd_running && devpriv->irq_was_now_closed) {
700                 if ((devpriv->neverending_ai || (!devpriv->neverending_ai &&
701                                                  devpriv->ai_act_scan > 0)) &&
702                     (devpriv->ai_mode == INT_TYPE_AI1_DMA ||
703                      devpriv->ai_mode == INT_TYPE_AI3_DMA)) {
704                         /* The cleanup from ai_cancel() has been delayed
705                            until now because the card doesn't seem to like
706                            being reprogrammed while a DMA transfer is in
707                            progress.
708                          */
709                         devpriv->ai_act_scan = 0;
710                         devpriv->neverending_ai = 0;
711                         s->cancel(dev, s);
712                 }
713
714                 outb(0, dev->iobase + PCL818_CLRINT);   /* clear INT request */
715
716                 return IRQ_HANDLED;
717         }
718
719         switch (devpriv->ai_mode) {
720         case INT_TYPE_AI1_DMA:
721         case INT_TYPE_AI3_DMA:
722                 return interrupt_pcl818_ai_mode13_dma(irq, d);
723         case INT_TYPE_AI1_INT:
724         case INT_TYPE_AI3_INT:
725                 return interrupt_pcl818_ai_mode13_int(irq, d);
726         case INT_TYPE_AI1_FIFO:
727         case INT_TYPE_AI3_FIFO:
728                 return interrupt_pcl818_ai_mode13_fifo(irq, d);
729         default:
730                 break;
731         }
732
733         outb(0, dev->iobase + PCL818_CLRINT);   /* clear INT request */
734
735         if (!devpriv->ai_cmd_running || !devpriv->ai_mode) {
736                 comedi_error(dev, "bad IRQ!");
737                 return IRQ_NONE;
738         }
739
740         comedi_error(dev, "IRQ from unknown source!");
741         return IRQ_NONE;
742 }
743
744 static void pcl818_ai_mode13dma_int(int mode, struct comedi_device *dev,
745                                     struct comedi_subdevice *s)
746 {
747         struct pcl818_private *devpriv = dev->private;
748         struct comedi_cmd *cmd = &s->async->cmd;
749         unsigned int flags;
750         unsigned int bytes;
751
752         disable_dma(devpriv->dma);      /*  disable dma */
753         bytes = devpriv->hwdmasize;
754         if (!devpriv->neverending_ai) {
755                 bytes = cmd->chanlist_len * cmd->stop_arg * sizeof(short);
756                 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize;
757                 devpriv->last_dma_run = bytes % devpriv->hwdmasize;
758                 devpriv->dma_runs_to_end--;
759                 if (devpriv->dma_runs_to_end >= 0)
760                         bytes = devpriv->hwdmasize;
761         }
762
763         devpriv->next_dma_buf = 0;
764         set_dma_mode(devpriv->dma, DMA_MODE_READ);
765         flags = claim_dma_lock();
766         clear_dma_ff(devpriv->dma);
767         set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
768         set_dma_count(devpriv->dma, bytes);
769         release_dma_lock(flags);
770         enable_dma(devpriv->dma);
771
772         if (mode == 1) {
773                 devpriv->ai_mode = INT_TYPE_AI1_DMA;
774                 outb(0x87 | (dev->irq << 4), dev->iobase + PCL818_CONTROL);     /* Pacer+IRQ+DMA */
775         } else {
776                 devpriv->ai_mode = INT_TYPE_AI3_DMA;
777                 outb(0x86 | (dev->irq << 4), dev->iobase + PCL818_CONTROL);     /* Ext trig+IRQ+DMA */
778         }
779 }
780
781 static int pcl818_ai_cmd_mode(int mode, struct comedi_device *dev,
782                               struct comedi_subdevice *s)
783 {
784         struct pcl818_private *devpriv = dev->private;
785         struct comedi_cmd *cmd = &s->async->cmd;
786         unsigned int seglen;
787
788         if (devpriv->ai_cmd_running)
789                 return -EBUSY;
790
791         pcl818_start_pacer(dev, false);
792
793         seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
794         if (seglen < 1)
795                 return -EINVAL;
796         setup_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len, seglen);
797
798         udelay(1);
799
800         devpriv->ai_act_scan = cmd->stop_arg;
801         devpriv->ai_act_chan = 0;
802         devpriv->ai_cmd_running = 1;
803         devpriv->irq_was_now_closed = 0;
804         devpriv->act_chanlist_pos = 0;
805         devpriv->dma_runs_to_end = 0;
806
807         outb(0, dev->iobase + PCL818_CNTENABLE);        /* enable pacer */
808
809         switch (devpriv->dma) {
810         case 1:         /*  DMA */
811         case 3:
812                 pcl818_ai_mode13dma_int(mode, dev, s);
813                 break;
814         case 0:
815                 if (!devpriv->usefifo) {
816                         /* IRQ */
817                         if (mode == 1) {
818                                 devpriv->ai_mode = INT_TYPE_AI1_INT;
819                                 /* Pacer+IRQ */
820                                 outb(0x83 | (dev->irq << 4),
821                                      dev->iobase + PCL818_CONTROL);
822                         } else {
823                                 devpriv->ai_mode = INT_TYPE_AI3_INT;
824                                 /* Ext trig+IRQ */
825                                 outb(0x82 | (dev->irq << 4),
826                                      dev->iobase + PCL818_CONTROL);
827                         }
828                 } else {
829                         /* FIFO */
830                         /* enable FIFO */
831                         outb(1, dev->iobase + PCL818_FI_ENABLE);
832                         if (mode == 1) {
833                                 devpriv->ai_mode = INT_TYPE_AI1_FIFO;
834                                 /* Pacer */
835                                 outb(0x03, dev->iobase + PCL818_CONTROL);
836                         } else {
837                                 devpriv->ai_mode = INT_TYPE_AI3_FIFO;
838                                 outb(0x02, dev->iobase + PCL818_CONTROL);
839                         }
840                 }
841         }
842
843         pcl818_start_pacer(dev, mode == 1);
844
845         return 0;
846 }
847
848 static int check_channel_list(struct comedi_device *dev,
849                               struct comedi_subdevice *s,
850                               unsigned int *chanlist, unsigned int n_chan)
851 {
852         unsigned int chansegment[16];
853         unsigned int i, nowmustbechan, seglen, segpos;
854
855         /* correct channel and range number check itself comedi/range.c */
856         if (n_chan < 1) {
857                 comedi_error(dev, "range/channel list is empty!");
858                 return 0;
859         }
860
861         if (n_chan > 1) {
862                 /*  first channel is every time ok */
863                 chansegment[0] = chanlist[0];
864                 /*  build part of chanlist */
865                 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
866                         /* we detect loop, this must by finish */
867
868                         if (chanlist[0] == chanlist[i])
869                                 break;
870                         nowmustbechan =
871                             (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
872                         if (nowmustbechan != CR_CHAN(chanlist[i])) {    /*  channel list isn't continuous :-( */
873                                 dev_dbg(dev->class_dev,
874                                         "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
875                                         i, CR_CHAN(chanlist[i]), nowmustbechan,
876                                         CR_CHAN(chanlist[0]));
877                                 return 0;
878                         }
879                         /*  well, this is next correct channel in list */
880                         chansegment[i] = chanlist[i];
881                 }
882
883                 /*  check whole chanlist */
884                 for (i = 0, segpos = 0; i < n_chan; i++) {
885                         if (chanlist[i] != chansegment[i % seglen]) {
886                                 dev_dbg(dev->class_dev,
887                                         "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
888                                         i, CR_CHAN(chansegment[i]),
889                                         CR_RANGE(chansegment[i]),
890                                         CR_AREF(chansegment[i]),
891                                         CR_CHAN(chanlist[i % seglen]),
892                                         CR_RANGE(chanlist[i % seglen]),
893                                         CR_AREF(chansegment[i % seglen]));
894                                 return 0;       /*  chan/gain list is strange */
895                         }
896                 }
897         } else {
898                 seglen = 1;
899         }
900         return seglen;
901 }
902
903 static void setup_channel_list(struct comedi_device *dev,
904                                struct comedi_subdevice *s,
905                                unsigned int *chanlist, unsigned int n_chan,
906                                unsigned int seglen)
907 {
908         struct pcl818_private *devpriv = dev->private;
909         int i;
910
911         devpriv->act_chanlist_len = seglen;
912         devpriv->act_chanlist_pos = 0;
913
914         for (i = 0; i < seglen; i++) {  /*  store range list to card */
915                 devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
916                 outb(muxonechan[CR_CHAN(chanlist[i])], dev->iobase + PCL818_MUX);       /* select channel */
917                 outb(CR_RANGE(chanlist[i]), dev->iobase + PCL818_RANGE);        /* select gain */
918         }
919
920         udelay(1);
921
922         /* select channel interval to scan */
923         outb(devpriv->act_chanlist[0] | (devpriv->act_chanlist[seglen -
924                                                                1] << 4),
925              dev->iobase + PCL818_MUX);
926 }
927
928 static int check_single_ended(unsigned int port)
929 {
930         if (inb(port + PCL818_STATUS) & 0x20)
931                 return 1;
932         return 0;
933 }
934
935 static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
936                       struct comedi_cmd *cmd)
937 {
938         const struct pcl818_board *board = comedi_board(dev);
939         struct pcl818_private *devpriv = dev->private;
940         int err = 0;
941         int tmp;
942
943         /* Step 1 : check if triggers are trivially valid */
944
945         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
946         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
947         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
948         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
949         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
950
951         if (err)
952                 return 1;
953
954         /* Step 2a : make sure trigger sources are unique */
955
956         err |= cfc_check_trigger_is_unique(cmd->convert_src);
957         err |= cfc_check_trigger_is_unique(cmd->stop_src);
958
959         /* Step 2b : and mutually compatible */
960
961         if (err)
962                 return 2;
963
964         /* Step 3: check if arguments are trivially valid */
965
966         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
967         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
968
969         if (cmd->convert_src == TRIG_TIMER)
970                 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
971                                                  board->ns_min);
972         else    /* TRIG_EXT */
973                 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
974
975         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
976
977         if (cmd->stop_src == TRIG_COUNT)
978                 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
979         else    /* TRIG_NONE */
980                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
981
982         if (err)
983                 return 3;
984
985         /* step 4: fix up any arguments */
986
987         if (cmd->convert_src == TRIG_TIMER) {
988                 tmp = cmd->convert_arg;
989                 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base,
990                                           &devpriv->divisor1,
991                                           &devpriv->divisor2,
992                                           &cmd->convert_arg, cmd->flags);
993                 if (cmd->convert_arg < board->ns_min)
994                         cmd->convert_arg = board->ns_min;
995                 if (tmp != cmd->convert_arg)
996                         err++;
997         }
998
999         if (err)
1000                 return 4;
1001
1002         /* step 5: complain about special chanlist considerations */
1003
1004         if (cmd->chanlist) {
1005                 if (!check_channel_list(dev, s, cmd->chanlist,
1006                                         cmd->chanlist_len))
1007                         return 5;       /*  incorrect channels list */
1008         }
1009
1010         return 0;
1011 }
1012
1013 static int ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
1014 {
1015         struct pcl818_private *devpriv = dev->private;
1016         struct comedi_cmd *cmd = &s->async->cmd;
1017         int retval;
1018
1019         devpriv->ai_data_len = s->async->prealloc_bufsz;
1020
1021         if (cmd->stop_src == TRIG_COUNT)
1022                 devpriv->neverending_ai = 0;
1023         else
1024                 devpriv->neverending_ai = 1;
1025
1026         if (cmd->scan_begin_src == TRIG_FOLLOW) {       /*  mode 1, 3 */
1027                 if (cmd->convert_src == TRIG_TIMER) {   /*  mode 1 */
1028                         retval = pcl818_ai_cmd_mode(1, dev, s);
1029                         return retval;
1030                 }
1031                 if (cmd->convert_src == TRIG_EXT) {     /*  mode 3 */
1032                         return pcl818_ai_cmd_mode(3, dev, s);
1033                 }
1034         }
1035
1036         return -1;
1037 }
1038
1039 static int pcl818_ai_cancel(struct comedi_device *dev,
1040                             struct comedi_subdevice *s)
1041 {
1042         struct pcl818_private *devpriv = dev->private;
1043
1044         if (devpriv->ai_cmd_running) {
1045                 devpriv->irq_was_now_closed = 1;
1046
1047                 switch (devpriv->ai_mode) {
1048                 case INT_TYPE_AI1_DMA:
1049                 case INT_TYPE_AI3_DMA:
1050                         if (devpriv->neverending_ai ||
1051                             (!devpriv->neverending_ai &&
1052                              devpriv->ai_act_scan > 0)) {
1053                                 /* wait for running dma transfer to end, do cleanup in interrupt */
1054                                 goto end;
1055                         }
1056                         disable_dma(devpriv->dma);
1057                 case INT_TYPE_AI1_INT:
1058                 case INT_TYPE_AI3_INT:
1059                 case INT_TYPE_AI1_FIFO:
1060                 case INT_TYPE_AI3_FIFO:
1061                         outb(inb(dev->iobase + PCL818_CONTROL) & 0x73, dev->iobase + PCL818_CONTROL);   /* Stop A/D */
1062                         udelay(1);
1063                         pcl818_start_pacer(dev, false);
1064                         outb(0, dev->iobase + PCL818_AD_LO);
1065                         pcl818_ai_get_sample(dev, s, NULL);
1066                         outb(0, dev->iobase + PCL818_CLRINT);   /* clear INT request */
1067                         outb(0, dev->iobase + PCL818_CONTROL);  /* Stop A/D */
1068                         if (devpriv->usefifo) { /*  FIFO shutdown */
1069                                 outb(0, dev->iobase + PCL818_FI_INTCLR);
1070                                 outb(0, dev->iobase + PCL818_FI_FLUSH);
1071                                 outb(0, dev->iobase + PCL818_FI_ENABLE);
1072                         }
1073                         devpriv->ai_cmd_running = 0;
1074                         devpriv->neverending_ai = 0;
1075                         devpriv->ai_mode = 0;
1076                         devpriv->irq_was_now_closed = 0;
1077                         break;
1078                 }
1079         }
1080
1081 end:
1082         return 0;
1083 }
1084
1085 static void pcl818_reset(struct comedi_device *dev)
1086 {
1087         const struct pcl818_board *board = comedi_board(dev);
1088         struct pcl818_private *devpriv = dev->private;
1089         unsigned long timer_base = dev->iobase + PCL818_TIMER_BASE;
1090
1091         if (devpriv->usefifo) { /*  FIFO shutdown */
1092                 outb(0, dev->iobase + PCL818_FI_INTCLR);
1093                 outb(0, dev->iobase + PCL818_FI_FLUSH);
1094                 outb(0, dev->iobase + PCL818_FI_ENABLE);
1095         }
1096         outb(0, dev->iobase + PCL818_DA_LO);    /*  DAC=0V */
1097         outb(0, dev->iobase + PCL818_DA_HI);
1098         udelay(1);
1099         outb(0, dev->iobase + PCL818_DO_HI);    /*  DO=$0000 */
1100         outb(0, dev->iobase + PCL818_DO_LO);
1101         udelay(1);
1102         outb(0, dev->iobase + PCL818_CONTROL);
1103         outb(0, dev->iobase + PCL818_CNTENABLE);
1104         outb(0, dev->iobase + PCL818_MUX);
1105         outb(0, dev->iobase + PCL818_CLRINT);
1106
1107         /* Stop pacer */
1108         i8254_set_mode(timer_base, 0, 2, I8254_MODE0 | I8254_BINARY);
1109         i8254_set_mode(timer_base, 0, 1, I8254_MODE0 | I8254_BINARY);
1110         i8254_set_mode(timer_base, 0, 0, I8254_MODE0 | I8254_BINARY);
1111
1112         if (board->is_818) {
1113                 outb(0, dev->iobase + PCL818_RANGE);
1114         } else {
1115                 outb(0, dev->iobase + PCL718_DA2_LO);
1116                 outb(0, dev->iobase + PCL718_DA2_HI);
1117         }
1118 }
1119
1120 static void pcl818_set_ai_range_table(struct comedi_device *dev,
1121                                       struct comedi_subdevice *s,
1122                                       struct comedi_devconfig *it)
1123 {
1124         const struct pcl818_board *board = comedi_board(dev);
1125
1126         /* default to the range table from the boardinfo */
1127         s->range_table = board->ai_range_type;
1128
1129         /* now check the user config option based on the boardtype */
1130         if (board->is_818) {
1131                 if (it->options[4] == 1 || it->options[4] == 10) {
1132                         /* secondary range list jumper selectable */
1133                         s->range_table = &range_pcl818l_h_ai;
1134                 }
1135         } else {
1136                 switch (it->options[4]) {
1137                 case 0:
1138                         s->range_table = &range_bipolar10;
1139                         break;
1140                 case 1:
1141                         s->range_table = &range_bipolar5;
1142                         break;
1143                 case 2:
1144                         s->range_table = &range_bipolar2_5;
1145                         break;
1146                 case 3:
1147                         s->range_table = &range718_bipolar1;
1148                         break;
1149                 case 4:
1150                         s->range_table = &range718_bipolar0_5;
1151                         break;
1152                 case 6:
1153                         s->range_table = &range_unipolar10;
1154                         break;
1155                 case 7:
1156                         s->range_table = &range_unipolar5;
1157                         break;
1158                 case 8:
1159                         s->range_table = &range718_unipolar2;
1160                         break;
1161                 case 9:
1162                         s->range_table = &range718_unipolar1;
1163                         break;
1164                 default:
1165                         s->range_table = &range_unknown;
1166                         break;
1167                 }
1168         }
1169 }
1170
1171 static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1172 {
1173         const struct pcl818_board *board = comedi_board(dev);
1174         struct pcl818_private *devpriv;
1175         struct comedi_subdevice *s;
1176         int ret;
1177         int i;
1178
1179         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1180         if (!devpriv)
1181                 return -ENOMEM;
1182
1183         /* should we use the FIFO? */
1184         if (board->has_fifo && it->options[2] == -1)
1185                 devpriv->usefifo = 1;
1186
1187         ret = comedi_request_region(dev, it->options[0],
1188                                     devpriv->usefifo ? 0x20 : 0x10);
1189         if (ret)
1190                 return ret;
1191
1192         /* we can use IRQ 2-7 for async command support */
1193         if (it->options[1] >= 2 && it->options[1] <= 7) {
1194                 ret = request_irq(it->options[1], interrupt_pcl818, 0,
1195                                   dev->board_name, dev);
1196                 if (ret == 0)
1197                         dev->irq = it->options[1];
1198         }
1199
1200         /* we need an IRQ to do DMA on channel 3 or 1 */
1201         if (dev->irq && board->has_dma &&
1202             (it->options[2] == 3 || it->options[2] == 1)) {
1203                 ret = request_dma(it->options[2], dev->board_name);
1204                 if (ret) {
1205                         dev_err(dev->class_dev,
1206                                 "unable to request DMA channel %d\n",
1207                                 it->options[2]);
1208                         return -EBUSY;
1209                 }
1210                 devpriv->dma = it->options[2];
1211
1212                 devpriv->dmapages = 2;  /* we need 16KB */
1213                 devpriv->hwdmasize = (1 << devpriv->dmapages) * PAGE_SIZE;
1214
1215                 for (i = 0; i < 2; i++) {
1216                         unsigned long dmabuf;
1217
1218                         dmabuf = __get_dma_pages(GFP_KERNEL, devpriv->dmapages);
1219                         if (!dmabuf)
1220                                 return -ENOMEM;
1221
1222                         devpriv->dmabuf[i] = dmabuf;
1223                         devpriv->hwdmaptr[i] = virt_to_bus((void *)dmabuf);
1224                 }
1225         }
1226
1227         ret = comedi_alloc_subdevices(dev, 4);
1228         if (ret)
1229                 return ret;
1230
1231         s = &dev->subdevices[0];
1232         s->type         = COMEDI_SUBD_AI;
1233         s->subdev_flags = SDF_READABLE;
1234         if (check_single_ended(dev->iobase)) {
1235                 s->n_chan       = 16;
1236                 s->subdev_flags |= SDF_COMMON | SDF_GROUND;
1237         } else {
1238                 s->n_chan       = 8;
1239                 s->subdev_flags |= SDF_DIFF;
1240         }
1241         s->maxdata      = 0x0fff;
1242
1243         pcl818_set_ai_range_table(dev, s, it);
1244
1245         s->insn_read    = pcl818_ai_insn_read;
1246         if (dev->irq) {
1247                 dev->read_subdev = s;
1248                 s->subdev_flags |= SDF_CMD_READ;
1249                 s->len_chanlist = s->n_chan;
1250                 s->do_cmdtest   = ai_cmdtest;
1251                 s->do_cmd       = ai_cmd;
1252                 s->cancel       = pcl818_ai_cancel;
1253         }
1254
1255         s = &dev->subdevices[1];
1256         if (!board->n_aochan) {
1257                 s->type = COMEDI_SUBD_UNUSED;
1258         } else {
1259                 s->type = COMEDI_SUBD_AO;
1260                 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1261                 s->n_chan = board->n_aochan;
1262                 s->maxdata = 0x0fff;
1263                 s->range_table = &range_unipolar5;
1264                 s->insn_read = pcl818_ao_insn_read;
1265                 s->insn_write = pcl818_ao_insn_write;
1266                 if (board->is_818) {
1267                         if ((it->options[4] == 1) || (it->options[4] == 10))
1268                                 s->range_table = &range_unipolar10;
1269                         if (it->options[4] == 2)
1270                                 s->range_table = &range_unknown;
1271                 } else {
1272                         if ((it->options[5] == 1) || (it->options[5] == 10))
1273                                 s->range_table = &range_unipolar10;
1274                         if (it->options[5] == 2)
1275                                 s->range_table = &range_unknown;
1276                 }
1277         }
1278
1279         /* Digital Input subdevice */
1280         s = &dev->subdevices[2];
1281         s->type         = COMEDI_SUBD_DI;
1282         s->subdev_flags = SDF_READABLE;
1283         s->n_chan       = 16;
1284         s->maxdata      = 1;
1285         s->range_table  = &range_digital;
1286         s->insn_bits    = pcl818_di_insn_bits;
1287
1288         /* Digital Output subdevice */
1289         s = &dev->subdevices[3];
1290         s->type         = COMEDI_SUBD_DO;
1291         s->subdev_flags = SDF_WRITABLE;
1292         s->n_chan       = 16;
1293         s->maxdata      = 1;
1294         s->range_table  = &range_digital;
1295         s->insn_bits    = pcl818_do_insn_bits;
1296
1297         /* select 1/10MHz oscilator */
1298         if ((it->options[3] == 0) || (it->options[3] == 10))
1299                 devpriv->i8253_osc_base = I8254_OSC_BASE_10MHZ;
1300         else
1301                 devpriv->i8253_osc_base = I8254_OSC_BASE_1MHZ;
1302
1303         /* max sampling speed */
1304         devpriv->ns_min = board->ns_min;
1305
1306         if (!board->is_818) {
1307                 if ((it->options[6] == 1) || (it->options[6] == 100))
1308                         devpriv->ns_min = 10000;        /* extended PCL718 to 100kHz DAC */
1309         }
1310
1311         pcl818_reset(dev);
1312
1313         return 0;
1314 }
1315
1316 static void pcl818_detach(struct comedi_device *dev)
1317 {
1318         struct pcl818_private *devpriv = dev->private;
1319
1320         if (devpriv) {
1321                 pcl818_ai_cancel(dev, dev->read_subdev);
1322                 pcl818_reset(dev);
1323                 if (devpriv->dma)
1324                         free_dma(devpriv->dma);
1325                 if (devpriv->dmabuf[0])
1326                         free_pages(devpriv->dmabuf[0], devpriv->dmapages);
1327                 if (devpriv->dmabuf[1])
1328                         free_pages(devpriv->dmabuf[1], devpriv->dmapages);
1329         }
1330         comedi_legacy_detach(dev);
1331 }
1332
1333 static struct comedi_driver pcl818_driver = {
1334         .driver_name    = "pcl818",
1335         .module         = THIS_MODULE,
1336         .attach         = pcl818_attach,
1337         .detach         = pcl818_detach,
1338         .board_name     = &boardtypes[0].name,
1339         .num_names      = ARRAY_SIZE(boardtypes),
1340         .offset         = sizeof(struct pcl818_board),
1341 };
1342 module_comedi_driver(pcl818_driver);
1343
1344 MODULE_AUTHOR("Comedi http://www.comedi.org");
1345 MODULE_DESCRIPTION("Comedi low-level driver");
1346 MODULE_LICENSE("GPL");