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