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/interrupt.h>
57#include <linux/delay.h>
58
59#include "../comedidev.h"
60
61#include "comedi_8254.h"
62
63#define N_CHAN_AI 8
64
65
66
67#define DAS800_LSB 0
68#define FIFO_EMPTY 0x1
69#define FIFO_OVF 0x2
70#define DAS800_MSB 1
71#define DAS800_CONTROL1 2
72#define CONTROL1_INTE 0x8
73#define DAS800_CONV_CONTROL 2
74#define ITE 0x1
75#define CASC 0x2
76#define DTEN 0x4
77#define IEOC 0x8
78#define EACS 0x10
79#define CONV_HCEN 0x80
80#define DAS800_SCAN_LIMITS 2
81#define DAS800_STATUS 2
82#define IRQ 0x8
83#define BUSY 0x80
84#define DAS800_GAIN 3
85#define CIO_FFOV 0x8
86#define CIO_ENHF 0x90
87#define CONTROL1 0x80
88#define CONV_CONTROL 0xa0
89#define SCAN_LIMITS 0xc0
90#define ID 0xe0
91#define DAS800_8254 4
92#define DAS800_STATUS2 7
93#define STATUS2_HCEN 0x80
94#define STATUS2_INTE 0X20
95#define DAS800_ID 7
96
97#define DAS802_16_HALF_FIFO_SZ 128
98
99struct das800_board {
100 const char *name;
101 int ai_speed;
102 const struct comedi_lrange *ai_range;
103 int resolution;
104};
105
106static const struct comedi_lrange range_das801_ai = {
107 9, {
108 BIP_RANGE(5),
109 BIP_RANGE(10),
110 UNI_RANGE(10),
111 BIP_RANGE(0.5),
112 UNI_RANGE(1),
113 BIP_RANGE(0.05),
114 UNI_RANGE(0.1),
115 BIP_RANGE(0.01),
116 UNI_RANGE(0.02)
117 }
118};
119
120static const struct comedi_lrange range_cio_das801_ai = {
121 9, {
122 BIP_RANGE(5),
123 BIP_RANGE(10),
124 UNI_RANGE(10),
125 BIP_RANGE(0.5),
126 UNI_RANGE(1),
127 BIP_RANGE(0.05),
128 UNI_RANGE(0.1),
129 BIP_RANGE(0.005),
130 UNI_RANGE(0.01)
131 }
132};
133
134static const struct comedi_lrange range_das802_ai = {
135 9, {
136 BIP_RANGE(5),
137 BIP_RANGE(10),
138 UNI_RANGE(10),
139 BIP_RANGE(2.5),
140 UNI_RANGE(5),
141 BIP_RANGE(1.25),
142 UNI_RANGE(2.5),
143 BIP_RANGE(0.625),
144 UNI_RANGE(1.25)
145 }
146};
147
148static const struct comedi_lrange range_das80216_ai = {
149 8, {
150 BIP_RANGE(10),
151 UNI_RANGE(10),
152 BIP_RANGE(5),
153 UNI_RANGE(5),
154 BIP_RANGE(2.5),
155 UNI_RANGE(2.5),
156 BIP_RANGE(1.25),
157 UNI_RANGE(1.25)
158 }
159};
160
161enum das800_boardinfo {
162 BOARD_DAS800,
163 BOARD_CIODAS800,
164 BOARD_DAS801,
165 BOARD_CIODAS801,
166 BOARD_DAS802,
167 BOARD_CIODAS802,
168 BOARD_CIODAS80216,
169};
170
171static const struct das800_board das800_boards[] = {
172 [BOARD_DAS800] = {
173 .name = "das-800",
174 .ai_speed = 25000,
175 .ai_range = &range_bipolar5,
176 .resolution = 12,
177 },
178 [BOARD_CIODAS800] = {
179 .name = "cio-das800",
180 .ai_speed = 20000,
181 .ai_range = &range_bipolar5,
182 .resolution = 12,
183 },
184 [BOARD_DAS801] = {
185 .name = "das-801",
186 .ai_speed = 25000,
187 .ai_range = &range_das801_ai,
188 .resolution = 12,
189 },
190 [BOARD_CIODAS801] = {
191 .name = "cio-das801",
192 .ai_speed = 20000,
193 .ai_range = &range_cio_das801_ai,
194 .resolution = 12,
195 },
196 [BOARD_DAS802] = {
197 .name = "das-802",
198 .ai_speed = 25000,
199 .ai_range = &range_das802_ai,
200 .resolution = 12,
201 },
202 [BOARD_CIODAS802] = {
203 .name = "cio-das802",
204 .ai_speed = 20000,
205 .ai_range = &range_das802_ai,
206 .resolution = 12,
207 },
208 [BOARD_CIODAS80216] = {
209 .name = "cio-das802/16",
210 .ai_speed = 10000,
211 .ai_range = &range_das80216_ai,
212 .resolution = 16,
213 },
214};
215
216struct das800_private {
217 unsigned int do_bits;
218};
219
220static void das800_ind_write(struct comedi_device *dev,
221 unsigned val, unsigned reg)
222{
223
224
225
226
227 outb(reg, dev->iobase + DAS800_GAIN);
228 outb(val, dev->iobase + 2);
229}
230
231static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
232{
233
234
235
236
237 outb(reg, dev->iobase + DAS800_GAIN);
238 return inb(dev->iobase + 7);
239}
240
241static void das800_enable(struct comedi_device *dev)
242{
243 const struct das800_board *board = dev->board_ptr;
244 struct das800_private *devpriv = dev->private;
245 unsigned long irq_flags;
246
247 spin_lock_irqsave(&dev->spinlock, irq_flags);
248
249 if (board->resolution == 16)
250 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
251
252 das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
253
254 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
255 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
256}
257
258static void das800_disable(struct comedi_device *dev)
259{
260 unsigned long irq_flags;
261
262 spin_lock_irqsave(&dev->spinlock, irq_flags);
263
264 das800_ind_write(dev, 0x0, CONV_CONTROL);
265 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
266}
267
268static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
269{
270 das800_disable(dev);
271 return 0;
272}
273
274static int das800_ai_check_chanlist(struct comedi_device *dev,
275 struct comedi_subdevice *s,
276 struct comedi_cmd *cmd)
277{
278 unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
279 unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
280 int i;
281
282 for (i = 1; i < cmd->chanlist_len; i++) {
283 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
284 unsigned int range = CR_RANGE(cmd->chanlist[i]);
285
286 if (chan != (chan0 + i) % s->n_chan) {
287 dev_dbg(dev->class_dev,
288 "chanlist must be consecutive, counting upwards\n");
289 return -EINVAL;
290 }
291
292 if (range != range0) {
293 dev_dbg(dev->class_dev,
294 "chanlist must all have the same gain\n");
295 return -EINVAL;
296 }
297 }
298
299 return 0;
300}
301
302static int das800_ai_do_cmdtest(struct comedi_device *dev,
303 struct comedi_subdevice *s,
304 struct comedi_cmd *cmd)
305{
306 const struct das800_board *board = dev->board_ptr;
307 int err = 0;
308
309
310
311 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
312 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
313 err |= comedi_check_trigger_src(&cmd->convert_src,
314 TRIG_TIMER | TRIG_EXT);
315 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
316 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
317
318 if (err)
319 return 1;
320
321
322
323 err |= comedi_check_trigger_is_unique(cmd->start_src);
324 err |= comedi_check_trigger_is_unique(cmd->convert_src);
325 err |= comedi_check_trigger_is_unique(cmd->stop_src);
326
327
328
329 if (err)
330 return 2;
331
332
333
334 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
335
336 if (cmd->convert_src == TRIG_TIMER) {
337 err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
338 board->ai_speed);
339 }
340
341 err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
342 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
343 cmd->chanlist_len);
344
345 if (cmd->stop_src == TRIG_COUNT)
346 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
347 else
348 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
349
350 if (err)
351 return 3;
352
353
354
355 if (cmd->convert_src == TRIG_TIMER) {
356 unsigned int arg = cmd->convert_arg;
357
358 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
359 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
360 }
361
362 if (err)
363 return 4;
364
365
366 if (cmd->chanlist && cmd->chanlist_len > 0)
367 err |= das800_ai_check_chanlist(dev, s, cmd);
368
369 if (err)
370 return 5;
371
372 return 0;
373}
374
375static int das800_ai_do_cmd(struct comedi_device *dev,
376 struct comedi_subdevice *s)
377{
378 const struct das800_board *board = dev->board_ptr;
379 struct comedi_async *async = s->async;
380 struct comedi_cmd *cmd = &async->cmd;
381 unsigned int gain = CR_RANGE(cmd->chanlist[0]);
382 unsigned int start_chan = CR_CHAN(cmd->chanlist[0]);
383 unsigned int end_chan = (start_chan + cmd->chanlist_len - 1) % 8;
384 unsigned int scan_chans = (end_chan << 3) | start_chan;
385 int conv_bits;
386 unsigned long irq_flags;
387
388 das800_disable(dev);
389
390 spin_lock_irqsave(&dev->spinlock, irq_flags);
391
392 das800_ind_write(dev, scan_chans, SCAN_LIMITS);
393 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
394
395
396 if (board->resolution == 12 && gain > 0)
397 gain += 0x7;
398 gain &= 0xf;
399 outb(gain, dev->iobase + DAS800_GAIN);
400
401
402
403
404 conv_bits = 0;
405 conv_bits |= EACS | IEOC;
406 if (cmd->start_src == TRIG_EXT)
407 conv_bits |= DTEN;
408 if (cmd->convert_src == TRIG_TIMER) {
409 conv_bits |= CASC | ITE;
410 comedi_8254_update_divisors(dev->pacer);
411 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
412 }
413
414 spin_lock_irqsave(&dev->spinlock, irq_flags);
415 das800_ind_write(dev, conv_bits, CONV_CONTROL);
416 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
417
418 das800_enable(dev);
419 return 0;
420}
421
422static unsigned int das800_ai_get_sample(struct comedi_device *dev)
423{
424 unsigned int lsb = inb(dev->iobase + DAS800_LSB);
425 unsigned int msb = inb(dev->iobase + DAS800_MSB);
426
427 return (msb << 8) | lsb;
428}
429
430static irqreturn_t das800_interrupt(int irq, void *d)
431{
432 struct comedi_device *dev = d;
433 struct das800_private *devpriv = dev->private;
434 struct comedi_subdevice *s = dev->read_subdev;
435 struct comedi_async *async;
436 struct comedi_cmd *cmd;
437 unsigned long irq_flags;
438 unsigned int status;
439 unsigned int val;
440 bool fifo_empty;
441 bool fifo_overflow;
442 int i;
443
444 status = inb(dev->iobase + DAS800_STATUS);
445 if (!(status & IRQ))
446 return IRQ_NONE;
447 if (!dev->attached)
448 return IRQ_HANDLED;
449
450 async = s->async;
451 cmd = &async->cmd;
452
453 spin_lock_irqsave(&dev->spinlock, irq_flags);
454 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
455
456
457
458
459
460
461 if (status == 0) {
462 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
463 return IRQ_HANDLED;
464 }
465
466 for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
467 val = das800_ai_get_sample(dev);
468 if (s->maxdata == 0x0fff) {
469 fifo_empty = !!(val & FIFO_EMPTY);
470 fifo_overflow = !!(val & FIFO_OVF);
471 } else {
472
473 fifo_empty = false;
474 fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
475 CIO_FFOV);
476 }
477 if (fifo_empty || fifo_overflow)
478 break;
479
480 if (s->maxdata == 0x0fff)
481 val >>= 4;
482
483 val &= s->maxdata;
484 comedi_buf_write_samples(s, &val, 1);
485
486 if (cmd->stop_src == TRIG_COUNT &&
487 async->scans_done >= cmd->stop_arg) {
488 async->events |= COMEDI_CB_EOA;
489 break;
490 }
491 }
492
493 if (fifo_overflow) {
494 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
495 async->events |= COMEDI_CB_ERROR;
496 comedi_handle_events(dev, s);
497 return IRQ_HANDLED;
498 }
499
500 if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
501
502
503
504
505 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
506 CONTROL1);
507 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
508 } else {
509
510 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
511 das800_disable(dev);
512 }
513 comedi_handle_events(dev, s);
514 return IRQ_HANDLED;
515}
516
517static int das800_ai_eoc(struct comedi_device *dev,
518 struct comedi_subdevice *s,
519 struct comedi_insn *insn,
520 unsigned long context)
521{
522 unsigned int status;
523
524 status = inb(dev->iobase + DAS800_STATUS);
525 if ((status & BUSY) == 0)
526 return 0;
527 return -EBUSY;
528}
529
530static int das800_ai_insn_read(struct comedi_device *dev,
531 struct comedi_subdevice *s,
532 struct comedi_insn *insn,
533 unsigned int *data)
534{
535 struct das800_private *devpriv = dev->private;
536 unsigned int chan = CR_CHAN(insn->chanspec);
537 unsigned int range = CR_RANGE(insn->chanspec);
538 unsigned long irq_flags;
539 unsigned int val;
540 int ret;
541 int i;
542
543 das800_disable(dev);
544
545
546 spin_lock_irqsave(&dev->spinlock, irq_flags);
547 das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
548 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
549
550
551 if (s->maxdata == 0x0fff && range)
552 range += 0x7;
553 range &= 0xf;
554 outb(range, dev->iobase + DAS800_GAIN);
555
556 udelay(5);
557
558 for (i = 0; i < insn->n; i++) {
559
560 outb_p(0, dev->iobase + DAS800_MSB);
561
562 ret = comedi_timeout(dev, s, insn, das800_ai_eoc, 0);
563 if (ret)
564 return ret;
565
566 val = das800_ai_get_sample(dev);
567 if (s->maxdata == 0x0fff)
568 val >>= 4;
569 data[i] = val & s->maxdata;
570 }
571
572 return insn->n;
573}
574
575static int das800_di_insn_bits(struct comedi_device *dev,
576 struct comedi_subdevice *s,
577 struct comedi_insn *insn,
578 unsigned int *data)
579{
580 data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
581
582 return insn->n;
583}
584
585static int das800_do_insn_bits(struct comedi_device *dev,
586 struct comedi_subdevice *s,
587 struct comedi_insn *insn,
588 unsigned int *data)
589{
590 struct das800_private *devpriv = dev->private;
591 unsigned long irq_flags;
592
593 if (comedi_dio_update_state(s, data)) {
594 devpriv->do_bits = s->state << 4;
595
596 spin_lock_irqsave(&dev->spinlock, irq_flags);
597 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
598 CONTROL1);
599 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
600 }
601
602 data[1] = s->state;
603
604 return insn->n;
605}
606
607static const struct das800_board *das800_probe(struct comedi_device *dev)
608{
609 const struct das800_board *board = dev->board_ptr;
610 int index = board ? board - das800_boards : -EINVAL;
611 int id_bits;
612 unsigned long irq_flags;
613
614
615
616
617
618
619
620
621
622
623
624 spin_lock_irqsave(&dev->spinlock, irq_flags);
625 id_bits = das800_ind_read(dev, ID) & 0x3;
626 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
627
628 switch (id_bits) {
629 case 0x0:
630 if (index == BOARD_DAS800 || index == BOARD_CIODAS800)
631 return board;
632 index = BOARD_DAS800;
633 break;
634 case 0x2:
635 if (index == BOARD_DAS801 || index == BOARD_CIODAS801)
636 return board;
637 index = BOARD_DAS801;
638 break;
639 case 0x3:
640 if (index == BOARD_DAS802 || index == BOARD_CIODAS802 ||
641 index == BOARD_CIODAS80216)
642 return board;
643 index = BOARD_DAS802;
644 break;
645 default:
646 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
647 id_bits);
648 return NULL;
649 }
650 dev_dbg(dev->class_dev, "Board model (probed): %s series\n",
651 das800_boards[index].name);
652
653 return &das800_boards[index];
654}
655
656static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
657{
658 const struct das800_board *board;
659 struct das800_private *devpriv;
660 struct comedi_subdevice *s;
661 unsigned int irq = it->options[1];
662 unsigned long irq_flags;
663 int ret;
664
665 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
666 if (!devpriv)
667 return -ENOMEM;
668
669 ret = comedi_request_region(dev, it->options[0], 0x8);
670 if (ret)
671 return ret;
672
673 board = das800_probe(dev);
674 if (!board)
675 return -ENODEV;
676 dev->board_ptr = board;
677 dev->board_name = board->name;
678
679 if (irq > 1 && irq <= 7) {
680 ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
681 dev);
682 if (ret == 0)
683 dev->irq = irq;
684 }
685
686 dev->pacer = comedi_8254_init(dev->iobase + DAS800_8254,
687 I8254_OSC_BASE_1MHZ, I8254_IO8, 0);
688 if (!dev->pacer)
689 return -ENOMEM;
690
691 ret = comedi_alloc_subdevices(dev, 3);
692 if (ret)
693 return ret;
694
695
696 s = &dev->subdevices[0];
697 dev->read_subdev = s;
698 s->type = COMEDI_SUBD_AI;
699 s->subdev_flags = SDF_READABLE | SDF_GROUND;
700 s->n_chan = 8;
701 s->maxdata = (1 << board->resolution) - 1;
702 s->range_table = board->ai_range;
703 s->insn_read = das800_ai_insn_read;
704 if (dev->irq) {
705 s->subdev_flags |= SDF_CMD_READ;
706 s->len_chanlist = 8;
707 s->do_cmdtest = das800_ai_do_cmdtest;
708 s->do_cmd = das800_ai_do_cmd;
709 s->cancel = das800_cancel;
710 }
711
712
713 s = &dev->subdevices[1];
714 s->type = COMEDI_SUBD_DI;
715 s->subdev_flags = SDF_READABLE;
716 s->n_chan = 3;
717 s->maxdata = 1;
718 s->range_table = &range_digital;
719 s->insn_bits = das800_di_insn_bits;
720
721
722 s = &dev->subdevices[2];
723 s->type = COMEDI_SUBD_DO;
724 s->subdev_flags = SDF_WRITABLE;
725 s->n_chan = 4;
726 s->maxdata = 1;
727 s->range_table = &range_digital;
728 s->insn_bits = das800_do_insn_bits;
729
730 das800_disable(dev);
731
732
733 spin_lock_irqsave(&dev->spinlock, irq_flags);
734 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
735 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
736
737 return 0;
738};
739
740static struct comedi_driver driver_das800 = {
741 .driver_name = "das800",
742 .module = THIS_MODULE,
743 .attach = das800_attach,
744 .detach = comedi_legacy_detach,
745 .num_names = ARRAY_SIZE(das800_boards),
746 .board_name = &das800_boards[0].name,
747 .offset = sizeof(struct das800_board),
748};
749module_comedi_driver(driver_das800);
750
751MODULE_AUTHOR("Comedi http://www.comedi.org");
752MODULE_DESCRIPTION("Comedi low-level driver");
753MODULE_LICENSE("GPL");
754