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] |
0ea0dcff HS |
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 | */ | |
96a2d5f0 | 29 | |
ce157f80 | 30 | #include <linux/module.h> |
96a2d5f0 JLS |
31 | #include "../comedidev.h" |
32 | ||
145ff35b | 33 | /* |
08f81bf1 | 34 | * Register map |
145ff35b | 35 | * |
08f81bf1 HS |
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) | |
145ff35b | 73 | */ |
96a2d5f0 | 74 | |
292090dc | 75 | struct pcl730_board { |
145ff35b HS |
76 | const char *name; |
77 | unsigned int io_range; | |
f68d07f0 | 78 | unsigned is_pcl725:1; |
207fb16d HS |
79 | unsigned is_acl7225b:1; |
80 | unsigned has_readback:1; | |
f68d07f0 | 81 | unsigned has_ttl_io:1; |
82e71174 HS |
82 | int n_subdevs; |
83 | int n_iso_out_chan; | |
84 | int n_iso_in_chan; | |
f68d07f0 | 85 | int n_ttl_chan; |
292090dc BP |
86 | }; |
87 | ||
3e0d529c | 88 | static const struct pcl730_board pcl730_boards[] = { |
a32b885e HS |
89 | { |
90 | .name = "pcl730", | |
91 | .io_range = 0x04, | |
f68d07f0 | 92 | .has_ttl_io = 1, |
82e71174 HS |
93 | .n_subdevs = 4, |
94 | .n_iso_out_chan = 16, | |
95 | .n_iso_in_chan = 16, | |
f68d07f0 | 96 | .n_ttl_chan = 16, |
a32b885e HS |
97 | }, { |
98 | .name = "iso730", | |
99 | .io_range = 0x04, | |
82e71174 HS |
100 | .n_subdevs = 4, |
101 | .n_iso_out_chan = 16, | |
102 | .n_iso_in_chan = 16, | |
f68d07f0 | 103 | .n_ttl_chan = 16, |
a32b885e HS |
104 | }, { |
105 | .name = "acl7130", | |
106 | .io_range = 0x08, | |
f68d07f0 | 107 | .has_ttl_io = 1, |
82e71174 HS |
108 | .n_subdevs = 4, |
109 | .n_iso_out_chan = 16, | |
110 | .n_iso_in_chan = 16, | |
f68d07f0 | 111 | .n_ttl_chan = 16, |
145ff35b HS |
112 | }, { |
113 | .name = "pcm3730", | |
114 | .io_range = 0x04, | |
f68d07f0 | 115 | .has_ttl_io = 1, |
82e71174 HS |
116 | .n_subdevs = 4, |
117 | .n_iso_out_chan = 8, | |
118 | .n_iso_in_chan = 8, | |
f68d07f0 HS |
119 | .n_ttl_chan = 16, |
120 | }, { | |
121 | .name = "pcl725", | |
122 | .io_range = 0x02, | |
123 | .is_pcl725 = 1, | |
82e71174 HS |
124 | .n_subdevs = 2, |
125 | .n_iso_out_chan = 8, | |
126 | .n_iso_in_chan = 8, | |
ef5838d9 HS |
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, | |
207fb16d HS |
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, | |
82e71174 HS |
151 | }, { |
152 | .name = "pcl733", | |
153 | .io_range = 0x04, | |
154 | .n_subdevs = 1, | |
155 | .n_iso_in_chan = 32, | |
85a17285 HS |
156 | }, { |
157 | .name = "pcl734", | |
158 | .io_range = 0x04, | |
159 | .n_subdevs = 1, | |
160 | .n_iso_out_chan = 32, | |
a32b885e HS |
161 | }, |
162 | }; | |
163 | ||
d015d961 HS |
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) | |
96a2d5f0 | 168 | { |
d015d961 | 169 | unsigned long reg = (unsigned long)s->private; |
b3ff824a | 170 | unsigned int mask; |
d015d961 | 171 | |
b3ff824a | 172 | mask = comedi_dio_update_state(s, data); |
d015d961 | 173 | if (mask) { |
d015d961 HS |
174 | if (mask & 0x00ff) |
175 | outb(s->state & 0xff, dev->iobase + reg); | |
9382c06e | 176 | if ((mask & 0xff00) && (s->n_chan > 8)) |
d015d961 | 177 | outb((s->state >> 8) & 0xff, dev->iobase + reg + 1); |
9382c06e | 178 | if ((mask & 0xff0000) && (s->n_chan > 16)) |
85a17285 | 179 | outb((s->state >> 16) & 0xff, dev->iobase + reg + 2); |
9382c06e | 180 | if ((mask & 0xff000000) && (s->n_chan > 24)) |
85a17285 | 181 | outb((s->state >> 24) & 0xff, dev->iobase + reg + 3); |
96a2d5f0 | 182 | } |
96a2d5f0 JLS |
183 | |
184 | data[1] = s->state; | |
185 | ||
a2714e3e | 186 | return insn->n; |
96a2d5f0 JLS |
187 | } |
188 | ||
82e71174 HS |
189 | static unsigned int pcl730_get_bits(struct comedi_device *dev, |
190 | struct comedi_subdevice *s) | |
96a2d5f0 | 191 | { |
74f14a58 | 192 | unsigned long reg = (unsigned long)s->private; |
145ff35b HS |
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); | |
82e71174 HS |
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 | } | |
74f14a58 | 205 | |
82e71174 HS |
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); | |
96a2d5f0 | 212 | |
a2714e3e | 213 | return insn->n; |
96a2d5f0 JLS |
214 | } |
215 | ||
293d5671 HS |
216 | static int pcl730_attach(struct comedi_device *dev, |
217 | struct comedi_devconfig *it) | |
96a2d5f0 | 218 | { |
6f929aeb | 219 | const struct pcl730_board *board = comedi_board(dev); |
34c43922 | 220 | struct comedi_subdevice *s; |
82e71174 | 221 | int subdev; |
8b6c5694 | 222 | int ret; |
96a2d5f0 | 223 | |
6f9aa29b HS |
224 | ret = comedi_request_region(dev, it->options[0], board->io_range); |
225 | if (ret) | |
226 | return ret; | |
96a2d5f0 | 227 | |
82e71174 | 228 | ret = comedi_alloc_subdevices(dev, board->n_subdevs); |
8b6c5694 HS |
229 | if (ret) |
230 | return ret; | |
96a2d5f0 | 231 | |
82e71174 HS |
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; | |
08f81bf1 | 243 | s->private = (void *)0; |
207fb16d HS |
244 | |
245 | /* get the initial state if supported */ | |
246 | if (board->has_readback) | |
247 | s->state = pcl730_get_bits(dev, s); | |
82e71174 HS |
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; | |
08f81bf1 HS |
259 | s->private = board->is_acl7225b ? (void *)2 : |
260 | board->is_pcl725 ? (void *)1 : (void *)0; | |
82e71174 | 261 | } |
f68d07f0 HS |
262 | |
263 | if (board->has_ttl_io) { | |
264 | /* TTL Digital Outputs */ | |
82e71174 | 265 | s = &dev->subdevices[subdev++]; |
f68d07f0 HS |
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; | |
08f81bf1 | 272 | s->private = (void *)2; |
f68d07f0 HS |
273 | |
274 | /* TTL Digital Inputs */ | |
82e71174 | 275 | s = &dev->subdevices[subdev++]; |
f68d07f0 HS |
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; | |
08f81bf1 | 282 | s->private = (void *)2; |
f68d07f0 | 283 | } |
96a2d5f0 JLS |
284 | |
285 | return 0; | |
286 | } | |
287 | ||
294f930d | 288 | static struct comedi_driver pcl730_driver = { |
4c6dacbd HS |
289 | .driver_name = "pcl730", |
290 | .module = THIS_MODULE, | |
291 | .attach = pcl730_attach, | |
21208519 | 292 | .detach = comedi_legacy_detach, |
3e0d529c HS |
293 | .board_name = &pcl730_boards[0].name, |
294 | .num_names = ARRAY_SIZE(pcl730_boards), | |
4c6dacbd HS |
295 | .offset = sizeof(struct pcl730_board), |
296 | }; | |
294f930d | 297 | module_comedi_driver(pcl730_driver); |
4c6dacbd | 298 | |
90f703d3 AT |
299 | MODULE_AUTHOR("Comedi http://www.comedi.org"); |
300 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
301 | MODULE_LICENSE("GPL"); |