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 16/* 17 Driver: dyna_pci10xx 18 Devices: Dynalog India PCI DAQ Cards, http://www.dynalogindia.com/ 19 Author: Prashant Shah <pshah.mumbai@gmail.com> 20 Developed at Automation Labs, Chemical Dept., IIT Bombay, India. 21 Prof. Kannan Moudgalya <kannan@iitb.ac.in> 22 http://www.iitb.ac.in 23 Status: Stable 24 Version: 1.0 25 Device Supported : 26 - Dynalog PCI 1050 27 28 Notes : 29 - Dynalog India Pvt. Ltd. does not have a registered PCI Vendor ID and 30 they are using the PLX Technlogies Vendor ID since that is the PCI Chip used 31 in the card. 32 - Dynalog India Pvt. Ltd. has provided the internal register specification for 33 their cards in their manuals. 34*/ 35 36#include <linux/module.h> 37#include <linux/delay.h> 38#include <linux/pci.h> 39#include <linux/mutex.h> 40 41#include "../comedidev.h" 42 43#define READ_TIMEOUT 50 44 45static const struct comedi_lrange range_pci1050_ai = { 46 3, { 47 BIP_RANGE(10), 48 BIP_RANGE(5), 49 UNI_RANGE(10) 50 } 51}; 52 53static const char range_codes_pci1050_ai[] = { 0x00, 0x10, 0x30 }; 54 55struct dyna_pci10xx_private { 56 struct mutex mutex; 57 unsigned long BADR3; 58}; 59 60static int dyna_pci10xx_ai_eoc(struct comedi_device *dev, 61 struct comedi_subdevice *s, 62 struct comedi_insn *insn, 63 unsigned long context) 64{ 65 unsigned int status; 66 67 status = inw_p(dev->iobase); 68 if (status & (1 << 15)) 69 return 0; 70 return -EBUSY; 71} 72 73static int dyna_pci10xx_insn_read_ai(struct comedi_device *dev, 74 struct comedi_subdevice *s, 75 struct comedi_insn *insn, unsigned int *data) 76{ 77 struct dyna_pci10xx_private *devpriv = dev->private; 78 int n; 79 u16 d = 0; 80 int ret = 0; 81 unsigned int chan, range; 82 83 /* get the channel number and range */ 84 chan = CR_CHAN(insn->chanspec); 85 range = range_codes_pci1050_ai[CR_RANGE((insn->chanspec))]; 86 87 mutex_lock(&devpriv->mutex); 88 /* convert n samples */ 89 for (n = 0; n < insn->n; n++) { 90 /* trigger conversion */ 91 smp_mb(); 92 outw_p(0x0000 + range + chan, dev->iobase + 2); 93 udelay(10); 94 95 ret = comedi_timeout(dev, s, insn, dyna_pci10xx_ai_eoc, 0); 96 if (ret) 97 break; 98 99 /* read data */ 100 d = inw_p(dev->iobase); 101 /* mask the first 4 bits - EOC bits */ 102 d &= 0x0FFF; 103 data[n] = d; 104 } 105 mutex_unlock(&devpriv->mutex); 106 107 /* return the number of samples read/written */ 108 return ret ? ret : n; 109} 110 111/* analog output callback */ 112static int dyna_pci10xx_insn_write_ao(struct comedi_device *dev, 113 struct comedi_subdevice *s, 114 struct comedi_insn *insn, unsigned int *data) 115{ 116 struct dyna_pci10xx_private *devpriv = dev->private; 117 int n; 118 unsigned int chan, range; 119 120 chan = CR_CHAN(insn->chanspec); 121 range = range_codes_pci1050_ai[CR_RANGE((insn->chanspec))]; 122 123 mutex_lock(&devpriv->mutex); 124 for (n = 0; n < insn->n; n++) { 125 smp_mb(); 126 /* trigger conversion and write data */ 127 outw_p(data[n], dev->iobase); 128 udelay(10); 129 } 130 mutex_unlock(&devpriv->mutex); 131 return n; 132} 133 134/* digital input bit interface */ 135static int dyna_pci10xx_di_insn_bits(struct comedi_device *dev, 136 struct comedi_subdevice *s, 137 struct comedi_insn *insn, unsigned int *data) 138{ 139 struct dyna_pci10xx_private *devpriv = dev->private; 140 u16 d = 0; 141 142 mutex_lock(&devpriv->mutex); 143 smp_mb(); 144 d = inw_p(devpriv->BADR3); 145 udelay(10); 146 147 /* on return the data[0] contains output and data[1] contains input */ 148 data[1] = d; 149 data[0] = s->state; 150 mutex_unlock(&devpriv->mutex); 151 return insn->n; 152} 153 154static int dyna_pci10xx_do_insn_bits(struct comedi_device *dev, 155 struct comedi_subdevice *s, 156 struct comedi_insn *insn, 157 unsigned int *data) 158{ 159 struct dyna_pci10xx_private *devpriv = dev->private; 160 161 mutex_lock(&devpriv->mutex); 162 if (comedi_dio_update_state(s, data)) { 163 smp_mb(); 164 outw_p(s->state, devpriv->BADR3); 165 udelay(10); 166 } 167 168 data[1] = s->state; 169 mutex_unlock(&devpriv->mutex); 170 171 return insn->n; 172} 173 174static int dyna_pci10xx_auto_attach(struct comedi_device *dev, 175 unsigned long context_unused) 176{ 177 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 178 struct dyna_pci10xx_private *devpriv; 179 struct comedi_subdevice *s; 180 int ret; 181 182 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 183 if (!devpriv) 184 return -ENOMEM; 185 186 ret = comedi_pci_enable(dev); 187 if (ret) 188 return ret; 189 dev->iobase = pci_resource_start(pcidev, 2); 190 devpriv->BADR3 = pci_resource_start(pcidev, 3); 191 192 mutex_init(&devpriv->mutex); 193 194 ret = comedi_alloc_subdevices(dev, 4); 195 if (ret) 196 return ret; 197 198 /* analog input */ 199 s = &dev->subdevices[0]; 200 s->type = COMEDI_SUBD_AI; 201 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; 202 s->n_chan = 16; 203 s->maxdata = 0x0FFF; 204 s->range_table = &range_pci1050_ai; 205 s->len_chanlist = 16; 206 s->insn_read = dyna_pci10xx_insn_read_ai; 207 208 /* analog output */ 209 s = &dev->subdevices[1]; 210 s->type = COMEDI_SUBD_AO; 211 s->subdev_flags = SDF_WRITABLE; 212 s->n_chan = 16; 213 s->maxdata = 0x0FFF; 214 s->range_table = &range_unipolar10; 215 s->len_chanlist = 16; 216 s->insn_write = dyna_pci10xx_insn_write_ao; 217 218 /* digital input */ 219 s = &dev->subdevices[2]; 220 s->type = COMEDI_SUBD_DI; 221 s->subdev_flags = SDF_READABLE | SDF_GROUND; 222 s->n_chan = 16; 223 s->maxdata = 1; 224 s->range_table = &range_digital; 225 s->len_chanlist = 16; 226 s->insn_bits = dyna_pci10xx_di_insn_bits; 227 228 /* digital output */ 229 s = &dev->subdevices[3]; 230 s->type = COMEDI_SUBD_DO; 231 s->subdev_flags = SDF_WRITABLE | SDF_GROUND; 232 s->n_chan = 16; 233 s->maxdata = 1; 234 s->range_table = &range_digital; 235 s->len_chanlist = 16; 236 s->state = 0; 237 s->insn_bits = dyna_pci10xx_do_insn_bits; 238 239 return 0; 240} 241 242static void dyna_pci10xx_detach(struct comedi_device *dev) 243{ 244 struct dyna_pci10xx_private *devpriv = dev->private; 245 246 comedi_pci_detach(dev); 247 if (devpriv) 248 mutex_destroy(&devpriv->mutex); 249} 250 251static struct comedi_driver dyna_pci10xx_driver = { 252 .driver_name = "dyna_pci10xx", 253 .module = THIS_MODULE, 254 .auto_attach = dyna_pci10xx_auto_attach, 255 .detach = dyna_pci10xx_detach, 256}; 257 258static int dyna_pci10xx_pci_probe(struct pci_dev *dev, 259 const struct pci_device_id *id) 260{ 261 return comedi_pci_auto_config(dev, &dyna_pci10xx_driver, 262 id->driver_data); 263} 264 265static const struct pci_device_id dyna_pci10xx_pci_table[] = { 266 { PCI_DEVICE(PCI_VENDOR_ID_PLX, 0x1050) }, 267 { 0 } 268}; 269MODULE_DEVICE_TABLE(pci, dyna_pci10xx_pci_table); 270 271static struct pci_driver dyna_pci10xx_pci_driver = { 272 .name = "dyna_pci10xx", 273 .id_table = dyna_pci10xx_pci_table, 274 .probe = dyna_pci10xx_pci_probe, 275 .remove = comedi_pci_auto_unconfig, 276}; 277module_comedi_pci_driver(dyna_pci10xx_driver, dyna_pci10xx_pci_driver); 278 279MODULE_LICENSE("GPL"); 280MODULE_AUTHOR("Prashant Shah <pshah.mumbai@gmail.com>"); 281MODULE_DESCRIPTION("Comedi based drivers for Dynalog PCI DAQ cards"); 282