7abf3f74144e78e530474683cfc185bc60b9139b
[firefly-linux-kernel-4.4.55.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 /*
22 Driver: pcl711
23 Description: Advantech PCL-711 and 711b, ADLink ACL-8112
24 Author: ds, Janne Jalkanen <jalkanen@cs.hut.fi>, Eric Bunn <ebu@cs.hut.fi>
25 Status: mostly complete
26 Devices: [Advantech] PCL-711 (pcl711), PCL-711B (pcl711b),
27   [AdLink] ACL-8112HG (acl8112hg), ACL-8112DG (acl8112dg)
28
29 Since these boards do not have DMA or FIFOs, only immediate mode is
30 supported.
31
32 */
33
34 /*
35    Dave Andruczyk <dave@tech.buffalostate.edu> also wrote a
36    driver for the PCL-711.  I used a few ideas from his driver
37    here.  His driver also has more comments, if you are
38    interested in understanding how this driver works.
39    http://tech.buffalostate.edu/~dave/driver/
40
41    The ACL-8112 driver was hacked from the sources of the PCL-711
42    driver (the 744 chip used on the 8112 is almost the same as
43    the 711b chip, but it has more I/O channels) by
44    Janne Jalkanen (jalkanen@cs.hut.fi) and
45    Erik Bunn (ebu@cs.hut.fi).  Remerged with the PCL-711 driver
46    by ds.
47
48    [acl-8112]
49    This driver supports both TRIGNOW and TRIGCLK,
50    but does not yet support DMA transfers.  It also supports
51    both high (HG) and low (DG) versions of the card, though
52    the HG version has been untested.
53
54  */
55
56 #include <linux/interrupt.h>
57 #include "../comedidev.h"
58
59 #include <linux/ioport.h>
60 #include <linux/delay.h>
61
62 #include "comedi_fc.h"
63 #include "8253.h"
64
65 #define PCL711_SIZE 16
66
67 #define PCL711_CTR0 0
68 #define PCL711_CTR1 1
69 #define PCL711_CTR2 2
70 #define PCL711_CTRCTL 3
71 #define PCL711_AD_LO 4
72 #define PCL711_DA0_LO 4
73 #define PCL711_AD_HI 5
74 #define PCL711_DA0_HI 5
75 #define PCL711_DI_LO 6
76 #define PCL711_DA1_LO 6
77 #define PCL711_DI_HI 7
78 #define PCL711_DA1_HI 7
79 #define PCL711_CLRINTR 8
80 #define PCL711_GAIN 9
81 #define PCL711_MUX 10
82 #define PCL711_MODE 11
83 #define PCL711_SOFTTRIG 12
84 #define PCL711_DO_LO 13
85 #define PCL711_DO_HI 14
86
87 static const struct comedi_lrange range_pcl711b_ai = { 5, {
88                                                            BIP_RANGE(5),
89                                                            BIP_RANGE(2.5),
90                                                            BIP_RANGE(1.25),
91                                                            BIP_RANGE(0.625),
92                                                            BIP_RANGE(0.3125)
93                                                            }
94 };
95
96 static const struct comedi_lrange range_acl8112hg_ai = { 12, {
97                                                               BIP_RANGE(5),
98                                                               BIP_RANGE(0.5),
99                                                               BIP_RANGE(0.05),
100                                                               BIP_RANGE(0.005),
101                                                               UNI_RANGE(10),
102                                                               UNI_RANGE(1),
103                                                               UNI_RANGE(0.1),
104                                                               UNI_RANGE(0.01),
105                                                               BIP_RANGE(10),
106                                                               BIP_RANGE(1),
107                                                               BIP_RANGE(0.1),
108                                                               BIP_RANGE(0.01)
109                                                               }
110 };
111
112 static const struct comedi_lrange range_acl8112dg_ai = { 9, {
113                                                              BIP_RANGE(5),
114                                                              BIP_RANGE(2.5),
115                                                              BIP_RANGE(1.25),
116                                                              BIP_RANGE(0.625),
117                                                              UNI_RANGE(10),
118                                                              UNI_RANGE(5),
119                                                              UNI_RANGE(2.5),
120                                                              UNI_RANGE(1.25),
121                                                              BIP_RANGE(10)
122                                                              }
123 };
124
125 /*
126  * flags
127  */
128
129 #define PCL711_TIMEOUT 100
130 #define PCL711_DRDY 0x10
131
132 static const int i8253_osc_base = 500;  /* 2 Mhz */
133
134 struct pcl711_board {
135
136         const char *name;
137         int is_pcl711b;
138         int is_8112;
139         int is_dg;
140         int n_ranges;
141         int n_aichan;
142         int n_aochan;
143         int maxirq;
144         const struct comedi_lrange *ai_range_type;
145 };
146
147 struct pcl711_private {
148
149         int board;
150         int adchan;
151         int ntrig;
152         int aip[8];
153         int mode;
154         unsigned int ao_readback[2];
155         unsigned int divisor1;
156         unsigned int divisor2;
157 };
158
159 static irqreturn_t pcl711_interrupt(int irq, void *d)
160 {
161         int lo, hi;
162         int data;
163         struct comedi_device *dev = d;
164         const struct pcl711_board *board = comedi_board(dev);
165         struct pcl711_private *devpriv = dev->private;
166         struct comedi_subdevice *s = &dev->subdevices[0];
167
168         if (!dev->attached) {
169                 comedi_error(dev, "spurious interrupt");
170                 return IRQ_HANDLED;
171         }
172
173         hi = inb(dev->iobase + PCL711_AD_HI);
174         lo = inb(dev->iobase + PCL711_AD_LO);
175         outb(0, dev->iobase + PCL711_CLRINTR);
176
177         data = (hi << 8) | lo;
178
179         /* FIXME! Nothing else sets ntrig! */
180         if (!(--devpriv->ntrig)) {
181                 if (board->is_8112)
182                         outb(1, dev->iobase + PCL711_MODE);
183                 else
184                         outb(0, dev->iobase + PCL711_MODE);
185
186                 s->async->events |= COMEDI_CB_EOA;
187         }
188         comedi_event(dev, s);
189         return IRQ_HANDLED;
190 }
191
192 static void pcl711_set_changain(struct comedi_device *dev, int chan)
193 {
194         const struct pcl711_board *board = comedi_board(dev);
195         int chan_register;
196
197         outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
198
199         chan_register = CR_CHAN(chan);
200
201         if (board->is_8112) {
202
203                 /*
204                  *  Set the correct channel.  The two channel banks are switched
205                  *  using the mask value.
206                  *  NB: To use differential channels, you should use
207                  *  mask = 0x30, but I haven't written the support for this
208                  *  yet. /JJ
209                  */
210
211                 if (chan_register >= 8)
212                         chan_register = 0x20 | (chan_register & 0x7);
213                 else
214                         chan_register |= 0x10;
215         } else {
216                 outb(chan_register, dev->iobase + PCL711_MUX);
217         }
218 }
219
220 static int pcl711_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
221                           struct comedi_insn *insn, unsigned int *data)
222 {
223         const struct pcl711_board *board = comedi_board(dev);
224         int i, n;
225         int hi, lo;
226
227         pcl711_set_changain(dev, insn->chanspec);
228
229         for (n = 0; n < insn->n; n++) {
230                 /*
231                  *  Write the correct mode (software polling) and start polling
232                  *  by writing to the trigger register
233                  */
234                 outb(1, dev->iobase + PCL711_MODE);
235
236                 if (!board->is_8112)
237                         outb(0, dev->iobase + PCL711_SOFTTRIG);
238
239                 i = PCL711_TIMEOUT;
240                 while (--i) {
241                         hi = inb(dev->iobase + PCL711_AD_HI);
242                         if (!(hi & PCL711_DRDY))
243                                 goto ok;
244                         udelay(1);
245                 }
246                 printk(KERN_ERR "comedi%d: pcl711: A/D timeout\n", dev->minor);
247                 return -ETIME;
248
249 ok:
250                 lo = inb(dev->iobase + PCL711_AD_LO);
251
252                 data[n] = ((hi & 0xf) << 8) | lo;
253         }
254
255         return n;
256 }
257
258 static int pcl711_ai_cmdtest(struct comedi_device *dev,
259                              struct comedi_subdevice *s, struct comedi_cmd *cmd)
260 {
261         struct pcl711_private *devpriv = dev->private;
262         int tmp;
263         int err = 0;
264
265         /* Step 1 : check if triggers are trivially valid */
266
267         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
268         err |= cfc_check_trigger_src(&cmd->scan_begin_src,
269                                         TRIG_TIMER | TRIG_EXT);
270         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
271         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
272         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
273
274         if (err)
275                 return 1;
276
277         /* Step 2a : make sure trigger sources are unique */
278
279         err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
280         err |= cfc_check_trigger_is_unique(cmd->stop_src);
281
282         /* Step 2b : and mutually compatible */
283
284         if (err)
285                 return 2;
286
287         /* Step 3: check if arguments are trivially valid */
288
289         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
290
291         if (cmd->scan_begin_src == TRIG_EXT) {
292                 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
293         } else {
294 #define MAX_SPEED 1000
295 #define TIMER_BASE 100
296                 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
297                                                  MAX_SPEED);
298         }
299
300         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
301         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
302
303         if (cmd->stop_src == TRIG_NONE) {
304                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
305         } else {
306                 /* ignore */
307         }
308
309         if (err)
310                 return 3;
311
312         /* step 4 */
313
314         if (cmd->scan_begin_src == TRIG_TIMER) {
315                 tmp = cmd->scan_begin_arg;
316                 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
317                                                &devpriv->divisor1,
318                                                &devpriv->divisor2,
319                                                &cmd->scan_begin_arg,
320                                                cmd->flags & TRIG_ROUND_MASK);
321                 if (tmp != cmd->scan_begin_arg)
322                         err++;
323         }
324
325         if (err)
326                 return 4;
327
328         return 0;
329 }
330
331 static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
332 {
333         struct pcl711_private *devpriv = dev->private;
334         int timer1, timer2;
335         struct comedi_cmd *cmd = &s->async->cmd;
336
337         pcl711_set_changain(dev, cmd->chanlist[0]);
338
339         if (cmd->scan_begin_src == TRIG_TIMER) {
340                 /*
341                  *  Set timers
342                  *      timer chip is an 8253, with timers 1 and 2
343                  *      cascaded
344                  *  0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
345                  *        Mode 2 = Rate generator
346                  *
347                  *  0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
348                  */
349
350                 timer1 = timer2 = 0;
351                 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
352                                           &cmd->scan_begin_arg,
353                                           TRIG_ROUND_NEAREST);
354
355                 outb(0x74, dev->iobase + PCL711_CTRCTL);
356                 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
357                 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
358                 outb(0xb4, dev->iobase + PCL711_CTRCTL);
359                 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
360                 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
361
362                 /* clear pending interrupts (just in case) */
363                 outb(0, dev->iobase + PCL711_CLRINTR);
364
365                 /*
366                  *  Set mode to IRQ transfer
367                  */
368                 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
369         } else {
370                 /* external trigger */
371                 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
372         }
373
374         return 0;
375 }
376
377 /*
378    analog output
379 */
380 static int pcl711_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
381                           struct comedi_insn *insn, unsigned int *data)
382 {
383         struct pcl711_private *devpriv = dev->private;
384         int n;
385         int chan = CR_CHAN(insn->chanspec);
386
387         for (n = 0; n < insn->n; n++) {
388                 outb((data[n] & 0xff),
389                      dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
390                 outb((data[n] >> 8),
391                      dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
392
393                 devpriv->ao_readback[chan] = data[n];
394         }
395
396         return n;
397 }
398
399 static int pcl711_ao_insn_read(struct comedi_device *dev,
400                                struct comedi_subdevice *s,
401                                struct comedi_insn *insn, unsigned int *data)
402 {
403         struct pcl711_private *devpriv = dev->private;
404         int n;
405         int chan = CR_CHAN(insn->chanspec);
406
407         for (n = 0; n < insn->n; n++)
408                 data[n] = devpriv->ao_readback[chan];
409
410         return n;
411
412 }
413
414 /* Digital port read - Untested on 8112 */
415 static int pcl711_di_insn_bits(struct comedi_device *dev,
416                                struct comedi_subdevice *s,
417                                struct comedi_insn *insn, unsigned int *data)
418 {
419         data[1] = inb(dev->iobase + PCL711_DI_LO) |
420             (inb(dev->iobase + PCL711_DI_HI) << 8);
421
422         return insn->n;
423 }
424
425 /* Digital port write - Untested on 8112 */
426 static int pcl711_do_insn_bits(struct comedi_device *dev,
427                                struct comedi_subdevice *s,
428                                struct comedi_insn *insn, unsigned int *data)
429 {
430         if (data[0]) {
431                 s->state &= ~data[0];
432                 s->state |= data[0] & data[1];
433         }
434         if (data[0] & 0x00ff)
435                 outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
436         if (data[0] & 0xff00)
437                 outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
438
439         data[1] = s->state;
440
441         return insn->n;
442 }
443
444 static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
445 {
446         const struct pcl711_board *board = comedi_board(dev);
447         struct pcl711_private *devpriv;
448         int ret;
449         unsigned int irq;
450         struct comedi_subdevice *s;
451
452         ret = comedi_request_region(dev, it->options[0], PCL711_SIZE);
453         if (ret)
454                 return ret;
455
456         /* grab our IRQ */
457         irq = it->options[1];
458         if (irq > board->maxirq) {
459                 printk(KERN_ERR "irq out of range\n");
460                 return -EINVAL;
461         }
462         if (irq) {
463                 if (request_irq(irq, pcl711_interrupt, 0, dev->board_name,
464                                 dev)) {
465                         printk(KERN_ERR "unable to allocate irq %u\n", irq);
466                         return -EINVAL;
467                 } else {
468                         printk(KERN_INFO "( irq = %u )\n", irq);
469                 }
470         }
471         dev->irq = irq;
472
473         ret = comedi_alloc_subdevices(dev, 4);
474         if (ret)
475                 return ret;
476
477         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
478         if (!devpriv)
479                 return -ENOMEM;
480         dev->private = devpriv;
481
482         s = &dev->subdevices[0];
483         /* AI subdevice */
484         s->type = COMEDI_SUBD_AI;
485         s->subdev_flags = SDF_READABLE | SDF_GROUND;
486         s->n_chan = board->n_aichan;
487         s->maxdata = 0xfff;
488         s->len_chanlist = 1;
489         s->range_table = board->ai_range_type;
490         s->insn_read = pcl711_ai_insn;
491         if (irq) {
492                 dev->read_subdev = s;
493                 s->subdev_flags |= SDF_CMD_READ;
494                 s->do_cmdtest = pcl711_ai_cmdtest;
495                 s->do_cmd = pcl711_ai_cmd;
496         }
497
498         s = &dev->subdevices[1];
499         /* AO subdevice */
500         s->type = COMEDI_SUBD_AO;
501         s->subdev_flags = SDF_WRITABLE;
502         s->n_chan = board->n_aochan;
503         s->maxdata = 0xfff;
504         s->len_chanlist = 1;
505         s->range_table = &range_bipolar5;
506         s->insn_write = pcl711_ao_insn;
507         s->insn_read = pcl711_ao_insn_read;
508
509         s = &dev->subdevices[2];
510         /* 16-bit digital input */
511         s->type = COMEDI_SUBD_DI;
512         s->subdev_flags = SDF_READABLE;
513         s->n_chan = 16;
514         s->maxdata = 1;
515         s->len_chanlist = 16;
516         s->range_table = &range_digital;
517         s->insn_bits = pcl711_di_insn_bits;
518
519         s = &dev->subdevices[3];
520         /* 16-bit digital out */
521         s->type = COMEDI_SUBD_DO;
522         s->subdev_flags = SDF_WRITABLE;
523         s->n_chan = 16;
524         s->maxdata = 1;
525         s->len_chanlist = 16;
526         s->range_table = &range_digital;
527         s->state = 0;
528         s->insn_bits = pcl711_do_insn_bits;
529
530         /*
531            this is the "base value" for the mode register, which is
532            used for the irq on the PCL711
533          */
534         if (board->is_pcl711b)
535                 devpriv->mode = (dev->irq << 4);
536
537         /* clear DAC */
538         outb(0, dev->iobase + PCL711_DA0_LO);
539         outb(0, dev->iobase + PCL711_DA0_HI);
540         outb(0, dev->iobase + PCL711_DA1_LO);
541         outb(0, dev->iobase + PCL711_DA1_HI);
542
543         printk(KERN_INFO "\n");
544
545         return 0;
546 }
547
548 static const struct pcl711_board boardtypes[] = {
549         { "pcl711", 0, 0, 0, 5, 8, 1, 0, &range_bipolar5 },
550         { "pcl711b", 1, 0, 0, 5, 8, 1, 7, &range_pcl711b_ai },
551         { "acl8112hg", 0, 1, 0, 12, 16, 2, 15, &range_acl8112hg_ai },
552         { "acl8112dg", 0, 1, 1, 9, 16, 2, 15, &range_acl8112dg_ai },
553 };
554
555 static struct comedi_driver pcl711_driver = {
556         .driver_name    = "pcl711",
557         .module         = THIS_MODULE,
558         .attach         = pcl711_attach,
559         .detach         = comedi_legacy_detach,
560         .board_name     = &boardtypes[0].name,
561         .num_names      = ARRAY_SIZE(boardtypes),
562         .offset         = sizeof(struct pcl711_board),
563 };
564 module_comedi_driver(pcl711_driver);
565
566 MODULE_AUTHOR("Comedi http://www.comedi.org");
567 MODULE_DESCRIPTION("Comedi low-level driver");
568 MODULE_LICENSE("GPL");