blob: f1282c68d7efc43794b9217cd974fac8e8069e10 [file] [log] [blame]
Juan Grigeradd2996b2009-02-19 09:30:57 -08001/*
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/*
12Driver: pcl816
13Description: Advantech PCL-816 cards, PCL-814
14Author: Juan Grigera <juan@grigera.com.ar>
15Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
16Status: works
17Updated: Tue, 2 Apr 2002 23:15:21 -0800
18
19PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
20Differences are at resolution (16 vs 12 bits).
21
22The driver support AI command mode, other subdevices not written.
23
24Analog output and digital input and output are not supported.
25
26Configuration 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
93static const 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};
104typedef 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 comedi_lrange *ai_range_type; // default A/D rangelist
113 const 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
124static 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
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400149static int pcl816_attach(struct comedi_device * dev, comedi_devconfig * it);
150static int pcl816_detach(struct comedi_device * dev);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800151
152#ifdef unused
153static int RTC_lock = 0; /* RTC lock */
154static int RTC_timer_lock = 0; /* RTC int lock */
155#endif
156
157static 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
167COMEDI_INITCLEANUP(driver_pcl816);
168
169typedef 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
Bill Pemberton34c43922009-03-16 22:05:14 -0400196 struct comedi_subdevice *last_int_sub; // ptr to subdevice which now finish
Juan Grigeradd2996b2009-02-19 09:30:57 -0800197 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
Bill Pemberton34c43922009-03-16 22:05:14 -0400202 struct comedi_subdevice *sub_ai; // ptr to AI subdevice
Juan Grigeradd2996b2009-02-19 09:30:57 -0800203#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*/
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400212static int check_and_setup_channel_list(struct comedi_device * dev,
Bill Pemberton34c43922009-03-16 22:05:14 -0400213 struct comedi_subdevice * s, unsigned int *chanlist, int chanlen);
214static int pcl816_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400215static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
Juan Grigeradd2996b2009-02-19 09:30:57 -0800216 unsigned int divisor2);
217#ifdef unused
218static int set_rtc_irq_bit(unsigned char bit);
219#endif
220
Bill Pemberton34c43922009-03-16 22:05:14 -0400221static int pcl816_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
Juan Grigeradd2996b2009-02-19 09:30:57 -0800222 comedi_cmd * cmd);
Bill Pemberton34c43922009-03-16 22:05:14 -0400223static int pcl816_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800224
225/*
226==============================================================================
227 ANALOG INPUT MODE0, 816 cards, slow version
228*/
Bill Pemberton34c43922009-03-16 22:05:14 -0400229static int pcl816_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
Bill Pemberton790c5542009-03-16 22:05:02 -0400230 comedi_insn * insn, unsigned int * data)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800231{
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*/
281static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
282{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400283 struct comedi_device *dev = d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400284 struct comedi_subdevice *s = dev->subdevices + 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800285 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*/
Bill Pemberton34c43922009-03-16 22:05:14 -0400333static void transfer_from_dma_buf(struct comedi_device * dev, struct comedi_subdevice * s,
Bill Pemberton790c5542009-03-16 22:05:02 -0400334 short * ptr, unsigned int bufptr, unsigned int len)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800335{
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
362static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
363{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400364 struct comedi_device *dev = d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400365 struct comedi_subdevice *s = dev->subdevices + 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800366 int len, bufptr, this_dma_buf;
367 unsigned long dma_flags;
Bill Pemberton790c5542009-03-16 22:05:02 -0400368 short *ptr;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800369
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
Bill Pemberton790c5542009-03-16 22:05:02 -0400394 ptr = (short *) devpriv->dmabuf[this_dma_buf];
Juan Grigeradd2996b2009-02-19 09:30:57 -0800395
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*/
408static irqreturn_t interrupt_pcl816(int irq, void *d PT_REGS_ARG)
409{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400410 struct comedi_device *dev = d;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800411 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*/
446static void pcl816_cmdtest_out(int e, 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*/
Bill Pemberton34c43922009-03-16 22:05:14 -0400461static int pcl816_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
Juan Grigeradd2996b2009-02-19 09:30:57 -0800462 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
Bill Pemberton34c43922009-03-16 22:05:14 -0400596static int pcl816_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800597{
598 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
599 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) {
Bill Pemberton790c5542009-03-16 22:05:02 -0400661 bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short); // how many
Juan Grigeradd2996b2009-02-19 09:30:57 -0800662 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
Bill Pemberton34c43922009-03-16 22:05:14 -0400701static int pcl816_ai_poll(struct comedi_device * dev, struct comedi_subdevice * s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800702{
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,
Bill Pemberton790c5542009-03-16 22:05:02 -0400731 (short *) devpriv->dmabuf[devpriv->next_dma_buf],
Juan Grigeradd2996b2009-02-19 09:30:57 -0800732 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*/
Bill Pemberton34c43922009-03-16 22:05:14 -0400744static int pcl816_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800745{
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*/
789static 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*/
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400812static void pcl816_reset(struct comedi_device * dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800813{
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*/
833static void
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400834start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
Juan Grigeradd2996b2009-02-19 09:30:57 -0800835 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*/
863static int
Bill Pemberton34c43922009-03-16 22:05:14 -0400864check_and_setup_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
Juan Grigeradd2996b2009-02-19 09:30:57 -0800865 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*/
942static 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*/
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400978static void free_resources(struct comedi_device * dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800979{
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*/
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04001014static int pcl816_attach(struct comedi_device * dev, comedi_devconfig * it)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001015{
1016 int ret;
1017 unsigned long iobase;
1018 unsigned int irq, dma;
1019 unsigned long pages;
1020 //int i;
Bill Pemberton34c43922009-03-16 22:05:14 -04001021 struct comedi_subdevice *s;
Juan Grigeradd2996b2009-02-19 09:30:57 -08001022
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
1201case 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
1209case 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
1217case 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 */
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04001237static int pcl816_detach(struct comedi_device * dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001238{
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}