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