]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/pcl816.c
Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
[karo-tx-linux.git] / drivers / staging / comedi / drivers / pcl816.c
1 /*
2    comedi/drivers/pcl816.c
3
4    Author:  Juan Grigera <juan@grigera.com.ar>
5             based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812
6
7    hardware driver for Advantech cards:
8     card:   PCL-816, PCL814B
9     driver: pcl816
10 */
11 /*
12 Driver: pcl816
13 Description: Advantech PCL-816 cards, PCL-814
14 Author: Juan Grigera <juan@grigera.com.ar>
15 Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
16 Status: works
17 Updated: Tue,  2 Apr 2002 23:15:21 -0800
18
19 PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
20 Differences are at resolution (16 vs 12 bits).
21
22 The driver support AI command mode, other subdevices not written.
23
24 Analog output and digital input and output are not supported.
25
26 Configuration Options:
27   [0] - IO Base
28   [1] - IRQ     (0=disable, 2, 3, 4, 5, 6, 7)
29   [2] - DMA     (0=disable, 1, 3)
30   [3] - 0, 10=10MHz clock for 8254
31             1= 1MHz clock for 8254
32
33 */
34
35 #include "../comedidev.h"
36
37 #include <linux/ioport.h>
38 #include <linux/mc146818rtc.h>
39 #include <linux/gfp.h>
40 #include <linux/delay.h>
41 #include <linux/io.h>
42 #include <asm/dma.h>
43
44 #include "8253.h"
45
46 #define DEBUG(x) x
47
48 /* boards constants */
49 /* IO space len */
50 #define PCLx1x_RANGE 16
51
52 /* #define outb(x,y)  printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y) */
53
54 /* INTEL 8254 counters */
55 #define PCL816_CTR0 4
56 #define PCL816_CTR1 5
57 #define PCL816_CTR2 6
58 /* R: counter read-back register W: counter control */
59 #define PCL816_CTRCTL 7
60
61 /* R: A/D high byte W: A/D range control */
62 #define PCL816_RANGE 9
63 /* W: clear INT request */
64 #define PCL816_CLRINT 10
65 /* R: next mux scan channel W: mux scan channel & range control pointer */
66 #define PCL816_MUX 11
67 /* R/W: operation control register */
68 #define PCL816_CONTROL 12
69
70 /* R: return status byte  W: set DMA/IRQ */
71 #define PCL816_STATUS 13
72 #define PCL816_STATUS_DRDY_MASK 0x80
73
74 /* R: low byte of A/D W: soft A/D trigger */
75 #define PCL816_AD_LO 8
76 /* R: high byte of A/D W: A/D range control */
77 #define PCL816_AD_HI 9
78
79 /* type of interrupt handler */
80 #define INT_TYPE_AI1_INT 1
81 #define INT_TYPE_AI1_DMA 2
82 #define INT_TYPE_AI3_INT 4
83 #define INT_TYPE_AI3_DMA 5
84 #ifdef unused
85 #define INT_TYPE_AI1_DMA_RTC 9
86 #define INT_TYPE_AI3_DMA_RTC 10
87
88 /* RTC stuff... */
89 #define RTC_IRQ         8
90 #define RTC_IO_EXTENT   0x10
91 #endif
92
93 #define MAGIC_DMA_WORD 0x5a5a
94
95 static const struct comedi_lrange range_pcl816 = { 8, {
96                                                        BIP_RANGE(10),
97                                                        BIP_RANGE(5),
98                                                        BIP_RANGE(2.5),
99                                                        BIP_RANGE(1.25),
100                                                        UNI_RANGE(10),
101                                                        UNI_RANGE(5),
102                                                        UNI_RANGE(2.5),
103                                                        UNI_RANGE(1.25),
104                                                        }
105 };
106
107 struct pcl816_board {
108
109         const char *name;       /*  board name */
110         int n_ranges;           /*  len of range list */
111         int n_aichan;           /*  num of A/D chans in diferencial mode */
112         unsigned int ai_ns_min; /*  minimal allowed delay between samples (in ns) */
113         int n_aochan;           /*  num of D/A chans */
114         int n_dichan;           /*  num of DI chans */
115         int n_dochan;           /*  num of DO chans */
116         const struct comedi_lrange *ai_range_type;      /*  default A/D rangelist */
117         const struct comedi_lrange *ao_range_type;      /*  default D/A rangelist */
118         unsigned int io_range;  /*  len of IO space */
119         unsigned int IRQbits;   /*  allowed interrupts */
120         unsigned int DMAbits;   /*  allowed DMA chans */
121         int ai_maxdata;         /*  maxdata for A/D */
122         int ao_maxdata;         /*  maxdata for D/A */
123         int ai_chanlist;        /*  allowed len of channel list A/D */
124         int ao_chanlist;        /*  allowed len of channel list D/A */
125         int i8254_osc_base;     /*  1/frequency of on board oscilator in ns */
126 };
127
128 #define devpriv ((struct pcl816_private *)dev->private)
129
130 #ifdef unused
131 static int RTC_lock;    /* RTC lock */
132 static int RTC_timer_lock;      /* RTC int lock */
133 #endif
134
135 struct pcl816_private {
136
137         unsigned int dma;       /*  used DMA, 0=don't use DMA */
138         int dma_rtc;            /*  1=RTC used with DMA, 0=no RTC alloc */
139 #ifdef unused
140         unsigned long rtc_iobase;       /*  RTC port region */
141         unsigned int rtc_iosize;
142         unsigned int rtc_irq;
143 #endif
144         unsigned long dmabuf[2];        /*  pointers to begin of DMA buffers */
145         unsigned int dmapages[2];       /*  len of DMA buffers in PAGE_SIZEs */
146         unsigned int hwdmaptr[2];       /*  hardware address of DMA buffers */
147         unsigned int hwdmasize[2];      /*  len of DMA buffers in Bytes */
148         unsigned int dmasamplsize;      /*  size in samples hwdmasize[0]/2 */
149         unsigned int last_top_dma;      /*  DMA pointer in last RTC int */
150         int next_dma_buf;       /*  which DMA buffer will be used next round */
151         long dma_runs_to_end;   /*  how many we must permorm DMA transfer to end of record */
152         unsigned long last_dma_run;     /*  how many bytes we must transfer on last DMA page */
153
154         unsigned int ai_scans;  /*  len of scanlist */
155         unsigned char ai_neverending;   /*  if=1, then we do neverending record (you must use cancel()) */
156         int irq_free;           /*  1=have allocated IRQ */
157         int irq_blocked;        /*  1=IRQ now uses any subdev */
158 #ifdef unused
159         int rtc_irq_blocked;    /*  1=we now do AI with DMA&RTC */
160 #endif
161         int irq_was_now_closed; /*  when IRQ finish, there's stored int816_mode for last interrupt */
162         int int816_mode;        /*  who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
163         struct comedi_subdevice *last_int_sub;  /*  ptr to subdevice which now finish */
164         int ai_act_scan;        /*  how many scans we finished */
165         unsigned int ai_act_chanlist[16];       /*  MUX setting for actual AI operations */
166         unsigned int ai_act_chanlist_len;       /*  how long is actual MUX list */
167         unsigned int ai_act_chanlist_pos;       /*  actual position in MUX list */
168         unsigned int ai_n_chan;         /*  how many channels per scan */
169         unsigned int ai_poll_ptr;       /*  how many sampes transfer poll */
170         struct comedi_subdevice *sub_ai;        /*  ptr to AI subdevice */
171 #ifdef unused
172         struct timer_list rtc_irq_timer;        /*  timer for RTC sanity check */
173         unsigned long rtc_freq; /*  RTC int freq */
174 #endif
175 };
176
177 /*
178 ==============================================================================
179 */
180 static int check_channel_list(struct comedi_device *dev,
181                               struct comedi_subdevice *s,
182                               unsigned int *chanlist, unsigned int chanlen);
183 static void setup_channel_list(struct comedi_device *dev,
184                                struct comedi_subdevice *s,
185                                unsigned int *chanlist, unsigned int seglen);
186 static int pcl816_ai_cancel(struct comedi_device *dev,
187                             struct comedi_subdevice *s);
188 static void start_pacer(struct comedi_device *dev, int mode,
189                         unsigned int divisor1, unsigned int divisor2);
190 #ifdef unused
191 static int set_rtc_irq_bit(unsigned char bit);
192 #endif
193
194 static int pcl816_ai_cmdtest(struct comedi_device *dev,
195                              struct comedi_subdevice *s,
196                              struct comedi_cmd *cmd);
197 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
198
199 /*
200 ==============================================================================
201    ANALOG INPUT MODE0, 816 cards, slow version
202 */
203 static int pcl816_ai_insn_read(struct comedi_device *dev,
204                                struct comedi_subdevice *s,
205                                struct comedi_insn *insn, unsigned int *data)
206 {
207         int n;
208         int timeout;
209
210         DPRINTK("mode 0 analog input\n");
211         /*  software trigger, DMA and INT off */
212         outb(0, dev->iobase + PCL816_CONTROL);
213         /*  clear INT (conversion end) flag */
214         outb(0, dev->iobase + PCL816_CLRINT);
215
216         /*  Set the input channel */
217         outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
218         /* select gain */
219         outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE);
220
221         for (n = 0; n < insn->n; n++) {
222
223                 outb(0, dev->iobase + PCL816_AD_LO);    /* start conversion */
224
225                 timeout = 100;
226                 while (timeout--) {
227                         if (!(inb(dev->iobase + PCL816_STATUS) &
228                               PCL816_STATUS_DRDY_MASK)) {
229                                 /*  return read value */
230                                 data[n] =
231                                     ((inb(dev->iobase +
232                                           PCL816_AD_HI) << 8) |
233                                      (inb(dev->iobase + PCL816_AD_LO)));
234                                 /* clear INT (conversion end) flag */
235                                 outb(0, dev->iobase + PCL816_CLRINT);
236                                 break;
237                         }
238                         udelay(1);
239                 }
240                 /*  Return timeout error */
241                 if (!timeout) {
242                         comedi_error(dev, "A/D insn timeout\n");
243                         data[0] = 0;
244                         /* clear INT (conversion end) flag */
245                         outb(0, dev->iobase + PCL816_CLRINT);
246                         return -EIO;
247                 }
248
249         }
250         return n;
251 }
252
253 /*
254 ==============================================================================
255    analog input interrupt mode 1 & 3, 818 cards
256    one sample per interrupt version
257 */
258 static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
259 {
260         struct comedi_device *dev = d;
261         struct comedi_subdevice *s = dev->subdevices + 0;
262         int low, hi;
263         int timeout = 50;       /* wait max 50us */
264
265         while (timeout--) {
266                 if (!(inb(dev->iobase + PCL816_STATUS) &
267                       PCL816_STATUS_DRDY_MASK))
268                         break;
269                 udelay(1);
270         }
271         if (!timeout) {         /*  timeout, bail error */
272                 outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT request */
273                 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
274                 pcl816_ai_cancel(dev, s);
275                 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
276                 comedi_event(dev, s);
277                 return IRQ_HANDLED;
278
279         }
280
281         /*  get the sample */
282         low = inb(dev->iobase + PCL816_AD_LO);
283         hi = inb(dev->iobase + PCL816_AD_HI);
284
285         comedi_buf_put(s->async, (hi << 8) | low);
286
287         outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT request */
288
289         if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
290                 devpriv->ai_act_chanlist_pos = 0;
291
292         s->async->cur_chan++;
293         if (s->async->cur_chan >= devpriv->ai_n_chan) {
294                 s->async->cur_chan = 0;
295                 devpriv->ai_act_scan++;
296         }
297
298         if (!devpriv->ai_neverending)
299                                         /* all data sampled */
300                 if (devpriv->ai_act_scan >= devpriv->ai_scans) {
301                         /* all data sampled */
302                         pcl816_ai_cancel(dev, s);
303                         s->async->events |= COMEDI_CB_EOA;
304                 }
305         comedi_event(dev, s);
306         return IRQ_HANDLED;
307 }
308
309 /*
310 ==============================================================================
311    analog input dma mode 1 & 3, 816 cards
312 */
313 static void transfer_from_dma_buf(struct comedi_device *dev,
314                                   struct comedi_subdevice *s, short *ptr,
315                                   unsigned int bufptr, unsigned int len)
316 {
317         int i;
318
319         s->async->events = 0;
320
321         for (i = 0; i < len; i++) {
322
323                 comedi_buf_put(s->async, ptr[bufptr++]);
324
325                 if (++devpriv->ai_act_chanlist_pos >=
326                     devpriv->ai_act_chanlist_len) {
327                         devpriv->ai_act_chanlist_pos = 0;
328                 }
329
330                 s->async->cur_chan++;
331                 if (s->async->cur_chan >= devpriv->ai_n_chan) {
332                         s->async->cur_chan = 0;
333                         devpriv->ai_act_scan++;
334                 }
335
336                 if (!devpriv->ai_neverending)
337                                                 /*  all data sampled */
338                         if (devpriv->ai_act_scan >= devpriv->ai_scans) {
339                                 pcl816_ai_cancel(dev, s);
340                                 s->async->events |= COMEDI_CB_EOA;
341                                 s->async->events |= COMEDI_CB_BLOCK;
342                                 break;
343                         }
344         }
345
346         comedi_event(dev, s);
347 }
348
349 static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
350 {
351         struct comedi_device *dev = d;
352         struct comedi_subdevice *s = dev->subdevices + 0;
353         int len, bufptr, this_dma_buf;
354         unsigned long dma_flags;
355         short *ptr;
356
357         disable_dma(devpriv->dma);
358         this_dma_buf = devpriv->next_dma_buf;
359
360         /*  switch dma bufs */
361         if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) {
362
363                 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
364                 set_dma_mode(devpriv->dma, DMA_MODE_READ);
365                 dma_flags = claim_dma_lock();
366 /* clear_dma_ff (devpriv->dma); */
367                 set_dma_addr(devpriv->dma,
368                              devpriv->hwdmaptr[devpriv->next_dma_buf]);
369                 if (devpriv->dma_runs_to_end) {
370                         set_dma_count(devpriv->dma,
371                                       devpriv->hwdmasize[devpriv->
372                                                          next_dma_buf]);
373                 } else {
374                         set_dma_count(devpriv->dma, devpriv->last_dma_run);
375                 }
376                 release_dma_lock(dma_flags);
377                 enable_dma(devpriv->dma);
378         }
379
380         devpriv->dma_runs_to_end--;
381         outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT request */
382
383         ptr = (short *)devpriv->dmabuf[this_dma_buf];
384
385         len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
386         bufptr = devpriv->ai_poll_ptr;
387         devpriv->ai_poll_ptr = 0;
388
389         transfer_from_dma_buf(dev, s, ptr, bufptr, len);
390         return IRQ_HANDLED;
391 }
392
393 /*
394 ==============================================================================
395     INT procedure
396 */
397 static irqreturn_t interrupt_pcl816(int irq, void *d)
398 {
399         struct comedi_device *dev = d;
400         DPRINTK("<I>");
401
402         if (!dev->attached) {
403                 comedi_error(dev, "premature interrupt");
404                 return IRQ_HANDLED;
405         }
406
407         switch (devpriv->int816_mode) {
408         case INT_TYPE_AI1_DMA:
409         case INT_TYPE_AI3_DMA:
410                 return interrupt_pcl816_ai_mode13_dma(irq, d);
411         case INT_TYPE_AI1_INT:
412         case INT_TYPE_AI3_INT:
413                 return interrupt_pcl816_ai_mode13_int(irq, d);
414         }
415
416         outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT request */
417         if (!dev->irq || !devpriv->irq_free || !devpriv->irq_blocked ||
418             !devpriv->int816_mode) {
419                 if (devpriv->irq_was_now_closed) {
420                         devpriv->irq_was_now_closed = 0;
421                         /*  comedi_error(dev,"last IRQ.."); */
422                         return IRQ_HANDLED;
423                 }
424                 comedi_error(dev, "bad IRQ!");
425                 return IRQ_NONE;
426         }
427         comedi_error(dev, "IRQ from unknown source!");
428         return IRQ_NONE;
429 }
430
431 /*
432 ==============================================================================
433    COMMAND MODE
434 */
435 static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
436 {
437         printk(KERN_INFO "pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
438                cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
439         printk(KERN_INFO "pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
440                cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
441         printk(KERN_INFO "pcl816 e=%d stopsrc=%x scanend=%x\n", e,
442                cmd->stop_src, cmd->scan_end_src);
443         printk(KERN_INFO "pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n",
444                e, cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
445 }
446
447 /*
448 ==============================================================================
449 */
450 static int pcl816_ai_cmdtest(struct comedi_device *dev,
451                              struct comedi_subdevice *s, struct comedi_cmd *cmd)
452 {
453         const struct pcl816_board *board = comedi_board(dev);
454         int err = 0;
455         int tmp, divisor1 = 0, divisor2 = 0;
456
457         DEBUG(printk(KERN_INFO "pcl816 pcl812_ai_cmdtest\n");
458               pcl816_cmdtest_out(-1, cmd);
459              );
460
461         /* step 1: make sure trigger sources are trivially valid */
462         tmp = cmd->start_src;
463         cmd->start_src &= TRIG_NOW;
464         if (!cmd->start_src || tmp != cmd->start_src)
465                 err++;
466
467         tmp = cmd->scan_begin_src;
468         cmd->scan_begin_src &= TRIG_FOLLOW;
469         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
470                 err++;
471
472         tmp = cmd->convert_src;
473         cmd->convert_src &= TRIG_EXT | TRIG_TIMER;
474         if (!cmd->convert_src || tmp != cmd->convert_src)
475                 err++;
476
477         tmp = cmd->scan_end_src;
478         cmd->scan_end_src &= TRIG_COUNT;
479         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
480                 err++;
481
482         tmp = cmd->stop_src;
483         cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
484         if (!cmd->stop_src || tmp != cmd->stop_src)
485                 err++;
486
487         if (err)
488                 return 1;
489
490
491         /*
492          * step 2: make sure trigger sources
493          * are unique and mutually compatible
494          */
495
496         if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
497                 cmd->convert_src = TRIG_TIMER;
498                 err++;
499         }
500
501         if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
502                 err++;
503
504         if (err)
505                 return 2;
506
507
508         /* step 3: make sure arguments are trivially compatible */
509         if (cmd->start_arg != 0) {
510                 cmd->start_arg = 0;
511                 err++;
512         }
513
514         if (cmd->scan_begin_arg != 0) {
515                 cmd->scan_begin_arg = 0;
516                 err++;
517         }
518         if (cmd->convert_src == TRIG_TIMER) {
519                 if (cmd->convert_arg < board->ai_ns_min) {
520                         cmd->convert_arg = board->ai_ns_min;
521                         err++;
522                 }
523         } else {                /* TRIG_EXT */
524                 if (cmd->convert_arg != 0) {
525                         cmd->convert_arg = 0;
526                         err++;
527                 }
528         }
529
530         if (cmd->scan_end_arg != cmd->chanlist_len) {
531                 cmd->scan_end_arg = cmd->chanlist_len;
532                 err++;
533         }
534         if (cmd->stop_src == TRIG_COUNT) {
535                 if (!cmd->stop_arg) {
536                         cmd->stop_arg = 1;
537                         err++;
538                 }
539         } else {                /* TRIG_NONE */
540                 if (cmd->stop_arg != 0) {
541                         cmd->stop_arg = 0;
542                         err++;
543                 }
544         }
545
546         if (err)
547                 return 3;
548
549
550         /* step 4: fix up any arguments */
551         if (cmd->convert_src == TRIG_TIMER) {
552                 tmp = cmd->convert_arg;
553                 i8253_cascade_ns_to_timer(board->i8254_osc_base,
554                                           &divisor1, &divisor2,
555                                           &cmd->convert_arg,
556                                           cmd->flags & TRIG_ROUND_MASK);
557                 if (cmd->convert_arg < board->ai_ns_min)
558                         cmd->convert_arg = board->ai_ns_min;
559                 if (tmp != cmd->convert_arg)
560                         err++;
561         }
562
563         if (err)
564                 return 4;
565
566
567         /* step 5: complain about special chanlist considerations */
568
569         if (cmd->chanlist) {
570                 if (!check_channel_list(dev, s, cmd->chanlist,
571                                         cmd->chanlist_len))
572                         return 5;       /*  incorrect channels list */
573         }
574
575         return 0;
576 }
577
578 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
579 {
580         const struct pcl816_board *board = comedi_board(dev);
581         unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
582         struct comedi_cmd *cmd = &s->async->cmd;
583         unsigned int seglen;
584
585         if (cmd->start_src != TRIG_NOW)
586                 return -EINVAL;
587         if (cmd->scan_begin_src != TRIG_FOLLOW)
588                 return -EINVAL;
589         if (cmd->scan_end_src != TRIG_COUNT)
590                 return -EINVAL;
591         if (cmd->scan_end_arg != cmd->chanlist_len)
592                 return -EINVAL;
593 /* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
594         if (devpriv->irq_blocked)
595                 return -EBUSY;
596
597         if (cmd->convert_src == TRIG_TIMER) {
598                 if (cmd->convert_arg < board->ai_ns_min)
599                         cmd->convert_arg = board->ai_ns_min;
600
601                 i8253_cascade_ns_to_timer(board->i8254_osc_base, &divisor1,
602                                           &divisor2, &cmd->convert_arg,
603                                           cmd->flags & TRIG_ROUND_MASK);
604
605                 /*  PCL816 crash if any divisor is set to 1 */
606                 if (divisor1 == 1) {
607                         divisor1 = 2;
608                         divisor2 /= 2;
609                 }
610                 if (divisor2 == 1) {
611                         divisor2 = 2;
612                         divisor1 /= 2;
613                 }
614         }
615
616         start_pacer(dev, -1, 0, 0);     /*  stop pacer */
617
618         seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
619         if (seglen < 1)
620                 return -EINVAL;
621         setup_channel_list(dev, s, cmd->chanlist, seglen);
622         udelay(1);
623
624         devpriv->ai_n_chan = cmd->chanlist_len;
625         devpriv->ai_act_scan = 0;
626         s->async->cur_chan = 0;
627         devpriv->irq_blocked = 1;
628         devpriv->ai_poll_ptr = 0;
629         devpriv->irq_was_now_closed = 0;
630
631         if (cmd->stop_src == TRIG_COUNT) {
632                 devpriv->ai_scans = cmd->stop_arg;
633                 devpriv->ai_neverending = 0;
634         } else {
635                 devpriv->ai_scans = 0;
636                 devpriv->ai_neverending = 1;
637         }
638
639         /*  don't we want wake up every scan? */
640         if ((cmd->flags & TRIG_WAKE_EOS)) {
641                 printk(KERN_INFO
642                        "pl816: You wankt WAKE_EOS but I dont want handle it");
643                 /*               devpriv->ai_eos=1; */
644                 /* if (devpriv->ai_n_chan==1) */
645                 /*       devpriv->dma=0; // DMA is useless for this situation */
646         }
647
648         if (devpriv->dma) {
649                 bytes = devpriv->hwdmasize[0];
650                 if (!devpriv->ai_neverending) {
651                         /*  how many */
652                         bytes = s->async->cmd.chanlist_len *
653                         s->async->cmd.chanlist_len *
654                         sizeof(short);
655
656                         /*  how many DMA pages we must fill */
657                         devpriv->dma_runs_to_end = bytes /
658                         devpriv->hwdmasize[0];
659
660                         /* on last dma transfer must be moved */
661                         devpriv->last_dma_run = bytes % devpriv->hwdmasize[0];
662                         devpriv->dma_runs_to_end--;
663                         if (devpriv->dma_runs_to_end >= 0)
664                                 bytes = devpriv->hwdmasize[0];
665                 } else
666                         devpriv->dma_runs_to_end = -1;
667
668                 devpriv->next_dma_buf = 0;
669                 set_dma_mode(devpriv->dma, DMA_MODE_READ);
670                 dma_flags = claim_dma_lock();
671                 clear_dma_ff(devpriv->dma);
672                 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
673                 set_dma_count(devpriv->dma, bytes);
674                 release_dma_lock(dma_flags);
675                 enable_dma(devpriv->dma);
676         }
677
678         start_pacer(dev, 1, divisor1, divisor2);
679         dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
680
681         switch (cmd->convert_src) {
682         case TRIG_TIMER:
683                 devpriv->int816_mode = INT_TYPE_AI1_DMA;
684
685                 /*  Pacer+IRQ+DMA */
686                 outb(0x32, dev->iobase + PCL816_CONTROL);
687
688                 /*  write irq and DMA to card */
689                 outb(dmairq, dev->iobase + PCL816_STATUS);
690                 break;
691
692         default:
693                 devpriv->int816_mode = INT_TYPE_AI3_DMA;
694
695                 /*  Ext trig+IRQ+DMA */
696                 outb(0x34, dev->iobase + PCL816_CONTROL);
697
698                 /*  write irq to card */
699                 outb(dmairq, dev->iobase + PCL816_STATUS);
700                 break;
701         }
702
703         DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
704         return 0;
705 }
706
707 static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
708 {
709         unsigned long flags;
710         unsigned int top1, top2, i;
711
712         if (!devpriv->dma)
713                 return 0;       /*  poll is valid only for DMA transfer */
714
715         spin_lock_irqsave(&dev->spinlock, flags);
716
717         for (i = 0; i < 20; i++) {
718                 top1 = get_dma_residue(devpriv->dma);   /*  where is now DMA */
719                 top2 = get_dma_residue(devpriv->dma);
720                 if (top1 == top2)
721                         break;
722         }
723         if (top1 != top2) {
724                 spin_unlock_irqrestore(&dev->spinlock, flags);
725                 return 0;
726         }
727
728         /*  where is now DMA in buffer */
729         top1 = devpriv->hwdmasize[0] - top1;
730         top1 >>= 1;             /*  sample position */
731         top2 = top1 - devpriv->ai_poll_ptr;
732         if (top2 < 1) {         /*  no new samples */
733                 spin_unlock_irqrestore(&dev->spinlock, flags);
734                 return 0;
735         }
736
737         transfer_from_dma_buf(dev, s,
738                               (short *)devpriv->dmabuf[devpriv->next_dma_buf],
739                               devpriv->ai_poll_ptr, top2);
740
741         devpriv->ai_poll_ptr = top1;    /*  new buffer position */
742         spin_unlock_irqrestore(&dev->spinlock, flags);
743
744         return s->async->buf_write_count - s->async->buf_read_count;
745 }
746
747 /*
748 ==============================================================================
749  cancel any mode 1-4 AI
750 */
751 static int pcl816_ai_cancel(struct comedi_device *dev,
752                             struct comedi_subdevice *s)
753 {
754 /* DEBUG(printk("pcl816_ai_cancel()\n");) */
755
756         if (devpriv->irq_blocked > 0) {
757                 switch (devpriv->int816_mode) {
758 #ifdef unused
759                 case INT_TYPE_AI1_DMA_RTC:
760                 case INT_TYPE_AI3_DMA_RTC:
761                         set_rtc_irq_bit(0);     /*  stop RTC */
762                         del_timer(&devpriv->rtc_irq_timer);
763 #endif
764                 case INT_TYPE_AI1_DMA:
765                 case INT_TYPE_AI3_DMA:
766                         disable_dma(devpriv->dma);
767                 case INT_TYPE_AI1_INT:
768                 case INT_TYPE_AI3_INT:
769                         outb(inb(dev->iobase + PCL816_CONTROL) & 0x73,
770                              dev->iobase + PCL816_CONTROL);     /* Stop A/D */
771                         udelay(1);
772                         outb(0, dev->iobase + PCL816_CONTROL);  /* Stop A/D */
773
774                         /* Stop pacer */
775                         outb(0xb0, dev->iobase + PCL816_CTRCTL);
776                         outb(0x70, dev->iobase + PCL816_CTRCTL);
777                         outb(0, dev->iobase + PCL816_AD_LO);
778                         inb(dev->iobase + PCL816_AD_LO);
779                         inb(dev->iobase + PCL816_AD_HI);
780
781                         /* clear INT request */
782                         outb(0, dev->iobase + PCL816_CLRINT);
783
784                         /* Stop A/D */
785                         outb(0, dev->iobase + PCL816_CONTROL);
786                         devpriv->irq_blocked = 0;
787                         devpriv->irq_was_now_closed = devpriv->int816_mode;
788                         devpriv->int816_mode = 0;
789                         devpriv->last_int_sub = s;
790 /* s->busy = 0; */
791                         break;
792                 }
793         }
794
795         DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
796             return 0;
797 }
798
799 /*
800 ==============================================================================
801  chech for PCL816
802 */
803 static int pcl816_check(unsigned long iobase)
804 {
805         outb(0x00, iobase + PCL816_MUX);
806         udelay(1);
807         if (inb(iobase + PCL816_MUX) != 0x00)
808                 return 1;       /* there isn't card */
809         outb(0x55, iobase + PCL816_MUX);
810         udelay(1);
811         if (inb(iobase + PCL816_MUX) != 0x55)
812                 return 1;       /* there isn't card */
813         outb(0x00, iobase + PCL816_MUX);
814         udelay(1);
815         outb(0x18, iobase + PCL816_CONTROL);
816         udelay(1);
817         if (inb(iobase + PCL816_CONTROL) != 0x18)
818                 return 1;       /* there isn't card */
819         return 0;               /*  ok, card exist */
820 }
821
822 /*
823 ==============================================================================
824  reset whole PCL-816 cards
825 */
826 static void pcl816_reset(struct comedi_device *dev)
827 {
828 /* outb (0, dev->iobase + PCL818_DA_LO);         DAC=0V */
829 /* outb (0, dev->iobase + PCL818_DA_HI); */
830 /* udelay (1); */
831 /* outb (0, dev->iobase + PCL818_DO_HI);        DO=$0000 */
832 /* outb (0, dev->iobase + PCL818_DO_LO); */
833 /* udelay (1); */
834         outb(0, dev->iobase + PCL816_CONTROL);
835         outb(0, dev->iobase + PCL816_MUX);
836         outb(0, dev->iobase + PCL816_CLRINT);
837         outb(0xb0, dev->iobase + PCL816_CTRCTL);        /* Stop pacer */
838         outb(0x70, dev->iobase + PCL816_CTRCTL);
839         outb(0x30, dev->iobase + PCL816_CTRCTL);
840         outb(0, dev->iobase + PCL816_RANGE);
841 }
842
843 /*
844 ==============================================================================
845  Start/stop pacer onboard pacer
846 */
847 static void
848 start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
849             unsigned int divisor2)
850 {
851         outb(0x32, dev->iobase + PCL816_CTRCTL);
852         outb(0xff, dev->iobase + PCL816_CTR0);
853         outb(0x00, dev->iobase + PCL816_CTR0);
854         udelay(1);
855
856         /*  set counter 2 as mode 3 */
857         outb(0xb4, dev->iobase + PCL816_CTRCTL);
858         /*  set counter 1 as mode 3 */
859         outb(0x74, dev->iobase + PCL816_CTRCTL);
860         udelay(1);
861
862         if (mode == 1) {
863                 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
864                         divisor2);
865                 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
866                 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
867                 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
868                 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
869         }
870
871         /* clear pending interrupts (just in case) */
872 /* outb(0, dev->iobase + PCL816_CLRINT); */
873 }
874
875 /*
876 ==============================================================================
877  Check if channel list from user is builded correctly
878  If it's ok, then return non-zero length of repeated segment of channel list
879 */
880 static int
881 check_channel_list(struct comedi_device *dev,
882                    struct comedi_subdevice *s, unsigned int *chanlist,
883                    unsigned int chanlen)
884 {
885         unsigned int chansegment[16];
886         unsigned int i, nowmustbechan, seglen, segpos;
887
888         /*  correct channel and range number check itself comedi/range.c */
889         if (chanlen < 1) {
890                 comedi_error(dev, "range/channel list is empty!");
891                 return 0;
892         }
893
894         if (chanlen > 1) {
895                 /*  first channel is every time ok */
896                 chansegment[0] = chanlist[0];
897                 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
898                         /*  build part of chanlist */
899                         DEBUG(printk(KERN_INFO "%d. %d %d\n", i,
900                                      CR_CHAN(chanlist[i]),
901                                      CR_RANGE(chanlist[i]));)
902
903                         /*  we detect loop, this must by finish */
904                             if (chanlist[0] == chanlist[i])
905                                 break;
906                         nowmustbechan =
907                             (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
908                         if (nowmustbechan != CR_CHAN(chanlist[i])) {
909                                 /*  channel list isn't continuous :-( */
910                                 printk(KERN_WARNING
911                                        "comedi%d: pcl816: channel list must "
912                                        "be continuous! chanlist[%i]=%d but "
913                                        "must be %d or %d!\n", dev->minor,
914                                        i, CR_CHAN(chanlist[i]), nowmustbechan,
915                                        CR_CHAN(chanlist[0]));
916                                 return 0;
917                         }
918                         /*  well, this is next correct channel in list */
919                         chansegment[i] = chanlist[i];
920                 }
921
922                 /*  check whole chanlist */
923                 for (i = 0, segpos = 0; i < chanlen; i++) {
924                         DEBUG(printk("%d %d=%d %d\n",
925                                      CR_CHAN(chansegment[i % seglen]),
926                                      CR_RANGE(chansegment[i % seglen]),
927                                      CR_CHAN(chanlist[i]),
928                                      CR_RANGE(chanlist[i]));)
929                             if (chanlist[i] != chansegment[i % seglen]) {
930                                 printk(KERN_WARNING
931                                        "comedi%d: pcl816: bad channel or range"
932                                        " number! chanlist[%i]=%d,%d,%d and not"
933                                        " %d,%d,%d!\n", dev->minor, i,
934                                        CR_CHAN(chansegment[i]),
935                                        CR_RANGE(chansegment[i]),
936                                        CR_AREF(chansegment[i]),
937                                        CR_CHAN(chanlist[i % seglen]),
938                                        CR_RANGE(chanlist[i % seglen]),
939                                        CR_AREF(chansegment[i % seglen]));
940                                 return 0;       /*  chan/gain list is strange */
941                         }
942                 }
943         } else {
944                 seglen = 1;
945         }
946
947         return seglen;  /*  we can serve this with MUX logic */
948 }
949
950 /*
951 ==============================================================================
952  Program scan/gain logic with channel list.
953 */
954 static void
955 setup_channel_list(struct comedi_device *dev,
956                    struct comedi_subdevice *s, unsigned int *chanlist,
957                    unsigned int seglen)
958 {
959         unsigned int i;
960
961         devpriv->ai_act_chanlist_len = seglen;
962         devpriv->ai_act_chanlist_pos = 0;
963
964         for (i = 0; i < seglen; i++) {  /*  store range list to card */
965                 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
966                 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
967                 /* select gain */
968                 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE);
969         }
970
971         udelay(1);
972         /* select channel interval to scan */
973         outb(devpriv->ai_act_chanlist[0] |
974              (devpriv->ai_act_chanlist[seglen - 1] << 4),
975              dev->iobase + PCL816_MUX);
976 }
977
978 #ifdef unused
979 /*
980 ==============================================================================
981   Enable(1)/disable(0) periodic interrupts from RTC
982 */
983 static int set_rtc_irq_bit(unsigned char bit)
984 {
985         unsigned char val;
986         unsigned long flags;
987
988         if (bit == 1) {
989                 RTC_timer_lock++;
990                 if (RTC_timer_lock > 1)
991                         return 0;
992         } else {
993                 RTC_timer_lock--;
994                 if (RTC_timer_lock < 0)
995                         RTC_timer_lock = 0;
996                 if (RTC_timer_lock > 0)
997                         return 0;
998         }
999
1000         save_flags(flags);
1001         cli();
1002         val = CMOS_READ(RTC_CONTROL);
1003         if (bit)
1004                 val |= RTC_PIE;
1005         else
1006                 val &= ~RTC_PIE;
1007
1008         CMOS_WRITE(val, RTC_CONTROL);
1009         CMOS_READ(RTC_INTR_FLAGS);
1010         restore_flags(flags);
1011         return 0;
1012 }
1013 #endif
1014
1015 static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1016 {
1017         const struct pcl816_board *board = comedi_board(dev);
1018         int ret;
1019         unsigned long iobase;
1020         unsigned int irq, dma;
1021         unsigned long pages;
1022         /* int i; */
1023         struct comedi_subdevice *s;
1024
1025         /* claim our I/O space */
1026         iobase = it->options[0];
1027         printk("comedi%d: pcl816:  board=%s, ioport=0x%03lx", dev->minor,
1028                board->name, iobase);
1029
1030         if (!request_region(iobase, board->io_range, "pcl816")) {
1031                 printk("I/O port conflict\n");
1032                 return -EIO;
1033         }
1034
1035         dev->iobase = iobase;
1036
1037         if (pcl816_check(iobase)) {
1038                 printk(KERN_ERR ", I cann't detect board. FAIL!\n");
1039                 return -EIO;
1040         }
1041
1042         ret = alloc_private(dev, sizeof(struct pcl816_private));
1043         if (ret < 0)
1044                 return ret;     /* Can't alloc mem */
1045
1046         dev->board_name = board->name;
1047
1048         /* grab our IRQ */
1049         irq = 0;
1050         if (board->IRQbits != 0) {      /* board support IRQ */
1051                 irq = it->options[1];
1052                 if (irq) {      /* we want to use IRQ */
1053                         if (((1 << irq) & board->IRQbits) == 0) {
1054                                 printk
1055                                     (", IRQ %u is out of allowed range, "
1056                                      "DISABLING IT", irq);
1057                                 irq = 0;        /* Bad IRQ */
1058                         } else {
1059                                 if (request_irq
1060                                     (irq, interrupt_pcl816, 0, "pcl816", dev)) {
1061                                         printk
1062                                             (", unable to allocate IRQ %u, "
1063                                              "DISABLING IT", irq);
1064                                         irq = 0;        /* Can't use IRQ */
1065                                 } else {
1066                                         printk(KERN_INFO ", irq=%u", irq);
1067                                 }
1068                         }
1069                 }
1070         }
1071
1072         dev->irq = irq;
1073         if (irq)        /* 1=we have allocated irq */
1074                 devpriv->irq_free = 1;
1075         else
1076                 devpriv->irq_free = 0;
1077
1078         devpriv->irq_blocked = 0;       /* number of subdevice which use IRQ */
1079         devpriv->int816_mode = 0;       /* mode of irq */
1080
1081 #ifdef unused
1082         /* grab RTC for DMA operations */
1083         devpriv->dma_rtc = 0;
1084         if (it->options[2] > 0) {       /*  we want to use DMA */
1085                 if (RTC_lock == 0) {
1086                         if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
1087                                             "pcl816 (RTC)"))
1088                                 goto no_rtc;
1089                 }
1090                 devpriv->rtc_iobase = RTC_PORT(0);
1091                 devpriv->rtc_iosize = RTC_IO_EXTENT;
1092                 RTC_lock++;
1093 #ifdef UNTESTED_CODE
1094                 if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0,
1095                                  "pcl816 DMA (RTC)", dev)) {
1096                         devpriv->dma_rtc = 1;
1097                         devpriv->rtc_irq = RTC_IRQ;
1098                         printk(", dma_irq=%u", devpriv->rtc_irq);
1099                 } else {
1100                         RTC_lock--;
1101                         if (RTC_lock == 0) {
1102                                 if (devpriv->rtc_iobase)
1103                                         release_region(devpriv->rtc_iobase,
1104                                                        devpriv->rtc_iosize);
1105                         }
1106                         devpriv->rtc_iobase = 0;
1107                         devpriv->rtc_iosize = 0;
1108                 }
1109 #else
1110                 printk("pcl816: RTC code missing");
1111 #endif
1112
1113         }
1114
1115 no_rtc:
1116 #endif
1117         /* grab our DMA */
1118         dma = 0;
1119         devpriv->dma = dma;
1120         if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1121                 goto no_dma;    /* if we haven't IRQ, we can't use DMA */
1122
1123         if (board->DMAbits != 0) {      /* board support DMA */
1124                 dma = it->options[2];
1125                 if (dma < 1)
1126                         goto no_dma;    /* DMA disabled */
1127
1128                 if (((1 << dma) & board->DMAbits) == 0) {
1129                         printk(", DMA is out of allowed range, FAIL!\n");
1130                         return -EINVAL; /* Bad DMA */
1131                 }
1132                 ret = request_dma(dma, "pcl816");
1133                 if (ret) {
1134                         printk(KERN_ERR
1135                                ", unable to allocate DMA %u, FAIL!\n", dma);
1136                         return -EBUSY;  /* DMA isn't free */
1137                 }
1138
1139                 devpriv->dma = dma;
1140                 printk(KERN_INFO ", dma=%u", dma);
1141                 pages = 2;      /* we need 16KB */
1142                 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1143
1144                 if (!devpriv->dmabuf[0]) {
1145                         printk(", unable to allocate DMA buffer, FAIL!\n");
1146                         /*
1147                          * maybe experiment with try_to_free_pages()
1148                          * will help ....
1149                          */
1150                         return -EBUSY;  /* no buffer :-( */
1151                 }
1152                 devpriv->dmapages[0] = pages;
1153                 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1154                 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
1155                 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
1156
1157                 if (devpriv->dma_rtc == 0) {    /*  we must do duble buff :-( */
1158                         devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1159                         if (!devpriv->dmabuf[1]) {
1160                                 printk(KERN_ERR
1161                                        ", unable to allocate DMA buffer, "
1162                                        "FAIL!\n");
1163                                 return -EBUSY;
1164                         }
1165                         devpriv->dmapages[1] = pages;
1166                         devpriv->hwdmaptr[1] =
1167                             virt_to_bus((void *)devpriv->dmabuf[1]);
1168                         devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1169                 }
1170         }
1171
1172 no_dma:
1173
1174 /*  if (board->n_aochan > 0)
1175     subdevs[1] = COMEDI_SUBD_AO;
1176   if (board->n_dichan > 0)
1177     subdevs[2] = COMEDI_SUBD_DI;
1178   if (board->n_dochan > 0)
1179     subdevs[3] = COMEDI_SUBD_DO;
1180 */
1181
1182         ret = comedi_alloc_subdevices(dev, 1);
1183         if (ret)
1184                 return ret;
1185
1186         s = dev->subdevices + 0;
1187         if (board->n_aichan > 0) {
1188                 s->type = COMEDI_SUBD_AI;
1189                 devpriv->sub_ai = s;
1190                 dev->read_subdev = s;
1191                 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1192                 s->n_chan = board->n_aichan;
1193                 s->subdev_flags |= SDF_DIFF;
1194                 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
1195                 s->maxdata = board->ai_maxdata;
1196                 s->len_chanlist = board->ai_chanlist;
1197                 s->range_table = board->ai_range_type;
1198                 s->cancel = pcl816_ai_cancel;
1199                 s->do_cmdtest = pcl816_ai_cmdtest;
1200                 s->do_cmd = pcl816_ai_cmd;
1201                 s->poll = pcl816_ai_poll;
1202                 s->insn_read = pcl816_ai_insn_read;
1203         } else {
1204                 s->type = COMEDI_SUBD_UNUSED;
1205         }
1206
1207 #if 0
1208 case COMEDI_SUBD_AO:
1209         s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1210         s->n_chan = board->n_aochan;
1211         s->maxdata = board->ao_maxdata;
1212         s->len_chanlist = board->ao_chanlist;
1213         s->range_table = board->ao_range_type;
1214         break;
1215
1216 case COMEDI_SUBD_DI:
1217         s->subdev_flags = SDF_READABLE;
1218         s->n_chan = board->n_dichan;
1219         s->maxdata = 1;
1220         s->len_chanlist = board->n_dichan;
1221         s->range_table = &range_digital;
1222         break;
1223
1224 case COMEDI_SUBD_DO:
1225         s->subdev_flags = SDF_WRITABLE;
1226         s->n_chan = board->n_dochan;
1227         s->maxdata = 1;
1228         s->len_chanlist = board->n_dochan;
1229         s->range_table = &range_digital;
1230         break;
1231 #endif
1232
1233         pcl816_reset(dev);
1234
1235         printk("\n");
1236
1237         return 0;
1238 }
1239
1240 static void pcl816_detach(struct comedi_device *dev)
1241 {
1242         const struct pcl816_board *board = comedi_board(dev);
1243
1244         if (dev->private) {
1245                 pcl816_ai_cancel(dev, devpriv->sub_ai);
1246                 pcl816_reset(dev);
1247                 if (devpriv->dma)
1248                         free_dma(devpriv->dma);
1249                 if (devpriv->dmabuf[0])
1250                         free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1251                 if (devpriv->dmabuf[1])
1252                         free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1253 #ifdef unused
1254                 if (devpriv->rtc_irq)
1255                         free_irq(devpriv->rtc_irq, dev);
1256                 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
1257                         if (devpriv->rtc_iobase)
1258                                 release_region(devpriv->rtc_iobase,
1259                                                devpriv->rtc_iosize);
1260                 }
1261 #endif
1262         }
1263         if (dev->irq)
1264                 free_irq(dev->irq, dev);
1265         if (dev->iobase)
1266                 release_region(dev->iobase, board->io_range);
1267 #ifdef unused
1268         if (devpriv->dma_rtc)
1269                 RTC_lock--;
1270 #endif
1271 }
1272
1273 static const struct pcl816_board boardtypes[] = {
1274         {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
1275          &range_pcl816, PCLx1x_RANGE,
1276          0x00fc,                /*  IRQ mask */
1277          0x0a,                  /*  DMA mask */
1278          0xffff,                /*  16-bit card */
1279          0xffff,                /*  D/A maxdata */
1280          1024,
1281          1,                     /*  ao chan list */
1282          100},
1283         {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
1284          &range_pcl816, PCLx1x_RANGE,
1285          0x00fc,
1286          0x0a,
1287          0x3fff,                /* 14 bit card */
1288          0x3fff,
1289          1024,
1290          1,
1291          100},
1292 };
1293
1294 static struct comedi_driver pcl816_driver = {
1295         .driver_name    = "pcl816",
1296         .module         = THIS_MODULE,
1297         .attach         = pcl816_attach,
1298         .detach         = pcl816_detach,
1299         .board_name     = &boardtypes[0].name,
1300         .num_names      = ARRAY_SIZE(boardtypes),
1301         .offset         = sizeof(struct pcl816_board),
1302 };
1303 module_comedi_driver(pcl816_driver);
1304
1305 MODULE_AUTHOR("Comedi http://www.comedi.org");
1306 MODULE_DESCRIPTION("Comedi low-level driver");
1307 MODULE_LICENSE("GPL");