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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55#include <linux/module.h>
56#include <linux/slab.h>
57#include <linux/interrupt.h>
58#include "../comedidev.h"
59
60#include "8255.h"
61#include "comedi_8254.h"
62
63#define DAS16M1_SIZE2 8
64
65#define FIFO_SIZE 1024
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87#define DAS16M1_AI 0
88#define AI_CHAN(x) ((x) & 0xf)
89#define DAS16M1_CS 2
90#define EXT_TRIG_BIT 0x1
91#define OVRUN 0x20
92#define IRQDATA 0x80
93#define DAS16M1_DIO 3
94#define DAS16M1_CLEAR_INTR 4
95#define DAS16M1_INTR_CONTROL 5
96#define EXT_PACER 0x2
97#define INT_PACER 0x3
98#define PACER_MASK 0x3
99#define INTE 0x80
100#define DAS16M1_QUEUE_ADDR 6
101#define DAS16M1_QUEUE_DATA 7
102#define Q_CHAN(x) ((x) & 0x7)
103#define Q_RANGE(x) (((x) & 0xf) << 4)
104#define UNIPOLAR 0x40
105#define DAS16M1_8254_FIRST 0x8
106#define DAS16M1_8254_SECOND 0xc
107#define DAS16M1_82C55 0x400
108#define DAS16M1_8254_THIRD 0x404
109
110static const struct comedi_lrange range_das16m1 = {
111 9, {
112 BIP_RANGE(5),
113 BIP_RANGE(2.5),
114 BIP_RANGE(1.25),
115 BIP_RANGE(0.625),
116 UNI_RANGE(10),
117 UNI_RANGE(5),
118 UNI_RANGE(2.5),
119 UNI_RANGE(1.25),
120 BIP_RANGE(10)
121 }
122};
123
124struct das16m1_private_struct {
125 struct comedi_8254 *counter;
126 unsigned int control_state;
127 unsigned int adc_count;
128
129
130
131 u16 initial_hw_count;
132 unsigned short ai_buffer[FIFO_SIZE];
133 unsigned long extra_iobase;
134};
135
136static inline unsigned short munge_sample(unsigned short data)
137{
138 return (data >> 4) & 0xfff;
139}
140
141static void munge_sample_array(unsigned short *array, unsigned int num_elements)
142{
143 unsigned int i;
144
145 for (i = 0; i < num_elements; i++)
146 array[i] = munge_sample(array[i]);
147}
148
149static int das16m1_ai_check_chanlist(struct comedi_device *dev,
150 struct comedi_subdevice *s,
151 struct comedi_cmd *cmd)
152{
153 int i;
154
155 if (cmd->chanlist_len == 1)
156 return 0;
157
158 if ((cmd->chanlist_len % 2) != 0) {
159 dev_dbg(dev->class_dev,
160 "chanlist must be of even length or length 1\n");
161 return -EINVAL;
162 }
163
164 for (i = 0; i < cmd->chanlist_len; i++) {
165 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
166
167 if ((i % 2) != (chan % 2)) {
168 dev_dbg(dev->class_dev,
169 "even/odd channels must go have even/odd chanlist indices\n");
170 return -EINVAL;
171 }
172 }
173
174 return 0;
175}
176
177static int das16m1_cmd_test(struct comedi_device *dev,
178 struct comedi_subdevice *s, struct comedi_cmd *cmd)
179{
180 int err = 0;
181
182
183
184 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
185 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
186 err |= comedi_check_trigger_src(&cmd->convert_src,
187 TRIG_TIMER | TRIG_EXT);
188 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
189 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
190
191 if (err)
192 return 1;
193
194
195
196 err |= comedi_check_trigger_is_unique(cmd->start_src);
197 err |= comedi_check_trigger_is_unique(cmd->convert_src);
198 err |= comedi_check_trigger_is_unique(cmd->stop_src);
199
200
201
202 if (err)
203 return 2;
204
205
206
207 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
208
209 if (cmd->scan_begin_src == TRIG_FOLLOW)
210 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
211
212 if (cmd->convert_src == TRIG_TIMER)
213 err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 1000);
214
215 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
216 cmd->chanlist_len);
217
218 if (cmd->stop_src == TRIG_COUNT)
219 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
220 else
221 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
222
223 if (err)
224 return 3;
225
226
227
228 if (cmd->convert_src == TRIG_TIMER) {
229 unsigned int arg = cmd->convert_arg;
230
231 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
232 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
233 }
234
235 if (err)
236 return 4;
237
238
239 if (cmd->chanlist && cmd->chanlist_len > 0)
240 err |= das16m1_ai_check_chanlist(dev, s, cmd);
241
242 if (err)
243 return 5;
244
245 return 0;
246}
247
248static int das16m1_cmd_exec(struct comedi_device *dev,
249 struct comedi_subdevice *s)
250{
251 struct das16m1_private_struct *devpriv = dev->private;
252 struct comedi_async *async = s->async;
253 struct comedi_cmd *cmd = &async->cmd;
254 unsigned int byte, i;
255
256
257 devpriv->control_state &= ~INTE & ~PACER_MASK;
258 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
259
260
261 devpriv->adc_count = 0;
262
263
264
265
266
267
268 comedi_8254_set_mode(devpriv->counter, 1, I8254_MODE2 | I8254_BINARY);
269 comedi_8254_write(devpriv->counter, 1, 0);
270
271
272
273
274
275 devpriv->initial_hw_count = comedi_8254_read(devpriv->counter, 1);
276
277
278 for (i = 0; i < cmd->chanlist_len; i++) {
279 outb(i, dev->iobase + DAS16M1_QUEUE_ADDR);
280 byte =
281 Q_CHAN(CR_CHAN(cmd->chanlist[i])) |
282 Q_RANGE(CR_RANGE(cmd->chanlist[i]));
283 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
284 }
285
286
287 devpriv->control_state &= ~PACER_MASK;
288 if (cmd->convert_src == TRIG_TIMER) {
289 comedi_8254_update_divisors(dev->pacer);
290 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
291 devpriv->control_state |= INT_PACER;
292 } else {
293 devpriv->control_state |= EXT_PACER;
294 }
295
296
297 byte = 0;
298
299
300 if (cmd->start_src == TRIG_EXT && cmd->convert_src != TRIG_EXT)
301 byte |= EXT_TRIG_BIT;
302
303 outb(byte, dev->iobase + DAS16M1_CS);
304
305 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
306
307 devpriv->control_state |= INTE;
308 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
309
310 return 0;
311}
312
313static int das16m1_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
314{
315 struct das16m1_private_struct *devpriv = dev->private;
316
317 devpriv->control_state &= ~INTE & ~PACER_MASK;
318 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
319
320 return 0;
321}
322
323static int das16m1_ai_eoc(struct comedi_device *dev,
324 struct comedi_subdevice *s,
325 struct comedi_insn *insn,
326 unsigned long context)
327{
328 unsigned int status;
329
330 status = inb(dev->iobase + DAS16M1_CS);
331 if (status & IRQDATA)
332 return 0;
333 return -EBUSY;
334}
335
336static int das16m1_ai_rinsn(struct comedi_device *dev,
337 struct comedi_subdevice *s,
338 struct comedi_insn *insn, unsigned int *data)
339{
340 struct das16m1_private_struct *devpriv = dev->private;
341 int ret;
342 int n;
343 int byte;
344
345
346 devpriv->control_state &= ~INTE & ~PACER_MASK;
347 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
348
349
350 outb(0, dev->iobase + DAS16M1_QUEUE_ADDR);
351 byte =
352 Q_CHAN(CR_CHAN(insn->chanspec)) | Q_RANGE(CR_RANGE(insn->chanspec));
353 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
354
355 for (n = 0; n < insn->n; n++) {
356
357 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
358
359 outb(0, dev->iobase);
360
361 ret = comedi_timeout(dev, s, insn, das16m1_ai_eoc, 0);
362 if (ret)
363 return ret;
364
365 data[n] = munge_sample(inw(dev->iobase));
366 }
367
368 return n;
369}
370
371static int das16m1_di_rbits(struct comedi_device *dev,
372 struct comedi_subdevice *s,
373 struct comedi_insn *insn, unsigned int *data)
374{
375 unsigned int bits;
376
377 bits = inb(dev->iobase + DAS16M1_DIO) & 0xf;
378 data[1] = bits;
379 data[0] = 0;
380
381 return insn->n;
382}
383
384static int das16m1_do_wbits(struct comedi_device *dev,
385 struct comedi_subdevice *s,
386 struct comedi_insn *insn,
387 unsigned int *data)
388{
389 if (comedi_dio_update_state(s, data))
390 outb(s->state, dev->iobase + DAS16M1_DIO);
391
392 data[1] = s->state;
393
394 return insn->n;
395}
396
397static void das16m1_handler(struct comedi_device *dev, unsigned int status)
398{
399 struct das16m1_private_struct *devpriv = dev->private;
400 struct comedi_subdevice *s;
401 struct comedi_async *async;
402 struct comedi_cmd *cmd;
403 u16 num_samples;
404 u16 hw_counter;
405
406 s = dev->read_subdev;
407 async = s->async;
408 cmd = &async->cmd;
409
410
411 hw_counter = comedi_8254_read(devpriv->counter, 1);
412
413
414 if (devpriv->adc_count == 0 &&
415 hw_counter == devpriv->initial_hw_count) {
416 num_samples = 0;
417 } else {
418
419
420
421
422
423
424
425
426 num_samples = -hw_counter - devpriv->adc_count;
427 }
428
429 if (cmd->stop_src == TRIG_COUNT) {
430 if (num_samples > cmd->stop_arg * cmd->chanlist_len)
431 num_samples = cmd->stop_arg * cmd->chanlist_len;
432 }
433
434 if (num_samples > FIFO_SIZE)
435 num_samples = FIFO_SIZE;
436 insw(dev->iobase, devpriv->ai_buffer, num_samples);
437 munge_sample_array(devpriv->ai_buffer, num_samples);
438 comedi_buf_write_samples(s, devpriv->ai_buffer, num_samples);
439 devpriv->adc_count += num_samples;
440
441 if (cmd->stop_src == TRIG_COUNT) {
442 if (devpriv->adc_count >= cmd->stop_arg * cmd->chanlist_len) {
443
444 async->events |= COMEDI_CB_EOA;
445 }
446 }
447
448
449
450 if (status & OVRUN) {
451 async->events |= COMEDI_CB_ERROR;
452 dev_err(dev->class_dev, "fifo overflow\n");
453 }
454
455 comedi_handle_events(dev, s);
456}
457
458static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s)
459{
460 unsigned long flags;
461 unsigned int status;
462
463
464 spin_lock_irqsave(&dev->spinlock, flags);
465 status = inb(dev->iobase + DAS16M1_CS);
466 das16m1_handler(dev, status);
467 spin_unlock_irqrestore(&dev->spinlock, flags);
468
469 return comedi_buf_n_bytes_ready(s);
470}
471
472static irqreturn_t das16m1_interrupt(int irq, void *d)
473{
474 int status;
475 struct comedi_device *dev = d;
476
477 if (!dev->attached) {
478 dev_err(dev->class_dev, "premature interrupt\n");
479 return IRQ_HANDLED;
480 }
481
482 spin_lock(&dev->spinlock);
483
484 status = inb(dev->iobase + DAS16M1_CS);
485
486 if ((status & (IRQDATA | OVRUN)) == 0) {
487 dev_err(dev->class_dev, "spurious interrupt\n");
488 spin_unlock(&dev->spinlock);
489 return IRQ_NONE;
490 }
491
492 das16m1_handler(dev, status);
493
494
495 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
496
497 spin_unlock(&dev->spinlock);
498 return IRQ_HANDLED;
499}
500
501static int das16m1_irq_bits(unsigned int irq)
502{
503 switch (irq) {
504 case 10:
505 return 0x0;
506 case 11:
507 return 0x1;
508 case 12:
509 return 0x2;
510 case 15:
511 return 0x3;
512 case 2:
513 return 0x4;
514 case 3:
515 return 0x5;
516 case 5:
517 return 0x6;
518 case 7:
519 return 0x7;
520 default:
521 return 0x0;
522 }
523}
524
525
526
527
528
529
530static int das16m1_attach(struct comedi_device *dev,
531 struct comedi_devconfig *it)
532{
533 struct das16m1_private_struct *devpriv;
534 struct comedi_subdevice *s;
535 int ret;
536
537 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
538 if (!devpriv)
539 return -ENOMEM;
540
541 ret = comedi_request_region(dev, it->options[0], 0x10);
542 if (ret)
543 return ret;
544
545 ret = __comedi_request_region(dev, dev->iobase + DAS16M1_82C55,
546 DAS16M1_SIZE2);
547 if (ret)
548 return ret;
549 devpriv->extra_iobase = dev->iobase + DAS16M1_82C55;
550
551
552 if ((1 << it->options[1]) & 0xdcfc) {
553 ret = request_irq(it->options[1], das16m1_interrupt, 0,
554 dev->board_name, dev);
555 if (ret == 0)
556 dev->irq = it->options[1];
557 }
558
559 dev->pacer = comedi_8254_init(dev->iobase + DAS16M1_8254_SECOND,
560 I8254_OSC_BASE_10MHZ, I8254_IO8, 0);
561 if (!dev->pacer)
562 return -ENOMEM;
563
564 devpriv->counter = comedi_8254_init(dev->iobase + DAS16M1_8254_FIRST,
565 0, I8254_IO8, 0);
566 if (!devpriv->counter)
567 return -ENOMEM;
568
569 ret = comedi_alloc_subdevices(dev, 4);
570 if (ret)
571 return ret;
572
573 s = &dev->subdevices[0];
574
575 s->type = COMEDI_SUBD_AI;
576 s->subdev_flags = SDF_READABLE | SDF_DIFF;
577 s->n_chan = 8;
578 s->maxdata = (1 << 12) - 1;
579 s->range_table = &range_das16m1;
580 s->insn_read = das16m1_ai_rinsn;
581 if (dev->irq) {
582 dev->read_subdev = s;
583 s->subdev_flags |= SDF_CMD_READ;
584 s->len_chanlist = 256;
585 s->do_cmdtest = das16m1_cmd_test;
586 s->do_cmd = das16m1_cmd_exec;
587 s->cancel = das16m1_cancel;
588 s->poll = das16m1_poll;
589 }
590
591 s = &dev->subdevices[1];
592
593 s->type = COMEDI_SUBD_DI;
594 s->subdev_flags = SDF_READABLE;
595 s->n_chan = 4;
596 s->maxdata = 1;
597 s->range_table = &range_digital;
598 s->insn_bits = das16m1_di_rbits;
599
600 s = &dev->subdevices[2];
601
602 s->type = COMEDI_SUBD_DO;
603 s->subdev_flags = SDF_WRITABLE;
604 s->n_chan = 4;
605 s->maxdata = 1;
606 s->range_table = &range_digital;
607 s->insn_bits = das16m1_do_wbits;
608
609 s = &dev->subdevices[3];
610
611 ret = subdev_8255_init(dev, s, NULL, DAS16M1_82C55);
612 if (ret)
613 return ret;
614
615
616 outb(0, dev->iobase + DAS16M1_DIO);
617
618
619 devpriv->control_state = das16m1_irq_bits(dev->irq) << 4;
620 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
621
622 return 0;
623}
624
625static void das16m1_detach(struct comedi_device *dev)
626{
627 struct das16m1_private_struct *devpriv = dev->private;
628
629 if (devpriv) {
630 if (devpriv->extra_iobase)
631 release_region(devpriv->extra_iobase, DAS16M1_SIZE2);
632 kfree(devpriv->counter);
633 }
634 comedi_legacy_detach(dev);
635}
636
637static struct comedi_driver das16m1_driver = {
638 .driver_name = "das16m1",
639 .module = THIS_MODULE,
640 .attach = das16m1_attach,
641 .detach = das16m1_detach,
642};
643module_comedi_driver(das16m1_driver);
644
645MODULE_AUTHOR("Comedi http://www.comedi.org");
646MODULE_DESCRIPTION("Comedi low-level driver");
647MODULE_LICENSE("GPL");
648