staging: comedi: remove inline alloc_private()
[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
27020ffe 67#include "comedi_fc.h"
456af201
DS
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
9ced1de6 92static const struct comedi_lrange range_pcl711b_ai = { 5, {
0a85b6f0
MT
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 }
456af201 99};
0a85b6f0 100
9ced1de6 101static const struct comedi_lrange range_acl8112hg_ai = { 12, {
0a85b6f0
MT
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 }
456af201 115};
0a85b6f0 116
9ced1de6 117static const struct comedi_lrange range_acl8112dg_ai = { 9, {
0a85b6f0
MT
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 }
456af201
DS
128};
129
130/*
131 * flags
132 */
133
134#define PCL711_TIMEOUT 100
135#define PCL711_DRDY 0x10
136
137static const int i8253_osc_base = 500; /* 2 Mhz */
138
6445296e
BP
139struct pcl711_board {
140
456af201
DS
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;
9ced1de6 149 const struct comedi_lrange *ai_range_type;
6445296e
BP
150};
151
e1c8638f
BP
152struct pcl711_private {
153
456af201
DS
154 int board;
155 int adchan;
156 int ntrig;
157 int aip[8];
158 int mode;
790c5541 159 unsigned int ao_readback[2];
456af201
DS
160 unsigned int divisor1;
161 unsigned int divisor2;
e1c8638f
BP
162};
163
70265d24 164static irqreturn_t pcl711_interrupt(int irq, void *d)
456af201
DS
165{
166 int lo, hi;
167 int data;
71b5f4f1 168 struct comedi_device *dev = d;
90d662e3 169 const struct pcl711_board *board = comedi_board(dev);
9a1a6cf8 170 struct pcl711_private *devpriv = dev->private;
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 265{
9a1a6cf8 266 struct pcl711_private *devpriv = dev->private;
456af201
DS
267 int tmp;
268 int err = 0;
269
27020ffe 270 /* Step 1 : check if triggers are trivially valid */
456af201 271
27020ffe
HS
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);
456af201
DS
278
279 if (err)
280 return 1;
281
27020ffe 282 /* Step 2a : make sure trigger sources are unique */
456af201 283
27020ffe
HS
284 err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
285 err |= cfc_check_trigger_is_unique(cmd->stop_src);
286
287 /* Step 2b : and mutually compatible */
456af201
DS
288
289 if (err)
290 return 2;
291
292 /* step 3 */
293
294 if (cmd->start_arg != 0) {
295 cmd->start_arg = 0;
296 err++;
297 }
298 if (cmd->scan_begin_src == TRIG_EXT) {
299 if (cmd->scan_begin_arg != 0) {
300 cmd->scan_begin_arg = 0;
301 err++;
302 }
303 } else {
304#define MAX_SPEED 1000
305#define TIMER_BASE 100
306 if (cmd->scan_begin_arg < MAX_SPEED) {
307 cmd->scan_begin_arg = MAX_SPEED;
308 err++;
309 }
310 }
311 if (cmd->convert_arg != 0) {
312 cmd->convert_arg = 0;
313 err++;
314 }
315 if (cmd->scan_end_arg != cmd->chanlist_len) {
316 cmd->scan_end_arg = cmd->chanlist_len;
317 err++;
318 }
319 if (cmd->stop_src == TRIG_NONE) {
320 if (cmd->stop_arg != 0) {
321 cmd->stop_arg = 0;
322 err++;
323 }
324 } else {
325 /* ignore */
326 }
327
328 if (err)
329 return 3;
330
331 /* step 4 */
332
333 if (cmd->scan_begin_src == TRIG_TIMER) {
334 tmp = cmd->scan_begin_arg;
335 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
0a85b6f0
MT
336 &devpriv->divisor1,
337 &devpriv->divisor2,
338 &cmd->scan_begin_arg,
339 cmd->flags & TRIG_ROUND_MASK);
456af201
DS
340 if (tmp != cmd->scan_begin_arg)
341 err++;
342 }
343
344 if (err)
345 return 4;
346
347 return 0;
348}
349
da91b269 350static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
456af201 351{
9a1a6cf8 352 struct pcl711_private *devpriv = dev->private;
456af201 353 int timer1, timer2;
ea6d0d4c 354 struct comedi_cmd *cmd = &s->async->cmd;
456af201
DS
355
356 pcl711_set_changain(dev, cmd->chanlist[0]);
357
358 if (cmd->scan_begin_src == TRIG_TIMER) {
359 /*
360 * Set timers
361 * timer chip is an 8253, with timers 1 and 2
362 * cascaded
363 * 0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
364 * Mode 2 = Rate generator
365 *
366 * 0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
367 */
368
48b1aff5 369 timer1 = timer2 = 0;
456af201 370 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
0a85b6f0
MT
371 &cmd->scan_begin_arg,
372 TRIG_ROUND_NEAREST);
456af201
DS
373
374 outb(0x74, dev->iobase + PCL711_CTRCTL);
375 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
376 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
377 outb(0xb4, dev->iobase + PCL711_CTRCTL);
378 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
379 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
380
381 /* clear pending interrupts (just in case) */
382 outb(0, dev->iobase + PCL711_CLRINTR);
383
384 /*
385 * Set mode to IRQ transfer
386 */
387 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
388 } else {
389 /* external trigger */
390 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
391 }
392
393 return 0;
394}
395
396/*
397 analog output
398*/
da91b269 399static int pcl711_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 400 struct comedi_insn *insn, unsigned int *data)
456af201 401{
9a1a6cf8 402 struct pcl711_private *devpriv = dev->private;
456af201
DS
403 int n;
404 int chan = CR_CHAN(insn->chanspec);
405
406 for (n = 0; n < insn->n; n++) {
407 outb((data[n] & 0xff),
0a85b6f0 408 dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
456af201 409 outb((data[n] >> 8),
0a85b6f0 410 dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
456af201
DS
411
412 devpriv->ao_readback[chan] = data[n];
413 }
414
415 return n;
416}
417
0a85b6f0
MT
418static int pcl711_ao_insn_read(struct comedi_device *dev,
419 struct comedi_subdevice *s,
420 struct comedi_insn *insn, unsigned int *data)
456af201 421{
9a1a6cf8 422 struct pcl711_private *devpriv = dev->private;
456af201
DS
423 int n;
424 int chan = CR_CHAN(insn->chanspec);
425
266bfbdd 426 for (n = 0; n < insn->n; n++)
456af201 427 data[n] = devpriv->ao_readback[chan];
456af201
DS
428
429 return n;
430
431}
432
433/* Digital port read - Untested on 8112 */
0a85b6f0
MT
434static int pcl711_di_insn_bits(struct comedi_device *dev,
435 struct comedi_subdevice *s,
436 struct comedi_insn *insn, unsigned int *data)
456af201 437{
456af201 438 data[1] = inb(dev->iobase + PCL711_DI_LO) |
0a85b6f0 439 (inb(dev->iobase + PCL711_DI_HI) << 8);
456af201 440
a2714e3e 441 return insn->n;
456af201
DS
442}
443
444/* Digital port write - Untested on 8112 */
0a85b6f0
MT
445static int pcl711_do_insn_bits(struct comedi_device *dev,
446 struct comedi_subdevice *s,
447 struct comedi_insn *insn, unsigned int *data)
456af201 448{
456af201
DS
449 if (data[0]) {
450 s->state &= ~data[0];
451 s->state |= data[0] & data[1];
452 }
453 if (data[0] & 0x00ff)
454 outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
455 if (data[0] & 0xff00)
456 outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
457
458 data[1] = s->state;
459
a2714e3e 460 return insn->n;
456af201
DS
461}
462
da91b269 463static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
456af201 464{
90d662e3 465 const struct pcl711_board *board = comedi_board(dev);
9a1a6cf8 466 struct pcl711_private *devpriv;
456af201
DS
467 int ret;
468 unsigned long iobase;
469 unsigned int irq;
34c43922 470 struct comedi_subdevice *s;
456af201
DS
471
472 /* claim our I/O space */
473
474 iobase = it->options[0];
8f4e80af 475 printk(KERN_INFO "comedi%d: pcl711: 0x%04lx ", dev->minor, iobase);
456af201
DS
476 if (!request_region(iobase, PCL711_SIZE, "pcl711")) {
477 printk("I/O port conflict\n");
478 return -EIO;
479 }
480 dev->iobase = iobase;
481
482 /* there should be a sanity check here */
483
90d662e3 484 dev->board_name = board->name;
456af201
DS
485
486 /* grab our IRQ */
487 irq = it->options[1];
90d662e3 488 if (irq > board->maxirq) {
8f4e80af 489 printk(KERN_ERR "irq out of range\n");
456af201
DS
490 return -EINVAL;
491 }
492 if (irq) {
5f74ea14 493 if (request_irq(irq, pcl711_interrupt, 0, "pcl711", dev)) {
8f4e80af 494 printk(KERN_ERR "unable to allocate irq %u\n", irq);
456af201
DS
495 return -EINVAL;
496 } else {
8f4e80af 497 printk(KERN_INFO "( irq = %u )\n", irq);
456af201
DS
498 }
499 }
500 dev->irq = irq;
501
2f0b9d08 502 ret = comedi_alloc_subdevices(dev, 4);
8b6c5694 503 if (ret)
456af201 504 return ret;
c3744138 505
c34fa261
HS
506 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
507 if (!devpriv)
508 return -ENOMEM;
509 dev->private = devpriv;
456af201 510
af77727a 511 s = &dev->subdevices[0];
456af201
DS
512 /* AI subdevice */
513 s->type = COMEDI_SUBD_AI;
514 s->subdev_flags = SDF_READABLE | SDF_GROUND;
90d662e3 515 s->n_chan = board->n_aichan;
456af201
DS
516 s->maxdata = 0xfff;
517 s->len_chanlist = 1;
90d662e3 518 s->range_table = board->ai_range_type;
456af201
DS
519 s->insn_read = pcl711_ai_insn;
520 if (irq) {
521 dev->read_subdev = s;
522 s->subdev_flags |= SDF_CMD_READ;
523 s->do_cmdtest = pcl711_ai_cmdtest;
524 s->do_cmd = pcl711_ai_cmd;
525 }
526
af77727a 527 s = &dev->subdevices[1];
456af201
DS
528 /* AO subdevice */
529 s->type = COMEDI_SUBD_AO;
530 s->subdev_flags = SDF_WRITABLE;
90d662e3 531 s->n_chan = board->n_aochan;
456af201
DS
532 s->maxdata = 0xfff;
533 s->len_chanlist = 1;
534 s->range_table = &range_bipolar5;
535 s->insn_write = pcl711_ao_insn;
536 s->insn_read = pcl711_ao_insn_read;
537
af77727a 538 s = &dev->subdevices[2];
456af201
DS
539 /* 16-bit digital input */
540 s->type = COMEDI_SUBD_DI;
541 s->subdev_flags = SDF_READABLE;
542 s->n_chan = 16;
543 s->maxdata = 1;
544 s->len_chanlist = 16;
545 s->range_table = &range_digital;
546 s->insn_bits = pcl711_di_insn_bits;
547
af77727a 548 s = &dev->subdevices[3];
456af201
DS
549 /* 16-bit digital out */
550 s->type = COMEDI_SUBD_DO;
551 s->subdev_flags = SDF_WRITABLE;
552 s->n_chan = 16;
553 s->maxdata = 1;
554 s->len_chanlist = 16;
555 s->range_table = &range_digital;
556 s->state = 0;
557 s->insn_bits = pcl711_do_insn_bits;
558
559 /*
560 this is the "base value" for the mode register, which is
561 used for the irq on the PCL711
562 */
90d662e3 563 if (board->is_pcl711b)
456af201 564 devpriv->mode = (dev->irq << 4);
456af201
DS
565
566 /* clear DAC */
567 outb(0, dev->iobase + PCL711_DA0_LO);
568 outb(0, dev->iobase + PCL711_DA0_HI);
569 outb(0, dev->iobase + PCL711_DA1_LO);
570 outb(0, dev->iobase + PCL711_DA1_HI);
571
8f4e80af 572 printk(KERN_INFO "\n");
456af201
DS
573
574 return 0;
575}
90f703d3 576
484ecc95 577static void pcl711_detach(struct comedi_device *dev)
8dbf1460 578{
8dbf1460
HS
579 if (dev->irq)
580 free_irq(dev->irq, dev);
8dbf1460
HS
581 if (dev->iobase)
582 release_region(dev->iobase, PCL711_SIZE);
8dbf1460
HS
583}
584
585static const struct pcl711_board boardtypes[] = {
586 { "pcl711", 0, 0, 0, 5, 8, 1, 0, &range_bipolar5 },
587 { "pcl711b", 1, 0, 0, 5, 8, 1, 7, &range_pcl711b_ai },
588 { "acl8112hg", 0, 1, 0, 12, 16, 2, 15, &range_acl8112hg_ai },
589 { "acl8112dg", 0, 1, 1, 9, 16, 2, 15, &range_acl8112dg_ai },
590};
591
294f930d 592static struct comedi_driver pcl711_driver = {
8dbf1460
HS
593 .driver_name = "pcl711",
594 .module = THIS_MODULE,
595 .attach = pcl711_attach,
596 .detach = pcl711_detach,
597 .board_name = &boardtypes[0].name,
598 .num_names = ARRAY_SIZE(boardtypes),
599 .offset = sizeof(struct pcl711_board),
600};
294f930d 601module_comedi_driver(pcl711_driver);
8dbf1460 602
90f703d3
AT
603MODULE_AUTHOR("Comedi http://www.comedi.org");
604MODULE_DESCRIPTION("Comedi low-level driver");
605MODULE_LICENSE("GPL");
This page took 0.412798 seconds and 5 git commands to generate.