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 36 #include "../comedi_pci.h" 37 38 /* 39 * PCI Bar 2 I/O Register map (dev->iobase) 40 */ 41 #define PCI1723_AO_REG(x) (0x00 + ((x) * 2)) 42 #define PCI1723_BOARD_ID_REG 0x10 43 #define PCI1723_BOARD_ID_MASK (0xf << 0) 44 #define PCI1723_SYNC_CTRL_REG 0x12 45 #define PCI1723_SYNC_CTRL(x) (((x) & 0x1) << 0) 46 #define PCI1723_SYNC_CTRL_ASYNC PCI1723_SYNC_CTRL(0) 47 #define PCI1723_SYNC_CTRL_SYNC PCI1723_SYNC_CTRL(1) 48 #define PCI1723_CTRL_REG 0x14 49 #define PCI1723_CTRL_BUSY BIT(15) 50 #define PCI1723_CTRL_INIT BIT(14) 51 #define PCI1723_CTRL_SELF BIT(8) 52 #define PCI1723_CTRL_IDX(x) (((x) & 0x3) << 6) 53 #define PCI1723_CTRL_RANGE(x) (((x) & 0x3) << 4) 54 #define PCI1723_CTRL_SEL(x) (((x) & 0x1) << 3) 55 #define PCI1723_CTRL_GAIN PCI1723_CTRL_SEL(0) 56 #define PCI1723_CTRL_OFFSET PCI1723_CTRL_SEL(1) 57 #define PCI1723_CTRL_CHAN(x) (((x) & 0x7) << 0) 58 #define PCI1723_CALIB_CTRL_REG 0x16 59 #define PCI1723_CALIB_CTRL_CS BIT(2) 60 #define PCI1723_CALIB_CTRL_DAT BIT(1) 61 #define PCI1723_CALIB_CTRL_CLK BIT(0) 62 #define PCI1723_CALIB_STROBE_REG 0x18 63 #define PCI1723_DIO_CTRL_REG 0x1a 64 #define PCI1723_DIO_CTRL_HDIO BIT(1) 65 #define PCI1723_DIO_CTRL_LDIO BIT(0) 66 #define PCI1723_DIO_DATA_REG 0x1c 67 #define PCI1723_CALIB_DATA_REG 0x1e 68 #define PCI1723_SYNC_STROBE_REG 0x20 69 #define PCI1723_RESET_AO_STROBE_REG 0x22 70 #define PCI1723_RESET_CALIB_STROBE_REG 0x24 71 #define PCI1723_RANGE_STROBE_REG 0x26 72 #define PCI1723_VREF_REG 0x28 73 #define PCI1723_VREF(x) (((x) & 0x3) << 0) 74 #define PCI1723_VREF_NEG10V PCI1723_VREF(0) 75 #define PCI1723_VREF_0V PCI1723_VREF(1) 76 #define PCI1723_VREF_POS10V PCI1723_VREF(3) 77 78 static int pci1723_ao_insn_write(struct comedi_device *dev, 79 struct comedi_subdevice *s, 80 struct comedi_insn *insn, 81 unsigned int *data) 82 { 83 unsigned int chan = CR_CHAN(insn->chanspec); 84 int i; 85 86 for (i = 0; i < insn->n; i++) { 87 unsigned int val = data[i]; 88 89 outw(val, dev->iobase + PCI1723_AO_REG(chan)); 90 s->readback[chan] = val; 91 } 92 93 return insn->n; 94 } 95 96 static int pci1723_dio_insn_config(struct comedi_device *dev, 97 struct comedi_subdevice *s, 98 struct comedi_insn *insn, 99 unsigned int *data) 100 { 101 unsigned int chan = CR_CHAN(insn->chanspec); 102 unsigned int mask = (chan < 8) ? 0x00ff : 0xff00; 103 unsigned short mode = 0x0000; /* assume output */ 104 int ret; 105 106 ret = comedi_dio_insn_config(dev, s, insn, data, mask); 107 if (ret) 108 return ret; 109 110 if (!(s->io_bits & 0x00ff)) 111 mode |= PCI1723_DIO_CTRL_LDIO; /* low byte input */ 112 if (!(s->io_bits & 0xff00)) 113 mode |= PCI1723_DIO_CTRL_HDIO; /* high byte input */ 114 outw(mode, dev->iobase + PCI1723_DIO_CTRL_REG); 115 116 return insn->n; 117 } 118 119 static int pci1723_dio_insn_bits(struct comedi_device *dev, 120 struct comedi_subdevice *s, 121 struct comedi_insn *insn, 122 unsigned int *data) 123 { 124 if (comedi_dio_update_state(s, data)) 125 outw(s->state, dev->iobase + PCI1723_DIO_DATA_REG); 126 127 data[1] = inw(dev->iobase + PCI1723_DIO_DATA_REG); 128 129 return insn->n; 130 } 131 132 static int pci1723_auto_attach(struct comedi_device *dev, 133 unsigned long context_unused) 134 { 135 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 136 struct comedi_subdevice *s; 137 unsigned int val; 138 int ret; 139 int i; 140 141 ret = comedi_pci_enable(dev); 142 if (ret) 143 return ret; 144 dev->iobase = pci_resource_start(pcidev, 2); 145 146 ret = comedi_alloc_subdevices(dev, 2); 147 if (ret) 148 return ret; 149 150 s = &dev->subdevices[0]; 151 s->type = COMEDI_SUBD_AO; 152 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; 153 s->n_chan = 8; 154 s->maxdata = 0xffff; 155 s->range_table = &range_bipolar10; 156 s->insn_write = pci1723_ao_insn_write; 157 158 ret = comedi_alloc_subdev_readback(s); 159 if (ret) 160 return ret; 161 162 /* synchronously reset all analog outputs to 0V, +/-10V range */ 163 outw(PCI1723_SYNC_CTRL_SYNC, dev->iobase + PCI1723_SYNC_CTRL_REG); 164 for (i = 0; i < s->n_chan; i++) { 165 outw(PCI1723_CTRL_RANGE(0) | PCI1723_CTRL_CHAN(i), 166 PCI1723_CTRL_REG); 167 outw(0, dev->iobase + PCI1723_RANGE_STROBE_REG); 168 169 outw(0x8000, dev->iobase + PCI1723_AO_REG(i)); 170 s->readback[i] = 0x8000; 171 } 172 outw(0, dev->iobase + PCI1723_SYNC_STROBE_REG); 173 174 /* disable syncronous control */ 175 outw(PCI1723_SYNC_CTRL_ASYNC, dev->iobase + PCI1723_SYNC_CTRL_REG); 176 177 s = &dev->subdevices[1]; 178 s->type = COMEDI_SUBD_DIO; 179 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 180 s->n_chan = 16; 181 s->maxdata = 1; 182 s->range_table = &range_digital; 183 s->insn_config = pci1723_dio_insn_config; 184 s->insn_bits = pci1723_dio_insn_bits; 185 186 /* get initial DIO direction and state */ 187 val = inw(dev->iobase + PCI1723_DIO_CTRL_REG); 188 if (!(val & PCI1723_DIO_CTRL_LDIO)) 189 s->io_bits |= 0x00ff; /* low byte output */ 190 if (!(val & PCI1723_DIO_CTRL_HDIO)) 191 s->io_bits |= 0xff00; /* high byte output */ 192 s->state = inw(dev->iobase + PCI1723_DIO_DATA_REG); 193 194 return 0; 195 } 196 197 static struct comedi_driver adv_pci1723_driver = { 198 .driver_name = "adv_pci1723", 199 .module = THIS_MODULE, 200 .auto_attach = pci1723_auto_attach, 201 .detach = comedi_pci_detach, 202 }; 203 204 static int adv_pci1723_pci_probe(struct pci_dev *dev, 205 const struct pci_device_id *id) 206 { 207 return comedi_pci_auto_config(dev, &adv_pci1723_driver, 208 id->driver_data); 209 } 210 211 static const struct pci_device_id adv_pci1723_pci_table[] = { 212 { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1723) }, 213 { 0 } 214 }; 215 MODULE_DEVICE_TABLE(pci, adv_pci1723_pci_table); 216 217 static struct pci_driver adv_pci1723_pci_driver = { 218 .name = "adv_pci1723", 219 .id_table = adv_pci1723_pci_table, 220 .probe = adv_pci1723_pci_probe, 221 .remove = comedi_pci_auto_unconfig, 222 }; 223 module_comedi_pci_driver(adv_pci1723_driver, adv_pci1723_pci_driver); 224 225 MODULE_AUTHOR("Comedi https://www.comedi.org"); 226 MODULE_DESCRIPTION("Advantech PCI-1723 Comedi driver"); 227 MODULE_LICENSE("GPL"); 228