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