162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * comedi/drivers/pcl730.c 462306a36Sopenharmony_ci * Driver for Advantech PCL-730 and clones 562306a36Sopenharmony_ci * José Luis Sánchez 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * Driver: pcl730 1062306a36Sopenharmony_ci * Description: Advantech PCL-730 (& compatibles) 1162306a36Sopenharmony_ci * Devices: [Advantech] PCL-730 (pcl730), PCM-3730 (pcm3730), PCL-725 (pcl725), 1262306a36Sopenharmony_ci * PCL-733 (pcl733), PCL-734 (pcl734), 1362306a36Sopenharmony_ci * [ADLink] ACL-7130 (acl7130), ACL-7225b (acl7225b), 1462306a36Sopenharmony_ci * [ICP] ISO-730 (iso730), P8R8-DIO (p8r8dio), P16R16-DIO (p16r16dio), 1562306a36Sopenharmony_ci * [Diamond Systems] OPMM-1616-XT (opmm-1616-xt), PEARL-MM-P (pearl-mm-p), 1662306a36Sopenharmony_ci * IR104-PBF (ir104-pbf), 1762306a36Sopenharmony_ci * Author: José Luis Sánchez (jsanchezv@teleline.es) 1862306a36Sopenharmony_ci * Status: untested 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Configuration options: 2162306a36Sopenharmony_ci * [0] - I/O port base 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Interrupts are not supported. 2462306a36Sopenharmony_ci * The ACL-7130 card has an 8254 timer/counter not supported by this driver. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <linux/module.h> 2862306a36Sopenharmony_ci#include <linux/comedi/comedidev.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * Register map 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * The register map varies slightly depending on the board type but 3462306a36Sopenharmony_ci * all registers are 8-bit. 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * The boardinfo 'io_range' is used to allow comedi to request the 3762306a36Sopenharmony_ci * proper range required by the board. 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * The comedi_subdevice 'private' data is used to pass the register 4062306a36Sopenharmony_ci * offset to the (*insn_bits) functions to read/write the correct 4162306a36Sopenharmony_ci * registers. 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * The basic register mapping looks like this: 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * BASE+0 Isolated outputs 0-7 (write) / inputs 0-7 (read) 4662306a36Sopenharmony_ci * BASE+1 Isolated outputs 8-15 (write) / inputs 8-15 (read) 4762306a36Sopenharmony_ci * BASE+2 TTL outputs 0-7 (write) / inputs 0-7 (read) 4862306a36Sopenharmony_ci * BASE+3 TTL outputs 8-15 (write) / inputs 8-15 (read) 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * The pcm3730 board does not have register BASE+1. 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * The pcl725 and p8r8dio only have registers BASE+0 and BASE+1: 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci * BASE+0 Isolated outputs 0-7 (write) (read back on p8r8dio) 5562306a36Sopenharmony_ci * BASE+1 Isolated inputs 0-7 (read) 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * The acl7225b and p16r16dio boards have this register mapping: 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * BASE+0 Isolated outputs 0-7 (write) (read back) 6062306a36Sopenharmony_ci * BASE+1 Isolated outputs 8-15 (write) (read back) 6162306a36Sopenharmony_ci * BASE+2 Isolated inputs 0-7 (read) 6262306a36Sopenharmony_ci * BASE+3 Isolated inputs 8-15 (read) 6362306a36Sopenharmony_ci * 6462306a36Sopenharmony_ci * The pcl733 and pcl733 boards have this register mapping: 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci * BASE+0 Isolated outputs 0-7 (write) or inputs 0-7 (read) 6762306a36Sopenharmony_ci * BASE+1 Isolated outputs 8-15 (write) or inputs 8-15 (read) 6862306a36Sopenharmony_ci * BASE+2 Isolated outputs 16-23 (write) or inputs 16-23 (read) 6962306a36Sopenharmony_ci * BASE+3 Isolated outputs 24-31 (write) or inputs 24-31 (read) 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * The opmm-1616-xt board has this register mapping: 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * BASE+0 Isolated outputs 0-7 (write) (read back) 7462306a36Sopenharmony_ci * BASE+1 Isolated outputs 8-15 (write) (read back) 7562306a36Sopenharmony_ci * BASE+2 Isolated inputs 0-7 (read) 7662306a36Sopenharmony_ci * BASE+3 Isolated inputs 8-15 (read) 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * These registers are not currently supported: 7962306a36Sopenharmony_ci * 8062306a36Sopenharmony_ci * BASE+2 Relay select register (write) 8162306a36Sopenharmony_ci * BASE+3 Board reset control register (write) 8262306a36Sopenharmony_ci * BASE+4 Interrupt control register (write) 8362306a36Sopenharmony_ci * BASE+4 Change detect 7-0 status register (read) 8462306a36Sopenharmony_ci * BASE+5 LED control register (write) 8562306a36Sopenharmony_ci * BASE+5 Change detect 15-8 status register (read) 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * The pearl-mm-p board has this register mapping: 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * BASE+0 Isolated outputs 0-7 (write) 9062306a36Sopenharmony_ci * BASE+1 Isolated outputs 8-15 (write) 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * The ir104-pbf board has this register mapping: 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * BASE+0 Isolated outputs 0-7 (write) (read back) 9562306a36Sopenharmony_ci * BASE+1 Isolated outputs 8-15 (write) (read back) 9662306a36Sopenharmony_ci * BASE+2 Isolated outputs 16-19 (write) (read back) 9762306a36Sopenharmony_ci * BASE+4 Isolated inputs 0-7 (read) 9862306a36Sopenharmony_ci * BASE+5 Isolated inputs 8-15 (read) 9962306a36Sopenharmony_ci * BASE+6 Isolated inputs 16-19 (read) 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct pcl730_board { 10362306a36Sopenharmony_ci const char *name; 10462306a36Sopenharmony_ci unsigned int io_range; 10562306a36Sopenharmony_ci unsigned is_pcl725:1; 10662306a36Sopenharmony_ci unsigned is_acl7225b:1; 10762306a36Sopenharmony_ci unsigned is_ir104:1; 10862306a36Sopenharmony_ci unsigned has_readback:1; 10962306a36Sopenharmony_ci unsigned has_ttl_io:1; 11062306a36Sopenharmony_ci int n_subdevs; 11162306a36Sopenharmony_ci int n_iso_out_chan; 11262306a36Sopenharmony_ci int n_iso_in_chan; 11362306a36Sopenharmony_ci int n_ttl_chan; 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic const struct pcl730_board pcl730_boards[] = { 11762306a36Sopenharmony_ci { 11862306a36Sopenharmony_ci .name = "pcl730", 11962306a36Sopenharmony_ci .io_range = 0x04, 12062306a36Sopenharmony_ci .has_ttl_io = 1, 12162306a36Sopenharmony_ci .n_subdevs = 4, 12262306a36Sopenharmony_ci .n_iso_out_chan = 16, 12362306a36Sopenharmony_ci .n_iso_in_chan = 16, 12462306a36Sopenharmony_ci .n_ttl_chan = 16, 12562306a36Sopenharmony_ci }, { 12662306a36Sopenharmony_ci .name = "iso730", 12762306a36Sopenharmony_ci .io_range = 0x04, 12862306a36Sopenharmony_ci .n_subdevs = 4, 12962306a36Sopenharmony_ci .n_iso_out_chan = 16, 13062306a36Sopenharmony_ci .n_iso_in_chan = 16, 13162306a36Sopenharmony_ci .n_ttl_chan = 16, 13262306a36Sopenharmony_ci }, { 13362306a36Sopenharmony_ci .name = "acl7130", 13462306a36Sopenharmony_ci .io_range = 0x08, 13562306a36Sopenharmony_ci .has_ttl_io = 1, 13662306a36Sopenharmony_ci .n_subdevs = 4, 13762306a36Sopenharmony_ci .n_iso_out_chan = 16, 13862306a36Sopenharmony_ci .n_iso_in_chan = 16, 13962306a36Sopenharmony_ci .n_ttl_chan = 16, 14062306a36Sopenharmony_ci }, { 14162306a36Sopenharmony_ci .name = "pcm3730", 14262306a36Sopenharmony_ci .io_range = 0x04, 14362306a36Sopenharmony_ci .has_ttl_io = 1, 14462306a36Sopenharmony_ci .n_subdevs = 4, 14562306a36Sopenharmony_ci .n_iso_out_chan = 8, 14662306a36Sopenharmony_ci .n_iso_in_chan = 8, 14762306a36Sopenharmony_ci .n_ttl_chan = 16, 14862306a36Sopenharmony_ci }, { 14962306a36Sopenharmony_ci .name = "pcl725", 15062306a36Sopenharmony_ci .io_range = 0x02, 15162306a36Sopenharmony_ci .is_pcl725 = 1, 15262306a36Sopenharmony_ci .n_subdevs = 2, 15362306a36Sopenharmony_ci .n_iso_out_chan = 8, 15462306a36Sopenharmony_ci .n_iso_in_chan = 8, 15562306a36Sopenharmony_ci }, { 15662306a36Sopenharmony_ci .name = "p8r8dio", 15762306a36Sopenharmony_ci .io_range = 0x02, 15862306a36Sopenharmony_ci .is_pcl725 = 1, 15962306a36Sopenharmony_ci .has_readback = 1, 16062306a36Sopenharmony_ci .n_subdevs = 2, 16162306a36Sopenharmony_ci .n_iso_out_chan = 8, 16262306a36Sopenharmony_ci .n_iso_in_chan = 8, 16362306a36Sopenharmony_ci }, { 16462306a36Sopenharmony_ci .name = "acl7225b", 16562306a36Sopenharmony_ci .io_range = 0x08, /* only 4 are used */ 16662306a36Sopenharmony_ci .is_acl7225b = 1, 16762306a36Sopenharmony_ci .has_readback = 1, 16862306a36Sopenharmony_ci .n_subdevs = 2, 16962306a36Sopenharmony_ci .n_iso_out_chan = 16, 17062306a36Sopenharmony_ci .n_iso_in_chan = 16, 17162306a36Sopenharmony_ci }, { 17262306a36Sopenharmony_ci .name = "p16r16dio", 17362306a36Sopenharmony_ci .io_range = 0x04, 17462306a36Sopenharmony_ci .is_acl7225b = 1, 17562306a36Sopenharmony_ci .has_readback = 1, 17662306a36Sopenharmony_ci .n_subdevs = 2, 17762306a36Sopenharmony_ci .n_iso_out_chan = 16, 17862306a36Sopenharmony_ci .n_iso_in_chan = 16, 17962306a36Sopenharmony_ci }, { 18062306a36Sopenharmony_ci .name = "pcl733", 18162306a36Sopenharmony_ci .io_range = 0x04, 18262306a36Sopenharmony_ci .n_subdevs = 1, 18362306a36Sopenharmony_ci .n_iso_in_chan = 32, 18462306a36Sopenharmony_ci }, { 18562306a36Sopenharmony_ci .name = "pcl734", 18662306a36Sopenharmony_ci .io_range = 0x04, 18762306a36Sopenharmony_ci .n_subdevs = 1, 18862306a36Sopenharmony_ci .n_iso_out_chan = 32, 18962306a36Sopenharmony_ci }, { 19062306a36Sopenharmony_ci .name = "opmm-1616-xt", 19162306a36Sopenharmony_ci .io_range = 0x10, 19262306a36Sopenharmony_ci .is_acl7225b = 1, 19362306a36Sopenharmony_ci .has_readback = 1, 19462306a36Sopenharmony_ci .n_subdevs = 2, 19562306a36Sopenharmony_ci .n_iso_out_chan = 16, 19662306a36Sopenharmony_ci .n_iso_in_chan = 16, 19762306a36Sopenharmony_ci }, { 19862306a36Sopenharmony_ci .name = "pearl-mm-p", 19962306a36Sopenharmony_ci .io_range = 0x02, 20062306a36Sopenharmony_ci .n_subdevs = 1, 20162306a36Sopenharmony_ci .n_iso_out_chan = 16, 20262306a36Sopenharmony_ci }, { 20362306a36Sopenharmony_ci .name = "ir104-pbf", 20462306a36Sopenharmony_ci .io_range = 0x08, 20562306a36Sopenharmony_ci .is_ir104 = 1, 20662306a36Sopenharmony_ci .has_readback = 1, 20762306a36Sopenharmony_ci .n_iso_out_chan = 20, 20862306a36Sopenharmony_ci .n_iso_in_chan = 20, 20962306a36Sopenharmony_ci }, 21062306a36Sopenharmony_ci}; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int pcl730_do_insn_bits(struct comedi_device *dev, 21362306a36Sopenharmony_ci struct comedi_subdevice *s, 21462306a36Sopenharmony_ci struct comedi_insn *insn, 21562306a36Sopenharmony_ci unsigned int *data) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci unsigned long reg = (unsigned long)s->private; 21862306a36Sopenharmony_ci unsigned int mask; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci mask = comedi_dio_update_state(s, data); 22162306a36Sopenharmony_ci if (mask) { 22262306a36Sopenharmony_ci if (mask & 0x00ff) 22362306a36Sopenharmony_ci outb(s->state & 0xff, dev->iobase + reg); 22462306a36Sopenharmony_ci if ((mask & 0xff00) && (s->n_chan > 8)) 22562306a36Sopenharmony_ci outb((s->state >> 8) & 0xff, dev->iobase + reg + 1); 22662306a36Sopenharmony_ci if ((mask & 0xff0000) && (s->n_chan > 16)) 22762306a36Sopenharmony_ci outb((s->state >> 16) & 0xff, dev->iobase + reg + 2); 22862306a36Sopenharmony_ci if ((mask & 0xff000000) && (s->n_chan > 24)) 22962306a36Sopenharmony_ci outb((s->state >> 24) & 0xff, dev->iobase + reg + 3); 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci data[1] = s->state; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return insn->n; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic unsigned int pcl730_get_bits(struct comedi_device *dev, 23862306a36Sopenharmony_ci struct comedi_subdevice *s) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci unsigned long reg = (unsigned long)s->private; 24162306a36Sopenharmony_ci unsigned int val; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci val = inb(dev->iobase + reg); 24462306a36Sopenharmony_ci if (s->n_chan > 8) 24562306a36Sopenharmony_ci val |= (inb(dev->iobase + reg + 1) << 8); 24662306a36Sopenharmony_ci if (s->n_chan > 16) 24762306a36Sopenharmony_ci val |= (inb(dev->iobase + reg + 2) << 16); 24862306a36Sopenharmony_ci if (s->n_chan > 24) 24962306a36Sopenharmony_ci val |= (inb(dev->iobase + reg + 3) << 24); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return val; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int pcl730_di_insn_bits(struct comedi_device *dev, 25562306a36Sopenharmony_ci struct comedi_subdevice *s, 25662306a36Sopenharmony_ci struct comedi_insn *insn, 25762306a36Sopenharmony_ci unsigned int *data) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci data[1] = pcl730_get_bits(dev, s); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return insn->n; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int pcl730_attach(struct comedi_device *dev, 26562306a36Sopenharmony_ci struct comedi_devconfig *it) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci const struct pcl730_board *board = dev->board_ptr; 26862306a36Sopenharmony_ci struct comedi_subdevice *s; 26962306a36Sopenharmony_ci int subdev; 27062306a36Sopenharmony_ci int ret; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci ret = comedi_request_region(dev, it->options[0], board->io_range); 27362306a36Sopenharmony_ci if (ret) 27462306a36Sopenharmony_ci return ret; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci ret = comedi_alloc_subdevices(dev, board->n_subdevs); 27762306a36Sopenharmony_ci if (ret) 27862306a36Sopenharmony_ci return ret; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci subdev = 0; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (board->n_iso_out_chan) { 28362306a36Sopenharmony_ci /* Isolated Digital Outputs */ 28462306a36Sopenharmony_ci s = &dev->subdevices[subdev++]; 28562306a36Sopenharmony_ci s->type = COMEDI_SUBD_DO; 28662306a36Sopenharmony_ci s->subdev_flags = SDF_WRITABLE; 28762306a36Sopenharmony_ci s->n_chan = board->n_iso_out_chan; 28862306a36Sopenharmony_ci s->maxdata = 1; 28962306a36Sopenharmony_ci s->range_table = &range_digital; 29062306a36Sopenharmony_ci s->insn_bits = pcl730_do_insn_bits; 29162306a36Sopenharmony_ci s->private = (void *)0; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* get the initial state if supported */ 29462306a36Sopenharmony_ci if (board->has_readback) 29562306a36Sopenharmony_ci s->state = pcl730_get_bits(dev, s); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (board->n_iso_in_chan) { 29962306a36Sopenharmony_ci /* Isolated Digital Inputs */ 30062306a36Sopenharmony_ci s = &dev->subdevices[subdev++]; 30162306a36Sopenharmony_ci s->type = COMEDI_SUBD_DI; 30262306a36Sopenharmony_ci s->subdev_flags = SDF_READABLE; 30362306a36Sopenharmony_ci s->n_chan = board->n_iso_in_chan; 30462306a36Sopenharmony_ci s->maxdata = 1; 30562306a36Sopenharmony_ci s->range_table = &range_digital; 30662306a36Sopenharmony_ci s->insn_bits = pcl730_di_insn_bits; 30762306a36Sopenharmony_ci s->private = board->is_ir104 ? (void *)4 : 30862306a36Sopenharmony_ci board->is_acl7225b ? (void *)2 : 30962306a36Sopenharmony_ci board->is_pcl725 ? (void *)1 : (void *)0; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (board->has_ttl_io) { 31362306a36Sopenharmony_ci /* TTL Digital Outputs */ 31462306a36Sopenharmony_ci s = &dev->subdevices[subdev++]; 31562306a36Sopenharmony_ci s->type = COMEDI_SUBD_DO; 31662306a36Sopenharmony_ci s->subdev_flags = SDF_WRITABLE; 31762306a36Sopenharmony_ci s->n_chan = board->n_ttl_chan; 31862306a36Sopenharmony_ci s->maxdata = 1; 31962306a36Sopenharmony_ci s->range_table = &range_digital; 32062306a36Sopenharmony_ci s->insn_bits = pcl730_do_insn_bits; 32162306a36Sopenharmony_ci s->private = (void *)2; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* TTL Digital Inputs */ 32462306a36Sopenharmony_ci s = &dev->subdevices[subdev++]; 32562306a36Sopenharmony_ci s->type = COMEDI_SUBD_DI; 32662306a36Sopenharmony_ci s->subdev_flags = SDF_READABLE; 32762306a36Sopenharmony_ci s->n_chan = board->n_ttl_chan; 32862306a36Sopenharmony_ci s->maxdata = 1; 32962306a36Sopenharmony_ci s->range_table = &range_digital; 33062306a36Sopenharmony_ci s->insn_bits = pcl730_di_insn_bits; 33162306a36Sopenharmony_ci s->private = (void *)2; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic struct comedi_driver pcl730_driver = { 33862306a36Sopenharmony_ci .driver_name = "pcl730", 33962306a36Sopenharmony_ci .module = THIS_MODULE, 34062306a36Sopenharmony_ci .attach = pcl730_attach, 34162306a36Sopenharmony_ci .detach = comedi_legacy_detach, 34262306a36Sopenharmony_ci .board_name = &pcl730_boards[0].name, 34362306a36Sopenharmony_ci .num_names = ARRAY_SIZE(pcl730_boards), 34462306a36Sopenharmony_ci .offset = sizeof(struct pcl730_board), 34562306a36Sopenharmony_ci}; 34662306a36Sopenharmony_cimodule_comedi_driver(pcl730_driver); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ciMODULE_AUTHOR("Comedi https://www.comedi.org"); 34962306a36Sopenharmony_ciMODULE_DESCRIPTION("Comedi low-level driver"); 35062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 351