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