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 | ||
271 | static const int ni_irqpin[] = | |
0a85b6f0 | 272 | { -1, -1, -1, 0, 1, 2, -1, 3, -1, -1, 4, 5, 6, -1, -1, 7 }; |
a8b77430 DS |
273 | |
274 | #define interrupt_pin(a) (ni_irqpin[(a)]) | |
275 | ||
276 | #define IRQ_POLARITY 0 | |
277 | ||
278 | #define NI_E_IRQ_FLAGS 0 | |
279 | ||
3301cc76 | 280 | struct ni_private { |
a8b77430 | 281 | struct pnp_dev *isapnp_dev; |
0a85b6f0 | 282 | NI_PRIVATE_COMMON}; |
3301cc76 | 283 | #define devpriv ((struct ni_private *)dev->private) |
a8b77430 DS |
284 | |
285 | /* How we access registers */ | |
286 | ||
f7cbd7aa | 287 | #define ni_writel(a, b) (outl((a), (b)+dev->iobase)) |
a8b77430 | 288 | #define ni_readl(a) (inl((a)+dev->iobase)) |
f7cbd7aa | 289 | #define ni_writew(a, b) (outw((a), (b)+dev->iobase)) |
a8b77430 | 290 | #define ni_readw(a) (inw((a)+dev->iobase)) |
f7cbd7aa | 291 | #define ni_writeb(a, b) (outb((a), (b)+dev->iobase)) |
a8b77430 DS |
292 | #define ni_readb(a) (inb((a)+dev->iobase)) |
293 | ||
294 | /* How we access windowed registers */ | |
295 | ||
296 | /* We automatically take advantage of STC registers that can be | |
297 | * read/written directly in the I/O space of the board. The | |
298 | * AT-MIO devices map the low 8 STC registers to iobase+addr*2. */ | |
299 | ||
da91b269 | 300 | static void ni_atmio_win_out(struct comedi_device *dev, uint16_t data, int addr) |
a8b77430 DS |
301 | { |
302 | unsigned long flags; | |
303 | ||
5f74ea14 | 304 | spin_lock_irqsave(&devpriv->window_lock, flags); |
a8b77430 DS |
305 | if ((addr) < 8) { |
306 | ni_writew(data, addr * 2); | |
307 | } else { | |
308 | ni_writew(addr, Window_Address); | |
309 | ni_writew(data, Window_Data); | |
310 | } | |
5f74ea14 | 311 | spin_unlock_irqrestore(&devpriv->window_lock, flags); |
a8b77430 DS |
312 | } |
313 | ||
da91b269 | 314 | static uint16_t ni_atmio_win_in(struct comedi_device *dev, int addr) |
a8b77430 DS |
315 | { |
316 | unsigned long flags; | |
317 | uint16_t ret; | |
318 | ||
5f74ea14 | 319 | spin_lock_irqsave(&devpriv->window_lock, flags); |
a8b77430 DS |
320 | if (addr < 8) { |
321 | ret = ni_readw(addr * 2); | |
322 | } else { | |
323 | ni_writew(addr, Window_Address); | |
324 | ret = ni_readw(Window_Data); | |
325 | } | |
5f74ea14 | 326 | spin_unlock_irqrestore(&devpriv->window_lock, flags); |
a8b77430 DS |
327 | |
328 | return ret; | |
329 | } | |
330 | ||
331 | static struct pnp_device_id device_ids[] = { | |
bc2955dd GH |
332 | {.id = "NIC1900", .driver_data = 0}, |
333 | {.id = "NIC2400", .driver_data = 0}, | |
334 | {.id = "NIC2500", .driver_data = 0}, | |
335 | {.id = "NIC2600", .driver_data = 0}, | |
336 | {.id = "NIC2700", .driver_data = 0}, | |
a8b77430 DS |
337 | {.id = ""} |
338 | }; | |
339 | ||
340 | MODULE_DEVICE_TABLE(pnp, device_ids); | |
341 | ||
0a85b6f0 MT |
342 | static int ni_atmio_attach(struct comedi_device *dev, |
343 | struct comedi_devconfig *it); | |
da91b269 | 344 | static int ni_atmio_detach(struct comedi_device *dev); |
139dfbdf | 345 | static struct comedi_driver driver_atmio = { |
68c3dbff BP |
346 | .driver_name = "ni_atmio", |
347 | .module = THIS_MODULE, | |
348 | .attach = ni_atmio_attach, | |
349 | .detach = ni_atmio_detach, | |
a8b77430 DS |
350 | }; |
351 | ||
7114a280 AT |
352 | static int __init driver_atmio_init_module(void) |
353 | { | |
354 | return comedi_driver_register(&driver_atmio); | |
355 | } | |
356 | ||
357 | static void __exit driver_atmio_cleanup_module(void) | |
358 | { | |
359 | comedi_driver_unregister(&driver_atmio); | |
360 | } | |
361 | ||
362 | module_init(driver_atmio_init_module); | |
363 | module_exit(driver_atmio_cleanup_module); | |
a8b77430 DS |
364 | |
365 | #include "ni_mio_common.c" | |
366 | ||
da91b269 | 367 | static int ni_getboardtype(struct comedi_device *dev); |
a8b77430 DS |
368 | |
369 | /* clean up allocated resources */ | |
da91b269 | 370 | static int ni_atmio_detach(struct comedi_device *dev) |
a8b77430 DS |
371 | { |
372 | mio_common_detach(dev); | |
373 | ||
374 | if (dev->iobase) | |
375 | release_region(dev->iobase, NI_SIZE); | |
bc2955dd | 376 | if (dev->irq) |
5f74ea14 | 377 | free_irq(dev->irq, dev); |
bc2955dd | 378 | |
a8b77430 DS |
379 | if (devpriv->isapnp_dev) |
380 | pnp_device_detach(devpriv->isapnp_dev); | |
381 | ||
382 | return 0; | |
383 | } | |
384 | ||
385 | static int ni_isapnp_find_board(struct pnp_dev **dev) | |
386 | { | |
387 | struct pnp_dev *isapnp_dev = NULL; | |
388 | int i; | |
389 | ||
390 | for (i = 0; i < n_ni_boards; i++) { | |
391 | isapnp_dev = pnp_find_dev(NULL, | |
0a85b6f0 MT |
392 | ISAPNP_VENDOR('N', 'I', 'C'), |
393 | ISAPNP_FUNCTION(ni_boards[i]. | |
394 | isapnp_id), NULL); | |
a8b77430 DS |
395 | |
396 | if (isapnp_dev == NULL || isapnp_dev->card == NULL) | |
397 | continue; | |
398 | ||
399 | if (pnp_device_attach(isapnp_dev) < 0) { | |
0a85b6f0 | 400 | printk |
bc2955dd GH |
401 | ("ni_atmio: %s found but already active, skipping.\n", |
402 | ni_boards[i].name); | |
a8b77430 DS |
403 | continue; |
404 | } | |
405 | if (pnp_activate_dev(isapnp_dev) < 0) { | |
406 | pnp_device_detach(isapnp_dev); | |
407 | return -EAGAIN; | |
408 | } | |
409 | if (!pnp_port_valid(isapnp_dev, 0) | |
0a85b6f0 | 410 | || !pnp_irq_valid(isapnp_dev, 0)) { |
a8b77430 DS |
411 | pnp_device_detach(isapnp_dev); |
412 | printk("ni_atmio: pnp invalid port or irq, aborting\n"); | |
413 | return -ENOMEM; | |
414 | } | |
415 | break; | |
416 | } | |
417 | if (i == n_ni_boards) | |
418 | return -ENODEV; | |
419 | *dev = isapnp_dev; | |
420 | return 0; | |
421 | } | |
422 | ||
0a85b6f0 MT |
423 | static int ni_atmio_attach(struct comedi_device *dev, |
424 | struct comedi_devconfig *it) | |
a8b77430 DS |
425 | { |
426 | struct pnp_dev *isapnp_dev; | |
427 | int ret; | |
428 | unsigned long iobase; | |
429 | int board; | |
430 | unsigned int irq; | |
431 | ||
432 | /* allocate private area */ | |
c3744138 BP |
433 | ret = ni_alloc_private(dev); |
434 | if (ret < 0) | |
a8b77430 | 435 | return ret; |
c3744138 | 436 | |
a8b77430 DS |
437 | devpriv->stc_writew = &ni_atmio_win_out; |
438 | devpriv->stc_readw = &ni_atmio_win_in; | |
439 | devpriv->stc_writel = &win_out2; | |
440 | devpriv->stc_readl = &win_in2; | |
441 | ||
442 | iobase = it->options[0]; | |
443 | irq = it->options[1]; | |
444 | isapnp_dev = NULL; | |
445 | if (iobase == 0) { | |
446 | ret = ni_isapnp_find_board(&isapnp_dev); | |
447 | if (ret < 0) | |
448 | return ret; | |
449 | ||
450 | iobase = pnp_port_start(isapnp_dev, 0); | |
451 | irq = pnp_irq(isapnp_dev, 0); | |
452 | devpriv->isapnp_dev = isapnp_dev; | |
453 | } | |
454 | ||
455 | /* reserve our I/O region */ | |
456 | ||
457 | printk("comedi%d: ni_atmio: 0x%04lx", dev->minor, iobase); | |
458 | if (!request_region(iobase, NI_SIZE, "ni_atmio")) { | |
459 | printk(" I/O port conflict\n"); | |
460 | return -EIO; | |
461 | } | |
462 | ||
463 | dev->iobase = iobase; | |
464 | ||
465 | #ifdef DEBUG | |
466 | /* board existence sanity check */ | |
467 | { | |
468 | int i; | |
469 | ||
470 | printk(" board fingerprint:"); | |
471 | for (i = 0; i < 16; i += 2) { | |
472 | printk(" %04x %02x", inw(dev->iobase + i), | |
0a85b6f0 | 473 | inb(dev->iobase + i + 1)); |
a8b77430 DS |
474 | } |
475 | } | |
476 | #endif | |
477 | ||
478 | /* get board type */ | |
479 | ||
480 | board = ni_getboardtype(dev); | |
481 | if (board < 0) | |
482 | return -EIO; | |
483 | ||
484 | dev->board_ptr = ni_boards + board; | |
485 | ||
486 | printk(" %s", boardtype.name); | |
487 | dev->board_name = boardtype.name; | |
488 | ||
489 | /* irq stuff */ | |
490 | ||
491 | if (irq != 0) { | |
492 | if (irq > 15 || ni_irqpin[irq] == -1) { | |
493 | printk(" invalid irq %u\n", irq); | |
494 | return -EINVAL; | |
495 | } | |
496 | printk(" ( irq = %u )", irq); | |
5f74ea14 GKH |
497 | ret = request_irq(irq, ni_E_interrupt, NI_E_IRQ_FLAGS, |
498 | "ni_atmio", dev); | |
c3744138 BP |
499 | |
500 | if (ret < 0) { | |
a8b77430 DS |
501 | printk(" irq not available\n"); |
502 | return -EINVAL; | |
503 | } | |
504 | dev->irq = irq; | |
505 | } | |
506 | ||
507 | /* generic E series stuff in ni_mio_common.c */ | |
508 | ||
c3744138 | 509 | ret = ni_E_init(dev, it); |
bc2955dd | 510 | if (ret < 0) |
a8b77430 | 511 | return ret; |
bc2955dd | 512 | |
a8b77430 DS |
513 | |
514 | return 0; | |
515 | } | |
516 | ||
da91b269 | 517 | static int ni_getboardtype(struct comedi_device *dev) |
a8b77430 DS |
518 | { |
519 | int device_id = ni_read_eeprom(dev, 511); | |
520 | int i; | |
521 | ||
522 | for (i = 0; i < n_ni_boards; i++) { | |
bc2955dd | 523 | if (ni_boards[i].device_id == device_id) |
a8b77430 | 524 | return i; |
bc2955dd | 525 | |
a8b77430 | 526 | } |
bc2955dd | 527 | if (device_id == 255) |
a8b77430 | 528 | printk(" can't find board\n"); |
bc2955dd | 529 | else if (device_id == 0) |
a8b77430 | 530 | printk(" EEPROM read error (?) or device not found\n"); |
bc2955dd | 531 | else |
a8b77430 | 532 | printk(" unknown device ID %d -- contact author\n", device_id); |
bc2955dd | 533 | |
a8b77430 DS |
534 | return -1; |
535 | } |