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.
17*/
18/*
19Driver: ni_atmio
20Description: National Instruments AT-MIO-E series
21Author: ds
22Devices: [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
25Status: works
26Updated: Thu May  1 20:03:02 CDT 2003
27
28The driver has 2.6 kernel isapnp support, and
29will automatically probe for a supported board if the
30I/O base is left unspecified with comedi_config.
31However, many of
32the isapnp id numbers are unknown.  If your board is not
33recognized, 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
36id numbers for your board can be added to the driver.
37
38Otherwise, you can use the isapnptools package to configure
39your board.  Use isapnp to
40configure the I/O base and IRQ for the board, and then pass
41the same values as
42parameters in comedi_config.  A sample isapnp.conf file is included
43in the etc/ directory of Comedilib.
44
45Comedilib includes a utility to autocalibrate these boards.  The
46boards seem to boot into a state where the all calibration DACs
47are at one extreme of their range, thus the default calibration
48is terrible.  Calibration at boot is strongly encouraged.
49
50To use the extended digital I/O on some of the boards, enable the
518255 driver when configuring the Comedi source tree.
52
53External triggering is supported for some events.  The channel index
54(scan_begin_arg, etc.) maps to PFI0 - PFI9.
55
56Some of the more esoteric triggering possibilities of these boards
57are 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
92#include <linux/module.h>
93#include <linux/interrupt.h>
94#include "../comedidev.h"
95
96#include <linux/isapnp.h>
97
98#include "ni_stc.h"
99#include "8255.h"
100
101/*
102 *  AT specific setup
103 */
104
105static const struct ni_board_struct ni_boards[] = {
106	{
107		.name		= "at-mio-16e-1",
108		.device_id	= 44,
109		.isapnp_id	= 0x0000,	/* XXX unknown */
110		.n_adchan	= 16,
111		.ai_maxdata	= 0x0fff,
112		.ai_fifo_depth	= 8192,
113		.gainlkup	= ai_gain_16,
114		.ai_speed	= 800,
115		.n_aochan	= 2,
116		.ao_maxdata	= 0x0fff,
117		.ao_fifo_depth	= 2048,
118		.ao_range_table	= &range_ni_E_ao_ext,
119		.ao_speed	= 1000,
120		.caldac		= { mb88341 },
121	}, {
122		.name		= "at-mio-16e-2",
123		.device_id	= 25,
124		.isapnp_id	= 0x1900,
125		.n_adchan	= 16,
126		.ai_maxdata	= 0x0fff,
127		.ai_fifo_depth	= 2048,
128		.gainlkup	= ai_gain_16,
129		.ai_speed	= 2000,
130		.n_aochan	= 2,
131		.ao_maxdata	= 0x0fff,
132		.ao_fifo_depth	= 2048,
133		.ao_range_table	= &range_ni_E_ao_ext,
134		.ao_speed	= 1000,
135		.caldac		= { mb88341 },
136	}, {
137		.name		= "at-mio-16e-10",
138		.device_id	= 36,
139		.isapnp_id	= 0x2400,
140		.n_adchan	= 16,
141		.ai_maxdata	= 0x0fff,
142		.ai_fifo_depth	= 512,
143		.gainlkup	= ai_gain_16,
144		.ai_speed	= 10000,
145		.n_aochan	= 2,
146		.ao_maxdata	= 0x0fff,
147		.ao_range_table	= &range_ni_E_ao_ext,
148		.ao_speed	= 10000,
149		.caldac		= { ad8804_debug },
150	}, {
151		.name		= "at-mio-16de-10",
152		.device_id	= 37,
153		.isapnp_id	= 0x2500,
154		.n_adchan	= 16,
155		.ai_maxdata	= 0x0fff,
156		.ai_fifo_depth	= 512,
157		.gainlkup	= ai_gain_16,
158		.ai_speed	= 10000,
159		.n_aochan	= 2,
160		.ao_maxdata	= 0x0fff,
161		.ao_range_table	= &range_ni_E_ao_ext,
162		.ao_speed	= 10000,
163		.caldac		= { ad8804_debug },
164		.has_8255	= 1,
165	}, {
166		.name		= "at-mio-64e-3",
167		.device_id	= 38,
168		.isapnp_id	= 0x2600,
169		.n_adchan	= 64,
170		.ai_maxdata	= 0x0fff,
171		.ai_fifo_depth	= 2048,
172		.gainlkup	= ai_gain_16,
173		.ai_speed	= 2000,
174		.n_aochan	= 2,
175		.ao_maxdata	= 0x0fff,
176		.ao_fifo_depth	= 2048,
177		.ao_range_table	= &range_ni_E_ao_ext,
178		.ao_speed	= 1000,
179		.caldac		= { ad8804_debug },
180	}, {
181		.name		= "at-mio-16xe-50",
182		.device_id	= 39,
183		.isapnp_id	= 0x2700,
184		.n_adchan	= 16,
185		.ai_maxdata	= 0xffff,
186		.ai_fifo_depth	= 512,
187		.alwaysdither	= 1,
188		.gainlkup	= ai_gain_8,
189		.ai_speed	= 50000,
190		.n_aochan	= 2,
191		.ao_maxdata	= 0x0fff,
192		.ao_range_table	= &range_bipolar10,
193		.ao_speed	= 50000,
194		.caldac		= { dac8800, dac8043 },
195	}, {
196		.name		= "at-mio-16xe-10",
197		.device_id	= 50,
198		.isapnp_id	= 0x0000,	/* XXX unknown */
199		.n_adchan	= 16,
200		.ai_maxdata	= 0xffff,
201		.ai_fifo_depth	= 512,
202		.alwaysdither	= 1,
203		.gainlkup	= ai_gain_14,
204		.ai_speed	= 10000,
205		.n_aochan	= 2,
206		.ao_maxdata	= 0xffff,
207		.ao_fifo_depth	= 2048,
208		.ao_range_table	= &range_ni_E_ao_ext,
209		.ao_speed	= 1000,
210		.caldac		= { dac8800, dac8043, ad8522 },
211	}, {
212		.name		= "at-ai-16xe-10",
213		.device_id	= 51,
214		.isapnp_id	= 0x0000,	/* XXX unknown */
215		.n_adchan	= 16,
216		.ai_maxdata	= 0xffff,
217		.ai_fifo_depth	= 512,
218		.alwaysdither	= 1,	/* unknown */
219		.gainlkup	= ai_gain_14,
220		.ai_speed	= 10000,
221		.caldac		= { dac8800, dac8043, ad8522 },
222	},
223};
224
225static const int ni_irqpin[] = {
226	-1, -1, -1, 0, 1, 2, -1, 3, -1, -1, 4, 5, 6, -1, -1, 7
227};
228
229#include "ni_mio_common.c"
230
231static struct pnp_device_id device_ids[] = {
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},
237	{.id = ""}
238};
239
240MODULE_DEVICE_TABLE(pnp, device_ids);
241
242static int ni_isapnp_find_board(struct pnp_dev **dev)
243{
244	struct pnp_dev *isapnp_dev = NULL;
245	int i;
246
247	for (i = 0; i < ARRAY_SIZE(ni_boards); i++) {
248		isapnp_dev = pnp_find_dev(NULL,
249					  ISAPNP_VENDOR('N', 'I', 'C'),
250					  ISAPNP_FUNCTION(ni_boards[i].
251							  isapnp_id), NULL);
252
253		if (isapnp_dev == NULL || isapnp_dev->card == NULL)
254			continue;
255
256		if (pnp_device_attach(isapnp_dev) < 0)
257			continue;
258
259		if (pnp_activate_dev(isapnp_dev) < 0) {
260			pnp_device_detach(isapnp_dev);
261			return -EAGAIN;
262		}
263
264		if (!pnp_port_valid(isapnp_dev, 0) ||
265		    !pnp_irq_valid(isapnp_dev, 0)) {
266			pnp_device_detach(isapnp_dev);
267			return -ENOMEM;
268		}
269		break;
270	}
271	if (i == ARRAY_SIZE(ni_boards))
272		return -ENODEV;
273	*dev = isapnp_dev;
274	return 0;
275}
276
277static int ni_getboardtype(struct comedi_device *dev)
278{
279	int device_id = ni_read_eeprom(dev, 511);
280	int i;
281
282	for (i = 0; i < ARRAY_SIZE(ni_boards); i++) {
283		if (ni_boards[i].device_id == device_id)
284			return i;
285
286	}
287	if (device_id == 255)
288		dev_err(dev->class_dev, "can't find board\n");
289	 else if (device_id == 0)
290		dev_err(dev->class_dev,
291			"EEPROM read error (?) or device not found\n");
292	 else
293		dev_err(dev->class_dev,
294			"unknown device ID %d -- contact author\n", device_id);
295
296	return -1;
297}
298
299static int ni_atmio_attach(struct comedi_device *dev,
300			   struct comedi_devconfig *it)
301{
302	const struct ni_board_struct *boardtype;
303	struct ni_private *devpriv;
304	struct pnp_dev *isapnp_dev;
305	int ret;
306	unsigned long iobase;
307	int board;
308	unsigned int irq;
309
310	ret = ni_alloc_private(dev);
311	if (ret)
312		return ret;
313	devpriv = dev->private;
314
315	iobase = it->options[0];
316	irq = it->options[1];
317	isapnp_dev = NULL;
318	if (iobase == 0) {
319		ret = ni_isapnp_find_board(&isapnp_dev);
320		if (ret < 0)
321			return ret;
322
323		iobase = pnp_port_start(isapnp_dev, 0);
324		irq = pnp_irq(isapnp_dev, 0);
325		comedi_set_hw_dev(dev, &isapnp_dev->dev);
326	}
327
328	ret = comedi_request_region(dev, iobase, 0x20);
329	if (ret)
330		return ret;
331
332	/* get board type */
333
334	board = ni_getboardtype(dev);
335	if (board < 0)
336		return -EIO;
337
338	dev->board_ptr = ni_boards + board;
339	boardtype = dev->board_ptr;
340	dev->board_name = boardtype->name;
341
342	/* irq stuff */
343
344	if (irq != 0) {
345		if (irq > 15 || ni_irqpin[irq] == -1)
346			return -EINVAL;
347		ret = request_irq(irq, ni_E_interrupt, 0,
348				  dev->board_name, dev);
349		if (ret < 0)
350			return -EINVAL;
351		dev->irq = irq;
352	}
353
354	/* generic E series stuff in ni_mio_common.c */
355
356	ret = ni_E_init(dev, ni_irqpin[dev->irq], 0);
357	if (ret < 0)
358		return ret;
359
360
361	return 0;
362}
363
364static void ni_atmio_detach(struct comedi_device *dev)
365{
366	struct pnp_dev *isapnp_dev;
367
368	mio_common_detach(dev);
369	comedi_legacy_detach(dev);
370
371	isapnp_dev = dev->hw_dev ? to_pnp_dev(dev->hw_dev) : NULL;
372	if (isapnp_dev)
373		pnp_device_detach(isapnp_dev);
374}
375
376static struct comedi_driver ni_atmio_driver = {
377	.driver_name	= "ni_atmio",
378	.module		= THIS_MODULE,
379	.attach		= ni_atmio_attach,
380	.detach		= ni_atmio_detach,
381};
382module_comedi_driver(ni_atmio_driver);
383