GUI: Fix Tomato RAF theme for all builds. Compilation typo.
[tomato.git] / release / src-rt-6.x.4708 / linux / linux-2.6.36 / drivers / staging / comedi / drivers / pcl711.c
blob927e91de53e2c7c3cf524e2b60b831b73fcb2c53
1 /*
2 comedi/drivers/pcl711.c
3 hardware driver for PC-LabCard PCL-711 and AdSys ACL-8112
4 and compatibles
6 COMEDI - Linux Control and Measurement Device Interface
7 Copyright (C) 1998 David A. Schleef <ds@schleef.org>
8 Janne Jalkanen <jalkanen@cs.hut.fi>
9 Eric Bunn <ebu@cs.hut.fi>
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 Driver: pcl711
28 Description: Advantech PCL-711 and 711b, ADLink ACL-8112
29 Author: ds, Janne Jalkanen <jalkanen@cs.hut.fi>, Eric Bunn <ebu@cs.hut.fi>
30 Status: mostly complete
31 Devices: [Advantech] PCL-711 (pcl711), PCL-711B (pcl711b),
32 [AdLink] ACL-8112HG (acl8112hg), ACL-8112DG (acl8112dg)
34 Since these boards do not have DMA or FIFOs, only immediate mode is
35 supported.
40 Dave Andruczyk <dave@tech.buffalostate.edu> also wrote a
41 driver for the PCL-711. I used a few ideas from his driver
42 here. His driver also has more comments, if you are
43 interested in understanding how this driver works.
44 http://tech.buffalostate.edu/~dave/driver/
46 The ACL-8112 driver was hacked from the sources of the PCL-711
47 driver (the 744 chip used on the 8112 is almost the same as
48 the 711b chip, but it has more I/O channels) by
49 Janne Jalkanen (jalkanen@cs.hut.fi) and
50 Erik Bunn (ebu@cs.hut.fi). Remerged with the PCL-711 driver
51 by ds.
53 [acl-8112]
54 This driver supports both TRIGNOW and TRIGCLK,
55 but does not yet support DMA transfers. It also supports
56 both high (HG) and low (DG) versions of the card, though
57 the HG version has been untested.
61 #include <linux/interrupt.h>
62 #include "../comedidev.h"
64 #include <linux/ioport.h>
65 #include <linux/delay.h>
67 #include "8253.h"
69 #define PCL711_SIZE 16
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
91 static 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)
100 static 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)
116 static 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)
130 * flags
133 #define PCL711_TIMEOUT 100
134 #define PCL711_DRDY 0x10
136 static const int i8253_osc_base = 500; /* 2 Mhz */
138 struct pcl711_board {
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;
151 static 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},
158 #define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl711_board))
159 #define this_board ((const struct pcl711_board *)dev->board_ptr)
161 static int pcl711_attach(struct comedi_device *dev,
162 struct comedi_devconfig *it);
163 static int pcl711_detach(struct comedi_device *dev);
164 static 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),
174 static int __init driver_pcl711_init_module(void)
176 return comedi_driver_register(&driver_pcl711);
179 static void __exit driver_pcl711_cleanup_module(void)
181 comedi_driver_unregister(&driver_pcl711);
184 module_init(driver_pcl711_init_module);
185 module_exit(driver_pcl711_cleanup_module);
187 struct pcl711_private {
189 int board;
190 int adchan;
191 int ntrig;
192 int aip[8];
193 int mode;
194 unsigned int ao_readback[2];
195 unsigned int divisor1;
196 unsigned int divisor2;
199 #define devpriv ((struct pcl711_private *)dev->private)
201 static irqreturn_t pcl711_interrupt(int irq, void *d)
203 int lo, hi;
204 int data;
205 struct comedi_device *dev = d;
206 struct comedi_subdevice *s = dev->subdevices + 0;
208 if (!dev->attached) {
209 comedi_error(dev, "spurious interrupt");
210 return IRQ_HANDLED;
213 hi = inb(dev->iobase + PCL711_AD_HI);
214 lo = inb(dev->iobase + PCL711_AD_LO);
215 outb(0, dev->iobase + PCL711_CLRINTR);
217 data = (hi << 8) | lo;
219 if (!(--devpriv->ntrig)) {
220 if (this_board->is_8112)
221 outb(1, dev->iobase + PCL711_MODE);
222 else
223 outb(0, dev->iobase + PCL711_MODE);
225 s->async->events |= COMEDI_CB_EOA;
227 comedi_event(dev, s);
228 return IRQ_HANDLED;
231 static void pcl711_set_changain(struct comedi_device *dev, int chan)
233 int chan_register;
235 outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
237 chan_register = CR_CHAN(chan);
239 if (this_board->is_8112) {
242 * Set the correct channel. The two channel banks are switched
243 * using the mask value.
244 * NB: To use differential channels, you should use
245 * mask = 0x30, but I haven't written the support for this
246 * yet. /JJ
249 if (chan_register >= 8)
250 chan_register = 0x20 | (chan_register & 0x7);
251 else
252 chan_register |= 0x10;
253 } else {
254 outb(chan_register, dev->iobase + PCL711_MUX);
258 static int pcl711_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
259 struct comedi_insn *insn, unsigned int *data)
261 int i, n;
262 int hi, lo;
264 pcl711_set_changain(dev, insn->chanspec);
266 for (n = 0; n < insn->n; n++) {
268 * Write the correct mode (software polling) and start polling
269 * by writing to the trigger register
271 outb(1, dev->iobase + PCL711_MODE);
273 if (!this_board->is_8112)
274 outb(0, dev->iobase + PCL711_SOFTTRIG);
276 i = PCL711_TIMEOUT;
277 while (--i) {
278 hi = inb(dev->iobase + PCL711_AD_HI);
279 if (!(hi & PCL711_DRDY))
280 goto ok;
281 udelay(1);
283 printk(KERN_ERR "comedi%d: pcl711: A/D timeout\n", dev->minor);
284 return -ETIME;
287 lo = inb(dev->iobase + PCL711_AD_LO);
289 data[n] = ((hi & 0xf) << 8) | lo;
292 return n;
295 static int pcl711_ai_cmdtest(struct comedi_device *dev,
296 struct comedi_subdevice *s, struct comedi_cmd *cmd)
298 int tmp;
299 int err = 0;
301 /* step 1 */
302 tmp = cmd->start_src;
303 cmd->start_src &= TRIG_NOW;
304 if (!cmd->start_src || tmp != cmd->start_src)
305 err++;
307 tmp = cmd->scan_begin_src;
308 cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
309 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
310 err++;
312 tmp = cmd->convert_src;
313 cmd->convert_src &= TRIG_NOW;
314 if (!cmd->convert_src || tmp != cmd->convert_src)
315 err++;
317 tmp = cmd->scan_end_src;
318 cmd->scan_end_src &= TRIG_COUNT;
319 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
320 err++;
322 tmp = cmd->stop_src;
323 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
324 if (!cmd->stop_src || tmp != cmd->stop_src)
325 err++;
327 if (err)
328 return 1;
330 /* step 2 */
332 if (cmd->scan_begin_src != TRIG_TIMER &&
333 cmd->scan_begin_src != TRIG_EXT)
334 err++;
335 if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
336 err++;
338 if (err)
339 return 2;
341 /* step 3 */
343 if (cmd->start_arg != 0) {
344 cmd->start_arg = 0;
345 err++;
347 if (cmd->scan_begin_src == TRIG_EXT) {
348 if (cmd->scan_begin_arg != 0) {
349 cmd->scan_begin_arg = 0;
350 err++;
352 } else {
353 #define MAX_SPEED 1000
354 #define TIMER_BASE 100
355 if (cmd->scan_begin_arg < MAX_SPEED) {
356 cmd->scan_begin_arg = MAX_SPEED;
357 err++;
360 if (cmd->convert_arg != 0) {
361 cmd->convert_arg = 0;
362 err++;
364 if (cmd->scan_end_arg != cmd->chanlist_len) {
365 cmd->scan_end_arg = cmd->chanlist_len;
366 err++;
368 if (cmd->stop_src == TRIG_NONE) {
369 if (cmd->stop_arg != 0) {
370 cmd->stop_arg = 0;
371 err++;
373 } else {
374 /* ignore */
377 if (err)
378 return 3;
380 /* step 4 */
382 if (cmd->scan_begin_src == TRIG_TIMER) {
383 tmp = cmd->scan_begin_arg;
384 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
385 &devpriv->divisor1,
386 &devpriv->divisor2,
387 &cmd->scan_begin_arg,
388 cmd->flags & TRIG_ROUND_MASK);
389 if (tmp != cmd->scan_begin_arg)
390 err++;
393 if (err)
394 return 4;
396 return 0;
399 static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
401 int timer1, timer2;
402 struct comedi_cmd *cmd = &s->async->cmd;
404 pcl711_set_changain(dev, cmd->chanlist[0]);
406 if (cmd->scan_begin_src == TRIG_TIMER) {
408 * Set timers
409 * timer chip is an 8253, with timers 1 and 2
410 * cascaded
411 * 0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
412 * Mode 2 = Rate generator
414 * 0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
417 timer1 = timer2 = 0;
418 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
419 &cmd->scan_begin_arg,
420 TRIG_ROUND_NEAREST);
422 outb(0x74, dev->iobase + PCL711_CTRCTL);
423 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
424 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
425 outb(0xb4, dev->iobase + PCL711_CTRCTL);
426 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
427 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
429 /* clear pending interrupts (just in case) */
430 outb(0, dev->iobase + PCL711_CLRINTR);
433 * Set mode to IRQ transfer
435 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
436 } else {
437 /* external trigger */
438 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
441 return 0;
445 analog output
447 static int pcl711_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
448 struct comedi_insn *insn, unsigned int *data)
450 int n;
451 int chan = CR_CHAN(insn->chanspec);
453 for (n = 0; n < insn->n; n++) {
454 outb((data[n] & 0xff),
455 dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
456 outb((data[n] >> 8),
457 dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
459 devpriv->ao_readback[chan] = data[n];
462 return n;
465 static int pcl711_ao_insn_read(struct comedi_device *dev,
466 struct comedi_subdevice *s,
467 struct comedi_insn *insn, unsigned int *data)
469 int n;
470 int chan = CR_CHAN(insn->chanspec);
472 for (n = 0; n < insn->n; n++)
473 data[n] = devpriv->ao_readback[chan];
475 return n;
479 /* Digital port read - Untested on 8112 */
480 static int pcl711_di_insn_bits(struct comedi_device *dev,
481 struct comedi_subdevice *s,
482 struct comedi_insn *insn, unsigned int *data)
484 if (insn->n != 2)
485 return -EINVAL;
487 data[1] = inb(dev->iobase + PCL711_DI_LO) |
488 (inb(dev->iobase + PCL711_DI_HI) << 8);
490 return 2;
493 /* Digital port write - Untested on 8112 */
494 static int pcl711_do_insn_bits(struct comedi_device *dev,
495 struct comedi_subdevice *s,
496 struct comedi_insn *insn, unsigned int *data)
498 if (insn->n != 2)
499 return -EINVAL;
501 if (data[0]) {
502 s->state &= ~data[0];
503 s->state |= data[0] & data[1];
505 if (data[0] & 0x00ff)
506 outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
507 if (data[0] & 0xff00)
508 outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
510 data[1] = s->state;
512 return 2;
515 /* Free any resources that we have claimed */
516 static int pcl711_detach(struct comedi_device *dev)
518 printk(KERN_INFO "comedi%d: pcl711: remove\n", dev->minor);
520 if (dev->irq)
521 free_irq(dev->irq, dev);
523 if (dev->iobase)
524 release_region(dev->iobase, PCL711_SIZE);
526 return 0;
529 /* Initialization */
530 static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
532 int ret;
533 unsigned long iobase;
534 unsigned int irq;
535 struct comedi_subdevice *s;
537 /* claim our I/O space */
539 iobase = it->options[0];
540 printk(KERN_INFO "comedi%d: pcl711: 0x%04lx ", dev->minor, iobase);
541 if (!request_region(iobase, PCL711_SIZE, "pcl711")) {
542 printk("I/O port conflict\n");
543 return -EIO;
545 dev->iobase = iobase;
547 /* there should be a sanity check here */
549 /* set up some name stuff */
550 dev->board_name = this_board->name;
552 /* grab our IRQ */
553 irq = it->options[1];
554 if (irq > this_board->maxirq) {
555 printk(KERN_ERR "irq out of range\n");
556 return -EINVAL;
558 if (irq) {
559 if (request_irq(irq, pcl711_interrupt, 0, "pcl711", dev)) {
560 printk(KERN_ERR "unable to allocate irq %u\n", irq);
561 return -EINVAL;
562 } else {
563 printk(KERN_INFO "( irq = %u )\n", irq);
566 dev->irq = irq;
568 ret = alloc_subdevices(dev, 4);
569 if (ret < 0)
570 return ret;
572 ret = alloc_private(dev, sizeof(struct pcl711_private));
573 if (ret < 0)
574 return ret;
576 s = dev->subdevices + 0;
577 /* AI subdevice */
578 s->type = COMEDI_SUBD_AI;
579 s->subdev_flags = SDF_READABLE | SDF_GROUND;
580 s->n_chan = this_board->n_aichan;
581 s->maxdata = 0xfff;
582 s->len_chanlist = 1;
583 s->range_table = this_board->ai_range_type;
584 s->insn_read = pcl711_ai_insn;
585 if (irq) {
586 dev->read_subdev = s;
587 s->subdev_flags |= SDF_CMD_READ;
588 s->do_cmdtest = pcl711_ai_cmdtest;
589 s->do_cmd = pcl711_ai_cmd;
592 s++;
593 /* AO subdevice */
594 s->type = COMEDI_SUBD_AO;
595 s->subdev_flags = SDF_WRITABLE;
596 s->n_chan = this_board->n_aochan;
597 s->maxdata = 0xfff;
598 s->len_chanlist = 1;
599 s->range_table = &range_bipolar5;
600 s->insn_write = pcl711_ao_insn;
601 s->insn_read = pcl711_ao_insn_read;
603 s++;
604 /* 16-bit digital input */
605 s->type = COMEDI_SUBD_DI;
606 s->subdev_flags = SDF_READABLE;
607 s->n_chan = 16;
608 s->maxdata = 1;
609 s->len_chanlist = 16;
610 s->range_table = &range_digital;
611 s->insn_bits = pcl711_di_insn_bits;
613 s++;
614 /* 16-bit digital out */
615 s->type = COMEDI_SUBD_DO;
616 s->subdev_flags = SDF_WRITABLE;
617 s->n_chan = 16;
618 s->maxdata = 1;
619 s->len_chanlist = 16;
620 s->range_table = &range_digital;
621 s->state = 0;
622 s->insn_bits = pcl711_do_insn_bits;
625 this is the "base value" for the mode register, which is
626 used for the irq on the PCL711
628 if (this_board->is_pcl711b)
629 devpriv->mode = (dev->irq << 4);
631 /* clear DAC */
632 outb(0, dev->iobase + PCL711_DA0_LO);
633 outb(0, dev->iobase + PCL711_DA0_HI);
634 outb(0, dev->iobase + PCL711_DA1_LO);
635 outb(0, dev->iobase + PCL711_DA1_HI);
637 printk(KERN_INFO "\n");
639 return 0;
642 MODULE_AUTHOR("Comedi http://www.comedi.org");
643 MODULE_DESCRIPTION("Comedi low-level driver");
644 MODULE_LICENSE("GPL");