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