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