dt2815.c revision 0a85b6f0ab0d2edb0d41b32697111ce0e4f43496
1/*
2   comedi/drivers/dt2815.c
3   Hardware driver for Data Translation DT2815
4
5   COMEDI - Linux Control and Measurement Device Interface
6   Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
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: dt2815
25Description: Data Translation DT2815
26Author: ds
27Status: mostly complete, untested
28Devices: [Data Translation] DT2815 (dt2815)
29
30I'm not sure anyone has ever tested this board.  If you have information
31contrary, please update.
32
33Configuration options:
34  [0] - I/O port base base address
35  [1] - IRQ (unused)
36  [2] - Voltage unipolar/bipolar configuration
37          0 == unipolar 5V  (0V -- +5V)
38	  1 == bipolar 5V  (-5V -- +5V)
39  [3] - Current offset configuration
40          0 == disabled  (0mA -- +32mAV)
41          1 == enabled  (+4mA -- +20mAV)
42  [4] - Firmware program configuration
43          0 == program 1 (see manual table 5-4)
44          1 == program 2 (see manual table 5-4)
45          2 == program 3 (see manual table 5-4)
46          3 == program 4 (see manual table 5-4)
47  [5] - Analog output 0 range configuration
48          0 == voltage
49          1 == current
50  [6] - Analog output 1 range configuration (same options)
51  [7] - Analog output 2 range configuration (same options)
52  [8] - Analog output 3 range configuration (same options)
53  [9] - Analog output 4 range configuration (same options)
54  [10] - Analog output 5 range configuration (same options)
55  [11] - Analog output 6 range configuration (same options)
56  [12] - Analog output 7 range configuration (same options)
57*/
58
59#include "../comedidev.h"
60
61#include <linux/ioport.h>
62#include <linux/delay.h>
63
64static const struct comedi_lrange range_dt2815_ao_32_current = { 1, {
65								     RANGE_mA(0,
66									      32)
67								     }
68};
69
70static const struct comedi_lrange range_dt2815_ao_20_current = { 1, {
71								     RANGE_mA(4,
72									      20)
73								     }
74};
75
76#define DT2815_SIZE 2
77
78#define DT2815_DATA 0
79#define DT2815_STATUS 1
80
81static int dt2815_attach(struct comedi_device *dev,
82			 struct comedi_devconfig *it);
83static int dt2815_detach(struct comedi_device *dev);
84static struct comedi_driver driver_dt2815 = {
85	.driver_name = "dt2815",
86	.module = THIS_MODULE,
87	.attach = dt2815_attach,
88	.detach = dt2815_detach,
89};
90
91COMEDI_INITCLEANUP(driver_dt2815);
92
93static void dt2815_free_resources(struct comedi_device *dev);
94
95struct dt2815_private {
96
97	const struct comedi_lrange *range_type_list[8];
98	unsigned int ao_readback[8];
99};
100
101#define devpriv ((struct dt2815_private *)dev->private)
102
103static int dt2815_wait_for_status(struct comedi_device *dev, int status)
104{
105	int i;
106
107	for (i = 0; i < 100; i++) {
108		if (inb(dev->iobase + DT2815_STATUS) == status)
109			break;
110	}
111	return status;
112}
113
114static int dt2815_ao_insn_read(struct comedi_device *dev,
115			       struct comedi_subdevice *s,
116			       struct comedi_insn *insn, unsigned int *data)
117{
118	int i;
119	int chan = CR_CHAN(insn->chanspec);
120
121	for (i = 0; i < insn->n; i++) {
122		data[i] = devpriv->ao_readback[chan];
123	}
124
125	return i;
126}
127
128static int dt2815_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
129			  struct comedi_insn *insn, unsigned int *data)
130{
131	int i;
132	int chan = CR_CHAN(insn->chanspec);
133	unsigned int status;
134	unsigned int lo, hi;
135
136	for (i = 0; i < insn->n; i++) {
137		lo = ((data[i] & 0x0f) << 4) | (chan << 1) | 0x01;
138		hi = (data[i] & 0xff0) >> 4;
139
140		status = dt2815_wait_for_status(dev, 0x00);
141		if (status != 0) {
142			printk
143			    ("dt2815: failed to write low byte on %d reason %x\n",
144			     chan, status);
145			return -EBUSY;
146		}
147
148		outb(lo, dev->iobase + DT2815_DATA);
149
150		status = dt2815_wait_for_status(dev, 0x10);
151		if (status != 0x10) {
152			printk
153			    ("dt2815: failed to write high byte on %d reason %x\n",
154			     chan, status);
155			return -EBUSY;
156		}
157		devpriv->ao_readback[chan] = data[i];
158	}
159	return i;
160}
161
162/*
163  options[0]   Board base address
164  options[1]   IRQ (not applicable)
165  options[2]   Voltage unipolar/bipolar configuration
166                 0 == unipolar 5V  (0V -- +5V)
167		 1 == bipolar 5V  (-5V -- +5V)
168  options[3]   Current offset configuration
169                 0 == disabled  (0mA -- +32mAV)
170                 1 == enabled  (+4mA -- +20mAV)
171  options[4]   Firmware program configuration
172                 0 == program 1 (see manual table 5-4)
173                 1 == program 2 (see manual table 5-4)
174                 2 == program 3 (see manual table 5-4)
175                 3 == program 4 (see manual table 5-4)
176  options[5]   Analog output 0 range configuration
177                 0 == voltage
178                 1 == current
179  options[6]   Analog output 1 range configuration
180  ...
181  options[12]   Analog output 7 range configuration
182                 0 == voltage
183                 1 == current
184 */
185
186static int dt2815_attach(struct comedi_device *dev, struct comedi_devconfig *it)
187{
188	struct comedi_subdevice *s;
189	int i;
190	const struct comedi_lrange *current_range_type, *voltage_range_type;
191	unsigned long iobase;
192
193	iobase = it->options[0];
194	printk("comedi%d: dt2815: 0x%04lx ", dev->minor, iobase);
195	if (!request_region(iobase, DT2815_SIZE, "dt2815")) {
196		printk("I/O port conflict\n");
197		return -EIO;
198	}
199
200	dev->iobase = iobase;
201	dev->board_name = "dt2815";
202
203	if (alloc_subdevices(dev, 1) < 0)
204		return -ENOMEM;
205	if (alloc_private(dev, sizeof(struct dt2815_private)) < 0)
206		return -ENOMEM;
207
208	s = dev->subdevices;
209	/* ao subdevice */
210	s->type = COMEDI_SUBD_AO;
211	s->subdev_flags = SDF_WRITABLE;
212	s->maxdata = 0xfff;
213	s->n_chan = 8;
214	s->insn_write = dt2815_ao_insn;
215	s->insn_read = dt2815_ao_insn_read;
216	s->range_table_list = devpriv->range_type_list;
217
218	current_range_type = (it->options[3])
219	    ? &range_dt2815_ao_20_current : &range_dt2815_ao_32_current;
220	voltage_range_type = (it->options[2])
221	    ? &range_bipolar5 : &range_unipolar5;
222	for (i = 0; i < 8; i++) {
223		devpriv->range_type_list[i] = (it->options[5 + i])
224		    ? current_range_type : voltage_range_type;
225	}
226
227	/* Init the 2815 */
228	outb(0x00, dev->iobase + DT2815_STATUS);
229	for (i = 0; i < 100; i++) {
230		/* This is incredibly slow (approx 20 ms) */
231		unsigned int status;
232
233		udelay(1000);
234		status = inb(dev->iobase + DT2815_STATUS);
235		if (status == 4) {
236			unsigned int program;
237			program = (it->options[4] & 0x3) << 3 | 0x7;
238			outb(program, dev->iobase + DT2815_DATA);
239			printk(", program: 0x%x (@t=%d)\n", program, i);
240			break;
241		} else if (status != 0x00) {
242			printk("dt2815: unexpected status 0x%x (@t=%d)\n",
243			       status, i);
244			if (status & 0x60) {
245				outb(0x00, dev->iobase + DT2815_STATUS);
246			}
247		}
248	}
249
250	printk("\n");
251
252	return 0;
253}
254
255static void dt2815_free_resources(struct comedi_device *dev)
256{
257	if (dev->iobase)
258		release_region(dev->iobase, DT2815_SIZE);
259}
260
261static int dt2815_detach(struct comedi_device *dev)
262{
263	printk("comedi%d: dt2815: remove\n", dev->minor);
264
265	dt2815_free_resources(dev);
266
267	return 0;
268}
269