This source file includes following definitions.
- multiq3_set_ctrl
- multiq3_ai_status
- multiq3_ai_insn_read
- multiq3_ao_insn_write
- multiq3_di_insn_bits
- multiq3_do_insn_bits
- multiq3_encoder_insn_read
- multiq3_encoder_reset
- multiq3_encoder_insn_config
- multiq3_attach
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
28 #include <linux/module.h>
29
30 #include "../comedidev.h"
31
32
33
34
35 #define MULTIQ3_DI_REG 0x00
36 #define MULTIQ3_DO_REG 0x00
37 #define MULTIQ3_AO_REG 0x02
38 #define MULTIQ3_AI_REG 0x04
39 #define MULTIQ3_AI_CONV_REG 0x04
40 #define MULTIQ3_STATUS_REG 0x06
41 #define MULTIQ3_STATUS_EOC BIT(3)
42 #define MULTIQ3_STATUS_EOC_I BIT(4)
43 #define MULTIQ3_CTRL_REG 0x06
44 #define MULTIQ3_CTRL_AO_CHAN(x) (((x) & 0x7) << 0)
45 #define MULTIQ3_CTRL_RC(x) (((x) & 0x3) << 0)
46 #define MULTIQ3_CTRL_AI_CHAN(x) (((x) & 0x7) << 3)
47 #define MULTIQ3_CTRL_E_CHAN(x) (((x) & 0x7) << 3)
48 #define MULTIQ3_CTRL_EN BIT(6)
49 #define MULTIQ3_CTRL_AZ BIT(7)
50 #define MULTIQ3_CTRL_CAL BIT(8)
51 #define MULTIQ3_CTRL_SH BIT(9)
52 #define MULTIQ3_CTRL_CLK BIT(10)
53 #define MULTIQ3_CTRL_LD (3 << 11)
54 #define MULTIQ3_CLK_REG 0x08
55 #define MULTIQ3_ENC_DATA_REG 0x0c
56 #define MULTIQ3_ENC_CTRL_REG 0x0e
57
58
59
60
61 #define MULTIQ3_CLOCK_DATA 0x00
62 #define MULTIQ3_CLOCK_SETUP 0x18
63 #define MULTIQ3_INPUT_SETUP 0x41
64 #define MULTIQ3_QUAD_X4 0x38
65 #define MULTIQ3_BP_RESET 0x01
66 #define MULTIQ3_CNTR_RESET 0x02
67 #define MULTIQ3_TRSFRPR_CTR 0x08
68 #define MULTIQ3_TRSFRCNTR_OL 0x10
69 #define MULTIQ3_EFLAG_RESET 0x06
70
71 static void multiq3_set_ctrl(struct comedi_device *dev, unsigned int bits)
72 {
73
74
75
76
77 outw(MULTIQ3_CTRL_SH | MULTIQ3_CTRL_CLK | bits,
78 dev->iobase + MULTIQ3_CTRL_REG);
79 }
80
81 static int multiq3_ai_status(struct comedi_device *dev,
82 struct comedi_subdevice *s,
83 struct comedi_insn *insn,
84 unsigned long context)
85 {
86 unsigned int status;
87
88 status = inw(dev->iobase + MULTIQ3_STATUS_REG);
89 if (status & context)
90 return 0;
91 return -EBUSY;
92 }
93
94 static int multiq3_ai_insn_read(struct comedi_device *dev,
95 struct comedi_subdevice *s,
96 struct comedi_insn *insn,
97 unsigned int *data)
98 {
99 unsigned int chan = CR_CHAN(insn->chanspec);
100 unsigned int val;
101 int ret;
102 int i;
103
104 multiq3_set_ctrl(dev, MULTIQ3_CTRL_EN | MULTIQ3_CTRL_AI_CHAN(chan));
105
106 ret = comedi_timeout(dev, s, insn, multiq3_ai_status,
107 MULTIQ3_STATUS_EOC);
108 if (ret)
109 return ret;
110
111 for (i = 0; i < insn->n; i++) {
112 outw(0, dev->iobase + MULTIQ3_AI_CONV_REG);
113
114 ret = comedi_timeout(dev, s, insn, multiq3_ai_status,
115 MULTIQ3_STATUS_EOC_I);
116 if (ret)
117 return ret;
118
119
120 val = inb(dev->iobase + MULTIQ3_AI_REG) << 8;
121 val |= inb(dev->iobase + MULTIQ3_AI_REG);
122 val &= s->maxdata;
123
124
125 data[i] = comedi_offset_munge(s, val);
126 }
127
128 return insn->n;
129 }
130
131 static int multiq3_ao_insn_write(struct comedi_device *dev,
132 struct comedi_subdevice *s,
133 struct comedi_insn *insn,
134 unsigned int *data)
135 {
136 unsigned int chan = CR_CHAN(insn->chanspec);
137 unsigned int val = s->readback[chan];
138 int i;
139
140 for (i = 0; i < insn->n; i++) {
141 val = data[i];
142 multiq3_set_ctrl(dev, MULTIQ3_CTRL_LD |
143 MULTIQ3_CTRL_AO_CHAN(chan));
144 outw(val, dev->iobase + MULTIQ3_AO_REG);
145 multiq3_set_ctrl(dev, 0);
146 }
147 s->readback[chan] = val;
148
149 return insn->n;
150 }
151
152 static int multiq3_di_insn_bits(struct comedi_device *dev,
153 struct comedi_subdevice *s,
154 struct comedi_insn *insn, unsigned int *data)
155 {
156 data[1] = inw(dev->iobase + MULTIQ3_DI_REG);
157
158 return insn->n;
159 }
160
161 static int multiq3_do_insn_bits(struct comedi_device *dev,
162 struct comedi_subdevice *s,
163 struct comedi_insn *insn,
164 unsigned int *data)
165 {
166 if (comedi_dio_update_state(s, data))
167 outw(s->state, dev->iobase + MULTIQ3_DO_REG);
168
169 data[1] = s->state;
170
171 return insn->n;
172 }
173
174 static int multiq3_encoder_insn_read(struct comedi_device *dev,
175 struct comedi_subdevice *s,
176 struct comedi_insn *insn,
177 unsigned int *data)
178 {
179 unsigned int chan = CR_CHAN(insn->chanspec);
180 unsigned int val;
181 int i;
182
183 for (i = 0; i < insn->n; i++) {
184
185 multiq3_set_ctrl(dev, MULTIQ3_CTRL_EN |
186 MULTIQ3_CTRL_E_CHAN(chan));
187
188
189 outb(MULTIQ3_BP_RESET, dev->iobase + MULTIQ3_ENC_CTRL_REG);
190
191
192 outb(MULTIQ3_TRSFRCNTR_OL, dev->iobase + MULTIQ3_ENC_CTRL_REG);
193
194
195 val = inb(dev->iobase + MULTIQ3_ENC_DATA_REG);
196 val |= (inb(dev->iobase + MULTIQ3_ENC_DATA_REG) << 8);
197 val |= (inb(dev->iobase + MULTIQ3_ENC_DATA_REG) << 16);
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214 data[i] = (val + ((s->maxdata + 1) >> 1)) & s->maxdata;
215 }
216
217 return insn->n;
218 }
219
220 static void multiq3_encoder_reset(struct comedi_device *dev,
221 unsigned int chan)
222 {
223 multiq3_set_ctrl(dev, MULTIQ3_CTRL_EN | MULTIQ3_CTRL_E_CHAN(chan));
224 outb(MULTIQ3_EFLAG_RESET, dev->iobase + MULTIQ3_ENC_CTRL_REG);
225 outb(MULTIQ3_BP_RESET, dev->iobase + MULTIQ3_ENC_CTRL_REG);
226 outb(MULTIQ3_CLOCK_DATA, dev->iobase + MULTIQ3_ENC_DATA_REG);
227 outb(MULTIQ3_CLOCK_SETUP, dev->iobase + MULTIQ3_ENC_CTRL_REG);
228 outb(MULTIQ3_INPUT_SETUP, dev->iobase + MULTIQ3_ENC_CTRL_REG);
229 outb(MULTIQ3_QUAD_X4, dev->iobase + MULTIQ3_ENC_CTRL_REG);
230 outb(MULTIQ3_CNTR_RESET, dev->iobase + MULTIQ3_ENC_CTRL_REG);
231 }
232
233 static int multiq3_encoder_insn_config(struct comedi_device *dev,
234 struct comedi_subdevice *s,
235 struct comedi_insn *insn,
236 unsigned int *data)
237 {
238 unsigned int chan = CR_CHAN(insn->chanspec);
239
240 switch (data[0]) {
241 case INSN_CONFIG_RESET:
242 multiq3_encoder_reset(dev, chan);
243 break;
244 default:
245 return -EINVAL;
246 }
247
248 return insn->n;
249 }
250
251 static int multiq3_attach(struct comedi_device *dev,
252 struct comedi_devconfig *it)
253 {
254 struct comedi_subdevice *s;
255 int ret;
256 int i;
257
258 ret = comedi_request_region(dev, it->options[0], 0x10);
259 if (ret)
260 return ret;
261
262 ret = comedi_alloc_subdevices(dev, 5);
263 if (ret)
264 return ret;
265
266
267 s = &dev->subdevices[0];
268 s->type = COMEDI_SUBD_AI;
269 s->subdev_flags = SDF_READABLE | SDF_GROUND;
270 s->n_chan = 8;
271 s->maxdata = 0x1fff;
272 s->range_table = &range_bipolar5;
273 s->insn_read = multiq3_ai_insn_read;
274
275
276 s = &dev->subdevices[1];
277 s->type = COMEDI_SUBD_AO;
278 s->subdev_flags = SDF_WRITABLE;
279 s->n_chan = 8;
280 s->maxdata = 0x0fff;
281 s->range_table = &range_bipolar5;
282 s->insn_write = multiq3_ao_insn_write;
283
284 ret = comedi_alloc_subdev_readback(s);
285 if (ret)
286 return ret;
287
288
289 s = &dev->subdevices[2];
290 s->type = COMEDI_SUBD_DI;
291 s->subdev_flags = SDF_READABLE;
292 s->n_chan = 16;
293 s->maxdata = 1;
294 s->range_table = &range_digital;
295 s->insn_bits = multiq3_di_insn_bits;
296
297
298 s = &dev->subdevices[3];
299 s->type = COMEDI_SUBD_DO;
300 s->subdev_flags = SDF_WRITABLE;
301 s->n_chan = 16;
302 s->maxdata = 1;
303 s->range_table = &range_digital;
304 s->insn_bits = multiq3_do_insn_bits;
305
306
307 s = &dev->subdevices[4];
308 s->type = COMEDI_SUBD_COUNTER;
309 s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
310 s->n_chan = it->options[2] * 2;
311 s->maxdata = 0x00ffffff;
312 s->range_table = &range_unknown;
313 s->insn_read = multiq3_encoder_insn_read;
314 s->insn_config = multiq3_encoder_insn_config;
315
316 for (i = 0; i < s->n_chan; i++)
317 multiq3_encoder_reset(dev, i);
318
319 return 0;
320 }
321
322 static struct comedi_driver multiq3_driver = {
323 .driver_name = "multiq3",
324 .module = THIS_MODULE,
325 .attach = multiq3_attach,
326 .detach = comedi_legacy_detach,
327 };
328 module_comedi_driver(multiq3_driver);
329
330 MODULE_AUTHOR("Comedi http://www.comedi.org");
331 MODULE_DESCRIPTION("Comedi driver for Quanser Consulting MultiQ-3 board");
332 MODULE_LICENSE("GPL");