Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / comedi / drivers / pcl730.c
1 /*
2  * comedi/drivers/pcl730.c
3  * Driver for Advantech PCL-730 and clones
4  * José Luis Sánchez
5  */
6
7 /*
8  * Driver: pcl730
9  * Description: Advantech PCL-730 (& compatibles)
10  * Devices: [Advantech] PCL-730 (pcl730), PCM-3730 (pcm3730), PCL-725 (pcl725),
11  *   PCL-733 (pcl733), PCL-734 (pcl734),
12  *   [ADLink] ACL-7130 (acl7130), ACL-7225b (acl7225b),
13  *   [ICP] ISO-730 (iso730), P8R8-DIO (p8r8dio), P16R16-DIO (p16r16dio),
14  *   [Diamond Systems] OPMM-1616-XT (opmm-1616-xt), PEARL-MM-P (pearl-mm-p),
15  *   IR104-PBF (ir104-pbf),
16  * Author: José Luis Sánchez (jsanchezv@teleline.es)
17  * Status: untested
18  *
19  * Configuration options:
20  *   [0] - I/O port base
21  *
22  * Interrupts are not supported.
23  * The ACL-7130 card has an 8254 timer/counter not supported by this driver.
24  */
25
26 #include <linux/module.h>
27 #include "../comedidev.h"
28
29 /*
30  * Register map
31  *
32  * The register map varies slightly depending on the board type but
33  * all registers are 8-bit.
34  *
35  * The boardinfo 'io_range' is used to allow comedi to request the
36  * proper range required by the board.
37  *
38  * The comedi_subdevice 'private' data is used to pass the register
39  * offset to the (*insn_bits) functions to read/write the correct
40  * registers.
41  *
42  * The basic register mapping looks like this:
43  *
44  *     BASE+0  Isolated outputs 0-7 (write) / inputs 0-7 (read)
45  *     BASE+1  Isolated outputs 8-15 (write) / inputs 8-15 (read)
46  *     BASE+2  TTL outputs 0-7 (write) / inputs 0-7 (read)
47  *     BASE+3  TTL outputs 8-15 (write) / inputs 8-15 (read)
48  *
49  * The pcm3730 board does not have register BASE+1.
50  *
51  * The pcl725 and p8r8dio only have registers BASE+0 and BASE+1:
52  *
53  *     BASE+0  Isolated outputs 0-7 (write) (read back on p8r8dio)
54  *     BASE+1  Isolated inputs 0-7 (read)
55  *
56  * The acl7225b and p16r16dio boards have this register mapping:
57  *
58  *     BASE+0  Isolated outputs 0-7 (write) (read back)
59  *     BASE+1  Isolated outputs 8-15 (write) (read back)
60  *     BASE+2  Isolated inputs 0-7 (read)
61  *     BASE+3  Isolated inputs 8-15 (read)
62  *
63  * The pcl733 and pcl733 boards have this register mapping:
64  *
65  *     BASE+0  Isolated outputs 0-7 (write) or inputs 0-7 (read)
66  *     BASE+1  Isolated outputs 8-15 (write) or inputs 8-15 (read)
67  *     BASE+2  Isolated outputs 16-23 (write) or inputs 16-23 (read)
68  *     BASE+3  Isolated outputs 24-31 (write) or inputs 24-31 (read)
69  *
70  * The opmm-1616-xt board has this register mapping:
71  *
72  *     BASE+0  Isolated outputs 0-7 (write) (read back)
73  *     BASE+1  Isolated outputs 8-15 (write) (read back)
74  *     BASE+2  Isolated inputs 0-7 (read)
75  *     BASE+3  Isolated inputs 8-15 (read)
76  *
77  *     These registers are not currently supported:
78  *
79  *     BASE+2  Relay select register (write)
80  *     BASE+3  Board reset control register (write)
81  *     BASE+4  Interrupt control register (write)
82  *     BASE+4  Change detect 7-0 status register (read)
83  *     BASE+5  LED control register (write)
84  *     BASE+5  Change detect 15-8 status register (read)
85  *
86  * The pearl-mm-p board has this register mapping:
87  *
88  *     BASE+0  Isolated outputs 0-7 (write)
89  *     BASE+1  Isolated outputs 8-15 (write)
90  *
91  * The ir104-pbf board has this register mapping:
92  *
93  *     BASE+0  Isolated outputs 0-7 (write) (read back)
94  *     BASE+1  Isolated outputs 8-15 (write) (read back)
95  *     BASE+2  Isolated outputs 16-19 (write) (read back)
96  *     BASE+4  Isolated inputs 0-7 (read)
97  *     BASE+5  Isolated inputs 8-15 (read)
98  *     BASE+6  Isolated inputs 16-19 (read)
99  */
100
101 struct pcl730_board {
102         const char *name;
103         unsigned int io_range;
104         unsigned is_pcl725:1;
105         unsigned is_acl7225b:1;
106         unsigned is_ir104:1;
107         unsigned has_readback:1;
108         unsigned has_ttl_io:1;
109         int n_subdevs;
110         int n_iso_out_chan;
111         int n_iso_in_chan;
112         int n_ttl_chan;
113 };
114
115 static const struct pcl730_board pcl730_boards[] = {
116         {
117                 .name           = "pcl730",
118                 .io_range       = 0x04,
119                 .has_ttl_io     = 1,
120                 .n_subdevs      = 4,
121                 .n_iso_out_chan = 16,
122                 .n_iso_in_chan  = 16,
123                 .n_ttl_chan     = 16,
124         }, {
125                 .name           = "iso730",
126                 .io_range       = 0x04,
127                 .n_subdevs      = 4,
128                 .n_iso_out_chan = 16,
129                 .n_iso_in_chan  = 16,
130                 .n_ttl_chan     = 16,
131         }, {
132                 .name           = "acl7130",
133                 .io_range       = 0x08,
134                 .has_ttl_io     = 1,
135                 .n_subdevs      = 4,
136                 .n_iso_out_chan = 16,
137                 .n_iso_in_chan  = 16,
138                 .n_ttl_chan     = 16,
139         }, {
140                 .name           = "pcm3730",
141                 .io_range       = 0x04,
142                 .has_ttl_io     = 1,
143                 .n_subdevs      = 4,
144                 .n_iso_out_chan = 8,
145                 .n_iso_in_chan  = 8,
146                 .n_ttl_chan     = 16,
147         }, {
148                 .name           = "pcl725",
149                 .io_range       = 0x02,
150                 .is_pcl725      = 1,
151                 .n_subdevs      = 2,
152                 .n_iso_out_chan = 8,
153                 .n_iso_in_chan  = 8,
154         }, {
155                 .name           = "p8r8dio",
156                 .io_range       = 0x02,
157                 .is_pcl725      = 1,
158                 .has_readback   = 1,
159                 .n_subdevs      = 2,
160                 .n_iso_out_chan = 8,
161                 .n_iso_in_chan  = 8,
162         }, {
163                 .name           = "acl7225b",
164                 .io_range       = 0x08,         /* only 4 are used */
165                 .is_acl7225b    = 1,
166                 .has_readback   = 1,
167                 .n_subdevs      = 2,
168                 .n_iso_out_chan = 16,
169                 .n_iso_in_chan  = 16,
170         }, {
171                 .name           = "p16r16dio",
172                 .io_range       = 0x04,
173                 .is_acl7225b    = 1,
174                 .has_readback   = 1,
175                 .n_subdevs      = 2,
176                 .n_iso_out_chan = 16,
177                 .n_iso_in_chan  = 16,
178         }, {
179                 .name           = "pcl733",
180                 .io_range       = 0x04,
181                 .n_subdevs      = 1,
182                 .n_iso_in_chan  = 32,
183         }, {
184                 .name           = "pcl734",
185                 .io_range       = 0x04,
186                 .n_subdevs      = 1,
187                 .n_iso_out_chan = 32,
188         }, {
189                 .name           = "opmm-1616-xt",
190                 .io_range       = 0x10,
191                 .is_acl7225b    = 1,
192                 .has_readback   = 1,
193                 .n_subdevs      = 2,
194                 .n_iso_out_chan = 16,
195                 .n_iso_in_chan  = 16,
196         }, {
197                 .name           = "pearl-mm-p",
198                 .io_range       = 0x02,
199                 .n_subdevs      = 1,
200                 .n_iso_out_chan = 16,
201         }, {
202                 .name           = "ir104-pbf",
203                 .io_range       = 0x08,
204                 .is_ir104       = 1,
205                 .has_readback   = 1,
206                 .n_iso_out_chan = 20,
207                 .n_iso_in_chan  = 20,
208         },
209 };
210
211 static int pcl730_do_insn_bits(struct comedi_device *dev,
212                                struct comedi_subdevice *s,
213                                struct comedi_insn *insn,
214                                unsigned int *data)
215 {
216         unsigned long reg = (unsigned long)s->private;
217         unsigned int mask;
218
219         mask = comedi_dio_update_state(s, data);
220         if (mask) {
221                 if (mask & 0x00ff)
222                         outb(s->state & 0xff, dev->iobase + reg);
223                 if ((mask & 0xff00) && (s->n_chan > 8))
224                         outb((s->state >> 8) & 0xff, dev->iobase + reg + 1);
225                 if ((mask & 0xff0000) && (s->n_chan > 16))
226                         outb((s->state >> 16) & 0xff, dev->iobase + reg + 2);
227                 if ((mask & 0xff000000) && (s->n_chan > 24))
228                         outb((s->state >> 24) & 0xff, dev->iobase + reg + 3);
229         }
230
231         data[1] = s->state;
232
233         return insn->n;
234 }
235
236 static unsigned int pcl730_get_bits(struct comedi_device *dev,
237                                     struct comedi_subdevice *s)
238 {
239         unsigned long reg = (unsigned long)s->private;
240         unsigned int val;
241
242         val = inb(dev->iobase + reg);
243         if (s->n_chan > 8)
244                 val |= (inb(dev->iobase + reg + 1) << 8);
245         if (s->n_chan > 16)
246                 val |= (inb(dev->iobase + reg + 2) << 16);
247         if (s->n_chan > 24)
248                 val |= (inb(dev->iobase + reg + 3) << 24);
249
250         return val;
251 }
252
253 static int pcl730_di_insn_bits(struct comedi_device *dev,
254                                struct comedi_subdevice *s,
255                                struct comedi_insn *insn,
256                                unsigned int *data)
257 {
258         data[1] = pcl730_get_bits(dev, s);
259
260         return insn->n;
261 }
262
263 static int pcl730_attach(struct comedi_device *dev,
264                          struct comedi_devconfig *it)
265 {
266         const struct pcl730_board *board = dev->board_ptr;
267         struct comedi_subdevice *s;
268         int subdev;
269         int ret;
270
271         ret = comedi_request_region(dev, it->options[0], board->io_range);
272         if (ret)
273                 return ret;
274
275         ret = comedi_alloc_subdevices(dev, board->n_subdevs);
276         if (ret)
277                 return ret;
278
279         subdev = 0;
280
281         if (board->n_iso_out_chan) {
282                 /* Isolated Digital Outputs */
283                 s = &dev->subdevices[subdev++];
284                 s->type         = COMEDI_SUBD_DO;
285                 s->subdev_flags = SDF_WRITABLE;
286                 s->n_chan       = board->n_iso_out_chan;
287                 s->maxdata      = 1;
288                 s->range_table  = &range_digital;
289                 s->insn_bits    = pcl730_do_insn_bits;
290                 s->private      = (void *)0;
291
292                 /* get the initial state if supported */
293                 if (board->has_readback)
294                         s->state = pcl730_get_bits(dev, s);
295         }
296
297         if (board->n_iso_in_chan) {
298                 /* Isolated Digital Inputs */
299                 s = &dev->subdevices[subdev++];
300                 s->type         = COMEDI_SUBD_DI;
301                 s->subdev_flags = SDF_READABLE;
302                 s->n_chan       = board->n_iso_in_chan;
303                 s->maxdata      = 1;
304                 s->range_table  = &range_digital;
305                 s->insn_bits    = pcl730_di_insn_bits;
306                 s->private      = board->is_ir104 ? (void *)4 :
307                                   board->is_acl7225b ? (void *)2 :
308                                   board->is_pcl725 ? (void *)1 : (void *)0;
309         }
310
311         if (board->has_ttl_io) {
312                 /* TTL Digital Outputs */
313                 s = &dev->subdevices[subdev++];
314                 s->type         = COMEDI_SUBD_DO;
315                 s->subdev_flags = SDF_WRITABLE;
316                 s->n_chan       = board->n_ttl_chan;
317                 s->maxdata      = 1;
318                 s->range_table  = &range_digital;
319                 s->insn_bits    = pcl730_do_insn_bits;
320                 s->private      = (void *)2;
321
322                 /* TTL Digital Inputs */
323                 s = &dev->subdevices[subdev++];
324                 s->type         = COMEDI_SUBD_DI;
325                 s->subdev_flags = SDF_READABLE;
326                 s->n_chan       = board->n_ttl_chan;
327                 s->maxdata      = 1;
328                 s->range_table  = &range_digital;
329                 s->insn_bits    = pcl730_di_insn_bits;
330                 s->private      = (void *)2;
331         }
332
333         return 0;
334 }
335
336 static struct comedi_driver pcl730_driver = {
337         .driver_name    = "pcl730",
338         .module         = THIS_MODULE,
339         .attach         = pcl730_attach,
340         .detach         = comedi_legacy_detach,
341         .board_name     = &pcl730_boards[0].name,
342         .num_names      = ARRAY_SIZE(pcl730_boards),
343         .offset         = sizeof(struct pcl730_board),
344 };
345 module_comedi_driver(pcl730_driver);
346
347 MODULE_AUTHOR("Comedi http://www.comedi.org");
348 MODULE_DESCRIPTION("Comedi low-level driver");
349 MODULE_LICENSE("GPL");