adl_pci6208.c revision 818f569fe930c5b8a05d1a44ece3c63c99c13c88
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-6208/6216 Series Multi-channel Analog Output Cards 31Devices: (ADLink) PCI-6208 [adl_pci6208] 32 (ADLink) PCI-6216 [adl_pci6216] 33Author: nsyeow <nsyeow@pd.jaring.my> 34Updated: Fri, 30 Jan 2004 14:44:27 +0800 35Status: untested 36 37Configuration Options: not applicable, uses PCI auto config 38 39References: 40 - ni_660x.c 41 - adl_pci9111.c copied the entire pci setup section 42 - adl_pci9118.c 43*/ 44 45#include <linux/pci.h> 46 47#include "../comedidev.h" 48 49/* 50 * PCI-6208/6216-GL register map 51 */ 52#define PCI6208_AO_CONTROL(x) (0x00 + (2 * (x))) 53#define PCI6208_AO_STATUS 0x00 54#define PCI6208_AO_STATUS_DATA_SEND (1 << 0) 55#define PCI6208_DIO 0x40 56#define PCI6208_DIO_DO_MASK (0x0f) 57#define PCI6208_DIO_DO_SHIFT (0) 58#define PCI6208_DIO_DI_MASK (0xf0) 59#define PCI6208_DIO_DI_SHIFT (4) 60 61#define PCI6208_MAX_AO_CHANNELS 16 62 63enum pci6208_boardid { 64 BOARD_PCI6208, 65 BOARD_PCI6216, 66}; 67 68struct pci6208_board { 69 const char *name; 70 int ao_chans; 71}; 72 73static const struct pci6208_board pci6208_boards[] = { 74 [BOARD_PCI6208] = { 75 .name = "adl_pci6208", 76 .ao_chans = 8, 77 }, 78 [BOARD_PCI6216] = { 79 .name = "adl_pci6216", 80 .ao_chans = 16, 81 }, 82}; 83 84struct pci6208_private { 85 unsigned int ao_readback[PCI6208_MAX_AO_CHANNELS]; 86}; 87 88static int pci6208_ao_winsn(struct comedi_device *dev, 89 struct comedi_subdevice *s, 90 struct comedi_insn *insn, unsigned int *data) 91{ 92 struct pci6208_private *devpriv = dev->private; 93 int chan = CR_CHAN(insn->chanspec); 94 unsigned long invert = 1 << (16 - 1); 95 unsigned long value = 0; 96 unsigned short status; 97 int i; 98 99 for (i = 0; i < insn->n; i++) { 100 value = data[i] ^ invert; 101 102 do { 103 status = inw(dev->iobase + PCI6208_AO_STATUS); 104 } while (status & PCI6208_AO_STATUS_DATA_SEND); 105 106 outw(value, dev->iobase + PCI6208_AO_CONTROL(chan)); 107 } 108 devpriv->ao_readback[chan] = value; 109 110 return insn->n; 111} 112 113static int pci6208_ao_rinsn(struct comedi_device *dev, 114 struct comedi_subdevice *s, 115 struct comedi_insn *insn, unsigned int *data) 116{ 117 struct pci6208_private *devpriv = dev->private; 118 int chan = CR_CHAN(insn->chanspec); 119 int i; 120 121 for (i = 0; i < insn->n; i++) 122 data[i] = devpriv->ao_readback[chan]; 123 124 return insn->n; 125} 126 127static int pci6208_di_insn_bits(struct comedi_device *dev, 128 struct comedi_subdevice *s, 129 struct comedi_insn *insn, 130 unsigned int *data) 131{ 132 unsigned int val; 133 134 val = inw(dev->iobase + PCI6208_DIO); 135 val = (val & PCI6208_DIO_DI_MASK) >> PCI6208_DIO_DI_SHIFT; 136 137 data[1] = val; 138 139 return insn->n; 140} 141 142static int pci6208_do_insn_bits(struct comedi_device *dev, 143 struct comedi_subdevice *s, 144 struct comedi_insn *insn, 145 unsigned int *data) 146{ 147 unsigned int mask = data[0]; 148 unsigned int bits = data[1]; 149 150 if (mask) { 151 s->state &= ~mask; 152 s->state |= (bits & mask); 153 154 outw(s->state, dev->iobase + PCI6208_DIO); 155 } 156 157 data[1] = s->state; 158 159 return insn->n; 160} 161 162static int pci6208_auto_attach(struct comedi_device *dev, 163 unsigned long context) 164{ 165 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 166 const struct pci6208_board *boardinfo = NULL; 167 struct pci6208_private *devpriv; 168 struct comedi_subdevice *s; 169 unsigned int val; 170 int ret; 171 172 if (context < ARRAY_SIZE(pci6208_boards)) 173 boardinfo = &pci6208_boards[context]; 174 if (!boardinfo) 175 return -ENODEV; 176 dev->board_ptr = boardinfo; 177 dev->board_name = boardinfo->name; 178 179 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); 180 if (!devpriv) 181 return -ENOMEM; 182 dev->private = devpriv; 183 184 ret = comedi_pci_enable(dev); 185 if (ret) 186 return ret; 187 dev->iobase = pci_resource_start(pcidev, 2); 188 189 ret = comedi_alloc_subdevices(dev, 3); 190 if (ret) 191 return ret; 192 193 s = &dev->subdevices[0]; 194 /* analog output subdevice */ 195 s->type = COMEDI_SUBD_AO; 196 s->subdev_flags = SDF_WRITABLE; 197 s->n_chan = boardinfo->ao_chans; 198 s->maxdata = 0xffff; 199 s->range_table = &range_bipolar10; 200 s->insn_write = pci6208_ao_winsn; 201 s->insn_read = pci6208_ao_rinsn; 202 203 s = &dev->subdevices[1]; 204 /* digital input subdevice */ 205 s->type = COMEDI_SUBD_DI; 206 s->subdev_flags = SDF_READABLE; 207 s->n_chan = 4; 208 s->maxdata = 1; 209 s->range_table = &range_digital; 210 s->insn_bits = pci6208_di_insn_bits; 211 212 s = &dev->subdevices[2]; 213 /* digital output subdevice */ 214 s->type = COMEDI_SUBD_DO; 215 s->subdev_flags = SDF_WRITABLE; 216 s->n_chan = 4; 217 s->maxdata = 1; 218 s->range_table = &range_digital; 219 s->insn_bits = pci6208_do_insn_bits; 220 221 /* 222 * Get the read back signals from the digital outputs 223 * and save it as the initial state for the subdevice. 224 */ 225 val = inw(dev->iobase + PCI6208_DIO); 226 val = (val & PCI6208_DIO_DO_MASK) >> PCI6208_DIO_DO_SHIFT; 227 s->state = val; 228 s->io_bits = 0x0f; 229 230 dev_info(dev->class_dev, "%s: %s, I/O base=0x%04lx\n", 231 dev->driver->driver_name, dev->board_name, dev->iobase); 232 233 return 0; 234} 235 236static struct comedi_driver adl_pci6208_driver = { 237 .driver_name = "adl_pci6208", 238 .module = THIS_MODULE, 239 .auto_attach = pci6208_auto_attach, 240 .detach = comedi_pci_disable, 241}; 242 243static int adl_pci6208_pci_probe(struct pci_dev *dev, 244 const struct pci_device_id *id) 245{ 246 return comedi_pci_auto_config(dev, &adl_pci6208_driver, 247 id->driver_data); 248} 249 250static DEFINE_PCI_DEVICE_TABLE(adl_pci6208_pci_table) = { 251 { PCI_VDEVICE(ADLINK, 0x6208), BOARD_PCI6208 }, 252 { PCI_VDEVICE(ADLINK, 0x6216), BOARD_PCI6216 }, 253 { 0 } 254}; 255MODULE_DEVICE_TABLE(pci, adl_pci6208_pci_table); 256 257static struct pci_driver adl_pci6208_pci_driver = { 258 .name = "adl_pci6208", 259 .id_table = adl_pci6208_pci_table, 260 .probe = adl_pci6208_pci_probe, 261 .remove = comedi_pci_auto_unconfig, 262}; 263module_comedi_pci_driver(adl_pci6208_driver, adl_pci6208_pci_driver); 264 265MODULE_AUTHOR("Comedi http://www.comedi.org"); 266MODULE_DESCRIPTION("Comedi low-level driver"); 267MODULE_LICENSE("GPL"); 268