2 comedi/drivers/pcl711.c
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.
23 Description: Advantech PCL-711 and 711b, ADLink ACL-8112
24 Author: ds, Janne Jalkanen <jalkanen@cs.hut.fi>, Eric Bunn <ebu@cs.hut.fi>
25 Status: mostly complete
26 Devices: [Advantech] PCL-711 (pcl711), PCL-711B (pcl711b),
27 [AdLink] ACL-8112HG (acl8112hg), ACL-8112DG (acl8112dg)
29 Since these boards do not have DMA or FIFOs, only immediate mode is
35 Dave Andruczyk <dave@tech.buffalostate.edu> also wrote a
36 driver for the PCL-711. I used a few ideas from his driver
37 here. His driver also has more comments, if you are
38 interested in understanding how this driver works.
39 http://tech.buffalostate.edu/~dave/driver/
41 The ACL-8112 driver was hacked from the sources of the PCL-711
42 driver (the 744 chip used on the 8112 is almost the same as
43 the 711b chip, but it has more I/O channels) by
44 Janne Jalkanen (jalkanen@cs.hut.fi) and
45 Erik Bunn (ebu@cs.hut.fi). Remerged with the PCL-711 driver
49 This driver supports both TRIGNOW and TRIGCLK,
50 but does not yet support DMA transfers. It also supports
51 both high (HG) and low (DG) versions of the card, though
52 the HG version has been untested.
56 #include <linux/module.h>
57 #include <linux/interrupt.h>
58 #include "../comedidev.h"
60 #include <linux/delay.h>
62 #include "comedi_fc.h"
66 * I/O port register map
68 #define PCL711_CTR0 0x00
69 #define PCL711_CTR1 0x01
70 #define PCL711_CTR2 0x02
71 #define PCL711_CTRCTL 0x03
72 #define PCL711_AI_LSB_REG 0x04
73 #define PCL711_AI_MSB_REG 0x05
74 #define PCL711_AI_MSB_DRDY (1 << 4)
75 #define PCL711_AO_LSB_REG(x) (0x04 + ((x) * 2))
76 #define PCL711_AO_MSB_REG(x) (0x05 + ((x) * 2))
77 #define PCL711_DI_LO 0x06
78 #define PCL711_DI_HI 0x07
79 #define PCL711_CLRINTR 0x08
80 #define PCL711_GAIN 0x09
81 #define PCL711_MUX_REG 0x0a
82 #define PCL711_MUX_CHAN(x) (((x) & 0xf) << 0)
83 #define PCL711_MUX_CS0 (1 << 4)
84 #define PCL711_MUX_CS1 (1 << 5)
85 #define PCL711_MUX_DIFF (PCL711_MUX_CS0 | PCL711_MUX_CS1)
86 #define PCL711_MODE_REG 0x0b
87 #define PCL711_MODE_DEFAULT (0 << 0)
88 #define PCL711_MODE_SOFTTRIG (1 << 0)
89 #define PCL711_MODE_EXT (2 << 0)
90 #define PCL711_MODE_EXT_IRQ (3 << 0)
91 #define PCL711_MODE_PACER (4 << 0)
92 #define PCL711_MODE_PACER_IRQ (6 << 0)
93 #define PCL711_MODE_IRQ(x) (((x) & 0x7) << 4)
94 #define PCL711_SOFTTRIG_REG 0x0c
95 #define PCL711_SOFTTRIG (0 << 0) /* any value will work */
96 #define PCL711_DO_LO 0x0d
97 #define PCL711_DO_HI 0x0e
99 static const struct comedi_lrange range_pcl711b_ai = {
109 static const struct comedi_lrange range_acl8112hg_ai = {
126 static const struct comedi_lrange range_acl8112dg_ai = {
140 static const int i8253_osc_base = 500; /* 2 Mhz */
142 struct pcl711_board {
147 const struct comedi_lrange *ai_range_type;
150 static const struct pcl711_board boardtypes[] = {
155 .ai_range_type = &range_bipolar5,
161 .ai_range_type = &range_pcl711b_ai,
167 .ai_range_type = &range_acl8112hg_ai,
173 .ai_range_type = &range_acl8112dg_ai,
177 struct pcl711_private {
179 unsigned int ao_readback[2];
180 unsigned int divisor1;
181 unsigned int divisor2;
184 static void pcl711_ai_set_mode(struct comedi_device *dev, unsigned int mode)
187 * The pcl711b board uses bits in the mode register to select the
188 * interrupt. The other boards supported by this driver all use
189 * jumpers on the board.
191 * Enables the interrupt when needed on the pcl711b board. These
192 * bits do nothing on the other boards.
194 if (mode == PCL711_MODE_EXT_IRQ || mode == PCL711_MODE_PACER_IRQ)
195 mode |= PCL711_MODE_IRQ(dev->irq);
197 outb(mode, dev->iobase + PCL711_MODE_REG);
200 static unsigned int pcl711_ai_get_sample(struct comedi_device *dev,
201 struct comedi_subdevice *s)
205 val = inb(dev->iobase + PCL711_AI_MSB_REG) << 8;
206 val |= inb(dev->iobase + PCL711_AI_LSB_REG);
208 return val & s->maxdata;
211 static irqreturn_t pcl711_interrupt(int irq, void *d)
213 struct comedi_device *dev = d;
214 struct pcl711_private *devpriv = dev->private;
215 struct comedi_subdevice *s = dev->read_subdev;
218 if (!dev->attached) {
219 comedi_error(dev, "spurious interrupt");
223 data = pcl711_ai_get_sample(dev, s);
225 outb(0, dev->iobase + PCL711_CLRINTR);
227 /* FIXME! Nothing else sets ntrig! */
228 if (!(--devpriv->ntrig)) {
229 pcl711_ai_set_mode(dev, PCL711_MODE_SOFTTRIG);
231 s->async->events |= COMEDI_CB_EOA;
233 comedi_event(dev, s);
237 static void pcl711_set_changain(struct comedi_device *dev,
238 struct comedi_subdevice *s,
239 unsigned int chanspec)
241 unsigned int chan = CR_CHAN(chanspec);
242 unsigned int range = CR_RANGE(chanspec);
243 unsigned int aref = CR_AREF(chanspec);
244 unsigned int mux = 0;
246 outb(range, dev->iobase + PCL711_GAIN);
249 /* Select the correct MPC508A chip */
250 if (aref == AREF_DIFF) {
252 mux |= PCL711_MUX_DIFF;
255 mux |= PCL711_MUX_CS0;
257 mux |= PCL711_MUX_CS1;
260 outb(mux | PCL711_MUX_CHAN(chan), dev->iobase + PCL711_MUX_REG);
263 static int pcl711_ai_wait_for_eoc(struct comedi_device *dev,
264 unsigned int timeout)
269 msb = inb(dev->iobase + PCL711_AI_MSB_REG);
270 if ((msb & PCL711_AI_MSB_DRDY) == 0)
277 static int pcl711_ai_insn_read(struct comedi_device *dev,
278 struct comedi_subdevice *s,
279 struct comedi_insn *insn,
285 pcl711_set_changain(dev, s, insn->chanspec);
287 pcl711_ai_set_mode(dev, PCL711_MODE_SOFTTRIG);
289 for (i = 0; i < insn->n; i++) {
290 outb(PCL711_SOFTTRIG, dev->iobase + PCL711_SOFTTRIG_REG);
292 ret = pcl711_ai_wait_for_eoc(dev, 100);
296 data[i] = pcl711_ai_get_sample(dev, s);
302 static int pcl711_ai_cmdtest(struct comedi_device *dev,
303 struct comedi_subdevice *s, struct comedi_cmd *cmd)
305 struct pcl711_private *devpriv = dev->private;
309 /* Step 1 : check if triggers are trivially valid */
311 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
312 err |= cfc_check_trigger_src(&cmd->scan_begin_src,
313 TRIG_TIMER | TRIG_EXT);
314 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
315 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
316 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
321 /* Step 2a : make sure trigger sources are unique */
323 err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
324 err |= cfc_check_trigger_is_unique(cmd->stop_src);
326 /* Step 2b : and mutually compatible */
331 /* Step 3: check if arguments are trivially valid */
333 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
335 if (cmd->scan_begin_src == TRIG_EXT) {
336 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
338 #define MAX_SPEED 1000
339 #define TIMER_BASE 100
340 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
344 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
345 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
347 if (cmd->stop_src == TRIG_NONE) {
348 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
358 if (cmd->scan_begin_src == TRIG_TIMER) {
359 tmp = cmd->scan_begin_arg;
360 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
363 &cmd->scan_begin_arg,
364 cmd->flags & TRIG_ROUND_MASK);
365 if (tmp != cmd->scan_begin_arg)
375 static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
378 struct comedi_cmd *cmd = &s->async->cmd;
380 pcl711_set_changain(dev, s, cmd->chanlist[0]);
382 if (cmd->scan_begin_src == TRIG_TIMER) {
385 * timer chip is an 8253, with timers 1 and 2
387 * 0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
388 * Mode 2 = Rate generator
390 * 0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
394 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
395 &cmd->scan_begin_arg,
398 outb(0x74, dev->iobase + PCL711_CTRCTL);
399 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
400 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
401 outb(0xb4, dev->iobase + PCL711_CTRCTL);
402 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
403 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
405 /* clear pending interrupts (just in case) */
406 outb(0, dev->iobase + PCL711_CLRINTR);
408 pcl711_ai_set_mode(dev, PCL711_MODE_PACER_IRQ);
410 pcl711_ai_set_mode(dev, PCL711_MODE_EXT_IRQ);
416 static void pcl711_ao_write(struct comedi_device *dev,
417 unsigned int chan, unsigned int val)
419 outb(val & 0xff, dev->iobase + PCL711_AO_LSB_REG(chan));
420 outb((val >> 8) & 0xff, dev->iobase + PCL711_AO_MSB_REG(chan));
423 static int pcl711_ao_insn_write(struct comedi_device *dev,
424 struct comedi_subdevice *s,
425 struct comedi_insn *insn,
428 struct pcl711_private *devpriv = dev->private;
429 unsigned int chan = CR_CHAN(insn->chanspec);
430 unsigned int val = devpriv->ao_readback[chan];
433 for (i = 0; i < insn->n; i++) {
435 pcl711_ao_write(dev, chan, val);
437 devpriv->ao_readback[chan] = val;
442 static int pcl711_ao_insn_read(struct comedi_device *dev,
443 struct comedi_subdevice *s,
444 struct comedi_insn *insn,
447 struct pcl711_private *devpriv = dev->private;
448 unsigned int chan = CR_CHAN(insn->chanspec);
451 for (i = 0; i < insn->n; i++)
452 data[i] = devpriv->ao_readback[chan];
457 /* Digital port read - Untested on 8112 */
458 static int pcl711_di_insn_bits(struct comedi_device *dev,
459 struct comedi_subdevice *s,
460 struct comedi_insn *insn, unsigned int *data)
462 data[1] = inb(dev->iobase + PCL711_DI_LO) |
463 (inb(dev->iobase + PCL711_DI_HI) << 8);
468 static int pcl711_do_insn_bits(struct comedi_device *dev,
469 struct comedi_subdevice *s,
470 struct comedi_insn *insn,
475 mask = comedi_dio_update_state(s, data);
478 outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
480 outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
488 static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
490 const struct pcl711_board *board = comedi_board(dev);
491 struct pcl711_private *devpriv;
492 struct comedi_subdevice *s;
495 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
499 ret = comedi_request_region(dev, it->options[0], 0x10);
503 if (it->options[1] && it->options[1] <= board->maxirq) {
504 ret = request_irq(it->options[1], pcl711_interrupt, 0,
505 dev->board_name, dev);
507 dev->irq = it->options[1];
510 ret = comedi_alloc_subdevices(dev, 4);
514 /* Analog Input subdevice */
515 s = &dev->subdevices[0];
516 s->type = COMEDI_SUBD_AI;
517 s->subdev_flags = SDF_READABLE | SDF_GROUND;
518 if (board->n_aichan > 8)
519 s->subdev_flags |= SDF_DIFF;
520 s->n_chan = board->n_aichan;
522 s->range_table = board->ai_range_type;
523 s->insn_read = pcl711_ai_insn_read;
525 dev->read_subdev = s;
526 s->subdev_flags |= SDF_CMD_READ;
528 s->do_cmdtest = pcl711_ai_cmdtest;
529 s->do_cmd = pcl711_ai_cmd;
532 /* Analog Output subdevice */
533 s = &dev->subdevices[1];
534 s->type = COMEDI_SUBD_AO;
535 s->subdev_flags = SDF_WRITABLE;
536 s->n_chan = board->n_aochan;
538 s->range_table = &range_bipolar5;
539 s->insn_write = pcl711_ao_insn_write;
540 s->insn_read = pcl711_ao_insn_read;
542 /* Digital Input subdevice */
543 s = &dev->subdevices[2];
544 s->type = COMEDI_SUBD_DI;
545 s->subdev_flags = SDF_READABLE;
548 s->range_table = &range_digital;
549 s->insn_bits = pcl711_di_insn_bits;
551 /* Digital Output subdevice */
552 s = &dev->subdevices[3];
553 s->type = COMEDI_SUBD_DO;
554 s->subdev_flags = SDF_WRITABLE;
557 s->range_table = &range_digital;
558 s->insn_bits = pcl711_do_insn_bits;
561 pcl711_ao_write(dev, 0, 0x0);
562 pcl711_ao_write(dev, 1, 0x0);
567 static struct comedi_driver pcl711_driver = {
568 .driver_name = "pcl711",
569 .module = THIS_MODULE,
570 .attach = pcl711_attach,
571 .detach = comedi_legacy_detach,
572 .board_name = &boardtypes[0].name,
573 .num_names = ARRAY_SIZE(boardtypes),
574 .offset = sizeof(struct pcl711_board),
576 module_comedi_driver(pcl711_driver);
578 MODULE_AUTHOR("Comedi http://www.comedi.org");
579 MODULE_DESCRIPTION("Comedi low-level driver");
580 MODULE_LICENSE("GPL");