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. | |
a8b77430 DS |
17 | */ |
18 | /* | |
19 | Driver: ni_atmio | |
20 | Description: National Instruments AT-MIO-E series | |
21 | Author: ds | |
22 | Devices: [National Instruments] AT-MIO-16E-1 (ni_atmio), | |
23 | AT-MIO-16E-2, AT-MIO-16E-10, AT-MIO-16DE-10, AT-MIO-64E-3, | |
24 | AT-MIO-16XE-50, AT-MIO-16XE-10, AT-AI-16XE-10 | |
25 | Status: works | |
26 | Updated: Thu May 1 20:03:02 CDT 2003 | |
27 | ||
28 | The driver has 2.6 kernel isapnp support, and | |
29 | will automatically probe for a supported board if the | |
30 | I/O base is left unspecified with comedi_config. | |
31 | However, many of | |
32 | the isapnp id numbers are unknown. If your board is not | |
33 | recognized, please send the output of 'cat /proc/isapnp' | |
34 | (you may need to modprobe the isa-pnp module for | |
35 | /proc/isapnp to exist) so the | |
36 | id numbers for your board can be added to the driver. | |
37 | ||
38 | Otherwise, you can use the isapnptools package to configure | |
39 | your board. Use isapnp to | |
40 | configure the I/O base and IRQ for the board, and then pass | |
41 | the same values as | |
42 | parameters in comedi_config. A sample isapnp.conf file is included | |
43 | in the etc/ directory of Comedilib. | |
44 | ||
45 | Comedilib includes a utility to autocalibrate these boards. The | |
46 | boards seem to boot into a state where the all calibration DACs | |
47 | are at one extreme of their range, thus the default calibration | |
48 | is terrible. Calibration at boot is strongly encouraged. | |
49 | ||
50 | To use the extended digital I/O on some of the boards, enable the | |
51 | 8255 driver when configuring the Comedi source tree. | |
52 | ||
53 | External triggering is supported for some events. The channel index | |
54 | (scan_begin_arg, etc.) maps to PFI0 - PFI9. | |
55 | ||
56 | Some of the more esoteric triggering possibilities of these boards | |
57 | are not supported. | |
58 | */ | |
59 | /* | |
60 | The real guts of the driver is in ni_mio_common.c, which is included | |
61 | both here and in ni_pcimio.c | |
62 | ||
63 | Interrupt support added by Truxton Fulton <trux@truxton.com> | |
64 | ||
65 | References for specifications: | |
66 | ||
67 | 340747b.pdf Register Level Programmer Manual (obsolete) | |
68 | 340747c.pdf Register Level Programmer Manual (new) | |
69 | DAQ-STC reference manual | |
70 | ||
71 | Other possibly relevant info: | |
72 | ||
73 | 320517c.pdf User manual (obsolete) | |
74 | 320517f.pdf User manual (new) | |
75 | 320889a.pdf delete | |
76 | 320906c.pdf maximum signal ratings | |
77 | 321066a.pdf about 16x | |
78 | 321791a.pdf discontinuation of at-mio-16e-10 rev. c | |
79 | 321808a.pdf about at-mio-16e-10 rev P | |
80 | 321837a.pdf discontinuation of at-mio-16de-10 rev d | |
81 | 321838a.pdf about at-mio-16de-10 rev N | |
82 | ||
83 | ISSUES: | |
84 | ||
85 | need to deal with external reference for DAC, and other DAC | |
86 | properties in board properties | |
87 | ||
88 | deal with at-mio-16de-10 revision D to N changes, etc. | |
89 | ||
90 | */ | |
91 | ||
ce157f80 | 92 | #include <linux/module.h> |
25436dc9 | 93 | #include <linux/interrupt.h> |
a8b77430 DS |
94 | #include "../comedidev.h" |
95 | ||
a8b77430 DS |
96 | #include <linux/isapnp.h> |
97 | ||
98 | #include "ni_stc.h" | |
99 | #include "8255.h" | |
100 | ||
a8b77430 DS |
101 | /* |
102 | * AT specific setup | |
103 | */ | |
104 | ||
8ab41df0 | 105 | static const struct ni_board_struct ni_boards[] = { |
25294851 | 106 | { |
b674f9df | 107 | .name = "at-mio-16e-1", |
25294851 HS |
108 | .device_id = 44, |
109 | .isapnp_id = 0x0000, /* XXX unknown */ | |
25294851 | 110 | .n_adchan = 16, |
db2255f5 | 111 | .ai_maxdata = 0x0fff, |
25294851 HS |
112 | .ai_fifo_depth = 8192, |
113 | .gainlkup = ai_gain_16, | |
114 | .ai_speed = 800, | |
115 | .n_aochan = 2, | |
c5f26499 | 116 | .ao_maxdata = 0x0fff, |
25294851 HS |
117 | .ao_fifo_depth = 2048, |
118 | .ao_range_table = &range_ni_E_ao_ext, | |
25294851 | 119 | .ao_speed = 1000, |
25294851 HS |
120 | .caldac = { mb88341 }, |
121 | }, { | |
b674f9df | 122 | .name = "at-mio-16e-2", |
25294851 HS |
123 | .device_id = 25, |
124 | .isapnp_id = 0x1900, | |
25294851 | 125 | .n_adchan = 16, |
db2255f5 | 126 | .ai_maxdata = 0x0fff, |
25294851 HS |
127 | .ai_fifo_depth = 2048, |
128 | .gainlkup = ai_gain_16, | |
129 | .ai_speed = 2000, | |
130 | .n_aochan = 2, | |
c5f26499 | 131 | .ao_maxdata = 0x0fff, |
25294851 HS |
132 | .ao_fifo_depth = 2048, |
133 | .ao_range_table = &range_ni_E_ao_ext, | |
25294851 | 134 | .ao_speed = 1000, |
25294851 HS |
135 | .caldac = { mb88341 }, |
136 | }, { | |
b674f9df | 137 | .name = "at-mio-16e-10", |
25294851 HS |
138 | .device_id = 36, |
139 | .isapnp_id = 0x2400, | |
25294851 | 140 | .n_adchan = 16, |
db2255f5 | 141 | .ai_maxdata = 0x0fff, |
25294851 HS |
142 | .ai_fifo_depth = 512, |
143 | .gainlkup = ai_gain_16, | |
144 | .ai_speed = 10000, | |
145 | .n_aochan = 2, | |
c5f26499 | 146 | .ao_maxdata = 0x0fff, |
25294851 | 147 | .ao_range_table = &range_ni_E_ao_ext, |
25294851 | 148 | .ao_speed = 10000, |
25294851 HS |
149 | .caldac = { ad8804_debug }, |
150 | }, { | |
b674f9df | 151 | .name = "at-mio-16de-10", |
25294851 HS |
152 | .device_id = 37, |
153 | .isapnp_id = 0x2500, | |
25294851 | 154 | .n_adchan = 16, |
db2255f5 | 155 | .ai_maxdata = 0x0fff, |
25294851 HS |
156 | .ai_fifo_depth = 512, |
157 | .gainlkup = ai_gain_16, | |
158 | .ai_speed = 10000, | |
159 | .n_aochan = 2, | |
c5f26499 | 160 | .ao_maxdata = 0x0fff, |
25294851 | 161 | .ao_range_table = &range_ni_E_ao_ext, |
25294851 | 162 | .ao_speed = 10000, |
25294851 HS |
163 | .caldac = { ad8804_debug }, |
164 | .has_8255 = 1, | |
165 | }, { | |
b674f9df | 166 | .name = "at-mio-64e-3", |
25294851 HS |
167 | .device_id = 38, |
168 | .isapnp_id = 0x2600, | |
25294851 | 169 | .n_adchan = 64, |
db2255f5 | 170 | .ai_maxdata = 0x0fff, |
25294851 HS |
171 | .ai_fifo_depth = 2048, |
172 | .gainlkup = ai_gain_16, | |
173 | .ai_speed = 2000, | |
174 | .n_aochan = 2, | |
c5f26499 | 175 | .ao_maxdata = 0x0fff, |
25294851 HS |
176 | .ao_fifo_depth = 2048, |
177 | .ao_range_table = &range_ni_E_ao_ext, | |
25294851 | 178 | .ao_speed = 1000, |
25294851 HS |
179 | .caldac = { ad8804_debug }, |
180 | }, { | |
b674f9df | 181 | .name = "at-mio-16xe-50", |
25294851 HS |
182 | .device_id = 39, |
183 | .isapnp_id = 0x2700, | |
25294851 | 184 | .n_adchan = 16, |
db2255f5 | 185 | .ai_maxdata = 0xffff, |
25294851 HS |
186 | .ai_fifo_depth = 512, |
187 | .alwaysdither = 1, | |
188 | .gainlkup = ai_gain_8, | |
189 | .ai_speed = 50000, | |
190 | .n_aochan = 2, | |
c5f26499 | 191 | .ao_maxdata = 0x0fff, |
25294851 HS |
192 | .ao_range_table = &range_bipolar10, |
193 | .ao_speed = 50000, | |
25294851 HS |
194 | .caldac = { dac8800, dac8043 }, |
195 | }, { | |
b674f9df | 196 | .name = "at-mio-16xe-10", |
25294851 HS |
197 | .device_id = 50, |
198 | .isapnp_id = 0x0000, /* XXX unknown */ | |
25294851 | 199 | .n_adchan = 16, |
db2255f5 | 200 | .ai_maxdata = 0xffff, |
25294851 HS |
201 | .ai_fifo_depth = 512, |
202 | .alwaysdither = 1, | |
203 | .gainlkup = ai_gain_14, | |
204 | .ai_speed = 10000, | |
205 | .n_aochan = 2, | |
c5f26499 | 206 | .ao_maxdata = 0xffff, |
25294851 HS |
207 | .ao_fifo_depth = 2048, |
208 | .ao_range_table = &range_ni_E_ao_ext, | |
25294851 | 209 | .ao_speed = 1000, |
25294851 HS |
210 | .caldac = { dac8800, dac8043, ad8522 }, |
211 | }, { | |
b674f9df | 212 | .name = "at-ai-16xe-10", |
25294851 HS |
213 | .device_id = 51, |
214 | .isapnp_id = 0x0000, /* XXX unknown */ | |
25294851 | 215 | .n_adchan = 16, |
db2255f5 | 216 | .ai_maxdata = 0xffff, |
25294851 HS |
217 | .ai_fifo_depth = 512, |
218 | .alwaysdither = 1, /* unknown */ | |
219 | .gainlkup = ai_gain_14, | |
220 | .ai_speed = 10000, | |
25294851 HS |
221 | .caldac = { dac8800, dac8043, ad8522 }, |
222 | }, | |
a8b77430 DS |
223 | }; |
224 | ||
963ff774 JB |
225 | static const int ni_irqpin[] = { |
226 | -1, -1, -1, 0, 1, 2, -1, 3, -1, -1, 4, 5, 6, -1, -1, 7 | |
227 | }; | |
a8b77430 | 228 | |
ac63baf5 | 229 | #include "ni_mio_common.c" |
a8b77430 | 230 | |
a8b77430 | 231 | static struct pnp_device_id device_ids[] = { |
bc2955dd GH |
232 | {.id = "NIC1900", .driver_data = 0}, |
233 | {.id = "NIC2400", .driver_data = 0}, | |
234 | {.id = "NIC2500", .driver_data = 0}, | |
235 | {.id = "NIC2600", .driver_data = 0}, | |
236 | {.id = "NIC2700", .driver_data = 0}, | |
a8b77430 DS |
237 | {.id = ""} |
238 | }; | |
239 | ||
240 | MODULE_DEVICE_TABLE(pnp, device_ids); | |
241 | ||
a8b77430 DS |
242 | static int ni_isapnp_find_board(struct pnp_dev **dev) |
243 | { | |
244 | struct pnp_dev *isapnp_dev = NULL; | |
245 | int i; | |
246 | ||
ca4d4aa6 | 247 | for (i = 0; i < ARRAY_SIZE(ni_boards); i++) { |
a8b77430 | 248 | isapnp_dev = pnp_find_dev(NULL, |
0a85b6f0 MT |
249 | ISAPNP_VENDOR('N', 'I', 'C'), |
250 | ISAPNP_FUNCTION(ni_boards[i]. | |
251 | isapnp_id), NULL); | |
a8b77430 | 252 | |
77ba71f6 | 253 | if (!isapnp_dev || !isapnp_dev->card) |
a8b77430 DS |
254 | continue; |
255 | ||
ee68d168 | 256 | if (pnp_device_attach(isapnp_dev) < 0) |
a8b77430 | 257 | continue; |
ee68d168 | 258 | |
a8b77430 DS |
259 | if (pnp_activate_dev(isapnp_dev) < 0) { |
260 | pnp_device_detach(isapnp_dev); | |
261 | return -EAGAIN; | |
262 | } | |
ee68d168 HS |
263 | |
264 | if (!pnp_port_valid(isapnp_dev, 0) || | |
265 | !pnp_irq_valid(isapnp_dev, 0)) { | |
a8b77430 | 266 | pnp_device_detach(isapnp_dev); |
a8b77430 DS |
267 | return -ENOMEM; |
268 | } | |
269 | break; | |
270 | } | |
ca4d4aa6 | 271 | if (i == ARRAY_SIZE(ni_boards)) |
a8b77430 DS |
272 | return -ENODEV; |
273 | *dev = isapnp_dev; | |
274 | return 0; | |
275 | } | |
276 | ||
5aac8294 HS |
277 | static int ni_getboardtype(struct comedi_device *dev) |
278 | { | |
279 | int device_id = ni_read_eeprom(dev, 511); | |
280 | int i; | |
281 | ||
ca4d4aa6 | 282 | for (i = 0; i < ARRAY_SIZE(ni_boards); i++) { |
5aac8294 HS |
283 | if (ni_boards[i].device_id == device_id) |
284 | return i; | |
5aac8294 HS |
285 | } |
286 | if (device_id == 255) | |
b252ebfc | 287 | dev_err(dev->class_dev, "can't find board\n"); |
5aac8294 | 288 | else if (device_id == 0) |
b252ebfc HS |
289 | dev_err(dev->class_dev, |
290 | "EEPROM read error (?) or device not found\n"); | |
5aac8294 | 291 | else |
b252ebfc HS |
292 | dev_err(dev->class_dev, |
293 | "unknown device ID %d -- contact author\n", device_id); | |
5aac8294 HS |
294 | |
295 | return -1; | |
296 | } | |
297 | ||
0a85b6f0 MT |
298 | static int ni_atmio_attach(struct comedi_device *dev, |
299 | struct comedi_devconfig *it) | |
a8b77430 | 300 | { |
ca4d4aa6 | 301 | const struct ni_board_struct *boardtype; |
a8b77430 DS |
302 | struct pnp_dev *isapnp_dev; |
303 | int ret; | |
304 | unsigned long iobase; | |
305 | int board; | |
306 | unsigned int irq; | |
307 | ||
c3744138 | 308 | ret = ni_alloc_private(dev); |
0e05c552 | 309 | if (ret) |
a8b77430 | 310 | return ret; |
c3744138 | 311 | |
a8b77430 DS |
312 | iobase = it->options[0]; |
313 | irq = it->options[1]; | |
314 | isapnp_dev = NULL; | |
315 | if (iobase == 0) { | |
316 | ret = ni_isapnp_find_board(&isapnp_dev); | |
317 | if (ret < 0) | |
318 | return ret; | |
319 | ||
320 | iobase = pnp_port_start(isapnp_dev, 0); | |
321 | irq = pnp_irq(isapnp_dev, 0); | |
ffd0a782 | 322 | comedi_set_hw_dev(dev, &isapnp_dev->dev); |
a8b77430 DS |
323 | } |
324 | ||
551d7939 | 325 | ret = comedi_request_region(dev, iobase, 0x20); |
b1bc9276 HS |
326 | if (ret) |
327 | return ret; | |
a8b77430 | 328 | |
a8b77430 DS |
329 | /* get board type */ |
330 | ||
331 | board = ni_getboardtype(dev); | |
332 | if (board < 0) | |
333 | return -EIO; | |
334 | ||
335 | dev->board_ptr = ni_boards + board; | |
af169cf1 | 336 | boardtype = dev->board_ptr; |
ca4d4aa6 | 337 | dev->board_name = boardtype->name; |
a8b77430 DS |
338 | |
339 | /* irq stuff */ | |
340 | ||
341 | if (irq != 0) { | |
221fa08c | 342 | if (irq > 15 || ni_irqpin[irq] == -1) |
a8b77430 | 343 | return -EINVAL; |
32d878a2 | 344 | ret = request_irq(irq, ni_E_interrupt, 0, |
71e06874 | 345 | dev->board_name, dev); |
221fa08c | 346 | if (ret < 0) |
a8b77430 | 347 | return -EINVAL; |
a8b77430 DS |
348 | dev->irq = irq; |
349 | } | |
350 | ||
351 | /* generic E series stuff in ni_mio_common.c */ | |
352 | ||
1fa955ba | 353 | ret = ni_E_init(dev, ni_irqpin[dev->irq], 0); |
bc2955dd | 354 | if (ret < 0) |
a8b77430 | 355 | return ret; |
bc2955dd | 356 | |
a8b77430 DS |
357 | return 0; |
358 | } | |
359 | ||
484ecc95 | 360 | static void ni_atmio_detach(struct comedi_device *dev) |
a8b77430 | 361 | { |
ffd0a782 | 362 | struct pnp_dev *isapnp_dev; |
0e05c552 | 363 | |
5aac8294 | 364 | mio_common_detach(dev); |
a32c6d00 | 365 | comedi_legacy_detach(dev); |
ffd0a782 HS |
366 | |
367 | isapnp_dev = dev->hw_dev ? to_pnp_dev(dev->hw_dev) : NULL; | |
368 | if (isapnp_dev) | |
369 | pnp_device_detach(isapnp_dev); | |
a8b77430 | 370 | } |
5aac8294 HS |
371 | |
372 | static struct comedi_driver ni_atmio_driver = { | |
373 | .driver_name = "ni_atmio", | |
374 | .module = THIS_MODULE, | |
375 | .attach = ni_atmio_attach, | |
376 | .detach = ni_atmio_detach, | |
377 | }; | |
378 | module_comedi_driver(ni_atmio_driver); |