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