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/comedidev.h>
63 #include <linux/ioport.h>
64 #include <linux/delay.h>
70 #define PCL711_SIZE 16
75 #define PCL711_CTRCTL 3
76 #define PCL711_AD_LO 4
77 #define PCL711_DA0_LO 4
78 #define PCL711_AD_HI 5
79 #define PCL711_DA0_HI 5
80 #define PCL711_DI_LO 6
81 #define PCL711_DA1_LO 6
82 #define PCL711_DI_HI 7
83 #define PCL711_DA1_HI 7
84 #define PCL711_CLRINTR 8
87 #define PCL711_MODE 11
88 #define PCL711_SOFTTRIG 12
89 #define PCL711_DO_LO 13
90 #define PCL711_DO_HI 14
92 static const comedi_lrange range_pcl711b_ai = { 5, {
99 static const comedi_lrange range_acl8112hg_ai = { 12, {
113 static const comedi_lrange range_acl8112dg_ai = { 9, {
129 #define PCL711_TIMEOUT 100
130 #define PCL711_DRDY 0x10
132 static const int i8253_osc_base = 500; /* 2 Mhz */
143 const comedi_lrange * ai_range_type;
146 static const boardtype boardtypes[] =
148 {"pcl711", 0, 0, 0, 5, 8, 1, 0, &range_bipolar5},
149 {"pcl711b", 1, 0, 0, 5, 8, 1, 7, &range_pcl711b_ai},
150 {"acl8112hg", 0, 1, 0, 12, 16, 2, 15, &range_acl8112hg_ai},
151 {"acl8112dg", 0, 1, 1, 9, 16, 2, 15, &range_acl8112dg_ai},
153 #define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
154 #define this_board ((const boardtype *)dev->board_ptr)
156 static int pcl711_attach(comedi_device *dev,comedi_devconfig *it);
157 static int pcl711_detach(comedi_device *dev);
158 static comedi_driver driver_pcl711={
159 driver_name: "pcl711",
161 attach: pcl711_attach,
162 detach: pcl711_detach,
163 board_name: &boardtypes[0].name,
164 num_names: n_boardtypes,
165 offset: sizeof(boardtype),
167 COMEDI_INITCLEANUP(driver_pcl711);
175 lsampl_t ao_readback[2];
176 unsigned int divisor1;
177 unsigned int divisor2;
180 #define devpriv ((pcl711_private *)dev->private)
182 static irqreturn_t pcl711_interrupt(int irq, void *d PT_REGS_ARG)
186 comedi_device *dev = d;
187 comedi_subdevice *s = dev->subdevices + 0;
189 hi = inb(dev->iobase + PCL711_AD_HI);
190 lo = inb(dev->iobase + PCL711_AD_LO);
191 outb(0, dev->iobase + PCL711_CLRINTR);
193 data = (hi << 8) | lo;
195 if (!(--devpriv->ntrig)) {
196 if (this_board->is_8112) {
197 outb(1, dev->iobase + PCL711_MODE);
199 outb(0, dev->iobase + PCL711_MODE);
202 s->async->events |= COMEDI_CB_EOA;
204 comedi_event(dev, s, s->async->events);
208 static void pcl711_set_changain(comedi_device * dev, int chan)
212 outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
214 chan_register=CR_CHAN(chan);
216 if (this_board->is_8112) {
219 * Set the correct channel. The two channel banks are switched
220 * using the mask value.
221 * NB: To use differential channels, you should use mask = 0x30,
222 * but I haven't written the support for this yet. /JJ
225 if (chan_register >= 8){
226 chan_register = 0x20 | (chan_register & 0x7);
228 chan_register |= 0x10;
231 outb(chan_register, dev->iobase + PCL711_MUX);
235 static int pcl711_ai_insn(comedi_device *dev,comedi_subdevice *s,
236 comedi_insn *insn,lsampl_t *data)
241 pcl711_set_changain(dev,insn->chanspec);
243 for(n=0;n<insn->n;n++){
245 * Write the correct mode (software polling) and start polling by writing
246 * to the trigger register
248 outb(1, dev->iobase + PCL711_MODE);
250 if (this_board->is_8112) {
252 outb(0, dev->iobase + PCL711_SOFTTRIG);
257 hi = inb(dev->iobase + PCL711_AD_HI);
258 if (!(hi & PCL711_DRDY))
262 rt_printk("comedi%d: pcl711: A/D timeout\n", dev->minor);
266 lo = inb(dev->iobase + PCL711_AD_LO);
268 data[n] = ((hi & 0xf) << 8) | lo;
274 static int pcl711_ai_cmdtest(comedi_device *dev, comedi_subdevice *s,
282 cmd->start_src &= TRIG_NOW;
283 if(!cmd->start_src || tmp!=cmd->start_src)err++;
285 tmp=cmd->scan_begin_src;
286 cmd->scan_begin_src &= TRIG_TIMER|TRIG_EXT;
287 if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;
289 tmp=cmd->convert_src;
290 cmd->convert_src &= TRIG_NOW;
291 if(!cmd->convert_src || tmp!=cmd->convert_src)err++;
293 tmp=cmd->scan_end_src;
294 cmd->scan_end_src &= TRIG_COUNT;
295 if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++;
298 cmd->stop_src &= TRIG_COUNT|TRIG_NONE;
299 if(!cmd->stop_src || tmp!=cmd->stop_src)err++;
305 if(cmd->scan_begin_src!=TRIG_TIMER &&
306 cmd->scan_begin_src!=TRIG_EXT)err++;
307 if(cmd->stop_src!=TRIG_COUNT &&
308 cmd->stop_src!=TRIG_NONE)err++;
314 if(cmd->start_arg!=0){
318 if(cmd->scan_begin_src==TRIG_EXT){
319 if(cmd->scan_begin_arg!=0){
320 cmd->scan_begin_arg=0;
324 #define MAX_SPEED 1000
325 #define TIMER_BASE 100
326 if(cmd->scan_begin_arg<MAX_SPEED){
327 cmd->scan_begin_arg=MAX_SPEED;
331 if(cmd->convert_arg!=0){
335 if(cmd->scan_end_arg!=cmd->chanlist_len){
336 cmd->scan_end_arg=cmd->chanlist_len;
339 if(cmd->stop_src==TRIG_NONE){
340 if(cmd->stop_arg!=0){
352 if(cmd->scan_begin_src==TRIG_TIMER){
353 tmp = cmd->scan_begin_arg;
354 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
355 &devpriv->divisor1,&devpriv->divisor2,
356 &cmd->scan_begin_arg, cmd->flags&TRIG_ROUND_MASK);
357 if(tmp!=cmd->scan_begin_arg)
366 static int pcl711_ai_cmd(comedi_device *dev, comedi_subdevice *s)
369 comedi_cmd *cmd = &s->async->cmd;
371 pcl711_set_changain(dev,cmd->chanlist[0]);
373 if(cmd->scan_begin_src==TRIG_TIMER){
376 * timer chip is an 8253, with timers 1 and 2
378 * 0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
379 * Mode 2 = Rate generator
381 * 0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
384 i8253_cascade_ns_to_timer(i8253_osc_base,&timer1,&timer2,
385 &cmd->scan_begin_arg,TRIG_ROUND_NEAREST);
387 outb(0x74, dev->iobase + PCL711_CTRCTL);
388 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
389 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
390 outb(0xb4, dev->iobase + PCL711_CTRCTL);
391 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
392 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
394 /* clear pending interrupts (just in case) */
395 outb(0, dev->iobase + PCL711_CLRINTR);
398 * Set mode to IRQ transfer
400 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
402 /* external trigger */
403 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
412 static int pcl711_ao_insn(comedi_device *dev,comedi_subdevice *s,
413 comedi_insn *insn,lsampl_t *data)
416 int chan = CR_CHAN(insn->chanspec);
418 for(n=0;n<insn->n;n++){
419 outb((data[n] & 0xff), dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
420 outb((data[n] >> 8), dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
422 devpriv->ao_readback[chan] = data[n];
428 static int pcl711_ao_insn_read(comedi_device *dev,comedi_subdevice *s,
429 comedi_insn *insn,lsampl_t *data)
432 int chan = CR_CHAN(insn->chanspec);
434 for(n=0;n<insn->n;n++){
435 data[n] = devpriv->ao_readback[chan];
442 /* Digital port read - Untested on 8112 */
443 static int pcl711_di_insn_bits(comedi_device * dev, comedi_subdevice * s,
444 comedi_insn *insn,lsampl_t *data)
446 if(insn->n!=2)return -EINVAL;
448 data[1] = inb(dev->iobase + PCL711_DI_LO) |
449 (inb(dev->iobase + PCL711_DI_HI) << 8);
454 /* Digital port write - Untested on 8112 */
455 static int pcl711_do_insn_bits(comedi_device * dev, comedi_subdevice * s,
456 comedi_insn *insn,lsampl_t *data)
458 if(insn->n!=2)return -EINVAL;
461 s->state &= ~data[0];
462 s->state |= data[0]&data[1];
465 outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
467 outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
474 /* Free any resources that we have claimed */
475 static int pcl711_detach(comedi_device * dev)
477 printk("comedi%d: pcl711: remove\n", dev->minor);
480 comedi_free_irq(dev->irq, dev);
483 release_region(dev->iobase, PCL711_SIZE);
489 static int pcl711_attach(comedi_device * dev, comedi_devconfig * it)
492 unsigned long iobase;
496 /* claim our I/O space */
498 iobase = it->options[0];
499 printk("comedi%d: pcl711: 0x%04lx ", dev->minor, iobase);
500 if (!request_region(iobase, PCL711_SIZE, "pcl711")) {
501 printk("I/O port conflict\n");
504 dev->iobase = iobase;
506 /* there should be a sanity check here */
508 /* set up some name stuff */
509 dev->board_name = this_board->name;
512 irq = it->options[1];
513 if (irq > this_board->maxirq) {
514 printk("irq out of range\n");
518 if (comedi_request_irq(irq, pcl711_interrupt, 0, "pcl711", dev)) {
519 printk("unable to allocate irq %u\n", irq);
522 printk("( irq = %u )\n", irq);
527 if((ret=alloc_subdevices(dev, 4))<0)
529 if((ret=alloc_private(dev,sizeof(pcl711_private)))<0)
532 s = dev->subdevices + 0;
534 s->type = COMEDI_SUBD_AI;
535 s->subdev_flags = SDF_READABLE | SDF_GROUND;
536 s->n_chan = this_board->n_aichan;
539 s->range_table = this_board->ai_range_type;
540 s->insn_read = pcl711_ai_insn;
542 dev->read_subdev = s;
543 s->subdev_flags |= SDF_CMD_READ;
544 s->do_cmdtest = pcl711_ai_cmdtest;
545 s->do_cmd = pcl711_ai_cmd;
550 s->type = COMEDI_SUBD_AO;
551 s->subdev_flags = SDF_WRITABLE;
552 s->n_chan = this_board->n_aochan;
555 s->range_table = &range_bipolar5;
556 s->insn_write = pcl711_ao_insn;
557 s->insn_read = pcl711_ao_insn_read;
560 /* 16-bit digital input */
561 s->type = COMEDI_SUBD_DI;
562 s->subdev_flags = SDF_READABLE;
565 s->len_chanlist = 16;
566 s->range_table = &range_digital;
567 s->insn_bits = pcl711_di_insn_bits;
570 /* 16-bit digital out */
571 s->type = COMEDI_SUBD_DO;
572 s->subdev_flags = SDF_WRITABLE;
575 s->len_chanlist = 16;
576 s->range_table = &range_digital;
578 s->insn_bits = pcl711_do_insn_bits;
581 this is the "base value" for the mode register, which is
582 used for the irq on the PCL711
584 if(this_board->is_pcl711b){
585 devpriv->mode=(dev->irq<<4);
589 outb(0, dev->iobase + PCL711_DA0_LO);
590 outb(0, dev->iobase + PCL711_DA0_HI);
591 outb(0, dev->iobase + PCL711_DA1_LO);
592 outb(0, dev->iobase + PCL711_DA1_HI);