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@stm.lbl.gov>
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 Dave Andruczyk <dave@tech.buffalostate.edu> also wrote a
29 driver for the PCL-711. I used a few ideas from his driver
30 here. His driver also has more comments, if you are
31 interested in understanding how this driver works.
32 http://tech.buffalostate.edu/~dave/driver/
34 The ACL-8112 driver was hacked from the sources of the PCL-711
35 driver (the 744 chip used on the 8112 is almost the same as
36 the 711b chip, but it has more I/O channels) by
37 Janne Jalkanen (jalkanen@cs.hut.fi) and
38 Erik Bunn (ebu@cs.hut.fi). Remerged with the PCL-711 driver
42 This driver supports both TRIGNOW and TRIGCLK,
43 but does not yet support DMA transfers. It also supports
44 both high (HG) and low (DG) versions of the card, though
45 the HG version has been untested.
49 #include <linux/kernel.h>
50 #include <linux/module.h>
51 #include <linux/sched.h>
53 #include <linux/malloc.h>
54 #include <linux/errno.h>
55 #include <linux/ioport.h>
56 #include <linux/delay.h>
57 #include <linux/interrupt.h>
58 #include <linux/timex.h>
59 #include <linux/timer.h>
61 #include <linux/comedidev.h>
66 #define PCL711_SIZE 16
71 #define PCL711_CTRCTL 3
72 #define PCL711_AD_LO 4
73 #define PCL711_DA0_LO 4
74 #define PCL711_AD_HI 5
75 #define PCL711_DA0_HI 5
76 #define PCL711_DI_LO 6
77 #define PCL711_DA1_LO 6
78 #define PCL711_DI_HI 7
79 #define PCL711_DA1_HI 7
80 #define PCL711_CLRINTR 8
83 #define PCL711_MODE 11
84 #define PCL711_SOFTTRIG 12
85 #define PCL711_DO_LO 13
86 #define PCL711_DO_HI 14
88 static comedi_lrange range_pcl711b_ai = { 5, {
95 static comedi_lrange range_acl8112hg_ai = { 12, {
109 static comedi_lrange range_acl8112dg_ai = { 9, {
127 #define PCL711_TIMEOUT 100
128 #define PCL711_DRDY 0x10
130 static int i8253_osc_base = 500; /* 2 Mhz */
141 comedi_lrange * ai_range_type;
144 static boardtype boardtypes[] =
146 {"pcl711", 0, 0, 0, 5, 8, 1, 0, &range_bipolar5},
147 {"pcl711b", 1, 0, 0, 5, 8, 1, 7, &range_pcl711b_ai},
148 {"acl8112hg", 0, 1, 0, 12, 16, 2, 15, &range_acl8112hg_ai},
149 {"acl8112dg", 0, 1, 1, 9, 16, 2, 15, &range_acl8112dg_ai},
151 #define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
152 #define this_board ((boardtype *)dev->board_ptr)
154 static int pcl711_attach(comedi_device *dev,comedi_devconfig *it);
155 static int pcl711_detach(comedi_device *dev);
156 static comedi_driver driver_pcl711={
157 driver_name: "pcl711",
159 attach: pcl711_attach,
160 detach: pcl711_detach,
161 board_name: boardtypes,
162 num_names: n_boardtypes,
163 offset: sizeof(boardtype),
165 COMEDI_INITCLEANUP(driver_pcl711);
173 lsampl_t ao_readback[2];
174 unsigned int divisor1;
175 unsigned int divisor2;
178 #define devpriv ((pcl711_private *)dev->private)
180 static void pcl711_interrupt(int irq, void *d, struct pt_regs *regs)
184 comedi_device *dev = d;
185 comedi_subdevice *s = dev->subdevices + 0;
187 hi = inb(dev->iobase + PCL711_AD_HI);
188 lo = inb(dev->iobase + PCL711_AD_LO);
189 outb(0, dev->iobase + PCL711_CLRINTR);
191 data = (hi << 8) | lo;
193 if (!(--devpriv->ntrig)) {
194 if (this_board->is_8112) {
195 outb(1, dev->iobase + PCL711_MODE);
197 outb(0, dev->iobase + PCL711_MODE);
204 static void pcl711_set_changain(comedi_device * dev, int chan)
208 outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
210 chan_register=CR_CHAN(chan);
212 if (this_board->is_8112) {
215 * Set the correct channel. The two channel banks are switched
216 * using the mask value.
217 * NB: To use differential channels, you should use mask = 0x30,
218 * but I haven't written the support for this yet. /JJ
221 if (chan_register >= 8){
222 chan_register = 0x20 | (chan_register & 0x7);
224 chan_register |= 0x10;
227 outb(chan_register, dev->iobase + PCL711_MUX);
231 static int pcl711_ai_insn(comedi_device *dev,comedi_subdevice *s,
232 comedi_insn *insn,lsampl_t *data)
237 pcl711_set_changain(dev,insn->chanspec);
240 a sensible precaution to wait for the mux to
241 settle here. is 10us enough?
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))
263 rt_printk("comedi%d: pcl711: A/D timeout\n", dev->minor);
267 lo = inb(dev->iobase + PCL711_AD_LO);
269 data[n] = ((hi & 0xf) << 8) | lo;
275 static int pcl711_ai_cmdtest(comedi_device *dev, comedi_subdevice *s,
283 cmd->start_src &= TRIG_NOW;
284 if(!cmd->start_src || tmp!=cmd->start_src)err++;
286 tmp=cmd->scan_begin_src;
287 cmd->scan_begin_src &= TRIG_TIMER|TRIG_EXT;
288 if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;
290 tmp=cmd->convert_src;
291 cmd->convert_src &= TRIG_NOW;
292 if(!cmd->convert_src || tmp!=cmd->convert_src)err++;
294 tmp=cmd->scan_end_src;
295 cmd->scan_end_src &= TRIG_COUNT;
296 if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++;
299 cmd->stop_src &= TRIG_COUNT|TRIG_NONE;
300 if(!cmd->stop_src || tmp!=cmd->stop_src)err++;
306 if(cmd->scan_begin_src!=TRIG_TIMER &&
307 cmd->scan_begin_src!=TRIG_EXT)err++;
308 if(cmd->stop_src!=TRIG_COUNT &&
309 cmd->stop_src!=TRIG_NONE)err++;
315 if(cmd->start_arg!=0){
319 if(cmd->scan_begin_src==TRIG_EXT){
320 if(cmd->scan_begin_arg!=0){
321 cmd->scan_begin_arg=0;
325 #define MAX_SPEED 1000
326 #define TIMER_BASE 100
327 if(cmd->scan_begin_arg<MAX_SPEED){
328 cmd->scan_begin_arg=MAX_SPEED;
332 if(cmd->convert_arg!=0){
336 if(cmd->scan_end_arg!=cmd->chanlist_len){
337 cmd->scan_end_arg=cmd->chanlist_len;
340 if(cmd->stop_src==TRIG_NONE){
341 if(cmd->stop_arg!=0){
353 if(cmd->scan_begin_src==TRIG_TIMER){
354 tmp = cmd->scan_begin_arg;
355 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
356 &devpriv->divisor1,&devpriv->divisor2,
357 &cmd->scan_begin_arg, cmd->flags&TRIG_ROUND_MASK);
358 if(tmp!=cmd->scan_begin_arg)
367 static int pcl711_ai_cmd(comedi_device *dev, comedi_subdevice *s)
370 comedi_cmd *cmd = &s->async->cmd;
372 pcl711_set_changain(dev,cmd->chanlist[0]);
374 if(cmd->scan_begin_src==TRIG_TIMER){
377 * timer chip is an 8253, with timers 1 and 2
379 * 0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
380 * Mode 2 = Rate generator
382 * 0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
385 i8253_cascade_ns_to_timer(i8253_osc_base,&timer1,&timer2,
386 &cmd->scan_begin_arg,TRIG_ROUND_NEAREST);
388 outb(0x74, dev->iobase + PCL711_CTRCTL);
389 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
390 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
391 outb(0xb4, dev->iobase + PCL711_CTRCTL);
392 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
393 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
395 /* clear pending interrupts (just in case) */
396 outb(0, dev->iobase + PCL711_CLRINTR);
399 * Set mode to IRQ transfer
401 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
403 /* external trigger */
404 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
413 static int pcl711_ao_insn(comedi_device *dev,comedi_subdevice *s,
414 comedi_insn *insn,lsampl_t *data)
417 int chan = CR_CHAN(insn->chanspec);
419 for(n=0;n<insn->n;n++){
420 outb((data[n] & 0xff), dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
421 outb((data[n] >> 8), dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
423 devpriv->ao_readback[chan] = data[n];
429 static int pcl711_ao_insn_read(comedi_device *dev,comedi_subdevice *s,
430 comedi_insn *insn,lsampl_t *data)
433 int chan = CR_CHAN(insn->chanspec);
435 for(n=0;n<insn->n;n++){
436 data[n] = devpriv->ao_readback[chan];
443 /* Digital port read - Untested on 8112 */
444 static int pcl711_di_insn_bits(comedi_device * dev, comedi_subdevice * s,
445 comedi_insn *insn,lsampl_t *data)
447 if(insn->n!=2)return -EINVAL;
449 data[1] = inb(dev->iobase + PCL711_DI_LO) |
450 (inb(dev->iobase + PCL711_DI_HI) << 8);
455 /* Digital port write - Untested on 8112 */
456 static int pcl711_do_insn_bits(comedi_device * dev, comedi_subdevice * s,
457 comedi_insn *insn,lsampl_t *data)
459 if(insn->n!=2)return -EINVAL;
462 s->state &= ~data[0];
463 s->state |= data[0]&data[1];
466 outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
468 outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
475 /* Free any resources that we have claimed */
476 static int pcl711_detach(comedi_device * dev)
478 printk("comedi%d: pcl711: remove\n", dev->minor);
481 free_irq(dev->irq, dev);
484 release_region(dev->iobase, PCL711_SIZE);
490 static int pcl711_attach(comedi_device * dev, comedi_devconfig * it)
497 /* claim our I/O space */
499 iobase = it->options[0];
500 printk("comedi%d: pcl711: 0x%04x ", dev->minor, iobase);
501 if (check_region(iobase, PCL711_SIZE) < 0) {
502 printk("I/O port conflict\n");
505 request_region(iobase, PCL711_SIZE, "pcl711");
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 < 0 || 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 %d\n", irq);
524 printk("( irq = %d )\n", irq);
529 dev->n_subdevices = 4;
530 if((ret=alloc_subdevices(dev))<0)
532 if((ret=alloc_private(dev,sizeof(pcl711_private)))<0)
535 s = dev->subdevices + 0;
537 s->type = COMEDI_SUBD_AI;
538 s->subdev_flags = SDF_READABLE;
539 s->n_chan = this_board->n_aichan;
542 s->range_table = this_board->ai_range_type;
543 s->insn_read = pcl711_ai_insn;
545 s->do_cmdtest = pcl711_ai_cmdtest;
546 s->do_cmd = pcl711_ai_cmd;
551 s->type = COMEDI_SUBD_AO;
552 s->subdev_flags = SDF_WRITEABLE;
553 s->n_chan = this_board->n_aochan;
556 s->range_table = &range_bipolar5;
557 s->insn_write = pcl711_ao_insn;
558 s->insn_read = pcl711_ao_insn_read;
561 /* 16-bit digital input */
562 s->type = COMEDI_SUBD_DI;
563 s->subdev_flags = SDF_READABLE;
566 s->len_chanlist = 16;
567 s->range_table = &range_digital;
568 s->insn_bits = pcl711_di_insn_bits;
571 /* 16-bit digital out */
572 s->type = COMEDI_SUBD_DO;
573 s->subdev_flags = SDF_WRITEABLE;
576 s->len_chanlist = 16;
577 s->range_table = &range_digital;
579 s->insn_bits = pcl711_do_insn_bits;
582 this is the "base value" for the mode register, which is
583 used for the irq on the PCL711
585 if(this_board->is_pcl711b){
586 devpriv->mode=(dev->irq<<4);
590 outb(0, dev->iobase + PCL711_DA0_LO);
591 outb(0, dev->iobase + PCL711_DA0_HI);
592 outb(0, dev->iobase + PCL711_DA1_LO);
593 outb(0, dev->iobase + PCL711_DA1_HI);