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