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