162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * comedi/drivers/adl_pci8164.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Hardware comedi driver for PCI-8164 Adlink card 662306a36Sopenharmony_ci * Copyright (C) 2004 Michel Lachine <mike@mikelachaine.ca> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * Driver: adl_pci8164 1162306a36Sopenharmony_ci * Description: Driver for the Adlink PCI-8164 4 Axes Motion Control board 1262306a36Sopenharmony_ci * Devices: [ADLink] PCI-8164 (adl_pci8164) 1362306a36Sopenharmony_ci * Author: Michel Lachaine <mike@mikelachaine.ca> 1462306a36Sopenharmony_ci * Status: experimental 1562306a36Sopenharmony_ci * Updated: Mon, 14 Apr 2008 15:10:32 +0100 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Configuration Options: not applicable, uses PCI auto config 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/comedi/comedi_pci.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define PCI8164_AXIS(x) ((x) * 0x08) 2562306a36Sopenharmony_ci#define PCI8164_CMD_MSTS_REG 0x00 2662306a36Sopenharmony_ci#define PCI8164_OTP_SSTS_REG 0x02 2762306a36Sopenharmony_ci#define PCI8164_BUF0_REG 0x04 2862306a36Sopenharmony_ci#define PCI8164_BUF1_REG 0x06 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic int adl_pci8164_insn_read(struct comedi_device *dev, 3162306a36Sopenharmony_ci struct comedi_subdevice *s, 3262306a36Sopenharmony_ci struct comedi_insn *insn, 3362306a36Sopenharmony_ci unsigned int *data) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci unsigned long offset = (unsigned long)s->private; 3662306a36Sopenharmony_ci unsigned int chan = CR_CHAN(insn->chanspec); 3762306a36Sopenharmony_ci int i; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci for (i = 0; i < insn->n; i++) 4062306a36Sopenharmony_ci data[i] = inw(dev->iobase + PCI8164_AXIS(chan) + offset); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return insn->n; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int adl_pci8164_insn_write(struct comedi_device *dev, 4662306a36Sopenharmony_ci struct comedi_subdevice *s, 4762306a36Sopenharmony_ci struct comedi_insn *insn, 4862306a36Sopenharmony_ci unsigned int *data) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci unsigned long offset = (unsigned long)s->private; 5162306a36Sopenharmony_ci unsigned int chan = CR_CHAN(insn->chanspec); 5262306a36Sopenharmony_ci int i; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci for (i = 0; i < insn->n; i++) 5562306a36Sopenharmony_ci outw(data[i], dev->iobase + PCI8164_AXIS(chan) + offset); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return insn->n; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int adl_pci8164_auto_attach(struct comedi_device *dev, 6162306a36Sopenharmony_ci unsigned long context_unused) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct pci_dev *pcidev = comedi_to_pci_dev(dev); 6462306a36Sopenharmony_ci struct comedi_subdevice *s; 6562306a36Sopenharmony_ci int ret; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci ret = comedi_pci_enable(dev); 6862306a36Sopenharmony_ci if (ret) 6962306a36Sopenharmony_ci return ret; 7062306a36Sopenharmony_ci dev->iobase = pci_resource_start(pcidev, 2); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci ret = comedi_alloc_subdevices(dev, 4); 7362306a36Sopenharmony_ci if (ret) 7462306a36Sopenharmony_ci return ret; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* read MSTS register / write CMD register for each axis (channel) */ 7762306a36Sopenharmony_ci s = &dev->subdevices[0]; 7862306a36Sopenharmony_ci s->type = COMEDI_SUBD_PROC; 7962306a36Sopenharmony_ci s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 8062306a36Sopenharmony_ci s->n_chan = 4; 8162306a36Sopenharmony_ci s->maxdata = 0xffff; 8262306a36Sopenharmony_ci s->len_chanlist = 4; 8362306a36Sopenharmony_ci s->insn_read = adl_pci8164_insn_read; 8462306a36Sopenharmony_ci s->insn_write = adl_pci8164_insn_write; 8562306a36Sopenharmony_ci s->private = (void *)PCI8164_CMD_MSTS_REG; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* read SSTS register / write OTP register for each axis (channel) */ 8862306a36Sopenharmony_ci s = &dev->subdevices[1]; 8962306a36Sopenharmony_ci s->type = COMEDI_SUBD_PROC; 9062306a36Sopenharmony_ci s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 9162306a36Sopenharmony_ci s->n_chan = 4; 9262306a36Sopenharmony_ci s->maxdata = 0xffff; 9362306a36Sopenharmony_ci s->len_chanlist = 4; 9462306a36Sopenharmony_ci s->insn_read = adl_pci8164_insn_read; 9562306a36Sopenharmony_ci s->insn_write = adl_pci8164_insn_write; 9662306a36Sopenharmony_ci s->private = (void *)PCI8164_OTP_SSTS_REG; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* read/write BUF0 register for each axis (channel) */ 9962306a36Sopenharmony_ci s = &dev->subdevices[2]; 10062306a36Sopenharmony_ci s->type = COMEDI_SUBD_PROC; 10162306a36Sopenharmony_ci s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 10262306a36Sopenharmony_ci s->n_chan = 4; 10362306a36Sopenharmony_ci s->maxdata = 0xffff; 10462306a36Sopenharmony_ci s->len_chanlist = 4; 10562306a36Sopenharmony_ci s->insn_read = adl_pci8164_insn_read; 10662306a36Sopenharmony_ci s->insn_write = adl_pci8164_insn_write; 10762306a36Sopenharmony_ci s->private = (void *)PCI8164_BUF0_REG; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* read/write BUF1 register for each axis (channel) */ 11062306a36Sopenharmony_ci s = &dev->subdevices[3]; 11162306a36Sopenharmony_ci s->type = COMEDI_SUBD_PROC; 11262306a36Sopenharmony_ci s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 11362306a36Sopenharmony_ci s->n_chan = 4; 11462306a36Sopenharmony_ci s->maxdata = 0xffff; 11562306a36Sopenharmony_ci s->len_chanlist = 4; 11662306a36Sopenharmony_ci s->insn_read = adl_pci8164_insn_read; 11762306a36Sopenharmony_ci s->insn_write = adl_pci8164_insn_write; 11862306a36Sopenharmony_ci s->private = (void *)PCI8164_BUF1_REG; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic struct comedi_driver adl_pci8164_driver = { 12462306a36Sopenharmony_ci .driver_name = "adl_pci8164", 12562306a36Sopenharmony_ci .module = THIS_MODULE, 12662306a36Sopenharmony_ci .auto_attach = adl_pci8164_auto_attach, 12762306a36Sopenharmony_ci .detach = comedi_pci_detach, 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int adl_pci8164_pci_probe(struct pci_dev *dev, 13162306a36Sopenharmony_ci const struct pci_device_id *id) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci return comedi_pci_auto_config(dev, &adl_pci8164_driver, 13462306a36Sopenharmony_ci id->driver_data); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic const struct pci_device_id adl_pci8164_pci_table[] = { 13862306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x8164) }, 13962306a36Sopenharmony_ci { 0 } 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, adl_pci8164_pci_table); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic struct pci_driver adl_pci8164_pci_driver = { 14462306a36Sopenharmony_ci .name = "adl_pci8164", 14562306a36Sopenharmony_ci .id_table = adl_pci8164_pci_table, 14662306a36Sopenharmony_ci .probe = adl_pci8164_pci_probe, 14762306a36Sopenharmony_ci .remove = comedi_pci_auto_unconfig, 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_cimodule_comedi_pci_driver(adl_pci8164_driver, adl_pci8164_pci_driver); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ciMODULE_AUTHOR("Comedi https://www.comedi.org"); 15262306a36Sopenharmony_ciMODULE_DESCRIPTION("Comedi low-level driver"); 15362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 154