8f068ebc7609a05ad5adb146657091a2f206df32
[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_TIMER_BASE       0x00
69 #define PCL711_AI_LSB_REG       0x04
70 #define PCL711_AI_MSB_REG       0x05
71 #define PCL711_AI_MSB_DRDY      (1 << 4)
72 #define PCL711_AO_LSB_REG(x)    (0x04 + ((x) * 2))
73 #define PCL711_AO_MSB_REG(x)    (0x05 + ((x) * 2))
74 #define PCL711_DI_LO            0x06
75 #define PCL711_DI_HI            0x07
76 #define PCL711_CLRINTR          0x08
77 #define PCL711_GAIN             0x09
78 #define PCL711_MUX_REG          0x0a
79 #define PCL711_MUX_CHAN(x)      (((x) & 0xf) << 0)
80 #define PCL711_MUX_CS0          (1 << 4)
81 #define PCL711_MUX_CS1          (1 << 5)
82 #define PCL711_MUX_DIFF         (PCL711_MUX_CS0 | PCL711_MUX_CS1)
83 #define PCL711_MODE_REG         0x0b
84 #define PCL711_MODE_DEFAULT     (0 << 0)
85 #define PCL711_MODE_SOFTTRIG    (1 << 0)
86 #define PCL711_MODE_EXT         (2 << 0)
87 #define PCL711_MODE_EXT_IRQ     (3 << 0)
88 #define PCL711_MODE_PACER       (4 << 0)
89 #define PCL711_MODE_PACER_IRQ   (6 << 0)
90 #define PCL711_MODE_IRQ(x)      (((x) & 0x7) << 4)
91 #define PCL711_SOFTTRIG_REG     0x0c
92 #define PCL711_SOFTTRIG         (0 << 0)  /* any value will work */
93 #define PCL711_DO_LO            0x0d
94 #define PCL711_DO_HI            0x0e
95
96 static const struct comedi_lrange range_pcl711b_ai = {
97         5, {
98                 BIP_RANGE(5),
99                 BIP_RANGE(2.5),
100                 BIP_RANGE(1.25),
101                 BIP_RANGE(0.625),
102                 BIP_RANGE(0.3125)
103         }
104 };
105
106 static const struct comedi_lrange range_acl8112hg_ai = {
107         12, {
108                 BIP_RANGE(5),
109                 BIP_RANGE(0.5),
110                 BIP_RANGE(0.05),
111                 BIP_RANGE(0.005),
112                 UNI_RANGE(10),
113                 UNI_RANGE(1),
114                 UNI_RANGE(0.1),
115                 UNI_RANGE(0.01),
116                 BIP_RANGE(10),
117                 BIP_RANGE(1),
118                 BIP_RANGE(0.1),
119                 BIP_RANGE(0.01)
120         }
121 };
122
123 static const struct comedi_lrange range_acl8112dg_ai = {
124         9, {
125                 BIP_RANGE(5),
126                 BIP_RANGE(2.5),
127                 BIP_RANGE(1.25),
128                 BIP_RANGE(0.625),
129                 UNI_RANGE(10),
130                 UNI_RANGE(5),
131                 UNI_RANGE(2.5),
132                 UNI_RANGE(1.25),
133                 BIP_RANGE(10)
134         }
135 };
136
137 struct pcl711_board {
138         const char *name;
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                 .n_aichan       = 8,
154                 .n_aochan       = 1,
155                 .maxirq         = 7,
156                 .ai_range_type  = &range_pcl711b_ai,
157         }, {
158                 .name           = "acl8112hg",
159                 .n_aichan       = 16,
160                 .n_aochan       = 2,
161                 .maxirq         = 15,
162                 .ai_range_type  = &range_acl8112hg_ai,
163         }, {
164                 .name           = "acl8112dg",
165                 .n_aichan       = 16,
166                 .n_aochan       = 2,
167                 .maxirq         = 15,
168                 .ai_range_type  = &range_acl8112dg_ai,
169         },
170 };
171
172 struct pcl711_private {
173         int ntrig;
174         unsigned int ao_readback[2];
175         unsigned int divisor1;
176         unsigned int divisor2;
177 };
178
179 static void pcl711_ai_set_mode(struct comedi_device *dev, unsigned int mode)
180 {
181         /*
182          * The pcl711b board uses bits in the mode register to select the
183          * interrupt. The other boards supported by this driver all use
184          * jumpers on the board.
185          *
186          * Enables the interrupt when needed on the pcl711b board. These
187          * bits do nothing on the other boards.
188          */
189         if (mode == PCL711_MODE_EXT_IRQ || mode == PCL711_MODE_PACER_IRQ)
190                 mode |= PCL711_MODE_IRQ(dev->irq);
191
192         outb(mode, dev->iobase + PCL711_MODE_REG);
193 }
194
195 static unsigned int pcl711_ai_get_sample(struct comedi_device *dev,
196                                          struct comedi_subdevice *s)
197 {
198         unsigned int val;
199
200         val = inb(dev->iobase + PCL711_AI_MSB_REG) << 8;
201         val |= inb(dev->iobase + PCL711_AI_LSB_REG);
202
203         return val & s->maxdata;
204 }
205
206 static irqreturn_t pcl711_interrupt(int irq, void *d)
207 {
208         struct comedi_device *dev = d;
209         struct pcl711_private *devpriv = dev->private;
210         struct comedi_subdevice *s = dev->read_subdev;
211         unsigned int data;
212
213         if (!dev->attached) {
214                 comedi_error(dev, "spurious interrupt");
215                 return IRQ_HANDLED;
216         }
217
218         data = pcl711_ai_get_sample(dev, s);
219
220         outb(0, dev->iobase + PCL711_CLRINTR);
221
222         /* FIXME! Nothing else sets ntrig! */
223         if (!(--devpriv->ntrig)) {
224                 pcl711_ai_set_mode(dev, PCL711_MODE_SOFTTRIG);
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,
233                                 struct comedi_subdevice *s,
234                                 unsigned int chanspec)
235 {
236         unsigned int chan = CR_CHAN(chanspec);
237         unsigned int range = CR_RANGE(chanspec);
238         unsigned int aref = CR_AREF(chanspec);
239         unsigned int mux = 0;
240
241         outb(range, dev->iobase + PCL711_GAIN);
242
243         if (s->n_chan > 8) {
244                 /* Select the correct MPC508A chip */
245                 if (aref == AREF_DIFF) {
246                         chan &= 0x7;
247                         mux |= PCL711_MUX_DIFF;
248                 } else {
249                         if (chan < 8)
250                                 mux |= PCL711_MUX_CS0;
251                         else
252                                 mux |= PCL711_MUX_CS1;
253                 }
254         }
255         outb(mux | PCL711_MUX_CHAN(chan), dev->iobase + PCL711_MUX_REG);
256 }
257
258 static int pcl711_ai_wait_for_eoc(struct comedi_device *dev,
259                                   unsigned int timeout)
260 {
261         unsigned int msb;
262
263         while (timeout--) {
264                 msb = inb(dev->iobase + PCL711_AI_MSB_REG);
265                 if ((msb & PCL711_AI_MSB_DRDY) == 0)
266                         return 0;
267                 udelay(1);
268         }
269         return -ETIME;
270 }
271
272 static int pcl711_ai_insn_read(struct comedi_device *dev,
273                                struct comedi_subdevice *s,
274                                struct comedi_insn *insn,
275                                unsigned int *data)
276 {
277         int ret;
278         int i;
279
280         pcl711_set_changain(dev, s, insn->chanspec);
281
282         pcl711_ai_set_mode(dev, PCL711_MODE_SOFTTRIG);
283
284         for (i = 0; i < insn->n; i++) {
285                 outb(PCL711_SOFTTRIG, dev->iobase + PCL711_SOFTTRIG_REG);
286
287                 ret = pcl711_ai_wait_for_eoc(dev, 100);
288                 if (ret)
289                         return ret;
290
291                 data[i] = pcl711_ai_get_sample(dev, s);
292         }
293
294         return insn->n;
295 }
296
297 static int pcl711_ai_cmdtest(struct comedi_device *dev,
298                              struct comedi_subdevice *s, struct comedi_cmd *cmd)
299 {
300         struct pcl711_private *devpriv = dev->private;
301         int tmp;
302         int err = 0;
303
304         /* Step 1 : check if triggers are trivially valid */
305
306         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
307         err |= cfc_check_trigger_src(&cmd->scan_begin_src,
308                                         TRIG_TIMER | TRIG_EXT);
309         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
310         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
311         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
312
313         if (err)
314                 return 1;
315
316         /* Step 2a : make sure trigger sources are unique */
317
318         err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
319         err |= cfc_check_trigger_is_unique(cmd->stop_src);
320
321         /* Step 2b : and mutually compatible */
322
323         if (err)
324                 return 2;
325
326         /* Step 3: check if arguments are trivially valid */
327
328         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
329
330         if (cmd->scan_begin_src == TRIG_EXT) {
331                 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
332         } else {
333 #define MAX_SPEED 1000
334 #define TIMER_BASE 100
335                 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
336                                                  MAX_SPEED);
337         }
338
339         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
340         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
341
342         if (cmd->stop_src == TRIG_NONE) {
343                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
344         } else {
345                 /* ignore */
346         }
347
348         if (err)
349                 return 3;
350
351         /* step 4 */
352
353         if (cmd->scan_begin_src == TRIG_TIMER) {
354                 tmp = cmd->scan_begin_arg;
355                 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
356                                                &devpriv->divisor1,
357                                                &devpriv->divisor2,
358                                                &cmd->scan_begin_arg,
359                                                cmd->flags & TRIG_ROUND_MASK);
360                 if (tmp != cmd->scan_begin_arg)
361                         err++;
362         }
363
364         if (err)
365                 return 4;
366
367         return 0;
368 }
369
370 static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
371 {
372         int timer1, timer2;
373         struct comedi_cmd *cmd = &s->async->cmd;
374
375         pcl711_set_changain(dev, s, cmd->chanlist[0]);
376
377         if (cmd->scan_begin_src == TRIG_TIMER) {
378                 timer1 = timer2 = 0;
379                 i8253_cascade_ns_to_timer(500,          /* 2 Mhz */
380                                           &timer1, &timer2,
381                                           &cmd->scan_begin_arg,
382                                           TRIG_ROUND_NEAREST);
383
384                 i8254_load(dev->iobase + PCL711_TIMER_BASE, 0,
385                            1, timer1, I8254_MODE2 | I8254_BINARY);
386                 i8254_load(dev->iobase + PCL711_TIMER_BASE, 0,
387                            2, timer2, I8254_MODE2 | I8254_BINARY);
388
389                 /* clear pending interrupts (just in case) */
390                 outb(0, dev->iobase + PCL711_CLRINTR);
391
392                 pcl711_ai_set_mode(dev, PCL711_MODE_PACER_IRQ);
393         } else {
394                 pcl711_ai_set_mode(dev, PCL711_MODE_EXT_IRQ);
395         }
396
397         return 0;
398 }
399
400 static void pcl711_ao_write(struct comedi_device *dev,
401                             unsigned int chan, unsigned int val)
402 {
403         outb(val & 0xff, dev->iobase + PCL711_AO_LSB_REG(chan));
404         outb((val >> 8) & 0xff, dev->iobase + PCL711_AO_MSB_REG(chan));
405 }
406
407 static int pcl711_ao_insn_write(struct comedi_device *dev,
408                                 struct comedi_subdevice *s,
409                                 struct comedi_insn *insn,
410                                 unsigned int *data)
411 {
412         struct pcl711_private *devpriv = dev->private;
413         unsigned int chan = CR_CHAN(insn->chanspec);
414         unsigned int val = devpriv->ao_readback[chan];
415         int i;
416
417         for (i = 0; i < insn->n; i++) {
418                 val = data[i];
419                 pcl711_ao_write(dev, chan, val);
420         }
421         devpriv->ao_readback[chan] = val;
422
423         return insn->n;
424 }
425
426 static int pcl711_ao_insn_read(struct comedi_device *dev,
427                                struct comedi_subdevice *s,
428                                struct comedi_insn *insn,
429                                unsigned int *data)
430 {
431         struct pcl711_private *devpriv = dev->private;
432         unsigned int chan = CR_CHAN(insn->chanspec);
433         int i;
434
435         for (i = 0; i < insn->n; i++)
436                 data[i] = devpriv->ao_readback[chan];
437
438         return insn->n;
439 }
440
441 /* Digital port read - Untested on 8112 */
442 static int pcl711_di_insn_bits(struct comedi_device *dev,
443                                struct comedi_subdevice *s,
444                                struct comedi_insn *insn, unsigned int *data)
445 {
446         data[1] = inb(dev->iobase + PCL711_DI_LO) |
447             (inb(dev->iobase + PCL711_DI_HI) << 8);
448
449         return insn->n;
450 }
451
452 static int pcl711_do_insn_bits(struct comedi_device *dev,
453                                struct comedi_subdevice *s,
454                                struct comedi_insn *insn,
455                                unsigned int *data)
456 {
457         unsigned int mask;
458
459         mask = comedi_dio_update_state(s, data);
460         if (mask) {
461                 if (mask & 0x00ff)
462                         outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
463                 if (mask & 0xff00)
464                         outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
465         }
466
467         data[1] = s->state;
468
469         return insn->n;
470 }
471
472 static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
473 {
474         const struct pcl711_board *board = comedi_board(dev);
475         struct pcl711_private *devpriv;
476         struct comedi_subdevice *s;
477         int ret;
478
479         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
480         if (!devpriv)
481                 return -ENOMEM;
482
483         ret = comedi_request_region(dev, it->options[0], 0x10);
484         if (ret)
485                 return ret;
486
487         if (it->options[1] && it->options[1] <= board->maxirq) {
488                 ret = request_irq(it->options[1], pcl711_interrupt, 0,
489                                   dev->board_name, dev);
490                 if (ret == 0)
491                         dev->irq = it->options[1];
492         }
493
494         ret = comedi_alloc_subdevices(dev, 4);
495         if (ret)
496                 return ret;
497
498         /* Analog Input subdevice */
499         s = &dev->subdevices[0];
500         s->type         = COMEDI_SUBD_AI;
501         s->subdev_flags = SDF_READABLE | SDF_GROUND;
502         if (board->n_aichan > 8)
503                 s->subdev_flags |= SDF_DIFF;
504         s->n_chan       = board->n_aichan;
505         s->maxdata      = 0xfff;
506         s->range_table  = board->ai_range_type;
507         s->insn_read    = pcl711_ai_insn_read;
508         if (dev->irq) {
509                 dev->read_subdev = s;
510                 s->subdev_flags |= SDF_CMD_READ;
511                 s->len_chanlist = 1;
512                 s->do_cmdtest   = pcl711_ai_cmdtest;
513                 s->do_cmd       = pcl711_ai_cmd;
514         }
515
516         /* Analog Output subdevice */
517         s = &dev->subdevices[1];
518         s->type         = COMEDI_SUBD_AO;
519         s->subdev_flags = SDF_WRITABLE;
520         s->n_chan       = board->n_aochan;
521         s->maxdata      = 0xfff;
522         s->range_table  = &range_bipolar5;
523         s->insn_write   = pcl711_ao_insn_write;
524         s->insn_read    = pcl711_ao_insn_read;
525
526         /* Digital Input subdevice */
527         s = &dev->subdevices[2];
528         s->type         = COMEDI_SUBD_DI;
529         s->subdev_flags = SDF_READABLE;
530         s->n_chan       = 16;
531         s->maxdata      = 1;
532         s->range_table  = &range_digital;
533         s->insn_bits    = pcl711_di_insn_bits;
534
535         /* Digital Output subdevice */
536         s = &dev->subdevices[3];
537         s->type         = COMEDI_SUBD_DO;
538         s->subdev_flags = SDF_WRITABLE;
539         s->n_chan       = 16;
540         s->maxdata      = 1;
541         s->range_table  = &range_digital;
542         s->insn_bits    = pcl711_do_insn_bits;
543
544         /* clear DAC */
545         pcl711_ao_write(dev, 0, 0x0);
546         pcl711_ao_write(dev, 1, 0x0);
547
548         return 0;
549 }
550
551 static struct comedi_driver pcl711_driver = {
552         .driver_name    = "pcl711",
553         .module         = THIS_MODULE,
554         .attach         = pcl711_attach,
555         .detach         = comedi_legacy_detach,
556         .board_name     = &boardtypes[0].name,
557         .num_names      = ARRAY_SIZE(boardtypes),
558         .offset         = sizeof(struct pcl711_board),
559 };
560 module_comedi_driver(pcl711_driver);
561
562 MODULE_AUTHOR("Comedi http://www.comedi.org");
563 MODULE_DESCRIPTION("Comedi low-level driver");
564 MODULE_LICENSE("GPL");