Fix symbol leakage in a bunch of drivers
[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 } pcl711_private;
175
176 #define devpriv ((pcl711_private *)dev->private)
177
178 static void pcl711_interrupt(int irq, void *d, struct pt_regs *regs)
179 {
180         int lo, hi;
181         int data;
182         comedi_device *dev = d;
183         comedi_subdevice *s = dev->subdevices + 0;
184
185         hi = inb(dev->iobase + PCL711_AD_HI);
186         lo = inb(dev->iobase + PCL711_AD_LO);
187         outb(0, dev->iobase + PCL711_CLRINTR);
188
189         data = (hi << 8) | lo;
190
191         if (!(--devpriv->ntrig)) {
192                 if (this_board->is_8112) {
193                         outb(1, dev->iobase + PCL711_MODE);
194                 } else {
195                         outb(0, dev->iobase + PCL711_MODE);
196                 }
197
198                 comedi_done(dev,s);
199         }
200 }
201
202 static void pcl711_set_changain(comedi_device * dev, int chan)
203 {
204         int chan_register;
205
206         outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
207         
208         chan_register=CR_CHAN(chan);
209
210         if (this_board->is_8112) {
211
212                 /*
213                  *  Set the correct channel.  The two channel banks are switched
214                  *  using the mask value.
215                  *  NB: To use differential channels, you should use mask = 0x30,
216                  *  but I haven't written the support for this yet. /JJ
217                  */
218
219                 if (chan_register >= 8){
220                         chan_register = 0x20 | (chan_register & 0x7);
221                 }else{
222                         chan_register |= 0x10;
223                 }
224         } else {
225                 outb(chan_register, dev->iobase + PCL711_MUX);
226         }
227 }
228
229 static int pcl711_ai_insn(comedi_device *dev,comedi_subdevice *s,
230         comedi_insn *insn,lsampl_t *data)
231 {
232         int i,n;
233         int hi,lo;
234
235         pcl711_set_changain(dev,insn->chanspec);
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<insn->n;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                 }
261                 rt_printk("comedi%d: pcl711: A/D timeout\n", dev->minor);
262                 return -ETIME;
263         
264 ok:
265                 lo = inb(dev->iobase + PCL711_AD_LO);
266
267                 data[n] = ((hi & 0xf) << 8) | lo;
268         }
269
270         return n;
271 }
272
273 #ifdef CONFIG_COMEDI_TRIG
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 #endif
289
290
291 #ifdef CONFIG_COMEDI_TRIG
292 static int pcl711_ai_mode1(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
293 {
294         int timer1,timer2;
295
296         if (!dev->irq)
297                 return -EINVAL;
298
299         pcl711_set_changain(dev,it->chanlist[0]);
300
301         /*
302          *  Set timers
303          *      timer chip is an 8253, with timers 1 and 2
304          *      cascaded
305          *  0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
306          *        Mode 2 = Rate generator
307          *
308          *  0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
309          */
310
311         i8253_cascade_ns_to_timer(i8253_osc_base,&timer1,&timer2,&it->trigvar,TRIG_ROUND_NEAREST);
312
313         outb(0x74, dev->iobase + PCL711_CTRCTL);
314         outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
315         outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
316         outb(0xb4, dev->iobase + PCL711_CTRCTL);
317         outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
318         outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
319
320         /* clear pending interrupts (just in case) */
321         outb(0, dev->iobase + PCL711_CLRINTR);
322
323         /*
324          *  Set mode to IRQ transfer
325          */
326         outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
327
328         return 0;
329 }
330 #endif
331
332 /*
333    analog output
334 */
335 static int pcl711_ao_insn(comedi_device *dev,comedi_subdevice *s,
336         comedi_insn *insn,lsampl_t *data)
337 {
338         int n;
339         int chan = CR_CHAN(insn->chanspec);
340
341         for(n=0;n<insn->n;n++){
342                 outb((data[n] & 0xff), dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
343                 outb((data[n] >> 8), dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
344
345                 devpriv->ao_readback[chan] = data[n];
346         }
347
348         return n;
349 }
350
351 static int pcl711_ao_insn_read(comedi_device *dev,comedi_subdevice *s,
352         comedi_insn *insn,lsampl_t *data)
353 {
354         int n;
355         int chan = CR_CHAN(insn->chanspec);
356
357         for(n=0;n<insn->n;n++){
358                 data[n] = devpriv->ao_readback[chan];
359         }
360
361         return n;
362
363 }
364
365 /* Digital port read - Untested on 8112 */
366 static int pcl711_di_insn_bits(comedi_device * dev, comedi_subdevice * s,
367         comedi_insn *insn,lsampl_t *data)
368 {
369         if(insn->n!=2)return -EINVAL;
370
371         data[1] = inb(dev->iobase + PCL711_DI_LO) |
372             (inb(dev->iobase + PCL711_DI_HI) << 8);
373
374         return 2;
375 }
376
377 /* Digital port write - Untested on 8112 */
378 static int pcl711_do_insn_bits(comedi_device * dev, comedi_subdevice * s,
379         comedi_insn *insn,lsampl_t *data)
380 {
381         if(insn->n!=2)return -EINVAL;
382
383         if(data[0]){
384                 s->state &= ~data[0];
385                 s->state |= data[0]&data[1];
386         }
387         if(data[0]&0x00ff)
388                 outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
389         if(data[0]&0xff00)
390                 outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
391
392         data[1]=s->state;
393
394         return 2;
395 }
396
397 /*  Free any resources that we have claimed  */
398 static int pcl711_detach(comedi_device * dev)
399 {
400         printk("comedi%d: pcl711: remove\n", dev->minor);
401
402         if (dev->irq)
403                 free_irq(dev->irq, dev);
404
405         if (dev->iobase)
406                 release_region(dev->iobase, PCL711_SIZE);
407
408         return 0;
409 }
410
411 /*  Initialization */
412 static int pcl711_attach(comedi_device * dev, comedi_devconfig * it)
413 {
414         int ret;
415         int iobase;
416         int irq;
417         comedi_subdevice *s;
418
419         /* claim our I/O space */
420
421         iobase = it->options[0];
422         printk("comedi%d: pcl711: 0x%04x ", dev->minor, iobase);
423         if (check_region(iobase, PCL711_SIZE) < 0) {
424                 printk("I/O port conflict\n");
425                 return -EIO;
426         }
427         request_region(iobase, PCL711_SIZE, "pcl711");
428         dev->iobase = iobase;
429
430         /* there should be a sanity check here */
431
432         /* set up some name stuff */
433         dev->board_name = this_board->name;
434
435         /* grab our IRQ */
436         irq = it->options[1];
437         if (irq < 0 || irq > this_board->maxirq) {
438                 printk("irq out of range\n");
439                 return -EINVAL;
440         }
441         if (irq) {
442                 if (comedi_request_irq(irq, pcl711_interrupt, 0, "pcl711", dev)) {
443                         printk("unable to allocate irq %d\n", irq);
444                         return -EINVAL;
445                 } else {
446                         printk("( irq = %d )\n", irq);
447                 }
448         }
449         dev->irq = irq;
450
451         dev->n_subdevices = 4;
452         if((ret=alloc_subdevices(dev))<0)
453                 return ret;
454         if((ret=alloc_private(dev,sizeof(pcl711_private)))<0)
455                 return ret;
456
457         s = dev->subdevices + 0;
458         /* AI subdevice */
459         s->type = COMEDI_SUBD_AI;
460         s->subdev_flags = SDF_READABLE;
461         s->n_chan = this_board->n_aichan;
462         s->maxdata = 0xfff;
463         s->len_chanlist = 1;
464         s->range_table = this_board->ai_range_type;
465         s->insn_read = pcl711_ai_insn;
466 #ifdef CONFIG_COMEDI_TRIG
467         s->trig[1] = pcl711_ai_mode1;
468         s->trig[4] = pcl711_ai_mode4;
469 #endif
470
471         s++;
472         /* AO subdevice */
473         s->type = COMEDI_SUBD_AO;
474         s->subdev_flags = SDF_WRITEABLE;
475         s->n_chan = this_board->n_aochan;
476         s->maxdata = 0xfff;
477         s->len_chanlist = 1;
478         s->range_table = &range_bipolar5;
479         s->insn_write = pcl711_ao_insn;
480         s->insn_read = pcl711_ao_insn_read;
481
482         s++;
483         /* 16-bit digital input */
484         s->type = COMEDI_SUBD_DI;
485         s->subdev_flags = SDF_READABLE;
486         s->n_chan = 16;
487         s->maxdata = 1;
488         s->len_chanlist = 16;
489         s->range_table = &range_digital;
490         s->insn_bits = pcl711_di_insn_bits;
491
492         s++;
493         /* 16-bit digital out */
494         s->type = COMEDI_SUBD_DO;
495         s->subdev_flags = SDF_WRITEABLE;
496         s->n_chan = 16;
497         s->maxdata = 1;
498         s->len_chanlist = 16;
499         s->range_table = &range_digital;
500         s->state=0;
501         s->insn_bits = pcl711_do_insn_bits;
502
503         /*
504            this is the "base value" for the mode register, which is
505            used for the irq on the PCL711
506          */
507         if(this_board->is_pcl711b){
508                 devpriv->mode=(dev->irq<<4);
509         }
510
511         /* clear DAC */
512         outb(0, dev->iobase + PCL711_DA0_LO);
513         outb(0, dev->iobase + PCL711_DA0_HI);
514         outb(0, dev->iobase + PCL711_DA1_LO);
515         outb(0, dev->iobase + PCL711_DA1_HI);
516
517         printk("\n");
518
519         return 0;
520 }
521