Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

comedi: Add new driver for ADLink PCI-7250 series

The ADLink PCI-7250, LPCI-7250, and LPCIe-7250 are PCI/PCIe boards with
8 relay outputs and 8 isolated digital inputs. Add a new Comedi driver
"adl_pci7250" to support them.

It is possible to add up to three PCI-7251 plug-in modules to the
PCI-7250, with 8 relay outputs and 8 isolated digital inputs per plug-in
module. We cannot reliably detect whether the modules are fitted
without changing their state. It is harmless to assume the modules are
fitted; they just won't do anything, so the driver allows all 32 relay
outputs to be written (and their initial state to be read), and all 32
digital inputs to be read.

The LPCI-7250 and LPCIe-7250 are low-profile boards that do not support
the plug-in modules, but except for a newer variant of the LPCIe-7250,
they cannot be distinguished from the full-height boards by their PCI
IDs. For the newer variant of the LPCIe-7250, we can assume that there
are no plug-in modules fitted and limit the number of channels
accordingly. This newer variant of the LPCIe-7250 uses memory-mapped
registers, whereas all the other boards use port-mapped registers.

I have tested the PCI-7250. The new variant of the LPCIe-7250 has been
tested in an out-of-tree version of the Comedi drivers by someone else.

Tested-by: Ian Abbott <abbotti@mev.co.uk> # PCI-7250 only
Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Link: https://lore.kernel.org/r/20250821145914.10445-1-abbotti@mev.co.uk
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Ian Abbott and committed by
Greg Kroah-Hartman
20f2044b 0c82fd96

+230
+9
drivers/comedi/Kconfig
··· 705 705 To compile this driver as a module, choose M here: the module will be 706 706 called adl_pci6208. 707 707 708 + config COMEDI_ADL_PCI7250 709 + tristate "ADLink PCI-7250 support" 710 + help 711 + Enable support for ADLink PCI-7250/LPCI-7250/LPCIe-7250 relay output 712 + and isolated digital input boards. 713 + 714 + To compile this driver as a module, choose M here: the module will be 715 + called adl_pci7250. 716 + 708 717 config COMEDI_ADL_PCI7X3X 709 718 tristate "ADLink PCI-723X/743X isolated digital i/o board support" 710 719 depends on HAS_IOPORT
+1
drivers/comedi/drivers/Makefile
··· 73 73 obj-$(CONFIG_COMEDI_ADDI_APCI_3501) += addi_apci_3501.o 74 74 obj-$(CONFIG_COMEDI_ADDI_APCI_3XXX) += addi_apci_3xxx.o 75 75 obj-$(CONFIG_COMEDI_ADL_PCI6208) += adl_pci6208.o 76 + obj-$(CONFIG_COMEDI_ADL_PCI7250) += adl_pci7250.o 76 77 obj-$(CONFIG_COMEDI_ADL_PCI7X3X) += adl_pci7x3x.o 77 78 obj-$(CONFIG_COMEDI_ADL_PCI8164) += adl_pci8164.o 78 79 obj-$(CONFIG_COMEDI_ADL_PCI9111) += adl_pci9111.o
+220
drivers/comedi/drivers/adl_pci7250.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * adl_pci7250.c 4 + * 5 + * Comedi driver for ADLink PCI-7250 series cards. 6 + * 7 + * Copyright (C) 2015, 2025 Ian Abbott <abbotti@mev.co.uk> 8 + */ 9 + 10 + /* 11 + * Driver: adl_pci7250 12 + * Description: Driver for the ADLINK PCI-7250 relay output & digital input card 13 + * Devices: [ADLINK] PCI-7250 (adl_pci7250) LPCI-7250 LPCIe-7250 14 + * Author: Ian Abbott <abbotti@mev.co.uk> 15 + * Status: works 16 + * Updated: Mon, 02 Jun 2025 13:54:11 +0100 17 + * 18 + * The driver assumes that 3 PCI-7251 modules are fitted to the PCI-7250, 19 + * giving 32 channels of relay outputs and 32 channels of isolated digital 20 + * inputs. That is also the case for the LPCI-7250 and older LPCIe-7250 21 + * cards although they do not physically support the PCI-7251 modules. 22 + * Newer LPCIe-7250 cards have a different PCI subsystem device ID, so 23 + * set the number of channels to 8 for these cards. 24 + * 25 + * Not fitting the PCI-7251 modules shouldn't do any harm, but the extra 26 + * inputs and relay outputs won't work! 27 + * 28 + * Configuration Options: not applicable, uses PCI auto config 29 + */ 30 + 31 + #include <linux/module.h> 32 + #include <linux/comedi/comedi_pci.h> 33 + 34 + static unsigned char adl_pci7250_read8(struct comedi_device *dev, 35 + unsigned int offset) 36 + { 37 + #ifdef CONFIG_HAS_IOPORT 38 + if (!dev->mmio) 39 + return inb(dev->iobase + offset); 40 + #endif 41 + return readb(dev->mmio + offset); 42 + } 43 + 44 + static void adl_pci7250_write8(struct comedi_device *dev, unsigned int offset, 45 + unsigned char val) 46 + { 47 + #ifdef CONFIG_HAS_IOPORT 48 + if (!dev->mmio) { 49 + outb(val, dev->iobase + offset); 50 + return; 51 + } 52 + #endif 53 + writeb(val, dev->mmio + offset); 54 + } 55 + 56 + static int adl_pci7250_do_insn_bits(struct comedi_device *dev, 57 + struct comedi_subdevice *s, 58 + struct comedi_insn *insn, 59 + unsigned int *data) 60 + { 61 + unsigned int mask = comedi_dio_update_state(s, data); 62 + 63 + if (mask) { 64 + unsigned int state = s->state; 65 + unsigned int i; 66 + 67 + for (i = 0; i * 8 < s->n_chan; i++) { 68 + if ((mask & 0xffu) != 0) { 69 + /* write relay data to even offset registers */ 70 + adl_pci7250_write8(dev, i * 2, state & 0xffu); 71 + } 72 + state >>= 8; 73 + mask >>= 8; 74 + } 75 + } 76 + 77 + data[1] = s->state; 78 + 79 + return 2; 80 + } 81 + 82 + static int adl_pci7250_di_insn_bits(struct comedi_device *dev, 83 + struct comedi_subdevice *s, 84 + struct comedi_insn *insn, 85 + unsigned int *data) 86 + { 87 + unsigned int value = 0; 88 + unsigned int i; 89 + 90 + for (i = 0; i * 8 < s->n_chan; i++) { 91 + /* read DI value from odd offset registers */ 92 + value |= (unsigned int)adl_pci7250_read8(dev, i * 2 + 1) << 93 + (i * 8); 94 + } 95 + 96 + data[1] = value; 97 + 98 + return 2; 99 + } 100 + 101 + static int pci7250_auto_attach(struct comedi_device *dev, 102 + unsigned long context_unused) 103 + { 104 + struct pci_dev *pcidev = comedi_to_pci_dev(dev); 105 + struct comedi_subdevice *s; 106 + unsigned int max_chans; 107 + unsigned int i; 108 + int ret; 109 + 110 + ret = comedi_pci_enable(dev); 111 + if (ret) 112 + return ret; 113 + 114 + if (pci_resource_len(pcidev, 2) < 8) 115 + return -ENXIO; 116 + 117 + /* 118 + * Newer LPCIe-7250 boards use MMIO. Older LPCIe-7250, LPCI-7250, and 119 + * PCI-7250 boards use Port I/O. 120 + */ 121 + if (pci_resource_flags(pcidev, 2) & IORESOURCE_MEM) { 122 + dev->mmio = pci_ioremap_bar(pcidev, 2); 123 + if (!dev->mmio) 124 + return -ENOMEM; 125 + } else if (IS_ENABLED(CONFIG_HAS_IOPORT)) { 126 + dev->iobase = pci_resource_start(pcidev, 2); 127 + } else { 128 + dev_err(dev->class_dev, 129 + "error! need I/O port support\n"); 130 + return -ENXIO; 131 + } 132 + 133 + if (pcidev->subsystem_device == 0x7000) { 134 + /* 135 + * This is a newer LPCIe-7250 variant and cannot possibly 136 + * have PCI-7251 modules fitted, so limit the number of 137 + * channels to 8. 138 + */ 139 + max_chans = 8; 140 + } else { 141 + /* 142 + * It is unknown whether the board is a PCI-7250, an LPCI-7250, 143 + * or an older LPCIe-7250 variant, so treat it as a PCI-7250 144 + * and assume it can have PCI-7251 modules fitted to increase 145 + * the number of channels to a maximum of 32. 146 + */ 147 + max_chans = 32; 148 + } 149 + 150 + ret = comedi_alloc_subdevices(dev, 2); 151 + if (ret) 152 + return ret; 153 + 154 + /* Relay digital output. */ 155 + s = &dev->subdevices[0]; 156 + s->type = COMEDI_SUBD_DO; 157 + s->subdev_flags = SDF_WRITABLE; 158 + s->n_chan = max_chans; 159 + s->maxdata = 1; 160 + s->range_table = &range_digital; 161 + s->insn_bits = adl_pci7250_do_insn_bits; 162 + /* Read initial state of relays from the even offset registers. */ 163 + s->state = 0; 164 + for (i = 0; i * 8 < max_chans; i++) { 165 + s->state |= (unsigned int)adl_pci7250_read8(dev, i * 2) << 166 + (i * 8); 167 + } 168 + 169 + /* Isolated digital input. */ 170 + s = &dev->subdevices[1]; 171 + s->type = COMEDI_SUBD_DI; 172 + s->subdev_flags = SDF_READABLE; 173 + s->n_chan = max_chans; 174 + s->maxdata = 1; 175 + s->range_table = &range_digital; 176 + s->insn_bits = adl_pci7250_di_insn_bits; 177 + 178 + return 0; 179 + } 180 + 181 + static struct comedi_driver adl_pci7250_driver = { 182 + .driver_name = "adl_pci7250", 183 + .module = THIS_MODULE, 184 + .auto_attach = pci7250_auto_attach, 185 + .detach = comedi_pci_detach, 186 + }; 187 + 188 + static int adl_pci7250_pci_probe(struct pci_dev *dev, 189 + const struct pci_device_id *id) 190 + { 191 + return comedi_pci_auto_config(dev, &adl_pci7250_driver, 192 + id->driver_data); 193 + } 194 + 195 + static const struct pci_device_id adl_pci7250_pci_table[] = { 196 + #ifdef CONFIG_HAS_IOPORT 197 + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, 198 + 0x9999, 0x7250) }, 199 + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADLINK, 0x7250, 200 + 0x9999, 0x7250) }, 201 + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADLINK, 0x7250, 202 + PCI_VENDOR_ID_ADLINK, 0x7250) }, 203 + #endif 204 + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADLINK, 0x7250, 205 + PCI_VENDOR_ID_ADLINK, 0x7000) }, /* newer LPCIe-7250 */ 206 + { 0 } 207 + }; 208 + MODULE_DEVICE_TABLE(pci, adl_pci7250_pci_table); 209 + 210 + static struct pci_driver adl_pci7250_pci_driver = { 211 + .name = "adl_pci7250", 212 + .id_table = adl_pci7250_pci_table, 213 + .probe = adl_pci7250_pci_probe, 214 + .remove = comedi_pci_auto_unconfig, 215 + }; 216 + module_comedi_pci_driver(adl_pci7250_driver, adl_pci7250_pci_driver); 217 + 218 + MODULE_AUTHOR("Comedi https://www.comedi.org"); 219 + MODULE_DESCRIPTION("Comedi driver for ADLink PCI-7250 series boards"); 220 + MODULE_LICENSE("GPL");