das6402.c revision 70265d24e3404fe798b6edd55a02016b1edb49d7
1/*
2   Some comments on the code..
3
4   - it shouldn't be necessary to use outb_p().
5
6   - ignoreirq creates a race condition.  It needs to be fixed.
7
8 */
9
10/*
11   comedi/drivers/das6402.c
12   An experimental driver for Computerboards' DAS6402 I/O card
13
14   Copyright (C) 1999 Oystein Svendsen <svendsen@pvv.org>
15
16   This program is free software; you can redistribute it and/or modify
17   it under the terms of the GNU General Public License as published by
18   the Free Software Foundation; either version 2 of the License, or
19   (at your option) any later version.
20
21   This program is distributed in the hope that it will be useful,
22   but WITHOUT ANY WARRANTY; without even the implied warranty of
23   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24   GNU General Public License for more details.
25
26   You should have received a copy of the GNU General Public License
27   along with this program; if not, write to the Free Software
28   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29
30 */
31/*
32Driver: das6402
33Description: Keithley Metrabyte DAS6402 (& compatibles)
34Author: Oystein Svendsen <svendsen@pvv.org>
35Status: bitrotten
36Devices: [Keithley Metrabyte] DAS6402 (das6402)
37
38This driver has suffered bitrot.
39*/
40
41#include "../comedidev.h"
42
43#include <linux/ioport.h>
44
45#define DAS6402_SIZE 16
46
47#define N_WORDS 3000*64
48
49#define STOP    0
50#define START   1
51
52#define SCANL 0x3f00
53#define BYTE unsigned char
54#define WORD unsigned short
55
56/*----- register 8 ----*/
57#define CLRINT 0x01
58#define CLRXTR 0x02
59#define CLRXIN 0x04
60#define EXTEND 0x10
61#define ARMED 0x20		/* enable conting of post sample conv */
62#define POSTMODE 0x40
63#define MHZ 0x80		/* 10 MHz clock */
64/*---------------------*/
65
66/*----- register 9 ----*/
67#define IRQ (0x04 << 4)		/* these two are                         */
68#define IRQV 10			/*               dependent on each other */
69
70#define CONVSRC 0x03		/* trig src is Intarnal pacer */
71#define BURSTEN 0x04		/* enable burst */
72#define XINTE 0x08		/* use external int. trig */
73#define INTE 0x80		/* enable analog interrupts */
74/*---------------------*/
75
76/*----- register 10 ---*/
77#define TGEN 0x01		/* Use pin DI1 for externl trigging? */
78#define TGSEL 0x02		/* Use edge triggering */
79#define TGPOL 0x04		/* active edge is falling */
80#define PRETRIG 0x08		/* pretrig */
81/*---------------------*/
82
83/*----- register 11 ---*/
84#define EOB 0x0c
85#define FIFOHFULL 0x08
86#define GAIN 0x01
87#define FIFONEPTY 0x04
88#define MODE 0x10
89#define SEM 0x20
90#define BIP 0x40
91/*---------------------*/
92
93#define M0 0x00
94#define M2 0x04
95
96#define	C0 0x00
97#define	C1 0x40
98#define	C2 0x80
99#define	RWLH 0x30
100
101static int das6402_attach(struct comedi_device * dev, struct comedi_devconfig * it);
102static int das6402_detach(struct comedi_device * dev);
103static struct comedi_driver driver_das6402 = {
104      driver_name:"das6402",
105      module:THIS_MODULE,
106      attach:das6402_attach,
107      detach:das6402_detach,
108};
109
110COMEDI_INITCLEANUP(driver_das6402);
111
112struct das6402_private {
113	int ai_bytes_to_read;
114
115	int das6402_ignoreirq;
116};
117#define devpriv ((struct das6402_private *)dev->private)
118
119static void das6402_ai_fifo_dregs(struct comedi_device * dev, struct comedi_subdevice * s);
120
121static void das6402_setcounter(struct comedi_device * dev)
122{
123	BYTE p;
124	unsigned short ctrlwrd;
125
126	/* set up counter0 first, mode 0 */
127	p = M0 | C0 | RWLH;
128	outb_p(p, dev->iobase + 15);
129	ctrlwrd = 2000;
130	p = (BYTE) (0xff & ctrlwrd);
131	outb_p(p, dev->iobase + 12);
132	p = (BYTE) (0xff & (ctrlwrd >> 8));
133	outb_p(p, dev->iobase + 12);
134
135	/* set up counter1, mode 2 */
136	p = M2 | C1 | RWLH;
137	outb_p(p, dev->iobase + 15);
138	ctrlwrd = 10;
139	p = (BYTE) (0xff & ctrlwrd);
140	outb_p(p, dev->iobase + 13);
141	p = (BYTE) (0xff & (ctrlwrd >> 8));
142	outb_p(p, dev->iobase + 13);
143
144	/* set up counter1, mode 2 */
145	p = M2 | C2 | RWLH;
146	outb_p(p, dev->iobase + 15);
147	ctrlwrd = 1000;
148	p = (BYTE) (0xff & ctrlwrd);
149	outb_p(p, dev->iobase + 14);
150	p = (BYTE) (0xff & (ctrlwrd >> 8));
151	outb_p(p, dev->iobase + 14);
152}
153
154static irqreturn_t intr_handler(int irq, void *d)
155{
156	struct comedi_device *dev = d;
157	struct comedi_subdevice *s = dev->subdevices;
158
159	if (!dev->attached || devpriv->das6402_ignoreirq) {
160		printk("das6402: BUG: spurious interrupt\n");
161		return IRQ_HANDLED;
162	}
163#ifdef DEBUG
164	printk("das6402: interrupt! das6402_irqcount=%i\n",
165		devpriv->das6402_irqcount);
166	printk("das6402: iobase+2=%i\n", inw_p(dev->iobase + 2));
167#endif
168
169	das6402_ai_fifo_dregs(dev, s);
170
171	if (s->async->buf_write_count >= devpriv->ai_bytes_to_read) {
172		outw_p(SCANL, dev->iobase + 2);	/* clears the fifo */
173		outb(0x07, dev->iobase + 8);	/* clears all flip-flops */
174#ifdef DEBUG
175		printk("das6402: Got %i samples\n\n",
176			devpriv->das6402_wordsread - diff);
177#endif
178		s->async->events |= COMEDI_CB_EOA;
179		comedi_event(dev, s);
180	}
181
182	outb(0x01, dev->iobase + 8);	/* clear only the interrupt flip-flop */
183
184	comedi_event(dev, s);
185	return IRQ_HANDLED;
186}
187
188#if 0
189static void das6402_ai_fifo_read(struct comedi_device * dev, short * data, int n)
190{
191	int i;
192
193	for (i = 0; i < n; i++)
194		data[i] = inw(dev->iobase);
195}
196#endif
197
198static void das6402_ai_fifo_dregs(struct comedi_device * dev, struct comedi_subdevice * s)
199{
200	while (1) {
201		if (!(inb(dev->iobase + 8) & 0x01))
202			return;
203		comedi_buf_put(s->async, inw(dev->iobase));
204	}
205}
206
207static int das6402_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
208{
209	/*
210	 *  This function should reset the board from whatever condition it
211	 *  is in (i.e., acquiring data), to a non-active state.
212	 */
213
214	devpriv->das6402_ignoreirq = 1;
215#ifdef DEBUG
216	printk("das6402: Stopping acquisition\n");
217#endif
218	devpriv->das6402_ignoreirq = 1;
219	outb_p(0x02, dev->iobase + 10);	/* disable external trigging */
220	outw_p(SCANL, dev->iobase + 2);	/* resets the card fifo */
221	outb_p(0, dev->iobase + 9);	/* disables interrupts */
222
223	outw_p(SCANL, dev->iobase + 2);
224
225	return 0;
226}
227
228#ifdef unused
229static int das6402_ai_mode2(struct comedi_device * dev, struct comedi_subdevice * s,
230	comedi_trig * it)
231{
232	devpriv->das6402_ignoreirq = 1;
233
234#ifdef DEBUG
235	printk("das6402: Starting acquisition\n");
236#endif
237	outb_p(0x03, dev->iobase + 10);	/* enable external trigging */
238	outw_p(SCANL, dev->iobase + 2);	/* resets the card fifo */
239	outb_p(IRQ | CONVSRC | BURSTEN | INTE, dev->iobase + 9);
240
241	devpriv->ai_bytes_to_read = it->n * sizeof(short);
242
243	/* um... ignoreirq is a nasty race condition */
244	devpriv->das6402_ignoreirq = 0;
245
246	outw_p(SCANL, dev->iobase + 2);
247
248	return 0;
249}
250#endif
251
252static int board_init(struct comedi_device * dev)
253{
254	BYTE b;
255
256	devpriv->das6402_ignoreirq = 1;
257
258	outb(0x07, dev->iobase + 8);
259
260	/* register 11  */
261	outb_p(MODE, dev->iobase + 11);
262	b = BIP | SEM | MODE | GAIN | FIFOHFULL;
263	outb_p(b, dev->iobase + 11);
264
265	/* register 8   */
266	outb_p(EXTEND, dev->iobase + 8);
267	b = EXTEND | MHZ;
268	outb_p(b, dev->iobase + 8);
269	b = MHZ | CLRINT | CLRXTR | CLRXIN;
270	outb_p(b, dev->iobase + 8);
271
272	/* register 9    */
273	b = IRQ | CONVSRC | BURSTEN | INTE;
274	outb_p(b, dev->iobase + 9);
275
276	/* register 10   */
277	b = TGSEL | TGEN;
278	outb_p(b, dev->iobase + 10);
279
280	b = 0x07;
281	outb_p(b, dev->iobase + 8);
282
283	das6402_setcounter(dev);
284
285	outw_p(SCANL, dev->iobase + 2);	/* reset card fifo */
286
287	devpriv->das6402_ignoreirq = 0;
288
289	return 0;
290}
291
292static int das6402_detach(struct comedi_device * dev)
293{
294	if (dev->irq)
295		comedi_free_irq(dev->irq, dev);
296	if (dev->iobase)
297		release_region(dev->iobase, DAS6402_SIZE);
298
299	return 0;
300}
301
302static int das6402_attach(struct comedi_device * dev, struct comedi_devconfig * it)
303{
304	unsigned int irq;
305	unsigned long iobase;
306	int ret;
307	struct comedi_subdevice *s;
308
309	dev->board_name = "das6402";
310
311	iobase = it->options[0];
312	if (iobase == 0)
313		iobase = 0x300;
314
315	printk("comedi%d: das6402: 0x%04lx", dev->minor, iobase);
316
317	if (!request_region(iobase, DAS6402_SIZE, "das6402")) {
318		printk(" I/O port conflict\n");
319		return -EIO;
320	}
321	dev->iobase = iobase;
322
323	/* should do a probe here */
324
325	irq = it->options[0];
326	printk(" ( irq = %u )", irq);
327	ret = comedi_request_irq(irq, intr_handler, 0, "das6402", dev);
328	if (ret < 0) {
329		printk("irq conflict\n");
330		return ret;
331	}
332	dev->irq = irq;
333
334	if ((ret = alloc_private(dev, sizeof(struct das6402_private))) < 0)
335		return ret;
336
337	if ((ret = alloc_subdevices(dev, 1)) < 0)
338		return ret;
339
340	/* ai subdevice */
341	s = dev->subdevices + 0;
342	s->type = COMEDI_SUBD_AI;
343	s->subdev_flags = SDF_READABLE | SDF_GROUND;
344	s->n_chan = 8;
345	//s->trig[2]=das6402_ai_mode2;
346	s->cancel = das6402_ai_cancel;
347	s->maxdata = (1 << 12) - 1;
348	s->len_chanlist = 16;	/* ? */
349	s->range_table = &range_unknown;
350
351	board_init(dev);
352
353	return 0;
354}
355