1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28#include <linux/module.h>
29#include <linux/interrupt.h>
30#include "../comedidev.h"
31
32#include <linux/delay.h>
33
34#define DT2814_CSR 0
35#define DT2814_DATA 1
36
37
38
39
40
41#define DT2814_FINISH 0x80
42#define DT2814_ERR 0x40
43#define DT2814_BUSY 0x20
44#define DT2814_ENB 0x10
45#define DT2814_CHANMASK 0x0f
46
47#define DT2814_TIMEOUT 10
48#define DT2814_MAX_SPEED 100000
49
50static int dt2814_ai_notbusy(struct comedi_device *dev,
51 struct comedi_subdevice *s,
52 struct comedi_insn *insn,
53 unsigned long context)
54{
55 unsigned int status;
56
57 status = inb(dev->iobase + DT2814_CSR);
58 if (context)
59 *(unsigned int *)context = status;
60 if (status & DT2814_BUSY)
61 return -EBUSY;
62 return 0;
63}
64
65static int dt2814_ai_clear(struct comedi_device *dev)
66{
67 unsigned int status = 0;
68 int ret;
69
70
71 ret = comedi_timeout(dev, NULL, NULL, dt2814_ai_notbusy,
72 (unsigned long)&status);
73 if (ret)
74 return ret;
75
76 if (status & (DT2814_FINISH | DT2814_ERR)) {
77
78
79
80
81 inb(dev->iobase + DT2814_DATA);
82 inb(dev->iobase + DT2814_DATA);
83 }
84 return 0;
85}
86
87static int dt2814_ai_eoc(struct comedi_device *dev,
88 struct comedi_subdevice *s,
89 struct comedi_insn *insn,
90 unsigned long context)
91{
92 unsigned int status;
93
94 status = inb(dev->iobase + DT2814_CSR);
95 if (status & DT2814_FINISH)
96 return 0;
97 return -EBUSY;
98}
99
100static int dt2814_ai_insn_read(struct comedi_device *dev,
101 struct comedi_subdevice *s,
102 struct comedi_insn *insn, unsigned int *data)
103{
104 int n, hi, lo;
105 int chan;
106 int ret;
107
108 dt2814_ai_clear(dev);
109 for (n = 0; n < insn->n; n++) {
110 chan = CR_CHAN(insn->chanspec);
111
112 outb(chan, dev->iobase + DT2814_CSR);
113
114 ret = comedi_timeout(dev, s, insn, dt2814_ai_eoc, 0);
115 if (ret)
116 return ret;
117
118 hi = inb(dev->iobase + DT2814_DATA);
119 lo = inb(dev->iobase + DT2814_DATA);
120
121 data[n] = (hi << 4) | (lo >> 4);
122 }
123
124 return n;
125}
126
127static int dt2814_ns_to_timer(unsigned int *ns, unsigned int flags)
128{
129 int i;
130 unsigned int f;
131
132
133
134 f = 10000;
135 for (i = 0; i < 8; i++) {
136 if ((2 * (*ns)) < (f * 11))
137 break;
138 f *= 10;
139 }
140
141 *ns = f;
142
143 return i;
144}
145
146static int dt2814_ai_cmdtest(struct comedi_device *dev,
147 struct comedi_subdevice *s, struct comedi_cmd *cmd)
148{
149 int err = 0;
150 unsigned int arg;
151
152
153
154 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
155 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
156 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
157 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
158 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
159
160 if (err)
161 return 1;
162
163
164
165 err |= comedi_check_trigger_is_unique(cmd->stop_src);
166
167
168
169 if (err)
170 return 2;
171
172
173
174 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
175
176 err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 1000000000);
177 err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
178 DT2814_MAX_SPEED);
179
180 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
181 cmd->chanlist_len);
182
183 if (cmd->stop_src == TRIG_COUNT)
184 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 2);
185 else
186 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
187
188 if (err)
189 return 3;
190
191
192
193 arg = cmd->scan_begin_arg;
194 dt2814_ns_to_timer(&arg, cmd->flags);
195 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
196
197 if (err)
198 return 4;
199
200 return 0;
201}
202
203static int dt2814_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
204{
205 struct comedi_cmd *cmd = &s->async->cmd;
206 int chan;
207 int trigvar;
208
209 dt2814_ai_clear(dev);
210 trigvar = dt2814_ns_to_timer(&cmd->scan_begin_arg, cmd->flags);
211
212 chan = CR_CHAN(cmd->chanlist[0]);
213
214 outb(chan | DT2814_ENB | (trigvar << 5), dev->iobase + DT2814_CSR);
215
216 return 0;
217}
218
219static int dt2814_ai_cancel(struct comedi_device *dev,
220 struct comedi_subdevice *s)
221{
222 unsigned int status;
223 unsigned long flags;
224
225 spin_lock_irqsave(&dev->spinlock, flags);
226 status = inb(dev->iobase + DT2814_CSR);
227 if (status & DT2814_ENB) {
228
229
230
231
232
233
234
235 outb(status & DT2814_CHANMASK, dev->iobase + DT2814_CSR);
236 }
237 spin_unlock_irqrestore(&dev->spinlock, flags);
238 return 0;
239}
240
241static irqreturn_t dt2814_interrupt(int irq, void *d)
242{
243 struct comedi_device *dev = d;
244 struct comedi_subdevice *s = dev->read_subdev;
245 struct comedi_async *async;
246 unsigned int lo, hi;
247 unsigned short data;
248 unsigned int status;
249
250 if (!dev->attached) {
251 dev_err(dev->class_dev, "spurious interrupt\n");
252 return IRQ_HANDLED;
253 }
254
255 async = s->async;
256
257 spin_lock(&dev->spinlock);
258
259 status = inb(dev->iobase + DT2814_CSR);
260 if (!(status & DT2814_ENB)) {
261
262 spin_unlock(&dev->spinlock);
263 return IRQ_HANDLED;
264 }
265
266 if (!(status & (DT2814_FINISH | DT2814_ERR))) {
267
268 spin_unlock(&dev->spinlock);
269 return IRQ_HANDLED;
270 }
271
272
273 hi = inb(dev->iobase + DT2814_DATA);
274 lo = inb(dev->iobase + DT2814_DATA);
275
276 data = (hi << 4) | (lo >> 4);
277
278 if (status & DT2814_ERR) {
279 async->events |= COMEDI_CB_ERROR;
280 } else {
281 comedi_buf_write_samples(s, &data, 1);
282 if (async->cmd.stop_src == TRIG_COUNT &&
283 async->scans_done >= async->cmd.stop_arg) {
284 async->events |= COMEDI_CB_EOA;
285 }
286 }
287 if (async->events & COMEDI_CB_CANCEL_MASK) {
288
289
290
291
292
293
294
295 outb(status & DT2814_CHANMASK, dev->iobase + DT2814_CSR);
296 }
297
298 spin_unlock(&dev->spinlock);
299
300 comedi_handle_events(dev, s);
301 return IRQ_HANDLED;
302}
303
304static int dt2814_attach(struct comedi_device *dev, struct comedi_devconfig *it)
305{
306 struct comedi_subdevice *s;
307 int ret;
308
309 ret = comedi_request_region(dev, it->options[0], 0x2);
310 if (ret)
311 return ret;
312
313 outb(0, dev->iobase + DT2814_CSR);
314 if (dt2814_ai_clear(dev)) {
315 dev_err(dev->class_dev, "reset error (fatal)\n");
316 return -EIO;
317 }
318
319 if (it->options[1]) {
320 ret = request_irq(it->options[1], dt2814_interrupt, 0,
321 dev->board_name, dev);
322 if (ret == 0)
323 dev->irq = it->options[1];
324 }
325
326 ret = comedi_alloc_subdevices(dev, 1);
327 if (ret)
328 return ret;
329
330 s = &dev->subdevices[0];
331 s->type = COMEDI_SUBD_AI;
332 s->subdev_flags = SDF_READABLE | SDF_GROUND;
333 s->n_chan = 16;
334 s->insn_read = dt2814_ai_insn_read;
335 s->maxdata = 0xfff;
336 s->range_table = &range_unknown;
337 if (dev->irq) {
338 dev->read_subdev = s;
339 s->subdev_flags |= SDF_CMD_READ;
340 s->len_chanlist = 1;
341 s->do_cmd = dt2814_ai_cmd;
342 s->do_cmdtest = dt2814_ai_cmdtest;
343 s->cancel = dt2814_ai_cancel;
344 }
345
346 return 0;
347}
348
349static void dt2814_detach(struct comedi_device *dev)
350{
351 if (dev->irq) {
352
353
354
355
356
357 dt2814_ai_clear(dev);
358 }
359 comedi_legacy_detach(dev);
360}
361
362static struct comedi_driver dt2814_driver = {
363 .driver_name = "dt2814",
364 .module = THIS_MODULE,
365 .attach = dt2814_attach,
366 .detach = dt2814_detach,
367};
368module_comedi_driver(dt2814_driver);
369
370MODULE_AUTHOR("Comedi https://www.comedi.org");
371MODULE_DESCRIPTION("Comedi low-level driver");
372MODULE_LICENSE("GPL");
373