staging: comedi: comedi_test: simplify time since last AI scan
[deliverable/linux.git] / drivers / staging / comedi / drivers / dt2814.c
CommitLineData
a211ea97
DS
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.
a211ea97
DS
17*/
18/*
19Driver: dt2814
20Description: Data Translation DT2814
21Author: ds
22Status: complete
23Devices: [Data Translation] DT2814 (dt2814)
24
25Configuration options:
26 [0] - I/O port base address
27 [1] - IRQ
28
29This card has 16 analog inputs multiplexed onto a 12 bit ADC. There
30is a minimally useful onboard clock. The base frequency for the
31clock is selected by jumpers, and the clock divider can be selected
32via programmed I/O. Unfortunately, the clock divider can only be
33a power of 10, from 1 to 10^7, of which only 3 or 4 are useful. In
34addition, the clock does not seem to be very accurate.
35*/
36
ce157f80 37#include <linux/module.h>
25436dc9 38#include <linux/interrupt.h>
a211ea97
DS
39#include "../comedidev.h"
40
a211ea97
DS
41#include <linux/delay.h>
42
a211ea97
DS
43#define DT2814_CSR 0
44#define DT2814_DATA 1
45
46/*
47 * flags
48 */
49
50#define DT2814_FINISH 0x80
51#define DT2814_ERR 0x40
52#define DT2814_BUSY 0x20
53#define DT2814_ENB 0x10
54#define DT2814_CHANMASK 0x0f
55
ca6f10ca 56struct dt2814_private {
a211ea97
DS
57 int ntrig;
58 int curadchan;
ca6f10ca
BP
59};
60
a211ea97
DS
61#define DT2814_TIMEOUT 10
62#define DT2814_MAX_SPEED 100000 /* Arbitrary 10 khz limit */
63
82c7e864
HS
64static int dt2814_ai_eoc(struct comedi_device *dev,
65 struct comedi_subdevice *s,
66 struct comedi_insn *insn,
67 unsigned long context)
68{
69 unsigned int status;
70
71 status = inb(dev->iobase + DT2814_CSR);
72 if (status & DT2814_FINISH)
73 return 0;
74 return -EBUSY;
75}
76
0a85b6f0
MT
77static int dt2814_ai_insn_read(struct comedi_device *dev,
78 struct comedi_subdevice *s,
79 struct comedi_insn *insn, unsigned int *data)
a211ea97 80{
82c7e864 81 int n, hi, lo;
a211ea97 82 int chan;
82c7e864 83 int ret;
a211ea97
DS
84
85 for (n = 0; n < insn->n; n++) {
86 chan = CR_CHAN(insn->chanspec);
87
88 outb(chan, dev->iobase + DT2814_CSR);
82c7e864
HS
89
90 ret = comedi_timeout(dev, s, insn, dt2814_ai_eoc, 0);
91 if (ret)
92 return ret;
a211ea97
DS
93
94 hi = inb(dev->iobase + DT2814_DATA);
95 lo = inb(dev->iobase + DT2814_DATA);
96
97 data[n] = (hi << 4) | (lo >> 4);
98 }
99
100 return n;
101}
102
103static int dt2814_ns_to_timer(unsigned int *ns, unsigned int flags)
104{
105 int i;
106 unsigned int f;
107
108 /* XXX ignores flags */
109
110 f = 10000; /* ns */
111 for (i = 0; i < 8; i++) {
112 if ((2 * (*ns)) < (f * 11))
113 break;
114 f *= 10;
115 }
116
117 *ns = f;
118
119 return i;
120}
121
0a85b6f0
MT
122static int dt2814_ai_cmdtest(struct comedi_device *dev,
123 struct comedi_subdevice *s, struct comedi_cmd *cmd)
a211ea97
DS
124{
125 int err = 0;
704b0050 126 unsigned int arg;
a211ea97 127
27020ffe 128 /* Step 1 : check if triggers are trivially valid */
a211ea97 129
71bb49d0
IA
130 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
131 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
132 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
133 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
134 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
a211ea97
DS
135
136 if (err)
137 return 1;
138
27020ffe 139 /* Step 2a : make sure trigger sources are unique */
a211ea97 140
71bb49d0 141 err |= comedi_check_trigger_is_unique(cmd->stop_src);
27020ffe
HS
142
143 /* Step 2b : and mutually compatible */
a211ea97
DS
144
145 if (err)
146 return 2;
147
2345dc8b 148 /* Step 3: check if arguments are trivially valid */
a211ea97 149
71bb49d0 150 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
2345dc8b 151
71bb49d0
IA
152 err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 1000000000);
153 err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
154 DT2814_MAX_SPEED);
2345dc8b 155
71bb49d0
IA
156 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
157 cmd->chanlist_len);
2345dc8b
HS
158
159 if (cmd->stop_src == TRIG_COUNT)
71bb49d0 160 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 2);
2345dc8b 161 else /* TRIG_NONE */
71bb49d0 162 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
a211ea97
DS
163
164 if (err)
165 return 3;
166
167 /* step 4: fix up any arguments */
168
704b0050 169 arg = cmd->scan_begin_arg;
a207c12f 170 dt2814_ns_to_timer(&arg, cmd->flags);
71bb49d0 171 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
a211ea97
DS
172
173 if (err)
174 return 4;
175
176 return 0;
177}
178
da91b269 179static int dt2814_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
a211ea97 180{
9a1a6cf8 181 struct dt2814_private *devpriv = dev->private;
ea6d0d4c 182 struct comedi_cmd *cmd = &s->async->cmd;
a211ea97
DS
183 int chan;
184 int trigvar;
185
a207c12f 186 trigvar = dt2814_ns_to_timer(&cmd->scan_begin_arg, cmd->flags);
a211ea97
DS
187
188 chan = CR_CHAN(cmd->chanlist[0]);
189
190 devpriv->ntrig = cmd->stop_arg;
191 outb(chan | DT2814_ENB | (trigvar << 5), dev->iobase + DT2814_CSR);
192
193 return 0;
a211ea97
DS
194}
195
7806012e
HS
196static irqreturn_t dt2814_interrupt(int irq, void *d)
197{
198 int lo, hi;
199 struct comedi_device *dev = d;
9a1a6cf8 200 struct dt2814_private *devpriv = dev->private;
89d48385 201 struct comedi_subdevice *s = dev->read_subdev;
7806012e
HS
202 int data;
203
204 if (!dev->attached) {
dffe87cd 205 dev_err(dev->class_dev, "spurious interrupt\n");
7806012e
HS
206 return IRQ_HANDLED;
207 }
208
7806012e
HS
209 hi = inb(dev->iobase + DT2814_DATA);
210 lo = inb(dev->iobase + DT2814_DATA);
211
212 data = (hi << 4) | (lo >> 4);
213
214 if (!(--devpriv->ntrig)) {
215 int i;
216
217 outb(0, dev->iobase + DT2814_CSR);
218 /* note: turning off timed mode triggers another
219 sample. */
220
221 for (i = 0; i < DT2814_TIMEOUT; i++) {
222 if (inb(dev->iobase + DT2814_CSR) & DT2814_FINISH)
223 break;
224 }
225 inb(dev->iobase + DT2814_DATA);
226 inb(dev->iobase + DT2814_DATA);
227
228 s->async->events |= COMEDI_CB_EOA;
229 }
001cce48 230 comedi_handle_events(dev, s);
7806012e
HS
231 return IRQ_HANDLED;
232}
233
da91b269 234static int dt2814_attach(struct comedi_device *dev, struct comedi_devconfig *it)
a211ea97 235{
9a1a6cf8 236 struct dt2814_private *devpriv;
34c43922 237 struct comedi_subdevice *s;
c5a9595b
HS
238 int ret;
239 int i;
a211ea97 240
862755ec 241 ret = comedi_request_region(dev, it->options[0], 0x2);
c5c18cd3
HS
242 if (ret)
243 return ret;
a211ea97
DS
244
245 outb(0, dev->iobase + DT2814_CSR);
5f74ea14 246 udelay(100);
a211ea97 247 if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR) {
323cd355 248 dev_err(dev->class_dev, "reset error (fatal)\n");
a211ea97
DS
249 return -EIO;
250 }
251 i = inb(dev->iobase + DT2814_DATA);
252 i = inb(dev->iobase + DT2814_DATA);
253
c5a9595b
HS
254 if (it->options[1]) {
255 ret = request_irq(it->options[1], dt2814_interrupt, 0,
256 dev->board_name, dev);
257 if (ret == 0)
258 dev->irq = it->options[1];
a211ea97
DS
259 }
260
2f0b9d08 261 ret = comedi_alloc_subdevices(dev, 1);
8b6c5694 262 if (ret)
a211ea97 263 return ret;
c3744138 264
0bdab509 265 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
c34fa261
HS
266 if (!devpriv)
267 return -ENOMEM;
a211ea97 268
92af10e1 269 s = &dev->subdevices[0];
a211ea97 270 s->type = COMEDI_SUBD_AI;
c5a9595b 271 s->subdev_flags = SDF_READABLE | SDF_GROUND;
a211ea97 272 s->n_chan = 16; /* XXX */
a211ea97 273 s->insn_read = dt2814_ai_insn_read;
a211ea97
DS
274 s->maxdata = 0xfff;
275 s->range_table = &range_unknown; /* XXX */
c5a9595b
HS
276 if (dev->irq) {
277 dev->read_subdev = s;
278 s->subdev_flags |= SDF_CMD_READ;
279 s->len_chanlist = 1;
280 s->do_cmd = dt2814_ai_cmd;
281 s->do_cmdtest = dt2814_ai_cmdtest;
282 }
a211ea97
DS
283
284 return 0;
285}
286
7806012e
HS
287static struct comedi_driver dt2814_driver = {
288 .driver_name = "dt2814",
289 .module = THIS_MODULE,
290 .attach = dt2814_attach,
3d1fe3f7 291 .detach = comedi_legacy_detach,
7806012e
HS
292};
293module_comedi_driver(dt2814_driver);
90f703d3
AT
294
295MODULE_AUTHOR("Comedi http://www.comedi.org");
296MODULE_DESCRIPTION("Comedi low-level driver");
297MODULE_LICENSE("GPL");
This page took 0.835503 seconds and 5 git commands to generate.