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