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 comedi_lrange range_pcl711b_ai = { 5, {
99 static comedi_lrange range_acl8112hg_ai = { 12, {
113 static comedi_lrange range_acl8112dg_ai = { 9, {
131 #define PCL711_TIMEOUT 100
132 #define PCL711_DRDY 0x10
134 static int i8253_osc_base = 500; /* 2 Mhz */
145 comedi_lrange * ai_range_type;
148 static boardtype boardtypes[] =
150 {"pcl711", 0, 0, 0, 5, 8, 1, 0, &range_bipolar5},
151 {"pcl711b", 1, 0, 0, 5, 8, 1, 7, &range_pcl711b_ai},
152 {"acl8112hg", 0, 1, 0, 12, 16, 2, 15, &range_acl8112hg_ai},
153 {"acl8112dg", 0, 1, 1, 9, 16, 2, 15, &range_acl8112dg_ai},
155 #define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
156 #define this_board ((boardtype *)dev->board_ptr)
158 static int pcl711_attach(comedi_device *dev,comedi_devconfig *it);
159 static int pcl711_detach(comedi_device *dev);
160 static comedi_driver driver_pcl711={
161 driver_name: "pcl711",
163 attach: pcl711_attach,
164 detach: pcl711_detach,
165 board_name: boardtypes,
166 num_names: n_boardtypes,
167 offset: sizeof(boardtype),
169 COMEDI_INITCLEANUP(driver_pcl711);
177 lsampl_t ao_readback[2];
178 unsigned int divisor1;
179 unsigned int divisor2;
182 #define devpriv ((pcl711_private *)dev->private)
184 static irqreturn_t pcl711_interrupt(int irq, void *d, struct pt_regs *regs)
188 comedi_device *dev = d;
189 comedi_subdevice *s = dev->subdevices + 0;
191 hi = inb(dev->iobase + PCL711_AD_HI);
192 lo = inb(dev->iobase + PCL711_AD_LO);
193 outb(0, dev->iobase + PCL711_CLRINTR);
195 data = (hi << 8) | lo;
197 if (!(--devpriv->ntrig)) {
198 if (this_board->is_8112) {
199 outb(1, dev->iobase + PCL711_MODE);
201 outb(0, dev->iobase + PCL711_MODE);
204 s->async->events |= COMEDI_CB_EOA;
206 comedi_event(dev, s, s->async->events);
210 static void pcl711_set_changain(comedi_device * dev, int chan)
214 outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
216 chan_register=CR_CHAN(chan);
218 if (this_board->is_8112) {
221 * Set the correct channel. The two channel banks are switched
222 * using the mask value.
223 * NB: To use differential channels, you should use mask = 0x30,
224 * but I haven't written the support for this yet. /JJ
227 if (chan_register >= 8){
228 chan_register = 0x20 | (chan_register & 0x7);
230 chan_register |= 0x10;
233 outb(chan_register, dev->iobase + PCL711_MUX);
237 static int pcl711_ai_insn(comedi_device *dev,comedi_subdevice *s,
238 comedi_insn *insn,lsampl_t *data)
243 pcl711_set_changain(dev,insn->chanspec);
245 for(n=0;n<insn->n;n++){
247 * Write the correct mode (software polling) and start polling by writing
248 * to the trigger register
250 outb(1, dev->iobase + PCL711_MODE);
252 if (this_board->is_8112) {
254 outb(0, dev->iobase + PCL711_SOFTTRIG);
259 hi = inb(dev->iobase + PCL711_AD_HI);
260 if (!(hi & PCL711_DRDY))
264 rt_printk("comedi%d: pcl711: A/D timeout\n", dev->minor);
268 lo = inb(dev->iobase + PCL711_AD_LO);
270 data[n] = ((hi & 0xf) << 8) | lo;
276 static int pcl711_ai_cmdtest(comedi_device *dev, comedi_subdevice *s,
284 cmd->start_src &= TRIG_NOW;
285 if(!cmd->start_src || tmp!=cmd->start_src)err++;
287 tmp=cmd->scan_begin_src;
288 cmd->scan_begin_src &= TRIG_TIMER|TRIG_EXT;
289 if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;
291 tmp=cmd->convert_src;
292 cmd->convert_src &= TRIG_NOW;
293 if(!cmd->convert_src || tmp!=cmd->convert_src)err++;
295 tmp=cmd->scan_end_src;
296 cmd->scan_end_src &= TRIG_COUNT;
297 if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++;
300 cmd->stop_src &= TRIG_COUNT|TRIG_NONE;
301 if(!cmd->stop_src || tmp!=cmd->stop_src)err++;
307 if(cmd->scan_begin_src!=TRIG_TIMER &&
308 cmd->scan_begin_src!=TRIG_EXT)err++;
309 if(cmd->stop_src!=TRIG_COUNT &&
310 cmd->stop_src!=TRIG_NONE)err++;
316 if(cmd->start_arg!=0){
320 if(cmd->scan_begin_src==TRIG_EXT){
321 if(cmd->scan_begin_arg!=0){
322 cmd->scan_begin_arg=0;
326 #define MAX_SPEED 1000
327 #define TIMER_BASE 100
328 if(cmd->scan_begin_arg<MAX_SPEED){
329 cmd->scan_begin_arg=MAX_SPEED;
333 if(cmd->convert_arg!=0){
337 if(cmd->scan_end_arg!=cmd->chanlist_len){
338 cmd->scan_end_arg=cmd->chanlist_len;
341 if(cmd->stop_src==TRIG_NONE){
342 if(cmd->stop_arg!=0){
354 if(cmd->scan_begin_src==TRIG_TIMER){
355 tmp = cmd->scan_begin_arg;
356 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
357 &devpriv->divisor1,&devpriv->divisor2,
358 &cmd->scan_begin_arg, cmd->flags&TRIG_ROUND_MASK);
359 if(tmp!=cmd->scan_begin_arg)
368 static int pcl711_ai_cmd(comedi_device *dev, comedi_subdevice *s)
371 comedi_cmd *cmd = &s->async->cmd;
373 pcl711_set_changain(dev,cmd->chanlist[0]);
375 if(cmd->scan_begin_src==TRIG_TIMER){
378 * timer chip is an 8253, with timers 1 and 2
380 * 0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
381 * Mode 2 = Rate generator
383 * 0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
386 i8253_cascade_ns_to_timer(i8253_osc_base,&timer1,&timer2,
387 &cmd->scan_begin_arg,TRIG_ROUND_NEAREST);
389 outb(0x74, dev->iobase + PCL711_CTRCTL);
390 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
391 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
392 outb(0xb4, dev->iobase + PCL711_CTRCTL);
393 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
394 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
396 /* clear pending interrupts (just in case) */
397 outb(0, dev->iobase + PCL711_CLRINTR);
400 * Set mode to IRQ transfer
402 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
404 /* external trigger */
405 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
414 static int pcl711_ao_insn(comedi_device *dev,comedi_subdevice *s,
415 comedi_insn *insn,lsampl_t *data)
418 int chan = CR_CHAN(insn->chanspec);
420 for(n=0;n<insn->n;n++){
421 outb((data[n] & 0xff), dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
422 outb((data[n] >> 8), dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
424 devpriv->ao_readback[chan] = data[n];
430 static int pcl711_ao_insn_read(comedi_device *dev,comedi_subdevice *s,
431 comedi_insn *insn,lsampl_t *data)
434 int chan = CR_CHAN(insn->chanspec);
436 for(n=0;n<insn->n;n++){
437 data[n] = devpriv->ao_readback[chan];
444 /* Digital port read - Untested on 8112 */
445 static int pcl711_di_insn_bits(comedi_device * dev, comedi_subdevice * s,
446 comedi_insn *insn,lsampl_t *data)
448 if(insn->n!=2)return -EINVAL;
450 data[1] = inb(dev->iobase + PCL711_DI_LO) |
451 (inb(dev->iobase + PCL711_DI_HI) << 8);
456 /* Digital port write - Untested on 8112 */
457 static int pcl711_do_insn_bits(comedi_device * dev, comedi_subdevice * s,
458 comedi_insn *insn,lsampl_t *data)
460 if(insn->n!=2)return -EINVAL;
463 s->state &= ~data[0];
464 s->state |= data[0]&data[1];
467 outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
469 outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
476 /* Free any resources that we have claimed */
477 static int pcl711_detach(comedi_device * dev)
479 printk("comedi%d: pcl711: remove\n", dev->minor);
482 comedi_free_irq(dev->irq, dev);
485 release_region(dev->iobase, PCL711_SIZE);
491 static int pcl711_attach(comedi_device * dev, comedi_devconfig * it)
494 unsigned long iobase;
498 /* claim our I/O space */
500 iobase = it->options[0];
501 printk("comedi%d: pcl711: 0x%04lx ", dev->minor, iobase);
502 if (!request_region(iobase, PCL711_SIZE, "pcl711")) {
503 printk("I/O port conflict\n");
506 dev->iobase = iobase;
508 /* there should be a sanity check here */
510 /* set up some name stuff */
511 dev->board_name = this_board->name;
514 irq = it->options[1];
515 if (irq > this_board->maxirq) {
516 printk("irq out of range\n");
520 if (comedi_request_irq(irq, pcl711_interrupt, 0, "pcl711", dev)) {
521 printk("unable to allocate irq %u\n", irq);
524 printk("( irq = %u )\n", irq);
529 if((ret=alloc_subdevices(dev, 4))<0)
531 if((ret=alloc_private(dev,sizeof(pcl711_private)))<0)
534 s = dev->subdevices + 0;
536 s->type = COMEDI_SUBD_AI;
537 s->subdev_flags = SDF_READABLE|SDF_GROUND;
538 s->n_chan = this_board->n_aichan;
541 s->range_table = this_board->ai_range_type;
542 s->insn_read = pcl711_ai_insn;
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);