diff options
author | Michael Hillmann <hillmann@syscongroup.de> | 2008-11-19 17:26:25 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-01-06 16:52:26 -0500 |
commit | 85acac61096f946a78cf0c4b65f7cebe580693b6 (patch) | |
tree | eb11c512a812346f3b85aa5b5d3f106770630d7a /drivers/staging/comedi | |
parent | 3d9f073994925a2c8206e41b12a8c12282972cec (diff) |
Staging: comedi: add me_daq driver
This adds the me_daq comedi driver to the build.
From: Michael Hillmann <hillmann@syscongroup.de>
Cc: David Schleef <ds@schleef.org>
Cc: Frank Mori Hess <fmhess@users.sourceforge.net>
Cc: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/comedi')
-rw-r--r-- | drivers/staging/comedi/drivers/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/comedi/drivers/me_daq.c | 859 |
2 files changed, 860 insertions, 0 deletions
diff --git a/drivers/staging/comedi/drivers/Makefile b/drivers/staging/comedi/drivers/Makefile index 3dbd4f12139..eb7a615e085 100644 --- a/drivers/staging/comedi/drivers/Makefile +++ b/drivers/staging/comedi/drivers/Makefile | |||
@@ -10,6 +10,7 @@ obj-$(CONFIG_COMEDI) += comedi_parport.o | |||
10 | # Comedi PCI drivers | 10 | # Comedi PCI drivers |
11 | obj-$(CONFIG_COMEDI_PCI_DRIVERS) += mite.o | 11 | obj-$(CONFIG_COMEDI_PCI_DRIVERS) += mite.o |
12 | obj-$(CONFIG_COMEDI_PCI_DRIVERS) += icp_multi.o | 12 | obj-$(CONFIG_COMEDI_PCI_DRIVERS) += icp_multi.o |
13 | obj-$(CONFIG_COMEDI_PCI_DRIVERS) += me_daq.o | ||
13 | obj-$(CONFIG_COMEDI_PCI_DRIVERS) += me4000.o | 14 | obj-$(CONFIG_COMEDI_PCI_DRIVERS) += me4000.o |
14 | obj-$(CONFIG_COMEDI_PCI_DRIVERS) += rtd520.o | 15 | obj-$(CONFIG_COMEDI_PCI_DRIVERS) += rtd520.o |
15 | obj-$(CONFIG_COMEDI_PCI_DRIVERS) += s626.o | 16 | obj-$(CONFIG_COMEDI_PCI_DRIVERS) += s626.o |
diff --git a/drivers/staging/comedi/drivers/me_daq.c b/drivers/staging/comedi/drivers/me_daq.c new file mode 100644 index 00000000000..e741ec54527 --- /dev/null +++ b/drivers/staging/comedi/drivers/me_daq.c | |||
@@ -0,0 +1,859 @@ | |||
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 | |||
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 | // | ||
147 | // Function prototypes | ||
148 | // | ||
149 | |||
150 | static int me_attach(comedi_device * dev, comedi_devconfig * it); | ||
151 | static int me_detach(comedi_device * dev); | ||
152 | |||
153 | static const comedi_lrange me2000_ai_range = { | ||
154 | 8, | ||
155 | { | ||
156 | BIP_RANGE(10), | ||
157 | BIP_RANGE(5), | ||
158 | BIP_RANGE(2.5), | ||
159 | BIP_RANGE(1.25), | ||
160 | UNI_RANGE(10), | ||
161 | UNI_RANGE(5), | ||
162 | UNI_RANGE(2.5), | ||
163 | UNI_RANGE(1.25) | ||
164 | } | ||
165 | }; | ||
166 | |||
167 | static const comedi_lrange me2600_ai_range = { | ||
168 | 8, | ||
169 | { | ||
170 | BIP_RANGE(10), | ||
171 | BIP_RANGE(5), | ||
172 | BIP_RANGE(2.5), | ||
173 | BIP_RANGE(1.25), | ||
174 | UNI_RANGE(10), | ||
175 | UNI_RANGE(5), | ||
176 | UNI_RANGE(2.5), | ||
177 | UNI_RANGE(1.25) | ||
178 | } | ||
179 | }; | ||
180 | |||
181 | static const comedi_lrange me2600_ao_range = { | ||
182 | 3, | ||
183 | { | ||
184 | BIP_RANGE(10), | ||
185 | BIP_RANGE(5), | ||
186 | UNI_RANGE(10) | ||
187 | } | ||
188 | }; | ||
189 | |||
190 | static DEFINE_PCI_DEVICE_TABLE(me_pci_table) = { | ||
191 | {PCI_VENDOR_ID_MEILHAUS, ME2600_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, | ||
192 | 0}, | ||
193 | {PCI_VENDOR_ID_MEILHAUS, ME2000_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, | ||
194 | 0}, | ||
195 | {0} | ||
196 | }; | ||
197 | |||
198 | MODULE_DEVICE_TABLE(pci, me_pci_table); | ||
199 | |||
200 | // | ||
201 | // Board specification structure | ||
202 | // | ||
203 | |||
204 | typedef struct { | ||
205 | const char *name; // driver name | ||
206 | int device_id; | ||
207 | int ao_channel_nbr; // DA config | ||
208 | int ao_resolution; | ||
209 | int ao_resolution_mask; | ||
210 | const comedi_lrange *ao_range_list; | ||
211 | int ai_channel_nbr; // AD config | ||
212 | int ai_resolution; | ||
213 | int ai_resolution_mask; | ||
214 | const comedi_lrange *ai_range_list; | ||
215 | int dio_channel_nbr; // DIO config | ||
216 | } me_board_struct; | ||
217 | |||
218 | static const me_board_struct me_boards[] = { | ||
219 | { // -- ME-2600i -- | ||
220 | name: ME_DRIVER_NAME, | ||
221 | device_id:ME2600_DEVICE_ID, | ||
222 | ao_channel_nbr:4,// Analog Output | ||
223 | ao_resolution:12, | ||
224 | ao_resolution_mask:0x0fff, | ||
225 | ao_range_list:&me2600_ao_range, | ||
226 | ai_channel_nbr:16, | ||
227 | // Analog Input | ||
228 | ai_resolution:12, | ||
229 | ai_resolution_mask:0x0fff, | ||
230 | ai_range_list:&me2600_ai_range, | ||
231 | dio_channel_nbr:32, | ||
232 | }, | ||
233 | { // -- ME-2000i -- | ||
234 | name: ME_DRIVER_NAME, | ||
235 | device_id:ME2000_DEVICE_ID, | ||
236 | ao_channel_nbr:0,// Analog Output | ||
237 | ao_resolution:0, | ||
238 | ao_resolution_mask:0, | ||
239 | ao_range_list:0, | ||
240 | ai_channel_nbr:16, | ||
241 | // Analog Input | ||
242 | ai_resolution:12, | ||
243 | ai_resolution_mask:0x0fff, | ||
244 | ai_range_list:&me2000_ai_range, | ||
245 | dio_channel_nbr:32, | ||
246 | } | ||
247 | }; | ||
248 | |||
249 | #define me_board_nbr (sizeof(me_boards)/sizeof(me_board_struct)) | ||
250 | |||
251 | static comedi_driver me_driver = { | ||
252 | driver_name:ME_DRIVER_NAME, | ||
253 | module:THIS_MODULE, | ||
254 | attach:me_attach, | ||
255 | detach:me_detach, | ||
256 | }; | ||
257 | |||
258 | COMEDI_PCI_INITCLEANUP(me_driver, me_pci_table); | ||
259 | |||
260 | // | ||
261 | // Private data structure | ||
262 | // | ||
263 | |||
264 | typedef struct { | ||
265 | struct pci_dev *pci_device; | ||
266 | void *plx_regbase; // PLX configuration base address | ||
267 | void *me_regbase; // Base address of the Meilhaus card | ||
268 | unsigned long plx_regbase_size; // Size of PLX configuration space | ||
269 | unsigned long me_regbase_size; // Size of Meilhaus space | ||
270 | |||
271 | unsigned short control_1; // Mirror of CONTROL_1 register | ||
272 | unsigned short control_2; // Mirror of CONTROL_2 register | ||
273 | unsigned short dac_control; // Mirror of the DAC_CONTROL register | ||
274 | int ao_readback[4]; // Mirror of analog output data | ||
275 | |||
276 | } me_private_data_struct; | ||
277 | |||
278 | #define dev_private ((me_private_data_struct *)dev->private) | ||
279 | |||
280 | // ------------------------------------------------------------------ | ||
281 | // | ||
282 | // Helpful functions | ||
283 | // | ||
284 | // ------------------------------------------------------------------ | ||
285 | |||
286 | static __inline__ void sleep(unsigned sec) | ||
287 | { | ||
288 | current->state = TASK_INTERRUPTIBLE; | ||
289 | schedule_timeout(sec * HZ); | ||
290 | } | ||
291 | |||
292 | // ------------------------------------------------------------------ | ||
293 | // | ||
294 | // DIGITAL INPUT/OUTPUT SECTION | ||
295 | // | ||
296 | // ------------------------------------------------------------------ | ||
297 | |||
298 | static int me_dio_insn_config(comedi_device * dev, | ||
299 | comedi_subdevice * s, comedi_insn * insn, lsampl_t * data) | ||
300 | { | ||
301 | int bits; | ||
302 | int mask = 1 << CR_CHAN(insn->chanspec); | ||
303 | |||
304 | /* calculate port */ | ||
305 | if (mask & 0x0000ffff) { /* Port A in use */ | ||
306 | bits = 0x0000ffff; | ||
307 | |||
308 | /* Enable Port A */ | ||
309 | dev_private->control_2 |= ENABLE_PORT_A; | ||
310 | writew(dev_private->control_2, | ||
311 | dev_private->me_regbase + ME_CONTROL_2); | ||
312 | } else { /* Port B in use */ | ||
313 | |||
314 | bits = 0xffff0000; | ||
315 | |||
316 | /* Enable Port B */ | ||
317 | dev_private->control_2 |= ENABLE_PORT_B; | ||
318 | writew(dev_private->control_2, | ||
319 | dev_private->me_regbase + ME_CONTROL_2); | ||
320 | } | ||
321 | |||
322 | if (data[0]) { /* Config port as output */ | ||
323 | s->io_bits |= bits; | ||
324 | } else { /* Config port as input */ | ||
325 | |||
326 | s->io_bits &= ~bits; | ||
327 | } | ||
328 | |||
329 | return 1; | ||
330 | } | ||
331 | |||
332 | // | ||
333 | // Digital instant input/outputs | ||
334 | // | ||
335 | |||
336 | static int me_dio_insn_bits(comedi_device * dev, | ||
337 | comedi_subdevice * s, comedi_insn * insn, lsampl_t * data) | ||
338 | { | ||
339 | unsigned int mask = data[0]; | ||
340 | s->state &= ~mask; | ||
341 | s->state |= (mask & data[1]); | ||
342 | |||
343 | mask &= s->io_bits; | ||
344 | if (mask & 0x0000ffff) { /* Port A */ | ||
345 | writew((s->state & 0xffff), | ||
346 | dev_private->me_regbase + ME_DIO_PORT_A); | ||
347 | } else { | ||
348 | data[1] &= ~0x0000ffff; | ||
349 | data[1] |= readw(dev_private->me_regbase + ME_DIO_PORT_A); | ||
350 | } | ||
351 | |||
352 | if (mask & 0xffff0000) { /* Port B */ | ||
353 | writew(((s->state >> 16) & 0xffff), | ||
354 | dev_private->me_regbase + ME_DIO_PORT_B); | ||
355 | } else { | ||
356 | data[1] &= ~0xffff0000; | ||
357 | data[1] |= readw(dev_private->me_regbase + ME_DIO_PORT_B) << 16; | ||
358 | } | ||
359 | |||
360 | return 2; | ||
361 | } | ||
362 | |||
363 | // ------------------------------------------------------------------ | ||
364 | // | ||
365 | // ANALOG INPUT SECTION | ||
366 | // | ||
367 | // ------------------------------------------------------------------ | ||
368 | |||
369 | // | ||
370 | // Analog instant input | ||
371 | // | ||
372 | static int me_ai_insn_read(comedi_device * dev, | ||
373 | comedi_subdevice * subdevice, comedi_insn * insn, lsampl_t * data) | ||
374 | { | ||
375 | unsigned short value; | ||
376 | int chan = CR_CHAN((&insn->chanspec)[0]); | ||
377 | int rang = CR_RANGE((&insn->chanspec)[0]); | ||
378 | int aref = CR_AREF((&insn->chanspec)[0]); | ||
379 | int i; | ||
380 | |||
381 | /* stop any running conversion */ | ||
382 | dev_private->control_1 &= 0xFFFC; | ||
383 | writew(dev_private->control_1, dev_private->me_regbase + ME_CONTROL_1); | ||
384 | |||
385 | /* clear chanlist and ad 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 | /* reset any pending interrupt */ | ||
390 | writew(0x00, dev_private->me_regbase + ME_RESET_INTERRUPT); | ||
391 | |||
392 | /* enable the chanlist and ADC fifo */ | ||
393 | dev_private->control_2 |= (ENABLE_ADFIFO | ENABLE_CHANLIST); | ||
394 | writew(dev_private->control_2, dev_private->me_regbase + ME_CONTROL_2); | ||
395 | |||
396 | /* write to channel list fifo */ | ||
397 | value = chan & 0x0f; // b3:b0 are the channel number | ||
398 | value |= (rang & 0x03) << 4; // b5:b4 are the channel gain | ||
399 | value |= (rang & 0x04) << 4; // b6 channel polarity | ||
400 | value |= ((aref & AREF_DIFF) ? 0x80 : 0); // b7 single or differential | ||
401 | writew(value & 0xff, dev_private->me_regbase + ME_CHANNEL_LIST); | ||
402 | |||
403 | /* set ADC mode to software trigger */ | ||
404 | dev_private->control_1 |= SOFTWARE_TRIGGERED_ADC; | ||
405 | writew(dev_private->control_1, dev_private->me_regbase + ME_CONTROL_1); | ||
406 | |||
407 | /* start conversion by reading from ADC_START */ | ||
408 | readw(dev_private->me_regbase + ME_ADC_START); | ||
409 | |||
410 | /* wait for ADC fifo not empty flag */ | ||
411 | for (i = 100000; i > 0; i--) { | ||
412 | if (!(readw(dev_private->me_regbase + ME_STATUS) & 0x0004)) { | ||
413 | break; | ||
414 | } | ||
415 | } | ||
416 | |||
417 | /* get value from ADC fifo */ | ||
418 | if (i) { | ||
419 | data[0] = | ||
420 | (readw(dev_private->me_regbase + | ||
421 | ME_READ_AD_FIFO) ^ 0x800) & 0x0FFF; | ||
422 | } else { | ||
423 | printk("comedi%d: Cannot get single value\n", dev->minor); | ||
424 | return -EIO; | ||
425 | } | ||
426 | |||
427 | /* stop any running conversion */ | ||
428 | dev_private->control_1 &= 0xFFFC; | ||
429 | writew(dev_private->control_1, dev_private->me_regbase + ME_CONTROL_1); | ||
430 | |||
431 | return 1; | ||
432 | } | ||
433 | |||
434 | // ------------------------------------------------------------------ | ||
435 | // | ||
436 | // HARDWARE TRIGGERED ANALOG INPUT SECTION | ||
437 | // | ||
438 | // ------------------------------------------------------------------ | ||
439 | |||
440 | // | ||
441 | // Cancel analog input autoscan | ||
442 | // | ||
443 | static int me_ai_cancel(comedi_device * dev, comedi_subdevice * s) | ||
444 | { | ||
445 | /* disable interrupts */ | ||
446 | |||
447 | /* stop any running conversion */ | ||
448 | dev_private->control_1 &= 0xFFFC; | ||
449 | writew(dev_private->control_1, dev_private->me_regbase + ME_CONTROL_1); | ||
450 | |||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | // | ||
455 | // Test analog input command | ||
456 | // | ||
457 | static int me_ai_do_cmd_test(comedi_device * dev, | ||
458 | comedi_subdevice * s, comedi_cmd * cmd) | ||
459 | { | ||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | // | ||
464 | // Analog input command | ||
465 | // | ||
466 | static int me_ai_do_cmd(comedi_device * dev, comedi_subdevice * subdevice) | ||
467 | { | ||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | // ------------------------------------------------------------------ | ||
472 | // | ||
473 | // ANALOG OUTPUT SECTION | ||
474 | // | ||
475 | // ------------------------------------------------------------------ | ||
476 | |||
477 | // | ||
478 | // Analog instant output | ||
479 | // | ||
480 | static int me_ao_insn_write(comedi_device * dev, | ||
481 | comedi_subdevice * s, comedi_insn * insn, lsampl_t * data) | ||
482 | { | ||
483 | int chan; | ||
484 | int rang; | ||
485 | int i; | ||
486 | |||
487 | /* Enable all DAC */ | ||
488 | dev_private->control_2 |= ENABLE_DAC; | ||
489 | writew(dev_private->control_2, dev_private->me_regbase + ME_CONTROL_2); | ||
490 | |||
491 | /* and set DAC to "buffered" mode */ | ||
492 | dev_private->control_2 |= BUFFERED_DAC; | ||
493 | writew(dev_private->control_2, dev_private->me_regbase + ME_CONTROL_2); | ||
494 | |||
495 | /* Set dac-control register */ | ||
496 | for (i = 0; i < insn->n; i++) { | ||
497 | chan = CR_CHAN((&insn->chanspec)[i]); | ||
498 | rang = CR_RANGE((&insn->chanspec)[i]); | ||
499 | |||
500 | dev_private->dac_control &= ~(0x0880 >> chan); /* clear bits for this channel */ | ||
501 | if (rang == 0) | ||
502 | dev_private->dac_control |= | ||
503 | ((DAC_BIPOLAR_A | DAC_GAIN_1_A) >> chan); | ||
504 | else if (rang == 1) | ||
505 | dev_private->dac_control |= | ||
506 | ((DAC_BIPOLAR_A | DAC_GAIN_0_A) >> chan); | ||
507 | } | ||
508 | writew(dev_private->dac_control, | ||
509 | dev_private->me_regbase + ME_DAC_CONTROL); | ||
510 | |||
511 | /* Update dac-control register */ | ||
512 | readw(dev_private->me_regbase + ME_DAC_CONTROL_UPDATE); | ||
513 | |||
514 | /* Set data register */ | ||
515 | for (i = 0; i < insn->n; i++) { | ||
516 | chan = CR_CHAN((&insn->chanspec)[i]); | ||
517 | writew((data[0] & s->maxdata), | ||
518 | dev_private->me_regbase + ME_DAC_DATA_A + (chan << 1)); | ||
519 | dev_private->ao_readback[chan] = (data[0] & s->maxdata); | ||
520 | } | ||
521 | |||
522 | /* Update dac with data registers */ | ||
523 | readw(dev_private->me_regbase + ME_DAC_UPDATE); | ||
524 | |||
525 | return i; | ||
526 | } | ||
527 | |||
528 | // | ||
529 | // Analog output readback | ||
530 | // | ||
531 | static int me_ao_insn_read(comedi_device * dev, | ||
532 | comedi_subdevice * s, comedi_insn * insn, lsampl_t * data) | ||
533 | { | ||
534 | int i; | ||
535 | |||
536 | for (i = 0; i < insn->n; i++) { | ||
537 | data[i] = | ||
538 | dev_private->ao_readback[CR_CHAN((&insn->chanspec)[i])]; | ||
539 | } | ||
540 | |||
541 | return 1; | ||
542 | } | ||
543 | |||
544 | // ------------------------------------------------------------------ | ||
545 | // | ||
546 | // INITIALISATION SECTION | ||
547 | // | ||
548 | // ------------------------------------------------------------------ | ||
549 | |||
550 | // | ||
551 | // Xilinx firmware download for card: ME-2600i | ||
552 | // | ||
553 | |||
554 | static int me2600_xilinx_download(comedi_device * dev, unsigned char | ||
555 | *me2600_firmware, unsigned int length) | ||
556 | { | ||
557 | unsigned int value; | ||
558 | unsigned int file_length; | ||
559 | unsigned int i; | ||
560 | |||
561 | /* disable irq's on PLX */ | ||
562 | writel(0x00, dev_private->plx_regbase + PLX_INTCSR); | ||
563 | |||
564 | /* First, make a dummy read to reset xilinx */ | ||
565 | value = readw(dev_private->me_regbase + XILINX_DOWNLOAD_RESET); | ||
566 | |||
567 | /* Wait until reset is over */ | ||
568 | sleep(1); | ||
569 | |||
570 | /* Write a dummy value to Xilinx */ | ||
571 | writeb(0x00, dev_private->me_regbase + 0x0); | ||
572 | sleep(1); | ||
573 | |||
574 | /* | ||
575 | * Format of the firmware | ||
576 | * Build longs from the byte-wise coded header | ||
577 | * Byte 1-3: length of the array | ||
578 | * Byte 4-7: version | ||
579 | * Byte 8-11: date | ||
580 | * Byte 12-15: reserved | ||
581 | */ | ||
582 | if (length < 16) | ||
583 | return -EINVAL; | ||
584 | file_length = | ||
585 | (((unsigned int)me2600_firmware[0] & 0xff) << 24) + | ||
586 | (((unsigned int)me2600_firmware[1] & 0xff) << 16) + | ||
587 | (((unsigned int)me2600_firmware[2] & 0xff) << 8) + | ||
588 | ((unsigned int)me2600_firmware[3] & 0xff); | ||
589 | |||
590 | /* | ||
591 | * Loop for writing firmware byte by byte to xilinx | ||
592 | * Firmware data start at offfset 16 | ||
593 | */ | ||
594 | for (i = 0; i < file_length; i++) { | ||
595 | writeb((me2600_firmware[16 + i] & 0xff), | ||
596 | dev_private->me_regbase + 0x0); | ||
597 | } | ||
598 | |||
599 | /* Write 5 dummy values to xilinx */ | ||
600 | for (i = 0; i < 5; i++) { | ||
601 | writeb(0x00, dev_private->me_regbase + 0x0); | ||
602 | } | ||
603 | |||
604 | /* Test if there was an error during download -> INTB was thrown */ | ||
605 | value = readl(dev_private->plx_regbase + PLX_INTCSR); | ||
606 | if (value & 0x20) { | ||
607 | /* Disable interrupt */ | ||
608 | writel(0x00, dev_private->plx_regbase + PLX_INTCSR); | ||
609 | printk("comedi%d: Xilinx download failed\n", dev->minor); | ||
610 | return -EIO; | ||
611 | } | ||
612 | |||
613 | /* Wait until the Xilinx is ready for real work */ | ||
614 | sleep(1); | ||
615 | |||
616 | /* Enable PLX-Interrupts */ | ||
617 | writel(0x43, dev_private->plx_regbase + PLX_INTCSR); | ||
618 | |||
619 | return 0; | ||
620 | } | ||
621 | |||
622 | // | ||
623 | // Reset device | ||
624 | // | ||
625 | |||
626 | static int me_reset(comedi_device * dev) | ||
627 | { | ||
628 | /* Reset board */ | ||
629 | writew(0x00, dev_private->me_regbase + ME_CONTROL_1); | ||
630 | writew(0x00, dev_private->me_regbase + ME_CONTROL_2); | ||
631 | writew(0x00, dev_private->me_regbase + ME_RESET_INTERRUPT); | ||
632 | writew(0x00, dev_private->me_regbase + ME_DAC_CONTROL); | ||
633 | |||
634 | /* Save values in the board context */ | ||
635 | dev_private->dac_control = 0; | ||
636 | dev_private->control_1 = 0; | ||
637 | dev_private->control_2 = 0; | ||
638 | |||
639 | return 0; | ||
640 | } | ||
641 | |||
642 | // | ||
643 | // Attach | ||
644 | // | ||
645 | // - Register PCI device | ||
646 | // - Declare device driver capability | ||
647 | // | ||
648 | |||
649 | static int me_attach(comedi_device * dev, comedi_devconfig * it) | ||
650 | { | ||
651 | struct pci_dev *pci_device; | ||
652 | comedi_subdevice *subdevice; | ||
653 | me_board_struct *board; | ||
654 | resource_size_t plx_regbase_tmp; | ||
655 | unsigned long plx_regbase_size_tmp; | ||
656 | resource_size_t me_regbase_tmp; | ||
657 | unsigned long me_regbase_size_tmp; | ||
658 | resource_size_t swap_regbase_tmp; | ||
659 | unsigned long swap_regbase_size_tmp; | ||
660 | resource_size_t regbase_tmp; | ||
661 | int result, error, i; | ||
662 | |||
663 | // Allocate private memory | ||
664 | if (alloc_private(dev, sizeof(me_private_data_struct)) < 0) { | ||
665 | return -ENOMEM; | ||
666 | } | ||
667 | // | ||
668 | // Probe the device to determine what device in the series it is. | ||
669 | // | ||
670 | for (pci_device = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); | ||
671 | pci_device != NULL; | ||
672 | pci_device = | ||
673 | pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_device)) { | ||
674 | if (pci_device->vendor == PCI_VENDOR_ID_MEILHAUS) { | ||
675 | for (i = 0; i < me_board_nbr; i++) { | ||
676 | if (me_boards[i].device_id == | ||
677 | pci_device->device) { | ||
678 | // was a particular bus/slot requested? | ||
679 | if ((it->options[0] != 0) | ||
680 | || (it->options[1] != 0)) { | ||
681 | // are we on the wrong bus/slot? | ||
682 | if (pci_device->bus->number != | ||
683 | it->options[0] | ||
684 | || PCI_SLOT(pci_device-> | ||
685 | devfn) != | ||
686 | it->options[1]) { | ||
687 | continue; | ||
688 | } | ||
689 | } | ||
690 | |||
691 | dev->board_ptr = me_boards + i; | ||
692 | board = (me_board_struct *) dev-> | ||
693 | board_ptr; | ||
694 | dev_private->pci_device = pci_device; | ||
695 | goto found; | ||
696 | } | ||
697 | } | ||
698 | } | ||
699 | } | ||
700 | |||
701 | printk("comedi%d: no supported board found! (req. bus/slot : %d/%d)\n", | ||
702 | dev->minor, it->options[0], it->options[1]); | ||
703 | return -EIO; | ||
704 | |||
705 | found: | ||
706 | |||
707 | printk("comedi%d: found %s at PCI bus %d, slot %d\n", | ||
708 | dev->minor, me_boards[i].name, | ||
709 | pci_device->bus->number, PCI_SLOT(pci_device->devfn)); | ||
710 | |||
711 | // Enable PCI device and request PCI regions | ||
712 | if (comedi_pci_enable(pci_device, ME_DRIVER_NAME) < 0) { | ||
713 | printk("comedi%d: Failed to enable PCI device and request regions\n", dev->minor); | ||
714 | return -EIO; | ||
715 | } | ||
716 | // Set data in device structure | ||
717 | |||
718 | dev->board_name = board->name; | ||
719 | |||
720 | // Read PLX register base address [PCI_BASE_ADDRESS #0]. | ||
721 | |||
722 | plx_regbase_tmp = pci_resource_start(pci_device, 0); | ||
723 | plx_regbase_size_tmp = pci_resource_len(pci_device, 0); | ||
724 | dev_private->plx_regbase = | ||
725 | ioremap(plx_regbase_tmp, plx_regbase_size_tmp); | ||
726 | dev_private->plx_regbase_size = plx_regbase_size_tmp; | ||
727 | if (!dev_private->plx_regbase) { | ||
728 | printk("comedi%d: Failed to remap I/O memory\n", dev->minor); | ||
729 | return -ENOMEM; | ||
730 | } | ||
731 | // Read Swap base address [PCI_BASE_ADDRESS #5]. | ||
732 | |||
733 | swap_regbase_tmp = pci_resource_start(pci_device, 5); | ||
734 | swap_regbase_size_tmp = pci_resource_len(pci_device, 5); | ||
735 | |||
736 | if (!swap_regbase_tmp) { | ||
737 | printk("comedi%d: Swap not present\n", dev->minor); | ||
738 | } | ||
739 | |||
740 | /*----------------------------------------------------- Workaround start ---*/ | ||
741 | if (plx_regbase_tmp & 0x0080) { | ||
742 | printk("comedi%d: PLX-Bug detected\n", dev->minor); | ||
743 | |||
744 | if (swap_regbase_tmp) { | ||
745 | regbase_tmp = plx_regbase_tmp; | ||
746 | plx_regbase_tmp = swap_regbase_tmp; | ||
747 | swap_regbase_tmp = regbase_tmp; | ||
748 | |||
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 | result = pci_write_config_dword(pci_device, | ||
755 | PCI_BASE_ADDRESS_5, swap_regbase_tmp); | ||
756 | if (result != PCIBIOS_SUCCESSFUL) | ||
757 | return -EIO; | ||
758 | } else { | ||
759 | plx_regbase_tmp -= 0x80; | ||
760 | result = pci_write_config_dword(pci_device, | ||
761 | PCI_BASE_ADDRESS_0, plx_regbase_tmp); | ||
762 | if (result != PCIBIOS_SUCCESSFUL) | ||
763 | return -EIO; | ||
764 | } | ||
765 | } | ||
766 | /*----------------------------------------------------- Workaround end -----*/ | ||
767 | |||
768 | // Read Meilhaus register base address [PCI_BASE_ADDRESS #2]. | ||
769 | |||
770 | me_regbase_tmp = pci_resource_start(pci_device, 2); | ||
771 | me_regbase_size_tmp = pci_resource_len(pci_device, 2); | ||
772 | dev_private->me_regbase_size = me_regbase_size_tmp; | ||
773 | dev_private->me_regbase = ioremap(me_regbase_tmp, me_regbase_size_tmp); | ||
774 | if (!dev_private->me_regbase) { | ||
775 | printk("comedi%d: Failed to remap I/O memory\n", dev->minor); | ||
776 | return -ENOMEM; | ||
777 | } | ||
778 | // Download firmware and reset card | ||
779 | if (board->device_id == ME2600_DEVICE_ID) { | ||
780 | unsigned char *aux_data; | ||
781 | int aux_len; | ||
782 | |||
783 | aux_data = comedi_aux_data(it->options, 0); | ||
784 | aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]; | ||
785 | |||
786 | if (!aux_data || aux_len < 1) { | ||
787 | comedi_error(dev, | ||
788 | "You must provide me2600 firmware using the --init-data option of comedi_config"); | ||
789 | return -EINVAL; | ||
790 | } | ||
791 | me2600_xilinx_download(dev, aux_data, aux_len); | ||
792 | } | ||
793 | |||
794 | me_reset(dev); | ||
795 | |||
796 | // device driver capabilities | ||
797 | |||
798 | if ((error = alloc_subdevices(dev, 3)) < 0) | ||
799 | return error; | ||
800 | |||
801 | subdevice = dev->subdevices + 0; | ||
802 | subdevice->type = COMEDI_SUBD_AI; | ||
803 | subdevice->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_CMD_READ; | ||
804 | subdevice->n_chan = board->ai_channel_nbr; | ||
805 | subdevice->maxdata = board->ai_resolution_mask; | ||
806 | subdevice->len_chanlist = board->ai_channel_nbr; | ||
807 | subdevice->range_table = board->ai_range_list; | ||
808 | subdevice->cancel = me_ai_cancel; | ||
809 | subdevice->insn_read = me_ai_insn_read; | ||
810 | subdevice->do_cmdtest = me_ai_do_cmd_test; | ||
811 | subdevice->do_cmd = me_ai_do_cmd; | ||
812 | |||
813 | subdevice = dev->subdevices + 1; | ||
814 | subdevice->type = COMEDI_SUBD_AO; | ||
815 | subdevice->subdev_flags = SDF_WRITEABLE | SDF_COMMON; | ||
816 | subdevice->n_chan = board->ao_channel_nbr; | ||
817 | subdevice->maxdata = board->ao_resolution_mask; | ||
818 | subdevice->len_chanlist = board->ao_channel_nbr; | ||
819 | subdevice->range_table = board->ao_range_list; | ||
820 | subdevice->insn_read = me_ao_insn_read; | ||
821 | subdevice->insn_write = me_ao_insn_write; | ||
822 | |||
823 | subdevice = dev->subdevices + 2; | ||
824 | subdevice->type = COMEDI_SUBD_DIO; | ||
825 | subdevice->subdev_flags = SDF_READABLE | SDF_WRITEABLE; | ||
826 | subdevice->n_chan = board->dio_channel_nbr; | ||
827 | subdevice->maxdata = 1; | ||
828 | subdevice->len_chanlist = board->dio_channel_nbr; | ||
829 | subdevice->range_table = &range_digital; | ||
830 | subdevice->insn_bits = me_dio_insn_bits; | ||
831 | subdevice->insn_config = me_dio_insn_config; | ||
832 | subdevice->io_bits = 0; | ||
833 | |||
834 | printk("comedi%d: " ME_DRIVER_NAME " attached.\n", dev->minor); | ||
835 | return 0; | ||
836 | } | ||
837 | |||
838 | // | ||
839 | // Detach | ||
840 | // | ||
841 | |||
842 | static int me_detach(comedi_device * dev) | ||
843 | { | ||
844 | if (dev_private) { | ||
845 | if (dev_private->me_regbase) { | ||
846 | me_reset(dev); | ||
847 | iounmap(dev_private->me_regbase); | ||
848 | } | ||
849 | if (dev_private->plx_regbase) | ||
850 | iounmap(dev_private->plx_regbase); | ||
851 | if (dev_private->pci_device) { | ||
852 | if (dev_private->plx_regbase_size) { | ||
853 | comedi_pci_disable(dev_private->pci_device); | ||
854 | } | ||
855 | pci_dev_put(dev_private->pci_device); | ||
856 | } | ||
857 | } | ||
858 | return 0; | ||
859 | } | ||