Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / comedi / drivers / pcmda12.c
1 /*
2  * pcmda12.c
3  * Driver for Winsystems PC-104 based PCM-D/A-12 8-channel AO board.
4  *
5  * COMEDI - Linux Control and Measurement Device Interface
6  * Copyright (C) 2006 Calin A. Culianu <calin@ajvar.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  */
18
19 /*
20  * Driver: pcmda12
21  * Description: A driver for the Winsystems PCM-D/A-12
22  * Devices: [Winsystems] PCM-D/A-12 (pcmda12)
23  * Author: Calin Culianu <calin@ajvar.org>
24  * Updated: Fri, 13 Jan 2006 12:01:01 -0500
25  * Status: works
26  *
27  * A driver for the relatively straightforward-to-program PCM-D/A-12.
28  * This board doesn't support commands, and the only way to set its
29  * analog output range is to jumper the board. As such,
30  * comedi_data_write() ignores the range value specified.
31  *
32  * The board uses 16 consecutive I/O addresses starting at the I/O port
33  * base address. Each address corresponds to the LSB then MSB of a
34  * particular channel from 0-7.
35  *
36  * Note that the board is not ISA-PNP capable and thus needs the I/O
37  * port comedi_config parameter.
38  *
39  * Note that passing a nonzero value as the second config option will
40  * enable "simultaneous xfer" mode for this board, in which AO writes
41  * will not take effect until a subsequent read of any AO channel. This
42  * is so that one can speed up programming by preloading all AO registers
43  * with values before simultaneously setting them to take effect with one
44  * read command.
45  *
46  * Configuration Options:
47  *   [0] - I/O port base address
48  *   [1] - Do Simultaneous Xfer (see description)
49  */
50
51 #include <linux/module.h>
52 #include "../comedidev.h"
53
54 /* AI range is not configurable, it's set by jumpers on the board */
55 static const struct comedi_lrange pcmda12_ranges = {
56         3, {
57                 UNI_RANGE(5),
58                 UNI_RANGE(10),
59                 BIP_RANGE(5)
60         }
61 };
62
63 struct pcmda12_private {
64         int simultaneous_xfer_mode;
65 };
66
67 static int pcmda12_ao_insn_write(struct comedi_device *dev,
68                                  struct comedi_subdevice *s,
69                                  struct comedi_insn *insn,
70                                  unsigned int *data)
71 {
72         struct pcmda12_private *devpriv = dev->private;
73         unsigned int chan = CR_CHAN(insn->chanspec);
74         unsigned int val = s->readback[chan];
75         unsigned long ioreg = dev->iobase + (chan * 2);
76         int i;
77
78         for (i = 0; i < insn->n; ++i) {
79                 val = data[i];
80                 outb(val & 0xff, ioreg);
81                 outb((val >> 8) & 0xff, ioreg + 1);
82
83                 /*
84                  * Initiate transfer if not in simultaneaous xfer
85                  * mode by reading one of the AO registers.
86                  */
87                 if (!devpriv->simultaneous_xfer_mode)
88                         inb(ioreg);
89         }
90         s->readback[chan] = val;
91
92         return insn->n;
93 }
94
95 static int pcmda12_ao_insn_read(struct comedi_device *dev,
96                                 struct comedi_subdevice *s,
97                                 struct comedi_insn *insn,
98                                 unsigned int *data)
99 {
100         struct pcmda12_private *devpriv = dev->private;
101
102         /*
103          * Initiate simultaneaous xfer mode by reading one of the
104          * AO registers. All analog outputs will then be updated.
105          */
106         if (devpriv->simultaneous_xfer_mode)
107                 inb(dev->iobase);
108
109         return comedi_readback_insn_read(dev, s, insn, data);
110 }
111
112 static void pcmda12_ao_reset(struct comedi_device *dev,
113                              struct comedi_subdevice *s)
114 {
115         int i;
116
117         for (i = 0; i < s->n_chan; ++i) {
118                 outb(0, dev->iobase + (i * 2));
119                 outb(0, dev->iobase + (i * 2) + 1);
120         }
121         /* Initiate transfer by reading one of the AO registers. */
122         inb(dev->iobase);
123 }
124
125 static int pcmda12_attach(struct comedi_device *dev,
126                           struct comedi_devconfig *it)
127 {
128         struct pcmda12_private *devpriv;
129         struct comedi_subdevice *s;
130         int ret;
131
132         ret = comedi_request_region(dev, it->options[0], 0x10);
133         if (ret)
134                 return ret;
135
136         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
137         if (!devpriv)
138                 return -ENOMEM;
139
140         devpriv->simultaneous_xfer_mode = it->options[1];
141
142         ret = comedi_alloc_subdevices(dev, 1);
143         if (ret)
144                 return ret;
145
146         s = &dev->subdevices[0];
147         s->type         = COMEDI_SUBD_AO;
148         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
149         s->n_chan       = 8;
150         s->maxdata      = 0x0fff;
151         s->range_table  = &pcmda12_ranges;
152         s->insn_write   = pcmda12_ao_insn_write;
153         s->insn_read    = pcmda12_ao_insn_read;
154
155         ret = comedi_alloc_subdev_readback(s);
156         if (ret)
157                 return ret;
158
159         pcmda12_ao_reset(dev, s);
160
161         return 0;
162 }
163
164 static struct comedi_driver pcmda12_driver = {
165         .driver_name    = "pcmda12",
166         .module         = THIS_MODULE,
167         .attach         = pcmda12_attach,
168         .detach         = comedi_legacy_detach,
169 };
170 module_comedi_driver(pcmda12_driver);
171
172 MODULE_AUTHOR("Comedi http://www.comedi.org");
173 MODULE_DESCRIPTION("Comedi low-level driver");
174 MODULE_LICENSE("GPL");