1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * me4000.c |
4 | * Source code for the Meilhaus ME-4000 board family. |
5 | * |
6 | * COMEDI - Linux Control and Measurement Device Interface |
7 | * Copyright (C) 2000 David A. Schleef <ds@schleef.org> |
8 | */ |
9 | |
10 | /* |
11 | * Driver: me4000 |
12 | * Description: Meilhaus ME-4000 series boards |
13 | * Devices: [Meilhaus] ME-4650 (me4000), ME-4670i, ME-4680, ME-4680i, |
14 | * ME-4680is |
15 | * Author: gg (Guenter Gebhardt <g.gebhardt@meilhaus.com>) |
16 | * Updated: Mon, 18 Mar 2002 15:34:01 -0800 |
17 | * Status: untested |
18 | * |
19 | * Supports: |
20 | * - Analog Input |
21 | * - Analog Output |
22 | * - Digital I/O |
23 | * - Counter |
24 | * |
25 | * Configuration Options: not applicable, uses PCI auto config |
26 | * |
27 | * The firmware required by these boards is available in the |
28 | * comedi_nonfree_firmware tarball available from |
29 | * https://www.comedi.org. |
30 | */ |
31 | |
32 | #include <linux/module.h> |
33 | #include <linux/delay.h> |
34 | #include <linux/interrupt.h> |
35 | #include <linux/comedi/comedi_pci.h> |
36 | #include <linux/comedi/comedi_8254.h> |
37 | |
38 | #include "plx9052.h" |
39 | |
40 | #define ME4000_FIRMWARE "me4000_firmware.bin" |
41 | |
42 | /* |
43 | * ME4000 Register map and bit defines |
44 | */ |
45 | #define ME4000_AO_CHAN(x) ((x) * 0x18) |
46 | |
47 | #define ME4000_AO_CTRL_REG(x) (0x00 + ME4000_AO_CHAN(x)) |
48 | #define ME4000_AO_CTRL_MODE_0 BIT(0) |
49 | #define ME4000_AO_CTRL_MODE_1 BIT(1) |
50 | #define ME4000_AO_CTRL_STOP BIT(2) |
51 | #define ME4000_AO_CTRL_ENABLE_FIFO BIT(3) |
52 | #define ME4000_AO_CTRL_ENABLE_EX_TRIG BIT(4) |
53 | #define ME4000_AO_CTRL_EX_TRIG_EDGE BIT(5) |
54 | #define ME4000_AO_CTRL_IMMEDIATE_STOP BIT(7) |
55 | #define ME4000_AO_CTRL_ENABLE_DO BIT(8) |
56 | #define ME4000_AO_CTRL_ENABLE_IRQ BIT(9) |
57 | #define ME4000_AO_CTRL_RESET_IRQ BIT(10) |
58 | #define ME4000_AO_STATUS_REG(x) (0x04 + ME4000_AO_CHAN(x)) |
59 | #define ME4000_AO_STATUS_FSM BIT(0) |
60 | #define ME4000_AO_STATUS_FF BIT(1) |
61 | #define ME4000_AO_STATUS_HF BIT(2) |
62 | #define ME4000_AO_STATUS_EF BIT(3) |
63 | #define ME4000_AO_FIFO_REG(x) (0x08 + ME4000_AO_CHAN(x)) |
64 | #define ME4000_AO_SINGLE_REG(x) (0x0c + ME4000_AO_CHAN(x)) |
65 | #define ME4000_AO_TIMER_REG(x) (0x10 + ME4000_AO_CHAN(x)) |
66 | #define ME4000_AI_CTRL_REG 0x74 |
67 | #define ME4000_AI_STATUS_REG 0x74 |
68 | #define ME4000_AI_CTRL_MODE_0 BIT(0) |
69 | #define ME4000_AI_CTRL_MODE_1 BIT(1) |
70 | #define ME4000_AI_CTRL_MODE_2 BIT(2) |
71 | #define ME4000_AI_CTRL_SAMPLE_HOLD BIT(3) |
72 | #define ME4000_AI_CTRL_IMMEDIATE_STOP BIT(4) |
73 | #define ME4000_AI_CTRL_STOP BIT(5) |
74 | #define ME4000_AI_CTRL_CHANNEL_FIFO BIT(6) |
75 | #define ME4000_AI_CTRL_DATA_FIFO BIT(7) |
76 | #define ME4000_AI_CTRL_FULLSCALE BIT(8) |
77 | #define ME4000_AI_CTRL_OFFSET BIT(9) |
78 | #define ME4000_AI_CTRL_EX_TRIG_ANALOG BIT(10) |
79 | #define ME4000_AI_CTRL_EX_TRIG BIT(11) |
80 | #define ME4000_AI_CTRL_EX_TRIG_FALLING BIT(12) |
81 | #define ME4000_AI_CTRL_EX_IRQ BIT(13) |
82 | #define ME4000_AI_CTRL_EX_IRQ_RESET BIT(14) |
83 | #define ME4000_AI_CTRL_LE_IRQ BIT(15) |
84 | #define ME4000_AI_CTRL_LE_IRQ_RESET BIT(16) |
85 | #define ME4000_AI_CTRL_HF_IRQ BIT(17) |
86 | #define ME4000_AI_CTRL_HF_IRQ_RESET BIT(18) |
87 | #define ME4000_AI_CTRL_SC_IRQ BIT(19) |
88 | #define ME4000_AI_CTRL_SC_IRQ_RESET BIT(20) |
89 | #define ME4000_AI_CTRL_SC_RELOAD BIT(21) |
90 | #define ME4000_AI_STATUS_EF_CHANNEL BIT(22) |
91 | #define ME4000_AI_STATUS_HF_CHANNEL BIT(23) |
92 | #define ME4000_AI_STATUS_FF_CHANNEL BIT(24) |
93 | #define ME4000_AI_STATUS_EF_DATA BIT(25) |
94 | #define ME4000_AI_STATUS_HF_DATA BIT(26) |
95 | #define ME4000_AI_STATUS_FF_DATA BIT(27) |
96 | #define ME4000_AI_STATUS_LE BIT(28) |
97 | #define ME4000_AI_STATUS_FSM BIT(29) |
98 | #define ME4000_AI_CTRL_EX_TRIG_BOTH BIT(31) |
99 | #define ME4000_AI_CHANNEL_LIST_REG 0x78 |
100 | #define ME4000_AI_LIST_INPUT_DIFFERENTIAL BIT(5) |
101 | #define ME4000_AI_LIST_RANGE(x) ((3 - ((x) & 3)) << 6) |
102 | #define ME4000_AI_LIST_LAST_ENTRY BIT(8) |
103 | #define ME4000_AI_DATA_REG 0x7c |
104 | #define ME4000_AI_CHAN_TIMER_REG 0x80 |
105 | #define ME4000_AI_CHAN_PRE_TIMER_REG 0x84 |
106 | #define ME4000_AI_SCAN_TIMER_LOW_REG 0x88 |
107 | #define ME4000_AI_SCAN_TIMER_HIGH_REG 0x8c |
108 | #define ME4000_AI_SCAN_PRE_TIMER_LOW_REG 0x90 |
109 | #define ME4000_AI_SCAN_PRE_TIMER_HIGH_REG 0x94 |
110 | #define ME4000_AI_START_REG 0x98 |
111 | #define ME4000_IRQ_STATUS_REG 0x9c |
112 | #define ME4000_IRQ_STATUS_EX BIT(0) |
113 | #define ME4000_IRQ_STATUS_LE BIT(1) |
114 | #define ME4000_IRQ_STATUS_AI_HF BIT(2) |
115 | #define ME4000_IRQ_STATUS_AO_0_HF BIT(3) |
116 | #define ME4000_IRQ_STATUS_AO_1_HF BIT(4) |
117 | #define ME4000_IRQ_STATUS_AO_2_HF BIT(5) |
118 | #define ME4000_IRQ_STATUS_AO_3_HF BIT(6) |
119 | #define ME4000_IRQ_STATUS_SC BIT(7) |
120 | #define ME4000_DIO_PORT_0_REG 0xa0 |
121 | #define ME4000_DIO_PORT_1_REG 0xa4 |
122 | #define ME4000_DIO_PORT_2_REG 0xa8 |
123 | #define ME4000_DIO_PORT_3_REG 0xac |
124 | #define ME4000_DIO_DIR_REG 0xb0 |
125 | #define ME4000_AO_LOADSETREG_XX 0xb4 |
126 | #define ME4000_DIO_CTRL_REG 0xb8 |
127 | #define ME4000_DIO_CTRL_MODE_0 BIT(0) |
128 | #define ME4000_DIO_CTRL_MODE_1 BIT(1) |
129 | #define ME4000_DIO_CTRL_MODE_2 BIT(2) |
130 | #define ME4000_DIO_CTRL_MODE_3 BIT(3) |
131 | #define ME4000_DIO_CTRL_MODE_4 BIT(4) |
132 | #define ME4000_DIO_CTRL_MODE_5 BIT(5) |
133 | #define ME4000_DIO_CTRL_MODE_6 BIT(6) |
134 | #define ME4000_DIO_CTRL_MODE_7 BIT(7) |
135 | #define ME4000_DIO_CTRL_FUNCTION_0 BIT(8) |
136 | #define ME4000_DIO_CTRL_FUNCTION_1 BIT(9) |
137 | #define ME4000_DIO_CTRL_FIFO_HIGH_0 BIT(10) |
138 | #define ME4000_DIO_CTRL_FIFO_HIGH_1 BIT(11) |
139 | #define ME4000_DIO_CTRL_FIFO_HIGH_2 BIT(12) |
140 | #define ME4000_DIO_CTRL_FIFO_HIGH_3 BIT(13) |
141 | #define ME4000_AO_DEMUX_ADJUST_REG 0xbc |
142 | #define ME4000_AO_DEMUX_ADJUST_VALUE 0x4c |
143 | #define ME4000_AI_SAMPLE_COUNTER_REG 0xc0 |
144 | |
145 | #define ME4000_AI_FIFO_COUNT 2048 |
146 | |
147 | #define ME4000_AI_MIN_TICKS 66 |
148 | #define ME4000_AI_MIN_SAMPLE_TIME 2000 |
149 | |
150 | #define ME4000_AI_CHANNEL_LIST_COUNT 1024 |
151 | |
152 | struct me4000_private { |
153 | unsigned long plx_regbase; |
154 | unsigned int ai_ctrl_mode; |
155 | unsigned int ai_init_ticks; |
156 | unsigned int ai_scan_ticks; |
157 | unsigned int ai_chan_ticks; |
158 | }; |
159 | |
160 | enum me4000_boardid { |
161 | BOARD_ME4650, |
162 | BOARD_ME4660, |
163 | BOARD_ME4660I, |
164 | BOARD_ME4660S, |
165 | BOARD_ME4660IS, |
166 | BOARD_ME4670, |
167 | BOARD_ME4670I, |
168 | BOARD_ME4670S, |
169 | BOARD_ME4670IS, |
170 | BOARD_ME4680, |
171 | BOARD_ME4680I, |
172 | BOARD_ME4680S, |
173 | BOARD_ME4680IS, |
174 | }; |
175 | |
176 | struct me4000_board { |
177 | const char *name; |
178 | int ai_nchan; |
179 | unsigned int can_do_diff_ai:1; |
180 | unsigned int can_do_sh_ai:1; /* sample & hold (8 channels) */ |
181 | unsigned int ex_trig_analog:1; |
182 | unsigned int has_ao:1; |
183 | unsigned int has_ao_fifo:1; |
184 | unsigned int has_counter:1; |
185 | }; |
186 | |
187 | static const struct me4000_board me4000_boards[] = { |
188 | [BOARD_ME4650] = { |
189 | .name = "ME-4650" , |
190 | .ai_nchan = 16, |
191 | }, |
192 | [BOARD_ME4660] = { |
193 | .name = "ME-4660" , |
194 | .ai_nchan = 32, |
195 | .can_do_diff_ai = 1, |
196 | .has_counter = 1, |
197 | }, |
198 | [BOARD_ME4660I] = { |
199 | .name = "ME-4660i" , |
200 | .ai_nchan = 32, |
201 | .can_do_diff_ai = 1, |
202 | .has_counter = 1, |
203 | }, |
204 | [BOARD_ME4660S] = { |
205 | .name = "ME-4660s" , |
206 | .ai_nchan = 32, |
207 | .can_do_diff_ai = 1, |
208 | .can_do_sh_ai = 1, |
209 | .has_counter = 1, |
210 | }, |
211 | [BOARD_ME4660IS] = { |
212 | .name = "ME-4660is" , |
213 | .ai_nchan = 32, |
214 | .can_do_diff_ai = 1, |
215 | .can_do_sh_ai = 1, |
216 | .has_counter = 1, |
217 | }, |
218 | [BOARD_ME4670] = { |
219 | .name = "ME-4670" , |
220 | .ai_nchan = 32, |
221 | .can_do_diff_ai = 1, |
222 | .ex_trig_analog = 1, |
223 | .has_ao = 1, |
224 | .has_counter = 1, |
225 | }, |
226 | [BOARD_ME4670I] = { |
227 | .name = "ME-4670i" , |
228 | .ai_nchan = 32, |
229 | .can_do_diff_ai = 1, |
230 | .ex_trig_analog = 1, |
231 | .has_ao = 1, |
232 | .has_counter = 1, |
233 | }, |
234 | [BOARD_ME4670S] = { |
235 | .name = "ME-4670s" , |
236 | .ai_nchan = 32, |
237 | .can_do_diff_ai = 1, |
238 | .can_do_sh_ai = 1, |
239 | .ex_trig_analog = 1, |
240 | .has_ao = 1, |
241 | .has_counter = 1, |
242 | }, |
243 | [BOARD_ME4670IS] = { |
244 | .name = "ME-4670is" , |
245 | .ai_nchan = 32, |
246 | .can_do_diff_ai = 1, |
247 | .can_do_sh_ai = 1, |
248 | .ex_trig_analog = 1, |
249 | .has_ao = 1, |
250 | .has_counter = 1, |
251 | }, |
252 | [BOARD_ME4680] = { |
253 | .name = "ME-4680" , |
254 | .ai_nchan = 32, |
255 | .can_do_diff_ai = 1, |
256 | .ex_trig_analog = 1, |
257 | .has_ao = 1, |
258 | .has_ao_fifo = 1, |
259 | .has_counter = 1, |
260 | }, |
261 | [BOARD_ME4680I] = { |
262 | .name = "ME-4680i" , |
263 | .ai_nchan = 32, |
264 | .can_do_diff_ai = 1, |
265 | .ex_trig_analog = 1, |
266 | .has_ao = 1, |
267 | .has_ao_fifo = 1, |
268 | .has_counter = 1, |
269 | }, |
270 | [BOARD_ME4680S] = { |
271 | .name = "ME-4680s" , |
272 | .ai_nchan = 32, |
273 | .can_do_diff_ai = 1, |
274 | .can_do_sh_ai = 1, |
275 | .ex_trig_analog = 1, |
276 | .has_ao = 1, |
277 | .has_ao_fifo = 1, |
278 | .has_counter = 1, |
279 | }, |
280 | [BOARD_ME4680IS] = { |
281 | .name = "ME-4680is" , |
282 | .ai_nchan = 32, |
283 | .can_do_diff_ai = 1, |
284 | .can_do_sh_ai = 1, |
285 | .ex_trig_analog = 1, |
286 | .has_ao = 1, |
287 | .has_ao_fifo = 1, |
288 | .has_counter = 1, |
289 | }, |
290 | }; |
291 | |
292 | /* |
293 | * NOTE: the ranges here are inverted compared to the values |
294 | * written to the ME4000_AI_CHANNEL_LIST_REG, |
295 | * |
296 | * The ME4000_AI_LIST_RANGE() macro handles the inversion. |
297 | */ |
298 | static const struct comedi_lrange me4000_ai_range = { |
299 | 4, { |
300 | UNI_RANGE(2.5), |
301 | UNI_RANGE(10), |
302 | BIP_RANGE(2.5), |
303 | BIP_RANGE(10) |
304 | } |
305 | }; |
306 | |
307 | static int me4000_xilinx_download(struct comedi_device *dev, |
308 | const u8 *data, size_t size, |
309 | unsigned long context) |
310 | { |
311 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
312 | struct me4000_private *devpriv = dev->private; |
313 | unsigned long xilinx_iobase = pci_resource_start(pcidev, 5); |
314 | unsigned int file_length; |
315 | unsigned int val; |
316 | unsigned int i; |
317 | |
318 | if (!xilinx_iobase) |
319 | return -ENODEV; |
320 | |
321 | /* |
322 | * Set PLX local interrupt 2 polarity to high. |
323 | * Interrupt is thrown by init pin of xilinx. |
324 | */ |
325 | outl(PLX9052_INTCSR_LI2POL, port: devpriv->plx_regbase + PLX9052_INTCSR); |
326 | |
327 | /* Set /CS and /WRITE of the Xilinx */ |
328 | val = inl(port: devpriv->plx_regbase + PLX9052_CNTRL); |
329 | val |= PLX9052_CNTRL_UIO2_DATA; |
330 | outl(value: val, port: devpriv->plx_regbase + PLX9052_CNTRL); |
331 | |
332 | /* Init Xilinx with CS1 */ |
333 | inb(port: xilinx_iobase + 0xC8); |
334 | |
335 | /* Wait until /INIT pin is set */ |
336 | usleep_range(min: 20, max: 1000); |
337 | val = inl(port: devpriv->plx_regbase + PLX9052_INTCSR); |
338 | if (!(val & PLX9052_INTCSR_LI2STAT)) { |
339 | dev_err(dev->class_dev, "Can't init Xilinx\n" ); |
340 | return -EIO; |
341 | } |
342 | |
343 | /* Reset /CS and /WRITE of the Xilinx */ |
344 | val = inl(port: devpriv->plx_regbase + PLX9052_CNTRL); |
345 | val &= ~PLX9052_CNTRL_UIO2_DATA; |
346 | outl(value: val, port: devpriv->plx_regbase + PLX9052_CNTRL); |
347 | |
348 | /* Download Xilinx firmware */ |
349 | file_length = (((unsigned int)data[0] & 0xff) << 24) + |
350 | (((unsigned int)data[1] & 0xff) << 16) + |
351 | (((unsigned int)data[2] & 0xff) << 8) + |
352 | ((unsigned int)data[3] & 0xff); |
353 | usleep_range(min: 10, max: 1000); |
354 | |
355 | for (i = 0; i < file_length; i++) { |
356 | outb(value: data[16 + i], port: xilinx_iobase); |
357 | usleep_range(min: 10, max: 1000); |
358 | |
359 | /* Check if BUSY flag is low */ |
360 | val = inl(port: devpriv->plx_regbase + PLX9052_CNTRL); |
361 | if (val & PLX9052_CNTRL_UIO1_DATA) { |
362 | dev_err(dev->class_dev, |
363 | "Xilinx is still busy (i = %d)\n" , i); |
364 | return -EIO; |
365 | } |
366 | } |
367 | |
368 | /* If done flag is high download was successful */ |
369 | val = inl(port: devpriv->plx_regbase + PLX9052_CNTRL); |
370 | if (!(val & PLX9052_CNTRL_UIO0_DATA)) { |
371 | dev_err(dev->class_dev, "DONE flag is not set\n" ); |
372 | dev_err(dev->class_dev, "Download not successful\n" ); |
373 | return -EIO; |
374 | } |
375 | |
376 | /* Set /CS and /WRITE */ |
377 | val = inl(port: devpriv->plx_regbase + PLX9052_CNTRL); |
378 | val |= PLX9052_CNTRL_UIO2_DATA; |
379 | outl(value: val, port: devpriv->plx_regbase + PLX9052_CNTRL); |
380 | |
381 | return 0; |
382 | } |
383 | |
384 | static void me4000_ai_reset(struct comedi_device *dev) |
385 | { |
386 | unsigned int ctrl; |
387 | |
388 | /* Stop any running conversion */ |
389 | ctrl = inl(port: dev->iobase + ME4000_AI_CTRL_REG); |
390 | ctrl |= ME4000_AI_CTRL_STOP | ME4000_AI_CTRL_IMMEDIATE_STOP; |
391 | outl(value: ctrl, port: dev->iobase + ME4000_AI_CTRL_REG); |
392 | |
393 | /* Clear the control register */ |
394 | outl(value: 0x0, port: dev->iobase + ME4000_AI_CTRL_REG); |
395 | } |
396 | |
397 | static void me4000_reset(struct comedi_device *dev) |
398 | { |
399 | struct me4000_private *devpriv = dev->private; |
400 | unsigned int val; |
401 | int chan; |
402 | |
403 | /* Disable interrupts on the PLX */ |
404 | outl(value: 0, port: devpriv->plx_regbase + PLX9052_INTCSR); |
405 | |
406 | /* Software reset the PLX */ |
407 | val = inl(port: devpriv->plx_regbase + PLX9052_CNTRL); |
408 | val |= PLX9052_CNTRL_PCI_RESET; |
409 | outl(value: val, port: devpriv->plx_regbase + PLX9052_CNTRL); |
410 | val &= ~PLX9052_CNTRL_PCI_RESET; |
411 | outl(value: val, port: devpriv->plx_regbase + PLX9052_CNTRL); |
412 | |
413 | /* 0x8000 to the DACs means an output voltage of 0V */ |
414 | for (chan = 0; chan < 4; chan++) |
415 | outl(value: 0x8000, port: dev->iobase + ME4000_AO_SINGLE_REG(chan)); |
416 | |
417 | me4000_ai_reset(dev); |
418 | |
419 | /* Set both stop bits in the analog output control register */ |
420 | val = ME4000_AO_CTRL_IMMEDIATE_STOP | ME4000_AO_CTRL_STOP; |
421 | for (chan = 0; chan < 4; chan++) |
422 | outl(value: val, port: dev->iobase + ME4000_AO_CTRL_REG(chan)); |
423 | |
424 | /* Set the adustment register for AO demux */ |
425 | outl(ME4000_AO_DEMUX_ADJUST_VALUE, |
426 | port: dev->iobase + ME4000_AO_DEMUX_ADJUST_REG); |
427 | |
428 | /* |
429 | * Set digital I/O direction for port 0 |
430 | * to output on isolated versions |
431 | */ |
432 | if (!(inl(port: dev->iobase + ME4000_DIO_DIR_REG) & 0x1)) |
433 | outl(value: 0x1, port: dev->iobase + ME4000_DIO_CTRL_REG); |
434 | } |
435 | |
436 | static unsigned int me4000_ai_get_sample(struct comedi_device *dev, |
437 | struct comedi_subdevice *s) |
438 | { |
439 | unsigned int val; |
440 | |
441 | /* read two's complement value and munge to offset binary */ |
442 | val = inl(port: dev->iobase + ME4000_AI_DATA_REG); |
443 | return comedi_offset_munge(s, val); |
444 | } |
445 | |
446 | static int me4000_ai_eoc(struct comedi_device *dev, |
447 | struct comedi_subdevice *s, |
448 | struct comedi_insn *insn, |
449 | unsigned long context) |
450 | { |
451 | unsigned int status; |
452 | |
453 | status = inl(port: dev->iobase + ME4000_AI_STATUS_REG); |
454 | if (status & ME4000_AI_STATUS_EF_DATA) |
455 | return 0; |
456 | return -EBUSY; |
457 | } |
458 | |
459 | static int me4000_ai_insn_read(struct comedi_device *dev, |
460 | struct comedi_subdevice *s, |
461 | struct comedi_insn *insn, |
462 | unsigned int *data) |
463 | { |
464 | unsigned int chan = CR_CHAN(insn->chanspec); |
465 | unsigned int range = CR_RANGE(insn->chanspec); |
466 | unsigned int aref = CR_AREF(insn->chanspec); |
467 | unsigned int entry; |
468 | int ret = 0; |
469 | int i; |
470 | |
471 | entry = chan | ME4000_AI_LIST_RANGE(range); |
472 | if (aref == AREF_DIFF) { |
473 | if (!(s->subdev_flags & SDF_DIFF)) { |
474 | dev_err(dev->class_dev, |
475 | "Differential inputs are not available\n" ); |
476 | return -EINVAL; |
477 | } |
478 | |
479 | if (!comedi_range_is_bipolar(s, range)) { |
480 | dev_err(dev->class_dev, |
481 | "Range must be bipolar when aref = diff\n" ); |
482 | return -EINVAL; |
483 | } |
484 | |
485 | if (chan >= (s->n_chan / 2)) { |
486 | dev_err(dev->class_dev, |
487 | "Analog input is not available\n" ); |
488 | return -EINVAL; |
489 | } |
490 | entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL; |
491 | } |
492 | |
493 | entry |= ME4000_AI_LIST_LAST_ENTRY; |
494 | |
495 | /* Enable channel list and data fifo for single acquisition mode */ |
496 | outl(ME4000_AI_CTRL_CHANNEL_FIFO | ME4000_AI_CTRL_DATA_FIFO, |
497 | port: dev->iobase + ME4000_AI_CTRL_REG); |
498 | |
499 | /* Generate channel list entry */ |
500 | outl(value: entry, port: dev->iobase + ME4000_AI_CHANNEL_LIST_REG); |
501 | |
502 | /* Set the timer to maximum sample rate */ |
503 | outl(ME4000_AI_MIN_TICKS, port: dev->iobase + ME4000_AI_CHAN_TIMER_REG); |
504 | outl(ME4000_AI_MIN_TICKS, port: dev->iobase + ME4000_AI_CHAN_PRE_TIMER_REG); |
505 | |
506 | for (i = 0; i < insn->n; i++) { |
507 | unsigned int val; |
508 | |
509 | /* start conversion by dummy read */ |
510 | inl(port: dev->iobase + ME4000_AI_START_REG); |
511 | |
512 | ret = comedi_timeout(dev, s, insn, cb: me4000_ai_eoc, context: 0); |
513 | if (ret) |
514 | break; |
515 | |
516 | val = me4000_ai_get_sample(dev, s); |
517 | data[i] = comedi_offset_munge(s, val); |
518 | } |
519 | |
520 | me4000_ai_reset(dev); |
521 | |
522 | return ret ? ret : insn->n; |
523 | } |
524 | |
525 | static int me4000_ai_cancel(struct comedi_device *dev, |
526 | struct comedi_subdevice *s) |
527 | { |
528 | me4000_ai_reset(dev); |
529 | |
530 | return 0; |
531 | } |
532 | |
533 | static int me4000_ai_check_chanlist(struct comedi_device *dev, |
534 | struct comedi_subdevice *s, |
535 | struct comedi_cmd *cmd) |
536 | { |
537 | unsigned int aref0 = CR_AREF(cmd->chanlist[0]); |
538 | int i; |
539 | |
540 | for (i = 0; i < cmd->chanlist_len; i++) { |
541 | unsigned int chan = CR_CHAN(cmd->chanlist[i]); |
542 | unsigned int range = CR_RANGE(cmd->chanlist[i]); |
543 | unsigned int aref = CR_AREF(cmd->chanlist[i]); |
544 | |
545 | if (aref != aref0) { |
546 | dev_dbg(dev->class_dev, |
547 | "Mode is not equal for all entries\n" ); |
548 | return -EINVAL; |
549 | } |
550 | |
551 | if (aref == AREF_DIFF) { |
552 | if (!(s->subdev_flags & SDF_DIFF)) { |
553 | dev_err(dev->class_dev, |
554 | "Differential inputs are not available\n" ); |
555 | return -EINVAL; |
556 | } |
557 | |
558 | if (chan >= (s->n_chan / 2)) { |
559 | dev_dbg(dev->class_dev, |
560 | "Channel number to high\n" ); |
561 | return -EINVAL; |
562 | } |
563 | |
564 | if (!comedi_range_is_bipolar(s, range)) { |
565 | dev_dbg(dev->class_dev, |
566 | "Bipolar is not selected in differential mode\n" ); |
567 | return -EINVAL; |
568 | } |
569 | } |
570 | } |
571 | |
572 | return 0; |
573 | } |
574 | |
575 | static void me4000_ai_round_cmd_args(struct comedi_device *dev, |
576 | struct comedi_subdevice *s, |
577 | struct comedi_cmd *cmd) |
578 | { |
579 | struct me4000_private *devpriv = dev->private; |
580 | int rest; |
581 | |
582 | devpriv->ai_init_ticks = 0; |
583 | devpriv->ai_scan_ticks = 0; |
584 | devpriv->ai_chan_ticks = 0; |
585 | |
586 | if (cmd->start_arg) { |
587 | devpriv->ai_init_ticks = (cmd->start_arg * 33) / 1000; |
588 | rest = (cmd->start_arg * 33) % 1000; |
589 | |
590 | if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_NEAREST) { |
591 | if (rest > 33) |
592 | devpriv->ai_init_ticks++; |
593 | } else if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_UP) { |
594 | if (rest) |
595 | devpriv->ai_init_ticks++; |
596 | } |
597 | } |
598 | |
599 | if (cmd->scan_begin_arg) { |
600 | devpriv->ai_scan_ticks = (cmd->scan_begin_arg * 33) / 1000; |
601 | rest = (cmd->scan_begin_arg * 33) % 1000; |
602 | |
603 | if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_NEAREST) { |
604 | if (rest > 33) |
605 | devpriv->ai_scan_ticks++; |
606 | } else if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_UP) { |
607 | if (rest) |
608 | devpriv->ai_scan_ticks++; |
609 | } |
610 | } |
611 | |
612 | if (cmd->convert_arg) { |
613 | devpriv->ai_chan_ticks = (cmd->convert_arg * 33) / 1000; |
614 | rest = (cmd->convert_arg * 33) % 1000; |
615 | |
616 | if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_NEAREST) { |
617 | if (rest > 33) |
618 | devpriv->ai_chan_ticks++; |
619 | } else if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_UP) { |
620 | if (rest) |
621 | devpriv->ai_chan_ticks++; |
622 | } |
623 | } |
624 | } |
625 | |
626 | static void me4000_ai_write_chanlist(struct comedi_device *dev, |
627 | struct comedi_subdevice *s, |
628 | struct comedi_cmd *cmd) |
629 | { |
630 | int i; |
631 | |
632 | for (i = 0; i < cmd->chanlist_len; i++) { |
633 | unsigned int chan = CR_CHAN(cmd->chanlist[i]); |
634 | unsigned int range = CR_RANGE(cmd->chanlist[i]); |
635 | unsigned int aref = CR_AREF(cmd->chanlist[i]); |
636 | unsigned int entry; |
637 | |
638 | entry = chan | ME4000_AI_LIST_RANGE(range); |
639 | |
640 | if (aref == AREF_DIFF) |
641 | entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL; |
642 | |
643 | if (i == (cmd->chanlist_len - 1)) |
644 | entry |= ME4000_AI_LIST_LAST_ENTRY; |
645 | |
646 | outl(value: entry, port: dev->iobase + ME4000_AI_CHANNEL_LIST_REG); |
647 | } |
648 | } |
649 | |
650 | static int me4000_ai_do_cmd(struct comedi_device *dev, |
651 | struct comedi_subdevice *s) |
652 | { |
653 | struct me4000_private *devpriv = dev->private; |
654 | struct comedi_cmd *cmd = &s->async->cmd; |
655 | unsigned int ctrl; |
656 | |
657 | /* Write timer arguments */ |
658 | outl(value: devpriv->ai_init_ticks - 1, |
659 | port: dev->iobase + ME4000_AI_SCAN_PRE_TIMER_LOW_REG); |
660 | outl(value: 0x0, port: dev->iobase + ME4000_AI_SCAN_PRE_TIMER_HIGH_REG); |
661 | |
662 | if (devpriv->ai_scan_ticks) { |
663 | outl(value: devpriv->ai_scan_ticks - 1, |
664 | port: dev->iobase + ME4000_AI_SCAN_TIMER_LOW_REG); |
665 | outl(value: 0x0, port: dev->iobase + ME4000_AI_SCAN_TIMER_HIGH_REG); |
666 | } |
667 | |
668 | outl(value: devpriv->ai_chan_ticks - 1, |
669 | port: dev->iobase + ME4000_AI_CHAN_PRE_TIMER_REG); |
670 | outl(value: devpriv->ai_chan_ticks - 1, |
671 | port: dev->iobase + ME4000_AI_CHAN_TIMER_REG); |
672 | |
673 | /* Start sources */ |
674 | ctrl = devpriv->ai_ctrl_mode | |
675 | ME4000_AI_CTRL_CHANNEL_FIFO | |
676 | ME4000_AI_CTRL_DATA_FIFO; |
677 | |
678 | /* Stop triggers */ |
679 | if (cmd->stop_src == TRIG_COUNT) { |
680 | outl(value: cmd->chanlist_len * cmd->stop_arg, |
681 | port: dev->iobase + ME4000_AI_SAMPLE_COUNTER_REG); |
682 | ctrl |= ME4000_AI_CTRL_SC_IRQ; |
683 | } else if (cmd->stop_src == TRIG_NONE && |
684 | cmd->scan_end_src == TRIG_COUNT) { |
685 | outl(value: cmd->scan_end_arg, |
686 | port: dev->iobase + ME4000_AI_SAMPLE_COUNTER_REG); |
687 | ctrl |= ME4000_AI_CTRL_SC_IRQ; |
688 | } |
689 | ctrl |= ME4000_AI_CTRL_HF_IRQ; |
690 | |
691 | /* Write the setup to the control register */ |
692 | outl(value: ctrl, port: dev->iobase + ME4000_AI_CTRL_REG); |
693 | |
694 | /* Write the channel list */ |
695 | me4000_ai_write_chanlist(dev, s, cmd); |
696 | |
697 | /* Start acquistion by dummy read */ |
698 | inl(port: dev->iobase + ME4000_AI_START_REG); |
699 | |
700 | return 0; |
701 | } |
702 | |
703 | static int me4000_ai_do_cmd_test(struct comedi_device *dev, |
704 | struct comedi_subdevice *s, |
705 | struct comedi_cmd *cmd) |
706 | { |
707 | struct me4000_private *devpriv = dev->private; |
708 | int err = 0; |
709 | |
710 | /* Step 1 : check if triggers are trivially valid */ |
711 | |
712 | err |= comedi_check_trigger_src(src: &cmd->start_src, TRIG_NOW | TRIG_EXT); |
713 | err |= comedi_check_trigger_src(src: &cmd->scan_begin_src, |
714 | TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT); |
715 | err |= comedi_check_trigger_src(src: &cmd->convert_src, |
716 | TRIG_TIMER | TRIG_EXT); |
717 | err |= comedi_check_trigger_src(src: &cmd->scan_end_src, |
718 | TRIG_NONE | TRIG_COUNT); |
719 | err |= comedi_check_trigger_src(src: &cmd->stop_src, TRIG_NONE | TRIG_COUNT); |
720 | |
721 | if (err) |
722 | return 1; |
723 | |
724 | /* Step 2a : make sure trigger sources are unique */ |
725 | |
726 | err |= comedi_check_trigger_is_unique(src: cmd->start_src); |
727 | err |= comedi_check_trigger_is_unique(src: cmd->scan_begin_src); |
728 | err |= comedi_check_trigger_is_unique(src: cmd->convert_src); |
729 | err |= comedi_check_trigger_is_unique(src: cmd->scan_end_src); |
730 | err |= comedi_check_trigger_is_unique(src: cmd->stop_src); |
731 | |
732 | /* Step 2b : and mutually compatible */ |
733 | |
734 | if (cmd->start_src == TRIG_NOW && |
735 | cmd->scan_begin_src == TRIG_TIMER && |
736 | cmd->convert_src == TRIG_TIMER) { |
737 | devpriv->ai_ctrl_mode = ME4000_AI_CTRL_MODE_0; |
738 | } else if (cmd->start_src == TRIG_NOW && |
739 | cmd->scan_begin_src == TRIG_FOLLOW && |
740 | cmd->convert_src == TRIG_TIMER) { |
741 | devpriv->ai_ctrl_mode = ME4000_AI_CTRL_MODE_0; |
742 | } else if (cmd->start_src == TRIG_EXT && |
743 | cmd->scan_begin_src == TRIG_TIMER && |
744 | cmd->convert_src == TRIG_TIMER) { |
745 | devpriv->ai_ctrl_mode = ME4000_AI_CTRL_MODE_1; |
746 | } else if (cmd->start_src == TRIG_EXT && |
747 | cmd->scan_begin_src == TRIG_FOLLOW && |
748 | cmd->convert_src == TRIG_TIMER) { |
749 | devpriv->ai_ctrl_mode = ME4000_AI_CTRL_MODE_1; |
750 | } else if (cmd->start_src == TRIG_EXT && |
751 | cmd->scan_begin_src == TRIG_EXT && |
752 | cmd->convert_src == TRIG_TIMER) { |
753 | devpriv->ai_ctrl_mode = ME4000_AI_CTRL_MODE_2; |
754 | } else if (cmd->start_src == TRIG_EXT && |
755 | cmd->scan_begin_src == TRIG_EXT && |
756 | cmd->convert_src == TRIG_EXT) { |
757 | devpriv->ai_ctrl_mode = ME4000_AI_CTRL_MODE_0 | |
758 | ME4000_AI_CTRL_MODE_1; |
759 | } else { |
760 | err |= -EINVAL; |
761 | } |
762 | |
763 | if (err) |
764 | return 2; |
765 | |
766 | /* Step 3: check if arguments are trivially valid */ |
767 | |
768 | err |= comedi_check_trigger_arg_is(arg: &cmd->start_arg, val: 0); |
769 | |
770 | if (cmd->chanlist_len < 1) { |
771 | cmd->chanlist_len = 1; |
772 | err |= -EINVAL; |
773 | } |
774 | |
775 | /* Round the timer arguments */ |
776 | me4000_ai_round_cmd_args(dev, s, cmd); |
777 | |
778 | if (devpriv->ai_init_ticks < 66) { |
779 | cmd->start_arg = 2000; |
780 | err |= -EINVAL; |
781 | } |
782 | if (devpriv->ai_scan_ticks && devpriv->ai_scan_ticks < 67) { |
783 | cmd->scan_begin_arg = 2031; |
784 | err |= -EINVAL; |
785 | } |
786 | if (devpriv->ai_chan_ticks < 66) { |
787 | cmd->convert_arg = 2000; |
788 | err |= -EINVAL; |
789 | } |
790 | |
791 | if (cmd->stop_src == TRIG_COUNT) |
792 | err |= comedi_check_trigger_arg_min(arg: &cmd->stop_arg, val: 1); |
793 | else /* TRIG_NONE */ |
794 | err |= comedi_check_trigger_arg_is(arg: &cmd->stop_arg, val: 0); |
795 | |
796 | if (err) |
797 | return 3; |
798 | |
799 | /* |
800 | * Stage 4. Check for argument conflicts. |
801 | */ |
802 | if (cmd->start_src == TRIG_NOW && |
803 | cmd->scan_begin_src == TRIG_TIMER && |
804 | cmd->convert_src == TRIG_TIMER) { |
805 | /* Check timer arguments */ |
806 | if (devpriv->ai_init_ticks < ME4000_AI_MIN_TICKS) { |
807 | dev_err(dev->class_dev, "Invalid start arg\n" ); |
808 | cmd->start_arg = 2000; /* 66 ticks at least */ |
809 | err++; |
810 | } |
811 | if (devpriv->ai_chan_ticks < ME4000_AI_MIN_TICKS) { |
812 | dev_err(dev->class_dev, "Invalid convert arg\n" ); |
813 | cmd->convert_arg = 2000; /* 66 ticks at least */ |
814 | err++; |
815 | } |
816 | if (devpriv->ai_scan_ticks <= |
817 | cmd->chanlist_len * devpriv->ai_chan_ticks) { |
818 | dev_err(dev->class_dev, "Invalid scan end arg\n" ); |
819 | |
820 | /* At least one tick more */ |
821 | cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31; |
822 | err++; |
823 | } |
824 | } else if (cmd->start_src == TRIG_NOW && |
825 | cmd->scan_begin_src == TRIG_FOLLOW && |
826 | cmd->convert_src == TRIG_TIMER) { |
827 | /* Check timer arguments */ |
828 | if (devpriv->ai_init_ticks < ME4000_AI_MIN_TICKS) { |
829 | dev_err(dev->class_dev, "Invalid start arg\n" ); |
830 | cmd->start_arg = 2000; /* 66 ticks at least */ |
831 | err++; |
832 | } |
833 | if (devpriv->ai_chan_ticks < ME4000_AI_MIN_TICKS) { |
834 | dev_err(dev->class_dev, "Invalid convert arg\n" ); |
835 | cmd->convert_arg = 2000; /* 66 ticks at least */ |
836 | err++; |
837 | } |
838 | } else if (cmd->start_src == TRIG_EXT && |
839 | cmd->scan_begin_src == TRIG_TIMER && |
840 | cmd->convert_src == TRIG_TIMER) { |
841 | /* Check timer arguments */ |
842 | if (devpriv->ai_init_ticks < ME4000_AI_MIN_TICKS) { |
843 | dev_err(dev->class_dev, "Invalid start arg\n" ); |
844 | cmd->start_arg = 2000; /* 66 ticks at least */ |
845 | err++; |
846 | } |
847 | if (devpriv->ai_chan_ticks < ME4000_AI_MIN_TICKS) { |
848 | dev_err(dev->class_dev, "Invalid convert arg\n" ); |
849 | cmd->convert_arg = 2000; /* 66 ticks at least */ |
850 | err++; |
851 | } |
852 | if (devpriv->ai_scan_ticks <= |
853 | cmd->chanlist_len * devpriv->ai_chan_ticks) { |
854 | dev_err(dev->class_dev, "Invalid scan end arg\n" ); |
855 | |
856 | /* At least one tick more */ |
857 | cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31; |
858 | err++; |
859 | } |
860 | } else if (cmd->start_src == TRIG_EXT && |
861 | cmd->scan_begin_src == TRIG_FOLLOW && |
862 | cmd->convert_src == TRIG_TIMER) { |
863 | /* Check timer arguments */ |
864 | if (devpriv->ai_init_ticks < ME4000_AI_MIN_TICKS) { |
865 | dev_err(dev->class_dev, "Invalid start arg\n" ); |
866 | cmd->start_arg = 2000; /* 66 ticks at least */ |
867 | err++; |
868 | } |
869 | if (devpriv->ai_chan_ticks < ME4000_AI_MIN_TICKS) { |
870 | dev_err(dev->class_dev, "Invalid convert arg\n" ); |
871 | cmd->convert_arg = 2000; /* 66 ticks at least */ |
872 | err++; |
873 | } |
874 | } else if (cmd->start_src == TRIG_EXT && |
875 | cmd->scan_begin_src == TRIG_EXT && |
876 | cmd->convert_src == TRIG_TIMER) { |
877 | /* Check timer arguments */ |
878 | if (devpriv->ai_init_ticks < ME4000_AI_MIN_TICKS) { |
879 | dev_err(dev->class_dev, "Invalid start arg\n" ); |
880 | cmd->start_arg = 2000; /* 66 ticks at least */ |
881 | err++; |
882 | } |
883 | if (devpriv->ai_chan_ticks < ME4000_AI_MIN_TICKS) { |
884 | dev_err(dev->class_dev, "Invalid convert arg\n" ); |
885 | cmd->convert_arg = 2000; /* 66 ticks at least */ |
886 | err++; |
887 | } |
888 | } else if (cmd->start_src == TRIG_EXT && |
889 | cmd->scan_begin_src == TRIG_EXT && |
890 | cmd->convert_src == TRIG_EXT) { |
891 | /* Check timer arguments */ |
892 | if (devpriv->ai_init_ticks < ME4000_AI_MIN_TICKS) { |
893 | dev_err(dev->class_dev, "Invalid start arg\n" ); |
894 | cmd->start_arg = 2000; /* 66 ticks at least */ |
895 | err++; |
896 | } |
897 | } |
898 | if (cmd->scan_end_src == TRIG_COUNT) { |
899 | if (cmd->scan_end_arg == 0) { |
900 | dev_err(dev->class_dev, "Invalid scan end arg\n" ); |
901 | cmd->scan_end_arg = 1; |
902 | err++; |
903 | } |
904 | } |
905 | |
906 | if (err) |
907 | return 4; |
908 | |
909 | /* Step 5: check channel list if it exists */ |
910 | if (cmd->chanlist && cmd->chanlist_len > 0) |
911 | err |= me4000_ai_check_chanlist(dev, s, cmd); |
912 | |
913 | if (err) |
914 | return 5; |
915 | |
916 | return 0; |
917 | } |
918 | |
919 | static irqreturn_t me4000_ai_isr(int irq, void *dev_id) |
920 | { |
921 | unsigned int tmp; |
922 | struct comedi_device *dev = dev_id; |
923 | struct comedi_subdevice *s = dev->read_subdev; |
924 | int i; |
925 | int c = 0; |
926 | unsigned short lval; |
927 | |
928 | if (!dev->attached) |
929 | return IRQ_NONE; |
930 | |
931 | if (inl(port: dev->iobase + ME4000_IRQ_STATUS_REG) & |
932 | ME4000_IRQ_STATUS_AI_HF) { |
933 | /* Read status register to find out what happened */ |
934 | tmp = inl(port: dev->iobase + ME4000_AI_STATUS_REG); |
935 | |
936 | if (!(tmp & ME4000_AI_STATUS_FF_DATA) && |
937 | !(tmp & ME4000_AI_STATUS_HF_DATA) && |
938 | (tmp & ME4000_AI_STATUS_EF_DATA)) { |
939 | dev_err(dev->class_dev, "FIFO overflow\n" ); |
940 | s->async->events |= COMEDI_CB_ERROR; |
941 | c = ME4000_AI_FIFO_COUNT; |
942 | } else if ((tmp & ME4000_AI_STATUS_FF_DATA) && |
943 | !(tmp & ME4000_AI_STATUS_HF_DATA) && |
944 | (tmp & ME4000_AI_STATUS_EF_DATA)) { |
945 | c = ME4000_AI_FIFO_COUNT / 2; |
946 | } else { |
947 | dev_err(dev->class_dev, "Undefined FIFO state\n" ); |
948 | s->async->events |= COMEDI_CB_ERROR; |
949 | c = 0; |
950 | } |
951 | |
952 | for (i = 0; i < c; i++) { |
953 | lval = me4000_ai_get_sample(dev, s); |
954 | if (!comedi_buf_write_samples(s, data: &lval, nsamples: 1)) |
955 | break; |
956 | } |
957 | |
958 | /* Work is done, so reset the interrupt */ |
959 | tmp |= ME4000_AI_CTRL_HF_IRQ_RESET; |
960 | outl(value: tmp, port: dev->iobase + ME4000_AI_CTRL_REG); |
961 | tmp &= ~ME4000_AI_CTRL_HF_IRQ_RESET; |
962 | outl(value: tmp, port: dev->iobase + ME4000_AI_CTRL_REG); |
963 | } |
964 | |
965 | if (inl(port: dev->iobase + ME4000_IRQ_STATUS_REG) & |
966 | ME4000_IRQ_STATUS_SC) { |
967 | /* Acquisition is complete */ |
968 | s->async->events |= COMEDI_CB_EOA; |
969 | |
970 | /* Poll data until fifo empty */ |
971 | while (inl(port: dev->iobase + ME4000_AI_STATUS_REG) & |
972 | ME4000_AI_STATUS_EF_DATA) { |
973 | lval = me4000_ai_get_sample(dev, s); |
974 | if (!comedi_buf_write_samples(s, data: &lval, nsamples: 1)) |
975 | break; |
976 | } |
977 | |
978 | /* Work is done, so reset the interrupt */ |
979 | tmp = inl(port: dev->iobase + ME4000_AI_CTRL_REG); |
980 | tmp |= ME4000_AI_CTRL_SC_IRQ_RESET; |
981 | outl(value: tmp, port: dev->iobase + ME4000_AI_CTRL_REG); |
982 | tmp &= ~ME4000_AI_CTRL_SC_IRQ_RESET; |
983 | outl(value: tmp, port: dev->iobase + ME4000_AI_CTRL_REG); |
984 | } |
985 | |
986 | comedi_handle_events(dev, s); |
987 | |
988 | return IRQ_HANDLED; |
989 | } |
990 | |
991 | static int me4000_ao_insn_write(struct comedi_device *dev, |
992 | struct comedi_subdevice *s, |
993 | struct comedi_insn *insn, |
994 | unsigned int *data) |
995 | { |
996 | unsigned int chan = CR_CHAN(insn->chanspec); |
997 | unsigned int tmp; |
998 | |
999 | /* Stop any running conversion */ |
1000 | tmp = inl(port: dev->iobase + ME4000_AO_CTRL_REG(chan)); |
1001 | tmp |= ME4000_AO_CTRL_IMMEDIATE_STOP; |
1002 | outl(value: tmp, port: dev->iobase + ME4000_AO_CTRL_REG(chan)); |
1003 | |
1004 | /* Clear control register and set to single mode */ |
1005 | outl(value: 0x0, port: dev->iobase + ME4000_AO_CTRL_REG(chan)); |
1006 | |
1007 | /* Write data value */ |
1008 | outl(value: data[0], port: dev->iobase + ME4000_AO_SINGLE_REG(chan)); |
1009 | |
1010 | /* Store in the mirror */ |
1011 | s->readback[chan] = data[0]; |
1012 | |
1013 | return 1; |
1014 | } |
1015 | |
1016 | static int me4000_dio_insn_bits(struct comedi_device *dev, |
1017 | struct comedi_subdevice *s, |
1018 | struct comedi_insn *insn, |
1019 | unsigned int *data) |
1020 | { |
1021 | if (comedi_dio_update_state(s, data)) { |
1022 | outl(value: (s->state >> 0) & 0xFF, |
1023 | port: dev->iobase + ME4000_DIO_PORT_0_REG); |
1024 | outl(value: (s->state >> 8) & 0xFF, |
1025 | port: dev->iobase + ME4000_DIO_PORT_1_REG); |
1026 | outl(value: (s->state >> 16) & 0xFF, |
1027 | port: dev->iobase + ME4000_DIO_PORT_2_REG); |
1028 | outl(value: (s->state >> 24) & 0xFF, |
1029 | port: dev->iobase + ME4000_DIO_PORT_3_REG); |
1030 | } |
1031 | |
1032 | data[1] = ((inl(port: dev->iobase + ME4000_DIO_PORT_0_REG) & 0xFF) << 0) | |
1033 | ((inl(port: dev->iobase + ME4000_DIO_PORT_1_REG) & 0xFF) << 8) | |
1034 | ((inl(port: dev->iobase + ME4000_DIO_PORT_2_REG) & 0xFF) << 16) | |
1035 | ((inl(port: dev->iobase + ME4000_DIO_PORT_3_REG) & 0xFF) << 24); |
1036 | |
1037 | return insn->n; |
1038 | } |
1039 | |
1040 | static int me4000_dio_insn_config(struct comedi_device *dev, |
1041 | struct comedi_subdevice *s, |
1042 | struct comedi_insn *insn, |
1043 | unsigned int *data) |
1044 | { |
1045 | unsigned int chan = CR_CHAN(insn->chanspec); |
1046 | unsigned int mask; |
1047 | unsigned int tmp; |
1048 | int ret; |
1049 | |
1050 | if (chan < 8) |
1051 | mask = 0x000000ff; |
1052 | else if (chan < 16) |
1053 | mask = 0x0000ff00; |
1054 | else if (chan < 24) |
1055 | mask = 0x00ff0000; |
1056 | else |
1057 | mask = 0xff000000; |
1058 | |
1059 | ret = comedi_dio_insn_config(dev, s, insn, data, mask); |
1060 | if (ret) |
1061 | return ret; |
1062 | |
1063 | tmp = inl(port: dev->iobase + ME4000_DIO_CTRL_REG); |
1064 | tmp &= ~(ME4000_DIO_CTRL_MODE_0 | ME4000_DIO_CTRL_MODE_1 | |
1065 | ME4000_DIO_CTRL_MODE_2 | ME4000_DIO_CTRL_MODE_3 | |
1066 | ME4000_DIO_CTRL_MODE_4 | ME4000_DIO_CTRL_MODE_5 | |
1067 | ME4000_DIO_CTRL_MODE_6 | ME4000_DIO_CTRL_MODE_7); |
1068 | if (s->io_bits & 0x000000ff) |
1069 | tmp |= ME4000_DIO_CTRL_MODE_0; |
1070 | if (s->io_bits & 0x0000ff00) |
1071 | tmp |= ME4000_DIO_CTRL_MODE_2; |
1072 | if (s->io_bits & 0x00ff0000) |
1073 | tmp |= ME4000_DIO_CTRL_MODE_4; |
1074 | if (s->io_bits & 0xff000000) |
1075 | tmp |= ME4000_DIO_CTRL_MODE_6; |
1076 | |
1077 | /* |
1078 | * Check for optoisolated ME-4000 version. |
1079 | * If one the first port is a fixed output |
1080 | * port and the second is a fixed input port. |
1081 | */ |
1082 | if (inl(port: dev->iobase + ME4000_DIO_DIR_REG)) { |
1083 | s->io_bits |= 0x000000ff; |
1084 | s->io_bits &= ~0x0000ff00; |
1085 | tmp |= ME4000_DIO_CTRL_MODE_0; |
1086 | tmp &= ~(ME4000_DIO_CTRL_MODE_2 | ME4000_DIO_CTRL_MODE_3); |
1087 | } |
1088 | |
1089 | outl(value: tmp, port: dev->iobase + ME4000_DIO_CTRL_REG); |
1090 | |
1091 | return insn->n; |
1092 | } |
1093 | |
1094 | static int me4000_auto_attach(struct comedi_device *dev, |
1095 | unsigned long context) |
1096 | { |
1097 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
1098 | const struct me4000_board *board = NULL; |
1099 | struct me4000_private *devpriv; |
1100 | struct comedi_subdevice *s; |
1101 | int result; |
1102 | |
1103 | if (context < ARRAY_SIZE(me4000_boards)) |
1104 | board = &me4000_boards[context]; |
1105 | if (!board) |
1106 | return -ENODEV; |
1107 | dev->board_ptr = board; |
1108 | dev->board_name = board->name; |
1109 | |
1110 | devpriv = comedi_alloc_devpriv(dev, size: sizeof(*devpriv)); |
1111 | if (!devpriv) |
1112 | return -ENOMEM; |
1113 | |
1114 | result = comedi_pci_enable(dev); |
1115 | if (result) |
1116 | return result; |
1117 | |
1118 | devpriv->plx_regbase = pci_resource_start(pcidev, 1); |
1119 | dev->iobase = pci_resource_start(pcidev, 2); |
1120 | if (!devpriv->plx_regbase || !dev->iobase) |
1121 | return -ENODEV; |
1122 | |
1123 | result = comedi_load_firmware(dev, hw_dev: &pcidev->dev, ME4000_FIRMWARE, |
1124 | cb: me4000_xilinx_download, context: 0); |
1125 | if (result < 0) |
1126 | return result; |
1127 | |
1128 | me4000_reset(dev); |
1129 | |
1130 | if (pcidev->irq > 0) { |
1131 | result = request_irq(irq: pcidev->irq, handler: me4000_ai_isr, IRQF_SHARED, |
1132 | name: dev->board_name, dev); |
1133 | if (result == 0) { |
1134 | dev->irq = pcidev->irq; |
1135 | |
1136 | /* Enable interrupts on the PLX */ |
1137 | outl(PLX9052_INTCSR_LI1ENAB | PLX9052_INTCSR_LI1POL | |
1138 | PLX9052_INTCSR_PCIENAB, |
1139 | port: devpriv->plx_regbase + PLX9052_INTCSR); |
1140 | } |
1141 | } |
1142 | |
1143 | result = comedi_alloc_subdevices(dev, num_subdevices: 4); |
1144 | if (result) |
1145 | return result; |
1146 | |
1147 | /* Analog Input subdevice */ |
1148 | s = &dev->subdevices[0]; |
1149 | s->type = COMEDI_SUBD_AI; |
1150 | s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND; |
1151 | if (board->can_do_diff_ai) |
1152 | s->subdev_flags |= SDF_DIFF; |
1153 | s->n_chan = board->ai_nchan; |
1154 | s->maxdata = 0xffff; |
1155 | s->len_chanlist = ME4000_AI_CHANNEL_LIST_COUNT; |
1156 | s->range_table = &me4000_ai_range; |
1157 | s->insn_read = me4000_ai_insn_read; |
1158 | |
1159 | if (dev->irq) { |
1160 | dev->read_subdev = s; |
1161 | s->subdev_flags |= SDF_CMD_READ; |
1162 | s->cancel = me4000_ai_cancel; |
1163 | s->do_cmdtest = me4000_ai_do_cmd_test; |
1164 | s->do_cmd = me4000_ai_do_cmd; |
1165 | } |
1166 | |
1167 | /* Analog Output subdevice */ |
1168 | s = &dev->subdevices[1]; |
1169 | if (board->has_ao) { |
1170 | s->type = COMEDI_SUBD_AO; |
1171 | s->subdev_flags = SDF_WRITABLE | SDF_COMMON | SDF_GROUND; |
1172 | s->n_chan = 4; |
1173 | s->maxdata = 0xffff; |
1174 | s->range_table = &range_bipolar10; |
1175 | s->insn_write = me4000_ao_insn_write; |
1176 | |
1177 | result = comedi_alloc_subdev_readback(s); |
1178 | if (result) |
1179 | return result; |
1180 | } else { |
1181 | s->type = COMEDI_SUBD_UNUSED; |
1182 | } |
1183 | |
1184 | /* Digital I/O subdevice */ |
1185 | s = &dev->subdevices[2]; |
1186 | s->type = COMEDI_SUBD_DIO; |
1187 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; |
1188 | s->n_chan = 32; |
1189 | s->maxdata = 1; |
1190 | s->range_table = &range_digital; |
1191 | s->insn_bits = me4000_dio_insn_bits; |
1192 | s->insn_config = me4000_dio_insn_config; |
1193 | |
1194 | /* |
1195 | * Check for optoisolated ME-4000 version. If one the first |
1196 | * port is a fixed output port and the second is a fixed input port. |
1197 | */ |
1198 | if (!inl(port: dev->iobase + ME4000_DIO_DIR_REG)) { |
1199 | s->io_bits |= 0xFF; |
1200 | outl(ME4000_DIO_CTRL_MODE_0, |
1201 | port: dev->iobase + ME4000_DIO_DIR_REG); |
1202 | } |
1203 | |
1204 | /* Counter subdevice (8254) */ |
1205 | s = &dev->subdevices[3]; |
1206 | if (board->has_counter) { |
1207 | unsigned long timer_base = pci_resource_start(pcidev, 3); |
1208 | |
1209 | if (!timer_base) |
1210 | return -ENODEV; |
1211 | |
1212 | dev->pacer = comedi_8254_io_alloc(iobase: timer_base, osc_base: 0, I8254_IO8, regshift: 0); |
1213 | if (IS_ERR(ptr: dev->pacer)) |
1214 | return PTR_ERR(ptr: dev->pacer); |
1215 | |
1216 | comedi_8254_subdevice_init(s, i8254: dev->pacer); |
1217 | } else { |
1218 | s->type = COMEDI_SUBD_UNUSED; |
1219 | } |
1220 | |
1221 | return 0; |
1222 | } |
1223 | |
1224 | static void me4000_detach(struct comedi_device *dev) |
1225 | { |
1226 | if (dev->irq) { |
1227 | struct me4000_private *devpriv = dev->private; |
1228 | |
1229 | /* Disable interrupts on the PLX */ |
1230 | outl(value: 0, port: devpriv->plx_regbase + PLX9052_INTCSR); |
1231 | } |
1232 | comedi_pci_detach(dev); |
1233 | } |
1234 | |
1235 | static struct comedi_driver me4000_driver = { |
1236 | .driver_name = "me4000" , |
1237 | .module = THIS_MODULE, |
1238 | .auto_attach = me4000_auto_attach, |
1239 | .detach = me4000_detach, |
1240 | }; |
1241 | |
1242 | static int me4000_pci_probe(struct pci_dev *dev, |
1243 | const struct pci_device_id *id) |
1244 | { |
1245 | return comedi_pci_auto_config(pcidev: dev, driver: &me4000_driver, context: id->driver_data); |
1246 | } |
1247 | |
1248 | static const struct pci_device_id me4000_pci_table[] = { |
1249 | { PCI_VDEVICE(MEILHAUS, 0x4650), BOARD_ME4650 }, |
1250 | { PCI_VDEVICE(MEILHAUS, 0x4660), BOARD_ME4660 }, |
1251 | { PCI_VDEVICE(MEILHAUS, 0x4661), BOARD_ME4660I }, |
1252 | { PCI_VDEVICE(MEILHAUS, 0x4662), BOARD_ME4660S }, |
1253 | { PCI_VDEVICE(MEILHAUS, 0x4663), BOARD_ME4660IS }, |
1254 | { PCI_VDEVICE(MEILHAUS, 0x4670), BOARD_ME4670 }, |
1255 | { PCI_VDEVICE(MEILHAUS, 0x4671), BOARD_ME4670I }, |
1256 | { PCI_VDEVICE(MEILHAUS, 0x4672), BOARD_ME4670S }, |
1257 | { PCI_VDEVICE(MEILHAUS, 0x4673), BOARD_ME4670IS }, |
1258 | { PCI_VDEVICE(MEILHAUS, 0x4680), BOARD_ME4680 }, |
1259 | { PCI_VDEVICE(MEILHAUS, 0x4681), BOARD_ME4680I }, |
1260 | { PCI_VDEVICE(MEILHAUS, 0x4682), BOARD_ME4680S }, |
1261 | { PCI_VDEVICE(MEILHAUS, 0x4683), BOARD_ME4680IS }, |
1262 | { 0 } |
1263 | }; |
1264 | MODULE_DEVICE_TABLE(pci, me4000_pci_table); |
1265 | |
1266 | static struct pci_driver me4000_pci_driver = { |
1267 | .name = "me4000" , |
1268 | .id_table = me4000_pci_table, |
1269 | .probe = me4000_pci_probe, |
1270 | .remove = comedi_pci_auto_unconfig, |
1271 | }; |
1272 | module_comedi_pci_driver(me4000_driver, me4000_pci_driver); |
1273 | |
1274 | MODULE_AUTHOR("Comedi https://www.comedi.org" ); |
1275 | MODULE_DESCRIPTION("Comedi driver for Meilhaus ME-4000 series boards" ); |
1276 | MODULE_LICENSE("GPL" ); |
1277 | MODULE_FIRMWARE(ME4000_FIRMWARE); |
1278 | |