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