rti800.c revision 8629efa4cbf6f89a54a85af4b8bc31762af01800
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   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: rti800
25Description: Analog Devices RTI-800/815
26Author: ds
27Status: unknown
28Updated: Fri, 05 Sep 2008 14:50:44 +0100
29Devices: [Analog Devices] RTI-800 (rti800), RTI-815 (rti815)
30
31Configuration options:
32  [0] - I/O port base address
33  [1] - IRQ
34  [2] - A/D reference
35          0 = differential
36          1 = pseudodifferential (common)
37          2 = single-ended
38  [3] - A/D range
39          0 = [-10,10]
40          1 = [-5,5]
41          2 = [0,10]
42  [4] - A/D encoding
43          0 = two's complement
44          1 = straight binary
45  [5] - DAC 0 range
46          0 = [-10,10]
47          1 = [0,10]
48  [6] - DAC 0 encoding
49          0 = two's complement
50          1 = straight binary
51  [7] - DAC 1 range (same as DAC 0)
52  [8] - DAC 1 encoding (same as DAC 0)
53*/
54
55#include "../comedidev.h"
56
57#include <linux/ioport.h>
58
59#define RTI800_SIZE 16
60
61#define RTI800_CSR 0
62#define RTI800_MUXGAIN 1
63#define RTI800_CONVERT 2
64#define RTI800_ADCLO 3
65#define RTI800_ADCHI 4
66#define RTI800_DAC0LO 5
67#define RTI800_DAC0HI 6
68#define RTI800_DAC1LO 7
69#define RTI800_DAC1HI 8
70#define RTI800_CLRFLAGS 9
71#define RTI800_DI 10
72#define RTI800_DO 11
73#define RTI800_9513A_DATA 12
74#define RTI800_9513A_CNTRL 13
75#define RTI800_9513A_STATUS 13
76
77/*
78 * flags for CSR register
79 */
80
81#define RTI800_BUSY		0x80
82#define RTI800_DONE		0x40
83#define RTI800_OVERRUN		0x20
84#define RTI800_TCR		0x10
85#define RTI800_DMA_ENAB		0x08
86#define RTI800_INTR_TC		0x04
87#define RTI800_INTR_EC		0x02
88#define RTI800_INTR_OVRN	0x01
89
90#define Am9513_8BITBUS
91
92#define Am9513_output_control(a)	outb(a, dev->iobase+RTI800_9513A_CNTRL)
93#define Am9513_output_data(a)		outb(a, dev->iobase+RTI800_9513A_DATA)
94#define Am9513_input_data()		inb(dev->iobase+RTI800_9513A_DATA)
95#define Am9513_input_status()		inb(dev->iobase+RTI800_9513A_STATUS)
96
97#include "am9513.h"
98
99static const struct comedi_lrange range_rti800_ai_10_bipolar = { 4, {
100			BIP_RANGE(10),
101			BIP_RANGE(1),
102			BIP_RANGE(0.1),
103			BIP_RANGE(0.02)
104	}
105};
106static const struct comedi_lrange range_rti800_ai_5_bipolar = { 4, {
107			BIP_RANGE(5),
108			BIP_RANGE(0.5),
109			BIP_RANGE(0.05),
110			BIP_RANGE(0.01)
111	}
112};
113static const struct comedi_lrange range_rti800_ai_unipolar = { 4, {
114			UNI_RANGE(10),
115			UNI_RANGE(1),
116			UNI_RANGE(0.1),
117			UNI_RANGE(0.02)
118	}
119};
120
121struct rti800_board {
122
123	const char *name;
124	int has_ao;
125};
126
127static const struct rti800_board boardtypes[] = {
128	{"rti800", 0},
129	{"rti815", 1},
130};
131
132#define this_board ((const struct rti800_board *)dev->board_ptr)
133
134static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it);
135static int rti800_detach(struct comedi_device *dev);
136static struct comedi_driver driver_rti800 = {
137	.driver_name = "rti800",
138	.module = THIS_MODULE,
139	.attach = rti800_attach,
140	.detach = rti800_detach,
141	.num_names = ARRAY_SIZE(boardtypes),
142	.board_name = &boardtypes[0].name,
143	.offset = sizeof(struct rti800_board),
144};
145
146COMEDI_INITCLEANUP(driver_rti800);
147
148static irqreturn_t rti800_interrupt(int irq, void *dev);
149
150struct rti800_private {
151	enum {
152		adc_diff, adc_pseudodiff, adc_singleended
153	} adc_mux;
154	enum {
155		adc_bipolar10, adc_bipolar5, adc_unipolar10
156	} adc_range;
157	enum {
158		adc_2comp, adc_straight
159	} adc_coding;
160	enum {
161		dac_bipolar10, dac_unipolar10
162	} dac0_range, dac1_range;
163	enum {
164		dac_2comp, dac_straight
165	} dac0_coding, dac1_coding;
166	const struct comedi_lrange *ao_range_type_list[2];
167	unsigned int ao_readback[2];
168	int muxgain_bits;
169};
170
171#define devpriv ((struct rti800_private *)dev->private)
172
173#define RTI800_TIMEOUT 100
174
175static irqreturn_t rti800_interrupt(int irq, void *dev)
176{
177	return IRQ_HANDLED;
178}
179
180/* settling delay times in usec for different gains */
181static const int gaindelay[] = { 10, 20, 40, 80 };
182
183static int rti800_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
184	struct comedi_insn *insn, unsigned int *data)
185{
186	int i, t;
187	int status;
188	int chan = CR_CHAN(insn->chanspec);
189	unsigned gain = CR_RANGE(insn->chanspec);
190	unsigned muxgain_bits;
191
192	inb(dev->iobase + RTI800_ADCHI);
193	outb(0, dev->iobase + RTI800_CLRFLAGS);
194
195	muxgain_bits = chan | (gain << 5);
196	if (muxgain_bits != devpriv->muxgain_bits) {
197		devpriv->muxgain_bits = muxgain_bits;
198		outb(devpriv->muxgain_bits, dev->iobase + RTI800_MUXGAIN);
199		/* without a delay here, the RTI_OVERRUN bit
200		 * gets set, and you will have an error. */
201		if (insn->n > 0) {
202			BUG_ON(gain >= ARRAY_SIZE(gaindelay));
203			comedi_udelay(gaindelay[gain]);
204		}
205	}
206
207	for (i = 0; i < insn->n; i++) {
208		outb(0, dev->iobase + RTI800_CONVERT);
209		for (t = RTI800_TIMEOUT; t; t--) {
210			status = inb(dev->iobase + RTI800_CSR);
211			if (status & RTI800_OVERRUN) {
212				rt_printk("rti800: a/d overrun\n");
213				outb(0, dev->iobase + RTI800_CLRFLAGS);
214				return -EIO;
215			}
216			if (status & RTI800_DONE)
217				break;
218			comedi_udelay(1);
219		}
220		if (t == 0) {
221			rt_printk("rti800: timeout\n");
222			return -ETIME;
223		}
224		data[i] = inb(dev->iobase + RTI800_ADCLO);
225		data[i] |= (0xf & inb(dev->iobase + RTI800_ADCHI)) << 8;
226
227		if (devpriv->adc_coding == adc_2comp) {
228			data[i] ^= 0x800;
229		}
230	}
231
232	return i;
233}
234
235static int rti800_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
236	struct comedi_insn *insn, unsigned int *data)
237{
238	int i;
239	int chan = CR_CHAN(insn->chanspec);
240
241	for (i = 0; i < insn->n; i++)
242		data[i] = devpriv->ao_readback[chan];
243
244	return i;
245}
246
247static int rti800_ao_insn_write(struct comedi_device *dev, struct comedi_subdevice *s,
248	struct comedi_insn *insn, unsigned int *data)
249{
250	int chan = CR_CHAN(insn->chanspec);
251	int d;
252	int i;
253
254	for (i = 0; i < insn->n; i++) {
255		devpriv->ao_readback[chan] = d = data[i];
256		if (devpriv->dac0_coding == dac_2comp) {
257			d ^= 0x800;
258		}
259		outb(d & 0xff,
260			dev->iobase + (chan ? RTI800_DAC1LO : RTI800_DAC0LO));
261		outb(d >> 8,
262			dev->iobase + (chan ? RTI800_DAC1HI : RTI800_DAC0HI));
263	}
264	return i;
265}
266
267static int rti800_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
268	struct comedi_insn *insn, unsigned int *data)
269{
270	if (insn->n != 2)
271		return -EINVAL;
272	data[1] = inb(dev->iobase + RTI800_DI);
273	return 2;
274}
275
276static int rti800_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
277	struct comedi_insn *insn, unsigned int *data)
278{
279	if (insn->n != 2)
280		return -EINVAL;
281
282	if (data[0]) {
283		s->state &= ~data[0];
284		s->state |= data[0] & data[1];
285		/* Outputs are inverted... */
286		outb(s->state ^ 0xff, dev->iobase + RTI800_DO);
287	}
288
289	data[1] = s->state;
290
291	return 2;
292}
293
294/*
295   options[0] - I/O port
296   options[1] - irq
297   options[2] - a/d mux
298   	0=differential, 1=pseudodiff, 2=single
299   options[3] - a/d range
300   	0=bipolar10, 1=bipolar5, 2=unipolar10
301   options[4] - a/d coding
302   	0=2's comp, 1=straight binary
303   options[5] - dac0 range
304   	0=bipolar10, 1=unipolar10
305   options[6] - dac0 coding
306   	0=2's comp, 1=straight binary
307   options[7] - dac1 range
308   options[8] - dac1 coding
309 */
310
311static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
312{
313	unsigned int irq;
314	unsigned long iobase;
315	int ret;
316	struct comedi_subdevice *s;
317
318	iobase = it->options[0];
319	printk("comedi%d: rti800: 0x%04lx ", dev->minor, iobase);
320	if (!request_region(iobase, RTI800_SIZE, "rti800")) {
321		printk("I/O port conflict\n");
322		return -EIO;
323	}
324	dev->iobase = iobase;
325
326#ifdef DEBUG
327	printk("fingerprint=%x,%x,%x,%x,%x ",
328		inb(dev->iobase + 0),
329		inb(dev->iobase + 1),
330		inb(dev->iobase + 2),
331		inb(dev->iobase + 3), inb(dev->iobase + 4));
332#endif
333
334	outb(0, dev->iobase + RTI800_CSR);
335	inb(dev->iobase + RTI800_ADCHI);
336	outb(0, dev->iobase + RTI800_CLRFLAGS);
337
338	irq = it->options[1];
339	if (irq) {
340		printk("( irq = %u )", irq);
341		ret = comedi_request_irq(irq, rti800_interrupt, 0,
342					 "rti800", dev);
343		if (ret < 0) {
344			printk(" Failed to allocate IRQ\n");
345			return ret;
346		}
347		dev->irq = irq;
348	} else {
349		printk("( no irq )");
350	}
351
352	dev->board_name = this_board->name;
353
354	ret = alloc_subdevices(dev, 4);
355	if (ret < 0)
356		return ret;
357
358	ret = alloc_private(dev, sizeof(struct rti800_private));
359	if (ret < 0)
360		return ret;
361
362	devpriv->adc_mux = it->options[2];
363	devpriv->adc_range = it->options[3];
364	devpriv->adc_coding = it->options[4];
365	devpriv->dac0_range = it->options[5];
366	devpriv->dac0_coding = it->options[6];
367	devpriv->dac1_range = it->options[7];
368	devpriv->dac1_coding = it->options[8];
369	devpriv->muxgain_bits = -1;
370
371	s = dev->subdevices + 0;
372	/* ai subdevice */
373	s->type = COMEDI_SUBD_AI;
374	s->subdev_flags = SDF_READABLE | SDF_GROUND;
375	s->n_chan = (devpriv->adc_mux ? 16 : 8);
376	s->insn_read = rti800_ai_insn_read;
377	s->maxdata = 0xfff;
378	switch (devpriv->adc_range) {
379	case adc_bipolar10:
380		s->range_table = &range_rti800_ai_10_bipolar;
381		break;
382	case adc_bipolar5:
383		s->range_table = &range_rti800_ai_5_bipolar;
384		break;
385	case adc_unipolar10:
386		s->range_table = &range_rti800_ai_unipolar;
387		break;
388	}
389
390	s++;
391	if (this_board->has_ao) {
392		/* ao subdevice (only on rti815) */
393		s->type = COMEDI_SUBD_AO;
394		s->subdev_flags = SDF_WRITABLE;
395		s->n_chan = 2;
396		s->insn_read = rti800_ao_insn_read;
397		s->insn_write = rti800_ao_insn_write;
398		s->maxdata = 0xfff;
399		s->range_table_list = devpriv->ao_range_type_list;
400		switch (devpriv->dac0_range) {
401		case dac_bipolar10:
402			devpriv->ao_range_type_list[0] = &range_bipolar10;
403			break;
404		case dac_unipolar10:
405			devpriv->ao_range_type_list[0] = &range_unipolar10;
406			break;
407		}
408		switch (devpriv->dac1_range) {
409		case dac_bipolar10:
410			devpriv->ao_range_type_list[1] = &range_bipolar10;
411			break;
412		case dac_unipolar10:
413			devpriv->ao_range_type_list[1] = &range_unipolar10;
414			break;
415		}
416	} else {
417		s->type = COMEDI_SUBD_UNUSED;
418	}
419
420	s++;
421	/* di */
422	s->type = COMEDI_SUBD_DI;
423	s->subdev_flags = SDF_READABLE;
424	s->n_chan = 8;
425	s->insn_bits = rti800_di_insn_bits;
426	s->maxdata = 1;
427	s->range_table = &range_digital;
428
429	s++;
430	/* do */
431	s->type = COMEDI_SUBD_DO;
432	s->subdev_flags = SDF_WRITABLE;
433	s->n_chan = 8;
434	s->insn_bits = rti800_do_insn_bits;
435	s->maxdata = 1;
436	s->range_table = &range_digital;
437
438/* don't yet know how to deal with counter/timers */
439#if 0
440	s++;
441	/* do */
442	s->type = COMEDI_SUBD_TIMER;
443#endif
444
445	printk("\n");
446
447	return 0;
448}
449
450static int rti800_detach(struct comedi_device *dev)
451{
452	printk("comedi%d: rti800: remove\n", dev->minor);
453
454	if (dev->iobase)
455		release_region(dev->iobase, RTI800_SIZE);
456
457	if (dev->irq)
458		comedi_free_irq(dev->irq, dev);
459
460	return 0;
461}
462