Merge git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[linux-2.6] / drivers / staging / comedi / drivers / pcl711.c
1 /*
2    comedi/drivers/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
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 "../comedidev.h"
62
63 #include <linux/ioport.h>
64 #include <linux/delay.h>
65
66 #include "8253.h"
67
68 #define PCL711_SIZE 16
69
70 #define PCL711_CTR0 0
71 #define PCL711_CTR1 1
72 #define PCL711_CTR2 2
73 #define PCL711_CTRCTL 3
74 #define PCL711_AD_LO 4
75 #define PCL711_DA0_LO 4
76 #define PCL711_AD_HI 5
77 #define PCL711_DA0_HI 5
78 #define PCL711_DI_LO 6
79 #define PCL711_DA1_LO 6
80 #define PCL711_DI_HI 7
81 #define PCL711_DA1_HI 7
82 #define PCL711_CLRINTR 8
83 #define PCL711_GAIN 9
84 #define PCL711_MUX 10
85 #define PCL711_MODE 11
86 #define PCL711_SOFTTRIG 12
87 #define PCL711_DO_LO 13
88 #define PCL711_DO_HI 14
89
90 static const struct comedi_lrange range_pcl711b_ai = { 5, {
91                         BIP_RANGE(5),
92                         BIP_RANGE(2.5),
93                         BIP_RANGE(1.25),
94                         BIP_RANGE(0.625),
95                         BIP_RANGE(0.3125)
96         }
97 };
98 static const struct comedi_lrange range_acl8112hg_ai = { 12, {
99                         BIP_RANGE(5),
100                         BIP_RANGE(0.5),
101                         BIP_RANGE(0.05),
102                         BIP_RANGE(0.005),
103                         UNI_RANGE(10),
104                         UNI_RANGE(1),
105                         UNI_RANGE(0.1),
106                         UNI_RANGE(0.01),
107                         BIP_RANGE(10),
108                         BIP_RANGE(1),
109                         BIP_RANGE(0.1),
110                         BIP_RANGE(0.01)
111         }
112 };
113 static const struct 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
126 /*
127  * flags
128  */
129
130 #define PCL711_TIMEOUT 100
131 #define PCL711_DRDY 0x10
132
133 static const int i8253_osc_base = 500;  /* 2 Mhz */
134
135 struct pcl711_board {
136
137         const 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         const struct comedi_lrange *ai_range_type;
146 };
147
148
149 static const struct pcl711_board boardtypes[] = {
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
156 #define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl711_board))
157 #define this_board ((const struct pcl711_board *)dev->board_ptr)
158
159 static int pcl711_attach(struct comedi_device * dev, struct comedi_devconfig * it);
160 static int pcl711_detach(struct comedi_device * dev);
161 static struct comedi_driver driver_pcl711 = {
162       driver_name:"pcl711",
163       module:THIS_MODULE,
164       attach:pcl711_attach,
165       detach:pcl711_detach,
166       board_name:&boardtypes[0].name,
167       num_names:n_boardtypes,
168       offset:sizeof(struct pcl711_board),
169 };
170
171 COMEDI_INITCLEANUP(driver_pcl711);
172
173 struct pcl711_private {
174
175         int board;
176         int adchan;
177         int ntrig;
178         int aip[8];
179         int mode;
180         unsigned int ao_readback[2];
181         unsigned int divisor1;
182         unsigned int divisor2;
183 };
184
185
186 #define devpriv ((struct pcl711_private *)dev->private)
187
188 static irqreturn_t pcl711_interrupt(int irq, void *d PT_REGS_ARG)
189 {
190         int lo, hi;
191         int data;
192         struct comedi_device *dev = d;
193         struct comedi_subdevice *s = dev->subdevices + 0;
194
195         if (!dev->attached) {
196                 comedi_error(dev, "spurious interrupt");
197                 return IRQ_HANDLED;
198         }
199
200         hi = inb(dev->iobase + PCL711_AD_HI);
201         lo = inb(dev->iobase + PCL711_AD_LO);
202         outb(0, dev->iobase + PCL711_CLRINTR);
203
204         data = (hi << 8) | lo;
205
206         /* FIXME! Nothing else sets ntrig! */
207         if (!(--devpriv->ntrig)) {
208                 if (this_board->is_8112) {
209                         outb(1, dev->iobase + PCL711_MODE);
210                 } else {
211                         outb(0, dev->iobase + PCL711_MODE);
212                 }
213
214                 s->async->events |= COMEDI_CB_EOA;
215         }
216         comedi_event(dev, s);
217         return IRQ_HANDLED;
218 }
219
220 static void pcl711_set_changain(struct comedi_device * dev, int chan)
221 {
222         int chan_register;
223
224         outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
225
226         chan_register = CR_CHAN(chan);
227
228         if (this_board->is_8112) {
229
230                 /*
231                  *  Set the correct channel.  The two channel banks are switched
232                  *  using the mask value.
233                  *  NB: To use differential channels, you should use mask = 0x30,
234                  *  but I haven't written the support for this yet. /JJ
235                  */
236
237                 if (chan_register >= 8) {
238                         chan_register = 0x20 | (chan_register & 0x7);
239                 } else {
240                         chan_register |= 0x10;
241                 }
242         } else {
243                 outb(chan_register, dev->iobase + PCL711_MUX);
244         }
245 }
246
247 static int pcl711_ai_insn(struct comedi_device * dev, struct comedi_subdevice * s,
248         struct comedi_insn * insn, unsigned int * data)
249 {
250         int i, n;
251         int hi, lo;
252
253         pcl711_set_changain(dev, insn->chanspec);
254
255         for (n = 0; n < insn->n; n++) {
256                 /*
257                  *  Write the correct mode (software polling) and start polling by writing
258                  *  to the trigger register
259                  */
260                 outb(1, dev->iobase + PCL711_MODE);
261
262                 if (this_board->is_8112) {
263                 } else {
264                         outb(0, dev->iobase + PCL711_SOFTTRIG);
265                 }
266
267                 i = PCL711_TIMEOUT;
268                 while (--i) {
269                         hi = inb(dev->iobase + PCL711_AD_HI);
270                         if (!(hi & PCL711_DRDY))
271                                 goto ok;
272                         comedi_udelay(1);
273                 }
274                 rt_printk("comedi%d: pcl711: A/D timeout\n", dev->minor);
275                 return -ETIME;
276
277               ok:
278                 lo = inb(dev->iobase + PCL711_AD_LO);
279
280                 data[n] = ((hi & 0xf) << 8) | lo;
281         }
282
283         return n;
284 }
285
286 static int pcl711_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
287         struct comedi_cmd * cmd)
288 {
289         int tmp;
290         int err = 0;
291
292         /* step 1 */
293         tmp = cmd->start_src;
294         cmd->start_src &= TRIG_NOW;
295         if (!cmd->start_src || tmp != cmd->start_src)
296                 err++;
297
298         tmp = cmd->scan_begin_src;
299         cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
300         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
301                 err++;
302
303         tmp = cmd->convert_src;
304         cmd->convert_src &= TRIG_NOW;
305         if (!cmd->convert_src || tmp != cmd->convert_src)
306                 err++;
307
308         tmp = cmd->scan_end_src;
309         cmd->scan_end_src &= TRIG_COUNT;
310         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
311                 err++;
312
313         tmp = cmd->stop_src;
314         cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
315         if (!cmd->stop_src || tmp != cmd->stop_src)
316                 err++;
317
318         if (err)
319                 return 1;
320
321         /* step 2 */
322
323         if (cmd->scan_begin_src != TRIG_TIMER &&
324                 cmd->scan_begin_src != TRIG_EXT)
325                 err++;
326         if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
327                 err++;
328
329         if (err)
330                 return 2;
331
332         /* step 3 */
333
334         if (cmd->start_arg != 0) {
335                 cmd->start_arg = 0;
336                 err++;
337         }
338         if (cmd->scan_begin_src == TRIG_EXT) {
339                 if (cmd->scan_begin_arg != 0) {
340                         cmd->scan_begin_arg = 0;
341                         err++;
342                 }
343         } else {
344 #define MAX_SPEED 1000
345 #define TIMER_BASE 100
346                 if (cmd->scan_begin_arg < MAX_SPEED) {
347                         cmd->scan_begin_arg = MAX_SPEED;
348                         err++;
349                 }
350         }
351         if (cmd->convert_arg != 0) {
352                 cmd->convert_arg = 0;
353                 err++;
354         }
355         if (cmd->scan_end_arg != cmd->chanlist_len) {
356                 cmd->scan_end_arg = cmd->chanlist_len;
357                 err++;
358         }
359         if (cmd->stop_src == TRIG_NONE) {
360                 if (cmd->stop_arg != 0) {
361                         cmd->stop_arg = 0;
362                         err++;
363                 }
364         } else {
365                 /* ignore */
366         }
367
368         if (err)
369                 return 3;
370
371         /* step 4 */
372
373         if (cmd->scan_begin_src == TRIG_TIMER) {
374                 tmp = cmd->scan_begin_arg;
375                 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
376                         &devpriv->divisor1, &devpriv->divisor2,
377                         &cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
378                 if (tmp != cmd->scan_begin_arg)
379                         err++;
380         }
381
382         if (err)
383                 return 4;
384
385         return 0;
386 }
387
388 static int pcl711_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
389 {
390         int timer1, timer2;
391         struct comedi_cmd *cmd = &s->async->cmd;
392
393         pcl711_set_changain(dev, cmd->chanlist[0]);
394
395         if (cmd->scan_begin_src == TRIG_TIMER) {
396                 /*
397                  *  Set timers
398                  *      timer chip is an 8253, with timers 1 and 2
399                  *      cascaded
400                  *  0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
401                  *        Mode 2 = Rate generator
402                  *
403                  *  0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
404                  */
405
406                 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
407                         &cmd->scan_begin_arg, TRIG_ROUND_NEAREST);
408
409                 outb(0x74, dev->iobase + PCL711_CTRCTL);
410                 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
411                 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
412                 outb(0xb4, dev->iobase + PCL711_CTRCTL);
413                 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
414                 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
415
416                 /* clear pending interrupts (just in case) */
417                 outb(0, dev->iobase + PCL711_CLRINTR);
418
419                 /*
420                  *  Set mode to IRQ transfer
421                  */
422                 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
423         } else {
424                 /* external trigger */
425                 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
426         }
427
428         return 0;
429 }
430
431 /*
432    analog output
433 */
434 static int pcl711_ao_insn(struct comedi_device * dev, struct comedi_subdevice * s,
435         struct comedi_insn * insn, unsigned int * data)
436 {
437         int n;
438         int chan = CR_CHAN(insn->chanspec);
439
440         for (n = 0; n < insn->n; n++) {
441                 outb((data[n] & 0xff),
442                         dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
443                 outb((data[n] >> 8),
444                         dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
445
446                 devpriv->ao_readback[chan] = data[n];
447         }
448
449         return n;
450 }
451
452 static int pcl711_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
453         struct comedi_insn * insn, unsigned int * data)
454 {
455         int n;
456         int chan = CR_CHAN(insn->chanspec);
457
458         for (n = 0; n < insn->n; n++) {
459                 data[n] = devpriv->ao_readback[chan];
460         }
461
462         return n;
463
464 }
465
466 /* Digital port read - Untested on 8112 */
467 static int pcl711_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
468         struct comedi_insn * insn, unsigned int * data)
469 {
470         if (insn->n != 2)
471                 return -EINVAL;
472
473         data[1] = inb(dev->iobase + PCL711_DI_LO) |
474                 (inb(dev->iobase + PCL711_DI_HI) << 8);
475
476         return 2;
477 }
478
479 /* Digital port write - Untested on 8112 */
480 static int pcl711_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
481         struct comedi_insn * insn, unsigned int * data)
482 {
483         if (insn->n != 2)
484                 return -EINVAL;
485
486         if (data[0]) {
487                 s->state &= ~data[0];
488                 s->state |= data[0] & data[1];
489         }
490         if (data[0] & 0x00ff)
491                 outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
492         if (data[0] & 0xff00)
493                 outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
494
495         data[1] = s->state;
496
497         return 2;
498 }
499
500 /*  Free any resources that we have claimed  */
501 static int pcl711_detach(struct comedi_device * dev)
502 {
503         printk("comedi%d: pcl711: remove\n", dev->minor);
504
505         if (dev->irq)
506                 comedi_free_irq(dev->irq, dev);
507
508         if (dev->iobase)
509                 release_region(dev->iobase, PCL711_SIZE);
510
511         return 0;
512 }
513
514 /*  Initialization */
515 static int pcl711_attach(struct comedi_device * dev, struct comedi_devconfig * it)
516 {
517         int ret;
518         unsigned long iobase;
519         unsigned int irq;
520         struct comedi_subdevice *s;
521
522         /* claim our I/O space */
523
524         iobase = it->options[0];
525         printk("comedi%d: pcl711: 0x%04lx ", dev->minor, iobase);
526         if (!request_region(iobase, PCL711_SIZE, "pcl711")) {
527                 printk("I/O port conflict\n");
528                 return -EIO;
529         }
530         dev->iobase = iobase;
531
532         /* there should be a sanity check here */
533
534         /* set up some name stuff */
535         dev->board_name = this_board->name;
536
537         /* grab our IRQ */
538         irq = it->options[1];
539         if (irq > this_board->maxirq) {
540                 printk("irq out of range\n");
541                 return -EINVAL;
542         }
543         if (irq) {
544                 if (comedi_request_irq(irq, pcl711_interrupt, 0, "pcl711", dev)) {
545                         printk("unable to allocate irq %u\n", irq);
546                         return -EINVAL;
547                 } else {
548                         printk("( irq = %u )\n", irq);
549                 }
550         }
551         dev->irq = irq;
552
553         if ((ret = alloc_subdevices(dev, 4)) < 0)
554                 return ret;
555         if ((ret = alloc_private(dev, sizeof(struct pcl711_private))) < 0)
556                 return ret;
557
558         s = dev->subdevices + 0;
559         /* AI subdevice */
560         s->type = COMEDI_SUBD_AI;
561         s->subdev_flags = SDF_READABLE | SDF_GROUND;
562         s->n_chan = this_board->n_aichan;
563         s->maxdata = 0xfff;
564         s->len_chanlist = 1;
565         s->range_table = this_board->ai_range_type;
566         s->insn_read = pcl711_ai_insn;
567         if (irq) {
568                 dev->read_subdev = s;
569                 s->subdev_flags |= SDF_CMD_READ;
570                 s->do_cmdtest = pcl711_ai_cmdtest;
571                 s->do_cmd = pcl711_ai_cmd;
572         }
573
574         s++;
575         /* AO subdevice */
576         s->type = COMEDI_SUBD_AO;
577         s->subdev_flags = SDF_WRITABLE;
578         s->n_chan = this_board->n_aochan;
579         s->maxdata = 0xfff;
580         s->len_chanlist = 1;
581         s->range_table = &range_bipolar5;
582         s->insn_write = pcl711_ao_insn;
583         s->insn_read = pcl711_ao_insn_read;
584
585         s++;
586         /* 16-bit digital input */
587         s->type = COMEDI_SUBD_DI;
588         s->subdev_flags = SDF_READABLE;
589         s->n_chan = 16;
590         s->maxdata = 1;
591         s->len_chanlist = 16;
592         s->range_table = &range_digital;
593         s->insn_bits = pcl711_di_insn_bits;
594
595         s++;
596         /* 16-bit digital out */
597         s->type = COMEDI_SUBD_DO;
598         s->subdev_flags = SDF_WRITABLE;
599         s->n_chan = 16;
600         s->maxdata = 1;
601         s->len_chanlist = 16;
602         s->range_table = &range_digital;
603         s->state = 0;
604         s->insn_bits = pcl711_do_insn_bits;
605
606         /*
607            this is the "base value" for the mode register, which is
608            used for the irq on the PCL711
609          */
610         if (this_board->is_pcl711b) {
611                 devpriv->mode = (dev->irq << 4);
612         }
613
614         /* clear DAC */
615         outb(0, dev->iobase + PCL711_DA0_LO);
616         outb(0, dev->iobase + PCL711_DA0_HI);
617         outb(0, dev->iobase + PCL711_DA1_LO);
618         outb(0, dev->iobase + PCL711_DA1_HI);
619
620         printk("\n");
621
622         return 0;
623 }