]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/pcl730.c
Merge branch 'patchwork' into to_next
[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 <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 = data[0];
171         unsigned int bits = data[1];
172
173         if (mask) {
174                 s->state &= ~mask;
175                 s->state |= (bits & mask);
176
177                 if (mask & 0x00ff)
178                         outb(s->state & 0xff, dev->iobase + reg);
179                 if ((mask & 0xff00) && (s->n_chan > 8))
180                         outb((s->state >> 8) & 0xff, dev->iobase + reg + 1);
181                 if ((mask & 0xff0000) && (s->n_chan > 16))
182                         outb((s->state >> 16) & 0xff, dev->iobase + reg + 2);
183                 if ((mask & 0xff000000) && (s->n_chan > 24))
184                         outb((s->state >> 24) & 0xff, dev->iobase + reg + 3);
185         }
186
187         data[1] = s->state;
188
189         return insn->n;
190 }
191
192 static unsigned int pcl730_get_bits(struct comedi_device *dev,
193                                     struct comedi_subdevice *s)
194 {
195         unsigned long reg = (unsigned long)s->private;
196         unsigned int val;
197
198         val = inb(dev->iobase + reg);
199         if (s->n_chan > 8)
200                 val |= (inb(dev->iobase + reg + 1) << 8);
201         if (s->n_chan > 16)
202                 val |= (inb(dev->iobase + reg + 2) << 16);
203         if (s->n_chan > 24)
204                 val |= (inb(dev->iobase + reg + 3) << 24);
205
206         return val;
207 }
208
209 static int pcl730_di_insn_bits(struct comedi_device *dev,
210                                struct comedi_subdevice *s,
211                                struct comedi_insn *insn,
212                                unsigned int *data)
213 {
214         data[1] = pcl730_get_bits(dev, s);
215
216         return insn->n;
217 }
218
219 static int pcl730_attach(struct comedi_device *dev,
220                          struct comedi_devconfig *it)
221 {
222         const struct pcl730_board *board = comedi_board(dev);
223         struct comedi_subdevice *s;
224         int subdev;
225         int ret;
226
227         ret = comedi_request_region(dev, it->options[0], board->io_range);
228         if (ret)
229                 return ret;
230
231         ret = comedi_alloc_subdevices(dev, board->n_subdevs);
232         if (ret)
233                 return ret;
234
235         subdev = 0;
236
237         if (board->n_iso_out_chan) {
238                 /* Isolated Digital Outputs */
239                 s = &dev->subdevices[subdev++];
240                 s->type         = COMEDI_SUBD_DO;
241                 s->subdev_flags = SDF_WRITABLE;
242                 s->n_chan       = board->n_iso_out_chan;
243                 s->maxdata      = 1;
244                 s->range_table  = &range_digital;
245                 s->insn_bits    = pcl730_do_insn_bits;
246                 s->private      = (void *)0;
247
248                 /* get the initial state if supported */
249                 if (board->has_readback)
250                         s->state = pcl730_get_bits(dev, s);
251         }
252
253         if (board->n_iso_in_chan) {
254                 /* Isolated Digital Inputs */
255                 s = &dev->subdevices[subdev++];
256                 s->type         = COMEDI_SUBD_DI;
257                 s->subdev_flags = SDF_READABLE;
258                 s->n_chan       = board->n_iso_in_chan;
259                 s->maxdata      = 1;
260                 s->range_table  = &range_digital;
261                 s->insn_bits    = pcl730_di_insn_bits;
262                 s->private      = board->is_acl7225b ? (void *)2 :
263                                   board->is_pcl725 ? (void *)1 : (void *)0;
264         }
265
266         if (board->has_ttl_io) {
267                 /* TTL Digital Outputs */
268                 s = &dev->subdevices[subdev++];
269                 s->type         = COMEDI_SUBD_DO;
270                 s->subdev_flags = SDF_WRITABLE;
271                 s->n_chan       = board->n_ttl_chan;
272                 s->maxdata      = 1;
273                 s->range_table  = &range_digital;
274                 s->insn_bits    = pcl730_do_insn_bits;
275                 s->private      = (void *)2;
276
277                 /* TTL Digital Inputs */
278                 s = &dev->subdevices[subdev++];
279                 s->type         = COMEDI_SUBD_DI;
280                 s->subdev_flags = SDF_READABLE;
281                 s->n_chan       = board->n_ttl_chan;
282                 s->maxdata      = 1;
283                 s->range_table  = &range_digital;
284                 s->insn_bits    = pcl730_di_insn_bits;
285                 s->private      = (void *)2;
286         }
287
288         return 0;
289 }
290
291 static struct comedi_driver pcl730_driver = {
292         .driver_name    = "pcl730",
293         .module         = THIS_MODULE,
294         .attach         = pcl730_attach,
295         .detach         = comedi_legacy_detach,
296         .board_name     = &pcl730_boards[0].name,
297         .num_names      = ARRAY_SIZE(pcl730_boards),
298         .offset         = sizeof(struct pcl730_board),
299 };
300 module_comedi_driver(pcl730_driver);
301
302 MODULE_AUTHOR("Comedi http://www.comedi.org");
303 MODULE_DESCRIPTION("Comedi low-level driver");
304 MODULE_LICENSE("GPL");