Commit | Line | Data |
---|---|---|
96a2d5f0 JLS |
1 | /* |
2 | * comedi/drivers/pcl730.c | |
3 | * Driver for Advantech PCL-730 and clones | |
4 | * José Luis Sánchez | |
5 | */ | |
96a2d5f0 | 6 | |
0ea0dcff HS |
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] | |
145ff35b | 13 | * (Advantech) PCM-3730 [pcm3730] |
f68d07f0 | 14 | * (Advantech) PCL-725 [pcl725] |
ef5838d9 | 15 | * (ICP) P8R8-DIO [p16r16dio] |
207fb16d HS |
16 | * (Adlink) ACL-7225b [acl7225b] |
17 | * (ICP) P16R16-DIO [p16r16dio] | |
82e71174 | 18 | * (Advantech) PCL-733 [pcl733] |
85a17285 | 19 | * (Advantech) PCL-734 [pcl734] |
53030b13 HS |
20 | * (Diamond Systems) OPMM-1616-XT [opmm-1616-xt] |
21 | * (Diamond Systems) PEARL-MM-P [prearl-mm-p] | |
0ea0dcff HS |
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 | */ | |
96a2d5f0 | 31 | |
ce157f80 | 32 | #include <linux/module.h> |
96a2d5f0 JLS |
33 | #include "../comedidev.h" |
34 | ||
145ff35b | 35 | /* |
08f81bf1 | 36 | * Register map |
145ff35b | 37 | * |
08f81bf1 HS |
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) | |
53030b13 HS |
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) | |
145ff35b | 96 | */ |
96a2d5f0 | 97 | |
292090dc | 98 | struct pcl730_board { |
145ff35b HS |
99 | const char *name; |
100 | unsigned int io_range; | |
f68d07f0 | 101 | unsigned is_pcl725:1; |
207fb16d HS |
102 | unsigned is_acl7225b:1; |
103 | unsigned has_readback:1; | |
f68d07f0 | 104 | unsigned has_ttl_io:1; |
82e71174 HS |
105 | int n_subdevs; |
106 | int n_iso_out_chan; | |
107 | int n_iso_in_chan; | |
f68d07f0 | 108 | int n_ttl_chan; |
292090dc BP |
109 | }; |
110 | ||
3e0d529c | 111 | static const struct pcl730_board pcl730_boards[] = { |
a32b885e HS |
112 | { |
113 | .name = "pcl730", | |
114 | .io_range = 0x04, | |
f68d07f0 | 115 | .has_ttl_io = 1, |
82e71174 HS |
116 | .n_subdevs = 4, |
117 | .n_iso_out_chan = 16, | |
118 | .n_iso_in_chan = 16, | |
f68d07f0 | 119 | .n_ttl_chan = 16, |
a32b885e HS |
120 | }, { |
121 | .name = "iso730", | |
122 | .io_range = 0x04, | |
82e71174 HS |
123 | .n_subdevs = 4, |
124 | .n_iso_out_chan = 16, | |
125 | .n_iso_in_chan = 16, | |
f68d07f0 | 126 | .n_ttl_chan = 16, |
a32b885e HS |
127 | }, { |
128 | .name = "acl7130", | |
129 | .io_range = 0x08, | |
f68d07f0 | 130 | .has_ttl_io = 1, |
82e71174 HS |
131 | .n_subdevs = 4, |
132 | .n_iso_out_chan = 16, | |
133 | .n_iso_in_chan = 16, | |
f68d07f0 | 134 | .n_ttl_chan = 16, |
145ff35b HS |
135 | }, { |
136 | .name = "pcm3730", | |
137 | .io_range = 0x04, | |
f68d07f0 | 138 | .has_ttl_io = 1, |
82e71174 HS |
139 | .n_subdevs = 4, |
140 | .n_iso_out_chan = 8, | |
141 | .n_iso_in_chan = 8, | |
f68d07f0 HS |
142 | .n_ttl_chan = 16, |
143 | }, { | |
144 | .name = "pcl725", | |
145 | .io_range = 0x02, | |
146 | .is_pcl725 = 1, | |
82e71174 HS |
147 | .n_subdevs = 2, |
148 | .n_iso_out_chan = 8, | |
149 | .n_iso_in_chan = 8, | |
ef5838d9 HS |
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, | |
207fb16d HS |
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, | |
82e71174 HS |
174 | }, { |
175 | .name = "pcl733", | |
176 | .io_range = 0x04, | |
177 | .n_subdevs = 1, | |
178 | .n_iso_in_chan = 32, | |
85a17285 HS |
179 | }, { |
180 | .name = "pcl734", | |
181 | .io_range = 0x04, | |
182 | .n_subdevs = 1, | |
183 | .n_iso_out_chan = 32, | |
53030b13 HS |
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, | |
a32b885e HS |
197 | }, |
198 | }; | |
199 | ||
d015d961 HS |
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) | |
96a2d5f0 | 204 | { |
d015d961 | 205 | unsigned long reg = (unsigned long)s->private; |
b3ff824a | 206 | unsigned int mask; |
d015d961 | 207 | |
b3ff824a | 208 | mask = comedi_dio_update_state(s, data); |
d015d961 | 209 | if (mask) { |
d015d961 HS |
210 | if (mask & 0x00ff) |
211 | outb(s->state & 0xff, dev->iobase + reg); | |
9382c06e | 212 | if ((mask & 0xff00) && (s->n_chan > 8)) |
d015d961 | 213 | outb((s->state >> 8) & 0xff, dev->iobase + reg + 1); |
9382c06e | 214 | if ((mask & 0xff0000) && (s->n_chan > 16)) |
85a17285 | 215 | outb((s->state >> 16) & 0xff, dev->iobase + reg + 2); |
9382c06e | 216 | if ((mask & 0xff000000) && (s->n_chan > 24)) |
85a17285 | 217 | outb((s->state >> 24) & 0xff, dev->iobase + reg + 3); |
96a2d5f0 | 218 | } |
96a2d5f0 JLS |
219 | |
220 | data[1] = s->state; | |
221 | ||
a2714e3e | 222 | return insn->n; |
96a2d5f0 JLS |
223 | } |
224 | ||
82e71174 HS |
225 | static unsigned int pcl730_get_bits(struct comedi_device *dev, |
226 | struct comedi_subdevice *s) | |
96a2d5f0 | 227 | { |
74f14a58 | 228 | unsigned long reg = (unsigned long)s->private; |
145ff35b HS |
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); | |
82e71174 HS |
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 | } | |
74f14a58 | 241 | |
82e71174 HS |
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); | |
96a2d5f0 | 248 | |
a2714e3e | 249 | return insn->n; |
96a2d5f0 JLS |
250 | } |
251 | ||
293d5671 HS |
252 | static int pcl730_attach(struct comedi_device *dev, |
253 | struct comedi_devconfig *it) | |
96a2d5f0 | 254 | { |
6f929aeb | 255 | const struct pcl730_board *board = comedi_board(dev); |
34c43922 | 256 | struct comedi_subdevice *s; |
82e71174 | 257 | int subdev; |
8b6c5694 | 258 | int ret; |
96a2d5f0 | 259 | |
6f9aa29b HS |
260 | ret = comedi_request_region(dev, it->options[0], board->io_range); |
261 | if (ret) | |
262 | return ret; | |
96a2d5f0 | 263 | |
82e71174 | 264 | ret = comedi_alloc_subdevices(dev, board->n_subdevs); |
8b6c5694 HS |
265 | if (ret) |
266 | return ret; | |
96a2d5f0 | 267 | |
82e71174 HS |
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; | |
08f81bf1 | 279 | s->private = (void *)0; |
207fb16d HS |
280 | |
281 | /* get the initial state if supported */ | |
282 | if (board->has_readback) | |
283 | s->state = pcl730_get_bits(dev, s); | |
82e71174 HS |
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; | |
08f81bf1 HS |
295 | s->private = board->is_acl7225b ? (void *)2 : |
296 | board->is_pcl725 ? (void *)1 : (void *)0; | |
82e71174 | 297 | } |
f68d07f0 HS |
298 | |
299 | if (board->has_ttl_io) { | |
300 | /* TTL Digital Outputs */ | |
82e71174 | 301 | s = &dev->subdevices[subdev++]; |
f68d07f0 HS |
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; | |
08f81bf1 | 308 | s->private = (void *)2; |
f68d07f0 HS |
309 | |
310 | /* TTL Digital Inputs */ | |
82e71174 | 311 | s = &dev->subdevices[subdev++]; |
f68d07f0 HS |
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; | |
08f81bf1 | 318 | s->private = (void *)2; |
f68d07f0 | 319 | } |
96a2d5f0 JLS |
320 | |
321 | return 0; | |
322 | } | |
323 | ||
294f930d | 324 | static struct comedi_driver pcl730_driver = { |
4c6dacbd HS |
325 | .driver_name = "pcl730", |
326 | .module = THIS_MODULE, | |
327 | .attach = pcl730_attach, | |
21208519 | 328 | .detach = comedi_legacy_detach, |
3e0d529c HS |
329 | .board_name = &pcl730_boards[0].name, |
330 | .num_names = ARRAY_SIZE(pcl730_boards), | |
4c6dacbd HS |
331 | .offset = sizeof(struct pcl730_board), |
332 | }; | |
294f930d | 333 | module_comedi_driver(pcl730_driver); |
4c6dacbd | 334 | |
90f703d3 AT |
335 | MODULE_AUTHOR("Comedi http://www.comedi.org"); |
336 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
337 | MODULE_LICENSE("GPL"); |