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