7888781efcc90ea9d6c1e80751852854cae12523
[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/module.h>
57 #include <linux/interrupt.h>
58 #include "../comedidev.h"
59
60 #include <linux/delay.h>
61
62 #include "comedi_fc.h"
63 #include "8253.h"
64
65 /*
66  * I/O port register map
67  */
68 #define PCL711_CTR0             0x00
69 #define PCL711_CTR1             0x01
70 #define PCL711_CTR2             0x02
71 #define PCL711_CTRCTL           0x03
72 #define PCL711_AI_LSB_REG       0x04
73 #define PCL711_AI_MSB_REG       0x05
74 #define PCL711_AO_LSB_REG(x)    (0x04 + ((x) * 2))
75 #define PCL711_AO_MSB_REG(x)    (0x05 + ((x) * 2))
76 #define PCL711_DI_LO            0x06
77 #define PCL711_DI_HI            0x07
78 #define PCL711_CLRINTR          0x08
79 #define PCL711_GAIN             0x09
80 #define PCL711_MUX              0x0a
81 #define PCL711_MODE             0x0b
82 #define PCL711_SOFTTRIG         0x0c
83 #define PCL711_DO_LO            0x0d
84 #define PCL711_DO_HI            0x0e
85
86 static const struct comedi_lrange range_pcl711b_ai = {
87         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 = {
97         12, {
98                 BIP_RANGE(5),
99                 BIP_RANGE(0.5),
100                 BIP_RANGE(0.05),
101                 BIP_RANGE(0.005),
102                 UNI_RANGE(10),
103                 UNI_RANGE(1),
104                 UNI_RANGE(0.1),
105                 UNI_RANGE(0.01),
106                 BIP_RANGE(10),
107                 BIP_RANGE(1),
108                 BIP_RANGE(0.1),
109                 BIP_RANGE(0.01)
110         }
111 };
112
113 static const struct comedi_lrange range_acl8112dg_ai = {
114         9, {
115                 BIP_RANGE(5),
116                 BIP_RANGE(2.5),
117                 BIP_RANGE(1.25),
118                 BIP_RANGE(0.625),
119                 UNI_RANGE(10),
120                 UNI_RANGE(5),
121                 UNI_RANGE(2.5),
122                 UNI_RANGE(1.25),
123                 BIP_RANGE(10)
124         }
125 };
126
127 /*
128  * flags
129  */
130
131 #define PCL711_TIMEOUT 100
132 #define PCL711_DRDY 0x10
133
134 static const int i8253_osc_base = 500;  /* 2 Mhz */
135
136 struct pcl711_board {
137         const char *name;
138         unsigned int is_pcl711b:1;
139         unsigned int is_8112:1;
140         int n_aichan;
141         int n_aochan;
142         int maxirq;
143         const struct comedi_lrange *ai_range_type;
144 };
145
146 static const struct pcl711_board boardtypes[] = {
147         {
148                 .name           = "pcl711",
149                 .n_aichan       = 8,
150                 .n_aochan       = 1,
151                 .ai_range_type  = &range_bipolar5,
152         }, {
153                 .name           = "pcl711b",
154                 .is_pcl711b     = 1,
155                 .n_aichan       = 8,
156                 .n_aochan       = 1,
157                 .maxirq         = 7,
158                 .ai_range_type  = &range_pcl711b_ai,
159         }, {
160                 .name           = "acl8112hg",
161                 .is_8112        = 1,
162                 .n_aichan       = 16,
163                 .n_aochan       = 2,
164                 .maxirq         = 15,
165                 .ai_range_type  = &range_acl8112hg_ai,
166         }, {
167                 .name           = "acl8112dg",
168                 .is_8112        = 1,
169                 .n_aichan       = 16,
170                 .n_aochan       = 2,
171                 .maxirq         = 15,
172                 .ai_range_type  = &range_acl8112dg_ai,
173         },
174 };
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 static unsigned int pcl711_ai_get_sample(struct comedi_device *dev,
189                                          struct comedi_subdevice *s)
190 {
191         unsigned int val;
192
193         val = inb(dev->iobase + PCL711_AI_MSB_REG) << 8;
194         val |= inb(dev->iobase + PCL711_AI_LSB_REG);
195
196         return val & s->maxdata;
197 }
198
199 static irqreturn_t pcl711_interrupt(int irq, void *d)
200 {
201         struct comedi_device *dev = d;
202         const struct pcl711_board *board = comedi_board(dev);
203         struct pcl711_private *devpriv = dev->private;
204         struct comedi_subdevice *s = dev->read_subdev;
205         unsigned int data;
206
207         if (!dev->attached) {
208                 comedi_error(dev, "spurious interrupt");
209                 return IRQ_HANDLED;
210         }
211
212         data = pcl711_ai_get_sample(dev, s);
213
214         outb(0, dev->iobase + PCL711_CLRINTR);
215
216         /* FIXME! Nothing else sets ntrig! */
217         if (!(--devpriv->ntrig)) {
218                 if (board->is_8112)
219                         outb(1, dev->iobase + PCL711_MODE);
220                 else
221                         outb(0, dev->iobase + PCL711_MODE);
222
223                 s->async->events |= COMEDI_CB_EOA;
224         }
225         comedi_event(dev, s);
226         return IRQ_HANDLED;
227 }
228
229 static void pcl711_set_changain(struct comedi_device *dev, int chan)
230 {
231         const struct pcl711_board *board = comedi_board(dev);
232         int chan_register;
233
234         outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
235
236         chan_register = CR_CHAN(chan);
237
238         if (board->is_8112) {
239
240                 /*
241                  *  Set the correct channel.  The two channel banks are switched
242                  *  using the mask value.
243                  *  NB: To use differential channels, you should use
244                  *  mask = 0x30, but I haven't written the support for this
245                  *  yet. /JJ
246                  */
247
248                 if (chan_register >= 8)
249                         chan_register = 0x20 | (chan_register & 0x7);
250                 else
251                         chan_register |= 0x10;
252         } else {
253                 outb(chan_register, dev->iobase + PCL711_MUX);
254         }
255 }
256
257 static int pcl711_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
258                           struct comedi_insn *insn, unsigned int *data)
259 {
260         const struct pcl711_board *board = comedi_board(dev);
261         int i, n;
262         int hi;
263
264         pcl711_set_changain(dev, insn->chanspec);
265
266         for (n = 0; n < insn->n; n++) {
267                 /*
268                  *  Write the correct mode (software polling) and start polling
269                  *  by writing to the trigger register
270                  */
271                 outb(1, dev->iobase + PCL711_MODE);
272
273                 if (!board->is_8112)
274                         outb(0, dev->iobase + PCL711_SOFTTRIG);
275
276                 i = PCL711_TIMEOUT;
277                 while (--i) {
278                         hi = inb(dev->iobase + PCL711_AI_MSB_REG);
279                         if (!(hi & PCL711_DRDY))
280                                 goto ok;
281                         udelay(1);
282                 }
283                 printk(KERN_ERR "comedi%d: pcl711: A/D timeout\n", dev->minor);
284                 return -ETIME;
285
286 ok:
287                 data[n] = pcl711_ai_get_sample(dev, s);
288         }
289
290         return n;
291 }
292
293 static int pcl711_ai_cmdtest(struct comedi_device *dev,
294                              struct comedi_subdevice *s, struct comedi_cmd *cmd)
295 {
296         struct pcl711_private *devpriv = dev->private;
297         int tmp;
298         int err = 0;
299
300         /* Step 1 : check if triggers are trivially valid */
301
302         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
303         err |= cfc_check_trigger_src(&cmd->scan_begin_src,
304                                         TRIG_TIMER | TRIG_EXT);
305         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
306         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
307         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
308
309         if (err)
310                 return 1;
311
312         /* Step 2a : make sure trigger sources are unique */
313
314         err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
315         err |= cfc_check_trigger_is_unique(cmd->stop_src);
316
317         /* Step 2b : and mutually compatible */
318
319         if (err)
320                 return 2;
321
322         /* Step 3: check if arguments are trivially valid */
323
324         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
325
326         if (cmd->scan_begin_src == TRIG_EXT) {
327                 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
328         } else {
329 #define MAX_SPEED 1000
330 #define TIMER_BASE 100
331                 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
332                                                  MAX_SPEED);
333         }
334
335         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
336         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
337
338         if (cmd->stop_src == TRIG_NONE) {
339                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
340         } else {
341                 /* ignore */
342         }
343
344         if (err)
345                 return 3;
346
347         /* step 4 */
348
349         if (cmd->scan_begin_src == TRIG_TIMER) {
350                 tmp = cmd->scan_begin_arg;
351                 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
352                                                &devpriv->divisor1,
353                                                &devpriv->divisor2,
354                                                &cmd->scan_begin_arg,
355                                                cmd->flags & TRIG_ROUND_MASK);
356                 if (tmp != cmd->scan_begin_arg)
357                         err++;
358         }
359
360         if (err)
361                 return 4;
362
363         return 0;
364 }
365
366 static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
367 {
368         struct pcl711_private *devpriv = dev->private;
369         int timer1, timer2;
370         struct comedi_cmd *cmd = &s->async->cmd;
371
372         pcl711_set_changain(dev, cmd->chanlist[0]);
373
374         if (cmd->scan_begin_src == TRIG_TIMER) {
375                 /*
376                  *  Set timers
377                  *      timer chip is an 8253, with timers 1 and 2
378                  *      cascaded
379                  *  0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
380                  *        Mode 2 = Rate generator
381                  *
382                  *  0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
383                  */
384
385                 timer1 = timer2 = 0;
386                 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
387                                           &cmd->scan_begin_arg,
388                                           TRIG_ROUND_NEAREST);
389
390                 outb(0x74, dev->iobase + PCL711_CTRCTL);
391                 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
392                 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
393                 outb(0xb4, dev->iobase + PCL711_CTRCTL);
394                 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
395                 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
396
397                 /* clear pending interrupts (just in case) */
398                 outb(0, dev->iobase + PCL711_CLRINTR);
399
400                 /*
401                  *  Set mode to IRQ transfer
402                  */
403                 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
404         } else {
405                 /* external trigger */
406                 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
407         }
408
409         return 0;
410 }
411
412 static void pcl711_ao_write(struct comedi_device *dev,
413                             unsigned int chan, unsigned int val)
414 {
415         outb(val & 0xff, dev->iobase + PCL711_AO_LSB_REG(chan));
416         outb((val >> 8) & 0xff, dev->iobase + PCL711_AO_MSB_REG(chan));
417 }
418
419 static int pcl711_ao_insn_write(struct comedi_device *dev,
420                                 struct comedi_subdevice *s,
421                                 struct comedi_insn *insn,
422                                 unsigned int *data)
423 {
424         struct pcl711_private *devpriv = dev->private;
425         unsigned int chan = CR_CHAN(insn->chanspec);
426         unsigned int val = devpriv->ao_readback[chan];
427         int i;
428
429         for (i = 0; i < insn->n; i++) {
430                 val = data[i];
431                 pcl711_ao_write(dev, chan, val);
432         }
433         devpriv->ao_readback[chan] = val;
434
435         return insn->n;
436 }
437
438 static int pcl711_ao_insn_read(struct comedi_device *dev,
439                                struct comedi_subdevice *s,
440                                struct comedi_insn *insn,
441                                unsigned int *data)
442 {
443         struct pcl711_private *devpriv = dev->private;
444         unsigned int chan = CR_CHAN(insn->chanspec);
445         int i;
446
447         for (i = 0; i < insn->n; i++)
448                 data[i] = devpriv->ao_readback[chan];
449
450         return insn->n;
451 }
452
453 /* Digital port read - Untested on 8112 */
454 static int pcl711_di_insn_bits(struct comedi_device *dev,
455                                struct comedi_subdevice *s,
456                                struct comedi_insn *insn, unsigned int *data)
457 {
458         data[1] = inb(dev->iobase + PCL711_DI_LO) |
459             (inb(dev->iobase + PCL711_DI_HI) << 8);
460
461         return insn->n;
462 }
463
464 static int pcl711_do_insn_bits(struct comedi_device *dev,
465                                struct comedi_subdevice *s,
466                                struct comedi_insn *insn,
467                                unsigned int *data)
468 {
469         unsigned int mask;
470
471         mask = comedi_dio_update_state(s, data);
472         if (mask) {
473                 if (mask & 0x00ff)
474                         outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
475                 if (mask & 0xff00)
476                         outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
477         }
478
479         data[1] = s->state;
480
481         return insn->n;
482 }
483
484 static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
485 {
486         const struct pcl711_board *board = comedi_board(dev);
487         struct pcl711_private *devpriv;
488         struct comedi_subdevice *s;
489         int ret;
490
491         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
492         if (!devpriv)
493                 return -ENOMEM;
494
495         ret = comedi_request_region(dev, it->options[0], 0x10);
496         if (ret)
497                 return ret;
498
499         if (it->options[1] && it->options[1] <= board->maxirq) {
500                 ret = request_irq(it->options[1], pcl711_interrupt, 0,
501                                   dev->board_name, dev);
502                 if (ret == 0) {
503                         dev->irq = it->options[1];
504
505                         /*
506                          * The PCL711b needs the irq number in the
507                          * mode register.
508                          */
509                         if (board->is_pcl711b)
510                                 devpriv->mode = (dev->irq << 4);
511                 }
512         }
513
514         ret = comedi_alloc_subdevices(dev, 4);
515         if (ret)
516                 return ret;
517
518         /* Analog Input subdevice */
519         s = &dev->subdevices[0];
520         s->type         = COMEDI_SUBD_AI;
521         s->subdev_flags = SDF_READABLE | SDF_GROUND;
522         s->n_chan       = board->n_aichan;
523         s->maxdata      = 0xfff;
524         s->range_table  = board->ai_range_type;
525         s->insn_read    = pcl711_ai_insn;
526         if (dev->irq) {
527                 dev->read_subdev = s;
528                 s->subdev_flags |= SDF_CMD_READ;
529                 s->len_chanlist = 1;
530                 s->do_cmdtest   = pcl711_ai_cmdtest;
531                 s->do_cmd       = pcl711_ai_cmd;
532         }
533
534         /* Analog Output subdevice */
535         s = &dev->subdevices[1];
536         s->type         = COMEDI_SUBD_AO;
537         s->subdev_flags = SDF_WRITABLE;
538         s->n_chan       = board->n_aochan;
539         s->maxdata      = 0xfff;
540         s->range_table  = &range_bipolar5;
541         s->insn_write   = pcl711_ao_insn_write;
542         s->insn_read    = pcl711_ao_insn_read;
543
544         /* Digital Input subdevice */
545         s = &dev->subdevices[2];
546         s->type         = COMEDI_SUBD_DI;
547         s->subdev_flags = SDF_READABLE;
548         s->n_chan       = 16;
549         s->maxdata      = 1;
550         s->range_table  = &range_digital;
551         s->insn_bits    = pcl711_di_insn_bits;
552
553         /* Digital Output subdevice */
554         s = &dev->subdevices[3];
555         s->type         = COMEDI_SUBD_DO;
556         s->subdev_flags = SDF_WRITABLE;
557         s->n_chan       = 16;
558         s->maxdata      = 1;
559         s->range_table  = &range_digital;
560         s->insn_bits    = pcl711_do_insn_bits;
561
562         /* clear DAC */
563         pcl711_ao_write(dev, 0, 0x0);
564         pcl711_ao_write(dev, 1, 0x0);
565
566         return 0;
567 }
568
569 static struct comedi_driver pcl711_driver = {
570         .driver_name    = "pcl711",
571         .module         = THIS_MODULE,
572         .attach         = pcl711_attach,
573         .detach         = comedi_legacy_detach,
574         .board_name     = &boardtypes[0].name,
575         .num_names      = ARRAY_SIZE(boardtypes),
576         .offset         = sizeof(struct pcl711_board),
577 };
578 module_comedi_driver(pcl711_driver);
579
580 MODULE_AUTHOR("Comedi http://www.comedi.org");
581 MODULE_DESCRIPTION("Comedi low-level driver");
582 MODULE_LICENSE("GPL");