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