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