dyna_pci10xx.c revision 16a7373a8e1420cde99001759ec0eaf56f8931a5
1/* 2 * comedi/drivers/dyna_pci10xx.c 3 * Copyright (C) 2011 Prashant Shah, pshah.mumbai@gmail.com 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 20/* 21 Driver: dyna_pci10xx 22 Devices: Dynalog India PCI DAQ Cards, http://www.dynalogindia.com/ 23 Author: Prashant Shah <pshah.mumbai@gmail.com> 24 Developed at Automation Labs, Chemical Dept., IIT Bombay, India. 25 Prof. Kannan Moudgalya <kannan@iitb.ac.in> 26 http://www.iitb.ac.in 27 Status: Stable 28 Version: 1.0 29 Device Supported : 30 - Dynalog PCI 1050 31 32 Notes : 33 - Dynalog India Pvt. Ltd. does not have a registered PCI Vendor ID and 34 they are using the PLX Technlogies Vendor ID since that is the PCI Chip used 35 in the card. 36 - Dynalog India Pvt. Ltd. has provided the internal register specification for 37 their cards in their manuals. 38*/ 39 40#include "../comedidev.h" 41#include "comedi_pci.h" 42#include <linux/mutex.h> 43 44#define PCI_VENDOR_ID_DYNALOG 0x10b5 45#define DRV_NAME "dyna_pci10xx" 46 47#define READ_TIMEOUT 50 48 49static DEFINE_MUTEX(start_stop_sem); 50 51static DEFINE_PCI_DEVICE_TABLE(dyna_pci10xx_pci_table) = { 52 { PCI_DEVICE(PCI_VENDOR_ID_DYNALOG, 0x1050) }, 53 { 0 } 54}; 55 56MODULE_DEVICE_TABLE(pci, dyna_pci10xx_pci_table); 57 58static int dyna_pci10xx_attach(struct comedi_device *dev, 59 struct comedi_devconfig *it); 60static int dyna_pci10xx_detach(struct comedi_device *dev); 61 62static const struct comedi_lrange range_pci1050_ai = { 3, { 63 BIP_RANGE(10), 64 BIP_RANGE(5), 65 UNI_RANGE(10) 66 } 67}; 68 69static const char range_codes_pci1050_ai[] = { 0x00, 0x10, 0x30 }; 70 71static const struct comedi_lrange range_pci1050_ao = { 1, { 72 UNI_RANGE(10) 73 } 74}; 75 76static const char range_codes_pci1050_ao[] = { 0x00 }; 77 78struct boardtype { 79 const char *name; 80 int device_id; 81 int ai_chans; 82 int ai_bits; 83 int ao_chans; 84 int ao_bits; 85 int di_chans; 86 int di_bits; 87 int do_chans; 88 int do_bits; 89 const struct comedi_lrange *range_ai; 90 const char *range_codes_ai; 91 const struct comedi_lrange *range_ao; 92 const char *range_codes_ao; 93}; 94 95static const struct boardtype boardtypes[] = { 96 { 97 .name = "dyna_pci1050", 98 .device_id = 0x1050, 99 .ai_chans = 16, 100 .ai_bits = 12, 101 .ao_chans = 16, 102 .ao_bits = 12, 103 .di_chans = 16, 104 .di_bits = 16, 105 .do_chans = 16, 106 .do_bits = 16, 107 .range_ai = &range_pci1050_ai, 108 .range_codes_ai = range_codes_pci1050_ai, 109 .range_ao = &range_pci1050_ao, 110 .range_codes_ao = range_codes_pci1050_ao, 111 }, 112 /* dummy entry corresponding to driver name */ 113 {.name = DRV_NAME}, 114}; 115 116static struct comedi_driver driver_dyna_pci10xx = { 117 .driver_name = DRV_NAME, 118 .module = THIS_MODULE, 119 .attach = dyna_pci10xx_attach, 120 .detach = dyna_pci10xx_detach, 121 .board_name = &boardtypes[0].name, 122 .offset = sizeof(struct boardtype), 123 .num_names = ARRAY_SIZE(boardtypes), 124}; 125 126struct dyna_pci10xx_private { 127 struct pci_dev *pci_dev; /* ptr to PCI device */ 128 char valid; /* card is usable */ 129 struct mutex mutex; 130 131 /* device base address registers */ 132 unsigned long BADR0, BADR1, BADR2, BADR3, BADR4, BADR5; 133}; 134 135#define thisboard ((const struct boardtype *)dev->board_ptr) 136#define devpriv ((struct dyna_pci10xx_private *)dev->private) 137 138/******************************************************************************/ 139/************************** READ WRITE FUNCTIONS ******************************/ 140/******************************************************************************/ 141 142/* analog input callback */ 143static int dyna_pci10xx_insn_read_ai(struct comedi_device *dev, 144 struct comedi_subdevice *s, 145 struct comedi_insn *insn, unsigned int *data) 146{ 147 int n, counter; 148 u16 d = 0; 149 unsigned int chan, range; 150 151 /* get the channel number and range */ 152 chan = CR_CHAN(insn->chanspec); 153 range = thisboard->range_codes_ai[CR_RANGE((insn->chanspec))]; 154 155 mutex_lock(&devpriv->mutex); 156 /* convert n samples */ 157 for (n = 0; n < insn->n; n++) { 158 /* trigger conversion */ 159 smp_mb(); 160 outw_p(0x0000 + range + chan, devpriv->BADR2 + 2); 161 udelay(10); 162 /* read data */ 163 for (counter = 0; counter < READ_TIMEOUT; counter++) { 164 d = inw_p(devpriv->BADR2); 165 166 /* check if read is successfull if the EOC bit is set */ 167 if (d & (1 << 15)) 168 goto conv_finish; 169 } 170 data[n] = 0; 171 printk(KERN_DEBUG "comedi: dyna_pci10xx: " 172 "timeout reading analog input\n"); 173 continue; 174conv_finish: 175 /* mask the first 4 bits - EOC bits */ 176 d &= 0x0FFF; 177 data[n] = d; 178 } 179 mutex_unlock(&devpriv->mutex); 180 181 /* return the number of samples read/written */ 182 return n; 183} 184 185/* analog output callback */ 186static int dyna_pci10xx_insn_write_ao(struct comedi_device *dev, 187 struct comedi_subdevice *s, 188 struct comedi_insn *insn, unsigned int *data) 189{ 190 int n; 191 unsigned int chan, range; 192 193 chan = CR_CHAN(insn->chanspec); 194 range = thisboard->range_codes_ai[CR_RANGE((insn->chanspec))]; 195 196 mutex_lock(&devpriv->mutex); 197 for (n = 0; n < insn->n; n++) { 198 smp_mb(); 199 /* trigger conversion and write data */ 200 outw_p(data[n], devpriv->BADR2); 201 udelay(10); 202 } 203 mutex_unlock(&devpriv->mutex); 204 return n; 205} 206 207/* digital input bit interface */ 208static int dyna_pci10xx_di_insn_bits(struct comedi_device *dev, 209 struct comedi_subdevice *s, 210 struct comedi_insn *insn, unsigned int *data) 211{ 212 u16 d = 0; 213 214 if (insn->n != 2) 215 return -EINVAL; 216 217 mutex_lock(&devpriv->mutex); 218 smp_mb(); 219 d = inw_p(devpriv->BADR3); 220 udelay(10); 221 222 /* on return the data[0] contains output and data[1] contains input */ 223 data[1] = d; 224 data[0] = s->state; 225 mutex_unlock(&devpriv->mutex); 226 return 2; 227} 228 229/* digital output bit interface */ 230static int dyna_pci10xx_do_insn_bits(struct comedi_device *dev, 231 struct comedi_subdevice *s, 232 struct comedi_insn *insn, unsigned int *data) 233{ 234 if (insn->n != 2) 235 return -EINVAL; 236 237 /* The insn data is a mask in data[0] and the new data 238 * in data[1], each channel cooresponding to a bit. 239 * s->state contains the previous write data 240 */ 241 mutex_lock(&devpriv->mutex); 242 if (data[0]) { 243 s->state &= ~data[0]; 244 s->state |= (data[0] & data[1]); 245 smp_mb(); 246 outw_p(s->state, devpriv->BADR3); 247 udelay(10); 248 } 249 250 /* 251 * On return, data[1] contains the value of the digital 252 * input and output lines. We just return the software copy of the 253 * output values if it was a purely digital output subdevice. 254 */ 255 data[1] = s->state; 256 mutex_unlock(&devpriv->mutex); 257 return 2; 258} 259 260/******************************************************************************/ 261/*********************** INITIALIZATION FUNCTIONS *****************************/ 262/******************************************************************************/ 263 264static int dyna_pci10xx_attach(struct comedi_device *dev, 265 struct comedi_devconfig *it) 266{ 267 struct comedi_subdevice *s; 268 struct pci_dev *pcidev; 269 unsigned int opt_bus, opt_slot; 270 int board_index, i; 271 272 mutex_lock(&start_stop_sem); 273 274 if (alloc_private(dev, sizeof(struct dyna_pci10xx_private)) < 0) { 275 printk(KERN_ERR "comedi: dyna_pci10xx: " 276 "failed to allocate memory!\n"); 277 mutex_unlock(&start_stop_sem); 278 return -ENOMEM; 279 } 280 281 opt_bus = it->options[0]; 282 opt_slot = it->options[1]; 283 dev->board_name = thisboard->name; 284 dev->irq = 0; 285 286 /* 287 * Probe the PCI bus and located the matching device 288 */ 289 for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); 290 pcidev != NULL; 291 pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) { 292 293 board_index = -1; 294 for (i = 0; i < ARRAY_SIZE(boardtypes); ++i) { 295 if ((pcidev->vendor == PCI_VENDOR_ID_DYNALOG) && 296 (pcidev->device == boardtypes[i].device_id)) { 297 board_index = i; 298 break; 299 } 300 } 301 if (board_index < 0) 302 continue; 303 304 /* Found matching vendor/device. */ 305 if (opt_bus || opt_slot) { 306 /* Check bus/slot. */ 307 if (opt_bus != pcidev->bus->number 308 || opt_slot != PCI_SLOT(pcidev->devfn)) 309 continue; /* no match */ 310 } 311 312 goto found; 313 } 314 printk(KERN_ERR "comedi: dyna_pci10xx: no supported device found!\n"); 315 mutex_unlock(&start_stop_sem); 316 return -EIO; 317 318found: 319 320 if (!pcidev) { 321 if (opt_bus || opt_slot) { 322 printk(KERN_ERR "comedi: dyna_pci10xx: " 323 "invalid PCI device at b:s %d:%d\n", 324 opt_bus, opt_slot); 325 } else { 326 printk(KERN_ERR "comedi: dyna_pci10xx: " 327 "invalid PCI device\n"); 328 } 329 mutex_unlock(&start_stop_sem); 330 return -EIO; 331 } 332 333 if (comedi_pci_enable(pcidev, DRV_NAME)) { 334 printk(KERN_ERR "comedi: dyna_pci10xx: " 335 "failed to enable PCI device and request regions!"); 336 mutex_unlock(&start_stop_sem); 337 return -EIO; 338 } 339 340 mutex_init(&devpriv->mutex); 341 dev->board_ptr = &boardtypes[board_index]; 342 devpriv->pci_dev = pcidev; 343 344 printk(KERN_INFO "comedi: dyna_pci10xx: device found!\n"); 345 346 /* initialize device base address registers */ 347 devpriv->BADR0 = pci_resource_start(pcidev, 0); 348 devpriv->BADR1 = pci_resource_start(pcidev, 1); 349 devpriv->BADR2 = pci_resource_start(pcidev, 2); 350 devpriv->BADR3 = pci_resource_start(pcidev, 3); 351 devpriv->BADR4 = pci_resource_start(pcidev, 4); 352 devpriv->BADR5 = pci_resource_start(pcidev, 5); 353 354 if (alloc_subdevices(dev, 4) < 0) { 355 printk(KERN_ERR "comedi: dyna_pci10xx: " 356 "failed allocating subdevices\n"); 357 mutex_unlock(&start_stop_sem); 358 return -ENOMEM; 359 } 360 361 /* analog input */ 362 s = dev->subdevices + 0; 363 s->type = COMEDI_SUBD_AI; 364 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; 365 s->n_chan = thisboard->ai_chans; 366 s->maxdata = 0x0FFF; 367 s->range_table = thisboard->range_ai; 368 s->len_chanlist = 16; 369 s->insn_read = dyna_pci10xx_insn_read_ai; 370 371 /* analog output */ 372 s = dev->subdevices + 1; 373 s->type = COMEDI_SUBD_AO; 374 s->subdev_flags = SDF_WRITABLE; 375 s->n_chan = thisboard->ao_chans; 376 s->maxdata = 0x0FFF; 377 s->range_table = thisboard->range_ao; 378 s->len_chanlist = 16; 379 s->insn_write = dyna_pci10xx_insn_write_ao; 380 381 /* digital input */ 382 s = dev->subdevices + 2; 383 s->type = COMEDI_SUBD_DI; 384 s->subdev_flags = SDF_READABLE | SDF_GROUND; 385 s->n_chan = thisboard->di_chans; 386 s->maxdata = 1; 387 s->range_table = &range_digital; 388 s->len_chanlist = thisboard->di_chans; 389 s->insn_bits = dyna_pci10xx_di_insn_bits; 390 391 /* digital output */ 392 s = dev->subdevices + 3; 393 s->type = COMEDI_SUBD_DO; 394 s->subdev_flags = SDF_WRITABLE | SDF_GROUND; 395 s->n_chan = thisboard->do_chans; 396 s->maxdata = 1; 397 s->range_table = &range_digital; 398 s->len_chanlist = thisboard->do_chans; 399 s->state = 0; 400 s->insn_bits = dyna_pci10xx_do_insn_bits; 401 402 devpriv->valid = 1; 403 mutex_unlock(&start_stop_sem); 404 405 printk(KERN_INFO "comedi: dyna_pci10xx: %s - device setup completed!\n", 406 boardtypes[board_index].name); 407 408 return 1; 409} 410 411static int dyna_pci10xx_detach(struct comedi_device *dev) 412{ 413 if (devpriv && devpriv->pci_dev) { 414 comedi_pci_disable(devpriv->pci_dev); 415 mutex_destroy(&devpriv->mutex); 416 } 417 418 return 0; 419} 420 421static int __devinit driver_dyna_pci10xx_pci_probe(struct pci_dev *dev, 422 const struct pci_device_id *ent) 423{ 424 return comedi_pci_auto_config(dev, driver_dyna_pci10xx.driver_name); 425} 426 427static void __devexit driver_dyna_pci10xx_pci_remove(struct pci_dev *dev) 428{ 429 comedi_pci_auto_unconfig(dev); 430} 431 432static struct pci_driver driver_dyna_pci10xx_pci_driver = { 433 .id_table = dyna_pci10xx_pci_table, 434 .probe = &driver_dyna_pci10xx_pci_probe, 435 .remove = __devexit_p(&driver_dyna_pci10xx_pci_remove) 436}; 437 438static int __init driver_dyna_pci10xx_init_module(void) 439{ 440 int retval; 441 442 retval = comedi_driver_register(&driver_dyna_pci10xx); 443 if (retval < 0) 444 return retval; 445 446 driver_dyna_pci10xx_pci_driver.name = 447 (char *)driver_dyna_pci10xx.driver_name; 448 return pci_register_driver(&driver_dyna_pci10xx_pci_driver); 449} 450 451static void __exit driver_dyna_pci10xx_cleanup_module(void) 452{ 453 pci_unregister_driver(&driver_dyna_pci10xx_pci_driver); 454 comedi_driver_unregister(&driver_dyna_pci10xx); 455} 456 457module_init(driver_dyna_pci10xx_init_module); 458module_exit(driver_dyna_pci10xx_cleanup_module); 459 460MODULE_LICENSE("GPL"); 461MODULE_AUTHOR("Prashant Shah <pshah.mumbai@gmail.com>"); 462MODULE_DESCRIPTION("Comedi based drivers for Dynalog PCI DAQ cards"); 463