1 // SPDX-License-Identifier: GPL-2.0+
4 * Driver for MicroAxial ADQ12-B data acquisition and control card
5 * written by jeremy theler <thelerg@ib.cnea.gov.ar>
7 * commission nacional de energia atomica
8 * universidad nacional de cuyo
11 * COMEDI - Linux Control and Measurement Device Interface
12 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
17 * Description: Driver for MicroAxial ADQ12-B data acquisition and control card
18 * Devices: [MicroAxial] ADQ12-B (adq12b)
19 * Author: jeremy theler <thelerg@ib.cnea.gov.ar>
20 * Updated: Thu, 21 Feb 2008 02:56:27 -0300
23 * Configuration options:
24 * [0] - I/O base address (set with hardware jumpers)
26 * 0x300 1 (factory default)
32 * [1] - Analog Input unipolar/bipolar selection
33 * selection option JUB
34 * bipolar 0 2-3 (factory default)
36 * [2] - Analog Input single-ended/differential selection
37 * selection option JCHA JCHB
38 * single-ended 0 1-2 1-2 (factory default)
39 * differential 1 2-3 2-3
41 * Driver for the acquisition card ADQ12-B (without any add-on).
43 * - Analog input is subdevice 0 (16 channels single-ended or 8 differential)
44 * - Digital input is subdevice 1 (5 channels)
45 * - Digital output is subdevice 1 (8 channels)
46 * - The PACER is not supported in this version
49 #include <linux/module.h>
50 #include <linux/delay.h>
52 #include "../comedidev.h"
54 /* address scheme (page 2.17 of the manual) */
55 #define ADQ12B_CTREG 0x00
56 #define ADQ12B_CTREG_MSKP BIT(7) /* enable pacer interrupt */
57 #define ADQ12B_CTREG_GTP BIT(6) /* enable pacer */
58 #define ADQ12B_CTREG_RANGE(x) ((x) << 4)
59 #define ADQ12B_CTREG_CHAN(x) ((x) << 0)
60 #define ADQ12B_STINR 0x00
61 #define ADQ12B_STINR_OUT2 BIT(7) /* timer 2 output state */
62 #define ADQ12B_STINR_OUTP BIT(6) /* pacer output state */
63 #define ADQ12B_STINR_EOC BIT(5) /* A/D end-of-conversion */
64 #define ADQ12B_STINR_IN_MASK (0x1f << 0)
65 #define ADQ12B_OUTBR 0x04
66 #define ADQ12B_ADLOW 0x08
67 #define ADQ12B_ADHIG 0x09
68 #define ADQ12B_TIMER_BASE 0x0c
70 /* available ranges through the PGA gains */
71 static const struct comedi_lrange range_adq12b_ai_bipolar
= {
80 static const struct comedi_lrange range_adq12b_ai_unipolar
= {
89 struct adq12b_private
{
90 unsigned int last_ctreg
;
93 static int adq12b_ai_eoc(struct comedi_device
*dev
,
94 struct comedi_subdevice
*s
,
95 struct comedi_insn
*insn
,
96 unsigned long context
)
100 status
= inb(dev
->iobase
+ ADQ12B_STINR
);
101 if (status
& ADQ12B_STINR_EOC
)
106 static int adq12b_ai_insn_read(struct comedi_device
*dev
,
107 struct comedi_subdevice
*s
,
108 struct comedi_insn
*insn
,
111 struct adq12b_private
*devpriv
= dev
->private;
112 unsigned int chan
= CR_CHAN(insn
->chanspec
);
113 unsigned int range
= CR_RANGE(insn
->chanspec
);
118 /* change channel and range only if it is different from the previous */
119 val
= ADQ12B_CTREG_RANGE(range
) | ADQ12B_CTREG_CHAN(chan
);
120 if (val
!= devpriv
->last_ctreg
) {
121 outb(val
, dev
->iobase
+ ADQ12B_CTREG
);
122 devpriv
->last_ctreg
= val
;
123 usleep_range(50, 100); /* wait for the mux to settle */
126 val
= inb(dev
->iobase
+ ADQ12B_ADLOW
); /* trigger A/D */
128 for (i
= 0; i
< insn
->n
; i
++) {
129 ret
= comedi_timeout(dev
, s
, insn
, adq12b_ai_eoc
, 0);
133 val
= inb(dev
->iobase
+ ADQ12B_ADHIG
) << 8;
134 val
|= inb(dev
->iobase
+ ADQ12B_ADLOW
); /* retriggers A/D */
142 static int adq12b_di_insn_bits(struct comedi_device
*dev
,
143 struct comedi_subdevice
*s
,
144 struct comedi_insn
*insn
, unsigned int *data
)
146 /* only bits 0-4 have information about digital inputs */
147 data
[1] = (inb(dev
->iobase
+ ADQ12B_STINR
) & ADQ12B_STINR_IN_MASK
);
152 static int adq12b_do_insn_bits(struct comedi_device
*dev
,
153 struct comedi_subdevice
*s
,
154 struct comedi_insn
*insn
,
161 mask
= comedi_dio_update_state(s
, data
);
163 for (chan
= 0; chan
< 8; chan
++) {
164 if ((mask
>> chan
) & 0x01) {
165 val
= (s
->state
>> chan
) & 0x01;
166 outb((val
<< 3) | chan
,
167 dev
->iobase
+ ADQ12B_OUTBR
);
177 static int adq12b_attach(struct comedi_device
*dev
, struct comedi_devconfig
*it
)
179 struct adq12b_private
*devpriv
;
180 struct comedi_subdevice
*s
;
183 ret
= comedi_request_region(dev
, it
->options
[0], 0x10);
187 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
191 devpriv
->last_ctreg
= -1; /* force ctreg update */
193 ret
= comedi_alloc_subdevices(dev
, 3);
197 /* Analog Input subdevice */
198 s
= &dev
->subdevices
[0];
199 s
->type
= COMEDI_SUBD_AI
;
200 if (it
->options
[2]) {
201 s
->subdev_flags
= SDF_READABLE
| SDF_DIFF
;
204 s
->subdev_flags
= SDF_READABLE
| SDF_GROUND
;
208 s
->range_table
= it
->options
[1] ? &range_adq12b_ai_unipolar
209 : &range_adq12b_ai_bipolar
;
210 s
->insn_read
= adq12b_ai_insn_read
;
212 /* Digital Input subdevice */
213 s
= &dev
->subdevices
[1];
214 s
->type
= COMEDI_SUBD_DI
;
215 s
->subdev_flags
= SDF_READABLE
;
218 s
->range_table
= &range_digital
;
219 s
->insn_bits
= adq12b_di_insn_bits
;
221 /* Digital Output subdevice */
222 s
= &dev
->subdevices
[2];
223 s
->type
= COMEDI_SUBD_DO
;
224 s
->subdev_flags
= SDF_WRITABLE
;
227 s
->range_table
= &range_digital
;
228 s
->insn_bits
= adq12b_do_insn_bits
;
233 static struct comedi_driver adq12b_driver
= {
234 .driver_name
= "adq12b",
235 .module
= THIS_MODULE
,
236 .attach
= adq12b_attach
,
237 .detach
= comedi_legacy_detach
,
239 module_comedi_driver(adq12b_driver
);
241 MODULE_AUTHOR("Comedi http://www.comedi.org");
242 MODULE_DESCRIPTION("Comedi low-level driver");
243 MODULE_LICENSE("GPL");