adl_pci6208.c revision 7c1e5bc7544db67985fed627cba206fc5dee6405
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 chan = CR_CHAN(insn->chanspec); 86 unsigned long invert = 1 << (16 - 1); 87 unsigned long value = 0; 88 unsigned short status; 89 int i; 90 91 for (i = 0; i < insn->n; i++) { 92 value = data[i] ^ invert; 93 94 do { 95 status = inw(dev->iobase + PCI6208_AO_STATUS); 96 } while (status & PCI6208_AO_STATUS_DATA_SEND); 97 98 outw(value, dev->iobase + PCI6208_AO_CONTROL(chan)); 99 } 100 devpriv->ao_readback[chan] = value; 101 102 return insn->n; 103} 104 105static int pci6208_ao_rinsn(struct comedi_device *dev, 106 struct comedi_subdevice *s, 107 struct comedi_insn *insn, unsigned int *data) 108{ 109 struct pci6208_private *devpriv = dev->private; 110 int chan = CR_CHAN(insn->chanspec); 111 int i; 112 113 for (i = 0; i < insn->n; i++) 114 data[i] = devpriv->ao_readback[chan]; 115 116 return insn->n; 117} 118 119static int pci6208_dio_insn_bits(struct comedi_device *dev, 120 struct comedi_subdevice *s, 121 struct comedi_insn *insn, 122 unsigned int *data) 123{ 124 unsigned int mask = data[0] & PCI6208_DIO_DO_MASK; 125 unsigned int bits = data[1]; 126 127 if (mask) { 128 s->state &= ~mask; 129 s->state |= bits & mask; 130 131 outw(s->state, dev->iobase + PCI6208_DIO); 132 } 133 134 s->state = inw(dev->iobase + PCI6208_DIO); 135 data[1] = s->state; 136 137 return insn->n; 138} 139 140static int pci6208_dio_insn_config(struct comedi_device *dev, 141 struct comedi_subdevice *s, 142 struct comedi_insn *insn, 143 unsigned int *data) 144{ 145 int chan = CR_CHAN(insn->chanspec); 146 unsigned int mask = 1 << chan; 147 148 switch (data[0]) { 149 case INSN_CONFIG_DIO_QUERY: 150 data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT; 151 break; 152 default: 153 return -EINVAL; 154 } 155 156 return insn->n; 157} 158 159static struct pci_dev *pci6208_find_device(struct comedi_device *dev, 160 struct comedi_devconfig *it) 161{ 162 const struct pci6208_board *thisboard; 163 struct pci_dev *pci_dev = NULL; 164 int bus = it->options[0]; 165 int slot = it->options[1]; 166 int i; 167 168 for_each_pci_dev(pci_dev) { 169 if (pci_dev->vendor != PCI_VENDOR_ID_ADLINK) 170 continue; 171 for (i = 0; i < ARRAY_SIZE(pci6208_boards); i++) { 172 thisboard = &pci6208_boards[i]; 173 if (thisboard->dev_id != pci_dev->device) 174 continue; 175 /* was a particular bus/slot requested? */ 176 if (bus || slot) { 177 /* are we on the wrong bus/slot? */ 178 if (pci_dev->bus->number != bus || 179 PCI_SLOT(pci_dev->devfn) != slot) 180 continue; 181 } 182 dev_dbg(dev->class_dev, 183 "Found %s on bus %d, slot, %d, irq=%d\n", 184 thisboard->name, 185 pci_dev->bus->number, 186 PCI_SLOT(pci_dev->devfn), 187 pci_dev->irq); 188 dev->board_ptr = thisboard; 189 return pci_dev; 190 } 191 } 192 dev_err(dev->class_dev, 193 "No supported board found! (req. bus %d, slot %d)\n", 194 bus, slot); 195 return NULL; 196} 197 198static int pci6208_attach(struct comedi_device *dev, 199 struct comedi_devconfig *it) 200{ 201 const struct pci6208_board *thisboard; 202 struct pci6208_private *devpriv; 203 struct comedi_subdevice *s; 204 int ret; 205 206 ret = alloc_private(dev, sizeof(*devpriv)); 207 if (ret < 0) 208 return ret; 209 devpriv = dev->private; 210 211 devpriv->pci_dev = pci6208_find_device(dev, it); 212 if (!devpriv->pci_dev) 213 return -EIO; 214 thisboard = comedi_board(dev); 215 216 ret = comedi_pci_enable(devpriv->pci_dev, "adl_pci6208"); 217 if (ret) { 218 dev_err(dev->class_dev, 219 "Failed to enable PCI device and request regions\n"); 220 return ret; 221 } 222 223 dev->iobase = pci_resource_start(devpriv->pci_dev, 2); 224 225 dev->board_name = thisboard->name; 226 227 ret = comedi_alloc_subdevices(dev, 2); 228 if (ret) 229 return ret; 230 231 s = dev->subdevices + 0; 232 /* analog output subdevice */ 233 s->type = COMEDI_SUBD_AO; 234 s->subdev_flags = SDF_WRITABLE; 235 s->n_chan = thisboard->ao_chans; 236 s->maxdata = 0xffff; 237 s->range_table = &range_bipolar10; 238 s->insn_write = pci6208_ao_winsn; 239 s->insn_read = pci6208_ao_rinsn; 240 241 s = dev->subdevices + 1; 242 /* digital i/o subdevice */ 243 s->type = COMEDI_SUBD_DIO; 244 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 245 s->n_chan = 8; 246 s->maxdata = 1; 247 s->range_table = &range_digital; 248 s->insn_bits = pci6208_dio_insn_bits; 249 s->insn_config = pci6208_dio_insn_config; 250 251 s->io_bits = 0x0f; 252 s->state = inw(dev->iobase + PCI6208_DIO); 253 254 dev_info(dev->class_dev, "%s: %s, I/O base=0x%04lx\n", 255 dev->driver->driver_name, dev->board_name, dev->iobase); 256 257 return 0; 258} 259 260static void pci6208_detach(struct comedi_device *dev) 261{ 262 struct pci6208_private *devpriv = dev->private; 263 264 if (devpriv && devpriv->pci_dev) { 265 if (dev->iobase) 266 comedi_pci_disable(devpriv->pci_dev); 267 pci_dev_put(devpriv->pci_dev); 268 } 269} 270 271static struct comedi_driver adl_pci6208_driver = { 272 .driver_name = "adl_pci6208", 273 .module = THIS_MODULE, 274 .attach = pci6208_attach, 275 .detach = pci6208_detach, 276}; 277 278static int __devinit adl_pci6208_pci_probe(struct pci_dev *dev, 279 const struct pci_device_id *ent) 280{ 281 return comedi_pci_auto_config(dev, &adl_pci6208_driver); 282} 283 284static void __devexit adl_pci6208_pci_remove(struct pci_dev *dev) 285{ 286 comedi_pci_auto_unconfig(dev); 287} 288 289static DEFINE_PCI_DEVICE_TABLE(adl_pci6208_pci_table) = { 290 { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x6208) }, 291 { 0 } 292}; 293MODULE_DEVICE_TABLE(pci, adl_pci6208_pci_table); 294 295static struct pci_driver adl_pci6208_pci_driver = { 296 .name = "adl_pci6208", 297 .id_table = adl_pci6208_pci_table, 298 .probe = adl_pci6208_pci_probe, 299 .remove = __devexit_p(adl_pci6208_pci_remove), 300}; 301module_comedi_pci_driver(adl_pci6208_driver, adl_pci6208_pci_driver); 302 303MODULE_AUTHOR("Comedi http://www.comedi.org"); 304MODULE_DESCRIPTION("Comedi low-level driver"); 305MODULE_LICENSE("GPL"); 306