Commit | Line | Data |
---|---|---|
85acac61 | 1 | /* |
544c0550 HS |
2 | * comedi/drivers/me_daq.c |
3 | * Hardware driver for Meilhaus data acquisition cards: | |
4 | * ME-2000i, ME-2600i, ME-3000vm1 | |
5 | * | |
6 | * Copyright (C) 2002 Michael Hillmann <hillmann@syscongroup.de> | |
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. | |
544c0550 | 17 | */ |
85acac61 MH |
18 | |
19 | /* | |
544c0550 HS |
20 | * Driver: me_daq |
21 | * Description: Meilhaus PCI data acquisition cards | |
22 | * Devices: (Meilhaus) ME-2600i [me-2600i] | |
23 | * (Meilhaus) ME-2000i [me-2000i] | |
24 | * Author: Michael Hillmann <hillmann@syscongroup.de> | |
25 | * Status: experimental | |
26 | * | |
27 | * Configuration options: not applicable, uses PCI auto config | |
28 | * | |
29 | * Supports: | |
30 | * Analog Input, Analog Output, Digital I/O | |
31 | */ | |
85acac61 | 32 | |
33782dd5 | 33 | #include <linux/pci.h> |
25436dc9 | 34 | #include <linux/interrupt.h> |
4377a026 | 35 | #include <linux/sched.h> |
33782dd5 | 36 | |
85acac61 MH |
37 | #include "../comedidev.h" |
38 | ||
fb868541 HS |
39 | #include "plx9052.h" |
40 | ||
1e12ca34 | 41 | #define ME2600_FIRMWARE "me2600_firmware.bin" |
2ce411b5 | 42 | |
2ce411b5 GKH |
43 | #define XILINX_DOWNLOAD_RESET 0x42 /* Xilinx registers */ |
44 | ||
45 | #define ME_CONTROL_1 0x0000 /* - | W */ | |
46 | #define INTERRUPT_ENABLE (1<<15) | |
47 | #define COUNTER_B_IRQ (1<<12) | |
48 | #define COUNTER_A_IRQ (1<<11) | |
49 | #define CHANLIST_READY_IRQ (1<<10) | |
50 | #define EXT_IRQ (1<<9) | |
51 | #define ADFIFO_HALFFULL_IRQ (1<<8) | |
52 | #define SCAN_COUNT_ENABLE (1<<5) | |
53 | #define SIMULTANEOUS_ENABLE (1<<4) | |
54 | #define TRIGGER_FALLING_EDGE (1<<3) | |
55 | #define CONTINUOUS_MODE (1<<2) | |
56 | #define DISABLE_ADC (0<<0) | |
57 | #define SOFTWARE_TRIGGERED_ADC (1<<0) | |
58 | #define SCAN_TRIGGERED_ADC (2<<0) | |
59 | #define EXT_TRIGGERED_ADC (3<<0) | |
60 | #define ME_ADC_START 0x0000 /* R | - */ | |
61 | #define ME_CONTROL_2 0x0002 /* - | W */ | |
62 | #define ENABLE_ADFIFO (1<<10) | |
63 | #define ENABLE_CHANLIST (1<<9) | |
64 | #define ENABLE_PORT_B (1<<7) | |
65 | #define ENABLE_PORT_A (1<<6) | |
66 | #define ENABLE_COUNTER_B (1<<4) | |
67 | #define ENABLE_COUNTER_A (1<<3) | |
68 | #define ENABLE_DAC (1<<1) | |
69 | #define BUFFERED_DAC (1<<0) | |
70 | #define ME_DAC_UPDATE 0x0002 /* R | - */ | |
71 | #define ME_STATUS 0x0004 /* R | - */ | |
72 | #define COUNTER_B_IRQ_PENDING (1<<12) | |
73 | #define COUNTER_A_IRQ_PENDING (1<<11) | |
74 | #define CHANLIST_READY_IRQ_PENDING (1<<10) | |
75 | #define EXT_IRQ_PENDING (1<<9) | |
76 | #define ADFIFO_HALFFULL_IRQ_PENDING (1<<8) | |
77 | #define ADFIFO_FULL (1<<4) | |
78 | #define ADFIFO_HALFFULL (1<<3) | |
79 | #define ADFIFO_EMPTY (1<<2) | |
80 | #define CHANLIST_FULL (1<<1) | |
81 | #define FST_ACTIVE (1<<0) | |
82 | #define ME_RESET_INTERRUPT 0x0004 /* - | W */ | |
83 | #define ME_DIO_PORT_A 0x0006 /* R | W */ | |
84 | #define ME_DIO_PORT_B 0x0008 /* R | W */ | |
85 | #define ME_TIMER_DATA_0 0x000A /* - | W */ | |
86 | #define ME_TIMER_DATA_1 0x000C /* - | W */ | |
87 | #define ME_TIMER_DATA_2 0x000E /* - | W */ | |
88 | #define ME_CHANNEL_LIST 0x0010 /* - | W */ | |
89 | #define ADC_UNIPOLAR (1<<6) | |
90 | #define ADC_GAIN_0 (0<<4) | |
91 | #define ADC_GAIN_1 (1<<4) | |
92 | #define ADC_GAIN_2 (2<<4) | |
93 | #define ADC_GAIN_3 (3<<4) | |
94 | #define ME_READ_AD_FIFO 0x0010 /* R | - */ | |
95 | #define ME_DAC_CONTROL 0x0012 /* - | W */ | |
96 | #define DAC_UNIPOLAR_D (0<<4) | |
97 | #define DAC_BIPOLAR_D (1<<4) | |
98 | #define DAC_UNIPOLAR_C (0<<5) | |
99 | #define DAC_BIPOLAR_C (1<<5) | |
100 | #define DAC_UNIPOLAR_B (0<<6) | |
101 | #define DAC_BIPOLAR_B (1<<6) | |
102 | #define DAC_UNIPOLAR_A (0<<7) | |
103 | #define DAC_BIPOLAR_A (1<<7) | |
104 | #define DAC_GAIN_0_D (0<<8) | |
105 | #define DAC_GAIN_1_D (1<<8) | |
106 | #define DAC_GAIN_0_C (0<<9) | |
107 | #define DAC_GAIN_1_C (1<<9) | |
108 | #define DAC_GAIN_0_B (0<<10) | |
109 | #define DAC_GAIN_1_B (1<<10) | |
110 | #define DAC_GAIN_0_A (0<<11) | |
111 | #define DAC_GAIN_1_A (1<<11) | |
112 | #define ME_DAC_CONTROL_UPDATE 0x0012 /* R | - */ | |
113 | #define ME_DAC_DATA_A 0x0014 /* - | W */ | |
114 | #define ME_DAC_DATA_B 0x0016 /* - | W */ | |
115 | #define ME_DAC_DATA_C 0x0018 /* - | W */ | |
116 | #define ME_DAC_DATA_D 0x001A /* - | W */ | |
117 | #define ME_COUNTER_ENDDATA_A 0x001C /* - | W */ | |
118 | #define ME_COUNTER_ENDDATA_B 0x001E /* - | W */ | |
119 | #define ME_COUNTER_STARTDATA_A 0x0020 /* - | W */ | |
120 | #define ME_COUNTER_VALUE_A 0x0020 /* R | - */ | |
121 | #define ME_COUNTER_STARTDATA_B 0x0022 /* - | W */ | |
122 | #define ME_COUNTER_VALUE_B 0x0022 /* R | - */ | |
123 | ||
fa8eec60 | 124 | static const struct comedi_lrange me_ai_range = { |
cd0fa970 HS |
125 | 8, { |
126 | BIP_RANGE(10), | |
127 | BIP_RANGE(5), | |
128 | BIP_RANGE(2.5), | |
129 | BIP_RANGE(1.25), | |
130 | UNI_RANGE(10), | |
131 | UNI_RANGE(5), | |
132 | UNI_RANGE(2.5), | |
133 | UNI_RANGE(1.25) | |
134 | } | |
85acac61 MH |
135 | }; |
136 | ||
310239e7 | 137 | static const struct comedi_lrange me_ao_range = { |
cd0fa970 HS |
138 | 3, { |
139 | BIP_RANGE(10), | |
140 | BIP_RANGE(5), | |
141 | UNI_RANGE(10) | |
142 | } | |
85acac61 MH |
143 | }; |
144 | ||
a4493f07 HS |
145 | enum me_boardid { |
146 | BOARD_ME2600, | |
147 | BOARD_ME2000, | |
148 | }; | |
149 | ||
5e08c198 | 150 | struct me_board { |
32d39862 | 151 | const char *name; |
a4493f07 | 152 | int needs_firmware; |
310239e7 | 153 | int has_ao; |
5e08c198 | 154 | }; |
85acac61 | 155 | |
5e08c198 | 156 | static const struct me_board me_boards[] = { |
a4493f07 | 157 | [BOARD_ME2600] = { |
32d39862 | 158 | .name = "me-2600i", |
a4493f07 | 159 | .needs_firmware = 1, |
310239e7 | 160 | .has_ao = 1, |
a4493f07 HS |
161 | }, |
162 | [BOARD_ME2000] = { | |
32d39862 | 163 | .name = "me-2000i", |
a4493f07 | 164 | }, |
85acac61 MH |
165 | }; |
166 | ||
5e08c198 | 167 | struct me_private_data { |
39eca2a0 GKH |
168 | void __iomem *plx_regbase; /* PLX configuration base address */ |
169 | void __iomem *me_regbase; /* Base address of the Meilhaus card */ | |
85acac61 | 170 | |
2ce411b5 GKH |
171 | unsigned short control_1; /* Mirror of CONTROL_1 register */ |
172 | unsigned short control_2; /* Mirror of CONTROL_2 register */ | |
173 | unsigned short dac_control; /* Mirror of the DAC_CONTROL register */ | |
174 | int ao_readback[4]; /* Mirror of analog output data */ | |
5e08c198 | 175 | }; |
85acac61 | 176 | |
2ce411b5 | 177 | static inline void sleep(unsigned sec) |
85acac61 MH |
178 | { |
179 | current->state = TASK_INTERRUPTIBLE; | |
180 | schedule_timeout(sec * HZ); | |
181 | } | |
182 | ||
0a85b6f0 MT |
183 | static int me_dio_insn_config(struct comedi_device *dev, |
184 | struct comedi_subdevice *s, | |
5e177c45 HS |
185 | struct comedi_insn *insn, |
186 | unsigned int *data) | |
85acac61 | 187 | { |
9a1a6cf8 | 188 | struct me_private_data *dev_private = dev->private; |
5e177c45 HS |
189 | unsigned int mask = 1 << CR_CHAN(insn->chanspec); |
190 | unsigned int bits; | |
191 | unsigned int port; | |
85acac61 | 192 | |
5e177c45 | 193 | if (mask & 0x0000ffff) { |
85acac61 | 194 | bits = 0x0000ffff; |
5e177c45 HS |
195 | port = ENABLE_PORT_A; |
196 | } else { | |
85acac61 | 197 | bits = 0xffff0000; |
5e177c45 | 198 | port = ENABLE_PORT_B; |
85acac61 MH |
199 | } |
200 | ||
5e177c45 HS |
201 | switch (data[0]) { |
202 | case INSN_CONFIG_DIO_INPUT: | |
85acac61 | 203 | s->io_bits &= ~bits; |
5e177c45 HS |
204 | dev_private->control_2 &= ~port; |
205 | break; | |
206 | case INSN_CONFIG_DIO_OUTPUT: | |
207 | s->io_bits |= bits; | |
208 | dev_private->control_2 |= port; | |
209 | break; | |
210 | case INSN_CONFIG_DIO_QUERY: | |
211 | data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT; | |
212 | return insn->n; | |
213 | break; | |
214 | default: | |
215 | return -EINVAL; | |
85acac61 MH |
216 | } |
217 | ||
5e177c45 HS |
218 | /* Update the port configuration */ |
219 | writew(dev_private->control_2, dev_private->me_regbase + ME_CONTROL_2); | |
220 | ||
221 | return insn->n; | |
85acac61 MH |
222 | } |
223 | ||
0a85b6f0 MT |
224 | static int me_dio_insn_bits(struct comedi_device *dev, |
225 | struct comedi_subdevice *s, | |
ec6521a2 HS |
226 | struct comedi_insn *insn, |
227 | unsigned int *data) | |
85acac61 | 228 | { |
9a1a6cf8 | 229 | struct me_private_data *dev_private = dev->private; |
ec6521a2 HS |
230 | void __iomem *mmio_porta = dev_private->me_regbase + ME_DIO_PORT_A; |
231 | void __iomem *mmio_portb = dev_private->me_regbase + ME_DIO_PORT_B; | |
85acac61 | 232 | unsigned int mask = data[0]; |
ec6521a2 HS |
233 | unsigned int bits = data[1]; |
234 | unsigned int val; | |
9a1a6cf8 | 235 | |
ec6521a2 HS |
236 | mask &= s->io_bits; /* only update the COMEDI_OUTPUT channels */ |
237 | if (mask) { | |
238 | s->state &= ~mask; | |
239 | s->state |= (bits & mask); | |
85acac61 | 240 | |
ec6521a2 HS |
241 | if (mask & 0x0000ffff) |
242 | writew((s->state & 0xffff), mmio_porta); | |
243 | if (mask & 0xffff0000) | |
244 | writew(((s->state >> 16) & 0xffff), mmio_portb); | |
85acac61 MH |
245 | } |
246 | ||
ec6521a2 HS |
247 | if (s->io_bits & 0x0000ffff) |
248 | val = s->state & 0xffff; | |
249 | else | |
250 | val = readw(mmio_porta); | |
251 | ||
252 | if (s->io_bits & 0xffff0000) | |
253 | val |= (s->state & 0xffff0000); | |
254 | else | |
255 | val |= (readw(mmio_portb) << 16); | |
256 | ||
257 | data[1] = val; | |
85acac61 | 258 | |
a2714e3e | 259 | return insn->n; |
85acac61 MH |
260 | } |
261 | ||
0a85b6f0 | 262 | static int me_ai_insn_read(struct comedi_device *dev, |
bc1acb20 | 263 | struct comedi_subdevice *s, |
61532e9d HS |
264 | struct comedi_insn *insn, |
265 | unsigned int *data) | |
85acac61 | 266 | { |
9a1a6cf8 | 267 | struct me_private_data *dev_private = dev->private; |
61532e9d HS |
268 | unsigned int chan = CR_CHAN(insn->chanspec); |
269 | unsigned int rang = CR_RANGE(insn->chanspec); | |
270 | unsigned int aref = CR_AREF(insn->chanspec); | |
271 | unsigned short val; | |
85acac61 MH |
272 | int i; |
273 | ||
274 | /* stop any running conversion */ | |
275 | dev_private->control_1 &= 0xFFFC; | |
276 | writew(dev_private->control_1, dev_private->me_regbase + ME_CONTROL_1); | |
277 | ||
278 | /* clear chanlist and ad fifo */ | |
279 | dev_private->control_2 &= ~(ENABLE_ADFIFO | ENABLE_CHANLIST); | |
280 | writew(dev_private->control_2, dev_private->me_regbase + ME_CONTROL_2); | |
281 | ||
282 | /* reset any pending interrupt */ | |
283 | writew(0x00, dev_private->me_regbase + ME_RESET_INTERRUPT); | |
284 | ||
285 | /* enable the chanlist and ADC fifo */ | |
286 | dev_private->control_2 |= (ENABLE_ADFIFO | ENABLE_CHANLIST); | |
287 | writew(dev_private->control_2, dev_private->me_regbase + ME_CONTROL_2); | |
288 | ||
289 | /* write to channel list fifo */ | |
61532e9d HS |
290 | val = chan & 0x0f; /* b3:b0 channel */ |
291 | val |= (rang & 0x03) << 4; /* b5:b4 gain */ | |
292 | val |= (rang & 0x04) << 4; /* b6 polarity */ | |
293 | val |= ((aref & AREF_DIFF) ? 0x80 : 0); /* b7 differential */ | |
294 | writew(val & 0xff, dev_private->me_regbase + ME_CHANNEL_LIST); | |
85acac61 MH |
295 | |
296 | /* set ADC mode to software trigger */ | |
297 | dev_private->control_1 |= SOFTWARE_TRIGGERED_ADC; | |
298 | writew(dev_private->control_1, dev_private->me_regbase + ME_CONTROL_1); | |
299 | ||
300 | /* start conversion by reading from ADC_START */ | |
301 | readw(dev_private->me_regbase + ME_ADC_START); | |
302 | ||
303 | /* wait for ADC fifo not empty flag */ | |
2ce411b5 GKH |
304 | for (i = 100000; i > 0; i--) |
305 | if (!(readw(dev_private->me_regbase + ME_STATUS) & 0x0004)) | |
85acac61 | 306 | break; |
85acac61 MH |
307 | |
308 | /* get value from ADC fifo */ | |
309 | if (i) { | |
61532e9d HS |
310 | val = readw(dev_private->me_regbase + ME_READ_AD_FIFO); |
311 | val = (val ^ 0x800) & 0x0fff; | |
312 | data[0] = val; | |
85acac61 | 313 | } else { |
a74b5c51 | 314 | dev_err(dev->class_dev, "Cannot get single value\n"); |
85acac61 MH |
315 | return -EIO; |
316 | } | |
317 | ||
318 | /* stop any running conversion */ | |
319 | dev_private->control_1 &= 0xFFFC; | |
320 | writew(dev_private->control_1, dev_private->me_regbase + ME_CONTROL_1); | |
321 | ||
322 | return 1; | |
323 | } | |
324 | ||
0a85b6f0 MT |
325 | static int me_ao_insn_write(struct comedi_device *dev, |
326 | struct comedi_subdevice *s, | |
14d09f79 HS |
327 | struct comedi_insn *insn, |
328 | unsigned int *data) | |
85acac61 | 329 | { |
9a1a6cf8 | 330 | struct me_private_data *dev_private = dev->private; |
14d09f79 HS |
331 | unsigned int chan = CR_CHAN(insn->chanspec); |
332 | unsigned int rang = CR_RANGE(insn->chanspec); | |
85acac61 MH |
333 | int i; |
334 | ||
335 | /* Enable all DAC */ | |
336 | dev_private->control_2 |= ENABLE_DAC; | |
337 | writew(dev_private->control_2, dev_private->me_regbase + ME_CONTROL_2); | |
338 | ||
339 | /* and set DAC to "buffered" mode */ | |
340 | dev_private->control_2 |= BUFFERED_DAC; | |
341 | writew(dev_private->control_2, dev_private->me_regbase + ME_CONTROL_2); | |
342 | ||
343 | /* Set dac-control register */ | |
344 | for (i = 0; i < insn->n; i++) { | |
2ce411b5 GKH |
345 | /* clear bits for this channel */ |
346 | dev_private->dac_control &= ~(0x0880 >> chan); | |
85acac61 MH |
347 | if (rang == 0) |
348 | dev_private->dac_control |= | |
0a85b6f0 | 349 | ((DAC_BIPOLAR_A | DAC_GAIN_1_A) >> chan); |
85acac61 MH |
350 | else if (rang == 1) |
351 | dev_private->dac_control |= | |
0a85b6f0 | 352 | ((DAC_BIPOLAR_A | DAC_GAIN_0_A) >> chan); |
85acac61 MH |
353 | } |
354 | writew(dev_private->dac_control, | |
0a85b6f0 | 355 | dev_private->me_regbase + ME_DAC_CONTROL); |
85acac61 MH |
356 | |
357 | /* Update dac-control register */ | |
358 | readw(dev_private->me_regbase + ME_DAC_CONTROL_UPDATE); | |
359 | ||
360 | /* Set data register */ | |
361 | for (i = 0; i < insn->n; i++) { | |
85acac61 | 362 | writew((data[0] & s->maxdata), |
0a85b6f0 | 363 | dev_private->me_regbase + ME_DAC_DATA_A + (chan << 1)); |
85acac61 MH |
364 | dev_private->ao_readback[chan] = (data[0] & s->maxdata); |
365 | } | |
366 | ||
367 | /* Update dac with data registers */ | |
368 | readw(dev_private->me_regbase + ME_DAC_UPDATE); | |
369 | ||
14d09f79 | 370 | return insn->n; |
85acac61 MH |
371 | } |
372 | ||
0a85b6f0 | 373 | static int me_ao_insn_read(struct comedi_device *dev, |
91b0da57 HS |
374 | struct comedi_subdevice *s, |
375 | struct comedi_insn *insn, | |
0a85b6f0 | 376 | unsigned int *data) |
85acac61 | 377 | { |
9a1a6cf8 | 378 | struct me_private_data *dev_private = dev->private; |
91b0da57 | 379 | unsigned int chan = CR_CHAN(insn->chanspec); |
85acac61 MH |
380 | int i; |
381 | ||
91b0da57 HS |
382 | for (i = 0; i < insn->n; i++) |
383 | data[i] = dev_private->ao_readback[chan]; | |
85acac61 | 384 | |
91b0da57 | 385 | return insn->n; |
85acac61 MH |
386 | } |
387 | ||
71b5f4f1 | 388 | static int me2600_xilinx_download(struct comedi_device *dev, |
d569541e HS |
389 | const u8 *data, size_t size, |
390 | unsigned long context) | |
85acac61 | 391 | { |
9a1a6cf8 | 392 | struct me_private_data *dev_private = dev->private; |
85acac61 MH |
393 | unsigned int value; |
394 | unsigned int file_length; | |
395 | unsigned int i; | |
396 | ||
397 | /* disable irq's on PLX */ | |
fb868541 | 398 | writel(0x00, dev_private->plx_regbase + PLX9052_INTCSR); |
85acac61 MH |
399 | |
400 | /* First, make a dummy read to reset xilinx */ | |
401 | value = readw(dev_private->me_regbase + XILINX_DOWNLOAD_RESET); | |
402 | ||
403 | /* Wait until reset is over */ | |
404 | sleep(1); | |
405 | ||
406 | /* Write a dummy value to Xilinx */ | |
407 | writeb(0x00, dev_private->me_regbase + 0x0); | |
408 | sleep(1); | |
409 | ||
410 | /* | |
411 | * Format of the firmware | |
412 | * Build longs from the byte-wise coded header | |
413 | * Byte 1-3: length of the array | |
414 | * Byte 4-7: version | |
415 | * Byte 8-11: date | |
416 | * Byte 12-15: reserved | |
417 | */ | |
1e12ca34 | 418 | if (size < 16) |
85acac61 | 419 | return -EINVAL; |
1e12ca34 HS |
420 | |
421 | file_length = (((unsigned int)data[0] & 0xff) << 24) + | |
422 | (((unsigned int)data[1] & 0xff) << 16) + | |
423 | (((unsigned int)data[2] & 0xff) << 8) + | |
424 | ((unsigned int)data[3] & 0xff); | |
85acac61 MH |
425 | |
426 | /* | |
427 | * Loop for writing firmware byte by byte to xilinx | |
20ce161d | 428 | * Firmware data start at offset 16 |
85acac61 | 429 | */ |
2ce411b5 | 430 | for (i = 0; i < file_length; i++) |
1e12ca34 | 431 | writeb((data[16 + i] & 0xff), |
0a85b6f0 | 432 | dev_private->me_regbase + 0x0); |
85acac61 MH |
433 | |
434 | /* Write 5 dummy values to xilinx */ | |
2ce411b5 | 435 | for (i = 0; i < 5; i++) |
85acac61 | 436 | writeb(0x00, dev_private->me_regbase + 0x0); |
85acac61 MH |
437 | |
438 | /* Test if there was an error during download -> INTB was thrown */ | |
fb868541 HS |
439 | value = readl(dev_private->plx_regbase + PLX9052_INTCSR); |
440 | if (value & PLX9052_INTCSR_LI2STAT) { | |
85acac61 | 441 | /* Disable interrupt */ |
fb868541 | 442 | writel(0x00, dev_private->plx_regbase + PLX9052_INTCSR); |
a74b5c51 | 443 | dev_err(dev->class_dev, "Xilinx download failed\n"); |
85acac61 MH |
444 | return -EIO; |
445 | } | |
446 | ||
447 | /* Wait until the Xilinx is ready for real work */ | |
448 | sleep(1); | |
449 | ||
450 | /* Enable PLX-Interrupts */ | |
fb868541 HS |
451 | writel(PLX9052_INTCSR_LI1ENAB | |
452 | PLX9052_INTCSR_LI1POL | | |
453 | PLX9052_INTCSR_PCIENAB, | |
454 | dev_private->plx_regbase + PLX9052_INTCSR); | |
85acac61 MH |
455 | |
456 | return 0; | |
457 | } | |
458 | ||
71b5f4f1 | 459 | static int me_reset(struct comedi_device *dev) |
85acac61 | 460 | { |
9a1a6cf8 HS |
461 | struct me_private_data *dev_private = dev->private; |
462 | ||
85acac61 MH |
463 | /* Reset board */ |
464 | writew(0x00, dev_private->me_regbase + ME_CONTROL_1); | |
465 | writew(0x00, dev_private->me_regbase + ME_CONTROL_2); | |
466 | writew(0x00, dev_private->me_regbase + ME_RESET_INTERRUPT); | |
467 | writew(0x00, dev_private->me_regbase + ME_DAC_CONTROL); | |
468 | ||
469 | /* Save values in the board context */ | |
470 | dev_private->dac_control = 0; | |
471 | dev_private->control_1 = 0; | |
472 | dev_private->control_2 = 0; | |
473 | ||
474 | return 0; | |
475 | } | |
476 | ||
a690b7e5 | 477 | static int me_auto_attach(struct comedi_device *dev, |
a4493f07 | 478 | unsigned long context) |
b92169fd | 479 | { |
750af5e5 | 480 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
a4493f07 | 481 | const struct me_board *board = NULL; |
9a1a6cf8 | 482 | struct me_private_data *dev_private; |
bc1acb20 | 483 | struct comedi_subdevice *s; |
4bb153b4 | 484 | int ret; |
b92169fd | 485 | |
a4493f07 HS |
486 | if (context < ARRAY_SIZE(me_boards)) |
487 | board = &me_boards[context]; | |
94174847 HS |
488 | if (!board) |
489 | return -ENODEV; | |
490 | dev->board_ptr = board; | |
491 | dev->board_name = board->name; | |
492 | ||
0bdab509 | 493 | dev_private = comedi_alloc_devpriv(dev, sizeof(*dev_private)); |
c34fa261 HS |
494 | if (!dev_private) |
495 | return -ENOMEM; | |
b92169fd | 496 | |
818f569f | 497 | ret = comedi_pci_enable(dev); |
4ae76422 HS |
498 | if (ret) |
499 | return ret; | |
85acac61 | 500 | |
8a38e9a5 | 501 | dev_private->plx_regbase = pci_ioremap_bar(pcidev, 0); |
10cba302 | 502 | if (!dev_private->plx_regbase) |
85acac61 | 503 | return -ENOMEM; |
85acac61 | 504 | |
8a38e9a5 | 505 | dev_private->me_regbase = pci_ioremap_bar(pcidev, 2); |
52c4cbe9 | 506 | if (!dev_private->me_regbase) |
85acac61 | 507 | return -ENOMEM; |
1e12ca34 | 508 | |
2ce411b5 | 509 | /* Download firmware and reset card */ |
a4493f07 | 510 | if (board->needs_firmware) { |
cb43cc0f HS |
511 | ret = comedi_load_firmware(dev, &comedi_to_pci_dev(dev)->dev, |
512 | ME2600_FIRMWARE, | |
d569541e | 513 | me2600_xilinx_download, 0); |
4bb153b4 HS |
514 | if (ret < 0) |
515 | return ret; | |
85acac61 | 516 | } |
85acac61 MH |
517 | me_reset(dev); |
518 | ||
4bb153b4 HS |
519 | ret = comedi_alloc_subdevices(dev, 3); |
520 | if (ret) | |
521 | return ret; | |
85acac61 | 522 | |
f6e56027 | 523 | s = &dev->subdevices[0]; |
ab69b334 | 524 | s->type = COMEDI_SUBD_AI; |
aa0ed828 | 525 | s->subdev_flags = SDF_READABLE | SDF_COMMON; |
fa8eec60 HS |
526 | s->n_chan = 16; |
527 | s->maxdata = 0x0fff; | |
528 | s->len_chanlist = 16; | |
529 | s->range_table = &me_ai_range; | |
ab69b334 | 530 | s->insn_read = me_ai_insn_read; |
bc1acb20 | 531 | |
f6e56027 | 532 | s = &dev->subdevices[1]; |
310239e7 | 533 | if (board->has_ao) { |
ab69b334 HS |
534 | s->type = COMEDI_SUBD_AO; |
535 | s->subdev_flags = SDF_WRITEABLE | SDF_COMMON; | |
310239e7 HS |
536 | s->n_chan = 4; |
537 | s->maxdata = 0x0fff; | |
538 | s->len_chanlist = 4; | |
539 | s->range_table = &me_ao_range; | |
ab69b334 HS |
540 | s->insn_read = me_ao_insn_read; |
541 | s->insn_write = me_ao_insn_write; | |
43d51f88 HS |
542 | } else { |
543 | s->type = COMEDI_SUBD_UNUSED; | |
544 | } | |
bc1acb20 | 545 | |
f6e56027 | 546 | s = &dev->subdevices[2]; |
ab69b334 HS |
547 | s->type = COMEDI_SUBD_DIO; |
548 | s->subdev_flags = SDF_READABLE | SDF_WRITEABLE; | |
549 | s->n_chan = 32; | |
550 | s->maxdata = 1; | |
551 | s->len_chanlist = 32; | |
552 | s->range_table = &range_digital; | |
553 | s->insn_bits = me_dio_insn_bits; | |
554 | s->insn_config = me_dio_insn_config; | |
555 | s->io_bits = 0; | |
85acac61 | 556 | |
94174847 HS |
557 | dev_info(dev->class_dev, "%s: %s attached\n", |
558 | dev->driver->driver_name, dev->board_name); | |
559 | ||
85acac61 MH |
560 | return 0; |
561 | } | |
562 | ||
484ecc95 | 563 | static void me_detach(struct comedi_device *dev) |
85acac61 | 564 | { |
9a1a6cf8 | 565 | struct me_private_data *dev_private = dev->private; |
27034e8a | 566 | |
85acac61 MH |
567 | if (dev_private) { |
568 | if (dev_private->me_regbase) { | |
569 | me_reset(dev); | |
570 | iounmap(dev_private->me_regbase); | |
571 | } | |
572 | if (dev_private->plx_regbase) | |
573 | iounmap(dev_private->plx_regbase); | |
27034e8a | 574 | } |
7f072f54 | 575 | comedi_pci_disable(dev); |
85acac61 | 576 | } |
90f703d3 | 577 | |
75e6301b HS |
578 | static struct comedi_driver me_daq_driver = { |
579 | .driver_name = "me_daq", | |
4d76714b | 580 | .module = THIS_MODULE, |
750af5e5 | 581 | .auto_attach = me_auto_attach, |
4d76714b HS |
582 | .detach = me_detach, |
583 | }; | |
584 | ||
a690b7e5 | 585 | static int me_daq_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 586 | const struct pci_device_id *id) |
4d76714b | 587 | { |
b8f4ac23 | 588 | return comedi_pci_auto_config(dev, &me_daq_driver, id->driver_data); |
4d76714b HS |
589 | } |
590 | ||
75e6301b | 591 | static DEFINE_PCI_DEVICE_TABLE(me_daq_pci_table) = { |
a4493f07 HS |
592 | { PCI_VDEVICE(MEILHAUS, 0x2600), BOARD_ME2600 }, |
593 | { PCI_VDEVICE(MEILHAUS, 0x2000), BOARD_ME2000 }, | |
4d76714b HS |
594 | { 0 } |
595 | }; | |
75e6301b | 596 | MODULE_DEVICE_TABLE(pci, me_daq_pci_table); |
4d76714b | 597 | |
75e6301b HS |
598 | static struct pci_driver me_daq_pci_driver = { |
599 | .name = "me_daq", | |
600 | .id_table = me_daq_pci_table, | |
601 | .probe = me_daq_pci_probe, | |
9901a4d7 | 602 | .remove = comedi_pci_auto_unconfig, |
4d76714b | 603 | }; |
75e6301b | 604 | module_comedi_pci_driver(me_daq_driver, me_daq_pci_driver); |
4d76714b | 605 | |
90f703d3 AT |
606 | MODULE_AUTHOR("Comedi http://www.comedi.org"); |
607 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
608 | MODULE_LICENSE("GPL"); | |
1e12ca34 | 609 | MODULE_FIRMWARE(ME2600_FIRMWARE); |