dyna_pci10xx.c revision 4ced969ac63118fa4efdc367aef08215488f1eb0
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 = { 3, { 46 BIP_RANGE(10), 47 BIP_RANGE(5), 48 UNI_RANGE(10) 49 } 50}; 51 52static const char range_codes_pci1050_ai[] = { 0x00, 0x10, 0x30 }; 53 54struct dyna_pci10xx_private { 55 struct mutex mutex; 56 unsigned long BADR3; 57}; 58 59/******************************************************************************/ 60/************************** READ WRITE FUNCTIONS ******************************/ 61/******************************************************************************/ 62 63/* analog input callback */ 64static int dyna_pci10xx_insn_read_ai(struct comedi_device *dev, 65 struct comedi_subdevice *s, 66 struct comedi_insn *insn, unsigned int *data) 67{ 68 struct dyna_pci10xx_private *devpriv = dev->private; 69 int n, counter; 70 u16 d = 0; 71 unsigned int chan, range; 72 73 /* get the channel number and range */ 74 chan = CR_CHAN(insn->chanspec); 75 range = range_codes_pci1050_ai[CR_RANGE((insn->chanspec))]; 76 77 mutex_lock(&devpriv->mutex); 78 /* convert n samples */ 79 for (n = 0; n < insn->n; n++) { 80 /* trigger conversion */ 81 smp_mb(); 82 outw_p(0x0000 + range + chan, dev->iobase + 2); 83 udelay(10); 84 /* read data */ 85 for (counter = 0; counter < READ_TIMEOUT; counter++) { 86 d = inw_p(dev->iobase); 87 88 /* check if read is successful if the EOC bit is set */ 89 if (d & (1 << 15)) 90 goto conv_finish; 91 } 92 data[n] = 0; 93 dev_dbg(dev->class_dev, "timeout reading analog input\n"); 94 continue; 95conv_finish: 96 /* mask the first 4 bits - EOC bits */ 97 d &= 0x0FFF; 98 data[n] = d; 99 } 100 mutex_unlock(&devpriv->mutex); 101 102 /* return the number of samples read/written */ 103 return n; 104} 105 106/* analog output callback */ 107static int dyna_pci10xx_insn_write_ao(struct comedi_device *dev, 108 struct comedi_subdevice *s, 109 struct comedi_insn *insn, unsigned int *data) 110{ 111 struct dyna_pci10xx_private *devpriv = dev->private; 112 int n; 113 unsigned int chan, range; 114 115 chan = CR_CHAN(insn->chanspec); 116 range = range_codes_pci1050_ai[CR_RANGE((insn->chanspec))]; 117 118 mutex_lock(&devpriv->mutex); 119 for (n = 0; n < insn->n; n++) { 120 smp_mb(); 121 /* trigger conversion and write data */ 122 outw_p(data[n], dev->iobase); 123 udelay(10); 124 } 125 mutex_unlock(&devpriv->mutex); 126 return n; 127} 128 129/* digital input bit interface */ 130static int dyna_pci10xx_di_insn_bits(struct comedi_device *dev, 131 struct comedi_subdevice *s, 132 struct comedi_insn *insn, unsigned int *data) 133{ 134 struct dyna_pci10xx_private *devpriv = dev->private; 135 u16 d = 0; 136 137 mutex_lock(&devpriv->mutex); 138 smp_mb(); 139 d = inw_p(devpriv->BADR3); 140 udelay(10); 141 142 /* on return the data[0] contains output and data[1] contains input */ 143 data[1] = d; 144 data[0] = s->state; 145 mutex_unlock(&devpriv->mutex); 146 return insn->n; 147} 148 149static int dyna_pci10xx_do_insn_bits(struct comedi_device *dev, 150 struct comedi_subdevice *s, 151 struct comedi_insn *insn, 152 unsigned int *data) 153{ 154 struct dyna_pci10xx_private *devpriv = dev->private; 155 156 mutex_lock(&devpriv->mutex); 157 if (comedi_dio_update_state(s, data)) { 158 smp_mb(); 159 outw_p(s->state, devpriv->BADR3); 160 udelay(10); 161 } 162 163 data[1] = s->state; 164 mutex_unlock(&devpriv->mutex); 165 166 return insn->n; 167} 168 169static int dyna_pci10xx_auto_attach(struct comedi_device *dev, 170 unsigned long context_unused) 171{ 172 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 173 struct dyna_pci10xx_private *devpriv; 174 struct comedi_subdevice *s; 175 int ret; 176 177 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 178 if (!devpriv) 179 return -ENOMEM; 180 181 ret = comedi_pci_enable(dev); 182 if (ret) 183 return ret; 184 dev->iobase = pci_resource_start(pcidev, 2); 185 devpriv->BADR3 = pci_resource_start(pcidev, 3); 186 187 mutex_init(&devpriv->mutex); 188 189 ret = comedi_alloc_subdevices(dev, 4); 190 if (ret) 191 return ret; 192 193 /* analog input */ 194 s = &dev->subdevices[0]; 195 s->type = COMEDI_SUBD_AI; 196 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; 197 s->n_chan = 16; 198 s->maxdata = 0x0FFF; 199 s->range_table = &range_pci1050_ai; 200 s->len_chanlist = 16; 201 s->insn_read = dyna_pci10xx_insn_read_ai; 202 203 /* analog output */ 204 s = &dev->subdevices[1]; 205 s->type = COMEDI_SUBD_AO; 206 s->subdev_flags = SDF_WRITABLE; 207 s->n_chan = 16; 208 s->maxdata = 0x0FFF; 209 s->range_table = &range_unipolar10; 210 s->len_chanlist = 16; 211 s->insn_write = dyna_pci10xx_insn_write_ao; 212 213 /* digital input */ 214 s = &dev->subdevices[2]; 215 s->type = COMEDI_SUBD_DI; 216 s->subdev_flags = SDF_READABLE | SDF_GROUND; 217 s->n_chan = 16; 218 s->maxdata = 1; 219 s->range_table = &range_digital; 220 s->len_chanlist = 16; 221 s->insn_bits = dyna_pci10xx_di_insn_bits; 222 223 /* digital output */ 224 s = &dev->subdevices[3]; 225 s->type = COMEDI_SUBD_DO; 226 s->subdev_flags = SDF_WRITABLE | SDF_GROUND; 227 s->n_chan = 16; 228 s->maxdata = 1; 229 s->range_table = &range_digital; 230 s->len_chanlist = 16; 231 s->state = 0; 232 s->insn_bits = dyna_pci10xx_do_insn_bits; 233 234 dev_info(dev->class_dev, "%s attached\n", dev->board_name); 235 236 return 0; 237} 238 239static void dyna_pci10xx_detach(struct comedi_device *dev) 240{ 241 struct dyna_pci10xx_private *devpriv = dev->private; 242 243 if (devpriv) 244 mutex_destroy(&devpriv->mutex); 245 comedi_pci_disable(dev); 246} 247 248static struct comedi_driver dyna_pci10xx_driver = { 249 .driver_name = "dyna_pci10xx", 250 .module = THIS_MODULE, 251 .auto_attach = dyna_pci10xx_auto_attach, 252 .detach = dyna_pci10xx_detach, 253}; 254 255static int dyna_pci10xx_pci_probe(struct pci_dev *dev, 256 const struct pci_device_id *id) 257{ 258 return comedi_pci_auto_config(dev, &dyna_pci10xx_driver, 259 id->driver_data); 260} 261 262static DEFINE_PCI_DEVICE_TABLE(dyna_pci10xx_pci_table) = { 263 { PCI_DEVICE(PCI_VENDOR_ID_PLX, 0x1050) }, 264 { 0 } 265}; 266MODULE_DEVICE_TABLE(pci, dyna_pci10xx_pci_table); 267 268static struct pci_driver dyna_pci10xx_pci_driver = { 269 .name = "dyna_pci10xx", 270 .id_table = dyna_pci10xx_pci_table, 271 .probe = dyna_pci10xx_pci_probe, 272 .remove = comedi_pci_auto_unconfig, 273}; 274module_comedi_pci_driver(dyna_pci10xx_driver, dyna_pci10xx_pci_driver); 275 276MODULE_LICENSE("GPL"); 277MODULE_AUTHOR("Prashant Shah <pshah.mumbai@gmail.com>"); 278MODULE_DESCRIPTION("Comedi based drivers for Dynalog PCI DAQ cards"); 279