staging: comedi: remove all 'default N' in Kconfig
[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 151#define this_board ((const struct pcl711_board *)dev->board_ptr)
456af201 152
e1c8638f
BP
153struct pcl711_private {
154
456af201
DS
155 int board;
156 int adchan;
157 int ntrig;
158 int aip[8];
159 int mode;
790c5541 160 unsigned int ao_readback[2];
456af201
DS
161 unsigned int divisor1;
162 unsigned int divisor2;
e1c8638f
BP
163};
164
e1c8638f 165#define devpriv ((struct pcl711_private *)dev->private)
456af201 166
70265d24 167static irqreturn_t pcl711_interrupt(int irq, void *d)
456af201
DS
168{
169 int lo, hi;
170 int data;
71b5f4f1 171 struct comedi_device *dev = d;
34c43922 172 struct comedi_subdevice *s = dev->subdevices + 0;
456af201
DS
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 /* FIXME! Nothing else sets ntrig! */
186 if (!(--devpriv->ntrig)) {
266bfbdd 187 if (this_board->is_8112)
456af201 188 outb(1, dev->iobase + PCL711_MODE);
266bfbdd 189 else
456af201 190 outb(0, dev->iobase + PCL711_MODE);
456af201
DS
191
192 s->async->events |= COMEDI_CB_EOA;
193 }
194 comedi_event(dev, s);
195 return IRQ_HANDLED;
196}
197
da91b269 198static void pcl711_set_changain(struct comedi_device *dev, int chan)
456af201
DS
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 * Set the correct channel. The two channel banks are switched
210 * using the mask value.
266bfbdd
BA
211 * NB: To use differential channels, you should use
212 * mask = 0x30, but I haven't written the support for this
213 * yet. /JJ
456af201
DS
214 */
215
266bfbdd 216 if (chan_register >= 8)
456af201 217 chan_register = 0x20 | (chan_register & 0x7);
266bfbdd 218 else
456af201 219 chan_register |= 0x10;
456af201
DS
220 } else {
221 outb(chan_register, dev->iobase + PCL711_MUX);
222 }
223}
224
da91b269 225static int pcl711_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 226 struct comedi_insn *insn, unsigned int *data)
456af201
DS
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 /*
266bfbdd
BA
235 * Write the correct mode (software polling) and start polling
236 * by writing to the trigger register
456af201
DS
237 */
238 outb(1, dev->iobase + PCL711_MODE);
239
266bfbdd 240 if (!this_board->is_8112)
456af201 241 outb(0, dev->iobase + PCL711_SOFTTRIG);
456af201
DS
242
243 i = PCL711_TIMEOUT;
244 while (--i) {
245 hi = inb(dev->iobase + PCL711_AD_HI);
246 if (!(hi & PCL711_DRDY))
247 goto ok;
5f74ea14 248 udelay(1);
456af201 249 }
8f4e80af 250 printk(KERN_ERR "comedi%d: pcl711: A/D timeout\n", dev->minor);
456af201
DS
251 return -ETIME;
252
0a85b6f0 253ok:
456af201
DS
254 lo = inb(dev->iobase + PCL711_AD_LO);
255
256 data[n] = ((hi & 0xf) << 8) | lo;
257 }
258
259 return n;
260}
261
0a85b6f0
MT
262static int pcl711_ai_cmdtest(struct comedi_device *dev,
263 struct comedi_subdevice *s, struct comedi_cmd *cmd)
456af201
DS
264{
265 int tmp;
266 int err = 0;
267
268 /* step 1 */
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 /* step 2 */
298
299 if (cmd->scan_begin_src != TRIG_TIMER &&
0a85b6f0 300 cmd->scan_begin_src != TRIG_EXT)
456af201
DS
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 /* step 3 */
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 /* ignore */
342 }
343
344 if (err)
345 return 3;
346
347 /* step 4 */
348
349 if (cmd->scan_begin_src == TRIG_TIMER) {
350 tmp = cmd->scan_begin_arg;
351 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
0a85b6f0
MT
352 &devpriv->divisor1,
353 &devpriv->divisor2,
354 &cmd->scan_begin_arg,
355 cmd->flags & TRIG_ROUND_MASK);
456af201
DS
356 if (tmp != cmd->scan_begin_arg)
357 err++;
358 }
359
360 if (err)
361 return 4;
362
363 return 0;
364}
365
da91b269 366static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
456af201
DS
367{
368 int timer1, timer2;
ea6d0d4c 369 struct comedi_cmd *cmd = &s->async->cmd;
456af201
DS
370
371 pcl711_set_changain(dev, cmd->chanlist[0]);
372
373 if (cmd->scan_begin_src == TRIG_TIMER) {
374 /*
375 * Set timers
376 * timer chip is an 8253, with timers 1 and 2
377 * cascaded
378 * 0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
379 * Mode 2 = Rate generator
380 *
381 * 0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
382 */
383
48b1aff5 384 timer1 = timer2 = 0;
456af201 385 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
0a85b6f0
MT
386 &cmd->scan_begin_arg,
387 TRIG_ROUND_NEAREST);
456af201
DS
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 /* clear pending interrupts (just in case) */
397 outb(0, dev->iobase + PCL711_CLRINTR);
398
399 /*
400 * Set mode to IRQ transfer
401 */
402 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
403 } else {
404 /* external trigger */
405 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
406 }
407
408 return 0;
409}
410
411/*
412 analog output
413*/
da91b269 414static int pcl711_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 415 struct comedi_insn *insn, unsigned int *data)
456af201
DS
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),
0a85b6f0 422 dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
456af201 423 outb((data[n] >> 8),
0a85b6f0 424 dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
456af201
DS
425
426 devpriv->ao_readback[chan] = data[n];
427 }
428
429 return n;
430}
431
0a85b6f0
MT
432static int pcl711_ao_insn_read(struct comedi_device *dev,
433 struct comedi_subdevice *s,
434 struct comedi_insn *insn, unsigned int *data)
456af201
DS
435{
436 int n;
437 int chan = CR_CHAN(insn->chanspec);
438
266bfbdd 439 for (n = 0; n < insn->n; n++)
456af201 440 data[n] = devpriv->ao_readback[chan];
456af201
DS
441
442 return n;
443
444}
445
446/* Digital port read - Untested on 8112 */
0a85b6f0
MT
447static int pcl711_di_insn_bits(struct comedi_device *dev,
448 struct comedi_subdevice *s,
449 struct comedi_insn *insn, unsigned int *data)
456af201
DS
450{
451 if (insn->n != 2)
452 return -EINVAL;
453
454 data[1] = inb(dev->iobase + PCL711_DI_LO) |
0a85b6f0 455 (inb(dev->iobase + PCL711_DI_HI) << 8);
456af201
DS
456
457 return 2;
458}
459
460/* Digital port write - Untested on 8112 */
0a85b6f0
MT
461static int pcl711_do_insn_bits(struct comedi_device *dev,
462 struct comedi_subdevice *s,
463 struct comedi_insn *insn, unsigned int *data)
456af201
DS
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
da91b269 482static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
456af201
DS
483{
484 int ret;
485 unsigned long iobase;
486 unsigned int irq;
34c43922 487 struct comedi_subdevice *s;
456af201
DS
488
489 /* claim our I/O space */
490
491 iobase = it->options[0];
8f4e80af 492 printk(KERN_INFO "comedi%d: pcl711: 0x%04lx ", dev->minor, iobase);
456af201
DS
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 /* there should be a sanity check here */
500
501 /* set up some name stuff */
502 dev->board_name = this_board->name;
503
504 /* grab our IRQ */
505 irq = it->options[1];
506 if (irq > this_board->maxirq) {
8f4e80af 507 printk(KERN_ERR "irq out of range\n");
456af201
DS
508 return -EINVAL;
509 }
510 if (irq) {
5f74ea14 511 if (request_irq(irq, pcl711_interrupt, 0, "pcl711", dev)) {
8f4e80af 512 printk(KERN_ERR "unable to allocate irq %u\n", irq);
456af201
DS
513 return -EINVAL;
514 } else {
8f4e80af 515 printk(KERN_INFO "( irq = %u )\n", irq);
456af201
DS
516 }
517 }
518 dev->irq = irq;
519
c3744138
BP
520 ret = alloc_subdevices(dev, 4);
521 if (ret < 0)
456af201 522 return ret;
c3744138
BP
523
524 ret = alloc_private(dev, sizeof(struct pcl711_private));
525 if (ret < 0)
456af201
DS
526 return ret;
527
528 s = dev->subdevices + 0;
529 /* AI subdevice */
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 /* AO subdevice */
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 /* 16-bit digital input */
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 /* 16-bit digital out */
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 this is the "base value" for the mode register, which is
578 used for the irq on the PCL711
579 */
266bfbdd 580 if (this_board->is_pcl711b)
456af201 581 devpriv->mode = (dev->irq << 4);
456af201
DS
582
583 /* clear DAC */
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
8f4e80af 589 printk(KERN_INFO "\n");
456af201
DS
590
591 return 0;
592}
90f703d3 593
8dbf1460
HS
594static int pcl711_detach(struct comedi_device *dev)
595{
596 printk(KERN_INFO "comedi%d: pcl711: remove\n", dev->minor);
597
598 if (dev->irq)
599 free_irq(dev->irq, dev);
600
601 if (dev->iobase)
602 release_region(dev->iobase, PCL711_SIZE);
603
604 return 0;
605}
606
607static const struct pcl711_board boardtypes[] = {
608 { "pcl711", 0, 0, 0, 5, 8, 1, 0, &range_bipolar5 },
609 { "pcl711b", 1, 0, 0, 5, 8, 1, 7, &range_pcl711b_ai },
610 { "acl8112hg", 0, 1, 0, 12, 16, 2, 15, &range_acl8112hg_ai },
611 { "acl8112dg", 0, 1, 1, 9, 16, 2, 15, &range_acl8112dg_ai },
612};
613
294f930d 614static struct comedi_driver pcl711_driver = {
8dbf1460
HS
615 .driver_name = "pcl711",
616 .module = THIS_MODULE,
617 .attach = pcl711_attach,
618 .detach = pcl711_detach,
619 .board_name = &boardtypes[0].name,
620 .num_names = ARRAY_SIZE(boardtypes),
621 .offset = sizeof(struct pcl711_board),
622};
294f930d 623module_comedi_driver(pcl711_driver);
8dbf1460 624
90f703d3
AT
625MODULE_AUTHOR("Comedi http://www.comedi.org");
626MODULE_DESCRIPTION("Comedi low-level driver");
627MODULE_LICENSE("GPL");
This page took 0.310824 seconds and 5 git commands to generate.