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];
176 #define devpriv ((pcl711_private *)dev->private)
178 static void pcl711_interrupt(int irq, void *d, struct pt_regs *regs)
182 comedi_device *dev = d;
183 comedi_subdevice *s = dev->subdevices + 0;
185 hi = inb(dev->iobase + PCL711_AD_HI);
186 lo = inb(dev->iobase + PCL711_AD_LO);
187 outb(0, dev->iobase + PCL711_CLRINTR);
189 data = (hi << 8) | lo;
191 if (!(--devpriv->ntrig)) {
192 if (this_board->is_8112) {
193 outb(1, dev->iobase + PCL711_MODE);
195 outb(0, dev->iobase + PCL711_MODE);
202 static void pcl711_set_changain(comedi_device * dev, int chan)
206 outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
208 chan_register=CR_CHAN(chan);
210 if (this_board->is_8112) {
213 * Set the correct channel. The two channel banks are switched
214 * using the mask value.
215 * NB: To use differential channels, you should use mask = 0x30,
216 * but I haven't written the support for this yet. /JJ
219 if (chan_register >= 8){
220 chan_register = 0x20 | (chan_register & 0x7);
222 chan_register |= 0x10;
225 outb(chan_register, dev->iobase + PCL711_MUX);
229 static int pcl711_ai_insn(comedi_device *dev,comedi_subdevice *s,
230 comedi_insn *insn,lsampl_t *data)
235 pcl711_set_changain(dev,insn->chanspec);
238 a sensible precaution to wait for the mux to
239 settle here. is 10us enough?
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))
261 rt_printk("comedi%d: pcl711: A/D timeout\n", dev->minor);
265 lo = inb(dev->iobase + PCL711_AD_LO);
267 data[n] = ((hi & 0xf) << 8) | lo;
273 #ifdef CONFIG_COMEDI_TRIG
274 static int pcl711_ai_mode4(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
276 if (!this_board->is_pcl711b || dev->irq == 0)
279 pcl711_set_changain(dev,it->chanlist[0]);
282 * Set mode to "no internal trigger"
284 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
291 #ifdef CONFIG_COMEDI_TRIG
292 static int pcl711_ai_mode1(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
299 pcl711_set_changain(dev,it->chanlist[0]);
303 * timer chip is an 8253, with timers 1 and 2
305 * 0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
306 * Mode 2 = Rate generator
308 * 0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
311 i8253_cascade_ns_to_timer(i8253_osc_base,&timer1,&timer2,&it->trigvar,TRIG_ROUND_NEAREST);
313 outb(0x74, dev->iobase + PCL711_CTRCTL);
314 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
315 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
316 outb(0xb4, dev->iobase + PCL711_CTRCTL);
317 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
318 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
320 /* clear pending interrupts (just in case) */
321 outb(0, dev->iobase + PCL711_CLRINTR);
324 * Set mode to IRQ transfer
326 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
335 static int pcl711_ao_insn(comedi_device *dev,comedi_subdevice *s,
336 comedi_insn *insn,lsampl_t *data)
339 int chan = CR_CHAN(insn->chanspec);
341 for(n=0;n<insn->n;n++){
342 outb((data[n] & 0xff), dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
343 outb((data[n] >> 8), dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
345 devpriv->ao_readback[chan] = data[n];
351 static int pcl711_ao_insn_read(comedi_device *dev,comedi_subdevice *s,
352 comedi_insn *insn,lsampl_t *data)
355 int chan = CR_CHAN(insn->chanspec);
357 for(n=0;n<insn->n;n++){
358 data[n] = devpriv->ao_readback[chan];
365 /* Digital port read - Untested on 8112 */
366 static int pcl711_di_insn_bits(comedi_device * dev, comedi_subdevice * s,
367 comedi_insn *insn,lsampl_t *data)
369 if(insn->n!=2)return -EINVAL;
371 data[1] = inb(dev->iobase + PCL711_DI_LO) |
372 (inb(dev->iobase + PCL711_DI_HI) << 8);
377 /* Digital port write - Untested on 8112 */
378 static int pcl711_do_insn_bits(comedi_device * dev, comedi_subdevice * s,
379 comedi_insn *insn,lsampl_t *data)
381 if(insn->n!=2)return -EINVAL;
384 s->state &= ~data[0];
385 s->state |= data[0]&data[1];
388 outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
390 outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
397 /* Free any resources that we have claimed */
398 static int pcl711_detach(comedi_device * dev)
400 printk("comedi%d: pcl711: remove\n", dev->minor);
403 free_irq(dev->irq, dev);
406 release_region(dev->iobase, PCL711_SIZE);
412 static int pcl711_attach(comedi_device * dev, comedi_devconfig * it)
419 /* claim our I/O space */
421 iobase = it->options[0];
422 printk("comedi%d: pcl711: 0x%04x ", dev->minor, iobase);
423 if (check_region(iobase, PCL711_SIZE) < 0) {
424 printk("I/O port conflict\n");
427 request_region(iobase, PCL711_SIZE, "pcl711");
428 dev->iobase = iobase;
430 /* there should be a sanity check here */
432 /* set up some name stuff */
433 dev->board_name = this_board->name;
436 irq = it->options[1];
437 if (irq < 0 || irq > this_board->maxirq) {
438 printk("irq out of range\n");
442 if (comedi_request_irq(irq, pcl711_interrupt, 0, "pcl711", dev)) {
443 printk("unable to allocate irq %d\n", irq);
446 printk("( irq = %d )\n", irq);
451 dev->n_subdevices = 4;
452 if((ret=alloc_subdevices(dev))<0)
454 if((ret=alloc_private(dev,sizeof(pcl711_private)))<0)
457 s = dev->subdevices + 0;
459 s->type = COMEDI_SUBD_AI;
460 s->subdev_flags = SDF_READABLE;
461 s->n_chan = this_board->n_aichan;
464 s->range_table = this_board->ai_range_type;
465 s->insn_read = pcl711_ai_insn;
466 #ifdef CONFIG_COMEDI_TRIG
467 s->trig[1] = pcl711_ai_mode1;
468 s->trig[4] = pcl711_ai_mode4;
473 s->type = COMEDI_SUBD_AO;
474 s->subdev_flags = SDF_WRITEABLE;
475 s->n_chan = this_board->n_aochan;
478 s->range_table = &range_bipolar5;
479 s->insn_write = pcl711_ao_insn;
480 s->insn_read = pcl711_ao_insn_read;
483 /* 16-bit digital input */
484 s->type = COMEDI_SUBD_DI;
485 s->subdev_flags = SDF_READABLE;
488 s->len_chanlist = 16;
489 s->range_table = &range_digital;
490 s->insn_bits = pcl711_di_insn_bits;
493 /* 16-bit digital out */
494 s->type = COMEDI_SUBD_DO;
495 s->subdev_flags = SDF_WRITEABLE;
498 s->len_chanlist = 16;
499 s->range_table = &range_digital;
501 s->insn_bits = pcl711_do_insn_bits;
504 this is the "base value" for the mode register, which is
505 used for the irq on the PCL711
507 if(this_board->is_pcl711b){
508 devpriv->mode=(dev->irq<<4);
512 outb(0, dev->iobase + PCL711_DA0_LO);
513 outb(0, dev->iobase + PCL711_DA0_HI);
514 outb(0, dev->iobase + PCL711_DA1_LO);
515 outb(0, dev->iobase + PCL711_DA1_HI);