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