1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27#include <linux/module.h>
28#include <linux/interrupt.h>
29#include "../comedidev.h"
30
31#define MULTIQ3_SIZE 16
32
33
34
35
36#define MULTIQ3_DIGIN_PORT 0
37#define MULTIQ3_DIGOUT_PORT 0
38#define MULTIQ3_DAC_DATA 2
39#define MULTIQ3_AD_DATA 4
40#define MULTIQ3_AD_CS 4
41#define MULTIQ3_STATUS 6
42#define MULTIQ3_CONTROL 6
43#define MULTIQ3_CLK_DATA 8
44#define MULTIQ3_ENC_DATA 12
45#define MULTIQ3_ENC_CONTROL 14
46
47
48
49
50#define MULTIQ3_AD_MUX_EN 0x0040
51#define MULTIQ3_AD_AUTOZ 0x0080
52#define MULTIQ3_AD_AUTOCAL 0x0100
53#define MULTIQ3_AD_SH 0x0200
54#define MULTIQ3_AD_CLOCK_4M 0x0400
55#define MULTIQ3_DA_LOAD 0x1800
56
57#define MULTIQ3_CONTROL_MUST 0x0600
58
59
60
61
62#define MULTIQ3_STATUS_EOC 0x008
63#define MULTIQ3_STATUS_EOC_I 0x010
64
65
66
67
68#define MULTIQ3_CLOCK_DATA 0x00
69#define MULTIQ3_CLOCK_SETUP 0x18
70#define MULTIQ3_INPUT_SETUP 0x41
71#define MULTIQ3_QUAD_X4 0x38
72#define MULTIQ3_BP_RESET 0x01
73#define MULTIQ3_CNTR_RESET 0x02
74#define MULTIQ3_TRSFRPR_CTR 0x08
75#define MULTIQ3_TRSFRCNTR_OL 0x10
76#define MULTIQ3_EFLAG_RESET 0x06
77
78#define MULTIQ3_TIMEOUT 30
79
80struct multiq3_private {
81 unsigned int ao_readback[2];
82};
83
84static int multiq3_ai_status(struct comedi_device *dev,
85 struct comedi_subdevice *s,
86 struct comedi_insn *insn,
87 unsigned long context)
88{
89 unsigned int status;
90
91 status = inw(dev->iobase + MULTIQ3_STATUS);
92 if (status & context)
93 return 0;
94 return -EBUSY;
95}
96
97static int multiq3_ai_insn_read(struct comedi_device *dev,
98 struct comedi_subdevice *s,
99 struct comedi_insn *insn, unsigned int *data)
100{
101 int n;
102 int chan;
103 unsigned int hi, lo;
104 int ret;
105
106 chan = CR_CHAN(insn->chanspec);
107 outw(MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3),
108 dev->iobase + MULTIQ3_CONTROL);
109
110 ret = comedi_timeout(dev, s, insn, multiq3_ai_status,
111 MULTIQ3_STATUS_EOC);
112 if (ret)
113 return ret;
114
115 for (n = 0; n < insn->n; n++) {
116 outw(0, dev->iobase + MULTIQ3_AD_CS);
117
118 ret = comedi_timeout(dev, s, insn, multiq3_ai_status,
119 MULTIQ3_STATUS_EOC_I);
120 if (ret)
121 return ret;
122
123 hi = inb(dev->iobase + MULTIQ3_AD_CS);
124 lo = inb(dev->iobase + MULTIQ3_AD_CS);
125 data[n] = (((hi << 8) | lo) + 0x1000) & 0x1fff;
126 }
127
128 return n;
129}
130
131static int multiq3_ao_insn_read(struct comedi_device *dev,
132 struct comedi_subdevice *s,
133 struct comedi_insn *insn, unsigned int *data)
134{
135 struct multiq3_private *devpriv = dev->private;
136 int i;
137 int chan = CR_CHAN(insn->chanspec);
138
139 for (i = 0; i < insn->n; i++)
140 data[i] = devpriv->ao_readback[chan];
141
142 return i;
143}
144
145static int multiq3_ao_insn_write(struct comedi_device *dev,
146 struct comedi_subdevice *s,
147 struct comedi_insn *insn, unsigned int *data)
148{
149 struct multiq3_private *devpriv = dev->private;
150 int i;
151 int chan = CR_CHAN(insn->chanspec);
152
153 for (i = 0; i < insn->n; i++) {
154 outw(MULTIQ3_CONTROL_MUST | MULTIQ3_DA_LOAD | chan,
155 dev->iobase + MULTIQ3_CONTROL);
156 outw(data[i], dev->iobase + MULTIQ3_DAC_DATA);
157 outw(MULTIQ3_CONTROL_MUST, dev->iobase + MULTIQ3_CONTROL);
158
159 devpriv->ao_readback[chan] = data[i];
160 }
161
162 return i;
163}
164
165static int multiq3_di_insn_bits(struct comedi_device *dev,
166 struct comedi_subdevice *s,
167 struct comedi_insn *insn, unsigned int *data)
168{
169 data[1] = inw(dev->iobase + MULTIQ3_DIGIN_PORT);
170
171 return insn->n;
172}
173
174static int multiq3_do_insn_bits(struct comedi_device *dev,
175 struct comedi_subdevice *s,
176 struct comedi_insn *insn,
177 unsigned int *data)
178{
179 if (comedi_dio_update_state(s, data))
180 outw(s->state, dev->iobase + MULTIQ3_DIGOUT_PORT);
181
182 data[1] = s->state;
183
184 return insn->n;
185}
186
187static int multiq3_encoder_insn_read(struct comedi_device *dev,
188 struct comedi_subdevice *s,
189 struct comedi_insn *insn,
190 unsigned int *data)
191{
192 int n;
193 int chan = CR_CHAN(insn->chanspec);
194 int control = MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3);
195
196 for (n = 0; n < insn->n; n++) {
197 int value;
198 outw(control, dev->iobase + MULTIQ3_CONTROL);
199 outb(MULTIQ3_BP_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
200 outb(MULTIQ3_TRSFRCNTR_OL, dev->iobase + MULTIQ3_ENC_CONTROL);
201 value = inb(dev->iobase + MULTIQ3_ENC_DATA);
202 value |= (inb(dev->iobase + MULTIQ3_ENC_DATA) << 8);
203 value |= (inb(dev->iobase + MULTIQ3_ENC_DATA) << 16);
204 data[n] = (value + 0x800000) & 0xffffff;
205 }
206
207 return n;
208}
209
210static void encoder_reset(struct comedi_device *dev)
211{
212 struct comedi_subdevice *s = &dev->subdevices[4];
213 int chan;
214
215 for (chan = 0; chan < s->n_chan; chan++) {
216 int control =
217 MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3);
218 outw(control, dev->iobase + MULTIQ3_CONTROL);
219 outb(MULTIQ3_EFLAG_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
220 outb(MULTIQ3_BP_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
221 outb(MULTIQ3_CLOCK_DATA, dev->iobase + MULTIQ3_ENC_DATA);
222 outb(MULTIQ3_CLOCK_SETUP, dev->iobase + MULTIQ3_ENC_CONTROL);
223 outb(MULTIQ3_INPUT_SETUP, dev->iobase + MULTIQ3_ENC_CONTROL);
224 outb(MULTIQ3_QUAD_X4, dev->iobase + MULTIQ3_ENC_CONTROL);
225 outb(MULTIQ3_CNTR_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
226 }
227}
228
229static int multiq3_attach(struct comedi_device *dev,
230 struct comedi_devconfig *it)
231{
232 struct multiq3_private *devpriv;
233 struct comedi_subdevice *s;
234 int ret;
235
236 ret = comedi_request_region(dev, it->options[0], MULTIQ3_SIZE);
237 if (ret)
238 return ret;
239
240 ret = comedi_alloc_subdevices(dev, 5);
241 if (ret)
242 return ret;
243
244 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
245 if (!devpriv)
246 return -ENOMEM;
247
248 s = &dev->subdevices[0];
249
250 s->type = COMEDI_SUBD_AI;
251 s->subdev_flags = SDF_READABLE | SDF_GROUND;
252 s->n_chan = 8;
253 s->insn_read = multiq3_ai_insn_read;
254 s->maxdata = 0x1fff;
255 s->range_table = &range_bipolar5;
256
257 s = &dev->subdevices[1];
258
259 s->type = COMEDI_SUBD_AO;
260 s->subdev_flags = SDF_WRITABLE;
261 s->n_chan = 8;
262 s->insn_read = multiq3_ao_insn_read;
263 s->insn_write = multiq3_ao_insn_write;
264 s->maxdata = 0xfff;
265 s->range_table = &range_bipolar5;
266
267 s = &dev->subdevices[2];
268
269 s->type = COMEDI_SUBD_DI;
270 s->subdev_flags = SDF_READABLE;
271 s->n_chan = 16;
272 s->insn_bits = multiq3_di_insn_bits;
273 s->maxdata = 1;
274 s->range_table = &range_digital;
275
276 s = &dev->subdevices[3];
277
278 s->type = COMEDI_SUBD_DO;
279 s->subdev_flags = SDF_WRITABLE;
280 s->n_chan = 16;
281 s->insn_bits = multiq3_do_insn_bits;
282 s->maxdata = 1;
283 s->range_table = &range_digital;
284 s->state = 0;
285
286 s = &dev->subdevices[4];
287
288 s->type = COMEDI_SUBD_COUNTER;
289 s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
290 s->n_chan = it->options[2] * 2;
291 s->insn_read = multiq3_encoder_insn_read;
292 s->maxdata = 0xffffff;
293 s->range_table = &range_unknown;
294
295 encoder_reset(dev);
296
297 return 0;
298}
299
300static struct comedi_driver multiq3_driver = {
301 .driver_name = "multiq3",
302 .module = THIS_MODULE,
303 .attach = multiq3_attach,
304 .detach = comedi_legacy_detach,
305};
306module_comedi_driver(multiq3_driver);
307
308MODULE_AUTHOR("Comedi http://www.comedi.org");
309MODULE_DESCRIPTION("Comedi low-level driver");
310MODULE_LICENSE("GPL");
311