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