1 // SPDX-License-Identifier: GPL-2.0+
2 /*
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>
7 */
8
9 /*
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 */
45
46 /*
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 */
73
74 #include <linux/module.h>
75 #include <linux/interrupt.h>
76 #include <linux/comedi/comedidev.h>
77 #include <linux/isapnp.h>
78 #include <linux/comedi/comedi_8255.h>
79
80 #include "ni_stc.h"
81
82 static const struct comedi_lrange range_ni_E_ao_ext = {
83 4, {
84 BIP_RANGE(10),
85 UNI_RANGE(10),
86 RANGE_ext(-1, 1),
87 RANGE_ext(0, 1)
88 }
89 };
90
91 /* AT specific setup */
92 static const struct ni_board_struct ni_boards[] = {
93 {
94 .name = "at-mio-16e-1",
95 .device_id = 44,
96 .isapnp_id = 0x0000, /* XXX unknown */
97 .n_adchan = 16,
98 .ai_maxdata = 0x0fff,
99 .ai_fifo_depth = 8192,
100 .gainlkup = ai_gain_16,
101 .ai_speed = 800,
102 .n_aochan = 2,
103 .ao_maxdata = 0x0fff,
104 .ao_fifo_depth = 2048,
105 .ao_range_table = &range_ni_E_ao_ext,
106 .ao_speed = 1000,
107 .caldac = { mb88341 },
108 }, {
109 .name = "at-mio-16e-2",
110 .device_id = 25,
111 .isapnp_id = 0x1900,
112 .n_adchan = 16,
113 .ai_maxdata = 0x0fff,
114 .ai_fifo_depth = 2048,
115 .gainlkup = ai_gain_16,
116 .ai_speed = 2000,
117 .n_aochan = 2,
118 .ao_maxdata = 0x0fff,
119 .ao_fifo_depth = 2048,
120 .ao_range_table = &range_ni_E_ao_ext,
121 .ao_speed = 1000,
122 .caldac = { mb88341 },
123 }, {
124 .name = "at-mio-16e-10",
125 .device_id = 36,
126 .isapnp_id = 0x2400,
127 .n_adchan = 16,
128 .ai_maxdata = 0x0fff,
129 .ai_fifo_depth = 512,
130 .gainlkup = ai_gain_16,
131 .ai_speed = 10000,
132 .n_aochan = 2,
133 .ao_maxdata = 0x0fff,
134 .ao_range_table = &range_ni_E_ao_ext,
135 .ao_speed = 10000,
136 .caldac = { ad8804_debug },
137 }, {
138 .name = "at-mio-16de-10",
139 .device_id = 37,
140 .isapnp_id = 0x2500,
141 .n_adchan = 16,
142 .ai_maxdata = 0x0fff,
143 .ai_fifo_depth = 512,
144 .gainlkup = ai_gain_16,
145 .ai_speed = 10000,
146 .n_aochan = 2,
147 .ao_maxdata = 0x0fff,
148 .ao_range_table = &range_ni_E_ao_ext,
149 .ao_speed = 10000,
150 .caldac = { ad8804_debug },
151 .has_8255 = 1,
152 }, {
153 .name = "at-mio-64e-3",
154 .device_id = 38,
155 .isapnp_id = 0x2600,
156 .n_adchan = 64,
157 .ai_maxdata = 0x0fff,
158 .ai_fifo_depth = 2048,
159 .gainlkup = ai_gain_16,
160 .ai_speed = 2000,
161 .n_aochan = 2,
162 .ao_maxdata = 0x0fff,
163 .ao_fifo_depth = 2048,
164 .ao_range_table = &range_ni_E_ao_ext,
165 .ao_speed = 1000,
166 .caldac = { ad8804_debug },
167 }, {
168 .name = "at-mio-16xe-50",
169 .device_id = 39,
170 .isapnp_id = 0x2700,
171 .n_adchan = 16,
172 .ai_maxdata = 0xffff,
173 .ai_fifo_depth = 512,
174 .alwaysdither = 1,
175 .gainlkup = ai_gain_8,
176 .ai_speed = 50000,
177 .n_aochan = 2,
178 .ao_maxdata = 0x0fff,
179 .ao_range_table = &range_bipolar10,
180 .ao_speed = 50000,
181 .caldac = { dac8800, dac8043 },
182 }, {
183 .name = "at-mio-16xe-10",
184 .device_id = 50,
185 .isapnp_id = 0x0000, /* XXX unknown */
186 .n_adchan = 16,
187 .ai_maxdata = 0xffff,
188 .ai_fifo_depth = 512,
189 .alwaysdither = 1,
190 .gainlkup = ai_gain_14,
191 .ai_speed = 10000,
192 .n_aochan = 2,
193 .ao_maxdata = 0xffff,
194 .ao_fifo_depth = 2048,
195 .ao_range_table = &range_ni_E_ao_ext,
196 .ao_speed = 1000,
197 .caldac = { dac8800, dac8043, ad8522 },
198 }, {
199 .name = "at-ai-16xe-10",
200 .device_id = 51,
201 .isapnp_id = 0x0000, /* XXX unknown */
202 .n_adchan = 16,
203 .ai_maxdata = 0xffff,
204 .ai_fifo_depth = 512,
205 .alwaysdither = 1, /* unknown */
206 .gainlkup = ai_gain_14,
207 .ai_speed = 10000,
208 .caldac = { dac8800, dac8043, ad8522 },
209 },
210 };
211
212 static const int ni_irqpin[] = {
213 -1, -1, -1, 0, 1, 2, -1, 3, -1, -1, 4, 5, 6, -1, -1, 7
214 };
215
216 #include "ni_mio_common.c"
217
218 static const struct pnp_device_id device_ids[] = {
219 {.id = "NIC1900", .driver_data = 0},
220 {.id = "NIC2400", .driver_data = 0},
221 {.id = "NIC2500", .driver_data = 0},
222 {.id = "NIC2600", .driver_data = 0},
223 {.id = "NIC2700", .driver_data = 0},
224 {.id = ""}
225 };
226
227 MODULE_DEVICE_TABLE(pnp, device_ids);
228
ni_isapnp_find_board(struct pnp_dev ** dev)229 static int ni_isapnp_find_board(struct pnp_dev **dev)
230 {
231 struct pnp_dev *isapnp_dev = NULL;
232 int i;
233
234 for (i = 0; i < ARRAY_SIZE(ni_boards); i++) {
235 isapnp_dev =
236 pnp_find_dev(NULL,
237 ISAPNP_VENDOR('N', 'I', 'C'),
238 ISAPNP_FUNCTION(ni_boards[i].isapnp_id),
239 NULL);
240
241 if (!isapnp_dev || !isapnp_dev->card)
242 continue;
243
244 if (pnp_device_attach(isapnp_dev) < 0)
245 continue;
246
247 if (pnp_activate_dev(isapnp_dev) < 0) {
248 pnp_device_detach(isapnp_dev);
249 return -EAGAIN;
250 }
251
252 if (!pnp_port_valid(isapnp_dev, 0) ||
253 !pnp_irq_valid(isapnp_dev, 0)) {
254 pnp_device_detach(isapnp_dev);
255 return -ENOMEM;
256 }
257 break;
258 }
259 if (i == ARRAY_SIZE(ni_boards))
260 return -ENODEV;
261 *dev = isapnp_dev;
262 return 0;
263 }
264
ni_atmio_probe(struct comedi_device * dev)265 static const struct ni_board_struct *ni_atmio_probe(struct comedi_device *dev)
266 {
267 int device_id = ni_read_eeprom(dev, 511);
268 int i;
269
270 for (i = 0; i < ARRAY_SIZE(ni_boards); i++) {
271 const struct ni_board_struct *board = &ni_boards[i];
272
273 if (board->device_id == device_id)
274 return board;
275 }
276 if (device_id == 255)
277 dev_err(dev->class_dev, "can't find board\n");
278 else if (device_id == 0)
279 dev_err(dev->class_dev,
280 "EEPROM read error (?) or device not found\n");
281 else
282 dev_err(dev->class_dev,
283 "unknown device ID %d -- contact author\n", device_id);
284
285 return NULL;
286 }
287
ni_atmio_attach(struct comedi_device * dev,struct comedi_devconfig * it)288 static int ni_atmio_attach(struct comedi_device *dev,
289 struct comedi_devconfig *it)
290 {
291 const struct ni_board_struct *board;
292 struct pnp_dev *isapnp_dev;
293 int ret;
294 unsigned long iobase;
295 unsigned int irq;
296
297 ret = ni_alloc_private(dev);
298 if (ret)
299 return ret;
300
301 iobase = it->options[0];
302 irq = it->options[1];
303 isapnp_dev = NULL;
304 if (iobase == 0) {
305 ret = ni_isapnp_find_board(&isapnp_dev);
306 if (ret < 0)
307 return ret;
308
309 iobase = pnp_port_start(isapnp_dev, 0);
310 irq = pnp_irq(isapnp_dev, 0);
311 comedi_set_hw_dev(dev, &isapnp_dev->dev);
312 }
313
314 ret = comedi_request_region(dev, iobase, 0x20);
315 if (ret)
316 return ret;
317
318 board = ni_atmio_probe(dev);
319 if (!board)
320 return -ENODEV;
321 dev->board_ptr = board;
322 dev->board_name = board->name;
323
324 /* irq stuff */
325
326 if (irq != 0) {
327 if (irq > 15 || ni_irqpin[irq] == -1)
328 return -EINVAL;
329 ret = request_irq(irq, ni_E_interrupt, 0,
330 dev->board_name, dev);
331 if (ret < 0)
332 return -EINVAL;
333 dev->irq = irq;
334 }
335
336 /* generic E series stuff in ni_mio_common.c */
337
338 ret = ni_E_init(dev, ni_irqpin[dev->irq], 0);
339 if (ret < 0)
340 return ret;
341
342 return 0;
343 }
344
ni_atmio_detach(struct comedi_device * dev)345 static void ni_atmio_detach(struct comedi_device *dev)
346 {
347 struct pnp_dev *isapnp_dev;
348
349 mio_common_detach(dev);
350 comedi_legacy_detach(dev);
351
352 isapnp_dev = dev->hw_dev ? to_pnp_dev(dev->hw_dev) : NULL;
353 if (isapnp_dev)
354 pnp_device_detach(isapnp_dev);
355 }
356
357 static struct comedi_driver ni_atmio_driver = {
358 .driver_name = "ni_atmio",
359 .module = THIS_MODULE,
360 .attach = ni_atmio_attach,
361 .detach = ni_atmio_detach,
362 };
363 module_comedi_driver(ni_atmio_driver);
364
365 MODULE_AUTHOR("Comedi https://www.comedi.org");
366 MODULE_DESCRIPTION("Comedi low-level driver");
367 MODULE_LICENSE("GPL");
368
369