Commit | Line | Data |
---|---|---|
a8b77430 DS |
1 | /* |
2 | comedi/drivers/ni_atmio.c | |
3 | Hardware driver for NI AT-MIO E series cards | |
4 | ||
5 | COMEDI - Linux Control and Measurement Device Interface | |
6 | Copyright (C) 1997-2001 David A. Schleef <ds@schleef.org> | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program; if not, write to the Free Software | |
20 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
21 | */ | |
22 | /* | |
23 | Driver: ni_atmio | |
24 | Description: National Instruments AT-MIO-E series | |
25 | Author: ds | |
26 | Devices: [National Instruments] AT-MIO-16E-1 (ni_atmio), | |
27 | AT-MIO-16E-2, AT-MIO-16E-10, AT-MIO-16DE-10, AT-MIO-64E-3, | |
28 | AT-MIO-16XE-50, AT-MIO-16XE-10, AT-AI-16XE-10 | |
29 | Status: works | |
30 | Updated: Thu May 1 20:03:02 CDT 2003 | |
31 | ||
32 | The driver has 2.6 kernel isapnp support, and | |
33 | will automatically probe for a supported board if the | |
34 | I/O base is left unspecified with comedi_config. | |
35 | However, many of | |
36 | the isapnp id numbers are unknown. If your board is not | |
37 | recognized, please send the output of 'cat /proc/isapnp' | |
38 | (you may need to modprobe the isa-pnp module for | |
39 | /proc/isapnp to exist) so the | |
40 | id numbers for your board can be added to the driver. | |
41 | ||
42 | Otherwise, you can use the isapnptools package to configure | |
43 | your board. Use isapnp to | |
44 | configure the I/O base and IRQ for the board, and then pass | |
45 | the same values as | |
46 | parameters in comedi_config. A sample isapnp.conf file is included | |
47 | in the etc/ directory of Comedilib. | |
48 | ||
49 | Comedilib includes a utility to autocalibrate these boards. The | |
50 | boards seem to boot into a state where the all calibration DACs | |
51 | are at one extreme of their range, thus the default calibration | |
52 | is terrible. Calibration at boot is strongly encouraged. | |
53 | ||
54 | To use the extended digital I/O on some of the boards, enable the | |
55 | 8255 driver when configuring the Comedi source tree. | |
56 | ||
57 | External triggering is supported for some events. The channel index | |
58 | (scan_begin_arg, etc.) maps to PFI0 - PFI9. | |
59 | ||
60 | Some of the more esoteric triggering possibilities of these boards | |
61 | are not supported. | |
62 | */ | |
63 | /* | |
64 | The real guts of the driver is in ni_mio_common.c, which is included | |
65 | both here and in ni_pcimio.c | |
66 | ||
67 | Interrupt support added by Truxton Fulton <trux@truxton.com> | |
68 | ||
69 | References for specifications: | |
70 | ||
71 | 340747b.pdf Register Level Programmer Manual (obsolete) | |
72 | 340747c.pdf Register Level Programmer Manual (new) | |
73 | DAQ-STC reference manual | |
74 | ||
75 | Other possibly relevant info: | |
76 | ||
77 | 320517c.pdf User manual (obsolete) | |
78 | 320517f.pdf User manual (new) | |
79 | 320889a.pdf delete | |
80 | 320906c.pdf maximum signal ratings | |
81 | 321066a.pdf about 16x | |
82 | 321791a.pdf discontinuation of at-mio-16e-10 rev. c | |
83 | 321808a.pdf about at-mio-16e-10 rev P | |
84 | 321837a.pdf discontinuation of at-mio-16de-10 rev d | |
85 | 321838a.pdf about at-mio-16de-10 rev N | |
86 | ||
87 | ISSUES: | |
88 | ||
89 | need to deal with external reference for DAC, and other DAC | |
90 | properties in board properties | |
91 | ||
92 | deal with at-mio-16de-10 revision D to N changes, etc. | |
93 | ||
94 | */ | |
95 | ||
25436dc9 | 96 | #include <linux/interrupt.h> |
a8b77430 DS |
97 | #include "../comedidev.h" |
98 | ||
99 | #include <linux/delay.h> | |
100 | #include <linux/isapnp.h> | |
101 | ||
102 | #include "ni_stc.h" | |
103 | #include "8255.h" | |
104 | ||
105 | #undef DEBUG | |
106 | ||
107 | #define ATMIO 1 | |
108 | #undef PCIMIO | |
109 | ||
110 | /* | |
111 | * AT specific setup | |
112 | */ | |
113 | ||
114 | #define NI_SIZE 0x20 | |
115 | ||
116 | #define MAX_N_CALDACS 32 | |
117 | ||
8ab41df0 | 118 | static const struct ni_board_struct ni_boards[] = { |
68c3dbff | 119 | {.device_id = 44, |
0a85b6f0 MT |
120 | .isapnp_id = 0x0000, /* XXX unknown */ |
121 | .name = "at-mio-16e-1", | |
122 | .n_adchan = 16, | |
123 | .adbits = 12, | |
124 | .ai_fifo_depth = 8192, | |
125 | .alwaysdither = 0, | |
126 | .gainlkup = ai_gain_16, | |
127 | .ai_speed = 800, | |
128 | .n_aochan = 2, | |
129 | .aobits = 12, | |
130 | .ao_fifo_depth = 2048, | |
131 | .ao_range_table = &range_ni_E_ao_ext, | |
132 | .ao_unipolar = 1, | |
133 | .ao_speed = 1000, | |
134 | .has_8255 = 0, | |
135 | .num_p0_dio_channels = 8, | |
136 | .caldac = {mb88341}, | |
137 | }, | |
68c3dbff | 138 | {.device_id = 25, |
0a85b6f0 MT |
139 | .isapnp_id = 0x1900, |
140 | .name = "at-mio-16e-2", | |
141 | .n_adchan = 16, | |
142 | .adbits = 12, | |
143 | .ai_fifo_depth = 2048, | |
144 | .alwaysdither = 0, | |
145 | .gainlkup = ai_gain_16, | |
146 | .ai_speed = 2000, | |
147 | .n_aochan = 2, | |
148 | .aobits = 12, | |
149 | .ao_fifo_depth = 2048, | |
150 | .ao_range_table = &range_ni_E_ao_ext, | |
151 | .ao_unipolar = 1, | |
152 | .ao_speed = 1000, | |
153 | .has_8255 = 0, | |
154 | .num_p0_dio_channels = 8, | |
155 | .caldac = {mb88341}, | |
156 | }, | |
68c3dbff | 157 | {.device_id = 36, |
0a85b6f0 MT |
158 | .isapnp_id = 0x2400, |
159 | .name = "at-mio-16e-10", | |
160 | .n_adchan = 16, | |
161 | .adbits = 12, | |
162 | .ai_fifo_depth = 512, | |
163 | .alwaysdither = 0, | |
164 | .gainlkup = ai_gain_16, | |
165 | .ai_speed = 10000, | |
166 | .n_aochan = 2, | |
167 | .aobits = 12, | |
168 | .ao_fifo_depth = 0, | |
169 | .ao_range_table = &range_ni_E_ao_ext, | |
170 | .ao_unipolar = 1, | |
171 | .ao_speed = 10000, | |
172 | .num_p0_dio_channels = 8, | |
173 | .caldac = {ad8804_debug}, | |
174 | .has_8255 = 0, | |
175 | }, | |
68c3dbff | 176 | {.device_id = 37, |
0a85b6f0 MT |
177 | .isapnp_id = 0x2500, |
178 | .name = "at-mio-16de-10", | |
179 | .n_adchan = 16, | |
180 | .adbits = 12, | |
181 | .ai_fifo_depth = 512, | |
182 | .alwaysdither = 0, | |
183 | .gainlkup = ai_gain_16, | |
184 | .ai_speed = 10000, | |
185 | .n_aochan = 2, | |
186 | .aobits = 12, | |
187 | .ao_fifo_depth = 0, | |
188 | .ao_range_table = &range_ni_E_ao_ext, | |
189 | .ao_unipolar = 1, | |
190 | .ao_speed = 10000, | |
191 | .num_p0_dio_channels = 8, | |
192 | .caldac = {ad8804_debug}, | |
193 | .has_8255 = 1, | |
194 | }, | |
68c3dbff | 195 | {.device_id = 38, |
0a85b6f0 MT |
196 | .isapnp_id = 0x2600, |
197 | .name = "at-mio-64e-3", | |
198 | .n_adchan = 64, | |
199 | .adbits = 12, | |
200 | .ai_fifo_depth = 2048, | |
201 | .alwaysdither = 0, | |
202 | .gainlkup = ai_gain_16, | |
203 | .ai_speed = 2000, | |
204 | .n_aochan = 2, | |
205 | .aobits = 12, | |
206 | .ao_fifo_depth = 2048, | |
207 | .ao_range_table = &range_ni_E_ao_ext, | |
208 | .ao_unipolar = 1, | |
209 | .ao_speed = 1000, | |
210 | .has_8255 = 0, | |
211 | .num_p0_dio_channels = 8, | |
212 | .caldac = {ad8804_debug}, | |
213 | }, | |
68c3dbff | 214 | {.device_id = 39, |
0a85b6f0 MT |
215 | .isapnp_id = 0x2700, |
216 | .name = "at-mio-16xe-50", | |
217 | .n_adchan = 16, | |
218 | .adbits = 16, | |
219 | .ai_fifo_depth = 512, | |
220 | .alwaysdither = 1, | |
221 | .gainlkup = ai_gain_8, | |
222 | .ai_speed = 50000, | |
223 | .n_aochan = 2, | |
224 | .aobits = 12, | |
225 | .ao_fifo_depth = 0, | |
226 | .ao_range_table = &range_bipolar10, | |
227 | .ao_unipolar = 0, | |
228 | .ao_speed = 50000, | |
229 | .num_p0_dio_channels = 8, | |
230 | .caldac = {dac8800, dac8043}, | |
231 | .has_8255 = 0, | |
232 | }, | |
68c3dbff | 233 | {.device_id = 50, |
0a85b6f0 MT |
234 | .isapnp_id = 0x0000, /* XXX unknown */ |
235 | .name = "at-mio-16xe-10", | |
236 | .n_adchan = 16, | |
237 | .adbits = 16, | |
238 | .ai_fifo_depth = 512, | |
239 | .alwaysdither = 1, | |
240 | .gainlkup = ai_gain_14, | |
241 | .ai_speed = 10000, | |
242 | .n_aochan = 2, | |
243 | .aobits = 16, | |
244 | .ao_fifo_depth = 2048, | |
245 | .ao_range_table = &range_ni_E_ao_ext, | |
246 | .ao_unipolar = 1, | |
247 | .ao_speed = 1000, | |
248 | .num_p0_dio_channels = 8, | |
249 | .caldac = {dac8800, dac8043, ad8522}, | |
250 | .has_8255 = 0, | |
251 | }, | |
68c3dbff | 252 | {.device_id = 51, |
0a85b6f0 MT |
253 | .isapnp_id = 0x0000, /* XXX unknown */ |
254 | .name = "at-ai-16xe-10", | |
255 | .n_adchan = 16, | |
256 | .adbits = 16, | |
257 | .ai_fifo_depth = 512, | |
258 | .alwaysdither = 1, /* unknown */ | |
259 | .gainlkup = ai_gain_14, | |
260 | .ai_speed = 10000, | |
261 | .n_aochan = 0, | |
262 | .aobits = 0, | |
263 | .ao_fifo_depth = 0, | |
264 | .ao_unipolar = 0, | |
265 | .num_p0_dio_channels = 8, | |
266 | .caldac = {dac8800, dac8043, ad8522}, | |
267 | .has_8255 = 0, | |
268 | } | |
a8b77430 DS |
269 | }; |
270 | ||
963ff774 JB |
271 | static const int ni_irqpin[] = { |
272 | -1, -1, -1, 0, 1, 2, -1, 3, -1, -1, 4, 5, 6, -1, -1, 7 | |
273 | }; | |
a8b77430 DS |
274 | |
275 | #define interrupt_pin(a) (ni_irqpin[(a)]) | |
276 | ||
277 | #define IRQ_POLARITY 0 | |
278 | ||
279 | #define NI_E_IRQ_FLAGS 0 | |
280 | ||
3301cc76 | 281 | struct ni_private { |
a8b77430 | 282 | struct pnp_dev *isapnp_dev; |
963ff774 JB |
283 | NI_PRIVATE_COMMON |
284 | ||
285 | }; | |
286 | ||
3301cc76 | 287 | #define devpriv ((struct ni_private *)dev->private) |
a8b77430 DS |
288 | |
289 | /* How we access registers */ | |
290 | ||
f7cbd7aa | 291 | #define ni_writel(a, b) (outl((a), (b)+dev->iobase)) |
a8b77430 | 292 | #define ni_readl(a) (inl((a)+dev->iobase)) |
f7cbd7aa | 293 | #define ni_writew(a, b) (outw((a), (b)+dev->iobase)) |
a8b77430 | 294 | #define ni_readw(a) (inw((a)+dev->iobase)) |
f7cbd7aa | 295 | #define ni_writeb(a, b) (outb((a), (b)+dev->iobase)) |
a8b77430 DS |
296 | #define ni_readb(a) (inb((a)+dev->iobase)) |
297 | ||
298 | /* How we access windowed registers */ | |
299 | ||
300 | /* We automatically take advantage of STC registers that can be | |
301 | * read/written directly in the I/O space of the board. The | |
302 | * AT-MIO devices map the low 8 STC registers to iobase+addr*2. */ | |
303 | ||
da91b269 | 304 | static void ni_atmio_win_out(struct comedi_device *dev, uint16_t data, int addr) |
a8b77430 DS |
305 | { |
306 | unsigned long flags; | |
307 | ||
5f74ea14 | 308 | spin_lock_irqsave(&devpriv->window_lock, flags); |
a8b77430 DS |
309 | if ((addr) < 8) { |
310 | ni_writew(data, addr * 2); | |
311 | } else { | |
312 | ni_writew(addr, Window_Address); | |
313 | ni_writew(data, Window_Data); | |
314 | } | |
5f74ea14 | 315 | spin_unlock_irqrestore(&devpriv->window_lock, flags); |
a8b77430 DS |
316 | } |
317 | ||
da91b269 | 318 | static uint16_t ni_atmio_win_in(struct comedi_device *dev, int addr) |
a8b77430 DS |
319 | { |
320 | unsigned long flags; | |
321 | uint16_t ret; | |
322 | ||
5f74ea14 | 323 | spin_lock_irqsave(&devpriv->window_lock, flags); |
a8b77430 DS |
324 | if (addr < 8) { |
325 | ret = ni_readw(addr * 2); | |
326 | } else { | |
327 | ni_writew(addr, Window_Address); | |
328 | ret = ni_readw(Window_Data); | |
329 | } | |
5f74ea14 | 330 | spin_unlock_irqrestore(&devpriv->window_lock, flags); |
a8b77430 DS |
331 | |
332 | return ret; | |
333 | } | |
334 | ||
335 | static struct pnp_device_id device_ids[] = { | |
bc2955dd GH |
336 | {.id = "NIC1900", .driver_data = 0}, |
337 | {.id = "NIC2400", .driver_data = 0}, | |
338 | {.id = "NIC2500", .driver_data = 0}, | |
339 | {.id = "NIC2600", .driver_data = 0}, | |
340 | {.id = "NIC2700", .driver_data = 0}, | |
a8b77430 DS |
341 | {.id = ""} |
342 | }; | |
343 | ||
344 | MODULE_DEVICE_TABLE(pnp, device_ids); | |
345 | ||
a8b77430 DS |
346 | #include "ni_mio_common.c" |
347 | ||
a8b77430 DS |
348 | static int ni_isapnp_find_board(struct pnp_dev **dev) |
349 | { | |
350 | struct pnp_dev *isapnp_dev = NULL; | |
351 | int i; | |
352 | ||
353 | for (i = 0; i < n_ni_boards; i++) { | |
354 | isapnp_dev = pnp_find_dev(NULL, | |
0a85b6f0 MT |
355 | ISAPNP_VENDOR('N', 'I', 'C'), |
356 | ISAPNP_FUNCTION(ni_boards[i]. | |
357 | isapnp_id), NULL); | |
a8b77430 DS |
358 | |
359 | if (isapnp_dev == NULL || isapnp_dev->card == NULL) | |
360 | continue; | |
361 | ||
362 | if (pnp_device_attach(isapnp_dev) < 0) { | |
0a85b6f0 | 363 | printk |
bc2955dd GH |
364 | ("ni_atmio: %s found but already active, skipping.\n", |
365 | ni_boards[i].name); | |
a8b77430 DS |
366 | continue; |
367 | } | |
368 | if (pnp_activate_dev(isapnp_dev) < 0) { | |
369 | pnp_device_detach(isapnp_dev); | |
370 | return -EAGAIN; | |
371 | } | |
372 | if (!pnp_port_valid(isapnp_dev, 0) | |
0a85b6f0 | 373 | || !pnp_irq_valid(isapnp_dev, 0)) { |
a8b77430 DS |
374 | pnp_device_detach(isapnp_dev); |
375 | printk("ni_atmio: pnp invalid port or irq, aborting\n"); | |
376 | return -ENOMEM; | |
377 | } | |
378 | break; | |
379 | } | |
380 | if (i == n_ni_boards) | |
381 | return -ENODEV; | |
382 | *dev = isapnp_dev; | |
383 | return 0; | |
384 | } | |
385 | ||
5aac8294 HS |
386 | static int ni_getboardtype(struct comedi_device *dev) |
387 | { | |
388 | int device_id = ni_read_eeprom(dev, 511); | |
389 | int i; | |
390 | ||
391 | for (i = 0; i < n_ni_boards; i++) { | |
392 | if (ni_boards[i].device_id == device_id) | |
393 | return i; | |
394 | ||
395 | } | |
396 | if (device_id == 255) | |
397 | printk(" can't find board\n"); | |
398 | else if (device_id == 0) | |
399 | printk(" EEPROM read error (?) or device not found\n"); | |
400 | else | |
401 | printk(" unknown device ID %d -- contact author\n", device_id); | |
402 | ||
403 | return -1; | |
404 | } | |
405 | ||
0a85b6f0 MT |
406 | static int ni_atmio_attach(struct comedi_device *dev, |
407 | struct comedi_devconfig *it) | |
a8b77430 DS |
408 | { |
409 | struct pnp_dev *isapnp_dev; | |
410 | int ret; | |
411 | unsigned long iobase; | |
412 | int board; | |
413 | unsigned int irq; | |
414 | ||
415 | /* allocate private area */ | |
c3744138 BP |
416 | ret = ni_alloc_private(dev); |
417 | if (ret < 0) | |
a8b77430 | 418 | return ret; |
c3744138 | 419 | |
a8b77430 DS |
420 | devpriv->stc_writew = &ni_atmio_win_out; |
421 | devpriv->stc_readw = &ni_atmio_win_in; | |
422 | devpriv->stc_writel = &win_out2; | |
423 | devpriv->stc_readl = &win_in2; | |
424 | ||
425 | iobase = it->options[0]; | |
426 | irq = it->options[1]; | |
427 | isapnp_dev = NULL; | |
428 | if (iobase == 0) { | |
429 | ret = ni_isapnp_find_board(&isapnp_dev); | |
430 | if (ret < 0) | |
431 | return ret; | |
432 | ||
433 | iobase = pnp_port_start(isapnp_dev, 0); | |
434 | irq = pnp_irq(isapnp_dev, 0); | |
435 | devpriv->isapnp_dev = isapnp_dev; | |
436 | } | |
437 | ||
438 | /* reserve our I/O region */ | |
439 | ||
440 | printk("comedi%d: ni_atmio: 0x%04lx", dev->minor, iobase); | |
441 | if (!request_region(iobase, NI_SIZE, "ni_atmio")) { | |
442 | printk(" I/O port conflict\n"); | |
443 | return -EIO; | |
444 | } | |
445 | ||
446 | dev->iobase = iobase; | |
447 | ||
448 | #ifdef DEBUG | |
449 | /* board existence sanity check */ | |
450 | { | |
451 | int i; | |
452 | ||
453 | printk(" board fingerprint:"); | |
454 | for (i = 0; i < 16; i += 2) { | |
455 | printk(" %04x %02x", inw(dev->iobase + i), | |
0a85b6f0 | 456 | inb(dev->iobase + i + 1)); |
a8b77430 DS |
457 | } |
458 | } | |
459 | #endif | |
460 | ||
461 | /* get board type */ | |
462 | ||
463 | board = ni_getboardtype(dev); | |
464 | if (board < 0) | |
465 | return -EIO; | |
466 | ||
467 | dev->board_ptr = ni_boards + board; | |
468 | ||
469 | printk(" %s", boardtype.name); | |
470 | dev->board_name = boardtype.name; | |
471 | ||
472 | /* irq stuff */ | |
473 | ||
474 | if (irq != 0) { | |
475 | if (irq > 15 || ni_irqpin[irq] == -1) { | |
476 | printk(" invalid irq %u\n", irq); | |
477 | return -EINVAL; | |
478 | } | |
479 | printk(" ( irq = %u )", irq); | |
5f74ea14 GKH |
480 | ret = request_irq(irq, ni_E_interrupt, NI_E_IRQ_FLAGS, |
481 | "ni_atmio", dev); | |
c3744138 BP |
482 | |
483 | if (ret < 0) { | |
a8b77430 DS |
484 | printk(" irq not available\n"); |
485 | return -EINVAL; | |
486 | } | |
487 | dev->irq = irq; | |
488 | } | |
489 | ||
490 | /* generic E series stuff in ni_mio_common.c */ | |
491 | ||
c3744138 | 492 | ret = ni_E_init(dev, it); |
bc2955dd | 493 | if (ret < 0) |
a8b77430 | 494 | return ret; |
bc2955dd | 495 | |
a8b77430 DS |
496 | |
497 | return 0; | |
498 | } | |
499 | ||
484ecc95 | 500 | static void ni_atmio_detach(struct comedi_device *dev) |
a8b77430 | 501 | { |
5aac8294 | 502 | mio_common_detach(dev); |
5aac8294 HS |
503 | if (dev->iobase) |
504 | release_region(dev->iobase, NI_SIZE); | |
505 | if (dev->irq) | |
506 | free_irq(dev->irq, dev); | |
5aac8294 HS |
507 | if (devpriv->isapnp_dev) |
508 | pnp_device_detach(devpriv->isapnp_dev); | |
a8b77430 | 509 | } |
5aac8294 HS |
510 | |
511 | static struct comedi_driver ni_atmio_driver = { | |
512 | .driver_name = "ni_atmio", | |
513 | .module = THIS_MODULE, | |
514 | .attach = ni_atmio_attach, | |
515 | .detach = ni_atmio_detach, | |
516 | }; | |
517 | module_comedi_driver(ni_atmio_driver); |