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