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