#include <linux/module.h>
#include <linux/comedi/comedi_pci.h>
static unsigned char adl_pci7250_read8(struct comedi_device *dev,
unsigned int offset)
{
#ifdef CONFIG_HAS_IOPORT
if (!dev->mmio)
return inb(dev->iobase + offset);
#endif
return readb(dev->mmio + offset);
}
static void adl_pci7250_write8(struct comedi_device *dev, unsigned int offset,
unsigned char val)
{
#ifdef CONFIG_HAS_IOPORT
if (!dev->mmio) {
outb(val, dev->iobase + offset);
return;
}
#endif
writeb(val, dev->mmio + offset);
}
static int adl_pci7250_do_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
unsigned int mask = comedi_dio_update_state(s, data);
if (mask) {
unsigned int state = s->state;
unsigned int i;
for (i = 0; i * 8 < s->n_chan; i++) {
if ((mask & 0xffu) != 0) {
adl_pci7250_write8(dev, i * 2, state & 0xffu);
}
state >>= 8;
mask >>= 8;
}
}
data[1] = s->state;
return 2;
}
static int adl_pci7250_di_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
unsigned int value = 0;
unsigned int i;
for (i = 0; i * 8 < s->n_chan; i++) {
value |= (unsigned int)adl_pci7250_read8(dev, i * 2 + 1) <<
(i * 8);
}
data[1] = value;
return 2;
}
static int pci7250_auto_attach(struct comedi_device *dev,
unsigned long context_unused)
{
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
struct comedi_subdevice *s;
unsigned int max_chans;
unsigned int i;
int ret;
ret = comedi_pci_enable(dev);
if (ret)
return ret;
if (pci_resource_len(pcidev, 2) < 8)
return -ENXIO;
if (pci_resource_flags(pcidev, 2) & IORESOURCE_MEM) {
dev->mmio = pci_ioremap_bar(pcidev, 2);
if (!dev->mmio)
return -ENOMEM;
} else if (IS_ENABLED(CONFIG_HAS_IOPORT)) {
dev->iobase = pci_resource_start(pcidev, 2);
} else {
dev_err(dev->class_dev,
"error! need I/O port support\n");
return -ENXIO;
}
if (pcidev->subsystem_device == 0x7000) {
max_chans = 8;
} else {
max_chans = 32;
}
ret = comedi_alloc_subdevices(dev, 2);
if (ret)
return ret;
s = &dev->subdevices[0];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
s->n_chan = max_chans;
s->maxdata = 1;
s->range_table = &range_digital;
s->insn_bits = adl_pci7250_do_insn_bits;
s->state = 0;
for (i = 0; i * 8 < max_chans; i++) {
s->state |= (unsigned int)adl_pci7250_read8(dev, i * 2) <<
(i * 8);
}
s = &dev->subdevices[1];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->n_chan = max_chans;
s->maxdata = 1;
s->range_table = &range_digital;
s->insn_bits = adl_pci7250_di_insn_bits;
return 0;
}
static struct comedi_driver adl_pci7250_driver = {
.driver_name = "adl_pci7250",
.module = THIS_MODULE,
.auto_attach = pci7250_auto_attach,
.detach = comedi_pci_detach,
};
static int adl_pci7250_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
return comedi_pci_auto_config(dev, &adl_pci7250_driver,
id->driver_data);
}
static const struct pci_device_id adl_pci7250_pci_table[] = {
#ifdef CONFIG_HAS_IOPORT
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
0x9999, 0x7250) },
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADLINK, 0x7250,
0x9999, 0x7250) },
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADLINK, 0x7250,
PCI_VENDOR_ID_ADLINK, 0x7250) },
#endif
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADLINK, 0x7250,
PCI_VENDOR_ID_ADLINK, 0x7000) },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, adl_pci7250_pci_table);
static struct pci_driver adl_pci7250_pci_driver = {
.name = "adl_pci7250",
.id_table = adl_pci7250_pci_table,
.probe = adl_pci7250_pci_probe,
.remove = comedi_pci_auto_unconfig,
};
module_comedi_pci_driver(adl_pci7250_driver, adl_pci7250_pci_driver);
MODULE_AUTHOR("Comedi https://www.comedi.org");
MODULE_DESCRIPTION("Comedi driver for ADLink PCI-7250 series boards");
MODULE_LICENSE("GPL");