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