1/* 2 * comedi/drivers/cb_pcimdda.c 3 * Computer Boards PCIM-DDA06-16 Comedi driver 4 * Author: Calin Culianu <calin@ajvar.org> 5 * 6 * COMEDI - Linux Control and Measurement Device Interface 7 * Copyright (C) 2000 David A. Schleef <ds@schleef.org> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 */ 19/* 20 * Driver: cb_pcimdda 21 * Description: Measurement Computing PCIM-DDA06-16 22 * Devices: [Measurement Computing] PCIM-DDA06-16 (cb_pcimdda) 23 * Author: Calin Culianu <calin@ajvar.org> 24 * Updated: Mon, 14 Apr 2008 15:15:51 +0100 25 * Status: works 26 * 27 * All features of the PCIM-DDA06-16 board are supported. 28 * This board has 6 16-bit AO channels, and the usual 8255 DIO setup. 29 * (24 channels, configurable in banks of 8 and 4, etc.). 30 * This board does not support commands. 31 * 32 * The board has a peculiar way of specifying AO gain/range settings -- You have 33 * 1 jumper bank on the card, which either makes all 6 AO channels either 34 * 5 Volt unipolar, 5V bipolar, 10 Volt unipolar or 10V bipolar. 35 * 36 * Since there is absolutely _no_ way to tell in software how this jumper is set 37 * (well, at least according to the rather thin spec. from Measurement Computing 38 * that comes with the board), the driver assumes the jumper is at its factory 39 * default setting of +/-5V. 40 * 41 * Also of note is the fact that this board features another jumper, whose 42 * state is also completely invisible to software. It toggles two possible AO 43 * output modes on the board: 44 * 45 * - Update Mode: Writing to an AO channel instantaneously updates the actual 46 * signal output by the DAC on the board (this is the factory default). 47 * - Simultaneous XFER Mode: Writing to an AO channel has no effect until 48 * you read from any one of the AO channels. This is useful for loading 49 * all 6 AO values, and then reading from any one of the AO channels on the 50 * device to instantly update all 6 AO values in unison. Useful for some 51 * control apps, I would assume? If your jumper is in this setting, then you 52 * need to issue your comedi_data_write()s to load all the values you want, 53 * then issue one comedi_data_read() on any channel on the AO subdevice 54 * to initiate the simultaneous XFER. 55 * 56 * Configuration Options: not applicable, uses PCI auto config 57 */ 58 59/* 60 * This is a driver for the Computer Boards PCIM-DDA06-16 Analog Output 61 * card. This board has a unique register layout and as such probably 62 * deserves its own driver file. 63 * 64 * It is theoretically possible to integrate this board into the cb_pcidda 65 * file, but since that isn't my code, I didn't want to significantly 66 * modify that file to support this board (I thought it impolite to do so). 67 * 68 * At any rate, if you feel ambitious, please feel free to take 69 * the code out of this file and combine it with a more unified driver 70 * file. 71 * 72 * I would like to thank Timothy Curry <Timothy.Curry@rdec.redstone.army.mil> 73 * for lending me a board so that I could write this driver. 74 * 75 * -Calin Culianu <calin@ajvar.org> 76 */ 77 78#include <linux/module.h> 79 80#include "../comedi_pci.h" 81 82#include "8255.h" 83 84/* device ids of the cards we support -- currently only 1 card supported */ 85#define PCI_ID_PCIM_DDA06_16 0x0053 86 87/* 88 * Register map, 8-bit access only 89 */ 90#define PCIMDDA_DA_CHAN(x) (0x00 + (x) * 2) 91#define PCIMDDA_8255_BASE_REG 0x0c 92 93static int cb_pcimdda_ao_insn_write(struct comedi_device *dev, 94 struct comedi_subdevice *s, 95 struct comedi_insn *insn, 96 unsigned int *data) 97{ 98 unsigned int chan = CR_CHAN(insn->chanspec); 99 unsigned long offset = dev->iobase + PCIMDDA_DA_CHAN(chan); 100 unsigned int val = s->readback[chan]; 101 int i; 102 103 for (i = 0; i < insn->n; i++) { 104 val = data[i]; 105 106 /* 107 * Write the LSB then MSB. 108 * 109 * If the simultaneous xfer mode is selected by the 110 * jumper on the card, a read instruction is needed 111 * in order to initiate the simultaneous transfer. 112 * Otherwise, the DAC will be updated when the MSB 113 * is written. 114 */ 115 outb(val & 0x00ff, offset); 116 outb((val >> 8) & 0x00ff, offset + 1); 117 } 118 s->readback[chan] = val; 119 120 return insn->n; 121} 122 123static int cb_pcimdda_ao_insn_read(struct comedi_device *dev, 124 struct comedi_subdevice *s, 125 struct comedi_insn *insn, 126 unsigned int *data) 127{ 128 unsigned int chan = CR_CHAN(insn->chanspec); 129 130 /* Initiate the simultaneous transfer */ 131 inw(dev->iobase + PCIMDDA_DA_CHAN(chan)); 132 133 return comedi_readback_insn_read(dev, s, insn, data); 134} 135 136static int cb_pcimdda_auto_attach(struct comedi_device *dev, 137 unsigned long context_unused) 138{ 139 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 140 struct comedi_subdevice *s; 141 int ret; 142 143 ret = comedi_pci_enable(dev); 144 if (ret) 145 return ret; 146 dev->iobase = pci_resource_start(pcidev, 3); 147 148 ret = comedi_alloc_subdevices(dev, 2); 149 if (ret) 150 return ret; 151 152 s = &dev->subdevices[0]; 153 /* analog output subdevice */ 154 s->type = COMEDI_SUBD_AO; 155 s->subdev_flags = SDF_WRITABLE | SDF_READABLE; 156 s->n_chan = 6; 157 s->maxdata = 0xffff; 158 s->range_table = &range_bipolar5; 159 s->insn_write = cb_pcimdda_ao_insn_write; 160 s->insn_read = cb_pcimdda_ao_insn_read; 161 162 ret = comedi_alloc_subdev_readback(s); 163 if (ret) 164 return ret; 165 166 s = &dev->subdevices[1]; 167 /* digital i/o subdevice */ 168 return subdev_8255_init(dev, s, NULL, PCIMDDA_8255_BASE_REG); 169} 170 171static struct comedi_driver cb_pcimdda_driver = { 172 .driver_name = "cb_pcimdda", 173 .module = THIS_MODULE, 174 .auto_attach = cb_pcimdda_auto_attach, 175 .detach = comedi_pci_detach, 176}; 177 178static int cb_pcimdda_pci_probe(struct pci_dev *dev, 179 const struct pci_device_id *id) 180{ 181 return comedi_pci_auto_config(dev, &cb_pcimdda_driver, 182 id->driver_data); 183} 184 185static const struct pci_device_id cb_pcimdda_pci_table[] = { 186 { PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_ID_PCIM_DDA06_16) }, 187 { 0 } 188}; 189MODULE_DEVICE_TABLE(pci, cb_pcimdda_pci_table); 190 191static struct pci_driver cb_pcimdda_driver_pci_driver = { 192 .name = "cb_pcimdda", 193 .id_table = cb_pcimdda_pci_table, 194 .probe = cb_pcimdda_pci_probe, 195 .remove = comedi_pci_auto_unconfig, 196}; 197module_comedi_pci_driver(cb_pcimdda_driver, cb_pcimdda_driver_pci_driver); 198 199MODULE_AUTHOR("Calin A. Culianu <calin@rtlab.org>"); 200MODULE_DESCRIPTION("Comedi low-level driver for the Computerboards PCIM-DDA " 201 "series. Currently only supports PCIM-DDA06-16 (which " 202 "also happens to be the only board in this series. :) ) "); 203MODULE_LICENSE("GPL"); 204