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