2 * comedi/drivers/cb_pcimdas.c
3 * Comedi driver for Computer Boards PCIM-DAS1602/16 and PCIe-DAS1602/16
5 * COMEDI - Linux Control and Measurement Device Interface
6 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
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.
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.
21 * Description: Measurement Computing PCI Migration series boards
22 * Devices: [ComputerBoards] PCIM-DAS1602/16 (cb_pcimdas), PCIe-DAS1602/16
23 * Author: Richard Bytheway
24 * Updated: Mon, 13 Oct 2014 11:57:39 +0000
25 * Status: experimental
27 * Written to support the PCIM-DAS1602/16 and PCIe-DAS1602/16.
29 * Configuration Options:
32 * Manual configuration of PCI(e) cards is not supported; they are configured
35 * Developed from cb_pcidas and skel by Richard Bytheway (mocelet@sucs.org).
36 * Only supports DIO, AO and simple AI in it's present form.
37 * No interrupts, multi channel or FIFO AI,
38 * although the card looks like it could support this.
40 * http://www.mccdaq.com/PDFs/Manuals/pcim-das1602-16.pdf
41 * http://www.mccdaq.com/PDFs/Manuals/pcie-das1602-16.pdf
44 #include <linux/module.h>
45 #include <linux/pci.h>
46 #include <linux/interrupt.h>
48 #include "../comedidev.h"
53 /* Registers for the PCIM-DAS1602/16 and PCIe-DAS1602/16 */
60 /* AI and Counter Constants */
62 #define MAIN_CONN_DIO 1
64 #define ADC_CONV_STAT 3
69 #define CLK8254_1_DATA 8
70 #define CLK8254_2_DATA 9
71 #define CLK8254_3_DATA 10
72 #define CLK8254_CONTROL 11
73 #define USER_COUNTER 12
74 #define RESID_COUNT_H 13
75 #define RESID_COUNT_L 14
77 static const struct comedi_lrange cb_pcimdas_ai_bip_range = {
86 static const struct comedi_lrange cb_pcimdas_ai_uni_range = {
96 * this structure is for data unique to this hardware driver. If
97 * several hardware drivers keep similar information in this structure,
98 * feel free to suggest moving the variable to the struct comedi_device
101 struct cb_pcimdas_private {
107 static int cb_pcimdas_ai_eoc(struct comedi_device *dev,
108 struct comedi_subdevice *s,
109 struct comedi_insn *insn,
110 unsigned long context)
112 struct cb_pcimdas_private *devpriv = dev->private;
115 status = inb(devpriv->BADR3 + 2);
116 if ((status & 0x80) == 0)
121 static int cb_pcimdas_ai_rinsn(struct comedi_device *dev,
122 struct comedi_subdevice *s,
123 struct comedi_insn *insn, unsigned int *data)
125 struct cb_pcimdas_private *devpriv = dev->private;
126 unsigned int chan = CR_CHAN(insn->chanspec);
127 unsigned int range = CR_RANGE(insn->chanspec);
130 unsigned short chanlims;
133 /* only support sw initiated reads from a single channel */
135 /* configure for sw initiated read */
136 d = inb(devpriv->BADR3 + 5);
137 if ((d & 0x03) > 0) { /* only reset if needed. */
139 outb(d, devpriv->BADR3 + 5);
142 /* set bursting off, conversions on */
143 outb(0x01, devpriv->BADR3 + 6);
146 outb(range, devpriv->BADR3 + 7);
149 * write channel limits to multiplexer, set Low (bits 0-3) and
150 * High (bits 4-7) channels to chan.
152 chanlims = chan | (chan << 4);
153 outb(chanlims, devpriv->BADR3 + 0);
155 /* convert n samples */
156 for (n = 0; n < insn->n; n++) {
157 /* trigger conversion */
158 outw(0, devpriv->daqio + 0);
160 /* wait for conversion to end */
161 ret = comedi_timeout(dev, s, insn, cb_pcimdas_ai_eoc, 0);
166 data[n] = inw(devpriv->daqio + 0);
169 /* return the number of samples read/written */
173 static int cb_pcimdas_ao_insn_write(struct comedi_device *dev,
174 struct comedi_subdevice *s,
175 struct comedi_insn *insn,
178 struct cb_pcimdas_private *devpriv = dev->private;
179 unsigned int chan = CR_CHAN(insn->chanspec);
180 unsigned int val = s->readback[chan];
181 unsigned int reg = (chan) ? DAC1_OFFSET : DAC0_OFFSET;
184 for (i = 0; i < insn->n; i++) {
186 outw(val, devpriv->daqio + reg);
188 s->readback[chan] = val;
193 static bool cb_pcimdas_is_ai_se(struct comedi_device *dev)
195 struct cb_pcimdas_private *devpriv = dev->private;
199 * The number of Analog Input channels is set with the
200 * Analog Input Mode Switch on the board. The board can
201 * have 16 single-ended or 8 differential channels.
203 status = inb(devpriv->BADR3 + 2);
204 return status & 0x20;
207 static bool cb_pcimdas_is_ai_uni(struct comedi_device *dev)
209 struct cb_pcimdas_private *devpriv = dev->private;
213 * The Analog Input range polarity is set with the
214 * Analog Input Polarity Switch on the board. The
215 * inputs can be set to Unipolar or Bipolar ranges.
217 status = inb(devpriv->BADR3 + 2);
218 return status & 0x40;
221 static int cb_pcimdas_auto_attach(struct comedi_device *dev,
222 unsigned long context_unused)
224 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
225 struct cb_pcimdas_private *devpriv;
226 struct comedi_subdevice *s;
229 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
233 ret = comedi_pci_enable(dev);
237 devpriv->daqio = pci_resource_start(pcidev, 2);
238 devpriv->BADR3 = pci_resource_start(pcidev, 3);
239 dev->iobase = pci_resource_start(pcidev, 4);
241 ret = comedi_alloc_subdevices(dev, 3);
245 s = &dev->subdevices[0];
246 /* dev->read_subdev=s; */
247 /* analog input subdevice */
248 s->type = COMEDI_SUBD_AI;
249 s->subdev_flags = SDF_READABLE;
250 if (cb_pcimdas_is_ai_se(dev)) {
251 s->subdev_flags |= SDF_GROUND;
254 s->subdev_flags |= SDF_DIFF;
258 s->range_table = cb_pcimdas_is_ai_uni(dev) ? &cb_pcimdas_ai_uni_range
259 : &cb_pcimdas_ai_bip_range;
260 s->len_chanlist = 1; /* This is the maximum chanlist length that */
261 /* the board can handle */
262 s->insn_read = cb_pcimdas_ai_rinsn;
264 s = &dev->subdevices[1];
265 /* analog output subdevice */
266 s->type = COMEDI_SUBD_AO;
267 s->subdev_flags = SDF_WRITABLE;
270 /* ranges are hardware settable, but not software readable. */
271 s->range_table = &range_unknown;
272 s->insn_write = cb_pcimdas_ao_insn_write;
274 ret = comedi_alloc_subdev_readback(s);
278 s = &dev->subdevices[2];
279 /* digital i/o subdevice */
280 ret = subdev_8255_init(dev, s, NULL, 0x00);
287 static struct comedi_driver cb_pcimdas_driver = {
288 .driver_name = "cb_pcimdas",
289 .module = THIS_MODULE,
290 .auto_attach = cb_pcimdas_auto_attach,
291 .detach = comedi_pci_detach,
294 static int cb_pcimdas_pci_probe(struct pci_dev *dev,
295 const struct pci_device_id *id)
297 return comedi_pci_auto_config(dev, &cb_pcimdas_driver,
301 static const struct pci_device_id cb_pcimdas_pci_table[] = {
302 { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0056) }, /* PCIM-DAS1602/16 */
303 { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0115) }, /* PCIe-DAS1602/16 */
306 MODULE_DEVICE_TABLE(pci, cb_pcimdas_pci_table);
308 static struct pci_driver cb_pcimdas_pci_driver = {
309 .name = "cb_pcimdas",
310 .id_table = cb_pcimdas_pci_table,
311 .probe = cb_pcimdas_pci_probe,
312 .remove = comedi_pci_auto_unconfig,
314 module_comedi_pci_driver(cb_pcimdas_driver, cb_pcimdas_pci_driver);
316 MODULE_AUTHOR("Comedi http://www.comedi.org");
317 MODULE_DESCRIPTION("Comedi driver for PCIM-DAS1602/16 and PCIe-DAS1602/16");
318 MODULE_LICENSE("GPL");