3 hardware driver for PC-LabCard PCL-711 and AdSys ACL-8112
6 COMEDI - Linux Control and Measurement Device Interface
7 Copyright (C) 1998 David A. Schleef <ds@schleef.org>
8 Janne Jalkanen <jalkanen@cs.hut.fi>
9 Eric Bunn <ebu@cs.hut.fi>
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 Description: Advantech PCL-711 and 711b, ADLink ACL-8112
29 Author: ds, Janne Jalkanen <jalkanen@cs.hut.fi>, Eric Bunn <ebu@cs.hut.fi>
30 Status: mostly complete
31 Devices: [Advantech] PCL-711 (pcl711), PCL-711B (pcl711b),
32 [AdLink] ACL-8112HG (acl8112hg), ACL-8112DG (acl8112dg)
34 Since these boards do not have DMA or FIFOs, only immediate mode is
40 Dave Andruczyk <dave@tech.buffalostate.edu> also wrote a
41 driver for the PCL-711. I used a few ideas from his driver
42 here. His driver also has more comments, if you are
43 interested in understanding how this driver works.
44 http://tech.buffalostate.edu/~dave/driver/
46 The ACL-8112 driver was hacked from the sources of the PCL-711
47 driver (the 744 chip used on the 8112 is almost the same as
48 the 711b chip, but it has more I/O channels) by
49 Janne Jalkanen (jalkanen@cs.hut.fi) and
50 Erik Bunn (ebu@cs.hut.fi). Remerged with the PCL-711 driver
54 This driver supports both TRIGNOW and TRIGCLK,
55 but does not yet support DMA transfers. It also supports
56 both high (HG) and low (DG) versions of the card, though
57 the HG version has been untested.
61 #include <linux/kernel.h>
62 #include <linux/module.h>
63 #include <linux/sched.h>
65 #include <linux/slab.h>
66 #include <linux/errno.h>
67 #include <linux/ioport.h>
68 #include <linux/delay.h>
69 #include <linux/interrupt.h>
70 #include <linux/timex.h>
71 #include <linux/timer.h>
73 #include <linux/comedidev.h>
78 #define PCL711_SIZE 16
83 #define PCL711_CTRCTL 3
84 #define PCL711_AD_LO 4
85 #define PCL711_DA0_LO 4
86 #define PCL711_AD_HI 5
87 #define PCL711_DA0_HI 5
88 #define PCL711_DI_LO 6
89 #define PCL711_DA1_LO 6
90 #define PCL711_DI_HI 7
91 #define PCL711_DA1_HI 7
92 #define PCL711_CLRINTR 8
95 #define PCL711_MODE 11
96 #define PCL711_SOFTTRIG 12
97 #define PCL711_DO_LO 13
98 #define PCL711_DO_HI 14
100 static comedi_lrange range_pcl711b_ai = { 5, {
107 static comedi_lrange range_acl8112hg_ai = { 12, {
121 static comedi_lrange range_acl8112dg_ai = { 9, {
139 #define PCL711_TIMEOUT 100
140 #define PCL711_DRDY 0x10
142 static int i8253_osc_base = 500; /* 2 Mhz */
153 comedi_lrange * ai_range_type;
156 static boardtype boardtypes[] =
158 {"pcl711", 0, 0, 0, 5, 8, 1, 0, &range_bipolar5},
159 {"pcl711b", 1, 0, 0, 5, 8, 1, 7, &range_pcl711b_ai},
160 {"acl8112hg", 0, 1, 0, 12, 16, 2, 15, &range_acl8112hg_ai},
161 {"acl8112dg", 0, 1, 1, 9, 16, 2, 15, &range_acl8112dg_ai},
163 #define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
164 #define this_board ((boardtype *)dev->board_ptr)
166 static int pcl711_attach(comedi_device *dev,comedi_devconfig *it);
167 static int pcl711_detach(comedi_device *dev);
168 static comedi_driver driver_pcl711={
169 driver_name: "pcl711",
171 attach: pcl711_attach,
172 detach: pcl711_detach,
173 board_name: boardtypes,
174 num_names: n_boardtypes,
175 offset: sizeof(boardtype),
177 COMEDI_INITCLEANUP(driver_pcl711);
185 lsampl_t ao_readback[2];
186 unsigned int divisor1;
187 unsigned int divisor2;
190 #define devpriv ((pcl711_private *)dev->private)
192 static void pcl711_interrupt(int irq, void *d, struct pt_regs *regs)
196 comedi_device *dev = d;
197 comedi_subdevice *s = dev->subdevices + 0;
199 hi = inb(dev->iobase + PCL711_AD_HI);
200 lo = inb(dev->iobase + PCL711_AD_LO);
201 outb(0, dev->iobase + PCL711_CLRINTR);
203 data = (hi << 8) | lo;
205 if (!(--devpriv->ntrig)) {
206 if (this_board->is_8112) {
207 outb(1, dev->iobase + PCL711_MODE);
209 outb(0, dev->iobase + PCL711_MODE);
212 s->async->events |= COMEDI_CB_EOA;
214 comedi_event(dev, s, s->async->events);
217 static void pcl711_set_changain(comedi_device * dev, int chan)
221 outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
223 chan_register=CR_CHAN(chan);
225 if (this_board->is_8112) {
228 * Set the correct channel. The two channel banks are switched
229 * using the mask value.
230 * NB: To use differential channels, you should use mask = 0x30,
231 * but I haven't written the support for this yet. /JJ
234 if (chan_register >= 8){
235 chan_register = 0x20 | (chan_register & 0x7);
237 chan_register |= 0x10;
240 outb(chan_register, dev->iobase + PCL711_MUX);
244 static int pcl711_ai_insn(comedi_device *dev,comedi_subdevice *s,
245 comedi_insn *insn,lsampl_t *data)
250 pcl711_set_changain(dev,insn->chanspec);
253 a sensible precaution to wait for the mux to
254 settle here. is 10us enough?
258 for(n=0;n<insn->n;n++){
260 * Write the correct mode (software polling) and start polling by writing
261 * to the trigger register
263 outb(1, dev->iobase + PCL711_MODE);
265 if (this_board->is_8112) {
267 outb(0, dev->iobase + PCL711_SOFTTRIG);
272 hi = inb(dev->iobase + PCL711_AD_HI);
273 if (!(hi & PCL711_DRDY))
276 rt_printk("comedi%d: pcl711: A/D timeout\n", dev->minor);
280 lo = inb(dev->iobase + PCL711_AD_LO);
282 data[n] = ((hi & 0xf) << 8) | lo;
288 static int pcl711_ai_cmdtest(comedi_device *dev, comedi_subdevice *s,
296 cmd->start_src &= TRIG_NOW;
297 if(!cmd->start_src || tmp!=cmd->start_src)err++;
299 tmp=cmd->scan_begin_src;
300 cmd->scan_begin_src &= TRIG_TIMER|TRIG_EXT;
301 if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;
303 tmp=cmd->convert_src;
304 cmd->convert_src &= TRIG_NOW;
305 if(!cmd->convert_src || tmp!=cmd->convert_src)err++;
307 tmp=cmd->scan_end_src;
308 cmd->scan_end_src &= TRIG_COUNT;
309 if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++;
312 cmd->stop_src &= TRIG_COUNT|TRIG_NONE;
313 if(!cmd->stop_src || tmp!=cmd->stop_src)err++;
319 if(cmd->scan_begin_src!=TRIG_TIMER &&
320 cmd->scan_begin_src!=TRIG_EXT)err++;
321 if(cmd->stop_src!=TRIG_COUNT &&
322 cmd->stop_src!=TRIG_NONE)err++;
328 if(cmd->start_arg!=0){
332 if(cmd->scan_begin_src==TRIG_EXT){
333 if(cmd->scan_begin_arg!=0){
334 cmd->scan_begin_arg=0;
338 #define MAX_SPEED 1000
339 #define TIMER_BASE 100
340 if(cmd->scan_begin_arg<MAX_SPEED){
341 cmd->scan_begin_arg=MAX_SPEED;
345 if(cmd->convert_arg!=0){
349 if(cmd->scan_end_arg!=cmd->chanlist_len){
350 cmd->scan_end_arg=cmd->chanlist_len;
353 if(cmd->stop_src==TRIG_NONE){
354 if(cmd->stop_arg!=0){
366 if(cmd->scan_begin_src==TRIG_TIMER){
367 tmp = cmd->scan_begin_arg;
368 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
369 &devpriv->divisor1,&devpriv->divisor2,
370 &cmd->scan_begin_arg, cmd->flags&TRIG_ROUND_MASK);
371 if(tmp!=cmd->scan_begin_arg)
380 static int pcl711_ai_cmd(comedi_device *dev, comedi_subdevice *s)
383 comedi_cmd *cmd = &s->async->cmd;
385 pcl711_set_changain(dev,cmd->chanlist[0]);
387 if(cmd->scan_begin_src==TRIG_TIMER){
390 * timer chip is an 8253, with timers 1 and 2
392 * 0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
393 * Mode 2 = Rate generator
395 * 0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
398 i8253_cascade_ns_to_timer(i8253_osc_base,&timer1,&timer2,
399 &cmd->scan_begin_arg,TRIG_ROUND_NEAREST);
401 outb(0x74, dev->iobase + PCL711_CTRCTL);
402 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
403 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
404 outb(0xb4, dev->iobase + PCL711_CTRCTL);
405 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
406 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
408 /* clear pending interrupts (just in case) */
409 outb(0, dev->iobase + PCL711_CLRINTR);
412 * Set mode to IRQ transfer
414 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
416 /* external trigger */
417 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
426 static int pcl711_ao_insn(comedi_device *dev,comedi_subdevice *s,
427 comedi_insn *insn,lsampl_t *data)
430 int chan = CR_CHAN(insn->chanspec);
432 for(n=0;n<insn->n;n++){
433 outb((data[n] & 0xff), dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
434 outb((data[n] >> 8), dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
436 devpriv->ao_readback[chan] = data[n];
442 static int pcl711_ao_insn_read(comedi_device *dev,comedi_subdevice *s,
443 comedi_insn *insn,lsampl_t *data)
446 int chan = CR_CHAN(insn->chanspec);
448 for(n=0;n<insn->n;n++){
449 data[n] = devpriv->ao_readback[chan];
456 /* Digital port read - Untested on 8112 */
457 static int pcl711_di_insn_bits(comedi_device * dev, comedi_subdevice * s,
458 comedi_insn *insn,lsampl_t *data)
460 if(insn->n!=2)return -EINVAL;
462 data[1] = inb(dev->iobase + PCL711_DI_LO) |
463 (inb(dev->iobase + PCL711_DI_HI) << 8);
468 /* Digital port write - Untested on 8112 */
469 static int pcl711_do_insn_bits(comedi_device * dev, comedi_subdevice * s,
470 comedi_insn *insn,lsampl_t *data)
472 if(insn->n!=2)return -EINVAL;
475 s->state &= ~data[0];
476 s->state |= data[0]&data[1];
479 outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
481 outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
488 /* Free any resources that we have claimed */
489 static int pcl711_detach(comedi_device * dev)
491 printk("comedi%d: pcl711: remove\n", dev->minor);
494 comedi_free_irq(dev->irq, dev);
497 release_region(dev->iobase, PCL711_SIZE);
503 static int pcl711_attach(comedi_device * dev, comedi_devconfig * it)
510 /* claim our I/O space */
512 iobase = it->options[0];
513 printk("comedi%d: pcl711: 0x%04x ", dev->minor, iobase);
514 if (check_region(iobase, PCL711_SIZE) < 0) {
515 printk("I/O port conflict\n");
518 request_region(iobase, PCL711_SIZE, "pcl711");
519 dev->iobase = iobase;
521 /* there should be a sanity check here */
523 /* set up some name stuff */
524 dev->board_name = this_board->name;
527 irq = it->options[1];
528 if (irq < 0 || irq > this_board->maxirq) {
529 printk("irq out of range\n");
533 if (comedi_request_irq(irq, pcl711_interrupt, 0, "pcl711", dev)) {
534 printk("unable to allocate irq %d\n", irq);
537 printk("( irq = %d )\n", irq);
542 dev->n_subdevices = 4;
543 if((ret=alloc_subdevices(dev))<0)
545 if((ret=alloc_private(dev,sizeof(pcl711_private)))<0)
548 s = dev->subdevices + 0;
550 s->type = COMEDI_SUBD_AI;
551 s->subdev_flags = SDF_READABLE;
552 s->n_chan = this_board->n_aichan;
555 s->range_table = this_board->ai_range_type;
556 s->insn_read = pcl711_ai_insn;
558 s->do_cmdtest = pcl711_ai_cmdtest;
559 s->do_cmd = pcl711_ai_cmd;
564 s->type = COMEDI_SUBD_AO;
565 s->subdev_flags = SDF_WRITEABLE;
566 s->n_chan = this_board->n_aochan;
569 s->range_table = &range_bipolar5;
570 s->insn_write = pcl711_ao_insn;
571 s->insn_read = pcl711_ao_insn_read;
574 /* 16-bit digital input */
575 s->type = COMEDI_SUBD_DI;
576 s->subdev_flags = SDF_READABLE;
579 s->len_chanlist = 16;
580 s->range_table = &range_digital;
581 s->insn_bits = pcl711_di_insn_bits;
584 /* 16-bit digital out */
585 s->type = COMEDI_SUBD_DO;
586 s->subdev_flags = SDF_WRITEABLE;
589 s->len_chanlist = 16;
590 s->range_table = &range_digital;
592 s->insn_bits = pcl711_do_insn_bits;
595 this is the "base value" for the mode register, which is
596 used for the irq on the PCL711
598 if(this_board->is_pcl711b){
599 devpriv->mode=(dev->irq<<4);
603 outb(0, dev->iobase + PCL711_DA0_LO);
604 outb(0, dev->iobase + PCL711_DA0_HI);
605 outb(0, dev->iobase + PCL711_DA1_LO);
606 outb(0, dev->iobase + PCL711_DA1_HI);