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