ni_670x.c revision 25436dc9d84f1be60ff549c9ab712bba2835f284
1/*
2    comedi/drivers/ni_670x.c
3    Hardware driver for NI 670x devices
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    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22*/
23/*
24Driver: ni_670x
25Description: National Instruments 670x
26Author: Bart Joris <bjoris@advalvas.be>
27Updated: Wed, 11 Dec 2002 18:25:35 -0800
28Devices: [National Instruments] PCI-6703 (ni_670x), PCI-6704
29Status: unknown
30
31Commands are not supported.
32*/
33
34/*
35	Bart Joris <bjoris@advalvas.be> Last updated on 20/08/2001
36
37	Manuals:
38
39	322110a.pdf	PCI/PXI-6704 User Manual
40	322110b.pdf	PCI/PXI-6703/6704 User Manual
41
42*/
43
44#include <linux/interrupt.h>
45#include "../comedidev.h"
46
47#include "mite.h"
48
49#define PCI_VENDOR_ID_NATINST	0x1093
50
51#define AO_VALUE_OFFSET			0x00
52#define	AO_CHAN_OFFSET			0x0c
53#define	AO_STATUS_OFFSET		0x10
54#define AO_CONTROL_OFFSET		0x10
55#define	DIO_PORT0_DIR_OFFSET	0x20
56#define	DIO_PORT0_DATA_OFFSET	0x24
57#define	DIO_PORT1_DIR_OFFSET	0x28
58#define	DIO_PORT1_DATA_OFFSET	0x2c
59#define	MISC_STATUS_OFFSET		0x14
60#define	MISC_CONTROL_OFFSET		0x14
61
62/* Board description*/
63
64struct ni_670x_board {
65	unsigned short dev_id;
66	const char *name;
67	unsigned short ao_chans;
68	unsigned short ao_bits;
69};
70
71static const struct ni_670x_board ni_670x_boards[] = {
72	{
73	.dev_id = 0x2c90,
74	.name = "PCI-6703",
75	.ao_chans = 16,
76	.ao_bits = 16,
77		},
78	{
79	.dev_id = 0x1920,
80	.name = "PXI-6704",
81	.ao_chans = 32,
82	.ao_bits = 16,
83		},
84	{
85	.dev_id = 0x1290,
86	.name = "PCI-6704",
87	.ao_chans = 32,
88	.ao_bits = 16,
89		},
90};
91
92static DEFINE_PCI_DEVICE_TABLE(ni_670x_pci_table) = {
93	{PCI_VENDOR_ID_NATINST, 0x2c90, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
94	{PCI_VENDOR_ID_NATINST, 0x1920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
95	/* { PCI_VENDOR_ID_NATINST, 0x0000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, */
96	{0}
97};
98
99MODULE_DEVICE_TABLE(pci, ni_670x_pci_table);
100
101#define thisboard ((struct ni_670x_board *)dev->board_ptr)
102
103struct ni_670x_private {
104
105	struct mite_struct *mite;
106	int boardtype;
107	int dio;
108	unsigned int ao_readback[32];
109};
110
111
112#define devpriv ((struct ni_670x_private *)dev->private)
113#define n_ni_670x_boards (sizeof(ni_670x_boards)/sizeof(ni_670x_boards[0]))
114
115static int ni_670x_attach(struct comedi_device *dev, struct comedi_devconfig *it);
116static int ni_670x_detach(struct comedi_device *dev);
117
118static struct comedi_driver driver_ni_670x = {
119	.driver_name = "ni_670x",
120	.module = THIS_MODULE,
121	.attach = ni_670x_attach,
122	.detach = ni_670x_detach,
123};
124
125COMEDI_PCI_INITCLEANUP(driver_ni_670x, ni_670x_pci_table);
126
127static struct comedi_lrange range_0_20mA = { 1, {RANGE_mA(0, 20)} };
128
129static int ni_670x_find_device(struct comedi_device *dev, int bus, int slot);
130
131static int ni_670x_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
132	struct comedi_insn *insn, unsigned int *data);
133static int ni_670x_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
134	struct comedi_insn *insn, unsigned int *data);
135static int ni_670x_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
136	struct comedi_insn *insn, unsigned int *data);
137static int ni_670x_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
138	struct comedi_insn *insn, unsigned int *data);
139
140static int ni_670x_attach(struct comedi_device *dev, struct comedi_devconfig *it)
141{
142	struct comedi_subdevice *s;
143	int ret;
144	int i;
145
146	printk("comedi%d: ni_670x: ", dev->minor);
147
148	ret = alloc_private(dev, sizeof(struct ni_670x_private));
149	if (ret < 0)
150		return ret;
151
152	ret = ni_670x_find_device(dev, it->options[0], it->options[1]);
153	if (ret < 0)
154		return ret;
155
156	ret = mite_setup(devpriv->mite);
157	if (ret < 0) {
158		printk("error setting up mite\n");
159		return ret;
160	}
161	dev->board_name = thisboard->name;
162	dev->irq = mite_irq(devpriv->mite);
163	printk(" %s", dev->board_name);
164
165	if (alloc_subdevices(dev, 2) < 0)
166		return -ENOMEM;
167
168	s = dev->subdevices + 0;
169	/* analog output subdevice */
170	s->type = COMEDI_SUBD_AO;
171	s->subdev_flags = SDF_WRITABLE;
172	s->n_chan = thisboard->ao_chans;
173	s->maxdata = 0xffff;
174	if (s->n_chan == 32) {
175		const struct comedi_lrange **range_table_list;
176
177		range_table_list = kmalloc(sizeof(struct comedi_lrange *) * 32,
178			GFP_KERNEL);
179		if (!range_table_list)
180			return -ENOMEM;
181		s->range_table_list = range_table_list;
182		for (i = 0; i < 16; i++) {
183			range_table_list[i] = &range_bipolar10;
184			range_table_list[16 + i] = &range_0_20mA;
185		}
186	} else {
187		s->range_table = &range_bipolar10;
188	}
189	s->insn_write = &ni_670x_ao_winsn;
190	s->insn_read = &ni_670x_ao_rinsn;
191
192	s = dev->subdevices + 1;
193	/* digital i/o subdevice */
194	s->type = COMEDI_SUBD_DIO;
195	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
196	s->n_chan = 8;
197	s->maxdata = 1;
198	s->range_table = &range_digital;
199	s->insn_bits = ni_670x_dio_insn_bits;
200	s->insn_config = ni_670x_dio_insn_config;
201
202	writel(0x10, devpriv->mite->daq_io_addr + MISC_CONTROL_OFFSET);	/* Config of misc registers */
203	writel(0x00, devpriv->mite->daq_io_addr + AO_CONTROL_OFFSET);	/* Config of ao registers */
204
205	printk("attached\n");
206
207	return 1;
208}
209
210static int ni_670x_detach(struct comedi_device *dev)
211{
212	printk("comedi%d: ni_670x: remove\n", dev->minor);
213
214	if (dev->subdevices[0].range_table_list) {
215		kfree(dev->subdevices[0].range_table_list);
216	}
217	if (dev->private && devpriv->mite)
218		mite_unsetup(devpriv->mite);
219
220	if (dev->irq)
221		free_irq(dev->irq, dev);
222
223	return 0;
224}
225
226static int ni_670x_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
227	struct comedi_insn *insn, unsigned int *data)
228{
229	int i;
230	int chan = CR_CHAN(insn->chanspec);
231
232	/* Channel number mapping :
233
234	   NI 6703/ NI 6704     | NI 6704 Only
235	   ----------------------------------------------------
236	   vch(0)       :       0       | ich(16)       :       1
237	   vch(1)       :       2       | ich(17)       :       3
238	   .    :       .       |   .                   .
239	   .    :       .       |   .                   .
240	   .    :       .       |   .                   .
241	   vch(15)      :       30      | ich(31)       :       31      */
242
243	for (i = 0; i < insn->n; i++) {
244		writel(((chan & 15) << 1) | ((chan & 16) >> 4), devpriv->mite->daq_io_addr + AO_CHAN_OFFSET);	/* First write in channel register which channel to use */
245		writel(data[i], devpriv->mite->daq_io_addr + AO_VALUE_OFFSET);	/* write channel value */
246		devpriv->ao_readback[chan] = data[i];
247	}
248
249	return i;
250}
251
252static int ni_670x_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
253	struct comedi_insn *insn, unsigned int *data)
254{
255	int i;
256	int chan = CR_CHAN(insn->chanspec);
257
258	for (i = 0; i < insn->n; i++)
259		data[i] = devpriv->ao_readback[chan];
260
261	return i;
262}
263
264static int ni_670x_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
265	struct comedi_insn *insn, unsigned int *data)
266{
267	if (insn->n != 2)
268		return -EINVAL;
269
270	/* The insn data is a mask in data[0] and the new data
271	 * in data[1], each channel cooresponding to a bit. */
272	if (data[0]) {
273		s->state &= ~data[0];
274		s->state |= data[0] & data[1];
275		writel(s->state,
276			devpriv->mite->daq_io_addr + DIO_PORT0_DATA_OFFSET);
277	}
278
279	/* on return, data[1] contains the value of the digital
280	 * input lines. */
281	data[1] = readl(devpriv->mite->daq_io_addr + DIO_PORT0_DATA_OFFSET);
282
283	return 2;
284}
285
286static int ni_670x_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
287	struct comedi_insn *insn, unsigned int *data)
288{
289	int chan = CR_CHAN(insn->chanspec);
290
291	switch (data[0]) {
292	case INSN_CONFIG_DIO_OUTPUT:
293		s->io_bits |= 1 << chan;
294		break;
295	case INSN_CONFIG_DIO_INPUT:
296		s->io_bits &= ~(1 << chan);
297		break;
298	case INSN_CONFIG_DIO_QUERY:
299		data[1] =
300			(s->
301			io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
302		return insn->n;
303		break;
304	default:
305		return -EINVAL;
306		break;
307	}
308	writel(s->io_bits, devpriv->mite->daq_io_addr + DIO_PORT0_DIR_OFFSET);
309
310	return insn->n;
311}
312
313static int ni_670x_find_device(struct comedi_device *dev, int bus, int slot)
314{
315	struct mite_struct *mite;
316	int i;
317
318	for (mite = mite_devices; mite; mite = mite->next) {
319		if (mite->used)
320			continue;
321		if (bus || slot) {
322			if (bus != mite->pcidev->bus->number
323				|| slot != PCI_SLOT(mite->pcidev->devfn))
324				continue;
325		}
326
327		for (i = 0; i < n_ni_670x_boards; i++) {
328			if (mite_device_id(mite) == ni_670x_boards[i].dev_id) {
329				dev->board_ptr = ni_670x_boards + i;
330				devpriv->mite = mite;
331
332				return 0;
333			}
334		}
335	}
336	printk("no device found\n");
337	mite_list_devices();
338	return -EIO;
339}
340