• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /*
24 Driver: rti800
25 Description: Analog Devices RTI-800/815
26 Author: ds
27 Status: unknown
28 Updated: Fri, 05 Sep 2008 14:50:44 +0100
29 Devices: [Analog Devices] RTI-800 (rti800), RTI-815 (rti815)
30 
31 Configuration 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 
100 static 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 
112 static 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 
124 static 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 
135 struct rti800_board {
136 
137 	const char *name;
138 	int has_ao;
139 };
140 
141 static 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 
148 static int rti800_attach(struct comedi_device *dev,
149 			 struct comedi_devconfig *it);
150 static int rti800_detach(struct comedi_device *dev);
151 static 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 
driver_rti800_init_module(void)161 static int __init driver_rti800_init_module(void)
162 {
163 	return comedi_driver_register(&driver_rti800);
164 }
165 
driver_rti800_cleanup_module(void)166 static void __exit driver_rti800_cleanup_module(void)
167 {
168 	comedi_driver_unregister(&driver_rti800);
169 }
170 
171 module_init(driver_rti800_init_module);
172 module_exit(driver_rti800_cleanup_module);
173 
174 static irqreturn_t rti800_interrupt(int irq, void *dev);
175 
176 struct 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 
rti800_interrupt(int irq,void * dev)201 static irqreturn_t rti800_interrupt(int irq, void *dev)
202 {
203 	return IRQ_HANDLED;
204 }
205 
206 /* settling delay times in usec for different gains */
207 static const int gaindelay[] = { 10, 20, 40, 80 };
208 
rti800_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)209 static 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 
rti800_ao_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)261 static 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 
rti800_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)274 static 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 
rti800_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)295 static 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 
rti800_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)305 static 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 
rti800_attach(struct comedi_device * dev,struct comedi_devconfig * it)341 static 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 
rti800_detach(struct comedi_device * dev)477 static 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 
490 MODULE_AUTHOR("Comedi http://www.comedi.org");
491 MODULE_DESCRIPTION("Comedi low-level driver");
492 MODULE_LICENSE("GPL");
493