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/ioport.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 short *ai_data; /* data buffer */
293 unsigned int ai_timer1; /* timers */
294 unsigned int ai_timer2;
295 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
296 unsigned char usefifo; /* 1=use fifo */
297 unsigned int ao_readback[2];
300 static const unsigned int muxonechan[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, /* used for gain list programming */
301 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
305 ==============================================================================
307 static void setup_channel_list(struct comedi_device *dev,
308 struct comedi_subdevice *s,
309 unsigned int *chanlist, unsigned int n_chan,
310 unsigned int seglen);
311 static int check_channel_list(struct comedi_device *dev,
312 struct comedi_subdevice *s,
313 unsigned int *chanlist, unsigned int n_chan);
315 static int pcl818_ai_cancel(struct comedi_device *dev,
316 struct comedi_subdevice *s);
317 static void start_pacer(struct comedi_device *dev, int mode,
318 unsigned int divisor1, unsigned int divisor2);
321 ==============================================================================
322 ANALOG INPUT MODE0, 818 cards, slow version
324 static int pcl818_ai_insn_read(struct comedi_device *dev,
325 struct comedi_subdevice *s,
326 struct comedi_insn *insn, unsigned int *data)
331 /* software trigger, DMA and INT off */
332 outb(0, dev->iobase + PCL818_CONTROL);
335 outb(muxonechan[CR_CHAN(insn->chanspec)], dev->iobase + PCL818_MUX);
338 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL818_RANGE);
340 for (n = 0; n < insn->n; n++) {
342 /* clear INT (conversion end) flag */
343 outb(0, dev->iobase + PCL818_CLRINT);
345 /* start conversion */
346 outb(0, dev->iobase + PCL818_AD_LO);
350 if (inb(dev->iobase + PCL818_STATUS) & 0x10)
354 comedi_error(dev, "A/D insn timeout");
355 /* clear INT (conversion end) flag */
356 outb(0, dev->iobase + PCL818_CLRINT);
360 data[n] = ((inb(dev->iobase + PCL818_AD_HI) << 4) |
361 (inb(dev->iobase + PCL818_AD_LO) >> 4));
368 ==============================================================================
369 ANALOG OUTPUT MODE0, 818 cards
370 only one sample per call is supported
372 static int pcl818_ao_insn_read(struct comedi_device *dev,
373 struct comedi_subdevice *s,
374 struct comedi_insn *insn, unsigned int *data)
376 struct pcl818_private *devpriv = dev->private;
378 int chan = CR_CHAN(insn->chanspec);
380 for (n = 0; n < insn->n; n++)
381 data[n] = devpriv->ao_readback[chan];
386 static int pcl818_ao_insn_write(struct comedi_device *dev,
387 struct comedi_subdevice *s,
388 struct comedi_insn *insn, unsigned int *data)
390 struct pcl818_private *devpriv = dev->private;
392 int chan = CR_CHAN(insn->chanspec);
394 for (n = 0; n < insn->n; n++) {
395 devpriv->ao_readback[chan] = data[n];
396 outb((data[n] & 0x000f) << 4, dev->iobase +
397 (chan ? PCL718_DA2_LO : PCL818_DA_LO));
398 outb((data[n] & 0x0ff0) >> 4, dev->iobase +
399 (chan ? PCL718_DA2_HI : PCL818_DA_HI));
406 ==============================================================================
407 DIGITAL INPUT MODE0, 818 cards
409 only one sample per call is supported
411 static int pcl818_di_insn_bits(struct comedi_device *dev,
412 struct comedi_subdevice *s,
413 struct comedi_insn *insn, unsigned int *data)
415 data[1] = inb(dev->iobase + PCL818_DI_LO) |
416 (inb(dev->iobase + PCL818_DI_HI) << 8);
422 ==============================================================================
423 DIGITAL OUTPUT MODE0, 818 cards
425 only one sample per call is supported
427 static int pcl818_do_insn_bits(struct comedi_device *dev,
428 struct comedi_subdevice *s,
429 struct comedi_insn *insn, unsigned int *data)
431 s->state &= ~data[0];
432 s->state |= (data[0] & data[1]);
434 outb(s->state & 0xff, dev->iobase + PCL818_DO_LO);
435 outb((s->state >> 8), dev->iobase + PCL818_DO_HI);
443 ==============================================================================
444 analog input interrupt mode 1 & 3, 818 cards
445 one sample per interrupt version
447 static irqreturn_t interrupt_pcl818_ai_mode13_int(int irq, void *d)
449 struct comedi_device *dev = d;
450 struct pcl818_private *devpriv = dev->private;
451 struct comedi_subdevice *s = &dev->subdevices[0];
453 int timeout = 50; /* wait max 50us */
456 if (inb(dev->iobase + PCL818_STATUS) & 0x10)
460 outb(0, dev->iobase + PCL818_STATUS); /* clear INT request */
461 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
462 pcl818_ai_cancel(dev, s);
463 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
464 comedi_event(dev, s);
468 low = inb(dev->iobase + PCL818_AD_LO);
469 comedi_buf_put(s->async, ((inb(dev->iobase + PCL818_AD_HI) << 4) | (low >> 4))); /* get one sample */
470 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
472 if ((low & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { /* dropout! */
474 ("comedi: A/D mode1/3 IRQ - channel dropout %x!=%x !\n",
476 devpriv->act_chanlist[devpriv->act_chanlist_pos]);
477 pcl818_ai_cancel(dev, s);
478 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
479 comedi_event(dev, s);
482 devpriv->act_chanlist_pos++;
483 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
484 devpriv->act_chanlist_pos = 0;
486 s->async->cur_chan++;
487 if (s->async->cur_chan >= devpriv->ai_n_chan) {
489 s->async->cur_chan = 0;
490 devpriv->ai_act_scan--;
493 if (!devpriv->neverending_ai) {
494 if (devpriv->ai_act_scan == 0) { /* all data sampled */
495 pcl818_ai_cancel(dev, s);
496 s->async->events |= COMEDI_CB_EOA;
499 comedi_event(dev, s);
504 ==============================================================================
505 analog input dma mode 1 & 3, 818 cards
507 static irqreturn_t interrupt_pcl818_ai_mode13_dma(int irq, void *d)
509 struct comedi_device *dev = d;
510 struct pcl818_private *devpriv = dev->private;
511 struct comedi_subdevice *s = &dev->subdevices[0];
516 disable_dma(devpriv->dma);
517 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
518 if ((devpriv->dma_runs_to_end) > -1 || devpriv->neverending_ai) { /* switch dma bufs */
519 set_dma_mode(devpriv->dma, DMA_MODE_READ);
520 flags = claim_dma_lock();
521 set_dma_addr(devpriv->dma,
522 devpriv->hwdmaptr[devpriv->next_dma_buf]);
523 if (devpriv->dma_runs_to_end || devpriv->neverending_ai) {
524 set_dma_count(devpriv->dma,
525 devpriv->hwdmasize[devpriv->
528 set_dma_count(devpriv->dma, devpriv->last_dma_run);
530 release_dma_lock(flags);
531 enable_dma(devpriv->dma);
533 printk("comedi: A/D mode1/3 IRQ \n");
535 devpriv->dma_runs_to_end--;
536 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
537 ptr = (short *)devpriv->dmabuf[1 - devpriv->next_dma_buf];
539 len = devpriv->hwdmasize[0] >> 1;
542 for (i = 0; i < len; i++) {
543 if ((ptr[bufptr] & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { /* dropout! */
545 ("comedi: A/D mode1/3 DMA - channel dropout %d(card)!=%d(chanlist) at %d !\n",
547 devpriv->act_chanlist[devpriv->act_chanlist_pos],
548 devpriv->act_chanlist_pos);
549 pcl818_ai_cancel(dev, s);
550 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
551 comedi_event(dev, s);
555 comedi_buf_put(s->async, ptr[bufptr++] >> 4); /* get one sample */
557 devpriv->act_chanlist_pos++;
558 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
559 devpriv->act_chanlist_pos = 0;
561 s->async->cur_chan++;
562 if (s->async->cur_chan >= devpriv->ai_n_chan) {
563 s->async->cur_chan = 0;
564 devpriv->ai_act_scan--;
567 if (!devpriv->neverending_ai)
568 if (devpriv->ai_act_scan == 0) { /* all data sampled */
569 pcl818_ai_cancel(dev, s);
570 s->async->events |= COMEDI_CB_EOA;
571 comedi_event(dev, s);
572 /* printk("done int ai13 dma\n"); */
578 comedi_event(dev, s);
583 ==============================================================================
584 analog input interrupt mode 1 & 3, 818HD/HG cards
586 static irqreturn_t interrupt_pcl818_ai_mode13_fifo(int irq, void *d)
588 struct comedi_device *dev = d;
589 struct pcl818_private *devpriv = dev->private;
590 struct comedi_subdevice *s = &dev->subdevices[0];
593 outb(0, dev->iobase + PCL818_FI_INTCLR); /* clear fifo int request */
595 lo = inb(dev->iobase + PCL818_FI_STATUS);
598 comedi_error(dev, "A/D mode1/3 FIFO overflow!");
599 pcl818_ai_cancel(dev, s);
600 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
601 comedi_event(dev, s);
606 comedi_error(dev, "A/D mode1/3 FIFO interrupt without data!");
607 pcl818_ai_cancel(dev, s);
608 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
609 comedi_event(dev, s);
618 for (i = 0; i < len; i++) {
619 lo = inb(dev->iobase + PCL818_FI_DATALO);
620 if ((lo & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { /* dropout! */
622 ("comedi: A/D mode1/3 FIFO - channel dropout %d!=%d !\n",
624 devpriv->act_chanlist[devpriv->act_chanlist_pos]);
625 pcl818_ai_cancel(dev, s);
626 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
627 comedi_event(dev, s);
631 comedi_buf_put(s->async, (lo >> 4) | (inb(dev->iobase + PCL818_FI_DATAHI) << 4)); /* get one sample */
633 devpriv->act_chanlist_pos++;
634 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
635 devpriv->act_chanlist_pos = 0;
637 s->async->cur_chan++;
638 if (s->async->cur_chan >= devpriv->ai_n_chan) {
639 s->async->cur_chan = 0;
640 devpriv->ai_act_scan--;
643 if (!devpriv->neverending_ai)
644 if (devpriv->ai_act_scan == 0) { /* all data sampled */
645 pcl818_ai_cancel(dev, s);
646 s->async->events |= COMEDI_CB_EOA;
647 comedi_event(dev, s);
653 comedi_event(dev, s);
658 ==============================================================================
661 static irqreturn_t interrupt_pcl818(int irq, void *d)
663 struct comedi_device *dev = d;
664 struct pcl818_private *devpriv = dev->private;
666 if (!dev->attached) {
667 comedi_error(dev, "premature interrupt");
672 if (devpriv->irq_blocked && devpriv->irq_was_now_closed) {
673 if ((devpriv->neverending_ai || (!devpriv->neverending_ai &&
674 devpriv->ai_act_scan > 0)) &&
675 (devpriv->ai_mode == INT_TYPE_AI1_DMA ||
676 devpriv->ai_mode == INT_TYPE_AI3_DMA)) {
677 /* The cleanup from ai_cancel() has been delayed
678 until now because the card doesn't seem to like
679 being reprogrammed while a DMA transfer is in
682 struct comedi_subdevice *s = &dev->subdevices[0];
683 devpriv->ai_act_scan = 0;
684 devpriv->neverending_ai = 0;
685 pcl818_ai_cancel(dev, s);
688 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
693 switch (devpriv->ai_mode) {
694 case INT_TYPE_AI1_DMA:
695 case INT_TYPE_AI3_DMA:
696 return interrupt_pcl818_ai_mode13_dma(irq, d);
697 case INT_TYPE_AI1_INT:
698 case INT_TYPE_AI3_INT:
699 return interrupt_pcl818_ai_mode13_int(irq, d);
700 case INT_TYPE_AI1_FIFO:
701 case INT_TYPE_AI3_FIFO:
702 return interrupt_pcl818_ai_mode13_fifo(irq, d);
703 #ifdef PCL818_MODE13_AO
704 case INT_TYPE_AO1_INT:
705 case INT_TYPE_AO3_INT:
706 return interrupt_pcl818_ao_mode13_int(irq, d);
712 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
714 if ((!dev->irq) || (!devpriv->irq_free) || (!devpriv->irq_blocked)
715 || (!devpriv->ai_mode)) {
716 comedi_error(dev, "bad IRQ!");
720 comedi_error(dev, "IRQ from unknown source!");
725 ==============================================================================
726 ANALOG INPUT MODE 1 or 3 DMA , 818 cards
728 static void pcl818_ai_mode13dma_int(int mode, struct comedi_device *dev,
729 struct comedi_subdevice *s)
731 struct pcl818_private *devpriv = dev->private;
735 printk("mode13dma_int, mode: %d\n", mode);
736 disable_dma(devpriv->dma); /* disable dma */
737 bytes = devpriv->hwdmasize[0];
738 if (!devpriv->neverending_ai) {
739 bytes = devpriv->ai_n_chan * devpriv->ai_scans * sizeof(short); /* how many */
740 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; /* how many DMA pages we must fiil */
741 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; /* on last dma transfer must be moved */
742 devpriv->dma_runs_to_end--;
743 if (devpriv->dma_runs_to_end >= 0)
744 bytes = devpriv->hwdmasize[0];
747 devpriv->next_dma_buf = 0;
748 set_dma_mode(devpriv->dma, DMA_MODE_READ);
749 flags = claim_dma_lock();
750 clear_dma_ff(devpriv->dma);
751 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
752 set_dma_count(devpriv->dma, bytes);
753 release_dma_lock(flags);
754 enable_dma(devpriv->dma);
757 devpriv->ai_mode = INT_TYPE_AI1_DMA;
758 outb(0x87 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Pacer+IRQ+DMA */
760 devpriv->ai_mode = INT_TYPE_AI3_DMA;
761 outb(0x86 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Ext trig+IRQ+DMA */
766 ==============================================================================
767 ANALOG INPUT MODE 1 or 3, 818 cards
769 static int pcl818_ai_cmd_mode(int mode, struct comedi_device *dev,
770 struct comedi_subdevice *s)
772 struct pcl818_private *devpriv = dev->private;
773 struct comedi_cmd *cmd = &s->async->cmd;
774 int divisor1 = 0, divisor2 = 0;
777 dev_dbg(dev->class_dev, "pcl818_ai_cmd_mode()\n");
779 comedi_error(dev, "IRQ not defined!");
783 if (devpriv->irq_blocked)
786 start_pacer(dev, -1, 0, 0); /* stop pacer */
788 seglen = check_channel_list(dev, s, devpriv->ai_chanlist,
792 setup_channel_list(dev, s, devpriv->ai_chanlist,
793 devpriv->ai_n_chan, seglen);
797 devpriv->ai_act_scan = devpriv->ai_scans;
798 devpriv->ai_act_chan = 0;
799 devpriv->irq_blocked = 1;
800 devpriv->irq_was_now_closed = 0;
801 devpriv->neverending_ai = 0;
802 devpriv->act_chanlist_pos = 0;
803 devpriv->dma_runs_to_end = 0;
805 if ((devpriv->ai_scans == 0) || (devpriv->ai_scans == -1))
806 devpriv->neverending_ai = 1; /* well, user want neverending */
809 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base, &divisor1,
810 &divisor2, &cmd->convert_arg,
812 if (divisor1 == 1) { /* PCL718/818 crash if any divisor is set to 1 */
822 outb(0, dev->iobase + PCL818_CNTENABLE); /* enable pacer */
824 switch (devpriv->dma) {
827 pcl818_ai_mode13dma_int(mode, dev, s);
830 if (!devpriv->usefifo) {
832 /* printk("IRQ\n"); */
834 devpriv->ai_mode = INT_TYPE_AI1_INT;
836 outb(0x83 | (dev->irq << 4),
837 dev->iobase + PCL818_CONTROL);
839 devpriv->ai_mode = INT_TYPE_AI3_INT;
841 outb(0x82 | (dev->irq << 4),
842 dev->iobase + PCL818_CONTROL);
847 outb(1, dev->iobase + PCL818_FI_ENABLE);
849 devpriv->ai_mode = INT_TYPE_AI1_FIFO;
851 outb(0x03, dev->iobase + PCL818_CONTROL);
853 devpriv->ai_mode = INT_TYPE_AI3_FIFO;
854 outb(0x02, dev->iobase + PCL818_CONTROL);
859 start_pacer(dev, mode, divisor1, divisor2);
861 dev_dbg(dev->class_dev, "pcl818_ai_cmd_mode() end\n");
866 ==============================================================================
867 Start/stop pacer onboard pacer
869 static void start_pacer(struct comedi_device *dev, int mode,
870 unsigned int divisor1, unsigned int divisor2)
872 outb(0xb4, dev->iobase + PCL818_CTRCTL);
873 outb(0x74, dev->iobase + PCL818_CTRCTL);
877 outb(divisor2 & 0xff, dev->iobase + PCL818_CTR2);
878 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL818_CTR2);
879 outb(divisor1 & 0xff, dev->iobase + PCL818_CTR1);
880 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL818_CTR1);
885 ==============================================================================
886 Check if channel list from user is builded correctly
887 If it's ok, then program scan/gain logic
889 static int check_channel_list(struct comedi_device *dev,
890 struct comedi_subdevice *s,
891 unsigned int *chanlist, unsigned int n_chan)
893 unsigned int chansegment[16];
894 unsigned int i, nowmustbechan, seglen, segpos;
896 /* correct channel and range number check itself comedi/range.c */
898 comedi_error(dev, "range/channel list is empty!");
903 /* first channel is every time ok */
904 chansegment[0] = chanlist[0];
905 /* build part of chanlist */
906 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
908 /* printk("%d. %d * %d\n",i,
909 * CR_CHAN(it->chanlist[i]),CR_RANGE(it->chanlist[i]));*/
911 /* we detect loop, this must by finish */
913 if (chanlist[0] == chanlist[i])
916 (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
917 if (nowmustbechan != CR_CHAN(chanlist[i])) { /* channel list isn't continuous :-( */
919 ("comedi%d: pcl818: channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
920 dev->minor, i, CR_CHAN(chanlist[i]),
921 nowmustbechan, CR_CHAN(chanlist[0]));
924 /* well, this is next correct channel in list */
925 chansegment[i] = chanlist[i];
928 /* check whole chanlist */
929 for (i = 0, segpos = 0; i < n_chan; i++) {
930 /* 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])); */
931 if (chanlist[i] != chansegment[i % seglen]) {
933 ("comedi%d: pcl818: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
934 dev->minor, i, CR_CHAN(chansegment[i]),
935 CR_RANGE(chansegment[i]),
936 CR_AREF(chansegment[i]),
937 CR_CHAN(chanlist[i % seglen]),
938 CR_RANGE(chanlist[i % seglen]),
939 CR_AREF(chansegment[i % seglen]));
940 return 0; /* chan/gain list is strange */
946 printk("check_channel_list: seglen %d\n", seglen);
950 static void setup_channel_list(struct comedi_device *dev,
951 struct comedi_subdevice *s,
952 unsigned int *chanlist, unsigned int n_chan,
955 struct pcl818_private *devpriv = dev->private;
958 devpriv->act_chanlist_len = seglen;
959 devpriv->act_chanlist_pos = 0;
961 for (i = 0; i < seglen; i++) { /* store range list to card */
962 devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
963 outb(muxonechan[CR_CHAN(chanlist[i])], dev->iobase + PCL818_MUX); /* select channel */
964 outb(CR_RANGE(chanlist[i]), dev->iobase + PCL818_RANGE); /* select gain */
969 /* select channel interval to scan */
970 outb(devpriv->act_chanlist[0] | (devpriv->act_chanlist[seglen -
972 dev->iobase + PCL818_MUX);
976 ==============================================================================
977 Check if board is switched to SE (1) or DIFF(0) mode
979 static int check_single_ended(unsigned int port)
981 if (inb(port + PCL818_STATUS) & 0x20)
987 ==============================================================================
989 static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
990 struct comedi_cmd *cmd)
992 const struct pcl818_board *board = comedi_board(dev);
993 struct pcl818_private *devpriv = dev->private;
995 int tmp, divisor1 = 0, divisor2 = 0;
997 /* Step 1 : check if triggers are trivially valid */
999 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
1000 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
1001 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
1002 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
1003 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
1008 /* Step 2a : make sure trigger sources are unique */
1010 err |= cfc_check_trigger_is_unique(cmd->convert_src);
1011 err |= cfc_check_trigger_is_unique(cmd->stop_src);
1013 /* Step 2b : and mutually compatible */
1018 /* Step 3: check if arguments are trivially valid */
1020 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
1021 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
1023 if (cmd->convert_src == TRIG_TIMER)
1024 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
1027 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
1029 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
1031 if (cmd->stop_src == TRIG_COUNT)
1032 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
1033 else /* TRIG_NONE */
1034 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
1039 /* step 4: fix up any arguments */
1041 if (cmd->convert_src == TRIG_TIMER) {
1042 tmp = cmd->convert_arg;
1043 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base, &divisor1,
1044 &divisor2, &cmd->convert_arg,
1045 cmd->flags & TRIG_ROUND_MASK);
1046 if (cmd->convert_arg < board->ns_min)
1047 cmd->convert_arg = board->ns_min;
1048 if (tmp != cmd->convert_arg)
1055 /* step 5: complain about special chanlist considerations */
1057 if (cmd->chanlist) {
1058 if (!check_channel_list(dev, s, cmd->chanlist,
1060 return 5; /* incorrect channels list */
1067 ==============================================================================
1069 static int ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
1071 struct pcl818_private *devpriv = dev->private;
1072 struct comedi_cmd *cmd = &s->async->cmd;
1075 dev_dbg(dev->class_dev, "pcl818_ai_cmd()\n");
1076 devpriv->ai_n_chan = cmd->chanlist_len;
1077 devpriv->ai_chanlist = cmd->chanlist;
1078 devpriv->ai_flags = cmd->flags;
1079 devpriv->ai_data_len = s->async->prealloc_bufsz;
1080 devpriv->ai_data = s->async->prealloc_buf;
1081 devpriv->ai_timer1 = 0;
1082 devpriv->ai_timer2 = 0;
1084 if (cmd->stop_src == TRIG_COUNT)
1085 devpriv->ai_scans = cmd->stop_arg;
1087 devpriv->ai_scans = 0;
1089 if (cmd->scan_begin_src == TRIG_FOLLOW) { /* mode 1, 3 */
1090 if (cmd->convert_src == TRIG_TIMER) { /* mode 1 */
1091 devpriv->ai_timer1 = cmd->convert_arg;
1092 retval = pcl818_ai_cmd_mode(1, dev, s);
1093 dev_dbg(dev->class_dev, "pcl818_ai_cmd() end\n");
1096 if (cmd->convert_src == TRIG_EXT) { /* mode 3 */
1097 return pcl818_ai_cmd_mode(3, dev, s);
1105 ==============================================================================
1106 cancel any mode 1-4 AI
1108 static int pcl818_ai_cancel(struct comedi_device *dev,
1109 struct comedi_subdevice *s)
1111 struct pcl818_private *devpriv = dev->private;
1113 if (devpriv->irq_blocked > 0) {
1114 dev_dbg(dev->class_dev, "pcl818_ai_cancel()\n");
1115 devpriv->irq_was_now_closed = 1;
1117 switch (devpriv->ai_mode) {
1118 case INT_TYPE_AI1_DMA:
1119 case INT_TYPE_AI3_DMA:
1120 if (devpriv->neverending_ai ||
1121 (!devpriv->neverending_ai &&
1122 devpriv->ai_act_scan > 0)) {
1123 /* wait for running dma transfer to end, do cleanup in interrupt */
1126 disable_dma(devpriv->dma);
1127 case INT_TYPE_AI1_INT:
1128 case INT_TYPE_AI3_INT:
1129 case INT_TYPE_AI1_FIFO:
1130 case INT_TYPE_AI3_FIFO:
1131 #ifdef PCL818_MODE13_AO
1132 case INT_TYPE_AO1_INT:
1133 case INT_TYPE_AO3_INT:
1135 outb(inb(dev->iobase + PCL818_CONTROL) & 0x73, dev->iobase + PCL818_CONTROL); /* Stop A/D */
1137 start_pacer(dev, -1, 0, 0);
1138 outb(0, dev->iobase + PCL818_AD_LO);
1139 inb(dev->iobase + PCL818_AD_LO);
1140 inb(dev->iobase + PCL818_AD_HI);
1141 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
1142 outb(0, dev->iobase + PCL818_CONTROL); /* Stop A/D */
1143 if (devpriv->usefifo) { /* FIFO shutdown */
1144 outb(0, dev->iobase + PCL818_FI_INTCLR);
1145 outb(0, dev->iobase + PCL818_FI_FLUSH);
1146 outb(0, dev->iobase + PCL818_FI_ENABLE);
1148 devpriv->irq_blocked = 0;
1149 devpriv->last_int_sub = s;
1150 devpriv->neverending_ai = 0;
1151 devpriv->ai_mode = 0;
1152 devpriv->irq_was_now_closed = 0;
1158 dev_dbg(dev->class_dev, "pcl818_ai_cancel() end\n");
1163 ==============================================================================
1166 static int pcl818_check(unsigned long iobase)
1168 outb(0x00, iobase + PCL818_MUX);
1170 if (inb(iobase + PCL818_MUX) != 0x00)
1171 return 1; /* there isn't card */
1172 outb(0x55, iobase + PCL818_MUX);
1174 if (inb(iobase + PCL818_MUX) != 0x55)
1175 return 1; /* there isn't card */
1176 outb(0x00, iobase + PCL818_MUX);
1178 outb(0x18, iobase + PCL818_CONTROL);
1180 if (inb(iobase + PCL818_CONTROL) != 0x18)
1181 return 1; /* there isn't card */
1182 return 0; /* ok, card exist */
1186 ==============================================================================
1187 reset whole PCL-818 cards
1189 static void pcl818_reset(struct comedi_device *dev)
1191 const struct pcl818_board *board = comedi_board(dev);
1192 struct pcl818_private *devpriv = dev->private;
1194 if (devpriv->usefifo) { /* FIFO shutdown */
1195 outb(0, dev->iobase + PCL818_FI_INTCLR);
1196 outb(0, dev->iobase + PCL818_FI_FLUSH);
1197 outb(0, dev->iobase + PCL818_FI_ENABLE);
1199 outb(0, dev->iobase + PCL818_DA_LO); /* DAC=0V */
1200 outb(0, dev->iobase + PCL818_DA_HI);
1202 outb(0, dev->iobase + PCL818_DO_HI); /* DO=$0000 */
1203 outb(0, dev->iobase + PCL818_DO_LO);
1205 outb(0, dev->iobase + PCL818_CONTROL);
1206 outb(0, dev->iobase + PCL818_CNTENABLE);
1207 outb(0, dev->iobase + PCL818_MUX);
1208 outb(0, dev->iobase + PCL818_CLRINT);
1209 outb(0xb0, dev->iobase + PCL818_CTRCTL); /* Stop pacer */
1210 outb(0x70, dev->iobase + PCL818_CTRCTL);
1211 outb(0x30, dev->iobase + PCL818_CTRCTL);
1212 if (board->is_818) {
1213 outb(0, dev->iobase + PCL818_RANGE);
1215 outb(0, dev->iobase + PCL718_DA2_LO);
1216 outb(0, dev->iobase + PCL718_DA2_HI);
1220 static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1222 const struct pcl818_board *board = comedi_board(dev);
1223 struct pcl818_private *devpriv;
1227 unsigned long pages;
1228 struct comedi_subdevice *s;
1230 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
1233 dev->private = devpriv;
1235 devpriv->io_range = board->io_range;
1236 if ((board->fifo) && (it->options[2] == -1)) {
1237 /* we've board with FIFO and we want to use FIFO */
1238 devpriv->io_range = PCLx1xFIFO_RANGE;
1239 devpriv->usefifo = 1;
1241 ret = comedi_request_region(dev, it->options[0], devpriv->io_range);
1245 if (pcl818_check(dev->iobase)) {
1246 comedi_error(dev, "I can't detect board. FAIL!\n");
1252 if (board->IRQbits != 0) { /* board support IRQ */
1253 irq = it->options[1];
1254 if (irq) { /* we want to use IRQ */
1255 if (((1 << irq) & board->IRQbits) == 0) {
1257 (", IRQ %u is out of allowed range, DISABLING IT",
1259 irq = 0; /* Bad IRQ */
1261 if (request_irq(irq, interrupt_pcl818, 0,
1262 dev->board_name, dev)) {
1264 (", unable to allocate IRQ %u, DISABLING IT",
1266 irq = 0; /* Can't use IRQ */
1268 printk(KERN_DEBUG "irq=%u", irq);
1276 devpriv->irq_free = 1; /* 1=we have allocated irq */
1278 devpriv->irq_free = 0;
1280 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1281 devpriv->ai_mode = 0; /* mode of irq */
1286 if (!devpriv->irq_free)
1287 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1288 if (board->DMAbits != 0) { /* board support DMA */
1289 dma = it->options[2];
1291 goto no_dma; /* DMA disabled */
1292 if (((1 << dma) & board->DMAbits) == 0) {
1293 printk(KERN_ERR "DMA is out of allowed range, FAIL!\n");
1294 return -EINVAL; /* Bad DMA */
1296 ret = request_dma(dma, dev->board_name);
1298 return -EBUSY; /* DMA isn't free */
1300 pages = 2; /* we need 16KB */
1301 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1302 if (!devpriv->dmabuf[0])
1303 /* maybe experiment with try_to_free_pages() will help .... */
1304 return -EBUSY; /* no buffer :-( */
1305 devpriv->dmapages[0] = pages;
1306 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1307 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
1308 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
1309 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1310 if (!devpriv->dmabuf[1])
1312 devpriv->dmapages[1] = pages;
1313 devpriv->hwdmaptr[1] = virt_to_bus((void *)devpriv->dmabuf[1]);
1314 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1319 ret = comedi_alloc_subdevices(dev, 4);
1323 s = &dev->subdevices[0];
1324 if (!board->n_aichan_se) {
1325 s->type = COMEDI_SUBD_UNUSED;
1327 s->type = COMEDI_SUBD_AI;
1328 devpriv->sub_ai = s;
1329 s->subdev_flags = SDF_READABLE;
1330 if (check_single_ended(dev->iobase)) {
1331 s->n_chan = board->n_aichan_se;
1332 s->subdev_flags |= SDF_COMMON | SDF_GROUND;
1333 printk(", %dchans S.E. DAC", s->n_chan);
1335 s->n_chan = board->n_aichan_diff;
1336 s->subdev_flags |= SDF_DIFF;
1337 printk(", %dchans DIFF DAC", s->n_chan);
1339 s->maxdata = board->ai_maxdata;
1340 s->len_chanlist = s->n_chan;
1341 s->range_table = board->ai_range_type;
1342 s->cancel = pcl818_ai_cancel;
1343 s->insn_read = pcl818_ai_insn_read;
1345 dev->read_subdev = s;
1346 s->subdev_flags |= SDF_CMD_READ;
1347 s->do_cmdtest = ai_cmdtest;
1350 if (board->is_818) {
1351 if ((it->options[4] == 1) || (it->options[4] == 10))
1352 s->range_table = &range_pcl818l_h_ai; /* secondary range list jumper selectable */
1354 switch (it->options[4]) {
1356 s->range_table = &range_bipolar10;
1359 s->range_table = &range_bipolar5;
1362 s->range_table = &range_bipolar2_5;
1365 s->range_table = &range718_bipolar1;
1368 s->range_table = &range718_bipolar0_5;
1371 s->range_table = &range_unipolar10;
1374 s->range_table = &range_unipolar5;
1377 s->range_table = &range718_unipolar2;
1380 s->range_table = &range718_unipolar1;
1383 s->range_table = &range_unknown;
1389 s = &dev->subdevices[1];
1390 if (!board->n_aochan) {
1391 s->type = COMEDI_SUBD_UNUSED;
1393 s->type = COMEDI_SUBD_AO;
1394 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1395 s->n_chan = board->n_aochan;
1396 s->maxdata = board->ao_maxdata;
1397 s->len_chanlist = board->n_aochan;
1398 s->range_table = board->ao_range_type;
1399 s->insn_read = pcl818_ao_insn_read;
1400 s->insn_write = pcl818_ao_insn_write;
1401 if (board->is_818) {
1402 if ((it->options[4] == 1) || (it->options[4] == 10))
1403 s->range_table = &range_unipolar10;
1404 if (it->options[4] == 2)
1405 s->range_table = &range_unknown;
1407 if ((it->options[5] == 1) || (it->options[5] == 10))
1408 s->range_table = &range_unipolar10;
1409 if (it->options[5] == 2)
1410 s->range_table = &range_unknown;
1414 s = &dev->subdevices[2];
1415 if (!board->n_dichan) {
1416 s->type = COMEDI_SUBD_UNUSED;
1418 s->type = COMEDI_SUBD_DI;
1419 s->subdev_flags = SDF_READABLE;
1420 s->n_chan = board->n_dichan;
1422 s->len_chanlist = board->n_dichan;
1423 s->range_table = &range_digital;
1424 s->insn_bits = pcl818_di_insn_bits;
1427 s = &dev->subdevices[3];
1428 if (!board->n_dochan) {
1429 s->type = COMEDI_SUBD_UNUSED;
1431 s->type = COMEDI_SUBD_DO;
1432 s->subdev_flags = SDF_WRITABLE;
1433 s->n_chan = board->n_dochan;
1435 s->len_chanlist = board->n_dochan;
1436 s->range_table = &range_digital;
1437 s->insn_bits = pcl818_do_insn_bits;
1440 /* select 1/10MHz oscilator */
1441 if ((it->options[3] == 0) || (it->options[3] == 10))
1442 devpriv->i8253_osc_base = 100;
1444 devpriv->i8253_osc_base = 1000;
1446 /* max sampling speed */
1447 devpriv->ns_min = board->ns_min;
1449 if (!board->is_818) {
1450 if ((it->options[6] == 1) || (it->options[6] == 100))
1451 devpriv->ns_min = 10000; /* extended PCL718 to 100kHz DAC */
1461 static void pcl818_detach(struct comedi_device *dev)
1463 struct pcl818_private *devpriv = dev->private;
1466 pcl818_ai_cancel(dev, devpriv->sub_ai);
1469 free_dma(devpriv->dma);
1470 if (devpriv->dmabuf[0])
1471 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1472 if (devpriv->dmabuf[1])
1473 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1475 comedi_legacy_detach(dev);
1478 static const struct pcl818_board boardtypes[] = {
1479 {"pcl818l", 4, 16, 8, 25000, 1, 16, 16, &range_pcl818l_l_ai,
1480 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1481 0x0a, 0xfff, 0xfff, 0, 1},
1482 {"pcl818h", 9, 16, 8, 10000, 1, 16, 16, &range_pcl818h_ai,
1483 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1484 0x0a, 0xfff, 0xfff, 0, 1},
1485 {"pcl818hd", 9, 16, 8, 10000, 1, 16, 16, &range_pcl818h_ai,
1486 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1487 0x0a, 0xfff, 0xfff, 1, 1},
1488 {"pcl818hg", 12, 16, 8, 10000, 1, 16, 16, &range_pcl818hg_ai,
1489 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1490 0x0a, 0xfff, 0xfff, 1, 1},
1491 {"pcl818", 9, 16, 8, 10000, 2, 16, 16, &range_pcl818h_ai,
1492 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1493 0x0a, 0xfff, 0xfff, 0, 1},
1494 {"pcl718", 1, 16, 8, 16000, 2, 16, 16, &range_unipolar5,
1495 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1496 0x0a, 0xfff, 0xfff, 0, 0},
1498 {"pcm3718", 9, 16, 8, 10000, 0, 16, 16, &range_pcl818h_ai,
1499 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1500 0x0a, 0xfff, 0xfff, 0, 1 /* XXX ? */ },
1503 static struct comedi_driver pcl818_driver = {
1504 .driver_name = "pcl818",
1505 .module = THIS_MODULE,
1506 .attach = pcl818_attach,
1507 .detach = pcl818_detach,
1508 .board_name = &boardtypes[0].name,
1509 .num_names = ARRAY_SIZE(boardtypes),
1510 .offset = sizeof(struct pcl818_board),
1512 module_comedi_driver(pcl818_driver);
1514 MODULE_AUTHOR("Comedi http://www.comedi.org");
1515 MODULE_DESCRIPTION("Comedi low-level driver");
1516 MODULE_LICENSE("GPL");