• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  comedi/drivers/das08.c
3  *  comedi driver for common DAS08 support (used by ISA/PCI/PCMCIA drivers)
4  *
5  *  COMEDI - Linux Control and Measurement Device Interface
6  *  Copyright (C) 2000 David A. Schleef <ds@schleef.org>
7  *  Copyright (C) 2001,2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net>
8  *  Copyright (C) 2004 Salvador E. Tropea <set@users.sf.net> <set@ieee.org>
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  *****************************************************************
25  */
26 
27 /*
28  * Driver: das08
29  * Description: DAS-08 compatible boards
30  * Devices: various, see das08_isa, das08_cs, and das08_pci drivers
31  * Author: Warren Jasper, ds, Frank Hess
32  * Updated: Fri, 31 Aug 2012 19:19:06 +0100
33  * Status: works
34  *
35  * This driver is used by the das08_isa, das08_cs, and das08_pci
36  * drivers to provide the common support for the DAS-08 hardware.
37  *
38  * The driver doesn't support asynchronous commands, since the
39  * cheap das08 hardware doesn't really support them.
40  */
41 
42 #include <linux/delay.h>
43 
44 #include "../comedidev.h"
45 
46 #include "8255.h"
47 #include "8253.h"
48 #include "das08.h"
49 
50 /*
51     cio-das08.pdf
52 
53   "isa-das08"
54 
55   0	a/d bits 0-3		start 8 bit
56   1	a/d bits 4-11		start 12 bit
57   2	eoc, ip1-3, irq, mux	op1-4, inte, mux
58   3	unused			unused
59   4567	8254
60   89ab	8255
61 
62   requires hard-wiring for async ai
63 
64 */
65 
66 #define DAS08_LSB		0
67 #define DAS08_MSB		1
68 #define DAS08_TRIG_12BIT	1
69 #define DAS08_STATUS		2
70 #define   DAS08_EOC			(1<<7)
71 #define   DAS08_IRQ			(1<<3)
72 #define   DAS08_IP(x)			(((x)>>4)&0x7)
73 #define DAS08_CONTROL		2
74 #define   DAS08_MUX_MASK	0x7
75 #define   DAS08_MUX(x)		((x) & DAS08_MUX_MASK)
76 #define   DAS08_INTE			(1<<3)
77 #define   DAS08_DO_MASK		0xf0
78 #define   DAS08_OP(x)		(((x) << 4) & DAS08_DO_MASK)
79 
80 /*
81     cio-das08jr.pdf
82 
83   "das08/jr-ao"
84 
85   0	a/d bits 0-3		unused
86   1	a/d bits 4-11		start 12 bit
87   2	eoc, mux		mux
88   3	di			do
89   4	unused			ao0_lsb
90   5	unused			ao0_msb
91   6	unused			ao1_lsb
92   7	unused			ao1_msb
93 
94 */
95 
96 #define DAS08JR_DIO		3
97 #define DAS08JR_AO_LSB(x)	((x) ? 6 : 4)
98 #define DAS08JR_AO_MSB(x)	((x) ? 7 : 5)
99 
100 /*
101     cio-das08_aox.pdf
102 
103   "das08-aoh"
104   "das08-aol"
105   "das08-aom"
106 
107   0	a/d bits 0-3		start 8 bit
108   1	a/d bits 4-11		start 12 bit
109   2	eoc, ip1-3, irq, mux	op1-4, inte, mux
110   3	mux, gain status	gain control
111   4567	8254
112   8	unused			ao0_lsb
113   9	unused			ao0_msb
114   a	unused			ao1_lsb
115   b	unused			ao1_msb
116   89ab
117   cdef	8255
118 */
119 
120 #define DAS08AO_GAIN_CONTROL	3
121 #define DAS08AO_GAIN_STATUS	3
122 
123 #define DAS08AO_AO_LSB(x)	((x) ? 0xa : 8)
124 #define DAS08AO_AO_MSB(x)	((x) ? 0xb : 9)
125 #define DAS08AO_AO_UPDATE	8
126 
127 /* gainlist same as _pgx_ below */
128 
129 static const struct comedi_lrange range_das08_pgl = { 9, {
130 							  BIP_RANGE(10),
131 							  BIP_RANGE(5),
132 							  BIP_RANGE(2.5),
133 							  BIP_RANGE(1.25),
134 							  BIP_RANGE(0.625),
135 							  UNI_RANGE(10),
136 							  UNI_RANGE(5),
137 							  UNI_RANGE(2.5),
138 							  UNI_RANGE(1.25)
139 							  }
140 };
141 
142 static const struct comedi_lrange range_das08_pgh = { 12, {
143 							   BIP_RANGE(10),
144 							   BIP_RANGE(5),
145 							   BIP_RANGE(1),
146 							   BIP_RANGE(0.5),
147 							   BIP_RANGE(0.1),
148 							   BIP_RANGE(0.05),
149 							   BIP_RANGE(0.01),
150 							   BIP_RANGE(0.005),
151 							   UNI_RANGE(10),
152 							   UNI_RANGE(1),
153 							   UNI_RANGE(0.1),
154 							   UNI_RANGE(0.01),
155 							   }
156 };
157 
158 static const struct comedi_lrange range_das08_pgm = { 9, {
159 							  BIP_RANGE(10),
160 							  BIP_RANGE(5),
161 							  BIP_RANGE(0.5),
162 							  BIP_RANGE(0.05),
163 							  BIP_RANGE(0.01),
164 							  UNI_RANGE(10),
165 							  UNI_RANGE(1),
166 							  UNI_RANGE(0.1),
167 							  UNI_RANGE(0.01)
168 							  }
169 };				/*
170 				   cio-das08jr.pdf
171 
172 				   "das08/jr-ao"
173 
174 				   0 a/d bits 0-3            unused
175 				   1 a/d bits 4-11           start 12 bit
176 				   2 eoc, mux                mux
177 				   3 di                      do
178 				   4 unused                  ao0_lsb
179 				   5 unused                  ao0_msb
180 				   6 unused                  ao1_lsb
181 				   7 unused                  ao1_msb
182 
183 				 */
184 
185 static const struct comedi_lrange *const das08_ai_lranges[] = {
186 	&range_unknown,
187 	&range_bipolar5,
188 	&range_das08_pgh,
189 	&range_das08_pgl,
190 	&range_das08_pgm,
191 };
192 
193 static const int das08_pgh_gainlist[] = {
194 	8, 0, 10, 2, 12, 4, 14, 6, 1, 3, 5, 7
195 };
196 static const int das08_pgl_gainlist[] = { 8, 0, 2, 4, 6, 1, 3, 5, 7 };
197 static const int das08_pgm_gainlist[] = { 8, 0, 10, 12, 14, 9, 11, 13, 15 };
198 
199 static const int *const das08_gainlists[] = {
200 	NULL,
201 	NULL,
202 	das08_pgh_gainlist,
203 	das08_pgl_gainlist,
204 	das08_pgm_gainlist,
205 };
206 
207 #define TIMEOUT 100000
208 
das08_ai_rinsn(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)209 static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
210 			  struct comedi_insn *insn, unsigned int *data)
211 {
212 	const struct das08_board_struct *thisboard = comedi_board(dev);
213 	struct das08_private_struct *devpriv = dev->private;
214 	int i, n;
215 	int chan;
216 	int range;
217 	int lsb, msb;
218 
219 	chan = CR_CHAN(insn->chanspec);
220 	range = CR_RANGE(insn->chanspec);
221 
222 	/* clear crap */
223 	inb(dev->iobase + DAS08_LSB);
224 	inb(dev->iobase + DAS08_MSB);
225 
226 	/* set multiplexer */
227 	/*  lock to prevent race with digital output */
228 	spin_lock(&dev->spinlock);
229 	devpriv->do_mux_bits &= ~DAS08_MUX_MASK;
230 	devpriv->do_mux_bits |= DAS08_MUX(chan);
231 	outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
232 	spin_unlock(&dev->spinlock);
233 
234 	if (s->range_table->length > 1) {
235 		/* set gain/range */
236 		range = CR_RANGE(insn->chanspec);
237 		outb(devpriv->pg_gainlist[range],
238 		     dev->iobase + DAS08AO_GAIN_CONTROL);
239 	}
240 
241 	for (n = 0; n < insn->n; n++) {
242 		/* clear over-range bits for 16-bit boards */
243 		if (thisboard->ai_nbits == 16)
244 			if (inb(dev->iobase + DAS08_MSB) & 0x80)
245 				dev_info(dev->class_dev, "over-range\n");
246 
247 		/* trigger conversion */
248 		outb_p(0, dev->iobase + DAS08_TRIG_12BIT);
249 
250 		for (i = 0; i < TIMEOUT; i++) {
251 			if (!(inb(dev->iobase + DAS08_STATUS) & DAS08_EOC))
252 				break;
253 		}
254 		if (i == TIMEOUT) {
255 			dev_err(dev->class_dev, "timeout\n");
256 			return -ETIME;
257 		}
258 		msb = inb(dev->iobase + DAS08_MSB);
259 		lsb = inb(dev->iobase + DAS08_LSB);
260 		if (thisboard->ai_encoding == das08_encode12) {
261 			data[n] = (lsb >> 4) | (msb << 4);
262 		} else if (thisboard->ai_encoding == das08_pcm_encode12) {
263 			data[n] = (msb << 8) + lsb;
264 		} else if (thisboard->ai_encoding == das08_encode16) {
265 			/* FPOS 16-bit boards are sign-magnitude */
266 			if (msb & 0x80)
267 				data[n] = (1 << 15) | lsb | ((msb & 0x7f) << 8);
268 			else
269 				data[n] = (1 << 15) - (lsb | (msb & 0x7f) << 8);
270 		} else {
271 			comedi_error(dev, "bug! unknown ai encoding");
272 			return -1;
273 		}
274 	}
275 
276 	return n;
277 }
278 
das08_di_rbits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)279 static int das08_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
280 			  struct comedi_insn *insn, unsigned int *data)
281 {
282 	data[0] = 0;
283 	data[1] = DAS08_IP(inb(dev->iobase + DAS08_STATUS));
284 
285 	return insn->n;
286 }
287 
das08_do_wbits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)288 static int das08_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
289 			  struct comedi_insn *insn, unsigned int *data)
290 {
291 	struct das08_private_struct *devpriv = dev->private;
292 	int wbits;
293 
294 	/*  get current settings of digital output lines */
295 	wbits = (devpriv->do_mux_bits >> 4) & 0xf;
296 	/*  null bits we are going to set */
297 	wbits &= ~data[0];
298 	/*  set new bit values */
299 	wbits |= data[0] & data[1];
300 	/*  remember digital output bits */
301 	/*  prevent race with setting of analog input mux */
302 	spin_lock(&dev->spinlock);
303 	devpriv->do_mux_bits &= ~DAS08_DO_MASK;
304 	devpriv->do_mux_bits |= DAS08_OP(wbits);
305 	outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
306 	spin_unlock(&dev->spinlock);
307 
308 	data[1] = wbits;
309 
310 	return insn->n;
311 }
312 
das08jr_di_rbits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)313 static int das08jr_di_rbits(struct comedi_device *dev,
314 			    struct comedi_subdevice *s,
315 			    struct comedi_insn *insn, unsigned int *data)
316 {
317 	data[0] = 0;
318 	data[1] = inb(dev->iobase + DAS08JR_DIO);
319 
320 	return insn->n;
321 }
322 
das08jr_do_wbits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)323 static int das08jr_do_wbits(struct comedi_device *dev,
324 			    struct comedi_subdevice *s,
325 			    struct comedi_insn *insn, unsigned int *data)
326 {
327 	struct das08_private_struct *devpriv = dev->private;
328 
329 	/*  null bits we are going to set */
330 	devpriv->do_bits &= ~data[0];
331 	/*  set new bit values */
332 	devpriv->do_bits |= data[0] & data[1];
333 	outb(devpriv->do_bits, dev->iobase + DAS08JR_DIO);
334 
335 	data[1] = devpriv->do_bits;
336 
337 	return insn->n;
338 }
339 
das08_ao_set_data(struct comedi_device * dev,unsigned int chan,unsigned int data)340 static void das08_ao_set_data(struct comedi_device *dev,
341 			      unsigned int chan, unsigned int data)
342 {
343 	const struct das08_board_struct *thisboard = comedi_board(dev);
344 	struct das08_private_struct *devpriv = dev->private;
345 	unsigned char lsb;
346 	unsigned char msb;
347 
348 	lsb = data & 0xff;
349 	msb = (data >> 8) & 0xff;
350 	if (thisboard->is_jr) {
351 		outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan));
352 		outb(msb, dev->iobase + DAS08JR_AO_MSB(chan));
353 		/* load DACs */
354 		inb(dev->iobase + DAS08JR_DIO);
355 	} else {
356 		outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan));
357 		outb(msb, dev->iobase + DAS08AO_AO_MSB(chan));
358 		/* load DACs */
359 		inb(dev->iobase + DAS08AO_AO_UPDATE);
360 	}
361 	devpriv->ao_readback[chan] = data;
362 }
363 
das08_ao_initialize(struct comedi_device * dev,struct comedi_subdevice * s)364 static void das08_ao_initialize(struct comedi_device *dev,
365 				struct comedi_subdevice *s)
366 {
367 	int n;
368 	unsigned int data;
369 
370 	data = s->maxdata / 2;	/* should be about 0 volts */
371 	for (n = 0; n < s->n_chan; n++)
372 		das08_ao_set_data(dev, n, data);
373 }
374 
das08_ao_winsn(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)375 static int das08_ao_winsn(struct comedi_device *dev,
376 			  struct comedi_subdevice *s,
377 			  struct comedi_insn *insn, unsigned int *data)
378 {
379 	unsigned int n;
380 	unsigned int chan;
381 
382 	chan = CR_CHAN(insn->chanspec);
383 
384 	for (n = 0; n < insn->n; n++)
385 		das08_ao_set_data(dev, chan, *data);
386 
387 	return n;
388 }
389 
das08_ao_rinsn(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)390 static int das08_ao_rinsn(struct comedi_device *dev,
391 			  struct comedi_subdevice *s,
392 			  struct comedi_insn *insn, unsigned int *data)
393 {
394 	struct das08_private_struct *devpriv = dev->private;
395 	unsigned int n;
396 	unsigned int chan;
397 
398 	chan = CR_CHAN(insn->chanspec);
399 
400 	for (n = 0; n < insn->n; n++)
401 		data[n] = devpriv->ao_readback[chan];
402 
403 	return n;
404 }
405 
i8254_initialize(struct comedi_device * dev)406 static void i8254_initialize(struct comedi_device *dev)
407 {
408 	const struct das08_board_struct *thisboard = comedi_board(dev);
409 	unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
410 	unsigned int mode = I8254_MODE0 | I8254_BINARY;
411 	int i;
412 
413 	for (i = 0; i < 3; ++i)
414 		i8254_set_mode(i8254_iobase, 0, i, mode);
415 }
416 
das08_counter_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)417 static int das08_counter_read(struct comedi_device *dev,
418 			      struct comedi_subdevice *s,
419 			      struct comedi_insn *insn, unsigned int *data)
420 {
421 	const struct das08_board_struct *thisboard = comedi_board(dev);
422 	unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
423 	int chan = insn->chanspec;
424 
425 	data[0] = i8254_read(i8254_iobase, 0, chan);
426 	return 1;
427 }
428 
das08_counter_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)429 static int das08_counter_write(struct comedi_device *dev,
430 			       struct comedi_subdevice *s,
431 			       struct comedi_insn *insn, unsigned int *data)
432 {
433 	const struct das08_board_struct *thisboard = comedi_board(dev);
434 	unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
435 	int chan = insn->chanspec;
436 
437 	i8254_write(i8254_iobase, 0, chan, data[0]);
438 	return 1;
439 }
440 
das08_counter_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)441 static int das08_counter_config(struct comedi_device *dev,
442 				struct comedi_subdevice *s,
443 				struct comedi_insn *insn, unsigned int *data)
444 {
445 	const struct das08_board_struct *thisboard = comedi_board(dev);
446 	unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
447 	int chan = insn->chanspec;
448 
449 	switch (data[0]) {
450 	case INSN_CONFIG_SET_COUNTER_MODE:
451 		i8254_set_mode(i8254_iobase, 0, chan, data[1]);
452 		break;
453 	case INSN_CONFIG_8254_READ_STATUS:
454 		data[1] = i8254_status(i8254_iobase, 0, chan);
455 		break;
456 	default:
457 		return -EINVAL;
458 		break;
459 	}
460 	return 2;
461 }
462 
das08_common_attach(struct comedi_device * dev,unsigned long iobase)463 int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
464 {
465 	const struct das08_board_struct *thisboard = comedi_board(dev);
466 	struct das08_private_struct *devpriv = dev->private;
467 	struct comedi_subdevice *s;
468 	int ret;
469 
470 	dev->iobase = iobase;
471 
472 	dev->board_name = thisboard->name;
473 
474 	ret = comedi_alloc_subdevices(dev, 6);
475 	if (ret)
476 		return ret;
477 
478 	s = &dev->subdevices[0];
479 	/* ai */
480 	if (thisboard->ai_nbits) {
481 		s->type = COMEDI_SUBD_AI;
482 		/* XXX some boards actually have differential
483 		 * inputs instead of single ended.
484 		 * The driver does nothing with arefs though,
485 		 * so it's no big deal.
486 		 */
487 		s->subdev_flags = SDF_READABLE | SDF_GROUND;
488 		s->n_chan = 8;
489 		s->maxdata = (1 << thisboard->ai_nbits) - 1;
490 		s->range_table = das08_ai_lranges[thisboard->ai_pg];
491 		s->insn_read = das08_ai_rinsn;
492 		devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg];
493 	} else {
494 		s->type = COMEDI_SUBD_UNUSED;
495 	}
496 
497 	s = &dev->subdevices[1];
498 	/* ao */
499 	if (thisboard->ao_nbits) {
500 		s->type = COMEDI_SUBD_AO;
501 		s->subdev_flags = SDF_WRITABLE;
502 		s->n_chan = 2;
503 		s->maxdata = (1 << thisboard->ao_nbits) - 1;
504 		s->range_table = &range_bipolar5;
505 		s->insn_write = das08_ao_winsn;
506 		s->insn_read = das08_ao_rinsn;
507 		das08_ao_initialize(dev, s);
508 	} else {
509 		s->type = COMEDI_SUBD_UNUSED;
510 	}
511 
512 	s = &dev->subdevices[2];
513 	/* di */
514 	if (thisboard->di_nchan) {
515 		s->type = COMEDI_SUBD_DI;
516 		s->subdev_flags = SDF_READABLE;
517 		s->n_chan = thisboard->di_nchan;
518 		s->maxdata = 1;
519 		s->range_table = &range_digital;
520 		s->insn_bits =
521 			thisboard->is_jr ? das08jr_di_rbits : das08_di_rbits;
522 	} else {
523 		s->type = COMEDI_SUBD_UNUSED;
524 	}
525 
526 	s = &dev->subdevices[3];
527 	/* do */
528 	if (thisboard->do_nchan) {
529 		s->type = COMEDI_SUBD_DO;
530 		s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
531 		s->n_chan = thisboard->do_nchan;
532 		s->maxdata = 1;
533 		s->range_table = &range_digital;
534 		s->insn_bits =
535 			thisboard->is_jr ? das08jr_do_wbits : das08_do_wbits;
536 	} else {
537 		s->type = COMEDI_SUBD_UNUSED;
538 	}
539 
540 	s = &dev->subdevices[4];
541 	/* 8255 */
542 	if (thisboard->i8255_offset != 0) {
543 		subdev_8255_init(dev, s, NULL, (unsigned long)(dev->iobase +
544 							       thisboard->
545 							       i8255_offset));
546 	} else {
547 		s->type = COMEDI_SUBD_UNUSED;
548 	}
549 
550 	s = &dev->subdevices[5];
551 	/* 8254 */
552 	if (thisboard->i8254_offset != 0) {
553 		s->type = COMEDI_SUBD_COUNTER;
554 		s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
555 		s->n_chan = 3;
556 		s->maxdata = 0xFFFF;
557 		s->insn_read = das08_counter_read;
558 		s->insn_write = das08_counter_write;
559 		s->insn_config = das08_counter_config;
560 		i8254_initialize(dev);
561 	} else {
562 		s->type = COMEDI_SUBD_UNUSED;
563 	}
564 
565 	return 0;
566 }
567 EXPORT_SYMBOL_GPL(das08_common_attach);
568 
das08_common_detach(struct comedi_device * dev)569 void das08_common_detach(struct comedi_device *dev)
570 {
571 	comedi_spriv_free(dev, 4);
572 }
573 EXPORT_SYMBOL_GPL(das08_common_detach);
574 
das08_init(void)575 static int __init das08_init(void)
576 {
577 	return 0;
578 }
579 module_init(das08_init);
580 
das08_exit(void)581 static void __exit das08_exit(void)
582 {
583 }
584 module_exit(das08_exit);
585 
586 MODULE_AUTHOR("Comedi http://www.comedi.org");
587 MODULE_DESCRIPTION("Comedi low-level driver");
588 MODULE_LICENSE("GPL");
589