Staging: winbond: usb_free_urb(NULL) is safe
[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
e1c8638f
BP
151struct pcl711_private {
152
456af201
DS
153 int board;
154 int adchan;
155 int ntrig;
156 int aip[8];
157 int mode;
790c5541 158 unsigned int ao_readback[2];
456af201
DS
159 unsigned int divisor1;
160 unsigned int divisor2;
e1c8638f
BP
161};
162
e1c8638f 163#define devpriv ((struct pcl711_private *)dev->private)
456af201 164
70265d24 165static irqreturn_t pcl711_interrupt(int irq, void *d)
456af201
DS
166{
167 int lo, hi;
168 int data;
71b5f4f1 169 struct comedi_device *dev = d;
90d662e3 170 const struct pcl711_board *board = comedi_board(dev);
af77727a 171 struct comedi_subdevice *s = &dev->subdevices[0];
456af201
DS
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 /* FIXME! Nothing else sets ntrig! */
185 if (!(--devpriv->ntrig)) {
90d662e3 186 if (board->is_8112)
456af201 187 outb(1, dev->iobase + PCL711_MODE);
266bfbdd 188 else
456af201 189 outb(0, dev->iobase + PCL711_MODE);
456af201
DS
190
191 s->async->events |= COMEDI_CB_EOA;
192 }
193 comedi_event(dev, s);
194 return IRQ_HANDLED;
195}
196
da91b269 197static void pcl711_set_changain(struct comedi_device *dev, int chan)
456af201 198{
90d662e3 199 const struct pcl711_board *board = comedi_board(dev);
456af201
DS
200 int chan_register;
201
202 outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
203
204 chan_register = CR_CHAN(chan);
205
90d662e3 206 if (board->is_8112) {
456af201
DS
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 227{
90d662e3 228 const struct pcl711_board *board = comedi_board(dev);
456af201
DS
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 /*
266bfbdd
BA
236 * Write the correct mode (software polling) and start polling
237 * by writing to the trigger register
456af201
DS
238 */
239 outb(1, dev->iobase + PCL711_MODE);
240
90d662e3 241 if (!board->is_8112)
456af201 242 outb(0, dev->iobase + PCL711_SOFTTRIG);
456af201
DS
243
244 i = PCL711_TIMEOUT;
245 while (--i) {
246 hi = inb(dev->iobase + PCL711_AD_HI);
247 if (!(hi & PCL711_DRDY))
248 goto ok;
5f74ea14 249 udelay(1);
456af201 250 }
8f4e80af 251 printk(KERN_ERR "comedi%d: pcl711: A/D timeout\n", dev->minor);
456af201
DS
252 return -ETIME;
253
0a85b6f0 254ok:
456af201
DS
255 lo = inb(dev->iobase + PCL711_AD_LO);
256
257 data[n] = ((hi & 0xf) << 8) | lo;
258 }
259
260 return n;
261}
262
0a85b6f0
MT
263static int pcl711_ai_cmdtest(struct comedi_device *dev,
264 struct comedi_subdevice *s, struct comedi_cmd *cmd)
456af201
DS
265{
266 int tmp;
267 int err = 0;
268
269 /* step 1 */
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 /* step 2 */
299
300 if (cmd->scan_begin_src != TRIG_TIMER &&
0a85b6f0 301 cmd->scan_begin_src != TRIG_EXT)
456af201
DS
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 /* step 3 */
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 /* ignore */
343 }
344
345 if (err)
346 return 3;
347
348 /* step 4 */
349
350 if (cmd->scan_begin_src == TRIG_TIMER) {
351 tmp = cmd->scan_begin_arg;
352 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
0a85b6f0
MT
353 &devpriv->divisor1,
354 &devpriv->divisor2,
355 &cmd->scan_begin_arg,
356 cmd->flags & TRIG_ROUND_MASK);
456af201
DS
357 if (tmp != cmd->scan_begin_arg)
358 err++;
359 }
360
361 if (err)
362 return 4;
363
364 return 0;
365}
366
da91b269 367static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
456af201
DS
368{
369 int timer1, timer2;
ea6d0d4c 370 struct comedi_cmd *cmd = &s->async->cmd;
456af201
DS
371
372 pcl711_set_changain(dev, cmd->chanlist[0]);
373
374 if (cmd->scan_begin_src == TRIG_TIMER) {
375 /*
376 * Set timers
377 * timer chip is an 8253, with timers 1 and 2
378 * cascaded
379 * 0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
380 * Mode 2 = Rate generator
381 *
382 * 0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
383 */
384
48b1aff5 385 timer1 = timer2 = 0;
456af201 386 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
0a85b6f0
MT
387 &cmd->scan_begin_arg,
388 TRIG_ROUND_NEAREST);
456af201
DS
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 /* clear pending interrupts (just in case) */
398 outb(0, dev->iobase + PCL711_CLRINTR);
399
400 /*
401 * Set mode to IRQ transfer
402 */
403 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
404 } else {
405 /* external trigger */
406 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
407 }
408
409 return 0;
410}
411
412/*
413 analog output
414*/
da91b269 415static int pcl711_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 416 struct comedi_insn *insn, unsigned int *data)
456af201
DS
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),
0a85b6f0 423 dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
456af201 424 outb((data[n] >> 8),
0a85b6f0 425 dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
456af201
DS
426
427 devpriv->ao_readback[chan] = data[n];
428 }
429
430 return n;
431}
432
0a85b6f0
MT
433static int pcl711_ao_insn_read(struct comedi_device *dev,
434 struct comedi_subdevice *s,
435 struct comedi_insn *insn, unsigned int *data)
456af201
DS
436{
437 int n;
438 int chan = CR_CHAN(insn->chanspec);
439
266bfbdd 440 for (n = 0; n < insn->n; n++)
456af201 441 data[n] = devpriv->ao_readback[chan];
456af201
DS
442
443 return n;
444
445}
446
447/* Digital port read - Untested on 8112 */
0a85b6f0
MT
448static int pcl711_di_insn_bits(struct comedi_device *dev,
449 struct comedi_subdevice *s,
450 struct comedi_insn *insn, unsigned int *data)
456af201 451{
456af201 452 data[1] = inb(dev->iobase + PCL711_DI_LO) |
0a85b6f0 453 (inb(dev->iobase + PCL711_DI_HI) << 8);
456af201 454
a2714e3e 455 return insn->n;
456af201
DS
456}
457
458/* Digital port write - Untested on 8112 */
0a85b6f0
MT
459static int pcl711_do_insn_bits(struct comedi_device *dev,
460 struct comedi_subdevice *s,
461 struct comedi_insn *insn, unsigned int *data)
456af201 462{
456af201
DS
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
a2714e3e 474 return insn->n;
456af201
DS
475}
476
da91b269 477static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
456af201 478{
90d662e3 479 const struct pcl711_board *board = comedi_board(dev);
456af201
DS
480 int ret;
481 unsigned long iobase;
482 unsigned int irq;
34c43922 483 struct comedi_subdevice *s;
456af201
DS
484
485 /* claim our I/O space */
486
487 iobase = it->options[0];
8f4e80af 488 printk(KERN_INFO "comedi%d: pcl711: 0x%04lx ", dev->minor, iobase);
456af201
DS
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 /* there should be a sanity check here */
496
90d662e3 497 dev->board_name = board->name;
456af201
DS
498
499 /* grab our IRQ */
500 irq = it->options[1];
90d662e3 501 if (irq > board->maxirq) {
8f4e80af 502 printk(KERN_ERR "irq out of range\n");
456af201
DS
503 return -EINVAL;
504 }
505 if (irq) {
5f74ea14 506 if (request_irq(irq, pcl711_interrupt, 0, "pcl711", dev)) {
8f4e80af 507 printk(KERN_ERR "unable to allocate irq %u\n", irq);
456af201
DS
508 return -EINVAL;
509 } else {
8f4e80af 510 printk(KERN_INFO "( irq = %u )\n", irq);
456af201
DS
511 }
512 }
513 dev->irq = irq;
514
2f0b9d08 515 ret = comedi_alloc_subdevices(dev, 4);
8b6c5694 516 if (ret)
456af201 517 return ret;
c3744138
BP
518
519 ret = alloc_private(dev, sizeof(struct pcl711_private));
520 if (ret < 0)
456af201
DS
521 return ret;
522
af77727a 523 s = &dev->subdevices[0];
456af201
DS
524 /* AI subdevice */
525 s->type = COMEDI_SUBD_AI;
526 s->subdev_flags = SDF_READABLE | SDF_GROUND;
90d662e3 527 s->n_chan = board->n_aichan;
456af201
DS
528 s->maxdata = 0xfff;
529 s->len_chanlist = 1;
90d662e3 530 s->range_table = board->ai_range_type;
456af201
DS
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
af77727a 539 s = &dev->subdevices[1];
456af201
DS
540 /* AO subdevice */
541 s->type = COMEDI_SUBD_AO;
542 s->subdev_flags = SDF_WRITABLE;
90d662e3 543 s->n_chan = board->n_aochan;
456af201
DS
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
af77727a 550 s = &dev->subdevices[2];
456af201
DS
551 /* 16-bit digital input */
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
af77727a 560 s = &dev->subdevices[3];
456af201
DS
561 /* 16-bit digital out */
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 this is the "base value" for the mode register, which is
573 used for the irq on the PCL711
574 */
90d662e3 575 if (board->is_pcl711b)
456af201 576 devpriv->mode = (dev->irq << 4);
456af201
DS
577
578 /* clear DAC */
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
8f4e80af 584 printk(KERN_INFO "\n");
456af201
DS
585
586 return 0;
587}
90f703d3 588
484ecc95 589static void pcl711_detach(struct comedi_device *dev)
8dbf1460 590{
8dbf1460
HS
591 if (dev->irq)
592 free_irq(dev->irq, dev);
8dbf1460
HS
593 if (dev->iobase)
594 release_region(dev->iobase, PCL711_SIZE);
8dbf1460
HS
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
294f930d 604static struct comedi_driver pcl711_driver = {
8dbf1460
HS
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};
294f930d 613module_comedi_driver(pcl711_driver);
8dbf1460 614
90f703d3
AT
615MODULE_AUTHOR("Comedi http://www.comedi.org");
616MODULE_DESCRIPTION("Comedi low-level driver");
617MODULE_LICENSE("GPL");
This page took 0.393458 seconds and 5 git commands to generate.