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