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