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