Staging: comedi: remove RT code
[safe/jmp/linux-2.6] / drivers / staging / comedi / drivers / pcl711.c
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 /*
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)
33
34 Since these boards do not have DMA or FIFOs, only immediate mode is
35 supported.
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
61 #include <linux/interrupt.h>
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
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)
97         }
98 };
99 static const struct comedi_lrange range_acl8112hg_ai = { 12, {
100                         BIP_RANGE(5),
101                         BIP_RANGE(0.5),
102                         BIP_RANGE(0.05),
103                         BIP_RANGE(0.005),
104                         UNI_RANGE(10),
105                         UNI_RANGE(1),
106                         UNI_RANGE(0.1),
107                         UNI_RANGE(0.01),
108                         BIP_RANGE(10),
109                         BIP_RANGE(1),
110                         BIP_RANGE(0.1),
111                         BIP_RANGE(0.01)
112         }
113 };
114 static const struct comedi_lrange range_acl8112dg_ai = { 9, {
115                         BIP_RANGE(5),
116                         BIP_RANGE(2.5),
117                         BIP_RANGE(1.25),
118                         BIP_RANGE(0.625),
119                         UNI_RANGE(10),
120                         UNI_RANGE(5),
121                         UNI_RANGE(2.5),
122                         UNI_RANGE(1.25),
123                         BIP_RANGE(10)
124         }
125 };
126
127 /*
128  * flags
129  */
130
131 #define PCL711_TIMEOUT 100
132 #define PCL711_DRDY 0x10
133
134 static const int i8253_osc_base = 500;  /* 2 Mhz */
135
136 struct pcl711_board {
137
138         const char *name;
139         int is_pcl711b;
140         int is_8112;
141         int is_dg;
142         int n_ranges;
143         int n_aichan;
144         int n_aochan;
145         int maxirq;
146         const struct comedi_lrange *ai_range_type;
147 };
148
149
150 static const struct pcl711_board boardtypes[] = {
151         {"pcl711", 0, 0, 0, 5, 8, 1, 0, &range_bipolar5},
152         {"pcl711b", 1, 0, 0, 5, 8, 1, 7, &range_pcl711b_ai},
153         {"acl8112hg", 0, 1, 0, 12, 16, 2, 15, &range_acl8112hg_ai},
154         {"acl8112dg", 0, 1, 1, 9, 16, 2, 15, &range_acl8112dg_ai},
155 };
156
157 #define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl711_board))
158 #define this_board ((const struct pcl711_board *)dev->board_ptr)
159
160 static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it);
161 static int pcl711_detach(struct comedi_device *dev);
162 static struct comedi_driver driver_pcl711 = {
163         .driver_name = "pcl711",
164         .module = THIS_MODULE,
165         .attach = pcl711_attach,
166         .detach = pcl711_detach,
167         .board_name = &boardtypes[0].name,
168         .num_names = n_boardtypes,
169         .offset = sizeof(struct pcl711_board),
170 };
171
172 COMEDI_INITCLEANUP(driver_pcl711);
173
174 struct pcl711_private {
175
176         int board;
177         int adchan;
178         int ntrig;
179         int aip[8];
180         int mode;
181         unsigned int ao_readback[2];
182         unsigned int divisor1;
183         unsigned int divisor2;
184 };
185
186
187 #define devpriv ((struct pcl711_private *)dev->private)
188
189 static irqreturn_t pcl711_interrupt(int irq, void *d)
190 {
191         int lo, hi;
192         int data;
193         struct comedi_device *dev = d;
194         struct comedi_subdevice *s = dev->subdevices + 0;
195
196         if (!dev->attached) {
197                 comedi_error(dev, "spurious interrupt");
198                 return IRQ_HANDLED;
199         }
200
201         hi = inb(dev->iobase + PCL711_AD_HI);
202         lo = inb(dev->iobase + PCL711_AD_LO);
203         outb(0, dev->iobase + PCL711_CLRINTR);
204
205         data = (hi << 8) | lo;
206
207         /* FIXME! Nothing else sets ntrig! */
208         if (!(--devpriv->ntrig)) {
209                 if (this_board->is_8112) {
210                         outb(1, dev->iobase + PCL711_MODE);
211                 } else {
212                         outb(0, dev->iobase + PCL711_MODE);
213                 }
214
215                 s->async->events |= COMEDI_CB_EOA;
216         }
217         comedi_event(dev, s);
218         return IRQ_HANDLED;
219 }
220
221 static void pcl711_set_changain(struct comedi_device *dev, int chan)
222 {
223         int chan_register;
224
225         outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
226
227         chan_register = CR_CHAN(chan);
228
229         if (this_board->is_8112) {
230
231                 /*
232                  *  Set the correct channel.  The two channel banks are switched
233                  *  using the mask value.
234                  *  NB: To use differential channels, you should use mask = 0x30,
235                  *  but I haven't written the support for this yet. /JJ
236                  */
237
238                 if (chan_register >= 8) {
239                         chan_register = 0x20 | (chan_register & 0x7);
240                 } else {
241                         chan_register |= 0x10;
242                 }
243         } else {
244                 outb(chan_register, dev->iobase + PCL711_MUX);
245         }
246 }
247
248 static int pcl711_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
249         struct comedi_insn *insn, unsigned int *data)
250 {
251         int i, n;
252         int hi, lo;
253
254         pcl711_set_changain(dev, insn->chanspec);
255
256         for (n = 0; n < insn->n; n++) {
257                 /*
258                  *  Write the correct mode (software polling) and start polling by writing
259                  *  to the trigger register
260                  */
261                 outb(1, dev->iobase + PCL711_MODE);
262
263                 if (this_board->is_8112) {
264                 } else {
265                         outb(0, dev->iobase + PCL711_SOFTTRIG);
266                 }
267
268                 i = PCL711_TIMEOUT;
269                 while (--i) {
270                         hi = inb(dev->iobase + PCL711_AD_HI);
271                         if (!(hi & PCL711_DRDY))
272                                 goto ok;
273                         udelay(1);
274                 }
275                 printk("comedi%d: pcl711: A/D timeout\n", dev->minor);
276                 return -ETIME;
277
278               ok:
279                 lo = inb(dev->iobase + PCL711_AD_LO);
280
281                 data[n] = ((hi & 0xf) << 8) | lo;
282         }
283
284         return n;
285 }
286
287 static int pcl711_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
288         struct comedi_cmd *cmd)
289 {
290         int tmp;
291         int err = 0;
292
293         /* step 1 */
294         tmp = cmd->start_src;
295         cmd->start_src &= TRIG_NOW;
296         if (!cmd->start_src || tmp != cmd->start_src)
297                 err++;
298
299         tmp = cmd->scan_begin_src;
300         cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
301         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
302                 err++;
303
304         tmp = cmd->convert_src;
305         cmd->convert_src &= TRIG_NOW;
306         if (!cmd->convert_src || tmp != cmd->convert_src)
307                 err++;
308
309         tmp = cmd->scan_end_src;
310         cmd->scan_end_src &= TRIG_COUNT;
311         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
312                 err++;
313
314         tmp = cmd->stop_src;
315         cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
316         if (!cmd->stop_src || tmp != cmd->stop_src)
317                 err++;
318
319         if (err)
320                 return 1;
321
322         /* step 2 */
323
324         if (cmd->scan_begin_src != TRIG_TIMER &&
325                 cmd->scan_begin_src != TRIG_EXT)
326                 err++;
327         if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
328                 err++;
329
330         if (err)
331                 return 2;
332
333         /* step 3 */
334
335         if (cmd->start_arg != 0) {
336                 cmd->start_arg = 0;
337                 err++;
338         }
339         if (cmd->scan_begin_src == TRIG_EXT) {
340                 if (cmd->scan_begin_arg != 0) {
341                         cmd->scan_begin_arg = 0;
342                         err++;
343                 }
344         } else {
345 #define MAX_SPEED 1000
346 #define TIMER_BASE 100
347                 if (cmd->scan_begin_arg < MAX_SPEED) {
348                         cmd->scan_begin_arg = MAX_SPEED;
349                         err++;
350                 }
351         }
352         if (cmd->convert_arg != 0) {
353                 cmd->convert_arg = 0;
354                 err++;
355         }
356         if (cmd->scan_end_arg != cmd->chanlist_len) {
357                 cmd->scan_end_arg = cmd->chanlist_len;
358                 err++;
359         }
360         if (cmd->stop_src == TRIG_NONE) {
361                 if (cmd->stop_arg != 0) {
362                         cmd->stop_arg = 0;
363                         err++;
364                 }
365         } else {
366                 /* ignore */
367         }
368
369         if (err)
370                 return 3;
371
372         /* step 4 */
373
374         if (cmd->scan_begin_src == TRIG_TIMER) {
375                 tmp = cmd->scan_begin_arg;
376                 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
377                         &devpriv->divisor1, &devpriv->divisor2,
378                         &cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
379                 if (tmp != cmd->scan_begin_arg)
380                         err++;
381         }
382
383         if (err)
384                 return 4;
385
386         return 0;
387 }
388
389 static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
390 {
391         int timer1, timer2;
392         struct comedi_cmd *cmd = &s->async->cmd;
393
394         pcl711_set_changain(dev, cmd->chanlist[0]);
395
396         if (cmd->scan_begin_src == TRIG_TIMER) {
397                 /*
398                  *  Set timers
399                  *      timer chip is an 8253, with timers 1 and 2
400                  *      cascaded
401                  *  0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
402                  *        Mode 2 = Rate generator
403                  *
404                  *  0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
405                  */
406
407                 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
408                         &cmd->scan_begin_arg, TRIG_ROUND_NEAREST);
409
410                 outb(0x74, dev->iobase + PCL711_CTRCTL);
411                 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
412                 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
413                 outb(0xb4, dev->iobase + PCL711_CTRCTL);
414                 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
415                 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
416
417                 /* clear pending interrupts (just in case) */
418                 outb(0, dev->iobase + PCL711_CLRINTR);
419
420                 /*
421                  *  Set mode to IRQ transfer
422                  */
423                 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
424         } else {
425                 /* external trigger */
426                 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
427         }
428
429         return 0;
430 }
431
432 /*
433    analog output
434 */
435 static int pcl711_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
436         struct comedi_insn *insn, unsigned int *data)
437 {
438         int n;
439         int chan = CR_CHAN(insn->chanspec);
440
441         for (n = 0; n < insn->n; n++) {
442                 outb((data[n] & 0xff),
443                         dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
444                 outb((data[n] >> 8),
445                         dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
446
447                 devpriv->ao_readback[chan] = data[n];
448         }
449
450         return n;
451 }
452
453 static int pcl711_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
454         struct comedi_insn *insn, unsigned int *data)
455 {
456         int n;
457         int chan = CR_CHAN(insn->chanspec);
458
459         for (n = 0; n < insn->n; n++) {
460                 data[n] = devpriv->ao_readback[chan];
461         }
462
463         return n;
464
465 }
466
467 /* Digital port read - Untested on 8112 */
468 static int pcl711_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
469         struct comedi_insn *insn, unsigned int *data)
470 {
471         if (insn->n != 2)
472                 return -EINVAL;
473
474         data[1] = inb(dev->iobase + PCL711_DI_LO) |
475                 (inb(dev->iobase + PCL711_DI_HI) << 8);
476
477         return 2;
478 }
479
480 /* Digital port write - Untested on 8112 */
481 static int pcl711_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
482         struct comedi_insn *insn, unsigned int *data)
483 {
484         if (insn->n != 2)
485                 return -EINVAL;
486
487         if (data[0]) {
488                 s->state &= ~data[0];
489                 s->state |= data[0] & data[1];
490         }
491         if (data[0] & 0x00ff)
492                 outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
493         if (data[0] & 0xff00)
494                 outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
495
496         data[1] = s->state;
497
498         return 2;
499 }
500
501 /*  Free any resources that we have claimed  */
502 static int pcl711_detach(struct comedi_device *dev)
503 {
504         printk("comedi%d: pcl711: remove\n", dev->minor);
505
506         if (dev->irq)
507                 free_irq(dev->irq, dev);
508
509         if (dev->iobase)
510                 release_region(dev->iobase, PCL711_SIZE);
511
512         return 0;
513 }
514
515 /*  Initialization */
516 static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
517 {
518         int ret;
519         unsigned long iobase;
520         unsigned int irq;
521         struct comedi_subdevice *s;
522
523         /* claim our I/O space */
524
525         iobase = it->options[0];
526         printk("comedi%d: pcl711: 0x%04lx ", dev->minor, iobase);
527         if (!request_region(iobase, PCL711_SIZE, "pcl711")) {
528                 printk("I/O port conflict\n");
529                 return -EIO;
530         }
531         dev->iobase = iobase;
532
533         /* there should be a sanity check here */
534
535         /* set up some name stuff */
536         dev->board_name = this_board->name;
537
538         /* grab our IRQ */
539         irq = it->options[1];
540         if (irq > this_board->maxirq) {
541                 printk("irq out of range\n");
542                 return -EINVAL;
543         }
544         if (irq) {
545                 if (request_irq(irq, pcl711_interrupt, 0, "pcl711", dev)) {
546                         printk("unable to allocate irq %u\n", irq);
547                         return -EINVAL;
548                 } else {
549                         printk("( irq = %u )\n", irq);
550                 }
551         }
552         dev->irq = irq;
553
554         ret = alloc_subdevices(dev, 4);
555         if (ret < 0)
556                 return ret;
557
558         ret = alloc_private(dev, sizeof(struct pcl711_private));
559         if (ret < 0)
560                 return ret;
561
562         s = dev->subdevices + 0;
563         /* AI subdevice */
564         s->type = COMEDI_SUBD_AI;
565         s->subdev_flags = SDF_READABLE | SDF_GROUND;
566         s->n_chan = this_board->n_aichan;
567         s->maxdata = 0xfff;
568         s->len_chanlist = 1;
569         s->range_table = this_board->ai_range_type;
570         s->insn_read = pcl711_ai_insn;
571         if (irq) {
572                 dev->read_subdev = s;
573                 s->subdev_flags |= SDF_CMD_READ;
574                 s->do_cmdtest = pcl711_ai_cmdtest;
575                 s->do_cmd = pcl711_ai_cmd;
576         }
577
578         s++;
579         /* AO subdevice */
580         s->type = COMEDI_SUBD_AO;
581         s->subdev_flags = SDF_WRITABLE;
582         s->n_chan = this_board->n_aochan;
583         s->maxdata = 0xfff;
584         s->len_chanlist = 1;
585         s->range_table = &range_bipolar5;
586         s->insn_write = pcl711_ao_insn;
587         s->insn_read = pcl711_ao_insn_read;
588
589         s++;
590         /* 16-bit digital input */
591         s->type = COMEDI_SUBD_DI;
592         s->subdev_flags = SDF_READABLE;
593         s->n_chan = 16;
594         s->maxdata = 1;
595         s->len_chanlist = 16;
596         s->range_table = &range_digital;
597         s->insn_bits = pcl711_di_insn_bits;
598
599         s++;
600         /* 16-bit digital out */
601         s->type = COMEDI_SUBD_DO;
602         s->subdev_flags = SDF_WRITABLE;
603         s->n_chan = 16;
604         s->maxdata = 1;
605         s->len_chanlist = 16;
606         s->range_table = &range_digital;
607         s->state = 0;
608         s->insn_bits = pcl711_do_insn_bits;
609
610         /*
611            this is the "base value" for the mode register, which is
612            used for the irq on the PCL711
613          */
614         if (this_board->is_pcl711b) {
615                 devpriv->mode = (dev->irq << 4);
616         }
617
618         /* clear DAC */
619         outb(0, dev->iobase + PCL711_DA0_LO);
620         outb(0, dev->iobase + PCL711_DA0_HI);
621         outb(0, dev->iobase + PCL711_DA1_LO);
622         outb(0, dev->iobase + PCL711_DA1_HI);
623
624         printk("\n");
625
626         return 0;
627 }