adl_pci6208.c revision 5c67df8b98316fbfede8d4d6bac64d52d4327357
1/* 2 comedi/drivers/adl_pci6208.c 3 4 Hardware driver for ADLink 6208 series cards: 5 card | voltage output | current output 6 -------------+-------------------+--------------- 7 PCI-6208V | 8 channels | - 8 PCI-6216V | 16 channels | - 9 PCI-6208A | 8 channels | 8 channels 10 11 COMEDI - Linux Control and Measurement Device Interface 12 Copyright (C) 2000 David A. Schleef <ds@schleef.org> 13 14 This program is free software; you can redistribute it and/or modify 15 it under the terms of the GNU General Public License as published by 16 the Free Software Foundation; either version 2 of the License, or 17 (at your option) any later version. 18 19 This program is distributed in the hope that it will be useful, 20 but WITHOUT ANY WARRANTY; without even the implied warranty of 21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 GNU General Public License for more details. 23 24 You should have received a copy of the GNU General Public License 25 along with this program; if not, write to the Free Software 26 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 27*/ 28/* 29Driver: adl_pci6208 30Description: ADLink PCI-6208A 31Devices: [ADLink] PCI-6208A (adl_pci6208) 32Author: nsyeow <nsyeow@pd.jaring.my> 33Updated: Fri, 30 Jan 2004 14:44:27 +0800 34Status: untested 35 36Configuration Options: 37 none 38 39References: 40 - ni_660x.c 41 - adl_pci9111.c copied the entire pci setup section 42 - adl_pci9118.c 43*/ 44 45#include "../comedidev.h" 46 47/* 48 * PCI-6208/6216-GL register map 49 */ 50#define PCI6208_AO_CONTROL(x) (0x00 + (2 * (x))) 51#define PCI6208_AO_STATUS 0x00 52#define PCI6208_AO_STATUS_DATA_SEND (1 << 0) 53#define PCI6208_DIO 0x40 54#define PCI6208_DIO_DO_MASK (0x0f) 55#define PCI6208_DIO_DO_SHIFT (0) 56#define PCI6208_DIO_DI_MASK (0xf0) 57#define PCI6208_DIO_DI_SHIFT (4) 58 59#define PCI6208_MAX_AO_CHANNELS 8 60 61struct pci6208_board { 62 const char *name; 63 unsigned short dev_id; 64 int ao_chans; 65}; 66 67static const struct pci6208_board pci6208_boards[] = { 68 { 69 .name = "pci6208a", 70 .dev_id = 0x6208, 71 .ao_chans = 8, 72 }, 73}; 74 75struct pci6208_private { 76 struct pci_dev *pci_dev; 77 unsigned int ao_readback[PCI6208_MAX_AO_CHANNELS]; 78}; 79 80static int pci6208_ao_winsn(struct comedi_device *dev, 81 struct comedi_subdevice *s, 82 struct comedi_insn *insn, unsigned int *data) 83{ 84 struct pci6208_private *devpriv = dev->private; 85 int i = 0, Data_Read; 86 unsigned short chan = CR_CHAN(insn->chanspec); 87 unsigned long invert = 1 << (16 - 1); 88 unsigned long out_value; 89 90 for (i = 0; i < insn->n; i++) { 91 out_value = data[i] ^ invert; 92 do { 93 Data_Read = (inw(dev->iobase) & 1); 94 } while (Data_Read); 95 outw(out_value, dev->iobase + (0x02 * chan)); 96 devpriv->ao_readback[chan] = out_value; 97 } 98 99 return i; 100} 101 102static int pci6208_ao_rinsn(struct comedi_device *dev, 103 struct comedi_subdevice *s, 104 struct comedi_insn *insn, unsigned int *data) 105{ 106 struct pci6208_private *devpriv = dev->private; 107 int i; 108 int chan = CR_CHAN(insn->chanspec); 109 110 for (i = 0; i < insn->n; i++) 111 data[i] = devpriv->ao_readback[chan]; 112 113 return i; 114} 115 116static int pci6208_dio_insn_bits(struct comedi_device *dev, 117 struct comedi_subdevice *s, 118 struct comedi_insn *insn, 119 unsigned int *data) 120{ 121 unsigned int mask = data[0] & PCI6208_DIO_DO_MASK; 122 unsigned int bits = data[1]; 123 124 if (mask) { 125 s->state &= ~mask; 126 s->state |= bits & mask; 127 128 outw(s->state, dev->iobase + PCI6208_DIO); 129 } 130 131 s->state = inw(dev->iobase + PCI6208_DIO); 132 data[1] = s->state; 133 134 return insn->n; 135} 136 137static int pci6208_dio_insn_config(struct comedi_device *dev, 138 struct comedi_subdevice *s, 139 struct comedi_insn *insn, 140 unsigned int *data) 141{ 142 int chan = CR_CHAN(insn->chanspec); 143 unsigned int mask = 1 << chan; 144 145 switch (data[0]) { 146 case INSN_CONFIG_DIO_QUERY: 147 data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT; 148 break; 149 default: 150 return -EINVAL; 151 } 152 153 return insn->n; 154} 155 156static struct pci_dev *pci6208_find_device(struct comedi_device *dev, 157 struct comedi_devconfig *it) 158{ 159 const struct pci6208_board *thisboard; 160 struct pci_dev *pci_dev = NULL; 161 int bus = it->options[0]; 162 int slot = it->options[1]; 163 int i; 164 165 for_each_pci_dev(pci_dev) { 166 if (pci_dev->vendor != PCI_VENDOR_ID_ADLINK) 167 continue; 168 for (i = 0; i < ARRAY_SIZE(pci6208_boards); i++) { 169 thisboard = &pci6208_boards[i]; 170 if (thisboard->dev_id != pci_dev->device) 171 continue; 172 /* was a particular bus/slot requested? */ 173 if (bus || slot) { 174 /* are we on the wrong bus/slot? */ 175 if (pci_dev->bus->number != bus || 176 PCI_SLOT(pci_dev->devfn) != slot) 177 continue; 178 } 179 dev_dbg(dev->class_dev, 180 "Found %s on bus %d, slot, %d, irq=%d\n", 181 thisboard->name, 182 pci_dev->bus->number, 183 PCI_SLOT(pci_dev->devfn), 184 pci_dev->irq); 185 dev->board_ptr = thisboard; 186 return pci_dev; 187 } 188 } 189 dev_err(dev->class_dev, 190 "No supported board found! (req. bus %d, slot %d)\n", 191 bus, slot); 192 return NULL; 193} 194 195static int pci6208_attach(struct comedi_device *dev, 196 struct comedi_devconfig *it) 197{ 198 const struct pci6208_board *thisboard; 199 struct pci6208_private *devpriv; 200 struct comedi_subdevice *s; 201 int ret; 202 203 ret = alloc_private(dev, sizeof(*devpriv)); 204 if (ret < 0) 205 return ret; 206 devpriv = dev->private; 207 208 devpriv->pci_dev = pci6208_find_device(dev, it); 209 if (!devpriv->pci_dev) 210 return -EIO; 211 thisboard = comedi_board(dev); 212 213 ret = comedi_pci_enable(devpriv->pci_dev, "adl_pci6208"); 214 if (ret) { 215 dev_err(dev->class_dev, 216 "Failed to enable PCI device and request regions\n"); 217 return ret; 218 } 219 220 dev->iobase = pci_resource_start(devpriv->pci_dev, 2); 221 222 dev->board_name = thisboard->name; 223 224 ret = comedi_alloc_subdevices(dev, 2); 225 if (ret) 226 return ret; 227 228 s = dev->subdevices + 0; 229 /* analog output subdevice */ 230 s->type = COMEDI_SUBD_AO; 231 s->subdev_flags = SDF_WRITABLE; 232 s->n_chan = thisboard->ao_chans; 233 s->maxdata = 0xffff; 234 s->range_table = &range_bipolar10; 235 s->insn_write = pci6208_ao_winsn; 236 s->insn_read = pci6208_ao_rinsn; 237 238 s = dev->subdevices + 1; 239 /* digital i/o subdevice */ 240 s->type = COMEDI_SUBD_DIO; 241 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 242 s->n_chan = 8; 243 s->maxdata = 1; 244 s->range_table = &range_digital; 245 s->insn_bits = pci6208_dio_insn_bits; 246 s->insn_config = pci6208_dio_insn_config; 247 248 s->io_bits = 0x0f; 249 s->state = inw(dev->iobase + PCI6208_DIO); 250 251 dev_info(dev->class_dev, "%s: %s, I/O base=0x%04lx\n", 252 dev->driver->driver_name, dev->board_name, dev->iobase); 253 254 return 0; 255} 256 257static void pci6208_detach(struct comedi_device *dev) 258{ 259 struct pci6208_private *devpriv = dev->private; 260 261 if (devpriv && devpriv->pci_dev) { 262 if (dev->iobase) 263 comedi_pci_disable(devpriv->pci_dev); 264 pci_dev_put(devpriv->pci_dev); 265 } 266} 267 268static struct comedi_driver adl_pci6208_driver = { 269 .driver_name = "adl_pci6208", 270 .module = THIS_MODULE, 271 .attach = pci6208_attach, 272 .detach = pci6208_detach, 273}; 274 275static int __devinit adl_pci6208_pci_probe(struct pci_dev *dev, 276 const struct pci_device_id *ent) 277{ 278 return comedi_pci_auto_config(dev, &adl_pci6208_driver); 279} 280 281static void __devexit adl_pci6208_pci_remove(struct pci_dev *dev) 282{ 283 comedi_pci_auto_unconfig(dev); 284} 285 286static DEFINE_PCI_DEVICE_TABLE(adl_pci6208_pci_table) = { 287 { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x6208) }, 288 { 0 } 289}; 290MODULE_DEVICE_TABLE(pci, adl_pci6208_pci_table); 291 292static struct pci_driver adl_pci6208_pci_driver = { 293 .name = "adl_pci6208", 294 .id_table = adl_pci6208_pci_table, 295 .probe = adl_pci6208_pci_probe, 296 .remove = __devexit_p(adl_pci6208_pci_remove), 297}; 298module_comedi_pci_driver(adl_pci6208_driver, adl_pci6208_pci_driver); 299 300MODULE_AUTHOR("Comedi http://www.comedi.org"); 301MODULE_DESCRIPTION("Comedi low-level driver"); 302MODULE_LICENSE("GPL"); 303