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