adl_pci6208.c revision 270809a80fb610b54de5ee792a607dc37a71195c
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 "../comedidev.h" 46 47/* 48 * ADLINK PCI Device ID's supported by this driver 49 */ 50#define PCI_DEVICE_ID_PCI6208 0x6208 51#define PCI_DEVICE_ID_PCI6216 0x6216 52 53/* 54 * PCI-6208/6216-GL register map 55 */ 56#define PCI6208_AO_CONTROL(x) (0x00 + (2 * (x))) 57#define PCI6208_AO_STATUS 0x00 58#define PCI6208_AO_STATUS_DATA_SEND (1 << 0) 59#define PCI6208_DIO 0x40 60#define PCI6208_DIO_DO_MASK (0x0f) 61#define PCI6208_DIO_DO_SHIFT (0) 62#define PCI6208_DIO_DI_MASK (0xf0) 63#define PCI6208_DIO_DI_SHIFT (4) 64 65#define PCI6208_MAX_AO_CHANNELS 16 66 67struct pci6208_board { 68 const char *name; 69 unsigned short dev_id; 70 int ao_chans; 71}; 72 73static const struct pci6208_board pci6208_boards[] = { 74 { 75 .name = "adl_pci6208", 76 .dev_id = PCI_DEVICE_ID_PCI6208, 77 .ao_chans = 8, 78 }, { 79 .name = "adl_pci6216", 80 .dev_id = PCI_DEVICE_ID_PCI6216, 81 .ao_chans = 16, 82 }, 83}; 84 85struct pci6208_private { 86 unsigned int ao_readback[PCI6208_MAX_AO_CHANNELS]; 87}; 88 89static int pci6208_ao_winsn(struct comedi_device *dev, 90 struct comedi_subdevice *s, 91 struct comedi_insn *insn, unsigned int *data) 92{ 93 struct pci6208_private *devpriv = dev->private; 94 int chan = CR_CHAN(insn->chanspec); 95 unsigned long invert = 1 << (16 - 1); 96 unsigned long value = 0; 97 unsigned short status; 98 int i; 99 100 for (i = 0; i < insn->n; i++) { 101 value = data[i] ^ invert; 102 103 do { 104 status = inw(dev->iobase + PCI6208_AO_STATUS); 105 } while (status & PCI6208_AO_STATUS_DATA_SEND); 106 107 outw(value, dev->iobase + PCI6208_AO_CONTROL(chan)); 108 } 109 devpriv->ao_readback[chan] = value; 110 111 return insn->n; 112} 113 114static int pci6208_ao_rinsn(struct comedi_device *dev, 115 struct comedi_subdevice *s, 116 struct comedi_insn *insn, unsigned int *data) 117{ 118 struct pci6208_private *devpriv = dev->private; 119 int chan = CR_CHAN(insn->chanspec); 120 int i; 121 122 for (i = 0; i < insn->n; i++) 123 data[i] = devpriv->ao_readback[chan]; 124 125 return insn->n; 126} 127 128static int pci6208_di_insn_bits(struct comedi_device *dev, 129 struct comedi_subdevice *s, 130 struct comedi_insn *insn, 131 unsigned int *data) 132{ 133 unsigned int val; 134 135 val = inw(dev->iobase + PCI6208_DIO); 136 val = (val & PCI6208_DIO_DI_MASK) >> PCI6208_DIO_DI_SHIFT; 137 138 data[1] = val; 139 140 return insn->n; 141} 142 143static int pci6208_do_insn_bits(struct comedi_device *dev, 144 struct comedi_subdevice *s, 145 struct comedi_insn *insn, 146 unsigned int *data) 147{ 148 unsigned int mask = data[0]; 149 unsigned int bits = data[1]; 150 151 if (mask) { 152 s->state &= ~mask; 153 s->state |= (bits & mask); 154 155 outw(s->state, dev->iobase + PCI6208_DIO); 156 } 157 158 data[1] = s->state; 159 160 return insn->n; 161} 162 163static const void *pci6208_find_boardinfo(struct comedi_device *dev, 164 struct pci_dev *pcidev) 165{ 166 const struct pci6208_board *boardinfo; 167 int i; 168 169 for (i = 0; i < ARRAY_SIZE(pci6208_boards); i++) { 170 boardinfo = &pci6208_boards[i]; 171 if (boardinfo->dev_id == pcidev->device) 172 return boardinfo; 173 } 174 return NULL; 175} 176 177static int pci6208_attach_pci(struct comedi_device *dev, 178 struct pci_dev *pcidev) 179{ 180 const struct pci6208_board *boardinfo; 181 struct pci6208_private *devpriv; 182 struct comedi_subdevice *s; 183 unsigned int val; 184 int ret; 185 186 comedi_set_hw_dev(dev, &pcidev->dev); 187 188 boardinfo = pci6208_find_boardinfo(dev, pcidev); 189 if (!boardinfo) 190 return -ENODEV; 191 dev->board_ptr = boardinfo; 192 dev->board_name = boardinfo->name; 193 194 ret = alloc_private(dev, sizeof(*devpriv)); 195 if (ret < 0) 196 return ret; 197 devpriv = dev->private; 198 199 ret = comedi_pci_enable(pcidev, dev->driver->driver_name); 200 if (ret) { 201 dev_err(dev->class_dev, 202 "Failed to enable PCI device and request regions\n"); 203 return ret; 204 } 205 dev->iobase = pci_resource_start(pcidev, 2); 206 207 ret = comedi_alloc_subdevices(dev, 3); 208 if (ret) 209 return ret; 210 211 s = dev->subdevices + 0; 212 /* analog output subdevice */ 213 s->type = COMEDI_SUBD_AO; 214 s->subdev_flags = SDF_WRITABLE; 215 s->n_chan = boardinfo->ao_chans; 216 s->maxdata = 0xffff; 217 s->range_table = &range_bipolar10; 218 s->insn_write = pci6208_ao_winsn; 219 s->insn_read = pci6208_ao_rinsn; 220 221 s = dev->subdevices + 1; 222 /* digital input subdevice */ 223 s->type = COMEDI_SUBD_DI; 224 s->subdev_flags = SDF_READABLE; 225 s->n_chan = 4; 226 s->maxdata = 1; 227 s->range_table = &range_digital; 228 s->insn_bits = pci6208_di_insn_bits; 229 230 s = dev->subdevices + 2; 231 /* digital output subdevice */ 232 s->type = COMEDI_SUBD_DO; 233 s->subdev_flags = SDF_WRITABLE; 234 s->n_chan = 4; 235 s->maxdata = 1; 236 s->range_table = &range_digital; 237 s->insn_bits = pci6208_do_insn_bits; 238 239 /* 240 * Get the read back signals from the digital outputs 241 * and save it as the initial state for the subdevice. 242 */ 243 val = inw(dev->iobase + PCI6208_DIO); 244 val = (val & PCI6208_DIO_DO_MASK) >> PCI6208_DIO_DO_SHIFT; 245 s->state = val; 246 s->io_bits = 0x0f; 247 248 dev_info(dev->class_dev, "%s: %s, I/O base=0x%04lx\n", 249 dev->driver->driver_name, dev->board_name, dev->iobase); 250 251 return 0; 252} 253 254static int pci6208_attach(struct comedi_device *dev, 255 struct comedi_devconfig *it) 256{ 257 dev_warn(dev->class_dev, 258 "This driver does not support attach using comedi_config\n"); 259 260 return -ENOSYS; 261} 262 263static void pci6208_detach(struct comedi_device *dev) 264{ 265 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 266 267 if (pcidev) { 268 if (dev->iobase) 269 comedi_pci_disable(pcidev); 270 } 271} 272 273static struct comedi_driver adl_pci6208_driver = { 274 .driver_name = "adl_pci6208", 275 .module = THIS_MODULE, 276 .attach = pci6208_attach, 277 .attach_pci = pci6208_attach_pci, 278 .detach = pci6208_detach, 279}; 280 281static int __devinit adl_pci6208_pci_probe(struct pci_dev *dev, 282 const struct pci_device_id *ent) 283{ 284 return comedi_pci_auto_config(dev, &adl_pci6208_driver); 285} 286 287static void __devexit adl_pci6208_pci_remove(struct pci_dev *dev) 288{ 289 comedi_pci_auto_unconfig(dev); 290} 291 292static DEFINE_PCI_DEVICE_TABLE(adl_pci6208_pci_table) = { 293 { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI6208) }, 294 { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI6216) }, 295 { 0 } 296}; 297MODULE_DEVICE_TABLE(pci, adl_pci6208_pci_table); 298 299static struct pci_driver adl_pci6208_pci_driver = { 300 .name = "adl_pci6208", 301 .id_table = adl_pci6208_pci_table, 302 .probe = adl_pci6208_pci_probe, 303 .remove = __devexit_p(adl_pci6208_pci_remove), 304}; 305module_comedi_pci_driver(adl_pci6208_driver, adl_pci6208_pci_driver); 306 307MODULE_AUTHOR("Comedi http://www.comedi.org"); 308MODULE_DESCRIPTION("Comedi low-level driver"); 309MODULE_LICENSE("GPL"); 310