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