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