1/*
2 * das6402.c
3 * Comedi driver for DAS6402 compatible boards
4 * Copyright(c) 2014 H Hartley Sweeten <hsweeten@visionengravers.com>
5 *
6 * Rewrite of an experimental driver by:
7 * Copyright (C) 1999 Oystein Svendsen <svendsen@pvv.org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 */
19
20/*
21 * Driver: das6402
22 * Description: Keithley Metrabyte DAS6402 (& compatibles)
23 * Devices: (Keithley Metrabyte) DAS6402-12 (das6402-12)
24 *	    (Keithley Metrabyte) DAS6402-16 (das6402-16)
25 * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
26 * Updated: Fri, 14 Mar 2014 10:18:43 -0700
27 * Status: unknown
28 *
29 * Configuration Options:
30 *   [0] - I/O base address
31 *   [1] - IRQ (optional, needed for async command support)
32 */
33
34#include <linux/module.h>
35#include <linux/interrupt.h>
36
37#include "../comedidev.h"
38#include "8253.h"
39
40/*
41 * Register I/O map
42 */
43#define DAS6402_AI_DATA_REG		0x00
44#define DAS6402_AI_MUX_REG		0x02
45#define DAS6402_AI_MUX_LO(x)		(((x) & 0x3f) << 0)
46#define DAS6402_AI_MUX_HI(x)		(((x) & 0x3f) << 8)
47#define DAS6402_DI_DO_REG		0x03
48#define DAS6402_AO_DATA_REG(x)		(0x04 + ((x) * 2))
49#define DAS6402_AO_LSB_REG(x)		(0x04 + ((x) * 2))
50#define DAS6402_AO_MSB_REG(x)		(0x05 + ((x) * 2))
51#define DAS6402_STATUS_REG		0x08
52#define DAS6402_STATUS_FFNE		(1 << 0)
53#define DAS6402_STATUS_FHALF		(1 << 1)
54#define DAS6402_STATUS_FFULL		(1 << 2)
55#define DAS6402_STATUS_XINT		(1 << 3)
56#define DAS6402_STATUS_INT		(1 << 4)
57#define DAS6402_STATUS_XTRIG		(1 << 5)
58#define DAS6402_STATUS_INDGT		(1 << 6)
59#define DAS6402_STATUS_10MHZ		(1 << 7)
60#define DAS6402_STATUS_W_CLRINT		(1 << 0)
61#define DAS6402_STATUS_W_CLRXTR		(1 << 1)
62#define DAS6402_STATUS_W_CLRXIN		(1 << 2)
63#define DAS6402_STATUS_W_EXTEND		(1 << 4)
64#define DAS6402_STATUS_W_ARMED		(1 << 5)
65#define DAS6402_STATUS_W_POSTMODE	(1 << 6)
66#define DAS6402_STATUS_W_10MHZ		(1 << 7)
67#define DAS6402_CTRL_REG		0x09
68#define DAS6402_CTRL_SOFT_TRIG		(0 << 0)
69#define DAS6402_CTRL_EXT_FALL_TRIG	(1 << 0)
70#define DAS6402_CTRL_EXT_RISE_TRIG	(2 << 0)
71#define DAS6402_CTRL_PACER_TRIG		(3 << 0)
72#define DAS6402_CTRL_BURSTEN		(1 << 2)
73#define DAS6402_CTRL_XINTE		(1 << 3)
74#define DAS6402_CTRL_IRQ(x)		((x) << 4)
75#define DAS6402_CTRL_INTE		(1 << 7)
76#define DAS6402_TRIG_REG		0x0a
77#define DAS6402_TRIG_TGEN		(1 << 0)
78#define DAS6402_TRIG_TGSEL		(1 << 1)
79#define DAS6402_TRIG_TGPOL		(1 << 2)
80#define DAS6402_TRIG_PRETRIG		(1 << 3)
81#define DAS6402_AO_RANGE(_chan, _range)	((_range) << ((_chan) ? 6 : 4))
82#define DAS6402_AO_RANGE_MASK(_chan)	(3 << ((_chan) ? 6 : 4))
83#define DAS6402_MODE_REG		0x0b
84#define DAS6402_MODE_RANGE(x)		((x) << 0)
85#define DAS6402_MODE_POLLED		(0 << 2)
86#define DAS6402_MODE_FIFONEPTY		(1 << 2)
87#define DAS6402_MODE_FIFOHFULL		(2 << 2)
88#define DAS6402_MODE_EOB		(3 << 2)
89#define DAS6402_MODE_ENHANCED		(1 << 4)
90#define DAS6402_MODE_SE			(1 << 5)
91#define DAS6402_MODE_UNI		(1 << 6)
92#define DAS6402_MODE_DMA1		(0 << 7)
93#define DAS6402_MODE_DMA3		(1 << 7)
94#define DAS6402_TIMER_BASE		0x0c
95
96static const struct comedi_lrange das6402_ai_ranges = {
97	8, {
98		BIP_RANGE(10),
99		BIP_RANGE(5),
100		BIP_RANGE(2.5),
101		BIP_RANGE(1.25),
102		UNI_RANGE(10),
103		UNI_RANGE(5),
104		UNI_RANGE(2.5),
105		UNI_RANGE(1.25)
106	}
107};
108
109/*
110 * Analog output ranges are programmable on the DAS6402/12.
111 * For the DAS6402/16 the range bits have no function, the
112 * DAC ranges are selected by switches on the board.
113 */
114static const struct comedi_lrange das6402_ao_ranges = {
115	4, {
116		BIP_RANGE(5),
117		BIP_RANGE(10),
118		UNI_RANGE(5),
119		UNI_RANGE(10)
120	}
121};
122
123struct das6402_boardinfo {
124	const char *name;
125	unsigned int maxdata;
126};
127
128static struct das6402_boardinfo das6402_boards[] = {
129	{
130		.name		= "das6402-12",
131		.maxdata	= 0x0fff,
132	}, {
133		.name		= "das6402-16",
134		.maxdata	= 0xffff,
135	},
136};
137
138struct das6402_private {
139	unsigned int irq;
140
141	unsigned int count;
142	unsigned int divider1;
143	unsigned int divider2;
144
145	unsigned int ao_range;
146};
147
148static void das6402_set_mode(struct comedi_device *dev,
149			     unsigned int mode)
150{
151	outb(DAS6402_MODE_ENHANCED | mode, dev->iobase + DAS6402_MODE_REG);
152}
153
154static void das6402_set_extended(struct comedi_device *dev,
155				 unsigned int val)
156{
157	outb(DAS6402_STATUS_W_EXTEND, dev->iobase + DAS6402_STATUS_REG);
158	outb(DAS6402_STATUS_W_EXTEND | val, dev->iobase + DAS6402_STATUS_REG);
159	outb(val, dev->iobase + DAS6402_STATUS_REG);
160}
161
162static void das6402_clear_all_interrupts(struct comedi_device *dev)
163{
164	outb(DAS6402_STATUS_W_CLRINT |
165	     DAS6402_STATUS_W_CLRXTR |
166	     DAS6402_STATUS_W_CLRXIN, dev->iobase + DAS6402_STATUS_REG);
167}
168
169static void das6402_ai_clear_eoc(struct comedi_device *dev)
170{
171	outb(DAS6402_STATUS_W_CLRINT, dev->iobase + DAS6402_STATUS_REG);
172}
173
174static void das6402_enable_counter(struct comedi_device *dev, bool load)
175{
176	struct das6402_private *devpriv = dev->private;
177	unsigned long timer_iobase = dev->iobase + DAS6402_TIMER_BASE;
178
179	if (load) {
180		i8254_set_mode(timer_iobase, 0, 0, I8254_MODE0 | I8254_BINARY);
181		i8254_set_mode(timer_iobase, 0, 1, I8254_MODE2 | I8254_BINARY);
182		i8254_set_mode(timer_iobase, 0, 2, I8254_MODE2 | I8254_BINARY);
183
184		i8254_write(timer_iobase, 0, 0, devpriv->count);
185		i8254_write(timer_iobase, 0, 1, devpriv->divider1);
186		i8254_write(timer_iobase, 0, 2, devpriv->divider2);
187
188	} else {
189		i8254_set_mode(timer_iobase, 0, 0, I8254_MODE0 | I8254_BINARY);
190		i8254_set_mode(timer_iobase, 0, 1, I8254_MODE0 | I8254_BINARY);
191		i8254_set_mode(timer_iobase, 0, 2, I8254_MODE0 | I8254_BINARY);
192	}
193}
194
195static irqreturn_t das6402_interrupt(int irq, void *d)
196{
197	struct comedi_device *dev = d;
198
199	das6402_clear_all_interrupts(dev);
200
201	return IRQ_HANDLED;
202}
203
204static int das6402_ai_cmd(struct comedi_device *dev,
205			  struct comedi_subdevice *s)
206{
207	return -EINVAL;
208}
209
210static int das6402_ai_cmdtest(struct comedi_device *dev,
211			      struct comedi_subdevice *s,
212			      struct comedi_cmd *cmd)
213{
214	return -EINVAL;
215}
216
217static int das6402_ai_cancel(struct comedi_device *dev,
218			     struct comedi_subdevice *s)
219{
220	outb(DAS6402_CTRL_SOFT_TRIG, dev->iobase + DAS6402_CTRL_REG);
221
222	return 0;
223}
224
225static void das6402_ai_soft_trig(struct comedi_device *dev)
226{
227	outw(0, dev->iobase + DAS6402_AI_DATA_REG);
228}
229
230static int das6402_ai_eoc(struct comedi_device *dev,
231			  struct comedi_subdevice *s,
232			  struct comedi_insn *insn,
233			  unsigned long context)
234{
235	unsigned int status;
236
237	status = inb(dev->iobase + DAS6402_STATUS_REG);
238	if (status & DAS6402_STATUS_FFNE)
239		return 0;
240	return -EBUSY;
241}
242
243static int das6402_ai_insn_read(struct comedi_device *dev,
244				struct comedi_subdevice *s,
245				struct comedi_insn *insn,
246				unsigned int *data)
247{
248	unsigned int chan = CR_CHAN(insn->chanspec);
249	unsigned int range = CR_RANGE(insn->chanspec);
250	unsigned int aref = CR_AREF(insn->chanspec);
251	unsigned int val;
252	int ret;
253	int i;
254
255	val = DAS6402_MODE_RANGE(range) | DAS6402_MODE_POLLED;
256	if (aref == AREF_DIFF) {
257		if (chan > s->n_chan / 2)
258			return -EINVAL;
259	} else {
260		val |= DAS6402_MODE_SE;
261	}
262	if (comedi_range_is_unipolar(s, range))
263		val |= DAS6402_MODE_UNI;
264
265	/* enable software conversion trigger */
266	outb(DAS6402_CTRL_SOFT_TRIG, dev->iobase + DAS6402_CTRL_REG);
267
268	das6402_set_mode(dev, val);
269
270	/* load the mux for single channel conversion */
271	outw(DAS6402_AI_MUX_HI(chan) | DAS6402_AI_MUX_LO(chan),
272	     dev->iobase + DAS6402_AI_MUX_REG);
273
274	for (i = 0; i < insn->n; i++) {
275		das6402_ai_clear_eoc(dev);
276		das6402_ai_soft_trig(dev);
277
278		ret = comedi_timeout(dev, s, insn, das6402_ai_eoc, 0);
279		if (ret)
280			break;
281
282		val = inw(dev->iobase + DAS6402_AI_DATA_REG);
283
284		if (s->maxdata == 0x0fff)
285			val >>= 4;
286
287		data[i] = val;
288	}
289
290	das6402_ai_clear_eoc(dev);
291
292	return insn->n;
293}
294
295static int das6402_ao_insn_write(struct comedi_device *dev,
296				 struct comedi_subdevice *s,
297				 struct comedi_insn *insn,
298				 unsigned int *data)
299{
300	struct das6402_private *devpriv = dev->private;
301	unsigned int chan = CR_CHAN(insn->chanspec);
302	unsigned int range = CR_RANGE(insn->chanspec);
303	unsigned int val;
304	int i;
305
306	/* set the range for this channel */
307	val = devpriv->ao_range;
308	val &= ~DAS6402_AO_RANGE_MASK(chan);
309	val |= DAS6402_AO_RANGE(chan, range);
310	if (val != devpriv->ao_range) {
311		devpriv->ao_range = val;
312		outb(val, dev->iobase + DAS6402_TRIG_REG);
313	}
314
315	/*
316	 * The DAS6402/16 has a jumper to select either individual
317	 * update (UPDATE) or simultaneous updating (XFER) of both
318	 * DAC's. In UPDATE mode, when the MSB is written, that DAC
319	 * is updated. In XFER mode, after both DAC's are loaded,
320	 * a read cycle of any DAC register will update both DAC's
321	 * simultaneously.
322	 *
323	 * If you have XFER mode enabled a (*insn_read) will need
324	 * to be performed in order to update the DAC's with the
325	 * last value written.
326	 */
327	for (i = 0; i < insn->n; i++) {
328		val = data[i];
329
330		s->readback[chan] = val;
331
332		if (s->maxdata == 0x0fff) {
333			/*
334			 * DAS6402/12 has the two 8-bit DAC registers, left
335			 * justified (the 4 LSB bits are don't care). Data
336			 * can be written as one word.
337			 */
338			val <<= 4;
339			outw(val, dev->iobase + DAS6402_AO_DATA_REG(chan));
340		} else {
341			/*
342			 * DAS6402/16 uses both 8-bit DAC registers and needs
343			 * to be written LSB then MSB.
344			 */
345			outb(val & 0xff,
346			     dev->iobase + DAS6402_AO_LSB_REG(chan));
347			outb((val >> 8) & 0xff,
348			     dev->iobase + DAS6402_AO_LSB_REG(chan));
349		}
350	}
351
352	return insn->n;
353}
354
355static int das6402_ao_insn_read(struct comedi_device *dev,
356				struct comedi_subdevice *s,
357				struct comedi_insn *insn,
358				unsigned int *data)
359{
360	unsigned int chan = CR_CHAN(insn->chanspec);
361
362	/*
363	 * If XFER mode is enabled, reading any DAC register
364	 * will update both DAC's simultaneously.
365	 */
366	inw(dev->iobase + DAS6402_AO_LSB_REG(chan));
367
368	return comedi_readback_insn_read(dev, s, insn, data);
369}
370
371static int das6402_di_insn_bits(struct comedi_device *dev,
372				struct comedi_subdevice *s,
373				struct comedi_insn *insn,
374				unsigned int *data)
375{
376	data[1] = inb(dev->iobase + DAS6402_DI_DO_REG);
377
378	return insn->n;
379}
380
381static int das6402_do_insn_bits(struct comedi_device *dev,
382				struct comedi_subdevice *s,
383				struct comedi_insn *insn,
384				unsigned int *data)
385{
386	if (comedi_dio_update_state(s, data))
387		outb(s->state, dev->iobase + DAS6402_DI_DO_REG);
388
389	data[1] = s->state;
390
391	return insn->n;
392}
393
394static void das6402_reset(struct comedi_device *dev)
395{
396	struct das6402_private *devpriv = dev->private;
397
398	/* enable "Enhanced" mode */
399	outb(DAS6402_MODE_ENHANCED, dev->iobase + DAS6402_MODE_REG);
400
401	/* enable 10MHz pacer clock */
402	das6402_set_extended(dev, DAS6402_STATUS_W_10MHZ);
403
404	/* enable software conversion trigger */
405	outb(DAS6402_CTRL_SOFT_TRIG, dev->iobase + DAS6402_CTRL_REG);
406
407	/* default ADC to single-ended unipolar 10V inputs */
408	das6402_set_mode(dev, DAS6402_MODE_RANGE(0) |
409			      DAS6402_MODE_POLLED |
410			      DAS6402_MODE_SE |
411			      DAS6402_MODE_UNI);
412
413	/* default mux for single channel conversion (channel 0) */
414	outw(DAS6402_AI_MUX_HI(0) | DAS6402_AI_MUX_LO(0),
415	     dev->iobase + DAS6402_AI_MUX_REG);
416
417	/* set both DAC's for unipolar 5V output range */
418	devpriv->ao_range = DAS6402_AO_RANGE(0, 2) | DAS6402_AO_RANGE(1, 2);
419	outb(devpriv->ao_range, dev->iobase + DAS6402_TRIG_REG);
420
421	/* set both DAC's to 0V */
422	outw(0, dev->iobase + DAS6402_AO_DATA_REG(0));
423	outw(0, dev->iobase + DAS6402_AO_DATA_REG(0));
424	inw(dev->iobase + DAS6402_AO_LSB_REG(0));
425
426	das6402_enable_counter(dev, false);
427
428	/* set all digital outputs low */
429	outb(0, dev->iobase + DAS6402_DI_DO_REG);
430
431	das6402_clear_all_interrupts(dev);
432}
433
434static int das6402_attach(struct comedi_device *dev,
435			  struct comedi_devconfig *it)
436{
437	const struct das6402_boardinfo *board = dev->board_ptr;
438	struct das6402_private *devpriv;
439	struct comedi_subdevice *s;
440	int ret;
441
442	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
443	if (!devpriv)
444		return -ENOMEM;
445
446	ret = comedi_request_region(dev, it->options[0], 0x10);
447	if (ret)
448		return ret;
449
450	das6402_reset(dev);
451
452	/* IRQs 2,3,5,6,7, 10,11,15 are valid for "enhanced" mode */
453	if ((1 << it->options[1]) & 0x8cec) {
454		ret = request_irq(it->options[1], das6402_interrupt, 0,
455				  dev->board_name, dev);
456		if (ret == 0) {
457			dev->irq = it->options[1];
458
459			switch (dev->irq) {
460			case 10:
461				devpriv->irq = 4;
462				break;
463			case 11:
464				devpriv->irq = 1;
465				break;
466			case 15:
467				devpriv->irq = 6;
468				break;
469			default:
470				devpriv->irq = dev->irq;
471				break;
472			}
473		}
474	}
475
476	ret = comedi_alloc_subdevices(dev, 4);
477	if (ret)
478		return ret;
479
480	/* Analog Input subdevice */
481	s = &dev->subdevices[0];
482	s->type		= COMEDI_SUBD_AI;
483	s->subdev_flags	= SDF_READABLE | SDF_GROUND | SDF_DIFF;
484	s->n_chan	= 64;
485	s->maxdata	= board->maxdata;
486	s->range_table	= &das6402_ai_ranges;
487	s->insn_read	= das6402_ai_insn_read;
488	if (dev->irq) {
489		dev->read_subdev = s;
490		s->subdev_flags	|= SDF_CMD_READ;
491		s->len_chanlist	= s->n_chan;
492		s->do_cmdtest	= das6402_ai_cmdtest;
493		s->do_cmd	= das6402_ai_cmd;
494		s->cancel	= das6402_ai_cancel;
495	}
496
497	/* Analog Output subdevice */
498	s = &dev->subdevices[1];
499	s->type		= COMEDI_SUBD_AO;
500	s->subdev_flags	= SDF_WRITEABLE;
501	s->n_chan	= 2;
502	s->maxdata	= board->maxdata;
503	s->range_table	= &das6402_ao_ranges;
504	s->insn_write	= das6402_ao_insn_write;
505	s->insn_read	= das6402_ao_insn_read;
506
507	ret = comedi_alloc_subdev_readback(s);
508	if (ret)
509		return ret;
510
511	/* Digital Input subdevice */
512	s = &dev->subdevices[2];
513	s->type		= COMEDI_SUBD_DI;
514	s->subdev_flags	= SDF_READABLE;
515	s->n_chan	= 8;
516	s->maxdata	= 1;
517	s->range_table	= &range_digital;
518	s->insn_bits	= das6402_di_insn_bits;
519
520	/* Digital Input subdevice */
521	s = &dev->subdevices[3];
522	s->type		= COMEDI_SUBD_DO;
523	s->subdev_flags	= SDF_WRITEABLE;
524	s->n_chan	= 8;
525	s->maxdata	= 1;
526	s->range_table	= &range_digital;
527	s->insn_bits	= das6402_do_insn_bits;
528
529	return 0;
530}
531
532static struct comedi_driver das6402_driver = {
533	.driver_name	= "das6402",
534	.module		= THIS_MODULE,
535	.attach		= das6402_attach,
536	.detach		= comedi_legacy_detach,
537	.board_name	= &das6402_boards[0].name,
538	.num_names	= ARRAY_SIZE(das6402_boards),
539	.offset		= sizeof(struct das6402_boardinfo),
540};
541module_comedi_driver(das6402_driver)
542
543MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
544MODULE_DESCRIPTION("Comedi driver for DAS6402 compatible boards");
545MODULE_LICENSE("GPL");
546