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 "comedi_fc.h"
68#include "8253.h"
69
70#define PCL711_SIZE 16
71
72#define PCL711_CTR0 0
73#define PCL711_CTR1 1
74#define PCL711_CTR2 2
75#define PCL711_CTRCTL 3
76#define PCL711_AD_LO 4
77#define PCL711_DA0_LO 4
78#define PCL711_AD_HI 5
79#define PCL711_DA0_HI 5
80#define PCL711_DI_LO 6
81#define PCL711_DA1_LO 6
82#define PCL711_DI_HI 7
83#define PCL711_DA1_HI 7
84#define PCL711_CLRINTR 8
85#define PCL711_GAIN 9
86#define PCL711_MUX 10
87#define PCL711_MODE 11
88#define PCL711_SOFTTRIG 12
89#define PCL711_DO_LO 13
90#define PCL711_DO_HI 14
91
92static const struct comedi_lrange range_pcl711b_ai = { 5, {
93 BIP_RANGE(5),
94 BIP_RANGE(2.5),
95 BIP_RANGE(1.25),
96 BIP_RANGE(0.625),
97 BIP_RANGE(0.3125)
98 }
99};
100
101static const struct comedi_lrange range_acl8112hg_ai = { 12, {
102 BIP_RANGE(5),
103 BIP_RANGE(0.5),
104 BIP_RANGE(0.05),
105 BIP_RANGE(0.005),
106 UNI_RANGE(10),
107 UNI_RANGE(1),
108 UNI_RANGE(0.1),
109 UNI_RANGE(0.01),
110 BIP_RANGE(10),
111 BIP_RANGE(1),
112 BIP_RANGE(0.1),
113 BIP_RANGE(0.01)
114 }
115};
116
117static const struct comedi_lrange range_acl8112dg_ai = { 9, {
118 BIP_RANGE(5),
119 BIP_RANGE(2.5),
120 BIP_RANGE(1.25),
121 BIP_RANGE(0.625),
122 UNI_RANGE(10),
123 UNI_RANGE(5),
124 UNI_RANGE(2.5),
125 UNI_RANGE(1.25),
126 BIP_RANGE(10)
127 }
128};
129
130
131
132
133
134#define PCL711_TIMEOUT 100
135#define PCL711_DRDY 0x10
136
137static const int i8253_osc_base = 500;
138
139struct pcl711_board {
140
141 const char *name;
142 int is_pcl711b;
143 int is_8112;
144 int is_dg;
145 int n_ranges;
146 int n_aichan;
147 int n_aochan;
148 int maxirq;
149 const struct comedi_lrange *ai_range_type;
150};
151
152struct pcl711_private {
153
154 int board;
155 int adchan;
156 int ntrig;
157 int aip[8];
158 int mode;
159 unsigned int ao_readback[2];
160 unsigned int divisor1;
161 unsigned int divisor2;
162};
163
164static irqreturn_t pcl711_interrupt(int irq, void *d)
165{
166 int lo, hi;
167 int data;
168 struct comedi_device *dev = d;
169 const struct pcl711_board *board = comedi_board(dev);
170 struct pcl711_private *devpriv = dev->private;
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 struct pcl711_private *devpriv = dev->private;
267 int tmp;
268 int err = 0;
269
270
271
272 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
273 err |= cfc_check_trigger_src(&cmd->scan_begin_src,
274 TRIG_TIMER | TRIG_EXT);
275 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
276 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
277 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
278
279 if (err)
280 return 1;
281
282
283
284 err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
285 err |= cfc_check_trigger_is_unique(cmd->stop_src);
286
287
288
289 if (err)
290 return 2;
291
292
293
294 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
295
296 if (cmd->scan_begin_src == TRIG_EXT) {
297 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
298 } else {
299#define MAX_SPEED 1000
300#define TIMER_BASE 100
301 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
302 MAX_SPEED);
303 }
304
305 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
306 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
307
308 if (cmd->stop_src == TRIG_NONE) {
309 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
310 } else {
311
312 }
313
314 if (err)
315 return 3;
316
317
318
319 if (cmd->scan_begin_src == TRIG_TIMER) {
320 tmp = cmd->scan_begin_arg;
321 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
322 &devpriv->divisor1,
323 &devpriv->divisor2,
324 &cmd->scan_begin_arg,
325 cmd->flags & TRIG_ROUND_MASK);
326 if (tmp != cmd->scan_begin_arg)
327 err++;
328 }
329
330 if (err)
331 return 4;
332
333 return 0;
334}
335
336static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
337{
338 struct pcl711_private *devpriv = dev->private;
339 int timer1, timer2;
340 struct comedi_cmd *cmd = &s->async->cmd;
341
342 pcl711_set_changain(dev, cmd->chanlist[0]);
343
344 if (cmd->scan_begin_src == TRIG_TIMER) {
345
346
347
348
349
350
351
352
353
354
355 timer1 = timer2 = 0;
356 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
357 &cmd->scan_begin_arg,
358 TRIG_ROUND_NEAREST);
359
360 outb(0x74, dev->iobase + PCL711_CTRCTL);
361 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
362 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
363 outb(0xb4, dev->iobase + PCL711_CTRCTL);
364 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
365 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
366
367
368 outb(0, dev->iobase + PCL711_CLRINTR);
369
370
371
372
373 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
374 } else {
375
376 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
377 }
378
379 return 0;
380}
381
382
383
384
385static int pcl711_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
386 struct comedi_insn *insn, unsigned int *data)
387{
388 struct pcl711_private *devpriv = dev->private;
389 int n;
390 int chan = CR_CHAN(insn->chanspec);
391
392 for (n = 0; n < insn->n; n++) {
393 outb((data[n] & 0xff),
394 dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
395 outb((data[n] >> 8),
396 dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
397
398 devpriv->ao_readback[chan] = data[n];
399 }
400
401 return n;
402}
403
404static int pcl711_ao_insn_read(struct comedi_device *dev,
405 struct comedi_subdevice *s,
406 struct comedi_insn *insn, unsigned int *data)
407{
408 struct pcl711_private *devpriv = dev->private;
409 int n;
410 int chan = CR_CHAN(insn->chanspec);
411
412 for (n = 0; n < insn->n; n++)
413 data[n] = devpriv->ao_readback[chan];
414
415 return n;
416
417}
418
419
420static int pcl711_di_insn_bits(struct comedi_device *dev,
421 struct comedi_subdevice *s,
422 struct comedi_insn *insn, unsigned int *data)
423{
424 data[1] = inb(dev->iobase + PCL711_DI_LO) |
425 (inb(dev->iobase + PCL711_DI_HI) << 8);
426
427 return insn->n;
428}
429
430
431static int pcl711_do_insn_bits(struct comedi_device *dev,
432 struct comedi_subdevice *s,
433 struct comedi_insn *insn, unsigned int *data)
434{
435 if (data[0]) {
436 s->state &= ~data[0];
437 s->state |= data[0] & data[1];
438 }
439 if (data[0] & 0x00ff)
440 outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
441 if (data[0] & 0xff00)
442 outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
443
444 data[1] = s->state;
445
446 return insn->n;
447}
448
449static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
450{
451 const struct pcl711_board *board = comedi_board(dev);
452 struct pcl711_private *devpriv;
453 int ret;
454 unsigned int irq;
455 struct comedi_subdevice *s;
456
457 ret = comedi_request_region(dev, it->options[0], PCL711_SIZE);
458 if (ret)
459 return ret;
460
461
462 irq = it->options[1];
463 if (irq > board->maxirq) {
464 printk(KERN_ERR "irq out of range\n");
465 return -EINVAL;
466 }
467 if (irq) {
468 if (request_irq(irq, pcl711_interrupt, 0, dev->board_name,
469 dev)) {
470 printk(KERN_ERR "unable to allocate irq %u\n", irq);
471 return -EINVAL;
472 } else {
473 printk(KERN_INFO "( irq = %u )\n", irq);
474 }
475 }
476 dev->irq = irq;
477
478 ret = comedi_alloc_subdevices(dev, 4);
479 if (ret)
480 return ret;
481
482 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
483 if (!devpriv)
484 return -ENOMEM;
485 dev->private = devpriv;
486
487 s = &dev->subdevices[0];
488
489 s->type = COMEDI_SUBD_AI;
490 s->subdev_flags = SDF_READABLE | SDF_GROUND;
491 s->n_chan = board->n_aichan;
492 s->maxdata = 0xfff;
493 s->len_chanlist = 1;
494 s->range_table = board->ai_range_type;
495 s->insn_read = pcl711_ai_insn;
496 if (irq) {
497 dev->read_subdev = s;
498 s->subdev_flags |= SDF_CMD_READ;
499 s->do_cmdtest = pcl711_ai_cmdtest;
500 s->do_cmd = pcl711_ai_cmd;
501 }
502
503 s = &dev->subdevices[1];
504
505 s->type = COMEDI_SUBD_AO;
506 s->subdev_flags = SDF_WRITABLE;
507 s->n_chan = board->n_aochan;
508 s->maxdata = 0xfff;
509 s->len_chanlist = 1;
510 s->range_table = &range_bipolar5;
511 s->insn_write = pcl711_ao_insn;
512 s->insn_read = pcl711_ao_insn_read;
513
514 s = &dev->subdevices[2];
515
516 s->type = COMEDI_SUBD_DI;
517 s->subdev_flags = SDF_READABLE;
518 s->n_chan = 16;
519 s->maxdata = 1;
520 s->len_chanlist = 16;
521 s->range_table = &range_digital;
522 s->insn_bits = pcl711_di_insn_bits;
523
524 s = &dev->subdevices[3];
525
526 s->type = COMEDI_SUBD_DO;
527 s->subdev_flags = SDF_WRITABLE;
528 s->n_chan = 16;
529 s->maxdata = 1;
530 s->len_chanlist = 16;
531 s->range_table = &range_digital;
532 s->state = 0;
533 s->insn_bits = pcl711_do_insn_bits;
534
535
536
537
538
539 if (board->is_pcl711b)
540 devpriv->mode = (dev->irq << 4);
541
542
543 outb(0, dev->iobase + PCL711_DA0_LO);
544 outb(0, dev->iobase + PCL711_DA0_HI);
545 outb(0, dev->iobase + PCL711_DA1_LO);
546 outb(0, dev->iobase + PCL711_DA1_HI);
547
548 printk(KERN_INFO "\n");
549
550 return 0;
551}
552
553static const struct pcl711_board boardtypes[] = {
554 { "pcl711", 0, 0, 0, 5, 8, 1, 0, &range_bipolar5 },
555 { "pcl711b", 1, 0, 0, 5, 8, 1, 7, &range_pcl711b_ai },
556 { "acl8112hg", 0, 1, 0, 12, 16, 2, 15, &range_acl8112hg_ai },
557 { "acl8112dg", 0, 1, 1, 9, 16, 2, 15, &range_acl8112dg_ai },
558};
559
560static struct comedi_driver pcl711_driver = {
561 .driver_name = "pcl711",
562 .module = THIS_MODULE,
563 .attach = pcl711_attach,
564 .detach = comedi_legacy_detach,
565 .board_name = &boardtypes[0].name,
566 .num_names = ARRAY_SIZE(boardtypes),
567 .offset = sizeof(struct pcl711_board),
568};
569module_comedi_driver(pcl711_driver);
570
571MODULE_AUTHOR("Comedi http://www.comedi.org");
572MODULE_DESCRIPTION("Comedi low-level driver");
573MODULE_LICENSE("GPL");
574