2 comedi/drivers/adv_pci1724.c
3 This is a driver for the Advantech PCI-1724U card.
5 Author: Frank Mori Hess <fmh6jj@gmail.com>
6 Copyright (C) 2013 GnuBIO Inc
8 COMEDI - Linux Control and Measurement Device Interface
9 Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
25 Description: Advantech PCI-1724U
26 Author: Frank Mori Hess <fmh6jj@gmail.com>
29 Devices: [Advantech] PCI-1724U (adv_pci1724)
31 Subdevice 0 is the analog output.
32 Subdevice 1 is the offset calibration for the analog output.
33 Subdevice 2 is the gain calibration for the analog output.
35 The calibration offset and gains have quite a large effect
36 on the analog output, so it is possible to adjust the analog output to
37 have an output range significantly different from the board's
38 nominal output ranges. For a calibrated +/- 10V range, the analog
39 output's offset will be set somewhere near mid-range (0x2000) and its
40 gain will be near maximum (0x3fff).
42 There is really no difference between the board's documented 0-20mA
43 versus 4-20mA output ranges. To pick one or the other is simply a matter
44 of adjusting the offset and gain calibration until the board outputs in
47 Configuration options:
50 Manual configuration of comedi devices is not supported by this driver;
51 supported PCI devices are configured as comedi devices automatically.
55 #include <linux/module.h>
56 #include <linux/delay.h>
57 #include <linux/pci.h>
59 #include "../comedidev.h"
61 #define PCI_VENDOR_ID_ADVANTECH 0x13fe
63 #define NUM_AO_CHANNELS 32
65 /* register offsets */
66 enum board_registers {
67 DAC_CONTROL_REG = 0x0,
68 SYNC_OUTPUT_REG = 0x4,
69 EEPROM_CONTROL_REG = 0x8,
70 SYNC_OUTPUT_TRIGGER_REG = 0xc,
74 /* bit definitions for registers */
75 enum dac_control_contents {
76 DAC_DATA_MASK = 0x3fff,
77 DAC_DESTINATION_MASK = 0xc000,
78 DAC_NORMAL_MODE = 0xc000,
79 DAC_OFFSET_MODE = 0x8000,
80 DAC_GAIN_MODE = 0x4000,
81 DAC_CHANNEL_SELECT_MASK = 0xf0000,
82 DAC_GROUP_SELECT_MASK = 0xf00000
85 static uint32_t dac_data_bits(uint16_t dac_data)
87 return dac_data & DAC_DATA_MASK;
90 static uint32_t dac_channel_select_bits(unsigned channel)
92 return (channel << 16) & DAC_CHANNEL_SELECT_MASK;
95 static uint32_t dac_group_select_bits(unsigned group)
97 return (1 << (20 + group)) & DAC_GROUP_SELECT_MASK;
100 static uint32_t dac_channel_and_group_select_bits(unsigned comedi_channel)
102 return dac_channel_select_bits(comedi_channel % 8) |
103 dac_group_select_bits(comedi_channel / 8);
106 enum sync_output_contents {
108 DAC_BUSY = 0x2, /* dac state machine is not ready */
111 enum sync_output_trigger_contents {
112 SYNC_TRIGGER_BITS = 0x0 /* any value works */
115 enum board_id_contents {
119 static const struct comedi_lrange ao_ranges_1724 = { 4,
128 /* this structure is for data unique to this hardware driver. */
129 struct adv_pci1724_private {
130 int ao_value[NUM_AO_CHANNELS];
131 int offset_value[NUM_AO_CHANNELS];
132 int gain_value[NUM_AO_CHANNELS];
135 static int wait_for_dac_idle(struct comedi_device *dev)
137 static const int timeout = 10000;
140 for (i = 0; i < timeout; ++i) {
141 if ((inl(dev->iobase + SYNC_OUTPUT_REG) & DAC_BUSY) == 0)
146 comedi_error(dev, "Timed out waiting for dac to become idle.");
152 static int set_dac(struct comedi_device *dev, unsigned mode, unsigned channel,
156 unsigned control_bits;
158 retval = wait_for_dac_idle(dev);
163 control_bits |= dac_channel_and_group_select_bits(channel);
164 control_bits |= dac_data_bits(data);
165 outl(control_bits, dev->iobase + DAC_CONTROL_REG);
169 static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
170 struct comedi_insn *insn, unsigned int *data)
172 struct adv_pci1724_private *devpriv = dev->private;
173 int channel = CR_CHAN(insn->chanspec);
177 /* turn off synchronous mode */
178 outl(0, dev->iobase + SYNC_OUTPUT_REG);
180 for (i = 0; i < insn->n; ++i) {
181 retval = set_dac(dev, DAC_NORMAL_MODE, channel, data[i]);
184 devpriv->ao_value[channel] = data[i];
189 static int ao_readback_insn(struct comedi_device *dev,
190 struct comedi_subdevice *s,
191 struct comedi_insn *insn, unsigned int *data)
193 struct adv_pci1724_private *devpriv = dev->private;
194 int channel = CR_CHAN(insn->chanspec);
197 if (devpriv->ao_value[channel] < 0) {
199 "Cannot read back channels which have not yet been written to.");
202 for (i = 0; i < insn->n; i++)
203 data[i] = devpriv->ao_value[channel];
208 static int offset_write_insn(struct comedi_device *dev,
209 struct comedi_subdevice *s,
210 struct comedi_insn *insn, unsigned int *data)
212 struct adv_pci1724_private *devpriv = dev->private;
213 int channel = CR_CHAN(insn->chanspec);
217 /* turn off synchronous mode */
218 outl(0, dev->iobase + SYNC_OUTPUT_REG);
220 for (i = 0; i < insn->n; ++i) {
221 retval = set_dac(dev, DAC_OFFSET_MODE, channel, data[i]);
224 devpriv->offset_value[channel] = data[i];
230 static int offset_read_insn(struct comedi_device *dev,
231 struct comedi_subdevice *s,
232 struct comedi_insn *insn, unsigned int *data)
234 struct adv_pci1724_private *devpriv = dev->private;
235 unsigned int channel = CR_CHAN(insn->chanspec);
238 if (devpriv->offset_value[channel] < 0) {
240 "Cannot read back channels which have not yet been written to.");
243 for (i = 0; i < insn->n; i++)
244 data[i] = devpriv->offset_value[channel];
249 static int gain_write_insn(struct comedi_device *dev,
250 struct comedi_subdevice *s,
251 struct comedi_insn *insn, unsigned int *data)
253 struct adv_pci1724_private *devpriv = dev->private;
254 int channel = CR_CHAN(insn->chanspec);
258 /* turn off synchronous mode */
259 outl(0, dev->iobase + SYNC_OUTPUT_REG);
261 for (i = 0; i < insn->n; ++i) {
262 retval = set_dac(dev, DAC_GAIN_MODE, channel, data[i]);
265 devpriv->gain_value[channel] = data[i];
271 static int gain_read_insn(struct comedi_device *dev,
272 struct comedi_subdevice *s, struct comedi_insn *insn,
275 struct adv_pci1724_private *devpriv = dev->private;
276 unsigned int channel = CR_CHAN(insn->chanspec);
279 if (devpriv->gain_value[channel] < 0) {
281 "Cannot read back channels which have not yet been written to.");
284 for (i = 0; i < insn->n; i++)
285 data[i] = devpriv->gain_value[channel];
290 /* Allocate and initialize the subdevice structures.
292 static int setup_subdevices(struct comedi_device *dev)
294 struct comedi_subdevice *s;
297 ret = comedi_alloc_subdevices(dev, 3);
301 /* analog output subdevice */
302 s = &dev->subdevices[0];
303 s->type = COMEDI_SUBD_AO;
304 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND;
305 s->n_chan = NUM_AO_CHANNELS;
307 s->range_table = &ao_ranges_1724;
308 s->insn_read = ao_readback_insn;
309 s->insn_write = ao_winsn;
311 /* offset calibration */
312 s = &dev->subdevices[1];
313 s->type = COMEDI_SUBD_CALIB;
314 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
315 s->n_chan = NUM_AO_CHANNELS;
316 s->insn_read = offset_read_insn;
317 s->insn_write = offset_write_insn;
320 /* gain calibration */
321 s = &dev->subdevices[2];
322 s->type = COMEDI_SUBD_CALIB;
323 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
324 s->n_chan = NUM_AO_CHANNELS;
325 s->insn_read = gain_read_insn;
326 s->insn_write = gain_write_insn;
332 static int adv_pci1724_auto_attach(struct comedi_device *dev,
333 unsigned long context_unused)
335 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
336 struct adv_pci1724_private *devpriv;
339 unsigned int board_id;
341 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
345 /* init software copies of output values to indicate we don't know
346 * what the output value is since it has never been written. */
347 for (i = 0; i < NUM_AO_CHANNELS; ++i) {
348 devpriv->ao_value[i] = -1;
349 devpriv->offset_value[i] = -1;
350 devpriv->gain_value[i] = -1;
353 retval = comedi_pci_enable(dev);
357 dev->iobase = pci_resource_start(pcidev, 2);
358 board_id = inl(dev->iobase + BOARD_ID_REG) & BOARD_ID_MASK;
359 dev_info(dev->class_dev, "board id: %d\n", board_id);
361 retval = setup_subdevices(dev);
365 dev_info(dev->class_dev, "%s (pci %s) attached, board id: %u\n",
366 dev->board_name, pci_name(pcidev), board_id);
370 static struct comedi_driver adv_pci1724_driver = {
371 .driver_name = "adv_pci1724",
372 .module = THIS_MODULE,
373 .auto_attach = adv_pci1724_auto_attach,
374 .detach = comedi_pci_disable,
377 static int adv_pci1724_pci_probe(struct pci_dev *dev,
378 const struct pci_device_id *id)
380 return comedi_pci_auto_config(dev, &adv_pci1724_driver,
384 static DEFINE_PCI_DEVICE_TABLE(adv_pci1724_pci_table) = {
385 { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1724) },
388 MODULE_DEVICE_TABLE(pci, adv_pci1724_pci_table);
390 static struct pci_driver adv_pci1724_pci_driver = {
391 .name = "adv_pci1724",
392 .id_table = adv_pci1724_pci_table,
393 .probe = adv_pci1724_pci_probe,
394 .remove = comedi_pci_auto_unconfig,
397 module_comedi_pci_driver(adv_pci1724_driver, adv_pci1724_pci_driver);
399 MODULE_AUTHOR("Frank Mori Hess <fmh6jj@gmail.com>");
400 MODULE_DESCRIPTION("Advantech PCI-1724U Comedi driver");
401 MODULE_LICENSE("GPL");