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