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