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