1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * adv_pci1723.c 4 * Comedi driver for the Advantech PCI-1723 card. 5 * 6 * COMEDI - Linux Control and Measurement Device Interface 7 * Copyright (C) 2000 David A. Schleef <ds@schleef.org> 8 */ 9 10/* 11 * Driver: adv_pci1723 12 * Description: Advantech PCI-1723 13 * Author: yonggang <rsmgnu@gmail.com>, Ian Abbott <abbotti@mev.co.uk> 14 * Devices: [Advantech] PCI-1723 (adv_pci1723) 15 * Updated: Mon, 14 Apr 2008 15:12:56 +0100 16 * Status: works 17 * 18 * Configuration Options: not applicable, uses comedi PCI auto config 19 * 20 * Subdevice 0 is 8-channel AO, 16-bit, range +/- 10 V. 21 * 22 * Subdevice 1 is 16-channel DIO. The channels are configurable as 23 * input or output in 2 groups (0 to 7, 8 to 15). Configuring any 24 * channel implicitly configures all channels in the same group. 25 * 26 * TODO: 27 * 1. Add the two milliamp ranges to the AO subdevice (0 to 20 mA, 28 * 4 to 20 mA). 29 * 2. Read the initial ranges and values of the AO subdevice at 30 * start-up instead of reinitializing them. 31 * 3. Implement calibration. 32 */ 33 34#include <linux/module.h> 35#include <linux/comedi/comedi_pci.h> 36 37/* 38 * PCI Bar 2 I/O Register map (dev->iobase) 39 */ 40#define PCI1723_AO_REG(x) (0x00 + ((x) * 2)) 41#define PCI1723_BOARD_ID_REG 0x10 42#define PCI1723_BOARD_ID_MASK (0xf << 0) 43#define PCI1723_SYNC_CTRL_REG 0x12 44#define PCI1723_SYNC_CTRL(x) (((x) & 0x1) << 0) 45#define PCI1723_SYNC_CTRL_ASYNC PCI1723_SYNC_CTRL(0) 46#define PCI1723_SYNC_CTRL_SYNC PCI1723_SYNC_CTRL(1) 47#define PCI1723_CTRL_REG 0x14 48#define PCI1723_CTRL_BUSY BIT(15) 49#define PCI1723_CTRL_INIT BIT(14) 50#define PCI1723_CTRL_SELF BIT(8) 51#define PCI1723_CTRL_IDX(x) (((x) & 0x3) << 6) 52#define PCI1723_CTRL_RANGE(x) (((x) & 0x3) << 4) 53#define PCI1723_CTRL_SEL(x) (((x) & 0x1) << 3) 54#define PCI1723_CTRL_GAIN PCI1723_CTRL_SEL(0) 55#define PCI1723_CTRL_OFFSET PCI1723_CTRL_SEL(1) 56#define PCI1723_CTRL_CHAN(x) (((x) & 0x7) << 0) 57#define PCI1723_CALIB_CTRL_REG 0x16 58#define PCI1723_CALIB_CTRL_CS BIT(2) 59#define PCI1723_CALIB_CTRL_DAT BIT(1) 60#define PCI1723_CALIB_CTRL_CLK BIT(0) 61#define PCI1723_CALIB_STROBE_REG 0x18 62#define PCI1723_DIO_CTRL_REG 0x1a 63#define PCI1723_DIO_CTRL_HDIO BIT(1) 64#define PCI1723_DIO_CTRL_LDIO BIT(0) 65#define PCI1723_DIO_DATA_REG 0x1c 66#define PCI1723_CALIB_DATA_REG 0x1e 67#define PCI1723_SYNC_STROBE_REG 0x20 68#define PCI1723_RESET_AO_STROBE_REG 0x22 69#define PCI1723_RESET_CALIB_STROBE_REG 0x24 70#define PCI1723_RANGE_STROBE_REG 0x26 71#define PCI1723_VREF_REG 0x28 72#define PCI1723_VREF(x) (((x) & 0x3) << 0) 73#define PCI1723_VREF_NEG10V PCI1723_VREF(0) 74#define PCI1723_VREF_0V PCI1723_VREF(1) 75#define PCI1723_VREF_POS10V PCI1723_VREF(3) 76 77static int pci1723_ao_insn_write(struct comedi_device *dev, 78 struct comedi_subdevice *s, 79 struct comedi_insn *insn, 80 unsigned int *data) 81{ 82 unsigned int chan = CR_CHAN(insn->chanspec); 83 int i; 84 85 for (i = 0; i < insn->n; i++) { 86 unsigned int val = data[i]; 87 88 outw(val, dev->iobase + PCI1723_AO_REG(chan)); 89 s->readback[chan] = val; 90 } 91 92 return insn->n; 93} 94 95static int pci1723_dio_insn_config(struct comedi_device *dev, 96 struct comedi_subdevice *s, 97 struct comedi_insn *insn, 98 unsigned int *data) 99{ 100 unsigned int chan = CR_CHAN(insn->chanspec); 101 unsigned int mask = (chan < 8) ? 0x00ff : 0xff00; 102 unsigned short mode = 0x0000; /* assume output */ 103 int ret; 104 105 ret = comedi_dio_insn_config(dev, s, insn, data, mask); 106 if (ret) 107 return ret; 108 109 if (!(s->io_bits & 0x00ff)) 110 mode |= PCI1723_DIO_CTRL_LDIO; /* low byte input */ 111 if (!(s->io_bits & 0xff00)) 112 mode |= PCI1723_DIO_CTRL_HDIO; /* high byte input */ 113 outw(mode, dev->iobase + PCI1723_DIO_CTRL_REG); 114 115 return insn->n; 116} 117 118static int pci1723_dio_insn_bits(struct comedi_device *dev, 119 struct comedi_subdevice *s, 120 struct comedi_insn *insn, 121 unsigned int *data) 122{ 123 if (comedi_dio_update_state(s, data)) 124 outw(s->state, dev->iobase + PCI1723_DIO_DATA_REG); 125 126 data[1] = inw(dev->iobase + PCI1723_DIO_DATA_REG); 127 128 return insn->n; 129} 130 131static int pci1723_auto_attach(struct comedi_device *dev, 132 unsigned long context_unused) 133{ 134 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 135 struct comedi_subdevice *s; 136 unsigned int val; 137 int ret; 138 int i; 139 140 ret = comedi_pci_enable(dev); 141 if (ret) 142 return ret; 143 dev->iobase = pci_resource_start(pcidev, 2); 144 145 ret = comedi_alloc_subdevices(dev, 2); 146 if (ret) 147 return ret; 148 149 s = &dev->subdevices[0]; 150 s->type = COMEDI_SUBD_AO; 151 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; 152 s->n_chan = 8; 153 s->maxdata = 0xffff; 154 s->range_table = &range_bipolar10; 155 s->insn_write = pci1723_ao_insn_write; 156 157 ret = comedi_alloc_subdev_readback(s); 158 if (ret) 159 return ret; 160 161 /* synchronously reset all analog outputs to 0V, +/-10V range */ 162 outw(PCI1723_SYNC_CTRL_SYNC, dev->iobase + PCI1723_SYNC_CTRL_REG); 163 for (i = 0; i < s->n_chan; i++) { 164 outw(PCI1723_CTRL_RANGE(0) | PCI1723_CTRL_CHAN(i), 165 PCI1723_CTRL_REG); 166 outw(0, dev->iobase + PCI1723_RANGE_STROBE_REG); 167 168 outw(0x8000, dev->iobase + PCI1723_AO_REG(i)); 169 s->readback[i] = 0x8000; 170 } 171 outw(0, dev->iobase + PCI1723_SYNC_STROBE_REG); 172 173 /* disable syncronous control */ 174 outw(PCI1723_SYNC_CTRL_ASYNC, dev->iobase + PCI1723_SYNC_CTRL_REG); 175 176 s = &dev->subdevices[1]; 177 s->type = COMEDI_SUBD_DIO; 178 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 179 s->n_chan = 16; 180 s->maxdata = 1; 181 s->range_table = &range_digital; 182 s->insn_config = pci1723_dio_insn_config; 183 s->insn_bits = pci1723_dio_insn_bits; 184 185 /* get initial DIO direction and state */ 186 val = inw(dev->iobase + PCI1723_DIO_CTRL_REG); 187 if (!(val & PCI1723_DIO_CTRL_LDIO)) 188 s->io_bits |= 0x00ff; /* low byte output */ 189 if (!(val & PCI1723_DIO_CTRL_HDIO)) 190 s->io_bits |= 0xff00; /* high byte output */ 191 s->state = inw(dev->iobase + PCI1723_DIO_DATA_REG); 192 193 return 0; 194} 195 196static struct comedi_driver adv_pci1723_driver = { 197 .driver_name = "adv_pci1723", 198 .module = THIS_MODULE, 199 .auto_attach = pci1723_auto_attach, 200 .detach = comedi_pci_detach, 201}; 202 203static int adv_pci1723_pci_probe(struct pci_dev *dev, 204 const struct pci_device_id *id) 205{ 206 return comedi_pci_auto_config(dev, &adv_pci1723_driver, 207 id->driver_data); 208} 209 210static const struct pci_device_id adv_pci1723_pci_table[] = { 211 { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1723) }, 212 { 0 } 213}; 214MODULE_DEVICE_TABLE(pci, adv_pci1723_pci_table); 215 216static struct pci_driver adv_pci1723_pci_driver = { 217 .name = "adv_pci1723", 218 .id_table = adv_pci1723_pci_table, 219 .probe = adv_pci1723_pci_probe, 220 .remove = comedi_pci_auto_unconfig, 221}; 222module_comedi_pci_driver(adv_pci1723_driver, adv_pci1723_pci_driver); 223 224MODULE_AUTHOR("Comedi https://www.comedi.org"); 225MODULE_DESCRIPTION("Advantech PCI-1723 Comedi driver"); 226MODULE_LICENSE("GPL"); 227