adv_pci1723.c revision 7f072f54ae5dc9965cbe450419b1389d13e2b849
1/******************************************************************************* 2 comedi/drivers/pci1723.c 3 4 COMEDI - Linux Control and Measurement Device Interface 5 Copyright (C) 2000 David A. Schleef <ds@schleef.org> 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 21*******************************************************************************/ 22/* 23Driver: adv_pci1723 24Description: Advantech PCI-1723 25Author: yonggang <rsmgnu@gmail.com>, Ian Abbott <abbotti@mev.co.uk> 26Devices: [Advantech] PCI-1723 (adv_pci1723) 27Updated: Mon, 14 Apr 2008 15:12:56 +0100 28Status: works 29 30Configuration Options: 31 [0] - PCI bus of device (optional) 32 [1] - PCI slot of device (optional) 33 34 If bus/slot is not specified, the first supported 35 PCI device found will be used. 36 37Subdevice 0 is 8-channel AO, 16-bit, range +/- 10 V. 38 39Subdevice 1 is 16-channel DIO. The channels are configurable as input or 40output in 2 groups (0 to 7, 8 to 15). Configuring any channel implicitly 41configures all channels in the same group. 42 43TODO: 44 451. Add the two milliamp ranges to the AO subdevice (0 to 20 mA, 4 to 20 mA). 462. Read the initial ranges and values of the AO subdevice at start-up instead 47 of reinitializing them. 483. Implement calibration. 49*/ 50 51#include <linux/pci.h> 52 53#include "../comedidev.h" 54 55/* all the registers for the pci1723 board */ 56#define PCI1723_DA(N) ((N)<<1) /* W: D/A register N (0 to 7) */ 57 58#define PCI1723_SYN_SET 0x12 /* synchronized set register */ 59#define PCI1723_ALL_CHNNELE_SYN_STROBE 0x12 60 /* synchronized status register */ 61 62#define PCI1723_RANGE_CALIBRATION_MODE 0x14 63 /* range and calibration mode */ 64#define PCI1723_RANGE_CALIBRATION_STATUS 0x14 65 /* range and calibration status */ 66 67#define PCI1723_CONTROL_CMD_CALIBRATION_FUN 0x16 68 /* 69 * SADC control command for 70 * calibration function 71 */ 72#define PCI1723_STATUS_CMD_CALIBRATION_FUN 0x16 73 /* 74 * SADC control status for 75 * calibration function 76 */ 77 78#define PCI1723_CALIBRATION_PARA_STROBE 0x18 79 /* Calibration parameter strobe */ 80 81#define PCI1723_DIGITAL_IO_PORT_SET 0x1A /* Digital I/O port setting */ 82#define PCI1723_DIGITAL_IO_PORT_MODE 0x1A /* Digital I/O port mode */ 83 84#define PCI1723_WRITE_DIGITAL_OUTPUT_CMD 0x1C 85 /* Write digital output command */ 86#define PCI1723_READ_DIGITAL_INPUT_DATA 0x1C /* Read digital input data */ 87 88#define PCI1723_WRITE_CAL_CMD 0x1E /* Write calibration command */ 89#define PCI1723_READ_CAL_STATUS 0x1E /* Read calibration status */ 90 91#define PCI1723_SYN_STROBE 0x20 /* Synchronized strobe */ 92 93#define PCI1723_RESET_ALL_CHN_STROBE 0x22 94 /* Reset all D/A channels strobe */ 95 96#define PCI1723_RESET_CAL_CONTROL_STROBE 0x24 97 /* 98 * Reset the calibration 99 * controller strobe 100 */ 101 102#define PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE 0x26 103 /* 104 * Change D/A channels output 105 * type strobe 106 */ 107 108#define PCI1723_SELECT_CALIBRATION 0x28 /* Select the calibration Ref_V */ 109 110struct pci1723_private { 111 unsigned char da_range[8]; /* D/A output range for each channel */ 112 short ao_data[8]; /* data output buffer */ 113}; 114 115/* 116 * The pci1723 card reset; 117 */ 118static int pci1723_reset(struct comedi_device *dev) 119{ 120 struct pci1723_private *devpriv = dev->private; 121 int i; 122 123 outw(0x01, dev->iobase + PCI1723_SYN_SET); 124 /* set synchronous output mode */ 125 126 for (i = 0; i < 8; i++) { 127 /* set all outputs to 0V */ 128 devpriv->ao_data[i] = 0x8000; 129 outw(devpriv->ao_data[i], dev->iobase + PCI1723_DA(i)); 130 /* set all ranges to +/- 10V */ 131 devpriv->da_range[i] = 0; 132 outw(((devpriv->da_range[i] << 4) | i), 133 PCI1723_RANGE_CALIBRATION_MODE); 134 } 135 136 outw(0, dev->iobase + PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE); 137 /* update ranges */ 138 outw(0, dev->iobase + PCI1723_SYN_STROBE); /* update outputs */ 139 140 /* set asynchronous output mode */ 141 outw(0, dev->iobase + PCI1723_SYN_SET); 142 143 return 0; 144} 145 146static int pci1723_insn_read_ao(struct comedi_device *dev, 147 struct comedi_subdevice *s, 148 struct comedi_insn *insn, unsigned int *data) 149{ 150 struct pci1723_private *devpriv = dev->private; 151 int n, chan; 152 153 chan = CR_CHAN(insn->chanspec); 154 for (n = 0; n < insn->n; n++) 155 data[n] = devpriv->ao_data[chan]; 156 157 return n; 158} 159 160/* 161 analog data output; 162*/ 163static int pci1723_ao_write_winsn(struct comedi_device *dev, 164 struct comedi_subdevice *s, 165 struct comedi_insn *insn, unsigned int *data) 166{ 167 struct pci1723_private *devpriv = dev->private; 168 int n, chan; 169 chan = CR_CHAN(insn->chanspec); 170 171 for (n = 0; n < insn->n; n++) { 172 173 devpriv->ao_data[chan] = data[n]; 174 outw(data[n], dev->iobase + PCI1723_DA(chan)); 175 } 176 177 return n; 178} 179 180/* 181 digital i/o config/query 182*/ 183static int pci1723_dio_insn_config(struct comedi_device *dev, 184 struct comedi_subdevice *s, 185 struct comedi_insn *insn, unsigned int *data) 186{ 187 unsigned int mask; 188 unsigned int bits; 189 unsigned short dio_mode; 190 191 mask = 1 << CR_CHAN(insn->chanspec); 192 if (mask & 0x00FF) 193 bits = 0x00FF; 194 else 195 bits = 0xFF00; 196 197 switch (data[0]) { 198 case INSN_CONFIG_DIO_INPUT: 199 s->io_bits &= ~bits; 200 break; 201 case INSN_CONFIG_DIO_OUTPUT: 202 s->io_bits |= bits; 203 break; 204 case INSN_CONFIG_DIO_QUERY: 205 data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT; 206 return insn->n; 207 default: 208 return -EINVAL; 209 } 210 211 /* update hardware DIO mode */ 212 dio_mode = 0x0000; /* low byte output, high byte output */ 213 if ((s->io_bits & 0x00FF) == 0) 214 dio_mode |= 0x0001; /* low byte input */ 215 if ((s->io_bits & 0xFF00) == 0) 216 dio_mode |= 0x0002; /* high byte input */ 217 outw(dio_mode, dev->iobase + PCI1723_DIGITAL_IO_PORT_SET); 218 return 1; 219} 220 221/* 222 digital i/o bits read/write 223*/ 224static int pci1723_dio_insn_bits(struct comedi_device *dev, 225 struct comedi_subdevice *s, 226 struct comedi_insn *insn, unsigned int *data) 227{ 228 if (data[0]) { 229 s->state &= ~data[0]; 230 s->state |= (data[0] & data[1]); 231 outw(s->state, dev->iobase + PCI1723_WRITE_DIGITAL_OUTPUT_CMD); 232 } 233 data[1] = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA); 234 return insn->n; 235} 236 237static int pci1723_auto_attach(struct comedi_device *dev, 238 unsigned long context_unused) 239{ 240 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 241 struct pci1723_private *devpriv; 242 struct comedi_subdevice *s; 243 int ret; 244 245 dev->board_name = dev->driver->driver_name; 246 247 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); 248 if (!devpriv) 249 return -ENOMEM; 250 dev->private = devpriv; 251 252 ret = comedi_pci_enable(pcidev, dev->board_name); 253 if (ret) 254 return ret; 255 dev->iobase = pci_resource_start(pcidev, 2); 256 257 ret = comedi_alloc_subdevices(dev, 2); 258 if (ret) 259 return ret; 260 261 s = &dev->subdevices[0]; 262 dev->write_subdev = s; 263 s->type = COMEDI_SUBD_AO; 264 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON; 265 s->n_chan = 8; 266 s->maxdata = 0xffff; 267 s->len_chanlist = 8; 268 s->range_table = &range_bipolar10; 269 s->insn_write = pci1723_ao_write_winsn; 270 s->insn_read = pci1723_insn_read_ao; 271 272 s = &dev->subdevices[1]; 273 s->type = COMEDI_SUBD_DIO; 274 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 275 s->n_chan = 16; 276 s->maxdata = 1; 277 s->len_chanlist = 16; 278 s->range_table = &range_digital; 279 s->insn_config = pci1723_dio_insn_config; 280 s->insn_bits = pci1723_dio_insn_bits; 281 282 /* read DIO config */ 283 switch (inw(dev->iobase + PCI1723_DIGITAL_IO_PORT_MODE) & 0x03) { 284 case 0x00: /* low byte output, high byte output */ 285 s->io_bits = 0xFFFF; 286 break; 287 case 0x01: /* low byte input, high byte output */ 288 s->io_bits = 0xFF00; 289 break; 290 case 0x02: /* low byte output, high byte input */ 291 s->io_bits = 0x00FF; 292 break; 293 case 0x03: /* low byte input, high byte input */ 294 s->io_bits = 0x0000; 295 break; 296 } 297 /* read DIO port state */ 298 s->state = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA); 299 300 pci1723_reset(dev); 301 302 dev_info(dev->class_dev, "%s attached\n", dev->board_name); 303 304 return 0; 305} 306 307static void pci1723_detach(struct comedi_device *dev) 308{ 309 if (dev->iobase) 310 pci1723_reset(dev); 311 comedi_pci_disable(dev); 312} 313 314static struct comedi_driver adv_pci1723_driver = { 315 .driver_name = "adv_pci1723", 316 .module = THIS_MODULE, 317 .auto_attach = pci1723_auto_attach, 318 .detach = pci1723_detach, 319}; 320 321static int adv_pci1723_pci_probe(struct pci_dev *dev, 322 const struct pci_device_id *id) 323{ 324 return comedi_pci_auto_config(dev, &adv_pci1723_driver, 325 id->driver_data); 326} 327 328static DEFINE_PCI_DEVICE_TABLE(adv_pci1723_pci_table) = { 329 { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1723) }, 330 { 0 } 331}; 332MODULE_DEVICE_TABLE(pci, adv_pci1723_pci_table); 333 334static struct pci_driver adv_pci1723_pci_driver = { 335 .name = "adv_pci1723", 336 .id_table = adv_pci1723_pci_table, 337 .probe = adv_pci1723_pci_probe, 338 .remove = comedi_pci_auto_unconfig, 339}; 340module_comedi_pci_driver(adv_pci1723_driver, adv_pci1723_pci_driver); 341 342MODULE_AUTHOR("Comedi http://www.comedi.org"); 343MODULE_DESCRIPTION("Comedi low-level driver"); 344MODULE_LICENSE("GPL"); 345