[go: nahoru, domu]

1/*
2 * comedi/drivers/rti800.c
3 * Hardware driver for Analog Devices RTI-800/815 board
4 *
5 * COMEDI - Linux Control and Measurement Device Interface
6 * Copyright (C) 1998 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
19/*
20 * Driver: rti800
21 * Description: Analog Devices RTI-800/815
22 * Devices: (Analog Devices) RTI-800 [rti800]
23 *	    (Analog Devices) RTI-815 [rti815]
24 * Author: David A. Schleef <ds@schleef.org>
25 * Status: unknown
26 * Updated: Fri, 05 Sep 2008 14:50:44 +0100
27 *
28 * Configuration options:
29 *   [0] - I/O port base address
30 *   [1] - IRQ (not supported / unused)
31 *   [2] - A/D mux/reference (number of channels)
32 *	   0 = differential
33 *	   1 = pseudodifferential (common)
34 *	   2 = single-ended
35 *   [3] - A/D range
36 *	   0 = [-10,10]
37 *	   1 = [-5,5]
38 *	   2 = [0,10]
39 *   [4] - A/D encoding
40 *	   0 = two's complement
41 *	   1 = straight binary
42 *   [5] - DAC 0 range
43 *	   0 = [-10,10]
44 *	   1 = [0,10]
45 *   [6] - DAC 0 encoding
46 *	   0 = two's complement
47 *	   1 = straight binary
48 *   [7] - DAC 1 range (same as DAC 0)
49 *   [8] - DAC 1 encoding (same as DAC 0)
50 */
51
52#include <linux/module.h>
53#include <linux/delay.h>
54#include <linux/interrupt.h>
55#include "../comedidev.h"
56
57/*
58 * Register map
59 */
60#define RTI800_CSR		0x00
61#define RTI800_CSR_BUSY		(1 << 7)
62#define RTI800_CSR_DONE		(1 << 6)
63#define RTI800_CSR_OVERRUN	(1 << 5)
64#define RTI800_CSR_TCR		(1 << 4)
65#define RTI800_CSR_DMA_ENAB	(1 << 3)
66#define RTI800_CSR_INTR_TC	(1 << 2)
67#define RTI800_CSR_INTR_EC	(1 << 1)
68#define RTI800_CSR_INTR_OVRN	(1 << 0)
69#define RTI800_MUXGAIN		0x01
70#define RTI800_CONVERT		0x02
71#define RTI800_ADCLO		0x03
72#define RTI800_ADCHI		0x04
73#define RTI800_DAC0LO		0x05
74#define RTI800_DAC0HI		0x06
75#define RTI800_DAC1LO		0x07
76#define RTI800_DAC1HI		0x08
77#define RTI800_CLRFLAGS		0x09
78#define RTI800_DI		0x0a
79#define RTI800_DO		0x0b
80#define RTI800_9513A_DATA	0x0c
81#define RTI800_9513A_CNTRL	0x0d
82#define RTI800_9513A_STATUS	0x0d
83
84static const struct comedi_lrange range_rti800_ai_10_bipolar = {
85	4, {
86		BIP_RANGE(10),
87		BIP_RANGE(1),
88		BIP_RANGE(0.1),
89		BIP_RANGE(0.02)
90	}
91};
92
93static const struct comedi_lrange range_rti800_ai_5_bipolar = {
94	4, {
95		BIP_RANGE(5),
96		BIP_RANGE(0.5),
97		BIP_RANGE(0.05),
98		BIP_RANGE(0.01)
99	}
100};
101
102static const struct comedi_lrange range_rti800_ai_unipolar = {
103	4, {
104		UNI_RANGE(10),
105		UNI_RANGE(1),
106		UNI_RANGE(0.1),
107		UNI_RANGE(0.02)
108	}
109};
110
111static const struct comedi_lrange *const rti800_ai_ranges[] = {
112	&range_rti800_ai_10_bipolar,
113	&range_rti800_ai_5_bipolar,
114	&range_rti800_ai_unipolar,
115};
116
117static const struct comedi_lrange *const rti800_ao_ranges[] = {
118	&range_bipolar10,
119	&range_unipolar10,
120};
121
122struct rti800_board {
123	const char *name;
124	int has_ao;
125};
126
127static const struct rti800_board rti800_boardtypes[] = {
128	{
129		.name		= "rti800",
130	}, {
131		.name		= "rti815",
132		.has_ao		= 1,
133	},
134};
135
136struct rti800_private {
137	bool adc_2comp;
138	bool dac_2comp[2];
139	const struct comedi_lrange *ao_range_type_list[2];
140	unsigned char muxgain_bits;
141};
142
143static int rti800_ai_eoc(struct comedi_device *dev,
144			 struct comedi_subdevice *s,
145			 struct comedi_insn *insn,
146			 unsigned long context)
147{
148	unsigned char status;
149
150	status = inb(dev->iobase + RTI800_CSR);
151	if (status & RTI800_CSR_OVERRUN) {
152		outb(0, dev->iobase + RTI800_CLRFLAGS);
153		return -EOVERFLOW;
154	}
155	if (status & RTI800_CSR_DONE)
156		return 0;
157	return -EBUSY;
158}
159
160static int rti800_ai_insn_read(struct comedi_device *dev,
161			       struct comedi_subdevice *s,
162			       struct comedi_insn *insn,
163			       unsigned int *data)
164{
165	struct rti800_private *devpriv = dev->private;
166	unsigned int chan = CR_CHAN(insn->chanspec);
167	unsigned int gain = CR_RANGE(insn->chanspec);
168	unsigned char muxgain_bits;
169	int ret;
170	int i;
171
172	inb(dev->iobase + RTI800_ADCHI);
173	outb(0, dev->iobase + RTI800_CLRFLAGS);
174
175	muxgain_bits = chan | (gain << 5);
176	if (muxgain_bits != devpriv->muxgain_bits) {
177		devpriv->muxgain_bits = muxgain_bits;
178		outb(devpriv->muxgain_bits, dev->iobase + RTI800_MUXGAIN);
179		/*
180		 * Without a delay here, the RTI_CSR_OVERRUN bit
181		 * gets set, and you will have an error.
182		 */
183		if (insn->n > 0) {
184			int delay = (gain == 0) ? 10 :
185				    (gain == 1) ? 20 :
186				    (gain == 2) ? 40 : 80;
187
188			udelay(delay);
189		}
190	}
191
192	for (i = 0; i < insn->n; i++) {
193		outb(0, dev->iobase + RTI800_CONVERT);
194
195		ret = comedi_timeout(dev, s, insn, rti800_ai_eoc, 0);
196		if (ret)
197			return ret;
198
199		data[i] = inb(dev->iobase + RTI800_ADCLO);
200		data[i] |= (inb(dev->iobase + RTI800_ADCHI) & 0xf) << 8;
201
202		if (devpriv->adc_2comp)
203			data[i] ^= 0x800;
204	}
205
206	return insn->n;
207}
208
209static int rti800_ao_insn_write(struct comedi_device *dev,
210				struct comedi_subdevice *s,
211				struct comedi_insn *insn,
212				unsigned int *data)
213{
214	struct rti800_private *devpriv = dev->private;
215	unsigned int chan = CR_CHAN(insn->chanspec);
216	int reg_lo = chan ? RTI800_DAC1LO : RTI800_DAC0LO;
217	int reg_hi = chan ? RTI800_DAC1HI : RTI800_DAC0HI;
218	int i;
219
220	for (i = 0; i < insn->n; i++) {
221		unsigned int val = data[i];
222
223		s->readback[chan] = val;
224
225		if (devpriv->dac_2comp[chan])
226			val ^= 0x800;
227
228		outb(val & 0xff, dev->iobase + reg_lo);
229		outb((val >> 8) & 0xff, dev->iobase + reg_hi);
230	}
231
232	return insn->n;
233}
234
235static int rti800_di_insn_bits(struct comedi_device *dev,
236			       struct comedi_subdevice *s,
237			       struct comedi_insn *insn,
238			       unsigned int *data)
239{
240	data[1] = inb(dev->iobase + RTI800_DI);
241	return insn->n;
242}
243
244static int rti800_do_insn_bits(struct comedi_device *dev,
245			       struct comedi_subdevice *s,
246			       struct comedi_insn *insn,
247			       unsigned int *data)
248{
249	if (comedi_dio_update_state(s, data)) {
250		/* Outputs are inverted... */
251		outb(s->state ^ 0xff, dev->iobase + RTI800_DO);
252	}
253
254	data[1] = s->state;
255
256	return insn->n;
257}
258
259static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
260{
261	const struct rti800_board *board = dev->board_ptr;
262	struct rti800_private *devpriv;
263	struct comedi_subdevice *s;
264	int ret;
265
266	ret = comedi_request_region(dev, it->options[0], 0x10);
267	if (ret)
268		return ret;
269
270	outb(0, dev->iobase + RTI800_CSR);
271	inb(dev->iobase + RTI800_ADCHI);
272	outb(0, dev->iobase + RTI800_CLRFLAGS);
273
274	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
275	if (!devpriv)
276		return -ENOMEM;
277
278	devpriv->adc_2comp = (it->options[4] == 0);
279	devpriv->dac_2comp[0] = (it->options[6] == 0);
280	devpriv->dac_2comp[1] = (it->options[8] == 0);
281	/* invalid, forces the MUXGAIN register to be set when first used */
282	devpriv->muxgain_bits = 0xff;
283
284	ret = comedi_alloc_subdevices(dev, 4);
285	if (ret)
286		return ret;
287
288	s = &dev->subdevices[0];
289	/* ai subdevice */
290	s->type		= COMEDI_SUBD_AI;
291	s->subdev_flags	= SDF_READABLE | SDF_GROUND;
292	s->n_chan	= (it->options[2] ? 16 : 8);
293	s->insn_read	= rti800_ai_insn_read;
294	s->maxdata	= 0x0fff;
295	s->range_table	= (it->options[3] < ARRAY_SIZE(rti800_ai_ranges))
296				? rti800_ai_ranges[it->options[3]]
297				: &range_unknown;
298
299	s = &dev->subdevices[1];
300	if (board->has_ao) {
301		/* ao subdevice (only on rti815) */
302		s->type		= COMEDI_SUBD_AO;
303		s->subdev_flags	= SDF_WRITABLE;
304		s->n_chan	= 2;
305		s->maxdata	= 0x0fff;
306		s->range_table_list = devpriv->ao_range_type_list;
307		devpriv->ao_range_type_list[0] =
308			(it->options[5] < ARRAY_SIZE(rti800_ao_ranges))
309				? rti800_ao_ranges[it->options[5]]
310				: &range_unknown;
311		devpriv->ao_range_type_list[1] =
312			(it->options[7] < ARRAY_SIZE(rti800_ao_ranges))
313				? rti800_ao_ranges[it->options[7]]
314				: &range_unknown;
315		s->insn_write	= rti800_ao_insn_write;
316		s->insn_read	= comedi_readback_insn_read;
317
318		ret = comedi_alloc_subdev_readback(s);
319		if (ret)
320			return ret;
321	} else {
322		s->type		= COMEDI_SUBD_UNUSED;
323	}
324
325	s = &dev->subdevices[2];
326	/* di */
327	s->type		= COMEDI_SUBD_DI;
328	s->subdev_flags	= SDF_READABLE;
329	s->n_chan	= 8;
330	s->insn_bits	= rti800_di_insn_bits;
331	s->maxdata	= 1;
332	s->range_table	= &range_digital;
333
334	s = &dev->subdevices[3];
335	/* do */
336	s->type		= COMEDI_SUBD_DO;
337	s->subdev_flags	= SDF_WRITABLE;
338	s->n_chan	= 8;
339	s->insn_bits	= rti800_do_insn_bits;
340	s->maxdata	= 1;
341	s->range_table	= &range_digital;
342
343	/*
344	 * There is also an Am9513 timer on these boards. This subdevice
345	 * is not currently supported.
346	 */
347
348	return 0;
349}
350
351static struct comedi_driver rti800_driver = {
352	.driver_name	= "rti800",
353	.module		= THIS_MODULE,
354	.attach		= rti800_attach,
355	.detach		= comedi_legacy_detach,
356	.num_names	= ARRAY_SIZE(rti800_boardtypes),
357	.board_name	= &rti800_boardtypes[0].name,
358	.offset		= sizeof(struct rti800_board),
359};
360module_comedi_driver(rti800_driver);
361
362MODULE_DESCRIPTION("Comedi: RTI-800 Multifunction Analog/Digital board");
363MODULE_AUTHOR("Comedi http://www.comedi.org");
364MODULE_LICENSE("GPL");
365