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