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