]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/dt2814.c
Merge branch 'next' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[karo-tx-linux.git] / drivers / staging / comedi / drivers / dt2814.c
1 /*
2     comedi/drivers/dt2814.c
3     Hardware driver for Data Translation DT2814
4
5     COMEDI - Linux Control and Measurement Device Interface
6     Copyright (C) 1998 David A. Schleef <ds@schleef.org>
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 /*
19 Driver: dt2814
20 Description: Data Translation DT2814
21 Author: ds
22 Status: complete
23 Devices: [Data Translation] DT2814 (dt2814)
24
25 Configuration options:
26   [0] - I/O port base address
27   [1] - IRQ
28
29 This card has 16 analog inputs multiplexed onto a 12 bit ADC.  There
30 is a minimally useful onboard clock.  The base frequency for the
31 clock is selected by jumpers, and the clock divider can be selected
32 via programmed I/O.  Unfortunately, the clock divider can only be
33 a power of 10, from 1 to 10^7, of which only 3 or 4 are useful.  In
34 addition, the clock does not seem to be very accurate.
35 */
36
37 #include <linux/module.h>
38 #include <linux/interrupt.h>
39 #include "../comedidev.h"
40
41 #include <linux/delay.h>
42
43 #include "comedi_fc.h"
44
45 #define DT2814_SIZE 2
46
47 #define DT2814_CSR 0
48 #define DT2814_DATA 1
49
50 /*
51  * flags
52  */
53
54 #define DT2814_FINISH 0x80
55 #define DT2814_ERR 0x40
56 #define DT2814_BUSY 0x20
57 #define DT2814_ENB 0x10
58 #define DT2814_CHANMASK 0x0f
59
60 struct dt2814_private {
61
62         int ntrig;
63         int curadchan;
64 };
65
66 #define DT2814_TIMEOUT 10
67 #define DT2814_MAX_SPEED 100000 /* Arbitrary 10 khz limit */
68
69 static int dt2814_ai_insn_read(struct comedi_device *dev,
70                                struct comedi_subdevice *s,
71                                struct comedi_insn *insn, unsigned int *data)
72 {
73         int n, i, hi, lo;
74         int chan;
75         int status = 0;
76
77         for (n = 0; n < insn->n; n++) {
78                 chan = CR_CHAN(insn->chanspec);
79
80                 outb(chan, dev->iobase + DT2814_CSR);
81                 for (i = 0; i < DT2814_TIMEOUT; i++) {
82                         status = inb(dev->iobase + DT2814_CSR);
83                         printk(KERN_INFO "dt2814: status: %02x\n", status);
84                         udelay(10);
85                         if (status & DT2814_FINISH)
86                                 break;
87                 }
88                 if (i >= DT2814_TIMEOUT) {
89                         printk(KERN_INFO "dt2814: status: %02x\n", status);
90                         return -ETIMEDOUT;
91                 }
92
93                 hi = inb(dev->iobase + DT2814_DATA);
94                 lo = inb(dev->iobase + DT2814_DATA);
95
96                 data[n] = (hi << 4) | (lo >> 4);
97         }
98
99         return n;
100 }
101
102 static int dt2814_ns_to_timer(unsigned int *ns, unsigned int flags)
103 {
104         int i;
105         unsigned int f;
106
107         /* XXX ignores flags */
108
109         f = 10000;              /* ns */
110         for (i = 0; i < 8; i++) {
111                 if ((2 * (*ns)) < (f * 11))
112                         break;
113                 f *= 10;
114         }
115
116         *ns = f;
117
118         return i;
119 }
120
121 static int dt2814_ai_cmdtest(struct comedi_device *dev,
122                              struct comedi_subdevice *s, struct comedi_cmd *cmd)
123 {
124         int err = 0;
125         int tmp;
126
127         /* Step 1 : check if triggers are trivially valid */
128
129         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
130         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
131         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
132         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
133         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
134
135         if (err)
136                 return 1;
137
138         /* Step 2a : make sure trigger sources are unique */
139
140         err |= cfc_check_trigger_is_unique(cmd->stop_src);
141
142         /* Step 2b : and mutually compatible */
143
144         if (err)
145                 return 2;
146
147         /* Step 3: check if arguments are trivially valid */
148
149         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
150
151         err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg, 1000000000);
152         err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
153                                          DT2814_MAX_SPEED);
154
155         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
156
157         if (cmd->stop_src == TRIG_COUNT)
158                 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 2);
159         else    /* TRIG_NONE */
160                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
161
162         if (err)
163                 return 3;
164
165         /* step 4: fix up any arguments */
166
167         tmp = cmd->scan_begin_arg;
168         dt2814_ns_to_timer(&cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
169         if (tmp != cmd->scan_begin_arg)
170                 err++;
171
172         if (err)
173                 return 4;
174
175         return 0;
176 }
177
178 static int dt2814_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
179 {
180         struct dt2814_private *devpriv = dev->private;
181         struct comedi_cmd *cmd = &s->async->cmd;
182         int chan;
183         int trigvar;
184
185         trigvar =
186             dt2814_ns_to_timer(&cmd->scan_begin_arg,
187                                cmd->flags & TRIG_ROUND_MASK);
188
189         chan = CR_CHAN(cmd->chanlist[0]);
190
191         devpriv->ntrig = cmd->stop_arg;
192         outb(chan | DT2814_ENB | (trigvar << 5), dev->iobase + DT2814_CSR);
193
194         return 0;
195
196 }
197
198 static irqreturn_t dt2814_interrupt(int irq, void *d)
199 {
200         int lo, hi;
201         struct comedi_device *dev = d;
202         struct dt2814_private *devpriv = dev->private;
203         struct comedi_subdevice *s;
204         int data;
205
206         if (!dev->attached) {
207                 comedi_error(dev, "spurious interrupt");
208                 return IRQ_HANDLED;
209         }
210
211         s = &dev->subdevices[0];
212
213         hi = inb(dev->iobase + DT2814_DATA);
214         lo = inb(dev->iobase + DT2814_DATA);
215
216         data = (hi << 4) | (lo >> 4);
217
218         if (!(--devpriv->ntrig)) {
219                 int i;
220
221                 outb(0, dev->iobase + DT2814_CSR);
222                 /* note: turning off timed mode triggers another
223                    sample. */
224
225                 for (i = 0; i < DT2814_TIMEOUT; i++) {
226                         if (inb(dev->iobase + DT2814_CSR) & DT2814_FINISH)
227                                 break;
228                 }
229                 inb(dev->iobase + DT2814_DATA);
230                 inb(dev->iobase + DT2814_DATA);
231
232                 s->async->events |= COMEDI_CB_EOA;
233         }
234         comedi_event(dev, s);
235         return IRQ_HANDLED;
236 }
237
238 static int dt2814_attach(struct comedi_device *dev, struct comedi_devconfig *it)
239 {
240         struct dt2814_private *devpriv;
241         int i, irq;
242         int ret;
243         struct comedi_subdevice *s;
244
245         ret = comedi_request_region(dev, it->options[0], DT2814_SIZE);
246         if (ret)
247                 return ret;
248
249         outb(0, dev->iobase + DT2814_CSR);
250         udelay(100);
251         if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR) {
252                 printk(KERN_ERR "reset error (fatal)\n");
253                 return -EIO;
254         }
255         i = inb(dev->iobase + DT2814_DATA);
256         i = inb(dev->iobase + DT2814_DATA);
257
258         irq = it->options[1];
259 #if 0
260         if (irq < 0) {
261                 save_flags(flags);
262                 sti();
263                 irqs = probe_irq_on();
264
265                 outb(0, dev->iobase + DT2814_CSR);
266
267                 udelay(100);
268
269                 irq = probe_irq_off(irqs);
270                 restore_flags(flags);
271                 if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR)
272                         printk(KERN_DEBUG "error probing irq (bad)\n");
273
274
275                 i = inb(dev->iobase + DT2814_DATA);
276                 i = inb(dev->iobase + DT2814_DATA);
277         }
278 #endif
279         dev->irq = 0;
280         if (irq > 0) {
281                 if (request_irq(irq, dt2814_interrupt, 0, "dt2814", dev)) {
282                         printk(KERN_WARNING "(irq %d unavailable)\n", irq);
283                 } else {
284                         printk(KERN_INFO "( irq = %d )\n", irq);
285                         dev->irq = irq;
286                 }
287         } else if (irq == 0) {
288                 printk(KERN_WARNING "(no irq)\n");
289         } else {
290 #if 0
291                 printk(KERN_DEBUG "(probe returned multiple irqs--bad)\n");
292 #else
293                 printk(KERN_WARNING "(irq probe not implemented)\n");
294 #endif
295         }
296
297         ret = comedi_alloc_subdevices(dev, 1);
298         if (ret)
299                 return ret;
300
301         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
302         if (!devpriv)
303                 return -ENOMEM;
304
305         s = &dev->subdevices[0];
306         dev->read_subdev = s;
307         s->type = COMEDI_SUBD_AI;
308         s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
309         s->n_chan = 16;         /* XXX */
310         s->len_chanlist = 1;
311         s->insn_read = dt2814_ai_insn_read;
312         s->do_cmd = dt2814_ai_cmd;
313         s->do_cmdtest = dt2814_ai_cmdtest;
314         s->maxdata = 0xfff;
315         s->range_table = &range_unknown;        /* XXX */
316
317         return 0;
318 }
319
320 static struct comedi_driver dt2814_driver = {
321         .driver_name    = "dt2814",
322         .module         = THIS_MODULE,
323         .attach         = dt2814_attach,
324         .detach         = comedi_legacy_detach,
325 };
326 module_comedi_driver(dt2814_driver);
327
328 MODULE_AUTHOR("Comedi http://www.comedi.org");
329 MODULE_DESCRIPTION("Comedi low-level driver");
330 MODULE_LICENSE("GPL");