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