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