2 comedi/drivers/pcl818.c
4 Author: Michal Dobes <dobes@tesnet.cz>
6 hardware driver for Advantech cards:
7 card: PCL-818L, PCL-818H, PCL-818HD, PCL-818HG, PCL-818, PCL-718
8 driver: pcl818l, pcl818h, pcl818hd, pcl818hg, pcl818, pcl718
12 Description: Advantech PCL-818 cards, PCL-718
13 Author: Michal Dobes <dobes@tesnet.cz>
14 Devices: [Advantech] PCL-818L (pcl818l), PCL-818H (pcl818h),
15 PCL-818HD (pcl818hd), PCL-818HG (pcl818hg), PCL-818 (pcl818),
19 All cards have 16 SE/8 DIFF ADCs, one or two DACs, 16 DI and 16 DO.
20 Differences are only at maximal sample speed, range list and FIFO
22 The driver support AI mode 0, 1, 3 other subdevices (AO, DI, DO) support
23 only mode 0. If DMA/FIFO/INT are disabled then AI support only mode 0.
24 PCL-818HD and PCL-818HG support 1kword FIFO. Driver support this FIFO
25 but this code is untested.
26 A word or two about DMA. Driver support DMA operations at two ways:
27 1) DMA uses two buffers and after one is filled then is generated
28 INT and DMA restart with second buffer. With this mode I'm unable run
29 more that 80Ksamples/secs without data dropouts on K6/233.
30 2) DMA uses one buffer and run in autoinit mode and the data are
31 from DMA buffer moved on the fly with 2kHz interrupts from RTC.
32 This mode is used if the interrupt 8 is available for allocation.
33 If not, then first DMA mode is used. With this I can run at
34 full speed one card (100ksamples/secs) or two cards with
35 60ksamples/secs each (more is problem on account of ISA limitations).
36 To use this mode you must have compiled kernel with disabled
37 "Enhanced Real Time Clock Support".
38 Maybe you can have problems if you use xntpd or similar.
39 If you've data dropouts with DMA mode 2 then:
41 b) switch text mode console to fb.
45 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
46 [2] - DMA (0=disable, 1, 3)
47 [3] - 0, 10=10MHz clock for 8254
48 1= 1MHz clock for 8254
49 [4] - 0, 5=A/D input -5V.. +5V
50 1, 10=A/D input -10V..+10V
51 [5] - 0, 5=D/A output 0-5V (internal reference -5V)
52 1, 10=D/A output 0-10V (internal reference -10V)
53 2 =D/A output unknown (external reference)
55 Options for PCL-818, PCL-818H:
57 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
58 [2] - DMA (0=disable, 1, 3)
59 [3] - 0, 10=10MHz clock for 8254
60 1= 1MHz clock for 8254
61 [4] - 0, 5=D/A output 0-5V (internal reference -5V)
62 1, 10=D/A output 0-10V (internal reference -10V)
63 2 =D/A output unknown (external reference)
65 Options for PCL-818HD, PCL-818HG:
67 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
68 [2] - DMA/FIFO (-1=use FIFO, 0=disable both FIFO and DMA,
69 1=use DMA ch 1, 3=use DMA ch 3)
70 [3] - 0, 10=10MHz clock for 8254
71 1= 1MHz clock for 8254
72 [4] - 0, 5=D/A output 0-5V (internal reference -5V)
73 1, 10=D/A output 0-10V (internal reference -10V)
74 2 =D/A output unknown (external reference)
78 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
79 [2] - DMA (0=disable, 1, 3)
80 [3] - 0, 10=10MHz clock for 8254
81 1= 1MHz clock for 8254
82 [4] - 0=A/D Range is +/-10V
87 5= user defined bipolar
92 10= user defined unipolar
93 [5] - 0, 5=D/A outputs 0-5V (internal reference -5V)
94 1, 10=D/A outputs 0-10V (internal reference -10V)
95 2=D/A outputs unknown (external reference)
96 [6] - 0, 60=max 60kHz A/D sampling
97 1,100=max 100kHz A/D sampling (PCL-718 with Option 001 installed)
101 #include <linux/module.h>
102 #include <linux/gfp.h>
103 #include <linux/delay.h>
104 #include <linux/io.h>
105 #include <linux/interrupt.h>
108 #include "../comedidev.h"
110 #include "comedi_fc.h"
113 /* #define PCL818_MODE13_AO 1 */
115 /* boards constants */
117 #define boardPCL818L 0
118 #define boardPCL818H 1
119 #define boardPCL818HD 2
120 #define boardPCL818HG 3
121 #define boardPCL818 4
122 #define boardPCL718 5
125 #define PCLx1x_RANGE 16
126 /* IO space len if we use FIFO */
127 #define PCLx1xFIFO_RANGE 32
129 /* W: clear INT request */
130 #define PCL818_CLRINT 8
131 /* R: return status byte */
132 #define PCL818_STATUS 8
133 /* R: A/D high byte W: A/D range control */
134 #define PCL818_RANGE 1
135 /* R: next mux scan channel W: mux scan channel & range control pointer */
137 /* R/W: operation control register */
138 #define PCL818_CONTROL 9
139 /* W: counter enable */
140 #define PCL818_CNTENABLE 10
142 /* R: low byte of A/D W: soft A/D trigger */
143 #define PCL818_AD_LO 0
144 /* R: high byte of A/D W: A/D range control */
145 #define PCL818_AD_HI 1
146 /* W: D/A low&high byte */
147 #define PCL818_DA_LO 4
148 #define PCL818_DA_HI 5
149 /* R: low&high byte of DI */
150 #define PCL818_DI_LO 3
151 #define PCL818_DI_HI 11
152 /* W: low&high byte of DO */
153 #define PCL818_DO_LO 3
154 #define PCL818_DO_HI 11
155 /* W: PCL718 second D/A */
156 #define PCL718_DA2_LO 6
157 #define PCL718_DA2_HI 7
159 #define PCL818_CTR0 12
160 #define PCL818_CTR1 13
161 #define PCL818_CTR2 14
162 /* W: counter control */
163 #define PCL818_CTRCTL 15
165 /* W: fifo enable/disable */
166 #define PCL818_FI_ENABLE 6
167 /* W: fifo interrupt clear */
168 #define PCL818_FI_INTCLR 20
169 /* W: fifo interrupt clear */
170 #define PCL818_FI_FLUSH 25
172 #define PCL818_FI_STATUS 25
173 /* R: one record from FIFO */
174 #define PCL818_FI_DATALO 23
175 #define PCL818_FI_DATAHI 23
177 /* type of interrupt handler */
178 #define INT_TYPE_AI1_INT 1
179 #define INT_TYPE_AI1_DMA 2
180 #define INT_TYPE_AI1_FIFO 3
181 #define INT_TYPE_AI3_INT 4
182 #define INT_TYPE_AI3_DMA 5
183 #define INT_TYPE_AI3_FIFO 6
184 #ifdef PCL818_MODE13_AO
185 #define INT_TYPE_AO1_INT 7
186 #define INT_TYPE_AO3_INT 8
189 #define MAGIC_DMA_WORD 0x5a5a
191 static const struct comedi_lrange range_pcl818h_ai = { 9, {
204 static const struct comedi_lrange range_pcl818hg_ai = { 10, {
220 static const struct comedi_lrange range_pcl818l_l_ai = { 4, {
228 static const struct comedi_lrange range_pcl818l_h_ai = { 4, {
236 static const struct comedi_lrange range718_bipolar1 = { 1, {BIP_RANGE(1),} };
237 static const struct comedi_lrange range718_bipolar0_5 = {
238 1, {BIP_RANGE(0.5),} };
239 static const struct comedi_lrange range718_unipolar2 = { 1, {UNI_RANGE(2),} };
240 static const struct comedi_lrange range718_unipolar1 = { 1, {BIP_RANGE(1),} };
242 struct pcl818_board {
244 const char *name; /* driver name */
245 int n_ranges; /* len of range list */
246 int n_aichan_se; /* num of A/D chans in single ended mode */
247 int n_aichan_diff; /* num of A/D chans in diferencial mode */
248 unsigned int ns_min; /* minimal allowed delay between samples (in ns) */
249 int n_aochan; /* num of D/A chans */
250 int n_dichan; /* num of DI chans */
251 int n_dochan; /* num of DO chans */
252 const struct comedi_lrange *ai_range_type; /* default A/D rangelist */
253 const struct comedi_lrange *ao_range_type; /* default D/A rangelist */
254 unsigned int io_range; /* len of IO space */
255 unsigned int IRQbits; /* allowed interrupts */
256 unsigned int DMAbits; /* allowed DMA chans */
257 int ai_maxdata; /* maxdata for A/D */
258 int ao_maxdata; /* maxdata for D/A */
259 unsigned char fifo; /* 1=board has FIFO */
263 struct pcl818_private {
265 unsigned int dma; /* used DMA, 0=don't use DMA */
266 unsigned int io_range;
267 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
268 unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */
269 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
270 unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */
271 int next_dma_buf; /* which DMA buffer will be used next round */
272 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
273 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
274 unsigned char neverending_ai; /* if=1, then we do neverending record (you must use cancel()) */
275 unsigned int ns_min; /* manimal allowed delay between samples (in us) for actual card */
276 int i8253_osc_base; /* 1/frequency of on board oscilator in ns */
277 int irq_free; /* 1=have allocated IRQ */
278 int irq_blocked; /* 1=IRQ now uses any subdev */
279 int irq_was_now_closed; /* when IRQ finish, there's stored int818_mode for last interrupt */
280 int ai_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
281 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
282 int ai_act_scan; /* how many scans we finished */
283 int ai_act_chan; /* actual position in actual scan */
284 unsigned int act_chanlist[16]; /* MUX setting for actual AI operations */
285 unsigned int act_chanlist_len; /* how long is actual MUX list */
286 unsigned int act_chanlist_pos; /* actual position in MUX list */
287 unsigned int ai_scans; /* len of scanlist */
288 unsigned int ai_n_chan; /* how many channels is measured */
289 unsigned int *ai_chanlist; /* actaul chanlist */
290 unsigned int ai_flags; /* flaglist */
291 unsigned int ai_data_len; /* len of data buffer */
292 unsigned int ai_timer1; /* timers */
293 unsigned int ai_timer2;
294 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
295 unsigned char usefifo; /* 1=use fifo */
296 unsigned int ao_readback[2];
299 static const unsigned int muxonechan[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, /* used for gain list programming */
300 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
304 ==============================================================================
306 static void setup_channel_list(struct comedi_device *dev,
307 struct comedi_subdevice *s,
308 unsigned int *chanlist, unsigned int n_chan,
309 unsigned int seglen);
310 static int check_channel_list(struct comedi_device *dev,
311 struct comedi_subdevice *s,
312 unsigned int *chanlist, unsigned int n_chan);
314 static int pcl818_ai_cancel(struct comedi_device *dev,
315 struct comedi_subdevice *s);
316 static void start_pacer(struct comedi_device *dev, int mode,
317 unsigned int divisor1, unsigned int divisor2);
320 ==============================================================================
321 ANALOG INPUT MODE0, 818 cards, slow version
323 static int pcl818_ai_insn_read(struct comedi_device *dev,
324 struct comedi_subdevice *s,
325 struct comedi_insn *insn, unsigned int *data)
330 /* software trigger, DMA and INT off */
331 outb(0, dev->iobase + PCL818_CONTROL);
334 outb(muxonechan[CR_CHAN(insn->chanspec)], dev->iobase + PCL818_MUX);
337 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL818_RANGE);
339 for (n = 0; n < insn->n; n++) {
341 /* clear INT (conversion end) flag */
342 outb(0, dev->iobase + PCL818_CLRINT);
344 /* start conversion */
345 outb(0, dev->iobase + PCL818_AD_LO);
349 if (inb(dev->iobase + PCL818_STATUS) & 0x10)
353 comedi_error(dev, "A/D insn timeout");
354 /* clear INT (conversion end) flag */
355 outb(0, dev->iobase + PCL818_CLRINT);
359 data[n] = ((inb(dev->iobase + PCL818_AD_HI) << 4) |
360 (inb(dev->iobase + PCL818_AD_LO) >> 4));
367 ==============================================================================
368 ANALOG OUTPUT MODE0, 818 cards
369 only one sample per call is supported
371 static int pcl818_ao_insn_read(struct comedi_device *dev,
372 struct comedi_subdevice *s,
373 struct comedi_insn *insn, unsigned int *data)
375 struct pcl818_private *devpriv = dev->private;
377 int chan = CR_CHAN(insn->chanspec);
379 for (n = 0; n < insn->n; n++)
380 data[n] = devpriv->ao_readback[chan];
385 static int pcl818_ao_insn_write(struct comedi_device *dev,
386 struct comedi_subdevice *s,
387 struct comedi_insn *insn, unsigned int *data)
389 struct pcl818_private *devpriv = dev->private;
391 int chan = CR_CHAN(insn->chanspec);
393 for (n = 0; n < insn->n; n++) {
394 devpriv->ao_readback[chan] = data[n];
395 outb((data[n] & 0x000f) << 4, dev->iobase +
396 (chan ? PCL718_DA2_LO : PCL818_DA_LO));
397 outb((data[n] & 0x0ff0) >> 4, dev->iobase +
398 (chan ? PCL718_DA2_HI : PCL818_DA_HI));
405 ==============================================================================
406 DIGITAL INPUT MODE0, 818 cards
408 only one sample per call is supported
410 static int pcl818_di_insn_bits(struct comedi_device *dev,
411 struct comedi_subdevice *s,
412 struct comedi_insn *insn, unsigned int *data)
414 data[1] = inb(dev->iobase + PCL818_DI_LO) |
415 (inb(dev->iobase + PCL818_DI_HI) << 8);
420 static int pcl818_do_insn_bits(struct comedi_device *dev,
421 struct comedi_subdevice *s,
422 struct comedi_insn *insn,
425 if (comedi_dio_update_state(s, data)) {
426 outb(s->state & 0xff, dev->iobase + PCL818_DO_LO);
427 outb((s->state >> 8), dev->iobase + PCL818_DO_HI);
436 ==============================================================================
437 analog input interrupt mode 1 & 3, 818 cards
438 one sample per interrupt version
440 static irqreturn_t interrupt_pcl818_ai_mode13_int(int irq, void *d)
442 struct comedi_device *dev = d;
443 struct pcl818_private *devpriv = dev->private;
444 struct comedi_subdevice *s = &dev->subdevices[0];
446 int timeout = 50; /* wait max 50us */
449 if (inb(dev->iobase + PCL818_STATUS) & 0x10)
453 outb(0, dev->iobase + PCL818_STATUS); /* clear INT request */
454 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
455 pcl818_ai_cancel(dev, s);
456 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
457 comedi_event(dev, s);
461 low = inb(dev->iobase + PCL818_AD_LO);
462 comedi_buf_put(s->async, ((inb(dev->iobase + PCL818_AD_HI) << 4) | (low >> 4))); /* get one sample */
463 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
465 if ((low & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { /* dropout! */
467 ("comedi: A/D mode1/3 IRQ - channel dropout %x!=%x !\n",
469 devpriv->act_chanlist[devpriv->act_chanlist_pos]);
470 pcl818_ai_cancel(dev, s);
471 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
472 comedi_event(dev, s);
475 devpriv->act_chanlist_pos++;
476 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
477 devpriv->act_chanlist_pos = 0;
479 s->async->cur_chan++;
480 if (s->async->cur_chan >= devpriv->ai_n_chan) {
482 s->async->cur_chan = 0;
483 devpriv->ai_act_scan--;
486 if (!devpriv->neverending_ai) {
487 if (devpriv->ai_act_scan == 0) { /* all data sampled */
488 pcl818_ai_cancel(dev, s);
489 s->async->events |= COMEDI_CB_EOA;
492 comedi_event(dev, s);
497 ==============================================================================
498 analog input dma mode 1 & 3, 818 cards
500 static irqreturn_t interrupt_pcl818_ai_mode13_dma(int irq, void *d)
502 struct comedi_device *dev = d;
503 struct pcl818_private *devpriv = dev->private;
504 struct comedi_subdevice *s = &dev->subdevices[0];
509 disable_dma(devpriv->dma);
510 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
511 if ((devpriv->dma_runs_to_end) > -1 || devpriv->neverending_ai) { /* switch dma bufs */
512 set_dma_mode(devpriv->dma, DMA_MODE_READ);
513 flags = claim_dma_lock();
514 set_dma_addr(devpriv->dma,
515 devpriv->hwdmaptr[devpriv->next_dma_buf]);
516 if (devpriv->dma_runs_to_end || devpriv->neverending_ai) {
517 set_dma_count(devpriv->dma,
518 devpriv->hwdmasize[devpriv->
521 set_dma_count(devpriv->dma, devpriv->last_dma_run);
523 release_dma_lock(flags);
524 enable_dma(devpriv->dma);
526 printk("comedi: A/D mode1/3 IRQ \n");
528 devpriv->dma_runs_to_end--;
529 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
530 ptr = (unsigned short *)devpriv->dmabuf[1 - devpriv->next_dma_buf];
532 len = devpriv->hwdmasize[0] >> 1;
535 for (i = 0; i < len; i++) {
536 if ((ptr[bufptr] & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { /* dropout! */
538 ("comedi: A/D mode1/3 DMA - channel dropout %d(card)!=%d(chanlist) at %d !\n",
540 devpriv->act_chanlist[devpriv->act_chanlist_pos],
541 devpriv->act_chanlist_pos);
542 pcl818_ai_cancel(dev, s);
543 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
544 comedi_event(dev, s);
548 comedi_buf_put(s->async, ptr[bufptr++] >> 4); /* get one sample */
550 devpriv->act_chanlist_pos++;
551 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
552 devpriv->act_chanlist_pos = 0;
554 s->async->cur_chan++;
555 if (s->async->cur_chan >= devpriv->ai_n_chan) {
556 s->async->cur_chan = 0;
557 devpriv->ai_act_scan--;
560 if (!devpriv->neverending_ai)
561 if (devpriv->ai_act_scan == 0) { /* all data sampled */
562 pcl818_ai_cancel(dev, s);
563 s->async->events |= COMEDI_CB_EOA;
564 comedi_event(dev, s);
565 /* printk("done int ai13 dma\n"); */
571 comedi_event(dev, s);
576 ==============================================================================
577 analog input interrupt mode 1 & 3, 818HD/HG cards
579 static irqreturn_t interrupt_pcl818_ai_mode13_fifo(int irq, void *d)
581 struct comedi_device *dev = d;
582 struct pcl818_private *devpriv = dev->private;
583 struct comedi_subdevice *s = &dev->subdevices[0];
587 outb(0, dev->iobase + PCL818_FI_INTCLR); /* clear fifo int request */
589 lo = inb(dev->iobase + PCL818_FI_STATUS);
592 comedi_error(dev, "A/D mode1/3 FIFO overflow!");
593 pcl818_ai_cancel(dev, s);
594 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
595 comedi_event(dev, s);
600 comedi_error(dev, "A/D mode1/3 FIFO interrupt without data!");
601 pcl818_ai_cancel(dev, s);
602 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
603 comedi_event(dev, s);
612 for (i = 0; i < len; i++) {
613 lo = inb(dev->iobase + PCL818_FI_DATALO);
614 if ((lo & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { /* dropout! */
616 ("comedi: A/D mode1/3 FIFO - channel dropout %d!=%d !\n",
618 devpriv->act_chanlist[devpriv->act_chanlist_pos]);
619 pcl818_ai_cancel(dev, s);
620 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
621 comedi_event(dev, s);
625 comedi_buf_put(s->async, (lo >> 4) | (inb(dev->iobase + PCL818_FI_DATAHI) << 4)); /* get one sample */
627 devpriv->act_chanlist_pos++;
628 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
629 devpriv->act_chanlist_pos = 0;
631 s->async->cur_chan++;
632 if (s->async->cur_chan >= devpriv->ai_n_chan) {
633 s->async->cur_chan = 0;
634 devpriv->ai_act_scan--;
637 if (!devpriv->neverending_ai)
638 if (devpriv->ai_act_scan == 0) { /* all data sampled */
639 pcl818_ai_cancel(dev, s);
640 s->async->events |= COMEDI_CB_EOA;
641 comedi_event(dev, s);
647 comedi_event(dev, s);
652 ==============================================================================
655 static irqreturn_t interrupt_pcl818(int irq, void *d)
657 struct comedi_device *dev = d;
658 struct pcl818_private *devpriv = dev->private;
660 if (!dev->attached) {
661 comedi_error(dev, "premature interrupt");
666 if (devpriv->irq_blocked && devpriv->irq_was_now_closed) {
667 if ((devpriv->neverending_ai || (!devpriv->neverending_ai &&
668 devpriv->ai_act_scan > 0)) &&
669 (devpriv->ai_mode == INT_TYPE_AI1_DMA ||
670 devpriv->ai_mode == INT_TYPE_AI3_DMA)) {
671 /* The cleanup from ai_cancel() has been delayed
672 until now because the card doesn't seem to like
673 being reprogrammed while a DMA transfer is in
676 struct comedi_subdevice *s = &dev->subdevices[0];
677 devpriv->ai_act_scan = 0;
678 devpriv->neverending_ai = 0;
679 pcl818_ai_cancel(dev, s);
682 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
687 switch (devpriv->ai_mode) {
688 case INT_TYPE_AI1_DMA:
689 case INT_TYPE_AI3_DMA:
690 return interrupt_pcl818_ai_mode13_dma(irq, d);
691 case INT_TYPE_AI1_INT:
692 case INT_TYPE_AI3_INT:
693 return interrupt_pcl818_ai_mode13_int(irq, d);
694 case INT_TYPE_AI1_FIFO:
695 case INT_TYPE_AI3_FIFO:
696 return interrupt_pcl818_ai_mode13_fifo(irq, d);
697 #ifdef PCL818_MODE13_AO
698 case INT_TYPE_AO1_INT:
699 case INT_TYPE_AO3_INT:
700 return interrupt_pcl818_ao_mode13_int(irq, d);
706 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
708 if ((!dev->irq) || (!devpriv->irq_free) || (!devpriv->irq_blocked)
709 || (!devpriv->ai_mode)) {
710 comedi_error(dev, "bad IRQ!");
714 comedi_error(dev, "IRQ from unknown source!");
719 ==============================================================================
720 ANALOG INPUT MODE 1 or 3 DMA , 818 cards
722 static void pcl818_ai_mode13dma_int(int mode, struct comedi_device *dev,
723 struct comedi_subdevice *s)
725 struct pcl818_private *devpriv = dev->private;
729 printk("mode13dma_int, mode: %d\n", mode);
730 disable_dma(devpriv->dma); /* disable dma */
731 bytes = devpriv->hwdmasize[0];
732 if (!devpriv->neverending_ai) {
733 bytes = devpriv->ai_n_chan * devpriv->ai_scans * sizeof(short); /* how many */
734 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; /* how many DMA pages we must fiil */
735 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; /* on last dma transfer must be moved */
736 devpriv->dma_runs_to_end--;
737 if (devpriv->dma_runs_to_end >= 0)
738 bytes = devpriv->hwdmasize[0];
741 devpriv->next_dma_buf = 0;
742 set_dma_mode(devpriv->dma, DMA_MODE_READ);
743 flags = claim_dma_lock();
744 clear_dma_ff(devpriv->dma);
745 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
746 set_dma_count(devpriv->dma, bytes);
747 release_dma_lock(flags);
748 enable_dma(devpriv->dma);
751 devpriv->ai_mode = INT_TYPE_AI1_DMA;
752 outb(0x87 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Pacer+IRQ+DMA */
754 devpriv->ai_mode = INT_TYPE_AI3_DMA;
755 outb(0x86 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Ext trig+IRQ+DMA */
760 ==============================================================================
761 ANALOG INPUT MODE 1 or 3, 818 cards
763 static int pcl818_ai_cmd_mode(int mode, struct comedi_device *dev,
764 struct comedi_subdevice *s)
766 struct pcl818_private *devpriv = dev->private;
767 struct comedi_cmd *cmd = &s->async->cmd;
768 int divisor1 = 0, divisor2 = 0;
771 dev_dbg(dev->class_dev, "pcl818_ai_cmd_mode()\n");
773 comedi_error(dev, "IRQ not defined!");
777 if (devpriv->irq_blocked)
780 start_pacer(dev, -1, 0, 0); /* stop pacer */
782 seglen = check_channel_list(dev, s, devpriv->ai_chanlist,
786 setup_channel_list(dev, s, devpriv->ai_chanlist,
787 devpriv->ai_n_chan, seglen);
791 devpriv->ai_act_scan = devpriv->ai_scans;
792 devpriv->ai_act_chan = 0;
793 devpriv->irq_blocked = 1;
794 devpriv->irq_was_now_closed = 0;
795 devpriv->neverending_ai = 0;
796 devpriv->act_chanlist_pos = 0;
797 devpriv->dma_runs_to_end = 0;
799 if ((devpriv->ai_scans == 0) || (devpriv->ai_scans == -1))
800 devpriv->neverending_ai = 1; /* well, user want neverending */
803 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base,
804 &divisor1, &divisor2,
807 if (divisor1 == 1) { /* PCL718/818 crash if any divisor is set to 1 */
817 outb(0, dev->iobase + PCL818_CNTENABLE); /* enable pacer */
819 switch (devpriv->dma) {
822 pcl818_ai_mode13dma_int(mode, dev, s);
825 if (!devpriv->usefifo) {
827 /* printk("IRQ\n"); */
829 devpriv->ai_mode = INT_TYPE_AI1_INT;
831 outb(0x83 | (dev->irq << 4),
832 dev->iobase + PCL818_CONTROL);
834 devpriv->ai_mode = INT_TYPE_AI3_INT;
836 outb(0x82 | (dev->irq << 4),
837 dev->iobase + PCL818_CONTROL);
842 outb(1, dev->iobase + PCL818_FI_ENABLE);
844 devpriv->ai_mode = INT_TYPE_AI1_FIFO;
846 outb(0x03, dev->iobase + PCL818_CONTROL);
848 devpriv->ai_mode = INT_TYPE_AI3_FIFO;
849 outb(0x02, dev->iobase + PCL818_CONTROL);
854 start_pacer(dev, mode, divisor1, divisor2);
856 dev_dbg(dev->class_dev, "pcl818_ai_cmd_mode() end\n");
861 ==============================================================================
862 Start/stop pacer onboard pacer
864 static void start_pacer(struct comedi_device *dev, int mode,
865 unsigned int divisor1, unsigned int divisor2)
867 outb(0xb4, dev->iobase + PCL818_CTRCTL);
868 outb(0x74, dev->iobase + PCL818_CTRCTL);
872 outb(divisor2 & 0xff, dev->iobase + PCL818_CTR2);
873 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL818_CTR2);
874 outb(divisor1 & 0xff, dev->iobase + PCL818_CTR1);
875 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL818_CTR1);
880 ==============================================================================
881 Check if channel list from user is builded correctly
882 If it's ok, then program scan/gain logic
884 static int check_channel_list(struct comedi_device *dev,
885 struct comedi_subdevice *s,
886 unsigned int *chanlist, unsigned int n_chan)
888 unsigned int chansegment[16];
889 unsigned int i, nowmustbechan, seglen, segpos;
891 /* correct channel and range number check itself comedi/range.c */
893 comedi_error(dev, "range/channel list is empty!");
898 /* first channel is every time ok */
899 chansegment[0] = chanlist[0];
900 /* build part of chanlist */
901 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
903 /* printk("%d. %d * %d\n",i,
904 * CR_CHAN(it->chanlist[i]),CR_RANGE(it->chanlist[i]));*/
906 /* we detect loop, this must by finish */
908 if (chanlist[0] == chanlist[i])
911 (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
912 if (nowmustbechan != CR_CHAN(chanlist[i])) { /* channel list isn't continuous :-( */
914 ("comedi%d: pcl818: channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
915 dev->minor, i, CR_CHAN(chanlist[i]),
916 nowmustbechan, CR_CHAN(chanlist[0]));
919 /* well, this is next correct channel in list */
920 chansegment[i] = chanlist[i];
923 /* check whole chanlist */
924 for (i = 0, segpos = 0; i < n_chan; i++) {
925 /* printk("%d %d=%d %d\n",CR_CHAN(chansegment[i%seglen]),CR_RANGE(chansegment[i%seglen]),CR_CHAN(it->chanlist[i]),CR_RANGE(it->chanlist[i])); */
926 if (chanlist[i] != chansegment[i % seglen]) {
928 ("comedi%d: pcl818: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
929 dev->minor, i, CR_CHAN(chansegment[i]),
930 CR_RANGE(chansegment[i]),
931 CR_AREF(chansegment[i]),
932 CR_CHAN(chanlist[i % seglen]),
933 CR_RANGE(chanlist[i % seglen]),
934 CR_AREF(chansegment[i % seglen]));
935 return 0; /* chan/gain list is strange */
941 printk("check_channel_list: seglen %d\n", seglen);
945 static void setup_channel_list(struct comedi_device *dev,
946 struct comedi_subdevice *s,
947 unsigned int *chanlist, unsigned int n_chan,
950 struct pcl818_private *devpriv = dev->private;
953 devpriv->act_chanlist_len = seglen;
954 devpriv->act_chanlist_pos = 0;
956 for (i = 0; i < seglen; i++) { /* store range list to card */
957 devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
958 outb(muxonechan[CR_CHAN(chanlist[i])], dev->iobase + PCL818_MUX); /* select channel */
959 outb(CR_RANGE(chanlist[i]), dev->iobase + PCL818_RANGE); /* select gain */
964 /* select channel interval to scan */
965 outb(devpriv->act_chanlist[0] | (devpriv->act_chanlist[seglen -
967 dev->iobase + PCL818_MUX);
971 ==============================================================================
972 Check if board is switched to SE (1) or DIFF(0) mode
974 static int check_single_ended(unsigned int port)
976 if (inb(port + PCL818_STATUS) & 0x20)
982 ==============================================================================
984 static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
985 struct comedi_cmd *cmd)
987 const struct pcl818_board *board = comedi_board(dev);
988 struct pcl818_private *devpriv = dev->private;
990 int tmp, divisor1 = 0, divisor2 = 0;
992 /* Step 1 : check if triggers are trivially valid */
994 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
995 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
996 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
997 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
998 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
1003 /* Step 2a : make sure trigger sources are unique */
1005 err |= cfc_check_trigger_is_unique(cmd->convert_src);
1006 err |= cfc_check_trigger_is_unique(cmd->stop_src);
1008 /* Step 2b : and mutually compatible */
1013 /* Step 3: check if arguments are trivially valid */
1015 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
1016 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
1018 if (cmd->convert_src == TRIG_TIMER)
1019 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
1022 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
1024 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
1026 if (cmd->stop_src == TRIG_COUNT)
1027 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
1028 else /* TRIG_NONE */
1029 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
1034 /* step 4: fix up any arguments */
1036 if (cmd->convert_src == TRIG_TIMER) {
1037 tmp = cmd->convert_arg;
1038 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base,
1039 &divisor1, &divisor2,
1040 &cmd->convert_arg, cmd->flags);
1041 if (cmd->convert_arg < board->ns_min)
1042 cmd->convert_arg = board->ns_min;
1043 if (tmp != cmd->convert_arg)
1050 /* step 5: complain about special chanlist considerations */
1052 if (cmd->chanlist) {
1053 if (!check_channel_list(dev, s, cmd->chanlist,
1055 return 5; /* incorrect channels list */
1062 ==============================================================================
1064 static int ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
1066 struct pcl818_private *devpriv = dev->private;
1067 struct comedi_cmd *cmd = &s->async->cmd;
1070 dev_dbg(dev->class_dev, "pcl818_ai_cmd()\n");
1071 devpriv->ai_n_chan = cmd->chanlist_len;
1072 devpriv->ai_chanlist = cmd->chanlist;
1073 devpriv->ai_flags = cmd->flags;
1074 devpriv->ai_data_len = s->async->prealloc_bufsz;
1075 devpriv->ai_timer1 = 0;
1076 devpriv->ai_timer2 = 0;
1078 if (cmd->stop_src == TRIG_COUNT)
1079 devpriv->ai_scans = cmd->stop_arg;
1081 devpriv->ai_scans = 0;
1083 if (cmd->scan_begin_src == TRIG_FOLLOW) { /* mode 1, 3 */
1084 if (cmd->convert_src == TRIG_TIMER) { /* mode 1 */
1085 devpriv->ai_timer1 = cmd->convert_arg;
1086 retval = pcl818_ai_cmd_mode(1, dev, s);
1087 dev_dbg(dev->class_dev, "pcl818_ai_cmd() end\n");
1090 if (cmd->convert_src == TRIG_EXT) { /* mode 3 */
1091 return pcl818_ai_cmd_mode(3, dev, s);
1099 ==============================================================================
1100 cancel any mode 1-4 AI
1102 static int pcl818_ai_cancel(struct comedi_device *dev,
1103 struct comedi_subdevice *s)
1105 struct pcl818_private *devpriv = dev->private;
1107 if (devpriv->irq_blocked > 0) {
1108 dev_dbg(dev->class_dev, "pcl818_ai_cancel()\n");
1109 devpriv->irq_was_now_closed = 1;
1111 switch (devpriv->ai_mode) {
1112 case INT_TYPE_AI1_DMA:
1113 case INT_TYPE_AI3_DMA:
1114 if (devpriv->neverending_ai ||
1115 (!devpriv->neverending_ai &&
1116 devpriv->ai_act_scan > 0)) {
1117 /* wait for running dma transfer to end, do cleanup in interrupt */
1120 disable_dma(devpriv->dma);
1121 case INT_TYPE_AI1_INT:
1122 case INT_TYPE_AI3_INT:
1123 case INT_TYPE_AI1_FIFO:
1124 case INT_TYPE_AI3_FIFO:
1125 #ifdef PCL818_MODE13_AO
1126 case INT_TYPE_AO1_INT:
1127 case INT_TYPE_AO3_INT:
1129 outb(inb(dev->iobase + PCL818_CONTROL) & 0x73, dev->iobase + PCL818_CONTROL); /* Stop A/D */
1131 start_pacer(dev, -1, 0, 0);
1132 outb(0, dev->iobase + PCL818_AD_LO);
1133 inb(dev->iobase + PCL818_AD_LO);
1134 inb(dev->iobase + PCL818_AD_HI);
1135 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
1136 outb(0, dev->iobase + PCL818_CONTROL); /* Stop A/D */
1137 if (devpriv->usefifo) { /* FIFO shutdown */
1138 outb(0, dev->iobase + PCL818_FI_INTCLR);
1139 outb(0, dev->iobase + PCL818_FI_FLUSH);
1140 outb(0, dev->iobase + PCL818_FI_ENABLE);
1142 devpriv->irq_blocked = 0;
1143 devpriv->last_int_sub = s;
1144 devpriv->neverending_ai = 0;
1145 devpriv->ai_mode = 0;
1146 devpriv->irq_was_now_closed = 0;
1152 dev_dbg(dev->class_dev, "pcl818_ai_cancel() end\n");
1157 ==============================================================================
1160 static int pcl818_check(unsigned long iobase)
1162 outb(0x00, iobase + PCL818_MUX);
1164 if (inb(iobase + PCL818_MUX) != 0x00)
1165 return 1; /* there isn't card */
1166 outb(0x55, iobase + PCL818_MUX);
1168 if (inb(iobase + PCL818_MUX) != 0x55)
1169 return 1; /* there isn't card */
1170 outb(0x00, iobase + PCL818_MUX);
1172 outb(0x18, iobase + PCL818_CONTROL);
1174 if (inb(iobase + PCL818_CONTROL) != 0x18)
1175 return 1; /* there isn't card */
1176 return 0; /* ok, card exist */
1180 ==============================================================================
1181 reset whole PCL-818 cards
1183 static void pcl818_reset(struct comedi_device *dev)
1185 const struct pcl818_board *board = comedi_board(dev);
1186 struct pcl818_private *devpriv = dev->private;
1188 if (devpriv->usefifo) { /* FIFO shutdown */
1189 outb(0, dev->iobase + PCL818_FI_INTCLR);
1190 outb(0, dev->iobase + PCL818_FI_FLUSH);
1191 outb(0, dev->iobase + PCL818_FI_ENABLE);
1193 outb(0, dev->iobase + PCL818_DA_LO); /* DAC=0V */
1194 outb(0, dev->iobase + PCL818_DA_HI);
1196 outb(0, dev->iobase + PCL818_DO_HI); /* DO=$0000 */
1197 outb(0, dev->iobase + PCL818_DO_LO);
1199 outb(0, dev->iobase + PCL818_CONTROL);
1200 outb(0, dev->iobase + PCL818_CNTENABLE);
1201 outb(0, dev->iobase + PCL818_MUX);
1202 outb(0, dev->iobase + PCL818_CLRINT);
1203 outb(0xb0, dev->iobase + PCL818_CTRCTL); /* Stop pacer */
1204 outb(0x70, dev->iobase + PCL818_CTRCTL);
1205 outb(0x30, dev->iobase + PCL818_CTRCTL);
1206 if (board->is_818) {
1207 outb(0, dev->iobase + PCL818_RANGE);
1209 outb(0, dev->iobase + PCL718_DA2_LO);
1210 outb(0, dev->iobase + PCL718_DA2_HI);
1214 static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1216 const struct pcl818_board *board = comedi_board(dev);
1217 struct pcl818_private *devpriv;
1221 unsigned long pages;
1222 struct comedi_subdevice *s;
1224 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1228 devpriv->io_range = board->io_range;
1229 if ((board->fifo) && (it->options[2] == -1)) {
1230 /* we've board with FIFO and we want to use FIFO */
1231 devpriv->io_range = PCLx1xFIFO_RANGE;
1232 devpriv->usefifo = 1;
1234 ret = comedi_request_region(dev, it->options[0], devpriv->io_range);
1238 if (pcl818_check(dev->iobase)) {
1239 comedi_error(dev, "I can't detect board. FAIL!\n");
1245 if (board->IRQbits != 0) { /* board support IRQ */
1246 irq = it->options[1];
1247 if (irq) { /* we want to use IRQ */
1248 if (((1 << irq) & board->IRQbits) == 0) {
1250 (", IRQ %u is out of allowed range, DISABLING IT",
1252 irq = 0; /* Bad IRQ */
1254 if (request_irq(irq, interrupt_pcl818, 0,
1255 dev->board_name, dev)) {
1257 (", unable to allocate IRQ %u, DISABLING IT",
1259 irq = 0; /* Can't use IRQ */
1261 printk(KERN_DEBUG "irq=%u", irq);
1269 devpriv->irq_free = 1; /* 1=we have allocated irq */
1271 devpriv->irq_free = 0;
1273 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1274 devpriv->ai_mode = 0; /* mode of irq */
1279 if (!devpriv->irq_free)
1280 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1281 if (board->DMAbits != 0) { /* board support DMA */
1282 dma = it->options[2];
1284 goto no_dma; /* DMA disabled */
1285 if (((1 << dma) & board->DMAbits) == 0) {
1286 printk(KERN_ERR "DMA is out of allowed range, FAIL!\n");
1287 return -EINVAL; /* Bad DMA */
1289 ret = request_dma(dma, dev->board_name);
1291 return -EBUSY; /* DMA isn't free */
1293 pages = 2; /* we need 16KB */
1294 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1295 if (!devpriv->dmabuf[0])
1296 /* maybe experiment with try_to_free_pages() will help .... */
1297 return -EBUSY; /* no buffer :-( */
1298 devpriv->dmapages[0] = pages;
1299 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1300 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
1301 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
1302 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1303 if (!devpriv->dmabuf[1])
1305 devpriv->dmapages[1] = pages;
1306 devpriv->hwdmaptr[1] = virt_to_bus((void *)devpriv->dmabuf[1]);
1307 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1312 ret = comedi_alloc_subdevices(dev, 4);
1316 s = &dev->subdevices[0];
1317 if (!board->n_aichan_se) {
1318 s->type = COMEDI_SUBD_UNUSED;
1320 s->type = COMEDI_SUBD_AI;
1321 devpriv->sub_ai = s;
1322 s->subdev_flags = SDF_READABLE;
1323 if (check_single_ended(dev->iobase)) {
1324 s->n_chan = board->n_aichan_se;
1325 s->subdev_flags |= SDF_COMMON | SDF_GROUND;
1326 printk(", %dchans S.E. DAC", s->n_chan);
1328 s->n_chan = board->n_aichan_diff;
1329 s->subdev_flags |= SDF_DIFF;
1330 printk(", %dchans DIFF DAC", s->n_chan);
1332 s->maxdata = board->ai_maxdata;
1333 s->len_chanlist = s->n_chan;
1334 s->range_table = board->ai_range_type;
1335 s->cancel = pcl818_ai_cancel;
1336 s->insn_read = pcl818_ai_insn_read;
1338 dev->read_subdev = s;
1339 s->subdev_flags |= SDF_CMD_READ;
1340 s->do_cmdtest = ai_cmdtest;
1343 if (board->is_818) {
1344 if ((it->options[4] == 1) || (it->options[4] == 10))
1345 s->range_table = &range_pcl818l_h_ai; /* secondary range list jumper selectable */
1347 switch (it->options[4]) {
1349 s->range_table = &range_bipolar10;
1352 s->range_table = &range_bipolar5;
1355 s->range_table = &range_bipolar2_5;
1358 s->range_table = &range718_bipolar1;
1361 s->range_table = &range718_bipolar0_5;
1364 s->range_table = &range_unipolar10;
1367 s->range_table = &range_unipolar5;
1370 s->range_table = &range718_unipolar2;
1373 s->range_table = &range718_unipolar1;
1376 s->range_table = &range_unknown;
1382 s = &dev->subdevices[1];
1383 if (!board->n_aochan) {
1384 s->type = COMEDI_SUBD_UNUSED;
1386 s->type = COMEDI_SUBD_AO;
1387 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1388 s->n_chan = board->n_aochan;
1389 s->maxdata = board->ao_maxdata;
1390 s->len_chanlist = board->n_aochan;
1391 s->range_table = board->ao_range_type;
1392 s->insn_read = pcl818_ao_insn_read;
1393 s->insn_write = pcl818_ao_insn_write;
1394 if (board->is_818) {
1395 if ((it->options[4] == 1) || (it->options[4] == 10))
1396 s->range_table = &range_unipolar10;
1397 if (it->options[4] == 2)
1398 s->range_table = &range_unknown;
1400 if ((it->options[5] == 1) || (it->options[5] == 10))
1401 s->range_table = &range_unipolar10;
1402 if (it->options[5] == 2)
1403 s->range_table = &range_unknown;
1407 s = &dev->subdevices[2];
1408 if (!board->n_dichan) {
1409 s->type = COMEDI_SUBD_UNUSED;
1411 s->type = COMEDI_SUBD_DI;
1412 s->subdev_flags = SDF_READABLE;
1413 s->n_chan = board->n_dichan;
1415 s->len_chanlist = board->n_dichan;
1416 s->range_table = &range_digital;
1417 s->insn_bits = pcl818_di_insn_bits;
1420 s = &dev->subdevices[3];
1421 if (!board->n_dochan) {
1422 s->type = COMEDI_SUBD_UNUSED;
1424 s->type = COMEDI_SUBD_DO;
1425 s->subdev_flags = SDF_WRITABLE;
1426 s->n_chan = board->n_dochan;
1428 s->len_chanlist = board->n_dochan;
1429 s->range_table = &range_digital;
1430 s->insn_bits = pcl818_do_insn_bits;
1433 /* select 1/10MHz oscilator */
1434 if ((it->options[3] == 0) || (it->options[3] == 10))
1435 devpriv->i8253_osc_base = I8254_OSC_BASE_10MHZ;
1437 devpriv->i8253_osc_base = I8254_OSC_BASE_1MHZ;
1439 /* max sampling speed */
1440 devpriv->ns_min = board->ns_min;
1442 if (!board->is_818) {
1443 if ((it->options[6] == 1) || (it->options[6] == 100))
1444 devpriv->ns_min = 10000; /* extended PCL718 to 100kHz DAC */
1454 static void pcl818_detach(struct comedi_device *dev)
1456 struct pcl818_private *devpriv = dev->private;
1459 pcl818_ai_cancel(dev, devpriv->sub_ai);
1462 free_dma(devpriv->dma);
1463 if (devpriv->dmabuf[0])
1464 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1465 if (devpriv->dmabuf[1])
1466 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1468 comedi_legacy_detach(dev);
1471 static const struct pcl818_board boardtypes[] = {
1472 {"pcl818l", 4, 16, 8, 25000, 1, 16, 16, &range_pcl818l_l_ai,
1473 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1474 0x0a, 0xfff, 0xfff, 0, 1},
1475 {"pcl818h", 9, 16, 8, 10000, 1, 16, 16, &range_pcl818h_ai,
1476 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1477 0x0a, 0xfff, 0xfff, 0, 1},
1478 {"pcl818hd", 9, 16, 8, 10000, 1, 16, 16, &range_pcl818h_ai,
1479 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1480 0x0a, 0xfff, 0xfff, 1, 1},
1481 {"pcl818hg", 12, 16, 8, 10000, 1, 16, 16, &range_pcl818hg_ai,
1482 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1483 0x0a, 0xfff, 0xfff, 1, 1},
1484 {"pcl818", 9, 16, 8, 10000, 2, 16, 16, &range_pcl818h_ai,
1485 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1486 0x0a, 0xfff, 0xfff, 0, 1},
1487 {"pcl718", 1, 16, 8, 16000, 2, 16, 16, &range_unipolar5,
1488 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1489 0x0a, 0xfff, 0xfff, 0, 0},
1491 {"pcm3718", 9, 16, 8, 10000, 0, 16, 16, &range_pcl818h_ai,
1492 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1493 0x0a, 0xfff, 0xfff, 0, 1 /* XXX ? */ },
1496 static struct comedi_driver pcl818_driver = {
1497 .driver_name = "pcl818",
1498 .module = THIS_MODULE,
1499 .attach = pcl818_attach,
1500 .detach = pcl818_detach,
1501 .board_name = &boardtypes[0].name,
1502 .num_names = ARRAY_SIZE(boardtypes),
1503 .offset = sizeof(struct pcl818_board),
1505 module_comedi_driver(pcl818_driver);
1507 MODULE_AUTHOR("Comedi http://www.comedi.org");
1508 MODULE_DESCRIPTION("Comedi low-level driver");
1509 MODULE_LICENSE("GPL");