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