1/* 2 * adl_pci6208.c 3 * Comedi driver for ADLink 6208 series cards 4 * 5 * COMEDI - Linux Control and Measurement Device Interface 6 * Copyright (C) 2000 David A. Schleef <ds@schleef.org> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 */ 18 19/* 20 * Driver: adl_pci6208 21 * Description: ADLink PCI-6208/6216 Series Multi-channel Analog Output Cards 22 * Devices: (ADLink) PCI-6208 [adl_pci6208] 23 * (ADLink) PCI-6216 [adl_pci6216] 24 * Author: nsyeow <nsyeow@pd.jaring.my> 25 * Updated: Fri, 30 Jan 2004 14:44:27 +0800 26 * Status: untested 27 * 28 * Configuration Options: not applicable, uses PCI auto config 29 */ 30 31#include <linux/module.h> 32#include <linux/delay.h> 33#include <linux/pci.h> 34 35#include "../comedidev.h" 36 37/* 38 * PCI-6208/6216-GL register map 39 */ 40#define PCI6208_AO_CONTROL(x) (0x00 + (2 * (x))) 41#define PCI6208_AO_STATUS 0x00 42#define PCI6208_AO_STATUS_DATA_SEND (1 << 0) 43#define PCI6208_DIO 0x40 44#define PCI6208_DIO_DO_MASK (0x0f) 45#define PCI6208_DIO_DO_SHIFT (0) 46#define PCI6208_DIO_DI_MASK (0xf0) 47#define PCI6208_DIO_DI_SHIFT (4) 48 49enum pci6208_boardid { 50 BOARD_PCI6208, 51 BOARD_PCI6216, 52}; 53 54struct pci6208_board { 55 const char *name; 56 int ao_chans; 57}; 58 59static const struct pci6208_board pci6208_boards[] = { 60 [BOARD_PCI6208] = { 61 .name = "adl_pci6208", 62 .ao_chans = 8, 63 }, 64 [BOARD_PCI6216] = { 65 .name = "adl_pci6216", 66 .ao_chans = 16, 67 }, 68}; 69 70static int pci6208_ao_eoc(struct comedi_device *dev, 71 struct comedi_subdevice *s, 72 struct comedi_insn *insn, 73 unsigned long context) 74{ 75 unsigned int status; 76 77 status = inw(dev->iobase + PCI6208_AO_STATUS); 78 if ((status & PCI6208_AO_STATUS_DATA_SEND) == 0) 79 return 0; 80 return -EBUSY; 81} 82 83static int pci6208_ao_insn_write(struct comedi_device *dev, 84 struct comedi_subdevice *s, 85 struct comedi_insn *insn, 86 unsigned int *data) 87{ 88 unsigned int chan = CR_CHAN(insn->chanspec); 89 unsigned int val = s->readback[chan]; 90 int ret; 91 int i; 92 93 for (i = 0; i < insn->n; i++) { 94 val = data[i]; 95 96 /* D/A transfer rate is 2.2us */ 97 ret = comedi_timeout(dev, s, insn, pci6208_ao_eoc, 0); 98 if (ret) 99 return ret; 100 101 /* the hardware expects two's complement values */ 102 outw(comedi_offset_munge(s, val), 103 dev->iobase + PCI6208_AO_CONTROL(chan)); 104 105 s->readback[chan] = val; 106 } 107 108 return insn->n; 109} 110 111static int pci6208_di_insn_bits(struct comedi_device *dev, 112 struct comedi_subdevice *s, 113 struct comedi_insn *insn, 114 unsigned int *data) 115{ 116 unsigned int val; 117 118 val = inw(dev->iobase + PCI6208_DIO); 119 val = (val & PCI6208_DIO_DI_MASK) >> PCI6208_DIO_DI_SHIFT; 120 121 data[1] = val; 122 123 return insn->n; 124} 125 126static int pci6208_do_insn_bits(struct comedi_device *dev, 127 struct comedi_subdevice *s, 128 struct comedi_insn *insn, 129 unsigned int *data) 130{ 131 if (comedi_dio_update_state(s, data)) 132 outw(s->state, dev->iobase + PCI6208_DIO); 133 134 data[1] = s->state; 135 136 return insn->n; 137} 138 139static int pci6208_auto_attach(struct comedi_device *dev, 140 unsigned long context) 141{ 142 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 143 const struct pci6208_board *boardinfo = NULL; 144 struct comedi_subdevice *s; 145 unsigned int val; 146 int ret; 147 148 if (context < ARRAY_SIZE(pci6208_boards)) 149 boardinfo = &pci6208_boards[context]; 150 if (!boardinfo) 151 return -ENODEV; 152 dev->board_ptr = boardinfo; 153 dev->board_name = boardinfo->name; 154 155 ret = comedi_pci_enable(dev); 156 if (ret) 157 return ret; 158 dev->iobase = pci_resource_start(pcidev, 2); 159 160 ret = comedi_alloc_subdevices(dev, 3); 161 if (ret) 162 return ret; 163 164 s = &dev->subdevices[0]; 165 /* analog output subdevice */ 166 s->type = COMEDI_SUBD_AO; 167 s->subdev_flags = SDF_WRITABLE; 168 s->n_chan = boardinfo->ao_chans; 169 s->maxdata = 0xffff; 170 s->range_table = &range_bipolar10; 171 s->insn_write = pci6208_ao_insn_write; 172 s->insn_read = comedi_readback_insn_read; 173 174 ret = comedi_alloc_subdev_readback(s); 175 if (ret) 176 return ret; 177 178 s = &dev->subdevices[1]; 179 /* digital input subdevice */ 180 s->type = COMEDI_SUBD_DI; 181 s->subdev_flags = SDF_READABLE; 182 s->n_chan = 4; 183 s->maxdata = 1; 184 s->range_table = &range_digital; 185 s->insn_bits = pci6208_di_insn_bits; 186 187 s = &dev->subdevices[2]; 188 /* digital output subdevice */ 189 s->type = COMEDI_SUBD_DO; 190 s->subdev_flags = SDF_WRITABLE; 191 s->n_chan = 4; 192 s->maxdata = 1; 193 s->range_table = &range_digital; 194 s->insn_bits = pci6208_do_insn_bits; 195 196 /* 197 * Get the read back signals from the digital outputs 198 * and save it as the initial state for the subdevice. 199 */ 200 val = inw(dev->iobase + PCI6208_DIO); 201 val = (val & PCI6208_DIO_DO_MASK) >> PCI6208_DIO_DO_SHIFT; 202 s->state = val; 203 204 return 0; 205} 206 207static struct comedi_driver adl_pci6208_driver = { 208 .driver_name = "adl_pci6208", 209 .module = THIS_MODULE, 210 .auto_attach = pci6208_auto_attach, 211 .detach = comedi_pci_detach, 212}; 213 214static int adl_pci6208_pci_probe(struct pci_dev *dev, 215 const struct pci_device_id *id) 216{ 217 return comedi_pci_auto_config(dev, &adl_pci6208_driver, 218 id->driver_data); 219} 220 221static const struct pci_device_id adl_pci6208_pci_table[] = { 222 { PCI_VDEVICE(ADLINK, 0x6208), BOARD_PCI6208 }, 223 { PCI_VDEVICE(ADLINK, 0x6216), BOARD_PCI6216 }, 224 { 0 } 225}; 226MODULE_DEVICE_TABLE(pci, adl_pci6208_pci_table); 227 228static struct pci_driver adl_pci6208_pci_driver = { 229 .name = "adl_pci6208", 230 .id_table = adl_pci6208_pci_table, 231 .probe = adl_pci6208_pci_probe, 232 .remove = comedi_pci_auto_unconfig, 233}; 234module_comedi_pci_driver(adl_pci6208_driver, adl_pci6208_pci_driver); 235 236MODULE_AUTHOR("Comedi http://www.comedi.org"); 237MODULE_DESCRIPTION("Comedi driver for ADLink 6208 series cards"); 238MODULE_LICENSE("GPL"); 239