Commit | Line | Data |
---|---|---|
e55c95a3 GG |
1 | /* |
2 | comedi/drivers/me4000.c | |
3 | Source code for the Meilhaus ME-4000 board family. | |
4 | ||
5 | COMEDI - Linux Control and Measurement Device Interface | |
6 | Copyright (C) 2000 David A. Schleef <ds@schleef.org> | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program; if not, write to the Free Software | |
20 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
21 | ||
22 | */ | |
23 | /* | |
24 | Driver: me4000 | |
25 | Description: Meilhaus ME-4000 series boards | |
26 | Devices: [Meilhaus] ME-4650 (me4000), ME-4670i, ME-4680, ME-4680i, ME-4680is | |
27 | Author: gg (Guenter Gebhardt <g.gebhardt@meilhaus.com>) | |
28 | Updated: Mon, 18 Mar 2002 15:34:01 -0800 | |
29 | Status: broken (no support for loading firmware) | |
30 | ||
31 | Supports: | |
32 | ||
33 | - Analog Input | |
34 | - Analog Output | |
35 | - Digital I/O | |
36 | - Counter | |
37 | ||
5f8f8d43 | 38 | Configuration Options: not applicable, uses PCI auto config |
e55c95a3 GG |
39 | |
40 | The firmware required by these boards is available in the | |
41 | comedi_nonfree_firmware tarball available from | |
42 | http://www.comedi.org. However, the driver's support for | |
43 | loading the firmware through comedi_config is currently | |
44 | broken. | |
45 | ||
46 | */ | |
47 | ||
33782dd5 | 48 | #include <linux/pci.h> |
e55c95a3 | 49 | #include <linux/delay.h> |
33782dd5 | 50 | #include <linux/interrupt.h> |
e55c95a3 GG |
51 | #include <linux/list.h> |
52 | #include <linux/spinlock.h> | |
53 | ||
33782dd5 HS |
54 | #include "../comedidev.h" |
55 | ||
27020ffe | 56 | #include "comedi_fc.h" |
db9132e0 | 57 | #include "8253.h" |
58af6b92 | 58 | #include "plx9052.h" |
81dd1811 | 59 | |
e55c95a3 GG |
60 | #if 0 |
61 | /* file removed due to GPL incompatibility */ | |
62 | #include "me4000_fw.h" | |
63 | #endif | |
64 | ||
81dd1811 HS |
65 | /* |
66 | * ME4000 Register map and bit defines | |
67 | */ | |
68 | #define ME4000_AO_CHAN(x) ((x) * 0x18) | |
69 | ||
70 | #define ME4000_AO_CTRL_REG(x) (0x00 + ME4000_AO_CHAN(x)) | |
71 | #define ME4000_AO_CTRL_BIT_MODE_0 (1 << 0) | |
72 | #define ME4000_AO_CTRL_BIT_MODE_1 (1 << 1) | |
73 | #define ME4000_AO_CTRL_MASK_MODE (3 << 0) | |
74 | #define ME4000_AO_CTRL_BIT_STOP (1 << 2) | |
75 | #define ME4000_AO_CTRL_BIT_ENABLE_FIFO (1 << 3) | |
76 | #define ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG (1 << 4) | |
77 | #define ME4000_AO_CTRL_BIT_EX_TRIG_EDGE (1 << 5) | |
78 | #define ME4000_AO_CTRL_BIT_IMMEDIATE_STOP (1 << 7) | |
79 | #define ME4000_AO_CTRL_BIT_ENABLE_DO (1 << 8) | |
80 | #define ME4000_AO_CTRL_BIT_ENABLE_IRQ (1 << 9) | |
81 | #define ME4000_AO_CTRL_BIT_RESET_IRQ (1 << 10) | |
82 | #define ME4000_AO_STATUS_REG(x) (0x04 + ME4000_AO_CHAN(x)) | |
83 | #define ME4000_AO_STATUS_BIT_FSM (1 << 0) | |
84 | #define ME4000_AO_STATUS_BIT_FF (1 << 1) | |
85 | #define ME4000_AO_STATUS_BIT_HF (1 << 2) | |
86 | #define ME4000_AO_STATUS_BIT_EF (1 << 3) | |
87 | #define ME4000_AO_FIFO_REG(x) (0x08 + ME4000_AO_CHAN(x)) | |
88 | #define ME4000_AO_SINGLE_REG(x) (0x0c + ME4000_AO_CHAN(x)) | |
89 | #define ME4000_AO_TIMER_REG(x) (0x10 + ME4000_AO_CHAN(x)) | |
90 | #define ME4000_AI_CTRL_REG 0x74 | |
91 | #define ME4000_AI_STATUS_REG 0x74 | |
92 | #define ME4000_AI_CTRL_BIT_MODE_0 (1 << 0) | |
93 | #define ME4000_AI_CTRL_BIT_MODE_1 (1 << 1) | |
94 | #define ME4000_AI_CTRL_BIT_MODE_2 (1 << 2) | |
95 | #define ME4000_AI_CTRL_BIT_SAMPLE_HOLD (1 << 3) | |
96 | #define ME4000_AI_CTRL_BIT_IMMEDIATE_STOP (1 << 4) | |
97 | #define ME4000_AI_CTRL_BIT_STOP (1 << 5) | |
98 | #define ME4000_AI_CTRL_BIT_CHANNEL_FIFO (1 << 6) | |
99 | #define ME4000_AI_CTRL_BIT_DATA_FIFO (1 << 7) | |
100 | #define ME4000_AI_CTRL_BIT_FULLSCALE (1 << 8) | |
101 | #define ME4000_AI_CTRL_BIT_OFFSET (1 << 9) | |
102 | #define ME4000_AI_CTRL_BIT_EX_TRIG_ANALOG (1 << 10) | |
103 | #define ME4000_AI_CTRL_BIT_EX_TRIG (1 << 11) | |
104 | #define ME4000_AI_CTRL_BIT_EX_TRIG_FALLING (1 << 12) | |
105 | #define ME4000_AI_CTRL_BIT_EX_IRQ (1 << 13) | |
106 | #define ME4000_AI_CTRL_BIT_EX_IRQ_RESET (1 << 14) | |
107 | #define ME4000_AI_CTRL_BIT_LE_IRQ (1 << 15) | |
108 | #define ME4000_AI_CTRL_BIT_LE_IRQ_RESET (1 << 16) | |
109 | #define ME4000_AI_CTRL_BIT_HF_IRQ (1 << 17) | |
110 | #define ME4000_AI_CTRL_BIT_HF_IRQ_RESET (1 << 18) | |
111 | #define ME4000_AI_CTRL_BIT_SC_IRQ (1 << 19) | |
112 | #define ME4000_AI_CTRL_BIT_SC_IRQ_RESET (1 << 20) | |
113 | #define ME4000_AI_CTRL_BIT_SC_RELOAD (1 << 21) | |
114 | #define ME4000_AI_STATUS_BIT_EF_CHANNEL (1 << 22) | |
115 | #define ME4000_AI_STATUS_BIT_HF_CHANNEL (1 << 23) | |
116 | #define ME4000_AI_STATUS_BIT_FF_CHANNEL (1 << 24) | |
117 | #define ME4000_AI_STATUS_BIT_EF_DATA (1 << 25) | |
118 | #define ME4000_AI_STATUS_BIT_HF_DATA (1 << 26) | |
119 | #define ME4000_AI_STATUS_BIT_FF_DATA (1 << 27) | |
120 | #define ME4000_AI_STATUS_BIT_LE (1 << 28) | |
121 | #define ME4000_AI_STATUS_BIT_FSM (1 << 29) | |
122 | #define ME4000_AI_CTRL_BIT_EX_TRIG_BOTH (1 << 31) | |
123 | #define ME4000_AI_CHANNEL_LIST_REG 0x78 | |
124 | #define ME4000_AI_LIST_INPUT_SINGLE_ENDED (0 << 5) | |
125 | #define ME4000_AI_LIST_INPUT_DIFFERENTIAL (1 << 5) | |
126 | #define ME4000_AI_LIST_RANGE_BIPOLAR_10 (0 << 6) | |
127 | #define ME4000_AI_LIST_RANGE_BIPOLAR_2_5 (1 << 6) | |
128 | #define ME4000_AI_LIST_RANGE_UNIPOLAR_10 (2 << 6) | |
129 | #define ME4000_AI_LIST_RANGE_UNIPOLAR_2_5 (3 << 6) | |
130 | #define ME4000_AI_LIST_LAST_ENTRY (1 << 8) | |
131 | #define ME4000_AI_DATA_REG 0x7c | |
132 | #define ME4000_AI_CHAN_TIMER_REG 0x80 | |
133 | #define ME4000_AI_CHAN_PRE_TIMER_REG 0x84 | |
134 | #define ME4000_AI_SCAN_TIMER_LOW_REG 0x88 | |
135 | #define ME4000_AI_SCAN_TIMER_HIGH_REG 0x8c | |
136 | #define ME4000_AI_SCAN_PRE_TIMER_LOW_REG 0x90 | |
137 | #define ME4000_AI_SCAN_PRE_TIMER_HIGH_REG 0x94 | |
138 | #define ME4000_AI_START_REG 0x98 | |
139 | #define ME4000_IRQ_STATUS_REG 0x9c | |
140 | #define ME4000_IRQ_STATUS_BIT_EX (1 << 0) | |
141 | #define ME4000_IRQ_STATUS_BIT_LE (1 << 1) | |
142 | #define ME4000_IRQ_STATUS_BIT_AI_HF (1 << 2) | |
143 | #define ME4000_IRQ_STATUS_BIT_AO_0_HF (1 << 3) | |
144 | #define ME4000_IRQ_STATUS_BIT_AO_1_HF (1 << 4) | |
145 | #define ME4000_IRQ_STATUS_BIT_AO_2_HF (1 << 5) | |
146 | #define ME4000_IRQ_STATUS_BIT_AO_3_HF (1 << 6) | |
147 | #define ME4000_IRQ_STATUS_BIT_SC (1 << 7) | |
148 | #define ME4000_DIO_PORT_0_REG 0xa0 | |
149 | #define ME4000_DIO_PORT_1_REG 0xa4 | |
150 | #define ME4000_DIO_PORT_2_REG 0xa8 | |
151 | #define ME4000_DIO_PORT_3_REG 0xac | |
152 | #define ME4000_DIO_DIR_REG 0xb0 | |
153 | #define ME4000_AO_LOADSETREG_XX 0xb4 | |
154 | #define ME4000_DIO_CTRL_REG 0xb8 | |
155 | #define ME4000_DIO_CTRL_BIT_MODE_0 (1 << 0) | |
156 | #define ME4000_DIO_CTRL_BIT_MODE_1 (1 << 1) | |
157 | #define ME4000_DIO_CTRL_BIT_MODE_2 (1 << 2) | |
158 | #define ME4000_DIO_CTRL_BIT_MODE_3 (1 << 3) | |
159 | #define ME4000_DIO_CTRL_BIT_MODE_4 (1 << 4) | |
160 | #define ME4000_DIO_CTRL_BIT_MODE_5 (1 << 5) | |
161 | #define ME4000_DIO_CTRL_BIT_MODE_6 (1 << 6) | |
162 | #define ME4000_DIO_CTRL_BIT_MODE_7 (1 << 7) | |
163 | #define ME4000_DIO_CTRL_BIT_FUNCTION_0 (1 << 8) | |
164 | #define ME4000_DIO_CTRL_BIT_FUNCTION_1 (1 << 9) | |
165 | #define ME4000_DIO_CTRL_BIT_FIFO_HIGH_0 (1 << 10) | |
166 | #define ME4000_DIO_CTRL_BIT_FIFO_HIGH_1 (1 << 11) | |
167 | #define ME4000_DIO_CTRL_BIT_FIFO_HIGH_2 (1 << 12) | |
168 | #define ME4000_DIO_CTRL_BIT_FIFO_HIGH_3 (1 << 13) | |
169 | #define ME4000_AO_DEMUX_ADJUST_REG 0xbc | |
170 | #define ME4000_AO_DEMUX_ADJUST_VALUE 0x4c | |
171 | #define ME4000_AI_SAMPLE_COUNTER_REG 0xc0 | |
172 | ||
173 | /* | |
174 | * PLX Register map and bit defines | |
175 | */ | |
81dd1811 HS |
176 | #define PLX_ICR 0x50 |
177 | #define PLX_ICR_BIT_EEPROM_CLOCK_SET (1 << 24) | |
178 | #define PLX_ICR_BIT_EEPROM_CHIP_SELECT (1 << 25) | |
179 | #define PLX_ICR_BIT_EEPROM_WRITE (1 << 26) | |
180 | #define PLX_ICR_BIT_EEPROM_READ (1 << 27) | |
181 | #define PLX_ICR_BIT_EEPROM_VALID (1 << 28) | |
182 | #define PLX_ICR_MASK_EEPROM (0x1f << 24) | |
183 | ||
184 | #define EEPROM_DELAY 1 | |
185 | ||
186 | #define ME4000_AI_FIFO_COUNT 2048 | |
187 | ||
188 | #define ME4000_AI_MIN_TICKS 66 | |
189 | #define ME4000_AI_MIN_SAMPLE_TIME 2000 | |
190 | #define ME4000_AI_BASE_FREQUENCY (unsigned int) 33E6 | |
191 | ||
192 | #define ME4000_AI_CHANNEL_LIST_COUNT 1024 | |
193 | ||
cc6f3336 HS |
194 | struct me4000_info { |
195 | unsigned long plx_regbase; | |
196 | unsigned long timer_regbase; | |
197 | ||
198 | unsigned int ao_readback[4]; | |
199 | }; | |
200 | ||
8c355509 HS |
201 | enum me4000_boardid { |
202 | BOARD_ME4650, | |
203 | BOARD_ME4660, | |
204 | BOARD_ME4660I, | |
205 | BOARD_ME4660S, | |
206 | BOARD_ME4660IS, | |
207 | BOARD_ME4670, | |
208 | BOARD_ME4670I, | |
209 | BOARD_ME4670S, | |
210 | BOARD_ME4670IS, | |
211 | BOARD_ME4680, | |
212 | BOARD_ME4680I, | |
213 | BOARD_ME4680S, | |
214 | BOARD_ME4680IS, | |
215 | }; | |
216 | ||
06b60981 HS |
217 | struct me4000_board { |
218 | const char *name; | |
06b60981 HS |
219 | int ao_nchan; |
220 | int ao_fifo; | |
221 | int ai_nchan; | |
222 | int ai_diff_nchan; | |
223 | int ai_sh_nchan; | |
224 | int ex_trig_analog; | |
225 | int dio_nchan; | |
226 | int has_counter; | |
227 | }; | |
228 | ||
27f4caaa | 229 | static const struct me4000_board me4000_boards[] = { |
8c355509 | 230 | [BOARD_ME4650] = { |
035d432a | 231 | .name = "ME-4650", |
6ba8dfef | 232 | .ai_nchan = 16, |
898f5191 | 233 | .dio_nchan = 32, |
8c355509 HS |
234 | }, |
235 | [BOARD_ME4660] = { | |
035d432a | 236 | .name = "ME-4660", |
6ba8dfef HS |
237 | .ai_nchan = 32, |
238 | .ai_diff_nchan = 16, | |
898f5191 | 239 | .dio_nchan = 32, |
eedf4299 | 240 | .has_counter = 1, |
8c355509 HS |
241 | }, |
242 | [BOARD_ME4660I] = { | |
035d432a | 243 | .name = "ME-4660i", |
6ba8dfef HS |
244 | .ai_nchan = 32, |
245 | .ai_diff_nchan = 16, | |
898f5191 | 246 | .dio_nchan = 32, |
eedf4299 | 247 | .has_counter = 1, |
8c355509 HS |
248 | }, |
249 | [BOARD_ME4660S] = { | |
035d432a | 250 | .name = "ME-4660s", |
6ba8dfef HS |
251 | .ai_nchan = 32, |
252 | .ai_diff_nchan = 16, | |
253 | .ai_sh_nchan = 8, | |
898f5191 | 254 | .dio_nchan = 32, |
eedf4299 | 255 | .has_counter = 1, |
8c355509 HS |
256 | }, |
257 | [BOARD_ME4660IS] = { | |
035d432a | 258 | .name = "ME-4660is", |
6ba8dfef HS |
259 | .ai_nchan = 32, |
260 | .ai_diff_nchan = 16, | |
261 | .ai_sh_nchan = 8, | |
898f5191 | 262 | .dio_nchan = 32, |
eedf4299 | 263 | .has_counter = 1, |
8c355509 HS |
264 | }, |
265 | [BOARD_ME4670] = { | |
035d432a | 266 | .name = "ME-4670", |
2d504528 | 267 | .ao_nchan = 4, |
6ba8dfef HS |
268 | .ai_nchan = 32, |
269 | .ai_diff_nchan = 16, | |
270 | .ex_trig_analog = 1, | |
898f5191 | 271 | .dio_nchan = 32, |
eedf4299 | 272 | .has_counter = 1, |
8c355509 HS |
273 | }, |
274 | [BOARD_ME4670I] = { | |
035d432a | 275 | .name = "ME-4670i", |
2d504528 | 276 | .ao_nchan = 4, |
6ba8dfef HS |
277 | .ai_nchan = 32, |
278 | .ai_diff_nchan = 16, | |
279 | .ex_trig_analog = 1, | |
898f5191 | 280 | .dio_nchan = 32, |
eedf4299 | 281 | .has_counter = 1, |
8c355509 HS |
282 | }, |
283 | [BOARD_ME4670S] = { | |
035d432a | 284 | .name = "ME-4670s", |
2d504528 | 285 | .ao_nchan = 4, |
6ba8dfef HS |
286 | .ai_nchan = 32, |
287 | .ai_diff_nchan = 16, | |
288 | .ai_sh_nchan = 8, | |
289 | .ex_trig_analog = 1, | |
898f5191 | 290 | .dio_nchan = 32, |
eedf4299 | 291 | .has_counter = 1, |
8c355509 HS |
292 | }, |
293 | [BOARD_ME4670IS] = { | |
035d432a | 294 | .name = "ME-4670is", |
2d504528 | 295 | .ao_nchan = 4, |
6ba8dfef HS |
296 | .ai_nchan = 32, |
297 | .ai_diff_nchan = 16, | |
298 | .ai_sh_nchan = 8, | |
299 | .ex_trig_analog = 1, | |
898f5191 | 300 | .dio_nchan = 32, |
eedf4299 | 301 | .has_counter = 1, |
8c355509 HS |
302 | }, |
303 | [BOARD_ME4680] = { | |
035d432a | 304 | .name = "ME-4680", |
2d504528 HS |
305 | .ao_nchan = 4, |
306 | .ao_fifo = 4, | |
6ba8dfef HS |
307 | .ai_nchan = 32, |
308 | .ai_diff_nchan = 16, | |
309 | .ex_trig_analog = 1, | |
898f5191 | 310 | .dio_nchan = 32, |
eedf4299 | 311 | .has_counter = 1, |
8c355509 HS |
312 | }, |
313 | [BOARD_ME4680I] = { | |
035d432a | 314 | .name = "ME-4680i", |
2d504528 HS |
315 | .ao_nchan = 4, |
316 | .ao_fifo = 4, | |
6ba8dfef HS |
317 | .ai_nchan = 32, |
318 | .ai_diff_nchan = 16, | |
319 | .ex_trig_analog = 1, | |
898f5191 | 320 | .dio_nchan = 32, |
eedf4299 | 321 | .has_counter = 1, |
8c355509 HS |
322 | }, |
323 | [BOARD_ME4680S] = { | |
035d432a | 324 | .name = "ME-4680s", |
2d504528 HS |
325 | .ao_nchan = 4, |
326 | .ao_fifo = 4, | |
6ba8dfef HS |
327 | .ai_nchan = 32, |
328 | .ai_diff_nchan = 16, | |
329 | .ai_sh_nchan = 8, | |
330 | .ex_trig_analog = 1, | |
898f5191 | 331 | .dio_nchan = 32, |
eedf4299 | 332 | .has_counter = 1, |
8c355509 HS |
333 | }, |
334 | [BOARD_ME4680IS] = { | |
035d432a | 335 | .name = "ME-4680is", |
2d504528 HS |
336 | .ao_nchan = 4, |
337 | .ao_fifo = 4, | |
6ba8dfef HS |
338 | .ai_nchan = 32, |
339 | .ai_diff_nchan = 16, | |
340 | .ai_sh_nchan = 8, | |
341 | .ex_trig_analog = 1, | |
898f5191 | 342 | .dio_nchan = 32, |
eedf4299 | 343 | .has_counter = 1, |
035d432a | 344 | }, |
e55c95a3 GG |
345 | }; |
346 | ||
9ced1de6 | 347 | static const struct comedi_lrange me4000_ai_range = { |
e55c95a3 GG |
348 | 4, |
349 | { | |
0a85b6f0 MT |
350 | UNI_RANGE(2.5), |
351 | UNI_RANGE(10), | |
352 | BIP_RANGE(2.5), | |
353 | BIP_RANGE(10), | |
354 | } | |
e55c95a3 GG |
355 | }; |
356 | ||
e55c95a3 GG |
357 | #define FIRMWARE_NOT_AVAILABLE 1 |
358 | #if FIRMWARE_NOT_AVAILABLE | |
359 | extern unsigned char *xilinx_firm; | |
360 | #endif | |
361 | ||
71b5f4f1 | 362 | static int xilinx_download(struct comedi_device *dev) |
e55c95a3 | 363 | { |
fe531d12 | 364 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
09253b39 | 365 | struct me4000_info *info = dev->private; |
fe531d12 | 366 | unsigned long xilinx_iobase = pci_resource_start(pcidev, 5); |
e55c95a3 GG |
367 | u32 value = 0; |
368 | wait_queue_head_t queue; | |
369 | int idx = 0; | |
370 | int size = 0; | |
58af6b92 | 371 | unsigned int intcsr; |
e55c95a3 | 372 | |
fe531d12 HS |
373 | if (!xilinx_iobase) |
374 | return -ENODEV; | |
375 | ||
e55c95a3 GG |
376 | init_waitqueue_head(&queue); |
377 | ||
378 | /* | |
379 | * Set PLX local interrupt 2 polarity to high. | |
380 | * Interrupt is thrown by init pin of xilinx. | |
381 | */ | |
58af6b92 | 382 | outl(PLX9052_INTCSR_LI2POL, info->plx_regbase + PLX9052_INTCSR); |
e55c95a3 GG |
383 | |
384 | /* Set /CS and /WRITE of the Xilinx */ | |
385 | value = inl(info->plx_regbase + PLX_ICR); | |
386 | value |= 0x100; | |
387 | outl(value, info->plx_regbase + PLX_ICR); | |
388 | ||
389 | /* Init Xilinx with CS1 */ | |
fe531d12 | 390 | inb(xilinx_iobase + 0xC8); |
e55c95a3 GG |
391 | |
392 | /* Wait until /INIT pin is set */ | |
393 | udelay(20); | |
58af6b92 HS |
394 | intcsr = inl(info->plx_regbase + PLX9052_INTCSR); |
395 | if (!(intcsr & PLX9052_INTCSR_LI2STAT)) { | |
5da80ee8 | 396 | dev_err(dev->class_dev, "Can't init Xilinx\n"); |
e55c95a3 GG |
397 | return -EIO; |
398 | } | |
399 | ||
400 | /* Reset /CS and /WRITE of the Xilinx */ | |
401 | value = inl(info->plx_regbase + PLX_ICR); | |
402 | value &= ~0x100; | |
403 | outl(value, info->plx_regbase + PLX_ICR); | |
404 | if (FIRMWARE_NOT_AVAILABLE) { | |
5da80ee8 HS |
405 | dev_err(dev->class_dev, |
406 | "xilinx firmware unavailable due to licensing, aborting"); | |
e55c95a3 GG |
407 | return -EIO; |
408 | } else { | |
409 | /* Download Xilinx firmware */ | |
410 | size = (xilinx_firm[0] << 24) + (xilinx_firm[1] << 16) + | |
0a85b6f0 | 411 | (xilinx_firm[2] << 8) + xilinx_firm[3]; |
e55c95a3 GG |
412 | udelay(10); |
413 | ||
414 | for (idx = 0; idx < size; idx++) { | |
fe531d12 | 415 | outb(xilinx_firm[16 + idx], xilinx_iobase); |
e55c95a3 GG |
416 | udelay(10); |
417 | ||
418 | /* Check if BUSY flag is low */ | |
419 | if (inl(info->plx_regbase + PLX_ICR) & 0x20) { | |
5da80ee8 HS |
420 | dev_err(dev->class_dev, |
421 | "Xilinx is still busy (idx = %d)\n", | |
422 | idx); | |
e55c95a3 GG |
423 | return -EIO; |
424 | } | |
425 | } | |
426 | } | |
427 | ||
428 | /* If done flag is high download was successful */ | |
429 | if (inl(info->plx_regbase + PLX_ICR) & 0x4) { | |
430 | } else { | |
5da80ee8 HS |
431 | dev_err(dev->class_dev, "DONE flag is not set\n"); |
432 | dev_err(dev->class_dev, "Download not successful\n"); | |
e55c95a3 GG |
433 | return -EIO; |
434 | } | |
435 | ||
436 | /* Set /CS and /WRITE */ | |
437 | value = inl(info->plx_regbase + PLX_ICR); | |
438 | value |= 0x100; | |
439 | outl(value, info->plx_regbase + PLX_ICR); | |
440 | ||
441 | return 0; | |
442 | } | |
443 | ||
2f348ecd | 444 | static void me4000_reset(struct comedi_device *dev) |
e55c95a3 | 445 | { |
09253b39 | 446 | struct me4000_info *info = dev->private; |
e1d7ccb7 HS |
447 | unsigned long val; |
448 | int chan; | |
e55c95a3 | 449 | |
e55c95a3 | 450 | /* Make a hardware reset */ |
e1d7ccb7 HS |
451 | val = inl(info->plx_regbase + PLX_ICR); |
452 | val |= 0x40000000; | |
453 | outl(val, info->plx_regbase + PLX_ICR); | |
454 | val &= ~0x40000000; | |
455 | outl(val , info->plx_regbase + PLX_ICR); | |
e55c95a3 GG |
456 | |
457 | /* 0x8000 to the DACs means an output voltage of 0V */ | |
e1d7ccb7 HS |
458 | for (chan = 0; chan < 4; chan++) |
459 | outl(0x8000, dev->iobase + ME4000_AO_SINGLE_REG(chan)); | |
e55c95a3 GG |
460 | |
461 | /* Set both stop bits in the analog input control register */ | |
d6cbe537 | 462 | outl(ME4000_AI_CTRL_BIT_IMMEDIATE_STOP | ME4000_AI_CTRL_BIT_STOP, |
362bcbde | 463 | dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
464 | |
465 | /* Set both stop bits in the analog output control register */ | |
e1d7ccb7 HS |
466 | val = ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP; |
467 | for (chan = 0; chan < 4; chan++) | |
468 | outl(val, dev->iobase + ME4000_AO_CTRL_REG(chan)); | |
e55c95a3 GG |
469 | |
470 | /* Enable interrupts on the PLX */ | |
58af6b92 HS |
471 | outl(PLX9052_INTCSR_LI1ENAB | |
472 | PLX9052_INTCSR_LI1POL | | |
473 | PLX9052_INTCSR_PCIENAB, info->plx_regbase + PLX9052_INTCSR); | |
e55c95a3 GG |
474 | |
475 | /* Set the adustment register for AO demux */ | |
d6cbe537 | 476 | outl(ME4000_AO_DEMUX_ADJUST_VALUE, |
362bcbde | 477 | dev->iobase + ME4000_AO_DEMUX_ADJUST_REG); |
e55c95a3 | 478 | |
b6241fda GS |
479 | /* |
480 | * Set digital I/O direction for port 0 | |
481 | * to output on isolated versions | |
482 | */ | |
362bcbde HS |
483 | if (!(inl(dev->iobase + ME4000_DIO_DIR_REG) & 0x1)) |
484 | outl(0x1, dev->iobase + ME4000_DIO_CTRL_REG); | |
e55c95a3 GG |
485 | } |
486 | ||
e55c95a3 GG |
487 | /*============================================================================= |
488 | Analog input section | |
489 | ===========================================================================*/ | |
490 | ||
71b5f4f1 | 491 | static int me4000_ai_insn_read(struct comedi_device *dev, |
0a85b6f0 MT |
492 | struct comedi_subdevice *subdevice, |
493 | struct comedi_insn *insn, unsigned int *data) | |
e55c95a3 | 494 | { |
8407593a | 495 | const struct me4000_board *thisboard = comedi_board(dev); |
e55c95a3 GG |
496 | int chan = CR_CHAN(insn->chanspec); |
497 | int rang = CR_RANGE(insn->chanspec); | |
498 | int aref = CR_AREF(insn->chanspec); | |
499 | ||
500 | unsigned long entry = 0; | |
501 | unsigned long tmp; | |
502 | long lval; | |
503 | ||
e55c95a3 GG |
504 | if (insn->n == 0) { |
505 | return 0; | |
506 | } else if (insn->n > 1) { | |
5da80ee8 HS |
507 | dev_err(dev->class_dev, "Invalid instruction length %d\n", |
508 | insn->n); | |
e55c95a3 GG |
509 | return -EINVAL; |
510 | } | |
511 | ||
512 | switch (rang) { | |
513 | case 0: | |
514 | entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_2_5; | |
515 | break; | |
516 | case 1: | |
517 | entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_10; | |
518 | break; | |
519 | case 2: | |
520 | entry |= ME4000_AI_LIST_RANGE_BIPOLAR_2_5; | |
521 | break; | |
522 | case 3: | |
523 | entry |= ME4000_AI_LIST_RANGE_BIPOLAR_10; | |
524 | break; | |
525 | default: | |
5da80ee8 | 526 | dev_err(dev->class_dev, "Invalid range specified\n"); |
e55c95a3 GG |
527 | return -EINVAL; |
528 | } | |
529 | ||
530 | switch (aref) { | |
531 | case AREF_GROUND: | |
532 | case AREF_COMMON: | |
6ba8dfef | 533 | if (chan >= thisboard->ai_nchan) { |
5da80ee8 HS |
534 | dev_err(dev->class_dev, |
535 | "Analog input is not available\n"); | |
e55c95a3 GG |
536 | return -EINVAL; |
537 | } | |
538 | entry |= ME4000_AI_LIST_INPUT_SINGLE_ENDED | chan; | |
539 | break; | |
540 | ||
541 | case AREF_DIFF: | |
542 | if (rang == 0 || rang == 1) { | |
5da80ee8 HS |
543 | dev_err(dev->class_dev, |
544 | "Range must be bipolar when aref = diff\n"); | |
e55c95a3 GG |
545 | return -EINVAL; |
546 | } | |
547 | ||
6ba8dfef | 548 | if (chan >= thisboard->ai_diff_nchan) { |
5da80ee8 HS |
549 | dev_err(dev->class_dev, |
550 | "Analog input is not available\n"); | |
e55c95a3 GG |
551 | return -EINVAL; |
552 | } | |
553 | entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL | chan; | |
554 | break; | |
555 | default: | |
5da80ee8 | 556 | dev_err(dev->class_dev, "Invalid aref specified\n"); |
e55c95a3 GG |
557 | return -EINVAL; |
558 | } | |
559 | ||
560 | entry |= ME4000_AI_LIST_LAST_ENTRY; | |
561 | ||
562 | /* Clear channel list, data fifo and both stop bits */ | |
b08bfa38 | 563 | tmp = inl(dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 | 564 | tmp &= ~(ME4000_AI_CTRL_BIT_CHANNEL_FIFO | |
0a85b6f0 MT |
565 | ME4000_AI_CTRL_BIT_DATA_FIFO | |
566 | ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP); | |
b08bfa38 | 567 | outl(tmp, dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
568 | |
569 | /* Set the acquisition mode to single */ | |
570 | tmp &= ~(ME4000_AI_CTRL_BIT_MODE_0 | ME4000_AI_CTRL_BIT_MODE_1 | | |
0a85b6f0 | 571 | ME4000_AI_CTRL_BIT_MODE_2); |
b08bfa38 | 572 | outl(tmp, dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
573 | |
574 | /* Enable channel list and data fifo */ | |
575 | tmp |= ME4000_AI_CTRL_BIT_CHANNEL_FIFO | ME4000_AI_CTRL_BIT_DATA_FIFO; | |
b08bfa38 | 576 | outl(tmp, dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
577 | |
578 | /* Generate channel list entry */ | |
b08bfa38 | 579 | outl(entry, dev->iobase + ME4000_AI_CHANNEL_LIST_REG); |
e55c95a3 GG |
580 | |
581 | /* Set the timer to maximum sample rate */ | |
b08bfa38 HS |
582 | outl(ME4000_AI_MIN_TICKS, dev->iobase + ME4000_AI_CHAN_TIMER_REG); |
583 | outl(ME4000_AI_MIN_TICKS, dev->iobase + ME4000_AI_CHAN_PRE_TIMER_REG); | |
e55c95a3 GG |
584 | |
585 | /* Start conversion by dummy read */ | |
b08bfa38 | 586 | inl(dev->iobase + ME4000_AI_START_REG); |
e55c95a3 GG |
587 | |
588 | /* Wait until ready */ | |
589 | udelay(10); | |
b08bfa38 | 590 | if (!(inl(dev->iobase + ME4000_AI_STATUS_REG) & |
0a85b6f0 | 591 | ME4000_AI_STATUS_BIT_EF_DATA)) { |
5da80ee8 | 592 | dev_err(dev->class_dev, "Value not available after wait\n"); |
e55c95a3 GG |
593 | return -EIO; |
594 | } | |
595 | ||
596 | /* Read value from data fifo */ | |
b08bfa38 | 597 | lval = inl(dev->iobase + ME4000_AI_DATA_REG) & 0xFFFF; |
e55c95a3 GG |
598 | data[0] = lval ^ 0x8000; |
599 | ||
600 | return 1; | |
601 | } | |
602 | ||
0a85b6f0 MT |
603 | static int me4000_ai_cancel(struct comedi_device *dev, |
604 | struct comedi_subdevice *s) | |
e55c95a3 GG |
605 | { |
606 | unsigned long tmp; | |
607 | ||
e55c95a3 | 608 | /* Stop any running conversion */ |
b08bfa38 | 609 | tmp = inl(dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 | 610 | tmp &= ~(ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP); |
b08bfa38 | 611 | outl(tmp, dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
612 | |
613 | /* Clear the control register */ | |
b08bfa38 | 614 | outl(0x0, dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
615 | |
616 | return 0; | |
617 | } | |
618 | ||
71b5f4f1 | 619 | static int ai_check_chanlist(struct comedi_device *dev, |
0a85b6f0 | 620 | struct comedi_subdevice *s, struct comedi_cmd *cmd) |
e55c95a3 | 621 | { |
8407593a | 622 | const struct me4000_board *thisboard = comedi_board(dev); |
e55c95a3 GG |
623 | int aref; |
624 | int i; | |
625 | ||
e55c95a3 GG |
626 | /* Check whether a channel list is available */ |
627 | if (!cmd->chanlist_len) { | |
5da80ee8 | 628 | dev_err(dev->class_dev, "No channel list available\n"); |
e55c95a3 GG |
629 | return -EINVAL; |
630 | } | |
631 | ||
632 | /* Check the channel list size */ | |
633 | if (cmd->chanlist_len > ME4000_AI_CHANNEL_LIST_COUNT) { | |
5da80ee8 | 634 | dev_err(dev->class_dev, "Channel list is to large\n"); |
e55c95a3 GG |
635 | return -EINVAL; |
636 | } | |
637 | ||
638 | /* Check the pointer */ | |
639 | if (!cmd->chanlist) { | |
5da80ee8 | 640 | dev_err(dev->class_dev, "NULL pointer to channel list\n"); |
e55c95a3 GG |
641 | return -EFAULT; |
642 | } | |
643 | ||
644 | /* Check whether aref is equal for all entries */ | |
645 | aref = CR_AREF(cmd->chanlist[0]); | |
646 | for (i = 0; i < cmd->chanlist_len; i++) { | |
647 | if (CR_AREF(cmd->chanlist[i]) != aref) { | |
5da80ee8 HS |
648 | dev_err(dev->class_dev, |
649 | "Mode is not equal for all entries\n"); | |
e55c95a3 GG |
650 | return -EINVAL; |
651 | } | |
652 | } | |
653 | ||
654 | /* Check whether channels are available for this ending */ | |
655 | if (aref == SDF_DIFF) { | |
656 | for (i = 0; i < cmd->chanlist_len; i++) { | |
657 | if (CR_CHAN(cmd->chanlist[i]) >= | |
6ba8dfef | 658 | thisboard->ai_diff_nchan) { |
5da80ee8 HS |
659 | dev_err(dev->class_dev, |
660 | "Channel number to high\n"); | |
e55c95a3 GG |
661 | return -EINVAL; |
662 | } | |
663 | } | |
664 | } else { | |
665 | for (i = 0; i < cmd->chanlist_len; i++) { | |
6ba8dfef | 666 | if (CR_CHAN(cmd->chanlist[i]) >= thisboard->ai_nchan) { |
5da80ee8 HS |
667 | dev_err(dev->class_dev, |
668 | "Channel number to high\n"); | |
e55c95a3 GG |
669 | return -EINVAL; |
670 | } | |
671 | } | |
672 | } | |
673 | ||
674 | /* Check if bipolar is set for all entries when in differential mode */ | |
675 | if (aref == SDF_DIFF) { | |
676 | for (i = 0; i < cmd->chanlist_len; i++) { | |
677 | if (CR_RANGE(cmd->chanlist[i]) != 1 && | |
0a85b6f0 | 678 | CR_RANGE(cmd->chanlist[i]) != 2) { |
5da80ee8 HS |
679 | dev_err(dev->class_dev, |
680 | "Bipolar is not selected in differential mode\n"); | |
e55c95a3 GG |
681 | return -EINVAL; |
682 | } | |
683 | } | |
684 | } | |
685 | ||
686 | return 0; | |
687 | } | |
688 | ||
71b5f4f1 | 689 | static int ai_round_cmd_args(struct comedi_device *dev, |
0a85b6f0 MT |
690 | struct comedi_subdevice *s, |
691 | struct comedi_cmd *cmd, | |
692 | unsigned int *init_ticks, | |
693 | unsigned int *scan_ticks, unsigned int *chan_ticks) | |
e55c95a3 GG |
694 | { |
695 | ||
696 | int rest; | |
697 | ||
e55c95a3 GG |
698 | *init_ticks = 0; |
699 | *scan_ticks = 0; | |
700 | *chan_ticks = 0; | |
701 | ||
e55c95a3 GG |
702 | if (cmd->start_arg) { |
703 | *init_ticks = (cmd->start_arg * 33) / 1000; | |
704 | rest = (cmd->start_arg * 33) % 1000; | |
705 | ||
91211dd1 | 706 | if ((cmd->flags & TRIG_ROUND_MASK) == TRIG_ROUND_NEAREST) { |
82675f35 | 707 | if (rest > 33) |
e55c95a3 | 708 | (*init_ticks)++; |
91211dd1 | 709 | } else if ((cmd->flags & TRIG_ROUND_MASK) == TRIG_ROUND_UP) { |
e55c95a3 GG |
710 | if (rest) |
711 | (*init_ticks)++; | |
712 | } | |
713 | } | |
714 | ||
715 | if (cmd->scan_begin_arg) { | |
716 | *scan_ticks = (cmd->scan_begin_arg * 33) / 1000; | |
717 | rest = (cmd->scan_begin_arg * 33) % 1000; | |
718 | ||
91211dd1 | 719 | if ((cmd->flags & TRIG_ROUND_MASK) == TRIG_ROUND_NEAREST) { |
82675f35 | 720 | if (rest > 33) |
e55c95a3 | 721 | (*scan_ticks)++; |
91211dd1 | 722 | } else if ((cmd->flags & TRIG_ROUND_MASK) == TRIG_ROUND_UP) { |
e55c95a3 GG |
723 | if (rest) |
724 | (*scan_ticks)++; | |
725 | } | |
726 | } | |
727 | ||
728 | if (cmd->convert_arg) { | |
729 | *chan_ticks = (cmd->convert_arg * 33) / 1000; | |
730 | rest = (cmd->convert_arg * 33) % 1000; | |
731 | ||
91211dd1 | 732 | if ((cmd->flags & TRIG_ROUND_MASK) == TRIG_ROUND_NEAREST) { |
82675f35 | 733 | if (rest > 33) |
e55c95a3 | 734 | (*chan_ticks)++; |
91211dd1 | 735 | } else if ((cmd->flags & TRIG_ROUND_MASK) == TRIG_ROUND_UP) { |
e55c95a3 GG |
736 | if (rest) |
737 | (*chan_ticks)++; | |
738 | } | |
739 | } | |
740 | ||
e55c95a3 GG |
741 | return 0; |
742 | } | |
743 | ||
71b5f4f1 | 744 | static void ai_write_timer(struct comedi_device *dev, |
0a85b6f0 MT |
745 | unsigned int init_ticks, |
746 | unsigned int scan_ticks, unsigned int chan_ticks) | |
e55c95a3 | 747 | { |
b08bfa38 HS |
748 | outl(init_ticks - 1, dev->iobase + ME4000_AI_SCAN_PRE_TIMER_LOW_REG); |
749 | outl(0x0, dev->iobase + ME4000_AI_SCAN_PRE_TIMER_HIGH_REG); | |
e55c95a3 GG |
750 | |
751 | if (scan_ticks) { | |
b08bfa38 HS |
752 | outl(scan_ticks - 1, dev->iobase + ME4000_AI_SCAN_TIMER_LOW_REG); |
753 | outl(0x0, dev->iobase + ME4000_AI_SCAN_TIMER_HIGH_REG); | |
e55c95a3 GG |
754 | } |
755 | ||
b08bfa38 HS |
756 | outl(chan_ticks - 1, dev->iobase + ME4000_AI_CHAN_PRE_TIMER_REG); |
757 | outl(chan_ticks - 1, dev->iobase + ME4000_AI_CHAN_TIMER_REG); | |
e55c95a3 GG |
758 | } |
759 | ||
4b2f15f1 HS |
760 | static int ai_write_chanlist(struct comedi_device *dev, |
761 | struct comedi_subdevice *s, struct comedi_cmd *cmd) | |
762 | { | |
763 | unsigned int entry; | |
764 | unsigned int chan; | |
765 | unsigned int rang; | |
766 | unsigned int aref; | |
767 | int i; | |
768 | ||
769 | for (i = 0; i < cmd->chanlist_len; i++) { | |
770 | chan = CR_CHAN(cmd->chanlist[i]); | |
771 | rang = CR_RANGE(cmd->chanlist[i]); | |
772 | aref = CR_AREF(cmd->chanlist[i]); | |
773 | ||
774 | entry = chan; | |
775 | ||
776 | if (rang == 0) | |
777 | entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_2_5; | |
778 | else if (rang == 1) | |
779 | entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_10; | |
780 | else if (rang == 2) | |
781 | entry |= ME4000_AI_LIST_RANGE_BIPOLAR_2_5; | |
782 | else | |
783 | entry |= ME4000_AI_LIST_RANGE_BIPOLAR_10; | |
784 | ||
785 | if (aref == SDF_DIFF) | |
786 | entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL; | |
787 | else | |
788 | entry |= ME4000_AI_LIST_INPUT_SINGLE_ENDED; | |
789 | ||
790 | outl(entry, dev->iobase + ME4000_AI_CHANNEL_LIST_REG); | |
791 | } | |
792 | ||
793 | return 0; | |
794 | } | |
795 | ||
71b5f4f1 | 796 | static int ai_prepare(struct comedi_device *dev, |
0a85b6f0 MT |
797 | struct comedi_subdevice *s, |
798 | struct comedi_cmd *cmd, | |
799 | unsigned int init_ticks, | |
800 | unsigned int scan_ticks, unsigned int chan_ticks) | |
e55c95a3 GG |
801 | { |
802 | ||
803 | unsigned long tmp = 0; | |
804 | ||
e55c95a3 GG |
805 | /* Write timer arguments */ |
806 | ai_write_timer(dev, init_ticks, scan_ticks, chan_ticks); | |
807 | ||
808 | /* Reset control register */ | |
b08bfa38 | 809 | outl(tmp, dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
810 | |
811 | /* Start sources */ | |
812 | if ((cmd->start_src == TRIG_EXT && | |
0a85b6f0 MT |
813 | cmd->scan_begin_src == TRIG_TIMER && |
814 | cmd->convert_src == TRIG_TIMER) || | |
815 | (cmd->start_src == TRIG_EXT && | |
816 | cmd->scan_begin_src == TRIG_FOLLOW && | |
817 | cmd->convert_src == TRIG_TIMER)) { | |
e55c95a3 | 818 | tmp = ME4000_AI_CTRL_BIT_MODE_1 | |
0a85b6f0 MT |
819 | ME4000_AI_CTRL_BIT_CHANNEL_FIFO | |
820 | ME4000_AI_CTRL_BIT_DATA_FIFO; | |
e55c95a3 | 821 | } else if (cmd->start_src == TRIG_EXT && |
0a85b6f0 MT |
822 | cmd->scan_begin_src == TRIG_EXT && |
823 | cmd->convert_src == TRIG_TIMER) { | |
e55c95a3 | 824 | tmp = ME4000_AI_CTRL_BIT_MODE_2 | |
0a85b6f0 MT |
825 | ME4000_AI_CTRL_BIT_CHANNEL_FIFO | |
826 | ME4000_AI_CTRL_BIT_DATA_FIFO; | |
e55c95a3 | 827 | } else if (cmd->start_src == TRIG_EXT && |
0a85b6f0 MT |
828 | cmd->scan_begin_src == TRIG_EXT && |
829 | cmd->convert_src == TRIG_EXT) { | |
e55c95a3 | 830 | tmp = ME4000_AI_CTRL_BIT_MODE_0 | |
0a85b6f0 MT |
831 | ME4000_AI_CTRL_BIT_MODE_1 | |
832 | ME4000_AI_CTRL_BIT_CHANNEL_FIFO | | |
833 | ME4000_AI_CTRL_BIT_DATA_FIFO; | |
e55c95a3 GG |
834 | } else { |
835 | tmp = ME4000_AI_CTRL_BIT_MODE_0 | | |
0a85b6f0 MT |
836 | ME4000_AI_CTRL_BIT_CHANNEL_FIFO | |
837 | ME4000_AI_CTRL_BIT_DATA_FIFO; | |
e55c95a3 GG |
838 | } |
839 | ||
840 | /* Stop triggers */ | |
841 | if (cmd->stop_src == TRIG_COUNT) { | |
d6cbe537 | 842 | outl(cmd->chanlist_len * cmd->stop_arg, |
b08bfa38 | 843 | dev->iobase + ME4000_AI_SAMPLE_COUNTER_REG); |
e55c95a3 GG |
844 | tmp |= ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ; |
845 | } else if (cmd->stop_src == TRIG_NONE && | |
0a85b6f0 | 846 | cmd->scan_end_src == TRIG_COUNT) { |
d6cbe537 | 847 | outl(cmd->scan_end_arg, |
b08bfa38 | 848 | dev->iobase + ME4000_AI_SAMPLE_COUNTER_REG); |
e55c95a3 GG |
849 | tmp |= ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ; |
850 | } else { | |
851 | tmp |= ME4000_AI_CTRL_BIT_HF_IRQ; | |
852 | } | |
853 | ||
854 | /* Write the setup to the control register */ | |
b08bfa38 | 855 | outl(tmp, dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
856 | |
857 | /* Write the channel list */ | |
858 | ai_write_chanlist(dev, s, cmd); | |
859 | ||
860 | return 0; | |
861 | } | |
862 | ||
0a85b6f0 MT |
863 | static int me4000_ai_do_cmd(struct comedi_device *dev, |
864 | struct comedi_subdevice *s) | |
e55c95a3 GG |
865 | { |
866 | int err; | |
867 | unsigned int init_ticks = 0; | |
868 | unsigned int scan_ticks = 0; | |
869 | unsigned int chan_ticks = 0; | |
ea6d0d4c | 870 | struct comedi_cmd *cmd = &s->async->cmd; |
e55c95a3 | 871 | |
e55c95a3 GG |
872 | /* Reset the analog input */ |
873 | err = me4000_ai_cancel(dev, s); | |
874 | if (err) | |
875 | return err; | |
876 | ||
877 | /* Round the timer arguments */ | |
878 | err = ai_round_cmd_args(dev, | |
0a85b6f0 | 879 | s, cmd, &init_ticks, &scan_ticks, &chan_ticks); |
e55c95a3 GG |
880 | if (err) |
881 | return err; | |
882 | ||
883 | /* Prepare the AI for acquisition */ | |
884 | err = ai_prepare(dev, s, cmd, init_ticks, scan_ticks, chan_ticks); | |
885 | if (err) | |
886 | return err; | |
887 | ||
888 | /* Start acquistion by dummy read */ | |
b08bfa38 | 889 | inl(dev->iobase + ME4000_AI_START_REG); |
e55c95a3 GG |
890 | |
891 | return 0; | |
892 | } | |
893 | ||
71b5f4f1 | 894 | static int me4000_ai_do_cmd_test(struct comedi_device *dev, |
0a85b6f0 MT |
895 | struct comedi_subdevice *s, |
896 | struct comedi_cmd *cmd) | |
e55c95a3 GG |
897 | { |
898 | ||
899 | unsigned int init_ticks; | |
900 | unsigned int chan_ticks; | |
901 | unsigned int scan_ticks; | |
902 | int err = 0; | |
903 | ||
e55c95a3 GG |
904 | /* Only rounding flags are implemented */ |
905 | cmd->flags &= TRIG_ROUND_NEAREST | TRIG_ROUND_UP | TRIG_ROUND_DOWN; | |
906 | ||
907 | /* Round the timer arguments */ | |
908 | ai_round_cmd_args(dev, s, cmd, &init_ticks, &scan_ticks, &chan_ticks); | |
909 | ||
27020ffe HS |
910 | /* Step 1 : check if triggers are trivially valid */ |
911 | ||
912 | err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT); | |
913 | err |= cfc_check_trigger_src(&cmd->scan_begin_src, | |
914 | TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT); | |
915 | err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT); | |
916 | err |= cfc_check_trigger_src(&cmd->scan_end_src, | |
917 | TRIG_NONE | TRIG_COUNT); | |
918 | err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE | TRIG_COUNT); | |
919 | ||
82675f35 | 920 | if (err) |
e55c95a3 | 921 | return 1; |
e55c95a3 | 922 | |
27020ffe HS |
923 | /* Step 2a : make sure trigger sources are unique */ |
924 | ||
925 | err |= cfc_check_trigger_is_unique(cmd->start_src); | |
926 | err |= cfc_check_trigger_is_unique(cmd->scan_begin_src); | |
927 | err |= cfc_check_trigger_is_unique(cmd->convert_src); | |
928 | err |= cfc_check_trigger_is_unique(cmd->scan_end_src); | |
929 | err |= cfc_check_trigger_is_unique(cmd->stop_src); | |
930 | ||
931 | /* Step 2b : and mutually compatible */ | |
932 | ||
e55c95a3 | 933 | if (cmd->start_src == TRIG_NOW && |
0a85b6f0 MT |
934 | cmd->scan_begin_src == TRIG_TIMER && |
935 | cmd->convert_src == TRIG_TIMER) { | |
e55c95a3 | 936 | } else if (cmd->start_src == TRIG_NOW && |
0a85b6f0 MT |
937 | cmd->scan_begin_src == TRIG_FOLLOW && |
938 | cmd->convert_src == TRIG_TIMER) { | |
e55c95a3 | 939 | } else if (cmd->start_src == TRIG_EXT && |
0a85b6f0 MT |
940 | cmd->scan_begin_src == TRIG_TIMER && |
941 | cmd->convert_src == TRIG_TIMER) { | |
e55c95a3 | 942 | } else if (cmd->start_src == TRIG_EXT && |
0a85b6f0 MT |
943 | cmd->scan_begin_src == TRIG_FOLLOW && |
944 | cmd->convert_src == TRIG_TIMER) { | |
e55c95a3 | 945 | } else if (cmd->start_src == TRIG_EXT && |
0a85b6f0 MT |
946 | cmd->scan_begin_src == TRIG_EXT && |
947 | cmd->convert_src == TRIG_TIMER) { | |
e55c95a3 | 948 | } else if (cmd->start_src == TRIG_EXT && |
0a85b6f0 MT |
949 | cmd->scan_begin_src == TRIG_EXT && |
950 | cmd->convert_src == TRIG_EXT) { | |
e55c95a3 | 951 | } else { |
27020ffe | 952 | err |= -EINVAL; |
e55c95a3 GG |
953 | } |
954 | ||
955 | if (cmd->stop_src == TRIG_NONE && cmd->scan_end_src == TRIG_NONE) { | |
956 | } else if (cmd->stop_src == TRIG_COUNT && | |
0a85b6f0 | 957 | cmd->scan_end_src == TRIG_NONE) { |
e55c95a3 | 958 | } else if (cmd->stop_src == TRIG_NONE && |
0a85b6f0 | 959 | cmd->scan_end_src == TRIG_COUNT) { |
e55c95a3 | 960 | } else if (cmd->stop_src == TRIG_COUNT && |
0a85b6f0 | 961 | cmd->scan_end_src == TRIG_COUNT) { |
e55c95a3 | 962 | } else { |
27020ffe | 963 | err |= -EINVAL; |
e55c95a3 | 964 | } |
27020ffe | 965 | |
82675f35 | 966 | if (err) |
e55c95a3 | 967 | return 2; |
e55c95a3 | 968 | |
8c6c5a69 HS |
969 | /* Step 3: check if arguments are trivially valid */ |
970 | ||
e55c95a3 | 971 | if (cmd->chanlist_len < 1) { |
e55c95a3 | 972 | cmd->chanlist_len = 1; |
8c6c5a69 | 973 | err |= -EINVAL; |
e55c95a3 GG |
974 | } |
975 | if (init_ticks < 66) { | |
e55c95a3 | 976 | cmd->start_arg = 2000; |
8c6c5a69 | 977 | err |= -EINVAL; |
e55c95a3 GG |
978 | } |
979 | if (scan_ticks && scan_ticks < 67) { | |
e55c95a3 | 980 | cmd->scan_begin_arg = 2031; |
8c6c5a69 | 981 | err |= -EINVAL; |
e55c95a3 GG |
982 | } |
983 | if (chan_ticks < 66) { | |
e55c95a3 | 984 | cmd->convert_arg = 2000; |
8c6c5a69 | 985 | err |= -EINVAL; |
e55c95a3 | 986 | } |
82675f35 BP |
987 | |
988 | if (err) | |
e55c95a3 | 989 | return 3; |
e55c95a3 GG |
990 | |
991 | /* | |
992 | * Stage 4. Check for argument conflicts. | |
993 | */ | |
994 | if (cmd->start_src == TRIG_NOW && | |
0a85b6f0 MT |
995 | cmd->scan_begin_src == TRIG_TIMER && |
996 | cmd->convert_src == TRIG_TIMER) { | |
e55c95a3 GG |
997 | |
998 | /* Check timer arguments */ | |
999 | if (init_ticks < ME4000_AI_MIN_TICKS) { | |
5da80ee8 | 1000 | dev_err(dev->class_dev, "Invalid start arg\n"); |
b6c77757 | 1001 | cmd->start_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1002 | err++; |
1003 | } | |
1004 | if (chan_ticks < ME4000_AI_MIN_TICKS) { | |
5da80ee8 | 1005 | dev_err(dev->class_dev, "Invalid convert arg\n"); |
b6c77757 | 1006 | cmd->convert_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1007 | err++; |
1008 | } | |
1009 | if (scan_ticks <= cmd->chanlist_len * chan_ticks) { | |
5da80ee8 | 1010 | dev_err(dev->class_dev, "Invalid scan end arg\n"); |
b6241fda GS |
1011 | |
1012 | /* At least one tick more */ | |
1013 | cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31; | |
e55c95a3 GG |
1014 | err++; |
1015 | } | |
1016 | } else if (cmd->start_src == TRIG_NOW && | |
0a85b6f0 MT |
1017 | cmd->scan_begin_src == TRIG_FOLLOW && |
1018 | cmd->convert_src == TRIG_TIMER) { | |
e55c95a3 GG |
1019 | |
1020 | /* Check timer arguments */ | |
1021 | if (init_ticks < ME4000_AI_MIN_TICKS) { | |
5da80ee8 | 1022 | dev_err(dev->class_dev, "Invalid start arg\n"); |
b6c77757 | 1023 | cmd->start_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1024 | err++; |
1025 | } | |
1026 | if (chan_ticks < ME4000_AI_MIN_TICKS) { | |
5da80ee8 | 1027 | dev_err(dev->class_dev, "Invalid convert arg\n"); |
b6c77757 | 1028 | cmd->convert_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1029 | err++; |
1030 | } | |
1031 | } else if (cmd->start_src == TRIG_EXT && | |
0a85b6f0 MT |
1032 | cmd->scan_begin_src == TRIG_TIMER && |
1033 | cmd->convert_src == TRIG_TIMER) { | |
e55c95a3 GG |
1034 | |
1035 | /* Check timer arguments */ | |
1036 | if (init_ticks < ME4000_AI_MIN_TICKS) { | |
5da80ee8 | 1037 | dev_err(dev->class_dev, "Invalid start arg\n"); |
b6c77757 | 1038 | cmd->start_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1039 | err++; |
1040 | } | |
1041 | if (chan_ticks < ME4000_AI_MIN_TICKS) { | |
5da80ee8 | 1042 | dev_err(dev->class_dev, "Invalid convert arg\n"); |
b6c77757 | 1043 | cmd->convert_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1044 | err++; |
1045 | } | |
1046 | if (scan_ticks <= cmd->chanlist_len * chan_ticks) { | |
5da80ee8 | 1047 | dev_err(dev->class_dev, "Invalid scan end arg\n"); |
b6241fda GS |
1048 | |
1049 | /* At least one tick more */ | |
1050 | cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31; | |
e55c95a3 GG |
1051 | err++; |
1052 | } | |
1053 | } else if (cmd->start_src == TRIG_EXT && | |
0a85b6f0 MT |
1054 | cmd->scan_begin_src == TRIG_FOLLOW && |
1055 | cmd->convert_src == TRIG_TIMER) { | |
e55c95a3 GG |
1056 | |
1057 | /* Check timer arguments */ | |
1058 | if (init_ticks < ME4000_AI_MIN_TICKS) { | |
5da80ee8 | 1059 | dev_err(dev->class_dev, "Invalid start arg\n"); |
b6c77757 | 1060 | cmd->start_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1061 | err++; |
1062 | } | |
1063 | if (chan_ticks < ME4000_AI_MIN_TICKS) { | |
5da80ee8 | 1064 | dev_err(dev->class_dev, "Invalid convert arg\n"); |
b6c77757 | 1065 | cmd->convert_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1066 | err++; |
1067 | } | |
1068 | } else if (cmd->start_src == TRIG_EXT && | |
0a85b6f0 MT |
1069 | cmd->scan_begin_src == TRIG_EXT && |
1070 | cmd->convert_src == TRIG_TIMER) { | |
e55c95a3 GG |
1071 | |
1072 | /* Check timer arguments */ | |
1073 | if (init_ticks < ME4000_AI_MIN_TICKS) { | |
5da80ee8 | 1074 | dev_err(dev->class_dev, "Invalid start arg\n"); |
b6c77757 | 1075 | cmd->start_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1076 | err++; |
1077 | } | |
1078 | if (chan_ticks < ME4000_AI_MIN_TICKS) { | |
5da80ee8 | 1079 | dev_err(dev->class_dev, "Invalid convert arg\n"); |
b6c77757 | 1080 | cmd->convert_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1081 | err++; |
1082 | } | |
1083 | } else if (cmd->start_src == TRIG_EXT && | |
0a85b6f0 MT |
1084 | cmd->scan_begin_src == TRIG_EXT && |
1085 | cmd->convert_src == TRIG_EXT) { | |
e55c95a3 GG |
1086 | |
1087 | /* Check timer arguments */ | |
1088 | if (init_ticks < ME4000_AI_MIN_TICKS) { | |
5da80ee8 | 1089 | dev_err(dev->class_dev, "Invalid start arg\n"); |
b6c77757 | 1090 | cmd->start_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1091 | err++; |
1092 | } | |
1093 | } | |
1094 | if (cmd->stop_src == TRIG_COUNT) { | |
1095 | if (cmd->stop_arg == 0) { | |
5da80ee8 | 1096 | dev_err(dev->class_dev, "Invalid stop arg\n"); |
e55c95a3 GG |
1097 | cmd->stop_arg = 1; |
1098 | err++; | |
1099 | } | |
1100 | } | |
1101 | if (cmd->scan_end_src == TRIG_COUNT) { | |
1102 | if (cmd->scan_end_arg == 0) { | |
5da80ee8 | 1103 | dev_err(dev->class_dev, "Invalid scan end arg\n"); |
e55c95a3 GG |
1104 | cmd->scan_end_arg = 1; |
1105 | err++; | |
1106 | } | |
1107 | } | |
82675f35 BP |
1108 | |
1109 | if (err) | |
e55c95a3 | 1110 | return 4; |
e55c95a3 GG |
1111 | |
1112 | /* | |
1113 | * Stage 5. Check the channel list. | |
1114 | */ | |
1115 | if (ai_check_chanlist(dev, s, cmd)) | |
1116 | return 5; | |
1117 | ||
1118 | return 0; | |
1119 | } | |
1120 | ||
70265d24 | 1121 | static irqreturn_t me4000_ai_isr(int irq, void *dev_id) |
e55c95a3 GG |
1122 | { |
1123 | unsigned int tmp; | |
71b5f4f1 | 1124 | struct comedi_device *dev = dev_id; |
8aaf2717 | 1125 | struct comedi_subdevice *s = &dev->subdevices[0]; |
e55c95a3 GG |
1126 | int i; |
1127 | int c = 0; | |
1128 | long lval; | |
1129 | ||
ef5bbfcb | 1130 | if (!dev->attached) |
e55c95a3 | 1131 | return IRQ_NONE; |
e55c95a3 GG |
1132 | |
1133 | /* Reset all events */ | |
1134 | s->async->events = 0; | |
1135 | ||
1136 | /* Check if irq number is right */ | |
109daa79 | 1137 | if (irq != dev->irq) { |
5da80ee8 | 1138 | dev_err(dev->class_dev, "Incorrect interrupt num: %d\n", irq); |
e55c95a3 GG |
1139 | return IRQ_HANDLED; |
1140 | } | |
1141 | ||
b08bfa38 | 1142 | if (inl(dev->iobase + ME4000_IRQ_STATUS_REG) & |
0a85b6f0 | 1143 | ME4000_IRQ_STATUS_BIT_AI_HF) { |
e55c95a3 | 1144 | /* Read status register to find out what happened */ |
b08bfa38 | 1145 | tmp = inl(dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
1146 | |
1147 | if (!(tmp & ME4000_AI_STATUS_BIT_FF_DATA) && | |
0a85b6f0 MT |
1148 | !(tmp & ME4000_AI_STATUS_BIT_HF_DATA) && |
1149 | (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) { | |
e55c95a3 GG |
1150 | c = ME4000_AI_FIFO_COUNT; |
1151 | ||
b6241fda GS |
1152 | /* |
1153 | * FIFO overflow, so stop conversion | |
1154 | * and disable all interrupts | |
1155 | */ | |
e55c95a3 GG |
1156 | tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; |
1157 | tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ | | |
0a85b6f0 | 1158 | ME4000_AI_CTRL_BIT_SC_IRQ); |
b08bfa38 | 1159 | outl(tmp, dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
1160 | |
1161 | s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; | |
1162 | ||
5da80ee8 | 1163 | dev_err(dev->class_dev, "FIFO overflow\n"); |
e55c95a3 | 1164 | } else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA) |
0a85b6f0 MT |
1165 | && !(tmp & ME4000_AI_STATUS_BIT_HF_DATA) |
1166 | && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) { | |
e55c95a3 GG |
1167 | s->async->events |= COMEDI_CB_BLOCK; |
1168 | ||
1169 | c = ME4000_AI_FIFO_COUNT / 2; | |
1170 | } else { | |
5da80ee8 HS |
1171 | dev_err(dev->class_dev, |
1172 | "Can't determine state of fifo\n"); | |
e55c95a3 GG |
1173 | c = 0; |
1174 | ||
b6241fda GS |
1175 | /* |
1176 | * Undefined state, so stop conversion | |
1177 | * and disable all interrupts | |
1178 | */ | |
e55c95a3 GG |
1179 | tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; |
1180 | tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ | | |
0a85b6f0 | 1181 | ME4000_AI_CTRL_BIT_SC_IRQ); |
b08bfa38 | 1182 | outl(tmp, dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
1183 | |
1184 | s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; | |
1185 | ||
5da80ee8 | 1186 | dev_err(dev->class_dev, "Undefined FIFO state\n"); |
e55c95a3 GG |
1187 | } |
1188 | ||
e55c95a3 GG |
1189 | for (i = 0; i < c; i++) { |
1190 | /* Read value from data fifo */ | |
b08bfa38 | 1191 | lval = inl(dev->iobase + ME4000_AI_DATA_REG) & 0xFFFF; |
e55c95a3 GG |
1192 | lval ^= 0x8000; |
1193 | ||
1194 | if (!comedi_buf_put(s->async, lval)) { | |
b6241fda GS |
1195 | /* |
1196 | * Buffer overflow, so stop conversion | |
1197 | * and disable all interrupts | |
1198 | */ | |
e55c95a3 GG |
1199 | tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; |
1200 | tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ | | |
0a85b6f0 | 1201 | ME4000_AI_CTRL_BIT_SC_IRQ); |
b08bfa38 | 1202 | outl(tmp, dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
1203 | |
1204 | s->async->events |= COMEDI_CB_OVERFLOW; | |
1205 | ||
5da80ee8 | 1206 | dev_err(dev->class_dev, "Buffer overflow\n"); |
e55c95a3 GG |
1207 | |
1208 | break; | |
1209 | } | |
1210 | } | |
1211 | ||
1212 | /* Work is done, so reset the interrupt */ | |
e55c95a3 | 1213 | tmp |= ME4000_AI_CTRL_BIT_HF_IRQ_RESET; |
b08bfa38 | 1214 | outl(tmp, dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 | 1215 | tmp &= ~ME4000_AI_CTRL_BIT_HF_IRQ_RESET; |
b08bfa38 | 1216 | outl(tmp, dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
1217 | } |
1218 | ||
b08bfa38 HS |
1219 | if (inl(dev->iobase + ME4000_IRQ_STATUS_REG) & |
1220 | ME4000_IRQ_STATUS_BIT_SC) { | |
e55c95a3 GG |
1221 | s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOA; |
1222 | ||
b6241fda GS |
1223 | /* |
1224 | * Acquisition is complete, so stop | |
1225 | * conversion and disable all interrupts | |
1226 | */ | |
b08bfa38 | 1227 | tmp = inl(dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
1228 | tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; |
1229 | tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ); | |
b08bfa38 | 1230 | outl(tmp, dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
1231 | |
1232 | /* Poll data until fifo empty */ | |
b08bfa38 HS |
1233 | while (inl(dev->iobase + ME4000_AI_CTRL_REG) & |
1234 | ME4000_AI_STATUS_BIT_EF_DATA) { | |
e55c95a3 | 1235 | /* Read value from data fifo */ |
b08bfa38 | 1236 | lval = inl(dev->iobase + ME4000_AI_DATA_REG) & 0xFFFF; |
e55c95a3 GG |
1237 | lval ^= 0x8000; |
1238 | ||
1239 | if (!comedi_buf_put(s->async, lval)) { | |
5da80ee8 | 1240 | dev_err(dev->class_dev, "Buffer overflow\n"); |
e55c95a3 GG |
1241 | s->async->events |= COMEDI_CB_OVERFLOW; |
1242 | break; | |
1243 | } | |
1244 | } | |
1245 | ||
1246 | /* Work is done, so reset the interrupt */ | |
e55c95a3 | 1247 | tmp |= ME4000_AI_CTRL_BIT_SC_IRQ_RESET; |
b08bfa38 | 1248 | outl(tmp, dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 | 1249 | tmp &= ~ME4000_AI_CTRL_BIT_SC_IRQ_RESET; |
b08bfa38 | 1250 | outl(tmp, dev->iobase + ME4000_AI_CTRL_REG); |
e55c95a3 GG |
1251 | } |
1252 | ||
e55c95a3 GG |
1253 | if (s->async->events) |
1254 | comedi_event(dev, s); | |
1255 | ||
1256 | return IRQ_HANDLED; | |
1257 | } | |
1258 | ||
1259 | /*============================================================================= | |
1260 | Analog output section | |
1261 | ===========================================================================*/ | |
1262 | ||
71b5f4f1 | 1263 | static int me4000_ao_insn_write(struct comedi_device *dev, |
0a85b6f0 MT |
1264 | struct comedi_subdevice *s, |
1265 | struct comedi_insn *insn, unsigned int *data) | |
e55c95a3 | 1266 | { |
8407593a | 1267 | const struct me4000_board *thisboard = comedi_board(dev); |
09253b39 | 1268 | struct me4000_info *info = dev->private; |
e55c95a3 GG |
1269 | int chan = CR_CHAN(insn->chanspec); |
1270 | int rang = CR_RANGE(insn->chanspec); | |
1271 | int aref = CR_AREF(insn->chanspec); | |
1272 | unsigned long tmp; | |
1273 | ||
e55c95a3 GG |
1274 | if (insn->n == 0) { |
1275 | return 0; | |
1276 | } else if (insn->n > 1) { | |
5da80ee8 HS |
1277 | dev_err(dev->class_dev, "Invalid instruction length %d\n", |
1278 | insn->n); | |
e55c95a3 GG |
1279 | return -EINVAL; |
1280 | } | |
1281 | ||
2d504528 | 1282 | if (chan >= thisboard->ao_nchan) { |
5da80ee8 | 1283 | dev_err(dev->class_dev, "Invalid channel %d\n", insn->n); |
e55c95a3 GG |
1284 | return -EINVAL; |
1285 | } | |
1286 | ||
1287 | if (rang != 0) { | |
5da80ee8 | 1288 | dev_err(dev->class_dev, "Invalid range %d\n", insn->n); |
e55c95a3 GG |
1289 | return -EINVAL; |
1290 | } | |
1291 | ||
1292 | if (aref != AREF_GROUND && aref != AREF_COMMON) { | |
5da80ee8 | 1293 | dev_err(dev->class_dev, "Invalid aref %d\n", insn->n); |
e55c95a3 GG |
1294 | return -EINVAL; |
1295 | } | |
1296 | ||
1297 | /* Stop any running conversion */ | |
e1d7ccb7 | 1298 | tmp = inl(dev->iobase + ME4000_AO_CTRL_REG(chan)); |
e55c95a3 | 1299 | tmp |= ME4000_AO_CTRL_BIT_IMMEDIATE_STOP; |
e1d7ccb7 | 1300 | outl(tmp, dev->iobase + ME4000_AO_CTRL_REG(chan)); |
e55c95a3 GG |
1301 | |
1302 | /* Clear control register and set to single mode */ | |
e1d7ccb7 | 1303 | outl(0x0, dev->iobase + ME4000_AO_CTRL_REG(chan)); |
e55c95a3 GG |
1304 | |
1305 | /* Write data value */ | |
e1d7ccb7 | 1306 | outl(data[0], dev->iobase + ME4000_AO_SINGLE_REG(chan)); |
e55c95a3 GG |
1307 | |
1308 | /* Store in the mirror */ | |
e1d7ccb7 | 1309 | info->ao_readback[chan] = data[0]; |
e55c95a3 GG |
1310 | |
1311 | return 1; | |
1312 | } | |
1313 | ||
71b5f4f1 | 1314 | static int me4000_ao_insn_read(struct comedi_device *dev, |
0a85b6f0 MT |
1315 | struct comedi_subdevice *s, |
1316 | struct comedi_insn *insn, unsigned int *data) | |
e55c95a3 | 1317 | { |
09253b39 | 1318 | struct me4000_info *info = dev->private; |
e55c95a3 GG |
1319 | int chan = CR_CHAN(insn->chanspec); |
1320 | ||
1321 | if (insn->n == 0) { | |
1322 | return 0; | |
1323 | } else if (insn->n > 1) { | |
5da80ee8 | 1324 | dev_err(dev->class_dev, "Invalid instruction length\n"); |
e55c95a3 GG |
1325 | return -EINVAL; |
1326 | } | |
1327 | ||
e1d7ccb7 | 1328 | data[0] = info->ao_readback[chan]; |
e55c95a3 GG |
1329 | |
1330 | return 1; | |
1331 | } | |
1332 | ||
1333 | /*============================================================================= | |
1334 | Digital I/O section | |
1335 | ===========================================================================*/ | |
1336 | ||
71b5f4f1 | 1337 | static int me4000_dio_insn_bits(struct comedi_device *dev, |
0a85b6f0 MT |
1338 | struct comedi_subdevice *s, |
1339 | struct comedi_insn *insn, unsigned int *data) | |
e55c95a3 | 1340 | { |
e55c95a3 GG |
1341 | /* |
1342 | * The insn data consists of a mask in data[0] and the new data | |
1343 | * in data[1]. The mask defines which bits we are concerning about. | |
1344 | * The new data must be anded with the mask. | |
1345 | * Each channel corresponds to a bit. | |
1346 | */ | |
1347 | if (data[0]) { | |
1348 | /* Check if requested ports are configured for output */ | |
1349 | if ((s->io_bits & data[0]) != data[0]) | |
1350 | return -EIO; | |
1351 | ||
1352 | s->state &= ~data[0]; | |
1353 | s->state |= data[0] & data[1]; | |
1354 | ||
1355 | /* Write out the new digital output lines */ | |
d6cbe537 | 1356 | outl((s->state >> 0) & 0xFF, |
da755d15 | 1357 | dev->iobase + ME4000_DIO_PORT_0_REG); |
d6cbe537 | 1358 | outl((s->state >> 8) & 0xFF, |
da755d15 | 1359 | dev->iobase + ME4000_DIO_PORT_1_REG); |
d6cbe537 | 1360 | outl((s->state >> 16) & 0xFF, |
da755d15 | 1361 | dev->iobase + ME4000_DIO_PORT_2_REG); |
d6cbe537 | 1362 | outl((s->state >> 24) & 0xFF, |
da755d15 | 1363 | dev->iobase + ME4000_DIO_PORT_3_REG); |
e55c95a3 GG |
1364 | } |
1365 | ||
1366 | /* On return, data[1] contains the value of | |
1367 | the digital input and output lines. */ | |
da755d15 HS |
1368 | data[1] = ((inl(dev->iobase + ME4000_DIO_PORT_0_REG) & 0xFF) << 0) | |
1369 | ((inl(dev->iobase + ME4000_DIO_PORT_1_REG) & 0xFF) << 8) | | |
1370 | ((inl(dev->iobase + ME4000_DIO_PORT_2_REG) & 0xFF) << 16) | | |
1371 | ((inl(dev->iobase + ME4000_DIO_PORT_3_REG) & 0xFF) << 24); | |
e55c95a3 | 1372 | |
a2714e3e | 1373 | return insn->n; |
e55c95a3 GG |
1374 | } |
1375 | ||
71b5f4f1 | 1376 | static int me4000_dio_insn_config(struct comedi_device *dev, |
0a85b6f0 MT |
1377 | struct comedi_subdevice *s, |
1378 | struct comedi_insn *insn, unsigned int *data) | |
e55c95a3 GG |
1379 | { |
1380 | unsigned long tmp; | |
1381 | int chan = CR_CHAN(insn->chanspec); | |
1382 | ||
f3445c1e IA |
1383 | switch (data[0]) { |
1384 | default: | |
1385 | return -EINVAL; | |
1386 | case INSN_CONFIG_DIO_QUERY: | |
e55c95a3 | 1387 | data[1] = |
0a85b6f0 | 1388 | (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT; |
e55c95a3 | 1389 | return insn->n; |
f3445c1e IA |
1390 | case INSN_CONFIG_DIO_INPUT: |
1391 | case INSN_CONFIG_DIO_OUTPUT: | |
1392 | break; | |
e55c95a3 GG |
1393 | } |
1394 | ||
1395 | /* | |
1396 | * The input or output configuration of each digital line is | |
1397 | * configured by a special insn_config instruction. chanspec | |
1398 | * contains the channel to be changed, and data[0] contains the | |
f3445c1e | 1399 | * value INSN_CONFIG_DIO_INPUT or INSN_CONFIG_DIO_OUTPUT. |
e55c95a3 GG |
1400 | * On the ME-4000 it is only possible to switch port wise (8 bit) |
1401 | */ | |
1402 | ||
da755d15 | 1403 | tmp = inl(dev->iobase + ME4000_DIO_CTRL_REG); |
e55c95a3 | 1404 | |
f3445c1e | 1405 | if (data[0] == INSN_CONFIG_DIO_OUTPUT) { |
e55c95a3 GG |
1406 | if (chan < 8) { |
1407 | s->io_bits |= 0xFF; | |
1408 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_0 | | |
0a85b6f0 | 1409 | ME4000_DIO_CTRL_BIT_MODE_1); |
e55c95a3 GG |
1410 | tmp |= ME4000_DIO_CTRL_BIT_MODE_0; |
1411 | } else if (chan < 16) { | |
1412 | /* | |
b6241fda GS |
1413 | * Chech for optoisolated ME-4000 version. |
1414 | * If one the first port is a fixed output | |
1415 | * port and the second is a fixed input port. | |
e55c95a3 | 1416 | */ |
da755d15 | 1417 | if (!inl(dev->iobase + ME4000_DIO_DIR_REG)) |
e55c95a3 GG |
1418 | return -ENODEV; |
1419 | ||
1420 | s->io_bits |= 0xFF00; | |
1421 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_2 | | |
0a85b6f0 | 1422 | ME4000_DIO_CTRL_BIT_MODE_3); |
e55c95a3 GG |
1423 | tmp |= ME4000_DIO_CTRL_BIT_MODE_2; |
1424 | } else if (chan < 24) { | |
1425 | s->io_bits |= 0xFF0000; | |
1426 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_4 | | |
0a85b6f0 | 1427 | ME4000_DIO_CTRL_BIT_MODE_5); |
e55c95a3 GG |
1428 | tmp |= ME4000_DIO_CTRL_BIT_MODE_4; |
1429 | } else if (chan < 32) { | |
1430 | s->io_bits |= 0xFF000000; | |
1431 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_6 | | |
0a85b6f0 | 1432 | ME4000_DIO_CTRL_BIT_MODE_7); |
e55c95a3 GG |
1433 | tmp |= ME4000_DIO_CTRL_BIT_MODE_6; |
1434 | } else { | |
1435 | return -EINVAL; | |
1436 | } | |
1437 | } else { | |
1438 | if (chan < 8) { | |
1439 | /* | |
b6241fda GS |
1440 | * Chech for optoisolated ME-4000 version. |
1441 | * If one the first port is a fixed output | |
1442 | * port and the second is a fixed input port. | |
e55c95a3 | 1443 | */ |
da755d15 | 1444 | if (!inl(dev->iobase + ME4000_DIO_DIR_REG)) |
e55c95a3 GG |
1445 | return -ENODEV; |
1446 | ||
1447 | s->io_bits &= ~0xFF; | |
1448 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_0 | | |
0a85b6f0 | 1449 | ME4000_DIO_CTRL_BIT_MODE_1); |
e55c95a3 GG |
1450 | } else if (chan < 16) { |
1451 | s->io_bits &= ~0xFF00; | |
1452 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_2 | | |
0a85b6f0 | 1453 | ME4000_DIO_CTRL_BIT_MODE_3); |
e55c95a3 GG |
1454 | } else if (chan < 24) { |
1455 | s->io_bits &= ~0xFF0000; | |
1456 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_4 | | |
0a85b6f0 | 1457 | ME4000_DIO_CTRL_BIT_MODE_5); |
e55c95a3 GG |
1458 | } else if (chan < 32) { |
1459 | s->io_bits &= ~0xFF000000; | |
1460 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_6 | | |
0a85b6f0 | 1461 | ME4000_DIO_CTRL_BIT_MODE_7); |
e55c95a3 GG |
1462 | } else { |
1463 | return -EINVAL; | |
1464 | } | |
1465 | } | |
1466 | ||
da755d15 | 1467 | outl(tmp, dev->iobase + ME4000_DIO_CTRL_REG); |
e55c95a3 GG |
1468 | |
1469 | return 1; | |
1470 | } | |
1471 | ||
1472 | /*============================================================================= | |
1473 | Counter section | |
1474 | ===========================================================================*/ | |
1475 | ||
71b5f4f1 | 1476 | static int me4000_cnt_insn_config(struct comedi_device *dev, |
0a85b6f0 | 1477 | struct comedi_subdevice *s, |
83eef17c HS |
1478 | struct comedi_insn *insn, |
1479 | unsigned int *data) | |
e55c95a3 | 1480 | { |
83eef17c | 1481 | struct me4000_info *info = dev->private; |
e55c95a3 GG |
1482 | int err; |
1483 | ||
e55c95a3 GG |
1484 | switch (data[0]) { |
1485 | case GPCT_RESET: | |
83eef17c | 1486 | if (insn->n != 1) |
e55c95a3 | 1487 | return -EINVAL; |
e55c95a3 | 1488 | |
83eef17c HS |
1489 | err = i8254_load(info->timer_regbase, 0, insn->chanspec, 0, |
1490 | I8254_MODE0 | I8254_BINARY); | |
e55c95a3 GG |
1491 | if (err) |
1492 | return err; | |
1493 | break; | |
1494 | case GPCT_SET_OPERATION: | |
83eef17c | 1495 | if (insn->n != 2) |
e55c95a3 | 1496 | return -EINVAL; |
e55c95a3 | 1497 | |
83eef17c HS |
1498 | err = i8254_set_mode(info->timer_regbase, 0, insn->chanspec, |
1499 | (data[1] << 1) | I8254_BINARY); | |
e55c95a3 GG |
1500 | if (err) |
1501 | return err; | |
1502 | break; | |
1503 | default: | |
e55c95a3 GG |
1504 | return -EINVAL; |
1505 | } | |
1506 | ||
83eef17c | 1507 | return insn->n; |
e55c95a3 GG |
1508 | } |
1509 | ||
71b5f4f1 | 1510 | static int me4000_cnt_insn_read(struct comedi_device *dev, |
0a85b6f0 MT |
1511 | struct comedi_subdevice *s, |
1512 | struct comedi_insn *insn, unsigned int *data) | |
e55c95a3 | 1513 | { |
09253b39 | 1514 | struct me4000_info *info = dev->private; |
e55c95a3 | 1515 | |
82675f35 | 1516 | if (insn->n == 0) |
e55c95a3 | 1517 | return 0; |
82675f35 | 1518 | |
e55c95a3 | 1519 | if (insn->n > 1) { |
5da80ee8 HS |
1520 | dev_err(dev->class_dev, "Invalid instruction length %d\n", |
1521 | insn->n); | |
e55c95a3 GG |
1522 | return -EINVAL; |
1523 | } | |
1524 | ||
db9132e0 | 1525 | data[0] = i8254_read(info->timer_regbase, 0, insn->chanspec); |
e55c95a3 GG |
1526 | |
1527 | return 1; | |
1528 | } | |
1529 | ||
71b5f4f1 | 1530 | static int me4000_cnt_insn_write(struct comedi_device *dev, |
0a85b6f0 MT |
1531 | struct comedi_subdevice *s, |
1532 | struct comedi_insn *insn, unsigned int *data) | |
e55c95a3 | 1533 | { |
09253b39 | 1534 | struct me4000_info *info = dev->private; |
e55c95a3 | 1535 | |
e55c95a3 GG |
1536 | if (insn->n == 0) { |
1537 | return 0; | |
1538 | } else if (insn->n > 1) { | |
5da80ee8 HS |
1539 | dev_err(dev->class_dev, "Invalid instruction length %d\n", |
1540 | insn->n); | |
e55c95a3 GG |
1541 | return -EINVAL; |
1542 | } | |
1543 | ||
db9132e0 | 1544 | i8254_write(info->timer_regbase, 0, insn->chanspec, data[0]); |
e55c95a3 GG |
1545 | |
1546 | return 1; | |
1547 | } | |
1548 | ||
a690b7e5 | 1549 | static int me4000_auto_attach(struct comedi_device *dev, |
8c355509 | 1550 | unsigned long context) |
ba5cb4ba | 1551 | { |
750af5e5 | 1552 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
8c355509 | 1553 | const struct me4000_board *thisboard = NULL; |
ba5cb4ba | 1554 | struct me4000_info *info; |
ba5cb4ba HS |
1555 | struct comedi_subdevice *s; |
1556 | int result; | |
4b2f15f1 | 1557 | |
8c355509 HS |
1558 | if (context < ARRAY_SIZE(me4000_boards)) |
1559 | thisboard = &me4000_boards[context]; | |
5f8f8d43 HS |
1560 | if (!thisboard) |
1561 | return -ENODEV; | |
1562 | dev->board_ptr = thisboard; | |
ba5cb4ba | 1563 | dev->board_name = thisboard->name; |
4b2f15f1 | 1564 | |
c34fa261 HS |
1565 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
1566 | if (!info) | |
1567 | return -ENOMEM; | |
1568 | dev->private = info; | |
4b2f15f1 | 1569 | |
818f569f | 1570 | result = comedi_pci_enable(dev); |
ba5cb4ba HS |
1571 | if (result) |
1572 | return result; | |
1573 | ||
1574 | info->plx_regbase = pci_resource_start(pcidev, 1); | |
ba5cb4ba | 1575 | dev->iobase = pci_resource_start(pcidev, 2); |
ba5cb4ba | 1576 | info->timer_regbase = pci_resource_start(pcidev, 3); |
39780a1c | 1577 | if (!info->plx_regbase || !dev->iobase || !info->timer_regbase) |
4b2f15f1 HS |
1578 | return -ENODEV; |
1579 | ||
4b2f15f1 HS |
1580 | result = xilinx_download(dev); |
1581 | if (result) | |
1582 | return result; | |
1583 | ||
2f348ecd | 1584 | me4000_reset(dev); |
4b2f15f1 | 1585 | |
8b6c5694 HS |
1586 | result = comedi_alloc_subdevices(dev, 4); |
1587 | if (result) | |
1588 | return result; | |
3af09830 HS |
1589 | |
1590 | /*========================================================================= | |
1591 | Analog input subdevice | |
1592 | ========================================================================*/ | |
1593 | ||
8aaf2717 | 1594 | s = &dev->subdevices[0]; |
3af09830 | 1595 | |
6ba8dfef | 1596 | if (thisboard->ai_nchan) { |
3af09830 HS |
1597 | s->type = COMEDI_SUBD_AI; |
1598 | s->subdev_flags = | |
1599 | SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF; | |
6ba8dfef | 1600 | s->n_chan = thisboard->ai_nchan; |
3af09830 HS |
1601 | s->maxdata = 0xFFFF; /* 16 bit ADC */ |
1602 | s->len_chanlist = ME4000_AI_CHANNEL_LIST_COUNT; | |
1603 | s->range_table = &me4000_ai_range; | |
1604 | s->insn_read = me4000_ai_insn_read; | |
1605 | ||
b179748d HS |
1606 | if (pcidev->irq > 0) { |
1607 | if (request_irq(pcidev->irq, me4000_ai_isr, | |
1608 | IRQF_SHARED, dev->board_name, dev)) { | |
1609 | dev_warn(dev->class_dev, | |
1610 | "request_irq failed\n"); | |
3af09830 HS |
1611 | } else { |
1612 | dev->read_subdev = s; | |
1613 | s->subdev_flags |= SDF_CMD_READ; | |
1614 | s->cancel = me4000_ai_cancel; | |
1615 | s->do_cmdtest = me4000_ai_do_cmd_test; | |
1616 | s->do_cmd = me4000_ai_do_cmd; | |
b179748d HS |
1617 | |
1618 | dev->irq = pcidev->irq; | |
3af09830 HS |
1619 | } |
1620 | } else { | |
b179748d | 1621 | dev_warn(dev->class_dev, "No interrupt available\n"); |
3af09830 HS |
1622 | } |
1623 | } else { | |
1624 | s->type = COMEDI_SUBD_UNUSED; | |
1625 | } | |
1626 | ||
1627 | /*========================================================================= | |
1628 | Analog output subdevice | |
1629 | ========================================================================*/ | |
1630 | ||
8aaf2717 | 1631 | s = &dev->subdevices[1]; |
3af09830 | 1632 | |
2d504528 | 1633 | if (thisboard->ao_nchan) { |
3af09830 HS |
1634 | s->type = COMEDI_SUBD_AO; |
1635 | s->subdev_flags = SDF_WRITEABLE | SDF_COMMON | SDF_GROUND; | |
2d504528 | 1636 | s->n_chan = thisboard->ao_nchan; |
3af09830 | 1637 | s->maxdata = 0xFFFF; /* 16 bit DAC */ |
4683f9f8 | 1638 | s->range_table = &range_bipolar10; |
3af09830 HS |
1639 | s->insn_write = me4000_ao_insn_write; |
1640 | s->insn_read = me4000_ao_insn_read; | |
1641 | } else { | |
1642 | s->type = COMEDI_SUBD_UNUSED; | |
1643 | } | |
1644 | ||
1645 | /*========================================================================= | |
1646 | Digital I/O subdevice | |
1647 | ========================================================================*/ | |
1648 | ||
8aaf2717 | 1649 | s = &dev->subdevices[2]; |
3af09830 | 1650 | |
898f5191 | 1651 | if (thisboard->dio_nchan) { |
3af09830 HS |
1652 | s->type = COMEDI_SUBD_DIO; |
1653 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
898f5191 | 1654 | s->n_chan = thisboard->dio_nchan; |
3af09830 HS |
1655 | s->maxdata = 1; |
1656 | s->range_table = &range_digital; | |
1657 | s->insn_bits = me4000_dio_insn_bits; | |
1658 | s->insn_config = me4000_dio_insn_config; | |
1659 | } else { | |
1660 | s->type = COMEDI_SUBD_UNUSED; | |
1661 | } | |
1662 | ||
1663 | /* | |
1664 | * Check for optoisolated ME-4000 version. If one the first | |
1665 | * port is a fixed output port and the second is a fixed input port. | |
1666 | */ | |
da755d15 | 1667 | if (!inl(dev->iobase + ME4000_DIO_DIR_REG)) { |
3af09830 | 1668 | s->io_bits |= 0xFF; |
da755d15 HS |
1669 | outl(ME4000_DIO_CTRL_BIT_MODE_0, |
1670 | dev->iobase + ME4000_DIO_DIR_REG); | |
3af09830 HS |
1671 | } |
1672 | ||
1673 | /*========================================================================= | |
1674 | Counter subdevice | |
1675 | ========================================================================*/ | |
1676 | ||
8aaf2717 | 1677 | s = &dev->subdevices[3]; |
3af09830 | 1678 | |
eedf4299 | 1679 | if (thisboard->has_counter) { |
3af09830 HS |
1680 | s->type = COMEDI_SUBD_COUNTER; |
1681 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
eedf4299 | 1682 | s->n_chan = 3; |
3af09830 HS |
1683 | s->maxdata = 0xFFFF; /* 16 bit counters */ |
1684 | s->insn_read = me4000_cnt_insn_read; | |
1685 | s->insn_write = me4000_cnt_insn_write; | |
1686 | s->insn_config = me4000_cnt_insn_config; | |
1687 | } else { | |
1688 | s->type = COMEDI_SUBD_UNUSED; | |
1689 | } | |
1690 | ||
1691 | return 0; | |
1692 | } | |
1693 | ||
484ecc95 | 1694 | static void me4000_detach(struct comedi_device *dev) |
3af09830 | 1695 | { |
b179748d HS |
1696 | if (dev->irq) |
1697 | free_irq(dev->irq, dev); | |
7f072f54 HS |
1698 | if (dev->iobase) |
1699 | me4000_reset(dev); | |
1700 | comedi_pci_disable(dev); | |
3af09830 HS |
1701 | } |
1702 | ||
75e6301b | 1703 | static struct comedi_driver me4000_driver = { |
3af09830 HS |
1704 | .driver_name = "me4000", |
1705 | .module = THIS_MODULE, | |
750af5e5 | 1706 | .auto_attach = me4000_auto_attach, |
3af09830 HS |
1707 | .detach = me4000_detach, |
1708 | }; | |
1709 | ||
a690b7e5 | 1710 | static int me4000_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 1711 | const struct pci_device_id *id) |
727b286b | 1712 | { |
b8f4ac23 | 1713 | return comedi_pci_auto_config(dev, &me4000_driver, id->driver_data); |
727b286b AT |
1714 | } |
1715 | ||
3af09830 | 1716 | static DEFINE_PCI_DEVICE_TABLE(me4000_pci_table) = { |
8c355509 HS |
1717 | { PCI_VDEVICE(MEILHAUS, 0x4650), BOARD_ME4650 }, |
1718 | { PCI_VDEVICE(MEILHAUS, 0x4660), BOARD_ME4660 }, | |
1719 | { PCI_VDEVICE(MEILHAUS, 0x4661), BOARD_ME4660I }, | |
1720 | { PCI_VDEVICE(MEILHAUS, 0x4662), BOARD_ME4660S }, | |
1721 | { PCI_VDEVICE(MEILHAUS, 0x4663), BOARD_ME4660IS }, | |
1722 | { PCI_VDEVICE(MEILHAUS, 0x4670), BOARD_ME4670 }, | |
1723 | { PCI_VDEVICE(MEILHAUS, 0x4671), BOARD_ME4670I }, | |
1724 | { PCI_VDEVICE(MEILHAUS, 0x4672), BOARD_ME4670S }, | |
1725 | { PCI_VDEVICE(MEILHAUS, 0x4673), BOARD_ME4670IS }, | |
1726 | { PCI_VDEVICE(MEILHAUS, 0x4680), BOARD_ME4680 }, | |
1727 | { PCI_VDEVICE(MEILHAUS, 0x4681), BOARD_ME4680I }, | |
1728 | { PCI_VDEVICE(MEILHAUS, 0x4682), BOARD_ME4680S }, | |
1729 | { PCI_VDEVICE(MEILHAUS, 0x4683), BOARD_ME4680IS }, | |
1730 | { 0 } | |
3af09830 HS |
1731 | }; |
1732 | MODULE_DEVICE_TABLE(pci, me4000_pci_table); | |
1733 | ||
75e6301b HS |
1734 | static struct pci_driver me4000_pci_driver = { |
1735 | .name = "me4000", | |
3af09830 | 1736 | .id_table = me4000_pci_table, |
75e6301b | 1737 | .probe = me4000_pci_probe, |
9901a4d7 | 1738 | .remove = comedi_pci_auto_unconfig, |
727b286b | 1739 | }; |
75e6301b | 1740 | module_comedi_pci_driver(me4000_driver, me4000_pci_driver); |
90f703d3 AT |
1741 | |
1742 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
1743 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
1744 | MODULE_LICENSE("GPL"); |