Skip to content

Commit 20f2044

Browse files
ian-abbottgregkh
authored andcommitted
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 <[email protected]> # PCI-7250 only Signed-off-by: Ian Abbott <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 0c82fd9 commit 20f2044

File tree

3 files changed

+230
-0
lines changed

3 files changed

+230
-0
lines changed

drivers/comedi/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,15 @@ config COMEDI_ADL_PCI6208
705705
To compile this driver as a module, choose M here: the module will be
706706
called adl_pci6208.
707707

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+
708717
config COMEDI_ADL_PCI7X3X
709718
tristate "ADLink PCI-723X/743X isolated digital i/o board support"
710719
depends on HAS_IOPORT

drivers/comedi/drivers/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ obj-$(CONFIG_COMEDI_ADDI_APCI_3120) += addi_apci_3120.o
7373
obj-$(CONFIG_COMEDI_ADDI_APCI_3501) += addi_apci_3501.o
7474
obj-$(CONFIG_COMEDI_ADDI_APCI_3XXX) += addi_apci_3xxx.o
7575
obj-$(CONFIG_COMEDI_ADL_PCI6208) += adl_pci6208.o
76+
obj-$(CONFIG_COMEDI_ADL_PCI7250) += adl_pci7250.o
7677
obj-$(CONFIG_COMEDI_ADL_PCI7X3X) += adl_pci7x3x.o
7778
obj-$(CONFIG_COMEDI_ADL_PCI8164) += adl_pci8164.o
7879
obj-$(CONFIG_COMEDI_ADL_PCI9111) += adl_pci9111.o
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
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 <[email protected]>
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 <[email protected]>
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");

0 commit comments

Comments
 (0)