Merge branch 'core-rcu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[cascardo/linux.git] / drivers / staging / comedi / drivers / das6402.c
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 /*
27 Driver: das6402
28 Description: Keithley Metrabyte DAS6402 (& compatibles)
29 Author: Oystein Svendsen <svendsen@pvv.org>
30 Status: bitrotten
31 Devices: [Keithley Metrabyte] DAS6402 (das6402)
32
33 This driver has suffered bitrot.
34 */
35
36 #include <linux/module.h>
37 #include <linux/interrupt.h>
38 #include "../comedidev.h"
39
40 #define DAS6402_SIZE 16
41
42 #define N_WORDS (3000*64)
43
44 #define STOP    0
45 #define START   1
46
47 #define SCANL 0x3f00
48 #define BYTE unsigned char
49 #define WORD unsigned short
50
51 /*----- register 8 ----*/
52 #define CLRINT 0x01
53 #define CLRXTR 0x02
54 #define CLRXIN 0x04
55 #define EXTEND 0x10
56 #define ARMED 0x20              /* enable conting of post sample conv */
57 #define POSTMODE 0x40
58 #define MHZ 0x80                /* 10 MHz clock */
59 /*---------------------*/
60
61 /*----- register 9 ----*/
62 #define IRQ (0x04 << 4)         /* these two are                         */
63 #define IRQV 10                 /*               dependent on each other */
64
65 #define CONVSRC 0x03            /* trig src is Intarnal pacer */
66 #define BURSTEN 0x04            /* enable burst */
67 #define XINTE 0x08              /* use external int. trig */
68 #define INTE 0x80               /* enable analog interrupts */
69 /*---------------------*/
70
71 /*----- register 10 ---*/
72 #define TGEN 0x01               /* Use pin DI1 for externl trigging? */
73 #define TGSEL 0x02              /* Use edge triggering */
74 #define TGPOL 0x04              /* active edge is falling */
75 #define PRETRIG 0x08            /* pretrig */
76 /*---------------------*/
77
78 /*----- register 11 ---*/
79 #define EOB 0x0c
80 #define FIFOHFULL 0x08
81 #define GAIN 0x01
82 #define FIFONEPTY 0x04
83 #define MODE 0x10
84 #define SEM 0x20
85 #define BIP 0x40
86 /*---------------------*/
87
88 #define M0 0x00
89 #define M2 0x04
90
91 #define C0 0x00
92 #define C1 0x40
93 #define C2 0x80
94 #define RWLH 0x30
95
96 struct das6402_private {
97         int ai_bytes_to_read;
98
99         int das6402_ignoreirq;
100 };
101
102 static void das6402_ai_fifo_dregs(struct comedi_device *dev,
103                                   struct comedi_subdevice *s)
104 {
105         while (1) {
106                 if (!(inb(dev->iobase + 8) & 0x01))
107                         return;
108                 comedi_buf_put(s->async, inw(dev->iobase));
109         }
110 }
111
112 static void das6402_setcounter(struct comedi_device *dev)
113 {
114         BYTE p;
115         unsigned short ctrlwrd;
116
117         /* set up counter0 first, mode 0 */
118         p = M0 | C0 | RWLH;
119         outb_p(p, dev->iobase + 15);
120         ctrlwrd = 2000;
121         p = (BYTE) (0xff & ctrlwrd);
122         outb_p(p, dev->iobase + 12);
123         p = (BYTE) (0xff & (ctrlwrd >> 8));
124         outb_p(p, dev->iobase + 12);
125
126         /* set up counter1, mode 2 */
127         p = M2 | C1 | RWLH;
128         outb_p(p, dev->iobase + 15);
129         ctrlwrd = 10;
130         p = (BYTE) (0xff & ctrlwrd);
131         outb_p(p, dev->iobase + 13);
132         p = (BYTE) (0xff & (ctrlwrd >> 8));
133         outb_p(p, dev->iobase + 13);
134
135         /* set up counter1, mode 2 */
136         p = M2 | C2 | RWLH;
137         outb_p(p, dev->iobase + 15);
138         ctrlwrd = 1000;
139         p = (BYTE) (0xff & ctrlwrd);
140         outb_p(p, dev->iobase + 14);
141         p = (BYTE) (0xff & (ctrlwrd >> 8));
142         outb_p(p, dev->iobase + 14);
143 }
144
145 static irqreturn_t intr_handler(int irq, void *d)
146 {
147         struct comedi_device *dev = d;
148         struct das6402_private *devpriv = dev->private;
149         struct comedi_subdevice *s = &dev->subdevices[0];
150
151         if (!dev->attached || devpriv->das6402_ignoreirq) {
152                 dev_warn(dev->class_dev, "BUG: spurious interrupt\n");
153                 return IRQ_HANDLED;
154         }
155 #ifdef DEBUG
156         printk("das6402: interrupt! das6402_irqcount=%i\n",
157                devpriv->das6402_irqcount);
158         printk("das6402: iobase+2=%i\n", inw_p(dev->iobase + 2));
159 #endif
160
161         das6402_ai_fifo_dregs(dev, s);
162
163         if (s->async->buf_write_count >= devpriv->ai_bytes_to_read) {
164                 outw_p(SCANL, dev->iobase + 2); /* clears the fifo */
165                 outb(0x07, dev->iobase + 8);    /* clears all flip-flops */
166 #ifdef DEBUG
167                 printk("das6402: Got %i samples\n\n",
168                        devpriv->das6402_wordsread - diff);
169 #endif
170                 s->async->events |= COMEDI_CB_EOA;
171                 comedi_event(dev, s);
172         }
173
174         outb(0x01, dev->iobase + 8);    /* clear only the interrupt flip-flop */
175
176         comedi_event(dev, s);
177         return IRQ_HANDLED;
178 }
179
180 #if 0
181 static void das6402_ai_fifo_read(struct comedi_device *dev, short *data, int n)
182 {
183         int i;
184
185         for (i = 0; i < n; i++)
186                 data[i] = inw(dev->iobase);
187 }
188 #endif
189
190 static int das6402_ai_cancel(struct comedi_device *dev,
191                              struct comedi_subdevice *s)
192 {
193         struct das6402_private *devpriv = dev->private;
194
195         /*
196          *  This function should reset the board from whatever condition it
197          *  is in (i.e., acquiring data), to a non-active state.
198          */
199
200         devpriv->das6402_ignoreirq = 1;
201         dev_dbg(dev->class_dev, "Stopping acquisition\n");
202         devpriv->das6402_ignoreirq = 1;
203         outb_p(0x02, dev->iobase + 10); /* disable external trigging */
204         outw_p(SCANL, dev->iobase + 2); /* resets the card fifo */
205         outb_p(0, dev->iobase + 9);     /* disables interrupts */
206
207         outw_p(SCANL, dev->iobase + 2);
208
209         return 0;
210 }
211
212 #ifdef unused
213 static int das6402_ai_mode2(struct comedi_device *dev,
214                             struct comedi_subdevice *s, comedi_trig * it)
215 {
216         struct das6402_private *devpriv = dev->private;
217
218         devpriv->das6402_ignoreirq = 1;
219         dev_dbg(dev->class_dev, "Starting acquisition\n");
220         outb_p(0x03, dev->iobase + 10); /* enable external trigging */
221         outw_p(SCANL, dev->iobase + 2); /* resets the card fifo */
222         outb_p(IRQ | CONVSRC | BURSTEN | INTE, dev->iobase + 9);
223
224         devpriv->ai_bytes_to_read = it->n * sizeof(short);
225
226         /* um... ignoreirq is a nasty race condition */
227         devpriv->das6402_ignoreirq = 0;
228
229         outw_p(SCANL, dev->iobase + 2);
230
231         return 0;
232 }
233 #endif
234
235 static int board_init(struct comedi_device *dev)
236 {
237         struct das6402_private *devpriv = dev->private;
238         BYTE b;
239
240         devpriv->das6402_ignoreirq = 1;
241
242         outb(0x07, dev->iobase + 8);
243
244         /* register 11  */
245         outb_p(MODE, dev->iobase + 11);
246         b = BIP | SEM | MODE | GAIN | FIFOHFULL;
247         outb_p(b, dev->iobase + 11);
248
249         /* register 8   */
250         outb_p(EXTEND, dev->iobase + 8);
251         b = EXTEND | MHZ;
252         outb_p(b, dev->iobase + 8);
253         b = MHZ | CLRINT | CLRXTR | CLRXIN;
254         outb_p(b, dev->iobase + 8);
255
256         /* register 9    */
257         b = IRQ | CONVSRC | BURSTEN | INTE;
258         outb_p(b, dev->iobase + 9);
259
260         /* register 10   */
261         b = TGSEL | TGEN;
262         outb_p(b, dev->iobase + 10);
263
264         b = 0x07;
265         outb_p(b, dev->iobase + 8);
266
267         das6402_setcounter(dev);
268
269         outw_p(SCANL, dev->iobase + 2); /* reset card fifo */
270
271         devpriv->das6402_ignoreirq = 0;
272
273         return 0;
274 }
275
276 static int das6402_attach(struct comedi_device *dev,
277                           struct comedi_devconfig *it)
278 {
279         struct das6402_private *devpriv;
280         unsigned int irq;
281         int ret;
282         struct comedi_subdevice *s;
283
284         ret = comedi_request_region(dev, it->options[0], DAS6402_SIZE);
285         if (ret)
286                 return ret;
287
288         irq = it->options[0];
289         dev_dbg(dev->class_dev, "( irq = %u )\n", irq);
290         ret = request_irq(irq, intr_handler, 0, "das6402", dev);
291         if (ret < 0)
292                 return ret;
293
294         dev->irq = irq;
295
296         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
297         if (!devpriv)
298                 return -ENOMEM;
299
300         ret = comedi_alloc_subdevices(dev, 1);
301         if (ret)
302                 return ret;
303
304         /* ai subdevice */
305         s = &dev->subdevices[0];
306         s->type = COMEDI_SUBD_AI;
307         s->subdev_flags = SDF_READABLE | SDF_GROUND;
308         s->n_chan = 8;
309         /* s->trig[2]=das6402_ai_mode2; */
310         s->cancel = das6402_ai_cancel;
311         s->maxdata = (1 << 12) - 1;
312         s->len_chanlist = 16;   /* ? */
313         s->range_table = &range_unknown;
314
315         board_init(dev);
316
317         return 0;
318 }
319
320 static struct comedi_driver das6402_driver = {
321         .driver_name    = "das6402",
322         .module         = THIS_MODULE,
323         .attach         = das6402_attach,
324         .detach         = comedi_legacy_detach,
325 };
326 module_comedi_driver(das6402_driver)
327
328 MODULE_AUTHOR("Comedi http://www.comedi.org");
329 MODULE_DESCRIPTION("Comedi low-level driver");
330 MODULE_LICENSE("GPL");