adl_pci6208.c revision 7b5f9dbcd8eb5de65756a2b1c89d2d5adfb933d2
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/delay.h> 43#include <linux/pci.h> 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 16 60 61enum pci6208_boardid { 62 BOARD_PCI6208, 63 BOARD_PCI6216, 64}; 65 66struct pci6208_board { 67 const char *name; 68 int ao_chans; 69}; 70 71static const struct pci6208_board pci6208_boards[] = { 72 [BOARD_PCI6208] = { 73 .name = "adl_pci6208", 74 .ao_chans = 8, 75 }, 76 [BOARD_PCI6216] = { 77 .name = "adl_pci6216", 78 .ao_chans = 16, 79 }, 80}; 81 82struct pci6208_private { 83 unsigned int ao_readback[PCI6208_MAX_AO_CHANNELS]; 84}; 85 86static int pci6208_ao_wait_for_data_send(struct comedi_device *dev, 87 unsigned int timeout) 88{ 89 unsigned int status; 90 91 while (timeout--) { 92 status = inw(dev->iobase + PCI6208_AO_STATUS); 93 if ((status & PCI6208_AO_STATUS_DATA_SEND) == 0) 94 return 0; 95 udelay(1); 96 } 97 98 return -ETIME; 99} 100 101static int pci6208_ao_insn_write(struct comedi_device *dev, 102 struct comedi_subdevice *s, 103 struct comedi_insn *insn, 104 unsigned int *data) 105{ 106 struct pci6208_private *devpriv = dev->private; 107 unsigned int chan = CR_CHAN(insn->chanspec); 108 unsigned int val = devpriv->ao_readback[chan]; 109 int ret; 110 int i; 111 112 for (i = 0; i < insn->n; i++) { 113 val = data[i]; 114 115 /* D/A transfer rate is 2.2us, wait up to 10us */ 116 ret = pci6208_ao_wait_for_data_send(dev, 10); 117 if (ret) 118 return ret; 119 120 /* the hardware expects two's complement values */ 121 outw(comedi_offset_munge(s, val), 122 dev->iobase + PCI6208_AO_CONTROL(chan)); 123 } 124 devpriv->ao_readback[chan] = val; 125 126 return insn->n; 127} 128 129static int pci6208_ao_insn_read(struct comedi_device *dev, 130 struct comedi_subdevice *s, 131 struct comedi_insn *insn, 132 unsigned int *data) 133{ 134 struct pci6208_private *devpriv = dev->private; 135 unsigned int chan = CR_CHAN(insn->chanspec); 136 int i; 137 138 for (i = 0; i < insn->n; i++) 139 data[i] = devpriv->ao_readback[chan]; 140 141 return insn->n; 142} 143 144static int pci6208_di_insn_bits(struct comedi_device *dev, 145 struct comedi_subdevice *s, 146 struct comedi_insn *insn, 147 unsigned int *data) 148{ 149 unsigned int val; 150 151 val = inw(dev->iobase + PCI6208_DIO); 152 val = (val & PCI6208_DIO_DI_MASK) >> PCI6208_DIO_DI_SHIFT; 153 154 data[1] = val; 155 156 return insn->n; 157} 158 159static int pci6208_do_insn_bits(struct comedi_device *dev, 160 struct comedi_subdevice *s, 161 struct comedi_insn *insn, 162 unsigned int *data) 163{ 164 if (comedi_dio_update_state(s, data)) 165 outw(s->state, dev->iobase + PCI6208_DIO); 166 167 data[1] = s->state; 168 169 return insn->n; 170} 171 172static int pci6208_auto_attach(struct comedi_device *dev, 173 unsigned long context) 174{ 175 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 176 const struct pci6208_board *boardinfo = NULL; 177 struct pci6208_private *devpriv; 178 struct comedi_subdevice *s; 179 unsigned int val; 180 int ret; 181 182 if (context < ARRAY_SIZE(pci6208_boards)) 183 boardinfo = &pci6208_boards[context]; 184 if (!boardinfo) 185 return -ENODEV; 186 dev->board_ptr = boardinfo; 187 dev->board_name = boardinfo->name; 188 189 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 190 if (!devpriv) 191 return -ENOMEM; 192 193 ret = comedi_pci_enable(dev); 194 if (ret) 195 return ret; 196 dev->iobase = pci_resource_start(pcidev, 2); 197 198 ret = comedi_alloc_subdevices(dev, 3); 199 if (ret) 200 return ret; 201 202 s = &dev->subdevices[0]; 203 /* analog output subdevice */ 204 s->type = COMEDI_SUBD_AO; 205 s->subdev_flags = SDF_WRITABLE; 206 s->n_chan = boardinfo->ao_chans; 207 s->maxdata = 0xffff; 208 s->range_table = &range_bipolar10; 209 s->insn_write = pci6208_ao_insn_write; 210 s->insn_read = pci6208_ao_insn_read; 211 212 s = &dev->subdevices[1]; 213 /* digital input subdevice */ 214 s->type = COMEDI_SUBD_DI; 215 s->subdev_flags = SDF_READABLE; 216 s->n_chan = 4; 217 s->maxdata = 1; 218 s->range_table = &range_digital; 219 s->insn_bits = pci6208_di_insn_bits; 220 221 s = &dev->subdevices[2]; 222 /* digital output subdevice */ 223 s->type = COMEDI_SUBD_DO; 224 s->subdev_flags = SDF_WRITABLE; 225 s->n_chan = 4; 226 s->maxdata = 1; 227 s->range_table = &range_digital; 228 s->insn_bits = pci6208_do_insn_bits; 229 230 /* 231 * Get the read back signals from the digital outputs 232 * and save it as the initial state for the subdevice. 233 */ 234 val = inw(dev->iobase + PCI6208_DIO); 235 val = (val & PCI6208_DIO_DO_MASK) >> PCI6208_DIO_DO_SHIFT; 236 s->state = val; 237 238 return 0; 239} 240 241static struct comedi_driver adl_pci6208_driver = { 242 .driver_name = "adl_pci6208", 243 .module = THIS_MODULE, 244 .auto_attach = pci6208_auto_attach, 245 .detach = comedi_pci_disable, 246}; 247 248static int adl_pci6208_pci_probe(struct pci_dev *dev, 249 const struct pci_device_id *id) 250{ 251 return comedi_pci_auto_config(dev, &adl_pci6208_driver, 252 id->driver_data); 253} 254 255static DEFINE_PCI_DEVICE_TABLE(adl_pci6208_pci_table) = { 256 { PCI_VDEVICE(ADLINK, 0x6208), BOARD_PCI6208 }, 257 { PCI_VDEVICE(ADLINK, 0x6216), BOARD_PCI6216 }, 258 { 0 } 259}; 260MODULE_DEVICE_TABLE(pci, adl_pci6208_pci_table); 261 262static struct pci_driver adl_pci6208_pci_driver = { 263 .name = "adl_pci6208", 264 .id_table = adl_pci6208_pci_table, 265 .probe = adl_pci6208_pci_probe, 266 .remove = comedi_pci_auto_unconfig, 267}; 268module_comedi_pci_driver(adl_pci6208_driver, adl_pci6208_pci_driver); 269 270MODULE_AUTHOR("Comedi http://www.comedi.org"); 271MODULE_DESCRIPTION("Comedi low-level driver"); 272MODULE_LICENSE("GPL"); 273