Commit | Line | Data |
---|---|---|
85acac61 MH |
1 | /* |
2 | ||
3 | comedi/drivers/me_daq.c | |
4 | ||
5 | Hardware driver for Meilhaus data acquisition cards: | |
6 | ||
7 | ME-2000i, ME-2600i, ME-3000vm1 | |
8 | ||
9 | Copyright (C) 2002 Michael Hillmann <hillmann@syscongroup.de> | |
10 | ||
11 | This program is free software; you can redistribute it and/or modify | |
12 | it under the terms of the GNU General Public License as published by | |
13 | the Free Software Foundation; either version 2 of the License, or | |
14 | (at your option) any later version. | |
15 | ||
16 | This program is distributed in the hope that it will be useful, | |
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | GNU General Public License for more details. | |
20 | ||
21 | You should have received a copy of the GNU General Public License | |
22 | along with this program; if not, write to the Free Software | |
23 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
24 | */ | |
25 | ||
26 | /* | |
27 | Driver: me_daq | |
28 | Description: Meilhaus PCI data acquisition cards | |
29 | Author: Michael Hillmann <hillmann@syscongroup.de> | |
30 | Devices: [Meilhaus] ME-2600i (me_daq), ME-2000i | |
31 | Status: experimental | |
32 | ||
33 | Supports: | |
34 | ||
35 | Analog Output | |
36 | ||
37 | Configuration options: | |
38 | ||
39 | [0] - PCI bus number (optional) | |
40 | [1] - PCI slot number (optional) | |
41 | ||
42 | If bus/slot is not specified, the first available PCI | |
43 | device will be used. | |
44 | ||
45 | The 2600 requires a firmware upload, which can be accomplished | |
46 | using the -i or --init-data option of comedi_config. | |
47 | The firmware can be | |
48 | found in the comedi_nonfree_firmware tarball available | |
49 | from http://www.comedi.org | |
50 | ||
51 | */ | |
52 | ||
53 | #include "../comedidev.h" | |
54 | ||
55 | #include "comedi_pci.h" | |
56 | ||
2ce411b5 GKH |
57 | /*#include "me2600_fw.h" */ |
58 | ||
59 | #define ME_DRIVER_NAME "me_daq" | |
60 | ||
61 | #define ME2000_DEVICE_ID 0x2000 | |
62 | #define ME2600_DEVICE_ID 0x2600 | |
63 | ||
64 | #define PLX_INTCSR 0x4C /* PLX interrupt status register */ | |
65 | #define XILINX_DOWNLOAD_RESET 0x42 /* Xilinx registers */ | |
66 | ||
67 | #define ME_CONTROL_1 0x0000 /* - | W */ | |
68 | #define INTERRUPT_ENABLE (1<<15) | |
69 | #define COUNTER_B_IRQ (1<<12) | |
70 | #define COUNTER_A_IRQ (1<<11) | |
71 | #define CHANLIST_READY_IRQ (1<<10) | |
72 | #define EXT_IRQ (1<<9) | |
73 | #define ADFIFO_HALFFULL_IRQ (1<<8) | |
74 | #define SCAN_COUNT_ENABLE (1<<5) | |
75 | #define SIMULTANEOUS_ENABLE (1<<4) | |
76 | #define TRIGGER_FALLING_EDGE (1<<3) | |
77 | #define CONTINUOUS_MODE (1<<2) | |
78 | #define DISABLE_ADC (0<<0) | |
79 | #define SOFTWARE_TRIGGERED_ADC (1<<0) | |
80 | #define SCAN_TRIGGERED_ADC (2<<0) | |
81 | #define EXT_TRIGGERED_ADC (3<<0) | |
82 | #define ME_ADC_START 0x0000 /* R | - */ | |
83 | #define ME_CONTROL_2 0x0002 /* - | W */ | |
84 | #define ENABLE_ADFIFO (1<<10) | |
85 | #define ENABLE_CHANLIST (1<<9) | |
86 | #define ENABLE_PORT_B (1<<7) | |
87 | #define ENABLE_PORT_A (1<<6) | |
88 | #define ENABLE_COUNTER_B (1<<4) | |
89 | #define ENABLE_COUNTER_A (1<<3) | |
90 | #define ENABLE_DAC (1<<1) | |
91 | #define BUFFERED_DAC (1<<0) | |
92 | #define ME_DAC_UPDATE 0x0002 /* R | - */ | |
93 | #define ME_STATUS 0x0004 /* R | - */ | |
94 | #define COUNTER_B_IRQ_PENDING (1<<12) | |
95 | #define COUNTER_A_IRQ_PENDING (1<<11) | |
96 | #define CHANLIST_READY_IRQ_PENDING (1<<10) | |
97 | #define EXT_IRQ_PENDING (1<<9) | |
98 | #define ADFIFO_HALFFULL_IRQ_PENDING (1<<8) | |
99 | #define ADFIFO_FULL (1<<4) | |
100 | #define ADFIFO_HALFFULL (1<<3) | |
101 | #define ADFIFO_EMPTY (1<<2) | |
102 | #define CHANLIST_FULL (1<<1) | |
103 | #define FST_ACTIVE (1<<0) | |
104 | #define ME_RESET_INTERRUPT 0x0004 /* - | W */ | |
105 | #define ME_DIO_PORT_A 0x0006 /* R | W */ | |
106 | #define ME_DIO_PORT_B 0x0008 /* R | W */ | |
107 | #define ME_TIMER_DATA_0 0x000A /* - | W */ | |
108 | #define ME_TIMER_DATA_1 0x000C /* - | W */ | |
109 | #define ME_TIMER_DATA_2 0x000E /* - | W */ | |
110 | #define ME_CHANNEL_LIST 0x0010 /* - | W */ | |
111 | #define ADC_UNIPOLAR (1<<6) | |
112 | #define ADC_GAIN_0 (0<<4) | |
113 | #define ADC_GAIN_1 (1<<4) | |
114 | #define ADC_GAIN_2 (2<<4) | |
115 | #define ADC_GAIN_3 (3<<4) | |
116 | #define ME_READ_AD_FIFO 0x0010 /* R | - */ | |
117 | #define ME_DAC_CONTROL 0x0012 /* - | W */ | |
118 | #define DAC_UNIPOLAR_D (0<<4) | |
119 | #define DAC_BIPOLAR_D (1<<4) | |
120 | #define DAC_UNIPOLAR_C (0<<5) | |
121 | #define DAC_BIPOLAR_C (1<<5) | |
122 | #define DAC_UNIPOLAR_B (0<<6) | |
123 | #define DAC_BIPOLAR_B (1<<6) | |
124 | #define DAC_UNIPOLAR_A (0<<7) | |
125 | #define DAC_BIPOLAR_A (1<<7) | |
126 | #define DAC_GAIN_0_D (0<<8) | |
127 | #define DAC_GAIN_1_D (1<<8) | |
128 | #define DAC_GAIN_0_C (0<<9) | |
129 | #define DAC_GAIN_1_C (1<<9) | |
130 | #define DAC_GAIN_0_B (0<<10) | |
131 | #define DAC_GAIN_1_B (1<<10) | |
132 | #define DAC_GAIN_0_A (0<<11) | |
133 | #define DAC_GAIN_1_A (1<<11) | |
134 | #define ME_DAC_CONTROL_UPDATE 0x0012 /* R | - */ | |
135 | #define ME_DAC_DATA_A 0x0014 /* - | W */ | |
136 | #define ME_DAC_DATA_B 0x0016 /* - | W */ | |
137 | #define ME_DAC_DATA_C 0x0018 /* - | W */ | |
138 | #define ME_DAC_DATA_D 0x001A /* - | W */ | |
139 | #define ME_COUNTER_ENDDATA_A 0x001C /* - | W */ | |
140 | #define ME_COUNTER_ENDDATA_B 0x001E /* - | W */ | |
141 | #define ME_COUNTER_STARTDATA_A 0x0020 /* - | W */ | |
142 | #define ME_COUNTER_VALUE_A 0x0020 /* R | - */ | |
143 | #define ME_COUNTER_STARTDATA_B 0x0022 /* - | W */ | |
144 | #define ME_COUNTER_VALUE_B 0x0022 /* R | - */ | |
145 | ||
146 | /* Function prototypes */ | |
147 | static int me_attach(comedi_device *dev, comedi_devconfig *it); | |
148 | static int me_detach(comedi_device *dev); | |
85acac61 MH |
149 | |
150 | static const comedi_lrange me2000_ai_range = { | |
151 | 8, | |
152 | { | |
2ce411b5 GKH |
153 | BIP_RANGE(10), |
154 | BIP_RANGE(5), | |
155 | BIP_RANGE(2.5), | |
156 | BIP_RANGE(1.25), | |
157 | UNI_RANGE(10), | |
158 | UNI_RANGE(5), | |
159 | UNI_RANGE(2.5), | |
160 | UNI_RANGE(1.25) | |
161 | } | |
85acac61 MH |
162 | }; |
163 | ||
164 | static const comedi_lrange me2600_ai_range = { | |
165 | 8, | |
166 | { | |
167 | BIP_RANGE(10), | |
168 | BIP_RANGE(5), | |
169 | BIP_RANGE(2.5), | |
170 | BIP_RANGE(1.25), | |
171 | UNI_RANGE(10), | |
172 | UNI_RANGE(5), | |
173 | UNI_RANGE(2.5), | |
174 | UNI_RANGE(1.25) | |
175 | } | |
176 | }; | |
177 | ||
178 | static const comedi_lrange me2600_ao_range = { | |
179 | 3, | |
180 | { | |
181 | BIP_RANGE(10), | |
182 | BIP_RANGE(5), | |
183 | UNI_RANGE(10) | |
184 | } | |
185 | }; | |
186 | ||
187 | static DEFINE_PCI_DEVICE_TABLE(me_pci_table) = { | |
188 | {PCI_VENDOR_ID_MEILHAUS, ME2600_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, | |
189 | 0}, | |
190 | {PCI_VENDOR_ID_MEILHAUS, ME2000_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, | |
191 | 0}, | |
192 | {0} | |
193 | }; | |
194 | ||
195 | MODULE_DEVICE_TABLE(pci, me_pci_table); | |
196 | ||
2ce411b5 | 197 | /* Board specification structure */ |
5e08c198 | 198 | struct me_board { |
2ce411b5 | 199 | const char *name; /* driver name */ |
85acac61 | 200 | int device_id; |
2ce411b5 | 201 | int ao_channel_nbr; /* DA config */ |
85acac61 MH |
202 | int ao_resolution; |
203 | int ao_resolution_mask; | |
204 | const comedi_lrange *ao_range_list; | |
2ce411b5 | 205 | int ai_channel_nbr; /* AD config */ |
85acac61 MH |
206 | int ai_resolution; |
207 | int ai_resolution_mask; | |
208 | const comedi_lrange *ai_range_list; | |
2ce411b5 | 209 | int dio_channel_nbr; /* DIO config */ |
5e08c198 | 210 | }; |
85acac61 | 211 | |
5e08c198 | 212 | static const struct me_board me_boards[] = { |
2ce411b5 GKH |
213 | { |
214 | /* -- ME-2600i -- */ | |
215 | .name = ME_DRIVER_NAME, | |
216 | .device_id = ME2600_DEVICE_ID, | |
217 | /* Analog Output */ | |
218 | .ao_channel_nbr = 4, | |
219 | .ao_resolution = 12, | |
220 | .ao_resolution_mask = 0x0fff, | |
221 | .ao_range_list = &me2600_ao_range, | |
222 | .ai_channel_nbr = 16, | |
223 | /* Analog Input */ | |
224 | .ai_resolution = 12, | |
225 | .ai_resolution_mask = 0x0fff, | |
226 | .ai_range_list = &me2600_ai_range, | |
227 | .dio_channel_nbr = 32, | |
85acac61 | 228 | }, |
2ce411b5 GKH |
229 | { |
230 | /* -- ME-2000i -- */ | |
231 | .name = ME_DRIVER_NAME, | |
232 | .device_id = ME2000_DEVICE_ID, | |
233 | /* Analog Output */ | |
234 | .ao_channel_nbr = 0, | |
235 | .ao_resolution = 0, | |
236 | .ao_resolution_mask = 0, | |
39eca2a0 | 237 | .ao_range_list = NULL, |
2ce411b5 GKH |
238 | .ai_channel_nbr = 16, |
239 | /* Analog Input */ | |
240 | .ai_resolution = 12, | |
241 | .ai_resolution_mask = 0x0fff, | |
242 | .ai_range_list = &me2000_ai_range, | |
243 | .dio_channel_nbr = 32, | |
85acac61 MH |
244 | } |
245 | }; | |
246 | ||
5e08c198 | 247 | #define me_board_nbr (sizeof(me_boards)/sizeof(struct me_board)) |
85acac61 MH |
248 | |
249 | static comedi_driver me_driver = { | |
2ce411b5 GKH |
250 | .driver_name = ME_DRIVER_NAME, |
251 | .module = THIS_MODULE, | |
252 | .attach = me_attach, | |
253 | .detach = me_detach, | |
85acac61 | 254 | }; |
85acac61 MH |
255 | COMEDI_PCI_INITCLEANUP(me_driver, me_pci_table); |
256 | ||
2ce411b5 | 257 | /* Private data structure */ |
5e08c198 | 258 | struct me_private_data { |
85acac61 | 259 | struct pci_dev *pci_device; |
39eca2a0 GKH |
260 | void __iomem *plx_regbase; /* PLX configuration base address */ |
261 | void __iomem *me_regbase; /* Base address of the Meilhaus card */ | |
2ce411b5 GKH |
262 | unsigned long plx_regbase_size; /* Size of PLX configuration space */ |
263 | unsigned long me_regbase_size; /* Size of Meilhaus space */ | |
85acac61 | 264 | |
2ce411b5 GKH |
265 | unsigned short control_1; /* Mirror of CONTROL_1 register */ |
266 | unsigned short control_2; /* Mirror of CONTROL_2 register */ | |
267 | unsigned short dac_control; /* Mirror of the DAC_CONTROL register */ | |
268 | int ao_readback[4]; /* Mirror of analog output data */ | |
5e08c198 | 269 | }; |
85acac61 | 270 | |
5e08c198 | 271 | #define dev_private ((struct me_private_data *)dev->private) |
85acac61 | 272 | |
2ce411b5 GKH |
273 | /* |
274 | * ------------------------------------------------------------------ | |
275 | * | |
276 | * Helpful functions | |
277 | * | |
278 | * ------------------------------------------------------------------ | |
279 | */ | |
280 | static inline void sleep(unsigned sec) | |
85acac61 MH |
281 | { |
282 | current->state = TASK_INTERRUPTIBLE; | |
283 | schedule_timeout(sec * HZ); | |
284 | } | |
285 | ||
2ce411b5 GKH |
286 | /* |
287 | * ------------------------------------------------------------------ | |
288 | * | |
289 | * DIGITAL INPUT/OUTPUT SECTION | |
290 | * | |
291 | * ------------------------------------------------------------------ | |
292 | */ | |
293 | static int me_dio_insn_config(comedi_device *dev, comedi_subdevice *s, | |
790c5541 | 294 | comedi_insn *insn, unsigned int *data) |
85acac61 MH |
295 | { |
296 | int bits; | |
297 | int mask = 1 << CR_CHAN(insn->chanspec); | |
298 | ||
299 | /* calculate port */ | |
300 | if (mask & 0x0000ffff) { /* Port A in use */ | |
301 | bits = 0x0000ffff; | |
302 | ||
303 | /* Enable Port A */ | |
304 | dev_private->control_2 |= ENABLE_PORT_A; | |
305 | writew(dev_private->control_2, | |
306 | dev_private->me_regbase + ME_CONTROL_2); | |
307 | } else { /* Port B in use */ | |
308 | ||
309 | bits = 0xffff0000; | |
310 | ||
311 | /* Enable Port B */ | |
312 | dev_private->control_2 |= ENABLE_PORT_B; | |
313 | writew(dev_private->control_2, | |
314 | dev_private->me_regbase + ME_CONTROL_2); | |
315 | } | |
316 | ||
2ce411b5 GKH |
317 | if (data[0]) { |
318 | /* Config port as output */ | |
85acac61 | 319 | s->io_bits |= bits; |
2ce411b5 GKH |
320 | } else { |
321 | /* Config port as input */ | |
85acac61 MH |
322 | s->io_bits &= ~bits; |
323 | } | |
324 | ||
325 | return 1; | |
326 | } | |
327 | ||
2ce411b5 GKH |
328 | /* Digital instant input/outputs */ |
329 | static int me_dio_insn_bits(comedi_device *dev, comedi_subdevice *s, | |
790c5541 | 330 | comedi_insn *insn, unsigned int *data) |
85acac61 MH |
331 | { |
332 | unsigned int mask = data[0]; | |
333 | s->state &= ~mask; | |
334 | s->state |= (mask & data[1]); | |
335 | ||
336 | mask &= s->io_bits; | |
337 | if (mask & 0x0000ffff) { /* Port A */ | |
338 | writew((s->state & 0xffff), | |
339 | dev_private->me_regbase + ME_DIO_PORT_A); | |
340 | } else { | |
341 | data[1] &= ~0x0000ffff; | |
342 | data[1] |= readw(dev_private->me_regbase + ME_DIO_PORT_A); | |
343 | } | |
344 | ||
345 | if (mask & 0xffff0000) { /* Port B */ | |
346 | writew(((s->state >> 16) & 0xffff), | |
347 | dev_private->me_regbase + ME_DIO_PORT_B); | |
348 | } else { | |
349 | data[1] &= ~0xffff0000; | |
350 | data[1] |= readw(dev_private->me_regbase + ME_DIO_PORT_B) << 16; | |
351 | } | |
352 | ||
353 | return 2; | |
354 | } | |
355 | ||
2ce411b5 GKH |
356 | /* |
357 | * ------------------------------------------------------------------ | |
358 | * | |
359 | * ANALOG INPUT SECTION | |
360 | * | |
361 | * ------------------------------------------------------------------ | |
362 | */ | |
363 | ||
364 | /* Analog instant input */ | |
365 | static int me_ai_insn_read(comedi_device *dev, comedi_subdevice *subdevice, | |
790c5541 | 366 | comedi_insn *insn, unsigned int *data) |
85acac61 MH |
367 | { |
368 | unsigned short value; | |
369 | int chan = CR_CHAN((&insn->chanspec)[0]); | |
370 | int rang = CR_RANGE((&insn->chanspec)[0]); | |
371 | int aref = CR_AREF((&insn->chanspec)[0]); | |
372 | int i; | |
373 | ||
374 | /* stop any running conversion */ | |
375 | dev_private->control_1 &= 0xFFFC; | |
376 | writew(dev_private->control_1, dev_private->me_regbase + ME_CONTROL_1); | |
377 | ||
378 | /* clear chanlist and ad fifo */ | |
379 | dev_private->control_2 &= ~(ENABLE_ADFIFO | ENABLE_CHANLIST); | |
380 | writew(dev_private->control_2, dev_private->me_regbase + ME_CONTROL_2); | |
381 | ||
382 | /* reset any pending interrupt */ | |
383 | writew(0x00, dev_private->me_regbase + ME_RESET_INTERRUPT); | |
384 | ||
385 | /* enable the chanlist and ADC fifo */ | |
386 | dev_private->control_2 |= (ENABLE_ADFIFO | ENABLE_CHANLIST); | |
387 | writew(dev_private->control_2, dev_private->me_regbase + ME_CONTROL_2); | |
388 | ||
389 | /* write to channel list fifo */ | |
2ce411b5 GKH |
390 | /* b3:b0 are the channel number */ |
391 | value = chan & 0x0f; | |
392 | /* b5:b4 are the channel gain */ | |
393 | value |= (rang & 0x03) << 4; | |
394 | /* b6 channel polarity */ | |
395 | value |= (rang & 0x04) << 4; | |
396 | /* b7 single or differential */ | |
397 | value |= ((aref & AREF_DIFF) ? 0x80 : 0); | |
85acac61 MH |
398 | writew(value & 0xff, dev_private->me_regbase + ME_CHANNEL_LIST); |
399 | ||
400 | /* set ADC mode to software trigger */ | |
401 | dev_private->control_1 |= SOFTWARE_TRIGGERED_ADC; | |
402 | writew(dev_private->control_1, dev_private->me_regbase + ME_CONTROL_1); | |
403 | ||
404 | /* start conversion by reading from ADC_START */ | |
405 | readw(dev_private->me_regbase + ME_ADC_START); | |
406 | ||
407 | /* wait for ADC fifo not empty flag */ | |
2ce411b5 GKH |
408 | for (i = 100000; i > 0; i--) |
409 | if (!(readw(dev_private->me_regbase + ME_STATUS) & 0x0004)) | |
85acac61 | 410 | break; |
85acac61 MH |
411 | |
412 | /* get value from ADC fifo */ | |
413 | if (i) { | |
414 | data[0] = | |
415 | (readw(dev_private->me_regbase + | |
416 | ME_READ_AD_FIFO) ^ 0x800) & 0x0FFF; | |
417 | } else { | |
2ce411b5 GKH |
418 | printk(KERN_ERR "comedi%d: Cannot get single value\n", |
419 | dev->minor); | |
85acac61 MH |
420 | return -EIO; |
421 | } | |
422 | ||
423 | /* stop any running conversion */ | |
424 | dev_private->control_1 &= 0xFFFC; | |
425 | writew(dev_private->control_1, dev_private->me_regbase + ME_CONTROL_1); | |
426 | ||
427 | return 1; | |
428 | } | |
429 | ||
2ce411b5 GKH |
430 | /* |
431 | * ------------------------------------------------------------------ | |
432 | * | |
433 | * HARDWARE TRIGGERED ANALOG INPUT SECTION | |
434 | * | |
435 | * ------------------------------------------------------------------ | |
436 | */ | |
437 | ||
438 | /* Cancel analog input autoscan */ | |
439 | static int me_ai_cancel(comedi_device *dev, comedi_subdevice *s) | |
85acac61 MH |
440 | { |
441 | /* disable interrupts */ | |
442 | ||
443 | /* stop any running conversion */ | |
444 | dev_private->control_1 &= 0xFFFC; | |
445 | writew(dev_private->control_1, dev_private->me_regbase + ME_CONTROL_1); | |
446 | ||
447 | return 0; | |
448 | } | |
449 | ||
2ce411b5 GKH |
450 | /* Test analog input command */ |
451 | static int me_ai_do_cmd_test(comedi_device *dev, comedi_subdevice *s, | |
452 | comedi_cmd *cmd) | |
85acac61 MH |
453 | { |
454 | return 0; | |
455 | } | |
456 | ||
2ce411b5 GKH |
457 | /* Analog input command */ |
458 | static int me_ai_do_cmd(comedi_device *dev, comedi_subdevice *subdevice) | |
85acac61 MH |
459 | { |
460 | return 0; | |
461 | } | |
462 | ||
2ce411b5 GKH |
463 | /* |
464 | * ------------------------------------------------------------------ | |
465 | * | |
466 | * ANALOG OUTPUT SECTION | |
467 | * | |
468 | * ------------------------------------------------------------------ | |
469 | */ | |
470 | ||
471 | /* Analog instant output */ | |
472 | static int me_ao_insn_write(comedi_device *dev, comedi_subdevice *s, | |
790c5541 | 473 | comedi_insn *insn, unsigned int *data) |
85acac61 MH |
474 | { |
475 | int chan; | |
476 | int rang; | |
477 | int i; | |
478 | ||
479 | /* Enable all DAC */ | |
480 | dev_private->control_2 |= ENABLE_DAC; | |
481 | writew(dev_private->control_2, dev_private->me_regbase + ME_CONTROL_2); | |
482 | ||
483 | /* and set DAC to "buffered" mode */ | |
484 | dev_private->control_2 |= BUFFERED_DAC; | |
485 | writew(dev_private->control_2, dev_private->me_regbase + ME_CONTROL_2); | |
486 | ||
487 | /* Set dac-control register */ | |
488 | for (i = 0; i < insn->n; i++) { | |
489 | chan = CR_CHAN((&insn->chanspec)[i]); | |
490 | rang = CR_RANGE((&insn->chanspec)[i]); | |
491 | ||
2ce411b5 GKH |
492 | /* clear bits for this channel */ |
493 | dev_private->dac_control &= ~(0x0880 >> chan); | |
85acac61 MH |
494 | if (rang == 0) |
495 | dev_private->dac_control |= | |
496 | ((DAC_BIPOLAR_A | DAC_GAIN_1_A) >> chan); | |
497 | else if (rang == 1) | |
498 | dev_private->dac_control |= | |
499 | ((DAC_BIPOLAR_A | DAC_GAIN_0_A) >> chan); | |
500 | } | |
501 | writew(dev_private->dac_control, | |
502 | dev_private->me_regbase + ME_DAC_CONTROL); | |
503 | ||
504 | /* Update dac-control register */ | |
505 | readw(dev_private->me_regbase + ME_DAC_CONTROL_UPDATE); | |
506 | ||
507 | /* Set data register */ | |
508 | for (i = 0; i < insn->n; i++) { | |
509 | chan = CR_CHAN((&insn->chanspec)[i]); | |
510 | writew((data[0] & s->maxdata), | |
511 | dev_private->me_regbase + ME_DAC_DATA_A + (chan << 1)); | |
512 | dev_private->ao_readback[chan] = (data[0] & s->maxdata); | |
513 | } | |
514 | ||
515 | /* Update dac with data registers */ | |
516 | readw(dev_private->me_regbase + ME_DAC_UPDATE); | |
517 | ||
518 | return i; | |
519 | } | |
520 | ||
2ce411b5 GKH |
521 | /* Analog output readback */ |
522 | static int me_ao_insn_read(comedi_device *dev, comedi_subdevice *s, | |
790c5541 | 523 | comedi_insn *insn, unsigned int *data) |
85acac61 MH |
524 | { |
525 | int i; | |
526 | ||
527 | for (i = 0; i < insn->n; i++) { | |
528 | data[i] = | |
529 | dev_private->ao_readback[CR_CHAN((&insn->chanspec)[i])]; | |
530 | } | |
531 | ||
532 | return 1; | |
533 | } | |
534 | ||
2ce411b5 GKH |
535 | /* |
536 | * ------------------------------------------------------------------ | |
537 | * | |
538 | * INITIALISATION SECTION | |
539 | * | |
540 | * ------------------------------------------------------------------ | |
541 | */ | |
542 | ||
543 | /* Xilinx firmware download for card: ME-2600i */ | |
544 | static int me2600_xilinx_download(comedi_device *dev, | |
545 | unsigned char *me2600_firmware, | |
546 | unsigned int length) | |
85acac61 MH |
547 | { |
548 | unsigned int value; | |
549 | unsigned int file_length; | |
550 | unsigned int i; | |
551 | ||
552 | /* disable irq's on PLX */ | |
553 | writel(0x00, dev_private->plx_regbase + PLX_INTCSR); | |
554 | ||
555 | /* First, make a dummy read to reset xilinx */ | |
556 | value = readw(dev_private->me_regbase + XILINX_DOWNLOAD_RESET); | |
557 | ||
558 | /* Wait until reset is over */ | |
559 | sleep(1); | |
560 | ||
561 | /* Write a dummy value to Xilinx */ | |
562 | writeb(0x00, dev_private->me_regbase + 0x0); | |
563 | sleep(1); | |
564 | ||
565 | /* | |
566 | * Format of the firmware | |
567 | * Build longs from the byte-wise coded header | |
568 | * Byte 1-3: length of the array | |
569 | * Byte 4-7: version | |
570 | * Byte 8-11: date | |
571 | * Byte 12-15: reserved | |
572 | */ | |
573 | if (length < 16) | |
574 | return -EINVAL; | |
2ce411b5 GKH |
575 | file_length = (((unsigned int)me2600_firmware[0] & 0xff) << 24) + |
576 | (((unsigned int)me2600_firmware[1] & 0xff) << 16) + | |
577 | (((unsigned int)me2600_firmware[2] & 0xff) << 8) + | |
578 | ((unsigned int)me2600_firmware[3] & 0xff); | |
85acac61 MH |
579 | |
580 | /* | |
581 | * Loop for writing firmware byte by byte to xilinx | |
582 | * Firmware data start at offfset 16 | |
583 | */ | |
2ce411b5 | 584 | for (i = 0; i < file_length; i++) |
85acac61 MH |
585 | writeb((me2600_firmware[16 + i] & 0xff), |
586 | dev_private->me_regbase + 0x0); | |
85acac61 MH |
587 | |
588 | /* Write 5 dummy values to xilinx */ | |
2ce411b5 | 589 | for (i = 0; i < 5; i++) |
85acac61 | 590 | writeb(0x00, dev_private->me_regbase + 0x0); |
85acac61 MH |
591 | |
592 | /* Test if there was an error during download -> INTB was thrown */ | |
593 | value = readl(dev_private->plx_regbase + PLX_INTCSR); | |
594 | if (value & 0x20) { | |
595 | /* Disable interrupt */ | |
596 | writel(0x00, dev_private->plx_regbase + PLX_INTCSR); | |
2ce411b5 GKH |
597 | printk(KERN_ERR "comedi%d: Xilinx download failed\n", |
598 | dev->minor); | |
85acac61 MH |
599 | return -EIO; |
600 | } | |
601 | ||
602 | /* Wait until the Xilinx is ready for real work */ | |
603 | sleep(1); | |
604 | ||
605 | /* Enable PLX-Interrupts */ | |
606 | writel(0x43, dev_private->plx_regbase + PLX_INTCSR); | |
607 | ||
608 | return 0; | |
609 | } | |
610 | ||
2ce411b5 GKH |
611 | /* Reset device */ |
612 | static int me_reset(comedi_device *dev) | |
85acac61 MH |
613 | { |
614 | /* Reset board */ | |
615 | writew(0x00, dev_private->me_regbase + ME_CONTROL_1); | |
616 | writew(0x00, dev_private->me_regbase + ME_CONTROL_2); | |
617 | writew(0x00, dev_private->me_regbase + ME_RESET_INTERRUPT); | |
618 | writew(0x00, dev_private->me_regbase + ME_DAC_CONTROL); | |
619 | ||
620 | /* Save values in the board context */ | |
621 | dev_private->dac_control = 0; | |
622 | dev_private->control_1 = 0; | |
623 | dev_private->control_2 = 0; | |
624 | ||
625 | return 0; | |
626 | } | |
627 | ||
2ce411b5 GKH |
628 | /* |
629 | * Attach | |
630 | * | |
631 | * - Register PCI device | |
632 | * - Declare device driver capability | |
633 | */ | |
634 | static int me_attach(comedi_device *dev, comedi_devconfig *it) | |
85acac61 MH |
635 | { |
636 | struct pci_dev *pci_device; | |
637 | comedi_subdevice *subdevice; | |
5e08c198 | 638 | struct me_board *board; |
85acac61 MH |
639 | resource_size_t plx_regbase_tmp; |
640 | unsigned long plx_regbase_size_tmp; | |
641 | resource_size_t me_regbase_tmp; | |
642 | unsigned long me_regbase_size_tmp; | |
643 | resource_size_t swap_regbase_tmp; | |
644 | unsigned long swap_regbase_size_tmp; | |
645 | resource_size_t regbase_tmp; | |
646 | int result, error, i; | |
647 | ||
2ce411b5 | 648 | /* Allocate private memory */ |
5e08c198 | 649 | if (alloc_private(dev, sizeof(struct me_private_data)) < 0) |
85acac61 | 650 | return -ENOMEM; |
2ce411b5 GKH |
651 | |
652 | /* Probe the device to determine what device in the series it is. */ | |
85acac61 MH |
653 | for (pci_device = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); |
654 | pci_device != NULL; | |
655 | pci_device = | |
656 | pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_device)) { | |
657 | if (pci_device->vendor == PCI_VENDOR_ID_MEILHAUS) { | |
658 | for (i = 0; i < me_board_nbr; i++) { | |
659 | if (me_boards[i].device_id == | |
660 | pci_device->device) { | |
2ce411b5 GKH |
661 | /* |
662 | * was a particular bus/slot requested? | |
663 | */ | |
85acac61 MH |
664 | if ((it->options[0] != 0) |
665 | || (it->options[1] != 0)) { | |
2ce411b5 GKH |
666 | /* |
667 | * are we on the wrong bus/slot? | |
668 | */ | |
85acac61 MH |
669 | if (pci_device->bus->number != |
670 | it->options[0] | |
671 | || PCI_SLOT(pci_device-> | |
672 | devfn) != | |
673 | it->options[1]) { | |
674 | continue; | |
675 | } | |
676 | } | |
677 | ||
678 | dev->board_ptr = me_boards + i; | |
5e08c198 | 679 | board = (struct me_board *) dev-> |
85acac61 MH |
680 | board_ptr; |
681 | dev_private->pci_device = pci_device; | |
682 | goto found; | |
683 | } | |
684 | } | |
685 | } | |
686 | } | |
687 | ||
2ce411b5 GKH |
688 | printk(KERN_ERR |
689 | "comedi%d: no supported board found! (req. bus/slot : %d/%d)\n", | |
690 | dev->minor, it->options[0], it->options[1]); | |
85acac61 MH |
691 | return -EIO; |
692 | ||
2ce411b5 GKH |
693 | found: |
694 | printk(KERN_INFO "comedi%d: found %s at PCI bus %d, slot %d\n", | |
85acac61 MH |
695 | dev->minor, me_boards[i].name, |
696 | pci_device->bus->number, PCI_SLOT(pci_device->devfn)); | |
697 | ||
2ce411b5 | 698 | /* Enable PCI device and request PCI regions */ |
85acac61 | 699 | if (comedi_pci_enable(pci_device, ME_DRIVER_NAME) < 0) { |
2ce411b5 GKH |
700 | printk(KERN_ERR "comedi%d: Failed to enable PCI device and " |
701 | "request regions\n", dev->minor); | |
85acac61 MH |
702 | return -EIO; |
703 | } | |
85acac61 | 704 | |
2ce411b5 | 705 | /* Set data in device structure */ |
85acac61 MH |
706 | dev->board_name = board->name; |
707 | ||
2ce411b5 | 708 | /* Read PLX register base address [PCI_BASE_ADDRESS #0]. */ |
85acac61 MH |
709 | plx_regbase_tmp = pci_resource_start(pci_device, 0); |
710 | plx_regbase_size_tmp = pci_resource_len(pci_device, 0); | |
711 | dev_private->plx_regbase = | |
712 | ioremap(plx_regbase_tmp, plx_regbase_size_tmp); | |
713 | dev_private->plx_regbase_size = plx_regbase_size_tmp; | |
714 | if (!dev_private->plx_regbase) { | |
715 | printk("comedi%d: Failed to remap I/O memory\n", dev->minor); | |
716 | return -ENOMEM; | |
717 | } | |
2ce411b5 GKH |
718 | |
719 | /* Read Swap base address [PCI_BASE_ADDRESS #5]. */ | |
85acac61 MH |
720 | |
721 | swap_regbase_tmp = pci_resource_start(pci_device, 5); | |
722 | swap_regbase_size_tmp = pci_resource_len(pci_device, 5); | |
723 | ||
2ce411b5 GKH |
724 | if (!swap_regbase_tmp) |
725 | printk(KERN_ERR "comedi%d: Swap not present\n", dev->minor); | |
85acac61 | 726 | |
2ce411b5 | 727 | /*---------------------------------------------- Workaround start ---*/ |
85acac61 | 728 | if (plx_regbase_tmp & 0x0080) { |
2ce411b5 | 729 | printk(KERN_ERR "comedi%d: PLX-Bug detected\n", dev->minor); |
85acac61 MH |
730 | |
731 | if (swap_regbase_tmp) { | |
732 | regbase_tmp = plx_regbase_tmp; | |
733 | plx_regbase_tmp = swap_regbase_tmp; | |
734 | swap_regbase_tmp = regbase_tmp; | |
735 | ||
736 | result = pci_write_config_dword(pci_device, | |
737 | PCI_BASE_ADDRESS_0, plx_regbase_tmp); | |
738 | if (result != PCIBIOS_SUCCESSFUL) | |
739 | return -EIO; | |
740 | ||
741 | result = pci_write_config_dword(pci_device, | |
742 | PCI_BASE_ADDRESS_5, swap_regbase_tmp); | |
743 | if (result != PCIBIOS_SUCCESSFUL) | |
744 | return -EIO; | |
745 | } else { | |
746 | plx_regbase_tmp -= 0x80; | |
747 | result = pci_write_config_dword(pci_device, | |
748 | PCI_BASE_ADDRESS_0, plx_regbase_tmp); | |
749 | if (result != PCIBIOS_SUCCESSFUL) | |
750 | return -EIO; | |
751 | } | |
752 | } | |
2ce411b5 | 753 | /*--------------------------------------------- Workaround end -----*/ |
85acac61 | 754 | |
2ce411b5 | 755 | /* Read Meilhaus register base address [PCI_BASE_ADDRESS #2]. */ |
85acac61 MH |
756 | |
757 | me_regbase_tmp = pci_resource_start(pci_device, 2); | |
758 | me_regbase_size_tmp = pci_resource_len(pci_device, 2); | |
759 | dev_private->me_regbase_size = me_regbase_size_tmp; | |
760 | dev_private->me_regbase = ioremap(me_regbase_tmp, me_regbase_size_tmp); | |
761 | if (!dev_private->me_regbase) { | |
2ce411b5 GKH |
762 | printk(KERN_ERR "comedi%d: Failed to remap I/O memory\n", |
763 | dev->minor); | |
85acac61 MH |
764 | return -ENOMEM; |
765 | } | |
2ce411b5 | 766 | /* Download firmware and reset card */ |
85acac61 MH |
767 | if (board->device_id == ME2600_DEVICE_ID) { |
768 | unsigned char *aux_data; | |
769 | int aux_len; | |
770 | ||
771 | aux_data = comedi_aux_data(it->options, 0); | |
772 | aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]; | |
773 | ||
774 | if (!aux_data || aux_len < 1) { | |
2ce411b5 GKH |
775 | comedi_error(dev, "You must provide me2600 firmware " |
776 | "using the --init-data option of " | |
777 | "comedi_config"); | |
85acac61 MH |
778 | return -EINVAL; |
779 | } | |
780 | me2600_xilinx_download(dev, aux_data, aux_len); | |
781 | } | |
782 | ||
783 | me_reset(dev); | |
784 | ||
2ce411b5 GKH |
785 | /* device driver capabilities */ |
786 | error = alloc_subdevices(dev, 3); | |
787 | if (error < 0) | |
85acac61 MH |
788 | return error; |
789 | ||
790 | subdevice = dev->subdevices + 0; | |
791 | subdevice->type = COMEDI_SUBD_AI; | |
792 | subdevice->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_CMD_READ; | |
793 | subdevice->n_chan = board->ai_channel_nbr; | |
794 | subdevice->maxdata = board->ai_resolution_mask; | |
795 | subdevice->len_chanlist = board->ai_channel_nbr; | |
796 | subdevice->range_table = board->ai_range_list; | |
797 | subdevice->cancel = me_ai_cancel; | |
798 | subdevice->insn_read = me_ai_insn_read; | |
799 | subdevice->do_cmdtest = me_ai_do_cmd_test; | |
800 | subdevice->do_cmd = me_ai_do_cmd; | |
801 | ||
802 | subdevice = dev->subdevices + 1; | |
803 | subdevice->type = COMEDI_SUBD_AO; | |
804 | subdevice->subdev_flags = SDF_WRITEABLE | SDF_COMMON; | |
805 | subdevice->n_chan = board->ao_channel_nbr; | |
806 | subdevice->maxdata = board->ao_resolution_mask; | |
807 | subdevice->len_chanlist = board->ao_channel_nbr; | |
808 | subdevice->range_table = board->ao_range_list; | |
809 | subdevice->insn_read = me_ao_insn_read; | |
810 | subdevice->insn_write = me_ao_insn_write; | |
811 | ||
812 | subdevice = dev->subdevices + 2; | |
813 | subdevice->type = COMEDI_SUBD_DIO; | |
814 | subdevice->subdev_flags = SDF_READABLE | SDF_WRITEABLE; | |
815 | subdevice->n_chan = board->dio_channel_nbr; | |
816 | subdevice->maxdata = 1; | |
817 | subdevice->len_chanlist = board->dio_channel_nbr; | |
818 | subdevice->range_table = &range_digital; | |
819 | subdevice->insn_bits = me_dio_insn_bits; | |
820 | subdevice->insn_config = me_dio_insn_config; | |
821 | subdevice->io_bits = 0; | |
822 | ||
2ce411b5 | 823 | printk(KERN_INFO "comedi%d: "ME_DRIVER_NAME" attached.\n", dev->minor); |
85acac61 MH |
824 | return 0; |
825 | } | |
826 | ||
2ce411b5 GKH |
827 | /* Detach */ |
828 | static int me_detach(comedi_device *dev) | |
85acac61 MH |
829 | { |
830 | if (dev_private) { | |
831 | if (dev_private->me_regbase) { | |
832 | me_reset(dev); | |
833 | iounmap(dev_private->me_regbase); | |
834 | } | |
835 | if (dev_private->plx_regbase) | |
836 | iounmap(dev_private->plx_regbase); | |
837 | if (dev_private->pci_device) { | |
2ce411b5 | 838 | if (dev_private->plx_regbase_size) |
85acac61 | 839 | comedi_pci_disable(dev_private->pci_device); |
2ce411b5 | 840 | |
85acac61 MH |
841 | pci_dev_put(dev_private->pci_device); |
842 | } | |
843 | } | |
844 | return 0; | |
845 | } |