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/interrupt.h>
62#include "../comedidev.h"
63
64#include <linux/ioport.h>
65#include <linux/delay.h>
66
67#include "8253.h"
68
69#define PCL711_SIZE 16
70
71#define PCL711_CTR0 0
72#define PCL711_CTR1 1
73#define PCL711_CTR2 2
74#define PCL711_CTRCTL 3
75#define PCL711_AD_LO 4
76#define PCL711_DA0_LO 4
77#define PCL711_AD_HI 5
78#define PCL711_DA0_HI 5
79#define PCL711_DI_LO 6
80#define PCL711_DA1_LO 6
81#define PCL711_DI_HI 7
82#define PCL711_DA1_HI 7
83#define PCL711_CLRINTR 8
84#define PCL711_GAIN 9
85#define PCL711_MUX 10
86#define PCL711_MODE 11
87#define PCL711_SOFTTRIG 12
88#define PCL711_DO_LO 13
89#define PCL711_DO_HI 14
90
91static const struct comedi_lrange range_pcl711b_ai = { 5, {
92 BIP_RANGE(5),
93 BIP_RANGE(2.5),
94 BIP_RANGE(1.25),
95 BIP_RANGE(0.625),
96 BIP_RANGE(0.3125)
97 }
98};
99
100static const struct comedi_lrange range_acl8112hg_ai = { 12, {
101 BIP_RANGE(5),
102 BIP_RANGE(0.5),
103 BIP_RANGE(0.05),
104 BIP_RANGE(0.005),
105 UNI_RANGE(10),
106 UNI_RANGE(1),
107 UNI_RANGE(0.1),
108 UNI_RANGE(0.01),
109 BIP_RANGE(10),
110 BIP_RANGE(1),
111 BIP_RANGE(0.1),
112 BIP_RANGE(0.01)
113 }
114};
115
116static const struct comedi_lrange range_acl8112dg_ai = { 9, {
117 BIP_RANGE(5),
118 BIP_RANGE(2.5),
119 BIP_RANGE(1.25),
120 BIP_RANGE(0.625),
121 UNI_RANGE(10),
122 UNI_RANGE(5),
123 UNI_RANGE(2.5),
124 UNI_RANGE(1.25),
125 BIP_RANGE(10)
126 }
127};
128
129
130
131
132
133#define PCL711_TIMEOUT 100
134#define PCL711_DRDY 0x10
135
136static const int i8253_osc_base = 500;
137
138struct pcl711_board {
139
140 const char *name;
141 int is_pcl711b;
142 int is_8112;
143 int is_dg;
144 int n_ranges;
145 int n_aichan;
146 int n_aochan;
147 int maxirq;
148 const struct comedi_lrange *ai_range_type;
149};
150
151static const struct pcl711_board boardtypes[] = {
152 {"pcl711", 0, 0, 0, 5, 8, 1, 0, &range_bipolar5},
153 {"pcl711b", 1, 0, 0, 5, 8, 1, 7, &range_pcl711b_ai},
154 {"acl8112hg", 0, 1, 0, 12, 16, 2, 15, &range_acl8112hg_ai},
155 {"acl8112dg", 0, 1, 1, 9, 16, 2, 15, &range_acl8112dg_ai},
156};
157
158#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl711_board))
159#define this_board ((const struct pcl711_board *)dev->board_ptr)
160
161static int pcl711_attach(struct comedi_device *dev,
162 struct comedi_devconfig *it);
163static int pcl711_detach(struct comedi_device *dev);
164static struct comedi_driver driver_pcl711 = {
165 .driver_name = "pcl711",
166 .module = THIS_MODULE,
167 .attach = pcl711_attach,
168 .detach = pcl711_detach,
169 .board_name = &boardtypes[0].name,
170 .num_names = n_boardtypes,
171 .offset = sizeof(struct pcl711_board),
172};
173
174COMEDI_INITCLEANUP(driver_pcl711);
175
176struct pcl711_private {
177
178 int board;
179 int adchan;
180 int ntrig;
181 int aip[8];
182 int mode;
183 unsigned int ao_readback[2];
184 unsigned int divisor1;
185 unsigned int divisor2;
186};
187
188#define devpriv ((struct pcl711_private *)dev->private)
189
190static irqreturn_t pcl711_interrupt(int irq, void *d)
191{
192 int lo, hi;
193 int data;
194 struct comedi_device *dev = d;
195 struct comedi_subdevice *s = dev->subdevices + 0;
196
197 if (!dev->attached) {
198 comedi_error(dev, "spurious interrupt");
199 return IRQ_HANDLED;
200 }
201
202 hi = inb(dev->iobase + PCL711_AD_HI);
203 lo = inb(dev->iobase + PCL711_AD_LO);
204 outb(0, dev->iobase + PCL711_CLRINTR);
205
206 data = (hi << 8) | lo;
207
208
209 if (!(--devpriv->ntrig)) {
210 if (this_board->is_8112) {
211 outb(1, dev->iobase + PCL711_MODE);
212 } else {
213 outb(0, dev->iobase + PCL711_MODE);
214 }
215
216 s->async->events |= COMEDI_CB_EOA;
217 }
218 comedi_event(dev, s);
219 return IRQ_HANDLED;
220}
221
222static void pcl711_set_changain(struct comedi_device *dev, int chan)
223{
224 int chan_register;
225
226 outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
227
228 chan_register = CR_CHAN(chan);
229
230 if (this_board->is_8112) {
231
232
233
234
235
236
237
238
239 if (chan_register >= 8) {
240 chan_register = 0x20 | (chan_register & 0x7);
241 } else {
242 chan_register |= 0x10;
243 }
244 } else {
245 outb(chan_register, dev->iobase + PCL711_MUX);
246 }
247}
248
249static int pcl711_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
250 struct comedi_insn *insn, unsigned int *data)
251{
252 int i, n;
253 int hi, lo;
254
255 pcl711_set_changain(dev, insn->chanspec);
256
257 for (n = 0; n < insn->n; n++) {
258
259
260
261
262 outb(1, dev->iobase + PCL711_MODE);
263
264 if (this_board->is_8112) {
265 } else {
266 outb(0, dev->iobase + PCL711_SOFTTRIG);
267 }
268
269 i = PCL711_TIMEOUT;
270 while (--i) {
271 hi = inb(dev->iobase + PCL711_AD_HI);
272 if (!(hi & PCL711_DRDY))
273 goto ok;
274 udelay(1);
275 }
276 printk("comedi%d: pcl711: A/D timeout\n", dev->minor);
277 return -ETIME;
278
279ok:
280 lo = inb(dev->iobase + PCL711_AD_LO);
281
282 data[n] = ((hi & 0xf) << 8) | lo;
283 }
284
285 return n;
286}
287
288static int pcl711_ai_cmdtest(struct comedi_device *dev,
289 struct comedi_subdevice *s, struct comedi_cmd *cmd)
290{
291 int tmp;
292 int err = 0;
293
294
295 tmp = cmd->start_src;
296 cmd->start_src &= TRIG_NOW;
297 if (!cmd->start_src || tmp != cmd->start_src)
298 err++;
299
300 tmp = cmd->scan_begin_src;
301 cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
302 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
303 err++;
304
305 tmp = cmd->convert_src;
306 cmd->convert_src &= TRIG_NOW;
307 if (!cmd->convert_src || tmp != cmd->convert_src)
308 err++;
309
310 tmp = cmd->scan_end_src;
311 cmd->scan_end_src &= TRIG_COUNT;
312 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
313 err++;
314
315 tmp = cmd->stop_src;
316 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
317 if (!cmd->stop_src || tmp != cmd->stop_src)
318 err++;
319
320 if (err)
321 return 1;
322
323
324
325 if (cmd->scan_begin_src != TRIG_TIMER &&
326 cmd->scan_begin_src != TRIG_EXT)
327 err++;
328 if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
329 err++;
330
331 if (err)
332 return 2;
333
334
335
336 if (cmd->start_arg != 0) {
337 cmd->start_arg = 0;
338 err++;
339 }
340 if (cmd->scan_begin_src == TRIG_EXT) {
341 if (cmd->scan_begin_arg != 0) {
342 cmd->scan_begin_arg = 0;
343 err++;
344 }
345 } else {
346#define MAX_SPEED 1000
347#define TIMER_BASE 100
348 if (cmd->scan_begin_arg < MAX_SPEED) {
349 cmd->scan_begin_arg = MAX_SPEED;
350 err++;
351 }
352 }
353 if (cmd->convert_arg != 0) {
354 cmd->convert_arg = 0;
355 err++;
356 }
357 if (cmd->scan_end_arg != cmd->chanlist_len) {
358 cmd->scan_end_arg = cmd->chanlist_len;
359 err++;
360 }
361 if (cmd->stop_src == TRIG_NONE) {
362 if (cmd->stop_arg != 0) {
363 cmd->stop_arg = 0;
364 err++;
365 }
366 } else {
367
368 }
369
370 if (err)
371 return 3;
372
373
374
375 if (cmd->scan_begin_src == TRIG_TIMER) {
376 tmp = cmd->scan_begin_arg;
377 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
378 &devpriv->divisor1,
379 &devpriv->divisor2,
380 &cmd->scan_begin_arg,
381 cmd->flags & TRIG_ROUND_MASK);
382 if (tmp != cmd->scan_begin_arg)
383 err++;
384 }
385
386 if (err)
387 return 4;
388
389 return 0;
390}
391
392static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
393{
394 int timer1, timer2;
395 struct comedi_cmd *cmd = &s->async->cmd;
396
397 pcl711_set_changain(dev, cmd->chanlist[0]);
398
399 if (cmd->scan_begin_src == TRIG_TIMER) {
400
401
402
403
404
405
406
407
408
409
410 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
411 &cmd->scan_begin_arg,
412 TRIG_ROUND_NEAREST);
413
414 outb(0x74, dev->iobase + PCL711_CTRCTL);
415 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
416 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
417 outb(0xb4, dev->iobase + PCL711_CTRCTL);
418 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
419 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
420
421
422 outb(0, dev->iobase + PCL711_CLRINTR);
423
424
425
426
427 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
428 } else {
429
430 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
431 }
432
433 return 0;
434}
435
436
437
438
439static int pcl711_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
440 struct comedi_insn *insn, unsigned int *data)
441{
442 int n;
443 int chan = CR_CHAN(insn->chanspec);
444
445 for (n = 0; n < insn->n; n++) {
446 outb((data[n] & 0xff),
447 dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
448 outb((data[n] >> 8),
449 dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
450
451 devpriv->ao_readback[chan] = data[n];
452 }
453
454 return n;
455}
456
457static int pcl711_ao_insn_read(struct comedi_device *dev,
458 struct comedi_subdevice *s,
459 struct comedi_insn *insn, unsigned int *data)
460{
461 int n;
462 int chan = CR_CHAN(insn->chanspec);
463
464 for (n = 0; n < insn->n; n++) {
465 data[n] = devpriv->ao_readback[chan];
466 }
467
468 return n;
469
470}
471
472
473static int pcl711_di_insn_bits(struct comedi_device *dev,
474 struct comedi_subdevice *s,
475 struct comedi_insn *insn, unsigned int *data)
476{
477 if (insn->n != 2)
478 return -EINVAL;
479
480 data[1] = inb(dev->iobase + PCL711_DI_LO) |
481 (inb(dev->iobase + PCL711_DI_HI) << 8);
482
483 return 2;
484}
485
486
487static int pcl711_do_insn_bits(struct comedi_device *dev,
488 struct comedi_subdevice *s,
489 struct comedi_insn *insn, unsigned int *data)
490{
491 if (insn->n != 2)
492 return -EINVAL;
493
494 if (data[0]) {
495 s->state &= ~data[0];
496 s->state |= data[0] & data[1];
497 }
498 if (data[0] & 0x00ff)
499 outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
500 if (data[0] & 0xff00)
501 outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
502
503 data[1] = s->state;
504
505 return 2;
506}
507
508
509static int pcl711_detach(struct comedi_device *dev)
510{
511 printk("comedi%d: pcl711: remove\n", dev->minor);
512
513 if (dev->irq)
514 free_irq(dev->irq, dev);
515
516 if (dev->iobase)
517 release_region(dev->iobase, PCL711_SIZE);
518
519 return 0;
520}
521
522
523static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
524{
525 int ret;
526 unsigned long iobase;
527 unsigned int irq;
528 struct comedi_subdevice *s;
529
530
531
532 iobase = it->options[0];
533 printk("comedi%d: pcl711: 0x%04lx ", dev->minor, iobase);
534 if (!request_region(iobase, PCL711_SIZE, "pcl711")) {
535 printk("I/O port conflict\n");
536 return -EIO;
537 }
538 dev->iobase = iobase;
539
540
541
542
543 dev->board_name = this_board->name;
544
545
546 irq = it->options[1];
547 if (irq > this_board->maxirq) {
548 printk("irq out of range\n");
549 return -EINVAL;
550 }
551 if (irq) {
552 if (request_irq(irq, pcl711_interrupt, 0, "pcl711", dev)) {
553 printk("unable to allocate irq %u\n", irq);
554 return -EINVAL;
555 } else {
556 printk("( irq = %u )\n", irq);
557 }
558 }
559 dev->irq = irq;
560
561 ret = alloc_subdevices(dev, 4);
562 if (ret < 0)
563 return ret;
564
565 ret = alloc_private(dev, sizeof(struct pcl711_private));
566 if (ret < 0)
567 return ret;
568
569 s = dev->subdevices + 0;
570
571 s->type = COMEDI_SUBD_AI;
572 s->subdev_flags = SDF_READABLE | SDF_GROUND;
573 s->n_chan = this_board->n_aichan;
574 s->maxdata = 0xfff;
575 s->len_chanlist = 1;
576 s->range_table = this_board->ai_range_type;
577 s->insn_read = pcl711_ai_insn;
578 if (irq) {
579 dev->read_subdev = s;
580 s->subdev_flags |= SDF_CMD_READ;
581 s->do_cmdtest = pcl711_ai_cmdtest;
582 s->do_cmd = pcl711_ai_cmd;
583 }
584
585 s++;
586
587 s->type = COMEDI_SUBD_AO;
588 s->subdev_flags = SDF_WRITABLE;
589 s->n_chan = this_board->n_aochan;
590 s->maxdata = 0xfff;
591 s->len_chanlist = 1;
592 s->range_table = &range_bipolar5;
593 s->insn_write = pcl711_ao_insn;
594 s->insn_read = pcl711_ao_insn_read;
595
596 s++;
597
598 s->type = COMEDI_SUBD_DI;
599 s->subdev_flags = SDF_READABLE;
600 s->n_chan = 16;
601 s->maxdata = 1;
602 s->len_chanlist = 16;
603 s->range_table = &range_digital;
604 s->insn_bits = pcl711_di_insn_bits;
605
606 s++;
607
608 s->type = COMEDI_SUBD_DO;
609 s->subdev_flags = SDF_WRITABLE;
610 s->n_chan = 16;
611 s->maxdata = 1;
612 s->len_chanlist = 16;
613 s->range_table = &range_digital;
614 s->state = 0;
615 s->insn_bits = pcl711_do_insn_bits;
616
617
618
619
620
621 if (this_board->is_pcl711b) {
622 devpriv->mode = (dev->irq << 4);
623 }
624
625
626 outb(0, dev->iobase + PCL711_DA0_LO);
627 outb(0, dev->iobase + PCL711_DA0_HI);
628 outb(0, dev->iobase + PCL711_DA1_LO);
629 outb(0, dev->iobase + PCL711_DA1_HI);
630
631 printk("\n");
632
633 return 0;
634}
635