adv_pci1723.c revision 55c03cff7fd73349473cc0a964df9d55b312dbbc
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 "../comedidev.h" 52 53#define PCI_VENDOR_ID_ADVANTECH 0x13fe /* Advantech PCI vendor ID */ 54 55/* hardware types of the cards */ 56#define TYPE_PCI1723 0 57 58#define IORANGE_1723 0x2A 59 60/* all the registers for the pci1723 board */ 61#define PCI1723_DA(N) ((N)<<1) /* W: D/A register N (0 to 7) */ 62 63#define PCI1723_SYN_SET 0x12 /* synchronized set register */ 64#define PCI1723_ALL_CHNNELE_SYN_STROBE 0x12 65 /* synchronized status register */ 66 67#define PCI1723_RANGE_CALIBRATION_MODE 0x14 68 /* range and calibration mode */ 69#define PCI1723_RANGE_CALIBRATION_STATUS 0x14 70 /* range and calibration status */ 71 72#define PCI1723_CONTROL_CMD_CALIBRATION_FUN 0x16 73 /* 74 * SADC control command for 75 * calibration function 76 */ 77#define PCI1723_STATUS_CMD_CALIBRATION_FUN 0x16 78 /* 79 * SADC control status for 80 * calibration function 81 */ 82 83#define PCI1723_CALIBRATION_PARA_STROBE 0x18 84 /* Calibration parameter strobe */ 85 86#define PCI1723_DIGITAL_IO_PORT_SET 0x1A /* Digital I/O port setting */ 87#define PCI1723_DIGITAL_IO_PORT_MODE 0x1A /* Digital I/O port mode */ 88 89#define PCI1723_WRITE_DIGITAL_OUTPUT_CMD 0x1C 90 /* Write digital output command */ 91#define PCI1723_READ_DIGITAL_INPUT_DATA 0x1C /* Read digital input data */ 92 93#define PCI1723_WRITE_CAL_CMD 0x1E /* Write calibration command */ 94#define PCI1723_READ_CAL_STATUS 0x1E /* Read calibration status */ 95 96#define PCI1723_SYN_STROBE 0x20 /* Synchronized strobe */ 97 98#define PCI1723_RESET_ALL_CHN_STROBE 0x22 99 /* Reset all D/A channels strobe */ 100 101#define PCI1723_RESET_CAL_CONTROL_STROBE 0x24 102 /* 103 * Reset the calibration 104 * controller strobe 105 */ 106 107#define PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE 0x26 108 /* 109 * Change D/A channels output 110 * type strobe 111 */ 112 113#define PCI1723_SELECT_CALIBRATION 0x28 /* Select the calibration Ref_V */ 114 115/* static unsigned short pci_list_builded=0; =1 list of card is know */ 116 117static const struct comedi_lrange range_pci1723 = { 1, { 118 BIP_RANGE(10) 119 } 120}; 121 122/* 123 * Board descriptions for pci1723 boards. 124 */ 125struct pci1723_board { 126 const char *name; 127 int vendor_id; /* PCI vendor a device ID of card */ 128 int device_id; 129 int iorange; 130 char cardtype; 131 int n_aochan; /* num of D/A chans */ 132 int n_diochan; /* num of DIO chans */ 133 int ao_maxdata; /* resolution of D/A */ 134 const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */ 135}; 136 137static const struct pci1723_board boardtypes[] = { 138 { 139 .name = "pci1723", 140 .vendor_id = PCI_VENDOR_ID_ADVANTECH, 141 .device_id = 0x1723, 142 .iorange = IORANGE_1723, 143 .cardtype = TYPE_PCI1723, 144 .n_aochan = 8, 145 .n_diochan = 16, 146 .ao_maxdata = 0xffff, 147 .rangelist_ao = &range_pci1723, 148 }, 149}; 150 151/* This structure is for data unique to this hardware driver. */ 152struct pci1723_private { 153 int valid; /* card is usable; */ 154 155 struct pci_dev *pcidev; 156 unsigned char da_range[8]; /* D/A output range for each channel */ 157 158 short ao_data[8]; /* data output buffer */ 159}; 160 161/* The following macro to make it easy to access the private structure. */ 162#define devpriv ((struct pci1723_private *)dev->private) 163 164#define this_board boardtypes 165 166/* 167 * The pci1723 card reset; 168 */ 169static int pci1723_reset(struct comedi_device *dev) 170{ 171 int i; 172 DPRINTK("adv_pci1723 EDBG: BGN: pci1723_reset(...)\n"); 173 174 outw(0x01, dev->iobase + PCI1723_SYN_SET); 175 /* set synchronous output mode */ 176 177 for (i = 0; i < 8; i++) { 178 /* set all outputs to 0V */ 179 devpriv->ao_data[i] = 0x8000; 180 outw(devpriv->ao_data[i], dev->iobase + PCI1723_DA(i)); 181 /* set all ranges to +/- 10V */ 182 devpriv->da_range[i] = 0; 183 outw(((devpriv->da_range[i] << 4) | i), 184 PCI1723_RANGE_CALIBRATION_MODE); 185 } 186 187 outw(0, dev->iobase + PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE); 188 /* update ranges */ 189 outw(0, dev->iobase + PCI1723_SYN_STROBE); /* update outputs */ 190 191 /* set asynchronous output mode */ 192 outw(0, dev->iobase + PCI1723_SYN_SET); 193 194 DPRINTK("adv_pci1723 EDBG: END: pci1723_reset(...)\n"); 195 return 0; 196} 197 198static int pci1723_insn_read_ao(struct comedi_device *dev, 199 struct comedi_subdevice *s, 200 struct comedi_insn *insn, unsigned int *data) 201{ 202 int n, chan; 203 204 chan = CR_CHAN(insn->chanspec); 205 DPRINTK(" adv_PCI1723 DEBUG: pci1723_insn_read_ao() -----\n"); 206 for (n = 0; n < insn->n; n++) 207 data[n] = devpriv->ao_data[chan]; 208 209 return n; 210} 211 212/* 213 analog data output; 214*/ 215static int pci1723_ao_write_winsn(struct comedi_device *dev, 216 struct comedi_subdevice *s, 217 struct comedi_insn *insn, unsigned int *data) 218{ 219 int n, chan; 220 chan = CR_CHAN(insn->chanspec); 221 222 DPRINTK("PCI1723: the pci1723_ao_write_winsn() ------\n"); 223 224 for (n = 0; n < insn->n; n++) { 225 226 devpriv->ao_data[chan] = data[n]; 227 outw(data[n], dev->iobase + PCI1723_DA(chan)); 228 } 229 230 return n; 231} 232 233/* 234 digital i/o config/query 235*/ 236static int pci1723_dio_insn_config(struct comedi_device *dev, 237 struct comedi_subdevice *s, 238 struct comedi_insn *insn, unsigned int *data) 239{ 240 unsigned int mask; 241 unsigned int bits; 242 unsigned short dio_mode; 243 244 mask = 1 << CR_CHAN(insn->chanspec); 245 if (mask & 0x00FF) 246 bits = 0x00FF; 247 else 248 bits = 0xFF00; 249 250 switch (data[0]) { 251 case INSN_CONFIG_DIO_INPUT: 252 s->io_bits &= ~bits; 253 break; 254 case INSN_CONFIG_DIO_OUTPUT: 255 s->io_bits |= bits; 256 break; 257 case INSN_CONFIG_DIO_QUERY: 258 data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT; 259 return insn->n; 260 default: 261 return -EINVAL; 262 } 263 264 /* update hardware DIO mode */ 265 dio_mode = 0x0000; /* low byte output, high byte output */ 266 if ((s->io_bits & 0x00FF) == 0) 267 dio_mode |= 0x0001; /* low byte input */ 268 if ((s->io_bits & 0xFF00) == 0) 269 dio_mode |= 0x0002; /* high byte input */ 270 outw(dio_mode, dev->iobase + PCI1723_DIGITAL_IO_PORT_SET); 271 return 1; 272} 273 274/* 275 digital i/o bits read/write 276*/ 277static int pci1723_dio_insn_bits(struct comedi_device *dev, 278 struct comedi_subdevice *s, 279 struct comedi_insn *insn, unsigned int *data) 280{ 281 if (data[0]) { 282 s->state &= ~data[0]; 283 s->state |= (data[0] & data[1]); 284 outw(s->state, dev->iobase + PCI1723_WRITE_DIGITAL_OUTPUT_CMD); 285 } 286 data[1] = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA); 287 return 2; 288} 289 290static int pci1723_attach(struct comedi_device *dev, 291 struct comedi_devconfig *it) 292{ 293 struct comedi_subdevice *s; 294 int ret, subdev, n_subdevices; 295 struct pci_dev *pcidev; 296 unsigned int iobase; 297 unsigned char pci_bus, pci_slot, pci_func; 298 int opt_bus, opt_slot; 299 const char *errstr; 300 301 printk(KERN_ERR "comedi%d: adv_pci1723: board=%s", 302 dev->minor, this_board->name); 303 304 opt_bus = it->options[0]; 305 opt_slot = it->options[1]; 306 307 ret = alloc_private(dev, sizeof(struct pci1723_private)); 308 if (ret < 0) { 309 printk(" - Allocation failed!\n"); 310 return -ENOMEM; 311 } 312 313 /* Look for matching PCI device */ 314 errstr = "not found!"; 315 pcidev = NULL; 316 while (NULL != (pcidev = 317 pci_get_device(PCI_VENDOR_ID_ADVANTECH, 318 this_board->device_id, pcidev))) { 319 /* Found matching vendor/device. */ 320 if (opt_bus || opt_slot) { 321 /* Check bus/slot. */ 322 if (opt_bus != pcidev->bus->number 323 || opt_slot != PCI_SLOT(pcidev->devfn)) 324 continue; /* no match */ 325 } 326 /* 327 * Look for device that isn't in use. 328 * Enable PCI device and request regions. 329 */ 330 if (comedi_pci_enable(pcidev, "adv_pci1723")) { 331 errstr = 332 "failed to enable PCI device and request regions!"; 333 continue; 334 } 335 break; 336 } 337 338 if (!pcidev) { 339 if (opt_bus || opt_slot) { 340 printk(KERN_ERR " - Card at b:s %d:%d %s\n", 341 opt_bus, opt_slot, errstr); 342 } else { 343 printk(KERN_ERR " - Card %s\n", errstr); 344 } 345 return -EIO; 346 } 347 348 pci_bus = pcidev->bus->number; 349 pci_slot = PCI_SLOT(pcidev->devfn); 350 pci_func = PCI_FUNC(pcidev->devfn); 351 iobase = pci_resource_start(pcidev, 2); 352 353 printk(KERN_ERR ", b:s:f=%d:%d:%d, io=0x%4x", 354 pci_bus, pci_slot, pci_func, iobase); 355 356 dev->iobase = iobase; 357 358 dev->board_name = this_board->name; 359 devpriv->pcidev = pcidev; 360 361 n_subdevices = 0; 362 363 if (this_board->n_aochan) 364 n_subdevices++; 365 if (this_board->n_diochan) 366 n_subdevices++; 367 368 ret = alloc_subdevices(dev, n_subdevices); 369 if (ret < 0) { 370 printk(" - Allocation failed!\n"); 371 return ret; 372 } 373 374 pci1723_reset(dev); 375 subdev = 0; 376 if (this_board->n_aochan) { 377 s = dev->subdevices + subdev; 378 dev->write_subdev = s; 379 s->type = COMEDI_SUBD_AO; 380 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON; 381 s->n_chan = this_board->n_aochan; 382 s->maxdata = this_board->ao_maxdata; 383 s->len_chanlist = this_board->n_aochan; 384 s->range_table = this_board->rangelist_ao; 385 386 s->insn_write = pci1723_ao_write_winsn; 387 s->insn_read = pci1723_insn_read_ao; 388 389 /* read DIO config */ 390 switch (inw(dev->iobase + PCI1723_DIGITAL_IO_PORT_MODE) 391 & 0x03) { 392 case 0x00: /* low byte output, high byte output */ 393 s->io_bits = 0xFFFF; 394 break; 395 case 0x01: /* low byte input, high byte output */ 396 s->io_bits = 0xFF00; 397 break; 398 case 0x02: /* low byte output, high byte input */ 399 s->io_bits = 0x00FF; 400 break; 401 case 0x03: /* low byte input, high byte input */ 402 s->io_bits = 0x0000; 403 break; 404 } 405 /* read DIO port state */ 406 s->state = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA); 407 408 subdev++; 409 } 410 411 if (this_board->n_diochan) { 412 s = dev->subdevices + subdev; 413 s->type = COMEDI_SUBD_DIO; 414 s->subdev_flags = 415 SDF_READABLE | SDF_WRITABLE | SDF_GROUND | SDF_COMMON; 416 s->n_chan = this_board->n_diochan; 417 s->maxdata = 1; 418 s->len_chanlist = this_board->n_diochan; 419 s->range_table = &range_digital; 420 s->insn_config = pci1723_dio_insn_config; 421 s->insn_bits = pci1723_dio_insn_bits; 422 subdev++; 423 } 424 425 devpriv->valid = 1; 426 427 pci1723_reset(dev); 428 429 return 0; 430} 431 432static void pci1723_detach(struct comedi_device *dev) 433{ 434 if (dev->private) { 435 if (devpriv->valid) 436 pci1723_reset(dev); 437 if (devpriv->pcidev) { 438 if (dev->iobase) 439 comedi_pci_disable(devpriv->pcidev); 440 pci_dev_put(devpriv->pcidev); 441 } 442 } 443} 444 445static struct comedi_driver adv_pci1723_driver = { 446 .driver_name = "adv_pci1723", 447 .module = THIS_MODULE, 448 .attach = pci1723_attach, 449 .detach = pci1723_detach, 450}; 451 452static int __devinit adv_pci1723_pci_probe(struct pci_dev *dev, 453 const struct pci_device_id *ent) 454{ 455 return comedi_pci_auto_config(dev, &adv_pci1723_driver); 456} 457 458static void __devexit adv_pci1723_pci_remove(struct pci_dev *dev) 459{ 460 comedi_pci_auto_unconfig(dev); 461} 462 463static DEFINE_PCI_DEVICE_TABLE(adv_pci1723_pci_table) = { 464 { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1723) }, 465 { 0 } 466}; 467MODULE_DEVICE_TABLE(pci, adv_pci1723_pci_table); 468 469static struct pci_driver adv_pci1723_pci_driver = { 470 .name = "adv_pci1723", 471 .id_table = adv_pci1723_pci_table, 472 .probe = adv_pci1723_pci_probe, 473 .remove = __devexit_p(adv_pci1723_pci_remove), 474}; 475module_comedi_pci_driver(adv_pci1723_driver, adv_pci1723_pci_driver); 476 477MODULE_AUTHOR("Comedi http://www.comedi.org"); 478MODULE_DESCRIPTION("Comedi low-level driver"); 479MODULE_LICENSE("GPL"); 480