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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112#include <linux/module.h>
113#include <linux/interrupt.h>
114#include <linux/gfp.h>
115#include <linux/delay.h>
116#include <linux/io.h>
117
118#include "../comedidev.h"
119
120#include "comedi_isadma.h"
121#include "comedi_8254.h"
122
123
124
125
126#define PCL812_TIMER_BASE 0x00
127#define PCL812_AI_LSB_REG 0x04
128#define PCL812_AI_MSB_REG 0x05
129#define PCL812_AI_MSB_DRDY BIT(4)
130#define PCL812_AO_LSB_REG(x) (0x04 + ((x) * 2))
131#define PCL812_AO_MSB_REG(x) (0x05 + ((x) * 2))
132#define PCL812_DI_LSB_REG 0x06
133#define PCL812_DI_MSB_REG 0x07
134#define PCL812_STATUS_REG 0x08
135#define PCL812_STATUS_DRDY BIT(5)
136#define PCL812_RANGE_REG 0x09
137#define PCL812_MUX_REG 0x0a
138#define PCL812_MUX_CHAN(x) ((x) << 0)
139#define PCL812_MUX_CS0 BIT(4)
140#define PCL812_MUX_CS1 BIT(5)
141#define PCL812_CTRL_REG 0x0b
142#define PCL812_CTRL_TRIG(x) (((x) & 0x7) << 0)
143#define PCL812_CTRL_DISABLE_TRIG PCL812_CTRL_TRIG(0)
144#define PCL812_CTRL_SOFT_TRIG PCL812_CTRL_TRIG(1)
145#define PCL812_CTRL_PACER_DMA_TRIG PCL812_CTRL_TRIG(2)
146#define PCL812_CTRL_PACER_EOC_TRIG PCL812_CTRL_TRIG(6)
147#define PCL812_SOFTTRIG_REG 0x0c
148#define PCL812_DO_LSB_REG 0x0d
149#define PCL812_DO_MSB_REG 0x0e
150
151#define MAX_CHANLIST_LEN 256
152
153static const struct comedi_lrange range_pcl812pg_ai = {
154 5, {
155 BIP_RANGE(5),
156 BIP_RANGE(2.5),
157 BIP_RANGE(1.25),
158 BIP_RANGE(0.625),
159 BIP_RANGE(0.3125)
160 }
161};
162
163static const struct comedi_lrange range_pcl812pg2_ai = {
164 5, {
165 BIP_RANGE(10),
166 BIP_RANGE(5),
167 BIP_RANGE(2.5),
168 BIP_RANGE(1.25),
169 BIP_RANGE(0.625)
170 }
171};
172
173static const struct comedi_lrange range812_bipolar1_25 = {
174 1, {
175 BIP_RANGE(1.25)
176 }
177};
178
179static const struct comedi_lrange range812_bipolar0_625 = {
180 1, {
181 BIP_RANGE(0.625)
182 }
183};
184
185static const struct comedi_lrange range812_bipolar0_3125 = {
186 1, {
187 BIP_RANGE(0.3125)
188 }
189};
190
191static const struct comedi_lrange range_pcl813b_ai = {
192 4, {
193 BIP_RANGE(5),
194 BIP_RANGE(2.5),
195 BIP_RANGE(1.25),
196 BIP_RANGE(0.625)
197 }
198};
199
200static const struct comedi_lrange range_pcl813b2_ai = {
201 4, {
202 UNI_RANGE(10),
203 UNI_RANGE(5),
204 UNI_RANGE(2.5),
205 UNI_RANGE(1.25)
206 }
207};
208
209static const struct comedi_lrange range_iso813_1_ai = {
210 5, {
211 BIP_RANGE(5),
212 BIP_RANGE(2.5),
213 BIP_RANGE(1.25),
214 BIP_RANGE(0.625),
215 BIP_RANGE(0.3125)
216 }
217};
218
219static const struct comedi_lrange range_iso813_1_2_ai = {
220 5, {
221 UNI_RANGE(10),
222 UNI_RANGE(5),
223 UNI_RANGE(2.5),
224 UNI_RANGE(1.25),
225 UNI_RANGE(0.625)
226 }
227};
228
229static const struct comedi_lrange range_iso813_2_ai = {
230 4, {
231 BIP_RANGE(5),
232 BIP_RANGE(2.5),
233 BIP_RANGE(1.25),
234 BIP_RANGE(0.625)
235 }
236};
237
238static const struct comedi_lrange range_iso813_2_2_ai = {
239 4, {
240 UNI_RANGE(10),
241 UNI_RANGE(5),
242 UNI_RANGE(2.5),
243 UNI_RANGE(1.25)
244 }
245};
246
247static const struct comedi_lrange range_acl8113_1_ai = {
248 4, {
249 BIP_RANGE(5),
250 BIP_RANGE(2.5),
251 BIP_RANGE(1.25),
252 BIP_RANGE(0.625)
253 }
254};
255
256static const struct comedi_lrange range_acl8113_1_2_ai = {
257 4, {
258 UNI_RANGE(10),
259 UNI_RANGE(5),
260 UNI_RANGE(2.5),
261 UNI_RANGE(1.25)
262 }
263};
264
265static const struct comedi_lrange range_acl8113_2_ai = {
266 3, {
267 BIP_RANGE(5),
268 BIP_RANGE(2.5),
269 BIP_RANGE(1.25)
270 }
271};
272
273static const struct comedi_lrange range_acl8113_2_2_ai = {
274 3, {
275 UNI_RANGE(10),
276 UNI_RANGE(5),
277 UNI_RANGE(2.5)
278 }
279};
280
281static const struct comedi_lrange range_acl8112dg_ai = {
282 9, {
283 BIP_RANGE(5),
284 BIP_RANGE(2.5),
285 BIP_RANGE(1.25),
286 BIP_RANGE(0.625),
287 UNI_RANGE(10),
288 UNI_RANGE(5),
289 UNI_RANGE(2.5),
290 UNI_RANGE(1.25),
291 BIP_RANGE(10)
292 }
293};
294
295static const struct comedi_lrange range_acl8112hg_ai = {
296 12, {
297 BIP_RANGE(5),
298 BIP_RANGE(0.5),
299 BIP_RANGE(0.05),
300 BIP_RANGE(0.005),
301 UNI_RANGE(10),
302 UNI_RANGE(1),
303 UNI_RANGE(0.1),
304 UNI_RANGE(0.01),
305 BIP_RANGE(10),
306 BIP_RANGE(1),
307 BIP_RANGE(0.1),
308 BIP_RANGE(0.01)
309 }
310};
311
312static const struct comedi_lrange range_a821pgh_ai = {
313 4, {
314 BIP_RANGE(5),
315 BIP_RANGE(0.5),
316 BIP_RANGE(0.05),
317 BIP_RANGE(0.005)
318 }
319};
320
321enum pcl812_boardtype {
322 BOARD_PCL812PG = 0,
323 BOARD_PCL813B = 1,
324 BOARD_PCL812 = 2,
325 BOARD_PCL813 = 3,
326 BOARD_ISO813 = 5,
327 BOARD_ACL8113 = 6,
328 BOARD_ACL8112 = 7,
329 BOARD_ACL8216 = 8,
330 BOARD_A821 = 9,
331};
332
333struct pcl812_board {
334 const char *name;
335 enum pcl812_boardtype board_type;
336 int n_aichan;
337 int n_aochan;
338 unsigned int ai_ns_min;
339 const struct comedi_lrange *rangelist_ai;
340 unsigned int irq_bits;
341 unsigned int has_dma:1;
342 unsigned int has_16bit_ai:1;
343 unsigned int has_mpc508_mux:1;
344 unsigned int has_dio:1;
345};
346
347static const struct pcl812_board boardtypes[] = {
348 {
349 .name = "pcl812",
350 .board_type = BOARD_PCL812,
351 .n_aichan = 16,
352 .n_aochan = 2,
353 .ai_ns_min = 33000,
354 .rangelist_ai = &range_bipolar10,
355 .irq_bits = 0xdcfc,
356 .has_dma = 1,
357 .has_dio = 1,
358 }, {
359 .name = "pcl812pg",
360 .board_type = BOARD_PCL812PG,
361 .n_aichan = 16,
362 .n_aochan = 2,
363 .ai_ns_min = 33000,
364 .rangelist_ai = &range_pcl812pg_ai,
365 .irq_bits = 0xdcfc,
366 .has_dma = 1,
367 .has_dio = 1,
368 }, {
369 .name = "acl8112pg",
370 .board_type = BOARD_PCL812PG,
371 .n_aichan = 16,
372 .n_aochan = 2,
373 .ai_ns_min = 10000,
374 .rangelist_ai = &range_pcl812pg_ai,
375 .irq_bits = 0xdcfc,
376 .has_dma = 1,
377 .has_dio = 1,
378 }, {
379 .name = "acl8112dg",
380 .board_type = BOARD_ACL8112,
381 .n_aichan = 16,
382 .n_aochan = 2,
383 .ai_ns_min = 10000,
384 .rangelist_ai = &range_acl8112dg_ai,
385 .irq_bits = 0xdcfc,
386 .has_dma = 1,
387 .has_mpc508_mux = 1,
388 .has_dio = 1,
389 }, {
390 .name = "acl8112hg",
391 .board_type = BOARD_ACL8112,
392 .n_aichan = 16,
393 .n_aochan = 2,
394 .ai_ns_min = 10000,
395 .rangelist_ai = &range_acl8112hg_ai,
396 .irq_bits = 0xdcfc,
397 .has_dma = 1,
398 .has_mpc508_mux = 1,
399 .has_dio = 1,
400 }, {
401 .name = "a821pgl",
402 .board_type = BOARD_A821,
403 .n_aichan = 16,
404 .n_aochan = 1,
405 .ai_ns_min = 10000,
406 .rangelist_ai = &range_pcl813b_ai,
407 .irq_bits = 0x000c,
408 .has_dio = 1,
409 }, {
410 .name = "a821pglnda",
411 .board_type = BOARD_A821,
412 .n_aichan = 16,
413 .ai_ns_min = 10000,
414 .rangelist_ai = &range_pcl813b_ai,
415 .irq_bits = 0x000c,
416 }, {
417 .name = "a821pgh",
418 .board_type = BOARD_A821,
419 .n_aichan = 16,
420 .n_aochan = 1,
421 .ai_ns_min = 10000,
422 .rangelist_ai = &range_a821pgh_ai,
423 .irq_bits = 0x000c,
424 .has_dio = 1,
425 }, {
426 .name = "a822pgl",
427 .board_type = BOARD_ACL8112,
428 .n_aichan = 16,
429 .n_aochan = 2,
430 .ai_ns_min = 10000,
431 .rangelist_ai = &range_acl8112dg_ai,
432 .irq_bits = 0xdcfc,
433 .has_dma = 1,
434 .has_dio = 1,
435 }, {
436 .name = "a822pgh",
437 .board_type = BOARD_ACL8112,
438 .n_aichan = 16,
439 .n_aochan = 2,
440 .ai_ns_min = 10000,
441 .rangelist_ai = &range_acl8112hg_ai,
442 .irq_bits = 0xdcfc,
443 .has_dma = 1,
444 .has_dio = 1,
445 }, {
446 .name = "a823pgl",
447 .board_type = BOARD_ACL8112,
448 .n_aichan = 16,
449 .n_aochan = 2,
450 .ai_ns_min = 8000,
451 .rangelist_ai = &range_acl8112dg_ai,
452 .irq_bits = 0xdcfc,
453 .has_dma = 1,
454 .has_dio = 1,
455 }, {
456 .name = "a823pgh",
457 .board_type = BOARD_ACL8112,
458 .n_aichan = 16,
459 .n_aochan = 2,
460 .ai_ns_min = 8000,
461 .rangelist_ai = &range_acl8112hg_ai,
462 .irq_bits = 0xdcfc,
463 .has_dma = 1,
464 .has_dio = 1,
465 }, {
466 .name = "pcl813",
467 .board_type = BOARD_PCL813,
468 .n_aichan = 32,
469 .rangelist_ai = &range_pcl813b_ai,
470 }, {
471 .name = "pcl813b",
472 .board_type = BOARD_PCL813B,
473 .n_aichan = 32,
474 .rangelist_ai = &range_pcl813b_ai,
475 }, {
476 .name = "acl8113",
477 .board_type = BOARD_ACL8113,
478 .n_aichan = 32,
479 .rangelist_ai = &range_acl8113_1_ai,
480 }, {
481 .name = "iso813",
482 .board_type = BOARD_ISO813,
483 .n_aichan = 32,
484 .rangelist_ai = &range_iso813_1_ai,
485 }, {
486 .name = "acl8216",
487 .board_type = BOARD_ACL8216,
488 .n_aichan = 16,
489 .n_aochan = 2,
490 .ai_ns_min = 10000,
491 .rangelist_ai = &range_pcl813b2_ai,
492 .irq_bits = 0xdcfc,
493 .has_dma = 1,
494 .has_16bit_ai = 1,
495 .has_mpc508_mux = 1,
496 .has_dio = 1,
497 }, {
498 .name = "a826pg",
499 .board_type = BOARD_ACL8216,
500 .n_aichan = 16,
501 .n_aochan = 2,
502 .ai_ns_min = 10000,
503 .rangelist_ai = &range_pcl813b2_ai,
504 .irq_bits = 0xdcfc,
505 .has_dma = 1,
506 .has_16bit_ai = 1,
507 .has_dio = 1,
508 },
509};
510
511struct pcl812_private {
512 struct comedi_isadma *dma;
513 unsigned char range_correction;
514 unsigned int last_ai_chanspec;
515 unsigned char mode_reg_int;
516 unsigned int ai_poll_ptr;
517 unsigned int max_812_ai_mode0_rangewait;
518 unsigned int use_diff:1;
519 unsigned int use_mpc508:1;
520 unsigned int use_ext_trg:1;
521 unsigned int ai_dma:1;
522 unsigned int ai_eos:1;
523};
524
525static void pcl812_ai_setup_dma(struct comedi_device *dev,
526 struct comedi_subdevice *s,
527 unsigned int unread_samples)
528{
529 struct pcl812_private *devpriv = dev->private;
530 struct comedi_isadma *dma = devpriv->dma;
531 struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
532 unsigned int bytes;
533 unsigned int max_samples;
534 unsigned int nsamples;
535
536 comedi_isadma_disable(dma->chan);
537
538
539 bytes = devpriv->ai_eos ? comedi_bytes_per_scan(s) : desc->maxsize;
540 max_samples = comedi_bytes_to_samples(s, bytes);
541
542
543
544
545
546 nsamples = comedi_nsamples_left(s, max_samples + unread_samples);
547 if (nsamples > unread_samples) {
548 nsamples -= unread_samples;
549 desc->size = comedi_samples_to_bytes(s, nsamples);
550 comedi_isadma_program(desc);
551 }
552}
553
554static void pcl812_ai_set_chan_range(struct comedi_device *dev,
555 unsigned int chanspec, char wait)
556{
557 struct pcl812_private *devpriv = dev->private;
558 unsigned int chan = CR_CHAN(chanspec);
559 unsigned int range = CR_RANGE(chanspec);
560 unsigned int mux = 0;
561
562 if (chanspec == devpriv->last_ai_chanspec)
563 return;
564
565 devpriv->last_ai_chanspec = chanspec;
566
567 if (devpriv->use_mpc508) {
568 if (devpriv->use_diff) {
569 mux |= PCL812_MUX_CS0 | PCL812_MUX_CS1;
570 } else {
571 if (chan < 8)
572 mux |= PCL812_MUX_CS0;
573 else
574 mux |= PCL812_MUX_CS1;
575 }
576 }
577
578 outb(mux | PCL812_MUX_CHAN(chan), dev->iobase + PCL812_MUX_REG);
579 outb(range + devpriv->range_correction, dev->iobase + PCL812_RANGE_REG);
580
581 if (wait)
582
583
584
585
586 udelay(devpriv->max_812_ai_mode0_rangewait);
587}
588
589static void pcl812_ai_clear_eoc(struct comedi_device *dev)
590{
591
592 outb(0, dev->iobase + PCL812_STATUS_REG);
593}
594
595static void pcl812_ai_soft_trig(struct comedi_device *dev)
596{
597
598 outb(255, dev->iobase + PCL812_SOFTTRIG_REG);
599}
600
601static unsigned int pcl812_ai_get_sample(struct comedi_device *dev,
602 struct comedi_subdevice *s)
603{
604 unsigned int val;
605
606 val = inb(dev->iobase + PCL812_AI_MSB_REG) << 8;
607 val |= inb(dev->iobase + PCL812_AI_LSB_REG);
608
609 return val & s->maxdata;
610}
611
612static int pcl812_ai_eoc(struct comedi_device *dev,
613 struct comedi_subdevice *s,
614 struct comedi_insn *insn,
615 unsigned long context)
616{
617 unsigned int status;
618
619 if (s->maxdata > 0x0fff) {
620 status = inb(dev->iobase + PCL812_STATUS_REG);
621 if ((status & PCL812_STATUS_DRDY) == 0)
622 return 0;
623 } else {
624 status = inb(dev->iobase + PCL812_AI_MSB_REG);
625 if ((status & PCL812_AI_MSB_DRDY) == 0)
626 return 0;
627 }
628 return -EBUSY;
629}
630
631static int pcl812_ai_cmdtest(struct comedi_device *dev,
632 struct comedi_subdevice *s, struct comedi_cmd *cmd)
633{
634 const struct pcl812_board *board = dev->board_ptr;
635 struct pcl812_private *devpriv = dev->private;
636 int err = 0;
637 unsigned int flags;
638
639
640
641 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
642 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
643
644 if (devpriv->use_ext_trg)
645 flags = TRIG_EXT;
646 else
647 flags = TRIG_TIMER;
648 err |= comedi_check_trigger_src(&cmd->convert_src, flags);
649
650 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
651 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
652
653 if (err)
654 return 1;
655
656
657
658 err |= comedi_check_trigger_is_unique(cmd->stop_src);
659
660
661
662 if (err)
663 return 2;
664
665
666
667 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
668 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
669
670 if (cmd->convert_src == TRIG_TIMER) {
671 err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
672 board->ai_ns_min);
673 } else {
674 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
675 }
676
677 err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
678 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
679 cmd->chanlist_len);
680
681 if (cmd->stop_src == TRIG_COUNT)
682 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
683 else
684 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
685
686 if (err)
687 return 3;
688
689
690
691 if (cmd->convert_src == TRIG_TIMER) {
692 unsigned int arg = cmd->convert_arg;
693
694 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
695 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
696 }
697
698 if (err)
699 return 4;
700
701 return 0;
702}
703
704static int pcl812_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
705{
706 struct pcl812_private *devpriv = dev->private;
707 struct comedi_isadma *dma = devpriv->dma;
708 struct comedi_cmd *cmd = &s->async->cmd;
709 unsigned int ctrl = 0;
710 unsigned int i;
711
712 pcl812_ai_set_chan_range(dev, cmd->chanlist[0], 1);
713
714 if (dma) {
715 devpriv->ai_dma = 1;
716 for (i = 1; i < cmd->chanlist_len; i++)
717 if (cmd->chanlist[0] != cmd->chanlist[i]) {
718
719 devpriv->ai_dma = 0;
720 break;
721 }
722 } else {
723 devpriv->ai_dma = 0;
724 }
725
726 devpriv->ai_poll_ptr = 0;
727
728
729 if (cmd->flags & CMDF_WAKE_EOS) {
730 devpriv->ai_eos = 1;
731
732
733 if (cmd->chanlist_len == 1)
734 devpriv->ai_dma = 0;
735 }
736
737 if (devpriv->ai_dma) {
738
739 dma->cur_dma = 0;
740 pcl812_ai_setup_dma(dev, s, 0);
741 }
742
743 switch (cmd->convert_src) {
744 case TRIG_TIMER:
745 comedi_8254_update_divisors(dev->pacer);
746 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
747 break;
748 }
749
750 if (devpriv->ai_dma)
751 ctrl |= PCL812_CTRL_PACER_DMA_TRIG;
752 else
753 ctrl |= PCL812_CTRL_PACER_EOC_TRIG;
754 outb(devpriv->mode_reg_int | ctrl, dev->iobase + PCL812_CTRL_REG);
755
756 return 0;
757}
758
759static bool pcl812_ai_next_chan(struct comedi_device *dev,
760 struct comedi_subdevice *s)
761{
762 struct comedi_cmd *cmd = &s->async->cmd;
763
764 if (cmd->stop_src == TRIG_COUNT &&
765 s->async->scans_done >= cmd->stop_arg) {
766 s->async->events |= COMEDI_CB_EOA;
767 return false;
768 }
769
770 return true;
771}
772
773static void pcl812_handle_eoc(struct comedi_device *dev,
774 struct comedi_subdevice *s)
775{
776 struct comedi_cmd *cmd = &s->async->cmd;
777 unsigned int chan = s->async->cur_chan;
778 unsigned int next_chan;
779 unsigned short val;
780
781 if (pcl812_ai_eoc(dev, s, NULL, 0)) {
782 dev_dbg(dev->class_dev, "A/D cmd IRQ without DRDY!\n");
783 s->async->events |= COMEDI_CB_ERROR;
784 return;
785 }
786
787 val = pcl812_ai_get_sample(dev, s);
788 comedi_buf_write_samples(s, &val, 1);
789
790
791 next_chan = s->async->cur_chan;
792 if (cmd->chanlist[chan] != cmd->chanlist[next_chan])
793 pcl812_ai_set_chan_range(dev, cmd->chanlist[next_chan], 0);
794
795 pcl812_ai_next_chan(dev, s);
796}
797
798static void transfer_from_dma_buf(struct comedi_device *dev,
799 struct comedi_subdevice *s,
800 unsigned short *ptr,
801 unsigned int bufptr, unsigned int len)
802{
803 unsigned int i;
804 unsigned short val;
805
806 for (i = len; i; i--) {
807 val = ptr[bufptr++];
808 comedi_buf_write_samples(s, &val, 1);
809
810 if (!pcl812_ai_next_chan(dev, s))
811 break;
812 }
813}
814
815static void pcl812_handle_dma(struct comedi_device *dev,
816 struct comedi_subdevice *s)
817{
818 struct pcl812_private *devpriv = dev->private;
819 struct comedi_isadma *dma = devpriv->dma;
820 struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
821 unsigned int nsamples;
822 int bufptr;
823
824 nsamples = comedi_bytes_to_samples(s, desc->size) -
825 devpriv->ai_poll_ptr;
826 bufptr = devpriv->ai_poll_ptr;
827 devpriv->ai_poll_ptr = 0;
828
829
830 dma->cur_dma = 1 - dma->cur_dma;
831 pcl812_ai_setup_dma(dev, s, nsamples);
832
833 transfer_from_dma_buf(dev, s, desc->virt_addr, bufptr, nsamples);
834}
835
836static irqreturn_t pcl812_interrupt(int irq, void *d)
837{
838 struct comedi_device *dev = d;
839 struct comedi_subdevice *s = dev->read_subdev;
840 struct pcl812_private *devpriv = dev->private;
841
842 if (!dev->attached) {
843 pcl812_ai_clear_eoc(dev);
844 return IRQ_HANDLED;
845 }
846
847 if (devpriv->ai_dma)
848 pcl812_handle_dma(dev, s);
849 else
850 pcl812_handle_eoc(dev, s);
851
852 pcl812_ai_clear_eoc(dev);
853
854 comedi_handle_events(dev, s);
855 return IRQ_HANDLED;
856}
857
858static int pcl812_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
859{
860 struct pcl812_private *devpriv = dev->private;
861 struct comedi_isadma *dma = devpriv->dma;
862 struct comedi_isadma_desc *desc;
863 unsigned long flags;
864 unsigned int poll;
865 int ret;
866
867
868 if (!devpriv->ai_dma)
869 return 0;
870
871 spin_lock_irqsave(&dev->spinlock, flags);
872
873 poll = comedi_isadma_poll(dma);
874 poll = comedi_bytes_to_samples(s, poll);
875 if (poll > devpriv->ai_poll_ptr) {
876 desc = &dma->desc[dma->cur_dma];
877 transfer_from_dma_buf(dev, s, desc->virt_addr,
878 devpriv->ai_poll_ptr,
879 poll - devpriv->ai_poll_ptr);
880
881 devpriv->ai_poll_ptr = poll;
882
883 ret = comedi_buf_n_bytes_ready(s);
884 } else {
885
886 ret = 0;
887 }
888
889 spin_unlock_irqrestore(&dev->spinlock, flags);
890
891 return ret;
892}
893
894static int pcl812_ai_cancel(struct comedi_device *dev,
895 struct comedi_subdevice *s)
896{
897 struct pcl812_private *devpriv = dev->private;
898
899 if (devpriv->ai_dma)
900 comedi_isadma_disable(devpriv->dma->chan);
901
902 outb(devpriv->mode_reg_int | PCL812_CTRL_DISABLE_TRIG,
903 dev->iobase + PCL812_CTRL_REG);
904 comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
905 pcl812_ai_clear_eoc(dev);
906 return 0;
907}
908
909static int pcl812_ai_insn_read(struct comedi_device *dev,
910 struct comedi_subdevice *s,
911 struct comedi_insn *insn,
912 unsigned int *data)
913{
914 struct pcl812_private *devpriv = dev->private;
915 int ret = 0;
916 int i;
917
918 outb(devpriv->mode_reg_int | PCL812_CTRL_SOFT_TRIG,
919 dev->iobase + PCL812_CTRL_REG);
920
921 pcl812_ai_set_chan_range(dev, insn->chanspec, 1);
922
923 for (i = 0; i < insn->n; i++) {
924 pcl812_ai_clear_eoc(dev);
925 pcl812_ai_soft_trig(dev);
926
927 ret = comedi_timeout(dev, s, insn, pcl812_ai_eoc, 0);
928 if (ret)
929 break;
930
931 data[i] = pcl812_ai_get_sample(dev, s);
932 }
933 outb(devpriv->mode_reg_int | PCL812_CTRL_DISABLE_TRIG,
934 dev->iobase + PCL812_CTRL_REG);
935 pcl812_ai_clear_eoc(dev);
936
937 return ret ? ret : insn->n;
938}
939
940static int pcl812_ao_insn_write(struct comedi_device *dev,
941 struct comedi_subdevice *s,
942 struct comedi_insn *insn,
943 unsigned int *data)
944{
945 unsigned int chan = CR_CHAN(insn->chanspec);
946 unsigned int val = s->readback[chan];
947 int i;
948
949 for (i = 0; i < insn->n; i++) {
950 val = data[i];
951 outb(val & 0xff, dev->iobase + PCL812_AO_LSB_REG(chan));
952 outb((val >> 8) & 0x0f, dev->iobase + PCL812_AO_MSB_REG(chan));
953 }
954 s->readback[chan] = val;
955
956 return insn->n;
957}
958
959static int pcl812_di_insn_bits(struct comedi_device *dev,
960 struct comedi_subdevice *s,
961 struct comedi_insn *insn,
962 unsigned int *data)
963{
964 data[1] = inb(dev->iobase + PCL812_DI_LSB_REG) |
965 (inb(dev->iobase + PCL812_DI_MSB_REG) << 8);
966
967 return insn->n;
968}
969
970static int pcl812_do_insn_bits(struct comedi_device *dev,
971 struct comedi_subdevice *s,
972 struct comedi_insn *insn,
973 unsigned int *data)
974{
975 if (comedi_dio_update_state(s, data)) {
976 outb(s->state & 0xff, dev->iobase + PCL812_DO_LSB_REG);
977 outb((s->state >> 8), dev->iobase + PCL812_DO_MSB_REG);
978 }
979
980 data[1] = s->state;
981
982 return insn->n;
983}
984
985static void pcl812_reset(struct comedi_device *dev)
986{
987 const struct pcl812_board *board = dev->board_ptr;
988 struct pcl812_private *devpriv = dev->private;
989 unsigned int chan;
990
991
992 outb(devpriv->mode_reg_int | PCL812_CTRL_DISABLE_TRIG,
993 dev->iobase + PCL812_CTRL_REG);
994 pcl812_ai_clear_eoc(dev);
995
996
997
998
999
1000 devpriv->last_ai_chanspec = CR_PACK(16, 0, 0);
1001 pcl812_ai_set_chan_range(dev, CR_PACK(0, 0, 0), 0);
1002
1003
1004 for (chan = 0; chan < board->n_aochan; chan++) {
1005 outb(0, dev->iobase + PCL812_AO_LSB_REG(chan));
1006 outb(0, dev->iobase + PCL812_AO_MSB_REG(chan));
1007 }
1008
1009
1010 if (board->has_dio) {
1011 outb(0, dev->iobase + PCL812_DO_MSB_REG);
1012 outb(0, dev->iobase + PCL812_DO_LSB_REG);
1013 }
1014}
1015
1016static void pcl812_set_ai_range_table(struct comedi_device *dev,
1017 struct comedi_subdevice *s,
1018 struct comedi_devconfig *it)
1019{
1020 const struct pcl812_board *board = dev->board_ptr;
1021 struct pcl812_private *devpriv = dev->private;
1022
1023 switch (board->board_type) {
1024 case BOARD_PCL812PG:
1025 if (it->options[4] == 1)
1026 s->range_table = &range_pcl812pg2_ai;
1027 else
1028 s->range_table = board->rangelist_ai;
1029 break;
1030 case BOARD_PCL812:
1031 switch (it->options[4]) {
1032 case 0:
1033 s->range_table = &range_bipolar10;
1034 break;
1035 case 1:
1036 s->range_table = &range_bipolar5;
1037 break;
1038 case 2:
1039 s->range_table = &range_bipolar2_5;
1040 break;
1041 case 3:
1042 s->range_table = &range812_bipolar1_25;
1043 break;
1044 case 4:
1045 s->range_table = &range812_bipolar0_625;
1046 break;
1047 case 5:
1048 s->range_table = &range812_bipolar0_3125;
1049 break;
1050 default:
1051 s->range_table = &range_bipolar10;
1052 break;
1053 }
1054 break;
1055 case BOARD_PCL813B:
1056 if (it->options[1] == 1)
1057 s->range_table = &range_pcl813b2_ai;
1058 else
1059 s->range_table = board->rangelist_ai;
1060 break;
1061 case BOARD_ISO813:
1062 switch (it->options[1]) {
1063 case 0:
1064 s->range_table = &range_iso813_1_ai;
1065 break;
1066 case 1:
1067 s->range_table = &range_iso813_1_2_ai;
1068 break;
1069 case 2:
1070 s->range_table = &range_iso813_2_ai;
1071 devpriv->range_correction = 1;
1072 break;
1073 case 3:
1074 s->range_table = &range_iso813_2_2_ai;
1075 devpriv->range_correction = 1;
1076 break;
1077 default:
1078 s->range_table = &range_iso813_1_ai;
1079 break;
1080 }
1081 break;
1082 case BOARD_ACL8113:
1083 switch (it->options[1]) {
1084 case 0:
1085 s->range_table = &range_acl8113_1_ai;
1086 break;
1087 case 1:
1088 s->range_table = &range_acl8113_1_2_ai;
1089 break;
1090 case 2:
1091 s->range_table = &range_acl8113_2_ai;
1092 devpriv->range_correction = 1;
1093 break;
1094 case 3:
1095 s->range_table = &range_acl8113_2_2_ai;
1096 devpriv->range_correction = 1;
1097 break;
1098 default:
1099 s->range_table = &range_acl8113_1_ai;
1100 break;
1101 }
1102 break;
1103 default:
1104 s->range_table = board->rangelist_ai;
1105 break;
1106 }
1107}
1108
1109static void pcl812_alloc_dma(struct comedi_device *dev, unsigned int dma_chan)
1110{
1111 struct pcl812_private *devpriv = dev->private;
1112
1113
1114 if (!(dma_chan == 3 || dma_chan == 1))
1115 return;
1116
1117
1118 devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan,
1119 PAGE_SIZE * 2, COMEDI_ISADMA_READ);
1120}
1121
1122static void pcl812_free_dma(struct comedi_device *dev)
1123{
1124 struct pcl812_private *devpriv = dev->private;
1125
1126 if (devpriv)
1127 comedi_isadma_free(devpriv->dma);
1128}
1129
1130static int pcl812_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1131{
1132 const struct pcl812_board *board = dev->board_ptr;
1133 struct pcl812_private *devpriv;
1134 struct comedi_subdevice *s;
1135 int n_subdevices;
1136 int subdev;
1137 int ret;
1138
1139 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1140 if (!devpriv)
1141 return -ENOMEM;
1142
1143 ret = comedi_request_region(dev, it->options[0], 0x10);
1144 if (ret)
1145 return ret;
1146
1147 if (board->irq_bits) {
1148 dev->pacer = comedi_8254_init(dev->iobase + PCL812_TIMER_BASE,
1149 I8254_OSC_BASE_2MHZ,
1150 I8254_IO8, 0);
1151 if (!dev->pacer)
1152 return -ENOMEM;
1153
1154 if ((1 << it->options[1]) & board->irq_bits) {
1155 ret = request_irq(it->options[1], pcl812_interrupt, 0,
1156 dev->board_name, dev);
1157 if (ret == 0)
1158 dev->irq = it->options[1];
1159 }
1160 }
1161
1162
1163 if (dev->irq && board->has_dma)
1164 pcl812_alloc_dma(dev, it->options[2]);
1165
1166
1167 switch (board->board_type) {
1168 case BOARD_A821:
1169 if (it->options[2] == 1)
1170 devpriv->use_diff = 1;
1171 break;
1172 case BOARD_ACL8112:
1173 case BOARD_ACL8216:
1174 if (it->options[4] == 1)
1175 devpriv->use_diff = 1;
1176 break;
1177 default:
1178 break;
1179 }
1180
1181 n_subdevices = 1;
1182 if (board->n_aochan > 0)
1183 n_subdevices++;
1184 if (board->has_dio)
1185 n_subdevices += 2;
1186
1187 ret = comedi_alloc_subdevices(dev, n_subdevices);
1188 if (ret)
1189 return ret;
1190
1191 subdev = 0;
1192
1193
1194 s = &dev->subdevices[subdev];
1195 s->type = COMEDI_SUBD_AI;
1196 s->subdev_flags = SDF_READABLE;
1197 if (devpriv->use_diff) {
1198 s->subdev_flags |= SDF_DIFF;
1199 s->n_chan = board->n_aichan / 2;
1200 } else {
1201 s->subdev_flags |= SDF_GROUND;
1202 s->n_chan = board->n_aichan;
1203 }
1204 s->maxdata = board->has_16bit_ai ? 0xffff : 0x0fff;
1205
1206 pcl812_set_ai_range_table(dev, s, it);
1207
1208 s->insn_read = pcl812_ai_insn_read;
1209
1210 if (dev->irq) {
1211 dev->read_subdev = s;
1212 s->subdev_flags |= SDF_CMD_READ;
1213 s->len_chanlist = MAX_CHANLIST_LEN;
1214 s->do_cmdtest = pcl812_ai_cmdtest;
1215 s->do_cmd = pcl812_ai_cmd;
1216 s->poll = pcl812_ai_poll;
1217 s->cancel = pcl812_ai_cancel;
1218 }
1219
1220 devpriv->use_mpc508 = board->has_mpc508_mux;
1221
1222 subdev++;
1223
1224
1225 if (board->n_aochan > 0) {
1226 s = &dev->subdevices[subdev];
1227 s->type = COMEDI_SUBD_AO;
1228 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1229 s->n_chan = board->n_aochan;
1230 s->maxdata = 0xfff;
1231 switch (board->board_type) {
1232 case BOARD_A821:
1233 if (it->options[3] == 1)
1234 s->range_table = &range_unipolar10;
1235 else
1236 s->range_table = &range_unipolar5;
1237 break;
1238 case BOARD_PCL812:
1239 case BOARD_ACL8112:
1240 case BOARD_PCL812PG:
1241 case BOARD_ACL8216:
1242 switch (it->options[5]) {
1243 case 1:
1244 s->range_table = &range_unipolar10;
1245 break;
1246 case 2:
1247 s->range_table = &range_unknown;
1248 break;
1249 default:
1250 s->range_table = &range_unipolar5;
1251 break;
1252 }
1253 break;
1254 default:
1255 s->range_table = &range_unipolar5;
1256 break;
1257 }
1258 s->insn_write = pcl812_ao_insn_write;
1259
1260 ret = comedi_alloc_subdev_readback(s);
1261 if (ret)
1262 return ret;
1263
1264 subdev++;
1265 }
1266
1267 if (board->has_dio) {
1268
1269 s = &dev->subdevices[subdev];
1270 s->type = COMEDI_SUBD_DI;
1271 s->subdev_flags = SDF_READABLE;
1272 s->n_chan = 16;
1273 s->maxdata = 1;
1274 s->range_table = &range_digital;
1275 s->insn_bits = pcl812_di_insn_bits;
1276 subdev++;
1277
1278
1279 s = &dev->subdevices[subdev];
1280 s->type = COMEDI_SUBD_DO;
1281 s->subdev_flags = SDF_WRITABLE;
1282 s->n_chan = 16;
1283 s->maxdata = 1;
1284 s->range_table = &range_digital;
1285 s->insn_bits = pcl812_do_insn_bits;
1286 subdev++;
1287 }
1288
1289 switch (board->board_type) {
1290 case BOARD_ACL8216:
1291 case BOARD_PCL812PG:
1292 case BOARD_PCL812:
1293 case BOARD_ACL8112:
1294 devpriv->max_812_ai_mode0_rangewait = 1;
1295 if (it->options[3] > 0)
1296
1297 devpriv->use_ext_trg = 1;
1298 break;
1299 case BOARD_A821:
1300 devpriv->max_812_ai_mode0_rangewait = 1;
1301 devpriv->mode_reg_int = (dev->irq << 4) & 0xf0;
1302 break;
1303 case BOARD_PCL813B:
1304 case BOARD_PCL813:
1305 case BOARD_ISO813:
1306 case BOARD_ACL8113:
1307
1308 devpriv->max_812_ai_mode0_rangewait = 5;
1309 break;
1310 }
1311
1312 pcl812_reset(dev);
1313
1314 return 0;
1315}
1316
1317static void pcl812_detach(struct comedi_device *dev)
1318{
1319 pcl812_free_dma(dev);
1320 comedi_legacy_detach(dev);
1321}
1322
1323static struct comedi_driver pcl812_driver = {
1324 .driver_name = "pcl812",
1325 .module = THIS_MODULE,
1326 .attach = pcl812_attach,
1327 .detach = pcl812_detach,
1328 .board_name = &boardtypes[0].name,
1329 .num_names = ARRAY_SIZE(boardtypes),
1330 .offset = sizeof(struct pcl812_board),
1331};
1332module_comedi_driver(pcl812_driver);
1333
1334MODULE_AUTHOR("Comedi https://www.comedi.org");
1335MODULE_DESCRIPTION("Comedi low-level driver");
1336MODULE_LICENSE("GPL");
1337