c606052b05bde68e8753da42289f5b8590074b59
[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 #define PCL711_CTR0 0
66 #define PCL711_CTR1 1
67 #define PCL711_CTR2 2
68 #define PCL711_CTRCTL 3
69 #define PCL711_AD_LO 4
70 #define PCL711_DA0_LO 4
71 #define PCL711_AD_HI 5
72 #define PCL711_DA0_HI 5
73 #define PCL711_DI_LO 6
74 #define PCL711_DA1_LO 6
75 #define PCL711_DI_HI 7
76 #define PCL711_DA1_HI 7
77 #define PCL711_CLRINTR 8
78 #define PCL711_GAIN 9
79 #define PCL711_MUX 10
80 #define PCL711_MODE 11
81 #define PCL711_SOFTTRIG 12
82 #define PCL711_DO_LO 13
83 #define PCL711_DO_HI 14
84
85 static const struct comedi_lrange range_pcl711b_ai = {
86         5, {
87                 BIP_RANGE(5),
88                 BIP_RANGE(2.5),
89                 BIP_RANGE(1.25),
90                 BIP_RANGE(0.625),
91                 BIP_RANGE(0.3125)
92         }
93 };
94
95 static const struct comedi_lrange range_acl8112hg_ai = {
96         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 = {
113         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         const char *name;
137         unsigned int is_pcl711b:1;
138         unsigned int is_8112:1;
139         int n_aichan;
140         int n_aochan;
141         int maxirq;
142         const struct comedi_lrange *ai_range_type;
143 };
144
145 static const struct pcl711_board boardtypes[] = {
146         {
147                 .name           = "pcl711",
148                 .n_aichan       = 8,
149                 .n_aochan       = 1,
150                 .ai_range_type  = &range_bipolar5,
151         }, {
152                 .name           = "pcl711b",
153                 .is_pcl711b     = 1,
154                 .n_aichan       = 8,
155                 .n_aochan       = 1,
156                 .maxirq         = 7,
157                 .ai_range_type  = &range_pcl711b_ai,
158         }, {
159                 .name           = "acl8112hg",
160                 .is_8112        = 1,
161                 .n_aichan       = 16,
162                 .n_aochan       = 2,
163                 .maxirq         = 15,
164                 .ai_range_type  = &range_acl8112hg_ai,
165         }, {
166                 .name           = "acl8112dg",
167                 .is_8112        = 1,
168                 .n_aichan       = 16,
169                 .n_aochan       = 2,
170                 .maxirq         = 15,
171                 .ai_range_type  = &range_acl8112dg_ai,
172         },
173 };
174
175 struct pcl711_private {
176
177         int board;
178         int adchan;
179         int ntrig;
180         int aip[8];
181         int mode;
182         unsigned int ao_readback[2];
183         unsigned int divisor1;
184         unsigned int divisor2;
185 };
186
187 static irqreturn_t pcl711_interrupt(int irq, void *d)
188 {
189         int lo, hi;
190         int data;
191         struct comedi_device *dev = d;
192         const struct pcl711_board *board = comedi_board(dev);
193         struct pcl711_private *devpriv = dev->private;
194         struct comedi_subdevice *s = &dev->subdevices[0];
195
196         if (!dev->attached) {
197                 comedi_error(dev, "spurious interrupt");
198                 return IRQ_HANDLED;
199         }
200
201         hi = inb(dev->iobase + PCL711_AD_HI);
202         lo = inb(dev->iobase + PCL711_AD_LO);
203         outb(0, dev->iobase + PCL711_CLRINTR);
204
205         data = (hi << 8) | lo;
206
207         /* FIXME! Nothing else sets ntrig! */
208         if (!(--devpriv->ntrig)) {
209                 if (board->is_8112)
210                         outb(1, dev->iobase + PCL711_MODE);
211                 else
212                         outb(0, dev->iobase + PCL711_MODE);
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         const struct pcl711_board *board = comedi_board(dev);
223         int chan_register;
224
225         outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
226
227         chan_register = CR_CHAN(chan);
228
229         if (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         const struct pcl711_board *board = comedi_board(dev);
252         int i, n;
253         int hi, lo;
254
255         pcl711_set_changain(dev, insn->chanspec);
256
257         for (n = 0; n < insn->n; n++) {
258                 /*
259                  *  Write the correct mode (software polling) and start polling
260                  *  by writing to the trigger register
261                  */
262                 outb(1, dev->iobase + PCL711_MODE);
263
264                 if (!board->is_8112)
265                         outb(0, dev->iobase + PCL711_SOFTTRIG);
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                         udelay(1);
273                 }
274                 printk(KERN_ERR "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,
287                              struct comedi_subdevice *s, struct comedi_cmd *cmd)
288 {
289         struct pcl711_private *devpriv = dev->private;
290         int tmp;
291         int err = 0;
292
293         /* Step 1 : check if triggers are trivially valid */
294
295         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
296         err |= cfc_check_trigger_src(&cmd->scan_begin_src,
297                                         TRIG_TIMER | TRIG_EXT);
298         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
299         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
300         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
301
302         if (err)
303                 return 1;
304
305         /* Step 2a : make sure trigger sources are unique */
306
307         err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
308         err |= cfc_check_trigger_is_unique(cmd->stop_src);
309
310         /* Step 2b : and mutually compatible */
311
312         if (err)
313                 return 2;
314
315         /* Step 3: check if arguments are trivially valid */
316
317         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
318
319         if (cmd->scan_begin_src == TRIG_EXT) {
320                 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
321         } else {
322 #define MAX_SPEED 1000
323 #define TIMER_BASE 100
324                 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
325                                                  MAX_SPEED);
326         }
327
328         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
329         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
330
331         if (cmd->stop_src == TRIG_NONE) {
332                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
333         } else {
334                 /* ignore */
335         }
336
337         if (err)
338                 return 3;
339
340         /* step 4 */
341
342         if (cmd->scan_begin_src == TRIG_TIMER) {
343                 tmp = cmd->scan_begin_arg;
344                 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
345                                                &devpriv->divisor1,
346                                                &devpriv->divisor2,
347                                                &cmd->scan_begin_arg,
348                                                cmd->flags & TRIG_ROUND_MASK);
349                 if (tmp != cmd->scan_begin_arg)
350                         err++;
351         }
352
353         if (err)
354                 return 4;
355
356         return 0;
357 }
358
359 static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
360 {
361         struct pcl711_private *devpriv = dev->private;
362         int timer1, timer2;
363         struct comedi_cmd *cmd = &s->async->cmd;
364
365         pcl711_set_changain(dev, cmd->chanlist[0]);
366
367         if (cmd->scan_begin_src == TRIG_TIMER) {
368                 /*
369                  *  Set timers
370                  *      timer chip is an 8253, with timers 1 and 2
371                  *      cascaded
372                  *  0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
373                  *        Mode 2 = Rate generator
374                  *
375                  *  0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
376                  */
377
378                 timer1 = timer2 = 0;
379                 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
380                                           &cmd->scan_begin_arg,
381                                           TRIG_ROUND_NEAREST);
382
383                 outb(0x74, dev->iobase + PCL711_CTRCTL);
384                 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
385                 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
386                 outb(0xb4, dev->iobase + PCL711_CTRCTL);
387                 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
388                 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
389
390                 /* clear pending interrupts (just in case) */
391                 outb(0, dev->iobase + PCL711_CLRINTR);
392
393                 /*
394                  *  Set mode to IRQ transfer
395                  */
396                 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
397         } else {
398                 /* external trigger */
399                 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
400         }
401
402         return 0;
403 }
404
405 /*
406    analog output
407 */
408 static int pcl711_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
409                           struct comedi_insn *insn, unsigned int *data)
410 {
411         struct pcl711_private *devpriv = dev->private;
412         int n;
413         int chan = CR_CHAN(insn->chanspec);
414
415         for (n = 0; n < insn->n; n++) {
416                 outb((data[n] & 0xff),
417                      dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
418                 outb((data[n] >> 8),
419                      dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
420
421                 devpriv->ao_readback[chan] = data[n];
422         }
423
424         return n;
425 }
426
427 static int pcl711_ao_insn_read(struct comedi_device *dev,
428                                struct comedi_subdevice *s,
429                                struct comedi_insn *insn, unsigned int *data)
430 {
431         struct pcl711_private *devpriv = dev->private;
432         int n;
433         int chan = CR_CHAN(insn->chanspec);
434
435         for (n = 0; n < insn->n; n++)
436                 data[n] = devpriv->ao_readback[chan];
437
438         return n;
439
440 }
441
442 /* Digital port read - Untested on 8112 */
443 static int pcl711_di_insn_bits(struct comedi_device *dev,
444                                struct comedi_subdevice *s,
445                                struct comedi_insn *insn, unsigned int *data)
446 {
447         data[1] = inb(dev->iobase + PCL711_DI_LO) |
448             (inb(dev->iobase + PCL711_DI_HI) << 8);
449
450         return insn->n;
451 }
452
453 static int pcl711_do_insn_bits(struct comedi_device *dev,
454                                struct comedi_subdevice *s,
455                                struct comedi_insn *insn,
456                                unsigned int *data)
457 {
458         unsigned int mask;
459
460         mask = comedi_dio_update_state(s, data);
461         if (mask) {
462                 if (mask & 0x00ff)
463                         outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
464                 if (mask & 0xff00)
465                         outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
466         }
467
468         data[1] = s->state;
469
470         return insn->n;
471 }
472
473 static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
474 {
475         const struct pcl711_board *board = comedi_board(dev);
476         struct pcl711_private *devpriv;
477         struct comedi_subdevice *s;
478         int ret;
479
480         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
481         if (!devpriv)
482                 return -ENOMEM;
483
484         ret = comedi_request_region(dev, it->options[0], 0x10);
485         if (ret)
486                 return ret;
487
488         if (it->options[1] && it->options[1] <= board->maxirq) {
489                 ret = request_irq(it->options[1], pcl711_interrupt, 0,
490                                   dev->board_name, dev);
491                 if (ret == 0) {
492                         dev->irq = it->options[1];
493
494                         /*
495                          * The PCL711b needs the irq number in the
496                          * mode register.
497                          */
498                         if (board->is_pcl711b)
499                                 devpriv->mode = (dev->irq << 4);
500                 }
501         }
502
503         ret = comedi_alloc_subdevices(dev, 4);
504         if (ret)
505                 return ret;
506
507         /* Analog Input subdevice */
508         s = &dev->subdevices[0];
509         s->type         = COMEDI_SUBD_AI;
510         s->subdev_flags = SDF_READABLE | SDF_GROUND;
511         s->n_chan       = board->n_aichan;
512         s->maxdata      = 0xfff;
513         s->range_table  = board->ai_range_type;
514         s->insn_read    = pcl711_ai_insn;
515         if (dev->irq) {
516                 dev->read_subdev = s;
517                 s->subdev_flags |= SDF_CMD_READ;
518                 s->len_chanlist = 1;
519                 s->do_cmdtest   = pcl711_ai_cmdtest;
520                 s->do_cmd       = pcl711_ai_cmd;
521         }
522
523         /* Analog Output subdevice */
524         s = &dev->subdevices[1];
525         s->type         = COMEDI_SUBD_AO;
526         s->subdev_flags = SDF_WRITABLE;
527         s->n_chan       = board->n_aochan;
528         s->maxdata      = 0xfff;
529         s->range_table  = &range_bipolar5;
530         s->insn_write   = pcl711_ao_insn;
531         s->insn_read    = pcl711_ao_insn_read;
532
533         /* Digital Input subdevice */
534         s = &dev->subdevices[2];
535         s->type         = COMEDI_SUBD_DI;
536         s->subdev_flags = SDF_READABLE;
537         s->n_chan       = 16;
538         s->maxdata      = 1;
539         s->range_table  = &range_digital;
540         s->insn_bits    = pcl711_di_insn_bits;
541
542         /* Digital Output subdevice */
543         s = &dev->subdevices[3];
544         s->type         = COMEDI_SUBD_DO;
545         s->subdev_flags = SDF_WRITABLE;
546         s->n_chan       = 16;
547         s->maxdata      = 1;
548         s->range_table  = &range_digital;
549         s->insn_bits    = pcl711_do_insn_bits;
550
551         /* clear DAC */
552         outb(0, dev->iobase + PCL711_DA0_LO);
553         outb(0, dev->iobase + PCL711_DA0_HI);
554         outb(0, dev->iobase + PCL711_DA1_LO);
555         outb(0, dev->iobase + PCL711_DA1_HI);
556
557         printk(KERN_INFO "\n");
558
559         return 0;
560 }
561
562 static struct comedi_driver pcl711_driver = {
563         .driver_name    = "pcl711",
564         .module         = THIS_MODULE,
565         .attach         = pcl711_attach,
566         .detach         = comedi_legacy_detach,
567         .board_name     = &boardtypes[0].name,
568         .num_names      = ARRAY_SIZE(boardtypes),
569         .offset         = sizeof(struct pcl711_board),
570 };
571 module_comedi_driver(pcl711_driver);
572
573 MODULE_AUTHOR("Comedi http://www.comedi.org");
574 MODULE_DESCRIPTION("Comedi low-level driver");
575 MODULE_LICENSE("GPL");