Staging: go7007: Eliminate useless code
[deliverable/linux.git] / drivers / staging / comedi / drivers / pcl711.c
CommitLineData
456af201
DS
1/*
2 comedi/drivers/pcl711.c
3 hardware driver for PC-LabCard PCL-711 and AdSys ACL-8112
4 and compatibles
5
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>
10
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.
15
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.
20
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.
24
25 */
26/*
27Driver: pcl711
28Description: Advantech PCL-711 and 711b, ADLink ACL-8112
29Author: ds, Janne Jalkanen <jalkanen@cs.hut.fi>, Eric Bunn <ebu@cs.hut.fi>
30Status: mostly complete
31Devices: [Advantech] PCL-711 (pcl711), PCL-711B (pcl711b),
32 [AdLink] ACL-8112HG (acl8112hg), ACL-8112DG (acl8112dg)
33
34Since these boards do not have DMA or FIFOs, only immediate mode is
35supported.
36
37*/
38
39/*
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/
45
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.
52
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.
58
59 */
60
25436dc9 61#include <linux/interrupt.h>
456af201
DS
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
9ced1de6 91static const struct comedi_lrange range_pcl711b_ai = { 5, {
0a85b6f0
MT
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 }
456af201 98};
0a85b6f0 99
9ced1de6 100static const struct comedi_lrange range_acl8112hg_ai = { 12, {
0a85b6f0
MT
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 }
456af201 114};
0a85b6f0 115
9ced1de6 116static const struct comedi_lrange range_acl8112dg_ai = { 9, {
0a85b6f0
MT
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 }
456af201
DS
127};
128
129/*
130 * flags
131 */
132
133#define PCL711_TIMEOUT 100
134#define PCL711_DRDY 0x10
135
136static const int i8253_osc_base = 500; /* 2 Mhz */
137
6445296e
BP
138struct pcl711_board {
139
456af201
DS
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;
9ced1de6 148 const struct comedi_lrange *ai_range_type;
6445296e
BP
149};
150
6445296e 151static const struct pcl711_board boardtypes[] = {
456af201
DS
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
6445296e
BP
158#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl711_board))
159#define this_board ((const struct pcl711_board *)dev->board_ptr)
456af201 160
0a85b6f0
MT
161static int pcl711_attach(struct comedi_device *dev,
162 struct comedi_devconfig *it);
da91b269 163static int pcl711_detach(struct comedi_device *dev);
139dfbdf 164static struct comedi_driver driver_pcl711 = {
68c3dbff
BP
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),
456af201
DS
172};
173
174COMEDI_INITCLEANUP(driver_pcl711);
175
e1c8638f
BP
176struct pcl711_private {
177
456af201
DS
178 int board;
179 int adchan;
180 int ntrig;
181 int aip[8];
182 int mode;
790c5541 183 unsigned int ao_readback[2];
456af201
DS
184 unsigned int divisor1;
185 unsigned int divisor2;
e1c8638f
BP
186};
187
e1c8638f 188#define devpriv ((struct pcl711_private *)dev->private)
456af201 189
70265d24 190static irqreturn_t pcl711_interrupt(int irq, void *d)
456af201
DS
191{
192 int lo, hi;
193 int data;
71b5f4f1 194 struct comedi_device *dev = d;
34c43922 195 struct comedi_subdevice *s = dev->subdevices + 0;
456af201
DS
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 /* FIXME! Nothing else sets ntrig! */
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
da91b269 222static void pcl711_set_changain(struct comedi_device *dev, int chan)
456af201
DS
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 * Set the correct channel. The two channel banks are switched
234 * using the mask value.
235 * NB: To use differential channels, you should use mask = 0x30,
236 * but I haven't written the support for this yet. /JJ
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
da91b269 249static int pcl711_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 250 struct comedi_insn *insn, unsigned int *data)
456af201
DS
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 * Write the correct mode (software polling) and start polling by writing
260 * to the trigger register
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;
5f74ea14 274 udelay(1);
456af201 275 }
5f74ea14 276 printk("comedi%d: pcl711: A/D timeout\n", dev->minor);
456af201
DS
277 return -ETIME;
278
0a85b6f0 279ok:
456af201
DS
280 lo = inb(dev->iobase + PCL711_AD_LO);
281
282 data[n] = ((hi & 0xf) << 8) | lo;
283 }
284
285 return n;
286}
287
0a85b6f0
MT
288static int pcl711_ai_cmdtest(struct comedi_device *dev,
289 struct comedi_subdevice *s, struct comedi_cmd *cmd)
456af201
DS
290{
291 int tmp;
292 int err = 0;
293
294 /* step 1 */
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 /* step 2 */
324
325 if (cmd->scan_begin_src != TRIG_TIMER &&
0a85b6f0 326 cmd->scan_begin_src != TRIG_EXT)
456af201
DS
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 /* step 3 */
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 /* ignore */
368 }
369
370 if (err)
371 return 3;
372
373 /* step 4 */
374
375 if (cmd->scan_begin_src == TRIG_TIMER) {
376 tmp = cmd->scan_begin_arg;
377 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
0a85b6f0
MT
378 &devpriv->divisor1,
379 &devpriv->divisor2,
380 &cmd->scan_begin_arg,
381 cmd->flags & TRIG_ROUND_MASK);
456af201
DS
382 if (tmp != cmd->scan_begin_arg)
383 err++;
384 }
385
386 if (err)
387 return 4;
388
389 return 0;
390}
391
da91b269 392static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
456af201
DS
393{
394 int timer1, timer2;
ea6d0d4c 395 struct comedi_cmd *cmd = &s->async->cmd;
456af201
DS
396
397 pcl711_set_changain(dev, cmd->chanlist[0]);
398
399 if (cmd->scan_begin_src == TRIG_TIMER) {
400 /*
401 * Set timers
402 * timer chip is an 8253, with timers 1 and 2
403 * cascaded
404 * 0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
405 * Mode 2 = Rate generator
406 *
407 * 0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
408 */
409
48b1aff5 410 timer1 = timer2 = 0;
456af201 411 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
0a85b6f0
MT
412 &cmd->scan_begin_arg,
413 TRIG_ROUND_NEAREST);
456af201
DS
414
415 outb(0x74, dev->iobase + PCL711_CTRCTL);
416 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
417 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
418 outb(0xb4, dev->iobase + PCL711_CTRCTL);
419 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
420 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
421
422 /* clear pending interrupts (just in case) */
423 outb(0, dev->iobase + PCL711_CLRINTR);
424
425 /*
426 * Set mode to IRQ transfer
427 */
428 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
429 } else {
430 /* external trigger */
431 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
432 }
433
434 return 0;
435}
436
437/*
438 analog output
439*/
da91b269 440static int pcl711_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 441 struct comedi_insn *insn, unsigned int *data)
456af201
DS
442{
443 int n;
444 int chan = CR_CHAN(insn->chanspec);
445
446 for (n = 0; n < insn->n; n++) {
447 outb((data[n] & 0xff),
0a85b6f0 448 dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
456af201 449 outb((data[n] >> 8),
0a85b6f0 450 dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
456af201
DS
451
452 devpriv->ao_readback[chan] = data[n];
453 }
454
455 return n;
456}
457
0a85b6f0
MT
458static int pcl711_ao_insn_read(struct comedi_device *dev,
459 struct comedi_subdevice *s,
460 struct comedi_insn *insn, unsigned int *data)
456af201
DS
461{
462 int n;
463 int chan = CR_CHAN(insn->chanspec);
464
465 for (n = 0; n < insn->n; n++) {
466 data[n] = devpriv->ao_readback[chan];
467 }
468
469 return n;
470
471}
472
473/* Digital port read - Untested on 8112 */
0a85b6f0
MT
474static int pcl711_di_insn_bits(struct comedi_device *dev,
475 struct comedi_subdevice *s,
476 struct comedi_insn *insn, unsigned int *data)
456af201
DS
477{
478 if (insn->n != 2)
479 return -EINVAL;
480
481 data[1] = inb(dev->iobase + PCL711_DI_LO) |
0a85b6f0 482 (inb(dev->iobase + PCL711_DI_HI) << 8);
456af201
DS
483
484 return 2;
485}
486
487/* Digital port write - Untested on 8112 */
0a85b6f0
MT
488static int pcl711_do_insn_bits(struct comedi_device *dev,
489 struct comedi_subdevice *s,
490 struct comedi_insn *insn, unsigned int *data)
456af201
DS
491{
492 if (insn->n != 2)
493 return -EINVAL;
494
495 if (data[0]) {
496 s->state &= ~data[0];
497 s->state |= data[0] & data[1];
498 }
499 if (data[0] & 0x00ff)
500 outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
501 if (data[0] & 0xff00)
502 outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
503
504 data[1] = s->state;
505
506 return 2;
507}
508
509/* Free any resources that we have claimed */
da91b269 510static int pcl711_detach(struct comedi_device *dev)
456af201
DS
511{
512 printk("comedi%d: pcl711: remove\n", dev->minor);
513
514 if (dev->irq)
5f74ea14 515 free_irq(dev->irq, dev);
456af201
DS
516
517 if (dev->iobase)
518 release_region(dev->iobase, PCL711_SIZE);
519
520 return 0;
521}
522
523/* Initialization */
da91b269 524static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
456af201
DS
525{
526 int ret;
527 unsigned long iobase;
528 unsigned int irq;
34c43922 529 struct comedi_subdevice *s;
456af201
DS
530
531 /* claim our I/O space */
532
533 iobase = it->options[0];
534 printk("comedi%d: pcl711: 0x%04lx ", dev->minor, iobase);
535 if (!request_region(iobase, PCL711_SIZE, "pcl711")) {
536 printk("I/O port conflict\n");
537 return -EIO;
538 }
539 dev->iobase = iobase;
540
541 /* there should be a sanity check here */
542
543 /* set up some name stuff */
544 dev->board_name = this_board->name;
545
546 /* grab our IRQ */
547 irq = it->options[1];
548 if (irq > this_board->maxirq) {
549 printk("irq out of range\n");
550 return -EINVAL;
551 }
552 if (irq) {
5f74ea14 553 if (request_irq(irq, pcl711_interrupt, 0, "pcl711", dev)) {
456af201
DS
554 printk("unable to allocate irq %u\n", irq);
555 return -EINVAL;
556 } else {
557 printk("( irq = %u )\n", irq);
558 }
559 }
560 dev->irq = irq;
561
c3744138
BP
562 ret = alloc_subdevices(dev, 4);
563 if (ret < 0)
456af201 564 return ret;
c3744138
BP
565
566 ret = alloc_private(dev, sizeof(struct pcl711_private));
567 if (ret < 0)
456af201
DS
568 return ret;
569
570 s = dev->subdevices + 0;
571 /* AI subdevice */
572 s->type = COMEDI_SUBD_AI;
573 s->subdev_flags = SDF_READABLE | SDF_GROUND;
574 s->n_chan = this_board->n_aichan;
575 s->maxdata = 0xfff;
576 s->len_chanlist = 1;
577 s->range_table = this_board->ai_range_type;
578 s->insn_read = pcl711_ai_insn;
579 if (irq) {
580 dev->read_subdev = s;
581 s->subdev_flags |= SDF_CMD_READ;
582 s->do_cmdtest = pcl711_ai_cmdtest;
583 s->do_cmd = pcl711_ai_cmd;
584 }
585
586 s++;
587 /* AO subdevice */
588 s->type = COMEDI_SUBD_AO;
589 s->subdev_flags = SDF_WRITABLE;
590 s->n_chan = this_board->n_aochan;
591 s->maxdata = 0xfff;
592 s->len_chanlist = 1;
593 s->range_table = &range_bipolar5;
594 s->insn_write = pcl711_ao_insn;
595 s->insn_read = pcl711_ao_insn_read;
596
597 s++;
598 /* 16-bit digital input */
599 s->type = COMEDI_SUBD_DI;
600 s->subdev_flags = SDF_READABLE;
601 s->n_chan = 16;
602 s->maxdata = 1;
603 s->len_chanlist = 16;
604 s->range_table = &range_digital;
605 s->insn_bits = pcl711_di_insn_bits;
606
607 s++;
608 /* 16-bit digital out */
609 s->type = COMEDI_SUBD_DO;
610 s->subdev_flags = SDF_WRITABLE;
611 s->n_chan = 16;
612 s->maxdata = 1;
613 s->len_chanlist = 16;
614 s->range_table = &range_digital;
615 s->state = 0;
616 s->insn_bits = pcl711_do_insn_bits;
617
618 /*
619 this is the "base value" for the mode register, which is
620 used for the irq on the PCL711
621 */
622 if (this_board->is_pcl711b) {
623 devpriv->mode = (dev->irq << 4);
624 }
625
626 /* clear DAC */
627 outb(0, dev->iobase + PCL711_DA0_LO);
628 outb(0, dev->iobase + PCL711_DA0_HI);
629 outb(0, dev->iobase + PCL711_DA1_LO);
630 outb(0, dev->iobase + PCL711_DA1_HI);
631
632 printk("\n");
633
634 return 0;
635}
This page took 0.235357 seconds and 5 git commands to generate.