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