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