module, Makefile, config changes
[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 <comedi_module.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 static int pcl711_attach(comedi_device *dev,comedi_devconfig *it);
122 static int pcl711_detach(comedi_device *dev);
123 static int pcl711_recognize(char *name);
124 comedi_driver driver_pcl711={
125         driver_name:    "pcl711",
126         module:         THIS_MODULE,
127         attach:         pcl711_attach,
128         detach:         pcl711_detach,
129         recognize:      pcl711_recognize,
130 };
131
132 typedef int bool;
133
134 /*
135  * flags
136  */
137
138 #define PCL711_TIMEOUT 100
139 #define PCL711_DRDY 0x10
140
141 int i8253_osc_base = 500;       /* 2 Mhz */
142
143 typedef struct {
144         char *name;
145         int is_pcl711b;
146         int is_8112;
147         int is_dg;
148         int n_ranges;
149         int n_aichan;
150         int n_aochan;
151         int maxirq;
152         comedi_lrange * ai_range_type;
153 } boardtype;
154
155 static boardtype boardtypes[] =
156 {
157         {"pcl711", 0, 0, 0, 5, 8, 1, 0, &range_bipolar5},
158         {"pcl711b", 1, 0, 0, 5, 8, 1, 7, &range_pcl711b_ai},
159         {"acl8112hg", 0, 1, 0, 12, 16, 2, 15, &range_acl8112hg_ai},
160         {"acl8112dg", 0, 1, 1, 9, 16, 2, 15, &range_acl8112dg_ai},
161 };
162 #define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
163
164 typedef struct {
165         int board;
166         int adchan;
167         int ntrig;
168         int aip[8];
169         int mode;
170 } pcl711_private;
171
172 #define devpriv ((pcl711_private *)dev->private)
173 #define this_board (boardtypes+dev->board)
174
175 static void pcl711_interrupt(int irq, void *d, struct pt_regs *regs)
176 {
177         int lo, hi;
178         int data;
179         comedi_device *dev = d;
180         comedi_subdevice *s = dev->subdevices + 0;
181
182         hi = inb(dev->iobase + PCL711_AD_HI);
183         lo = inb(dev->iobase + PCL711_AD_LO);
184         outb(0, dev->iobase + PCL711_CLRINTR);
185
186         data = (hi << 8) | lo;
187
188         if (!(--devpriv->ntrig)) {
189                 if (this_board->is_8112) {
190                         outb(1, dev->iobase + PCL711_MODE);
191                 } else {
192                         outb(0, dev->iobase + PCL711_MODE);
193                 }
194                 s->busy = 0;
195
196                 comedi_done(dev,s);
197         }
198 }
199
200 static void pcl711_set_changain(comedi_device * dev, int chan)
201 {
202         int chan_register;
203
204         outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
205         
206         chan_register=CR_CHAN(chan);
207
208         if (this_board->is_8112) {
209
210                 /*
211                  *  Set the correct channel.  The two channel banks are switched
212                  *  using the mask value.
213                  *  NB: To use differential channels, you should use mask = 0x30,
214                  *  but I haven't written the support for this yet. /JJ
215                  */
216
217                 if (chan_register >= 8){
218                         chan_register = 0x20 | (chan_register & 0x7);
219                 }else{
220                         chan_register |= 0x10;
221                 }
222         } else {
223                 outb(chan_register, dev->iobase + PCL711_MUX);
224         }
225 }
226
227 static int pcl711_ai_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
228 {
229         int hi, lo, i;
230         int nmax=40;    /* 1000 us / 25 us */
231         int n;
232
233         if(it->n<=nmax)nmax=it->n;
234
235         pcl711_set_changain(dev,it->chanlist[0]);
236         
237         /*
238            a sensible precaution to wait for the mux to
239            settle here.  is 10us enough?
240         */
241         udelay(10);
242
243 for(n=0;n<nmax;n++){
244         /*
245          *  Write the correct mode (software polling) and start polling by writing
246          *  to the trigger register
247          */
248         outb(1, dev->iobase + PCL711_MODE);
249
250         if (this_board->is_8112) {
251         }else{
252                 outb(0, dev->iobase + PCL711_SOFTTRIG);
253         }
254
255         i=PCL711_TIMEOUT;
256         while(--i){
257                 hi = inb(dev->iobase + PCL711_AD_HI);
258                 if (!(hi & PCL711_DRDY))
259                         goto ok;
260                 udelay(5);
261         }
262         rt_printk("comedi%d: pcl711: A/D timeout\n", dev->minor);
263         return -ETIME;
264         
265 ok:
266         lo = inb(dev->iobase + PCL711_AD_LO);
267
268         it->data[n] = ((hi & 0xf) << 8) | lo;
269 }
270
271         return n;
272 }
273
274 static int pcl711_ai_mode4(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
275 {
276         if (!this_board->is_pcl711b || dev->irq == 0)
277                 return -EINVAL;
278
279         pcl711_set_changain(dev,it->chanlist[0]);
280
281         /*
282          *  Set mode to "no internal trigger"
283          */
284         outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
285
286         return 0;
287 }
288
289
290 static int pcl711_ai_mode1(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
291 {
292         int timer1,timer2;
293
294         if (!dev->irq)
295                 return -EINVAL;
296
297         pcl711_set_changain(dev,it->chanlist[0]);
298
299         /*
300          *  Set timers
301          *      timer chip is an 8253, with timers 1 and 2
302          *      cascaded
303          *  0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
304          *        Mode 2 = Rate generator
305          *
306          *  0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
307          */
308
309         i8253_cascade_ns_to_timer(i8253_osc_base,&timer1,&timer2,&it->trigvar,TRIG_ROUND_NEAREST);
310
311         outb(0x74, dev->iobase + PCL711_CTRCTL);
312         outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
313         outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
314         outb(0xb4, dev->iobase + PCL711_CTRCTL);
315         outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
316         outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
317
318         /* clear pending interrupts (just in case) */
319         outb(0, dev->iobase + PCL711_CLRINTR);
320
321         /*
322          *  Set mode to IRQ transfer
323          */
324         outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
325
326         return 0;
327 }
328
329 /*
330    analog output
331 */
332 static int pcl711_ao(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
333 {
334         int chan = CR_CHAN(it->chanlist[0]);
335         sampl_t data = it->data[0];
336
337         outb((data & 0xff), dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
338         outb((data >> 8), dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
339
340         return 0;
341 }
342
343 /* Digital port read - Untested on 8112 */
344 static int pcl711_di(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
345 {
346         int data;
347         int chan;
348         int i;
349
350         data = inb(dev->iobase + PCL711_DI_LO) |
351             (inb(dev->iobase + PCL711_DI_HI) << 8);
352
353         for(i=0;i<it->n_chan;i++){
354                 chan=CR_CHAN(it->chanlist[i]);
355                 it->data[i]=(data>>chan)&1;
356         }
357
358         return it->n_chan;
359 }
360
361 /* Digital port write - Untested on 8112 */
362 static int pcl711_do(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
363 {
364         int mask, data;
365         int chan;
366         int i;
367
368         data=s->state;
369         for(i=0;i<it->n_chan;i++){
370                 chan=CR_CHAN(it->chanlist[i]);
371                 mask=(1<<chan);
372                 data &= ~mask;
373                 if(it->data[i])
374                         data |= mask;
375         }
376         outb(data & 0xff, dev->iobase + PCL711_DO_LO);
377         outb((data >> 8), dev->iobase + PCL711_DO_HI);
378         s->state = data;
379
380         return it->n_chan;
381 }
382
383 /*  Free any resources that we have claimed  */
384 static void free_resources(comedi_device * dev)
385 {
386         if (dev->irq)
387                 free_irq(dev->irq, dev);
388
389         if (dev->iobase)
390                 release_region(dev->iobase, dev->iosize);
391 }
392
393 static int pcl711_recognize(char *name)
394 {
395         int i;
396
397         for (i = 0; i < n_boardtypes; i++) {
398                 if (!strcmp(boardtypes[i].name, name)) {
399                         return i;
400                 }
401         }
402         return -1;
403 }
404
405 /*  Initialization */
406 static int pcl711_attach(comedi_device * dev, comedi_devconfig * it)
407 {
408         int ret;
409         int iobase;
410         int irq;
411         comedi_subdevice *s;
412
413         /* claim our I/O space */
414
415         iobase = it->options[0];
416         printk("comedi%d: pcl711: 0x%04x ", dev->minor, iobase);
417         if (check_region(iobase, PCL711_SIZE) < 0) {
418                 printk("I/O port conflict\n");
419                 return -EIO;
420         }
421         request_region(dev->iobase, PCL711_SIZE, "pcl711");
422         dev->iobase = iobase;
423         dev->iosize = PCL711_SIZE;
424
425         /* there should be a sanity check here */
426
427         /* set up some name stuff */
428         dev->board_name = boardtypes[dev->board].name;
429
430         /* grab our IRQ */
431         irq = it->options[1];
432         if (irq < 0 || irq > boardtypes[dev->board].maxirq) {
433                 printk("irq out of range\n");
434                 free_resources(dev);
435                 return -EINVAL;
436         }
437         if (irq) {
438                 if (request_irq(irq, pcl711_interrupt, SA_INTERRUPT, "pcl711", dev)) {
439                         printk("unable to allocate irq %d\n", irq);
440                         free_resources(dev);
441                         return -EINVAL;
442                 } else {
443                         printk("( irq = %d )\n", irq);
444                 }
445         }
446         dev->irq = irq;
447
448         dev->n_subdevices = 4;
449         if((ret=alloc_subdevices(dev))<0)
450                 return ret;
451         if((ret=alloc_private(dev,sizeof(pcl711_private)))<0)
452                 return ret;
453
454         s = dev->subdevices + 0;
455         /* AI subdevice */
456         s->type = COMEDI_SUBD_AI;
457         s->subdev_flags = SDF_READABLE;
458         s->n_chan = this_board->n_aichan;
459         s->maxdata = 0xfff;
460         s->len_chanlist = 1;
461         s->range_table = this_board->ai_range_type;
462         s->trig[0] = pcl711_ai_mode0;
463         s->trig[1] = pcl711_ai_mode1;
464         s->trig[4] = pcl711_ai_mode4;
465
466         s++;
467         /* AO subdevice */
468         s->type = COMEDI_SUBD_AO;
469         s->subdev_flags = SDF_WRITEABLE;
470         s->n_chan = this_board->n_aochan;
471         s->maxdata = 0xfff;
472         s->len_chanlist = 1;
473         s->range_table = &range_bipolar5;
474         s->trig[0] = pcl711_ao;
475
476         s++;
477         /* 16-bit digital input */
478         s->type = COMEDI_SUBD_DI;
479         s->subdev_flags = SDF_READABLE;
480         s->n_chan = 16;
481         s->maxdata = 1;
482         s->len_chanlist = 16;
483         s->range_table = &range_digital;
484         s->trig[0] = pcl711_di;
485
486         s++;
487         /* 16-bit digital out */
488         s->type = COMEDI_SUBD_DO;
489         s->subdev_flags = SDF_WRITEABLE;
490         s->n_chan = 16;
491         s->maxdata = 1;
492         s->len_chanlist = 16;
493         s->range_table = &range_digital;
494         s->state=0;
495         s->trig[0] = pcl711_do;
496
497         /*
498            this is the "base value" for the mode register, which is
499            used for the irq on the PCL711
500          */
501         if(this_board->is_pcl711b){
502                 devpriv->mode=(dev->irq<<4);
503         }
504
505         /* clear DAC */
506         outb(0, dev->iobase + PCL711_DA0_LO);
507         outb(0, dev->iobase + PCL711_DA0_HI);
508         outb(0, dev->iobase + PCL711_DA1_LO);
509         outb(0, dev->iobase + PCL711_DA1_HI);
510
511         printk("\n");
512
513         return 0;
514 }
515
516
517 /*
518  *  Removes device
519  */
520
521 static int pcl711_detach(comedi_device * dev)
522 {
523         printk("comedi%d: pcl711: remove\n", dev->minor);
524
525         free_resources(dev);
526
527         return 0;
528 }
529
530 #ifdef MODULE
531 int init_module(void)
532 {
533         comedi_driver_register(&driver_pcl711);
534         
535         return 0;
536 }
537
538 void cleanup_module(void)
539 {
540         comedi_driver_unregister(&driver_pcl711);
541 }
542 #endif