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/pci.h>
57 #include "../comedidev.h"
59 #define PCI_VENDOR_ID_ADVANTECH 0x13fe
61 #define NUM_AO_CHANNELS 32
63 /* register offsets */
64 enum board_registers {
65 DAC_CONTROL_REG = 0x0,
66 SYNC_OUTPUT_REG = 0x4,
67 EEPROM_CONTROL_REG = 0x8,
68 SYNC_OUTPUT_TRIGGER_REG = 0xc,
72 /* bit definitions for registers */
73 enum dac_control_contents {
74 DAC_DATA_MASK = 0x3fff,
75 DAC_DESTINATION_MASK = 0xc000,
76 DAC_NORMAL_MODE = 0xc000,
77 DAC_OFFSET_MODE = 0x8000,
78 DAC_GAIN_MODE = 0x4000,
79 DAC_CHANNEL_SELECT_MASK = 0xf0000,
80 DAC_GROUP_SELECT_MASK = 0xf00000
83 static uint32_t dac_data_bits(uint16_t dac_data)
85 return dac_data & DAC_DATA_MASK;
88 static uint32_t dac_channel_select_bits(unsigned channel)
90 return (channel << 16) & DAC_CHANNEL_SELECT_MASK;
93 static uint32_t dac_group_select_bits(unsigned group)
95 return (1 << (20 + group)) & DAC_GROUP_SELECT_MASK;
98 static uint32_t dac_channel_and_group_select_bits(unsigned comedi_channel)
100 return dac_channel_select_bits(comedi_channel % 8) |
101 dac_group_select_bits(comedi_channel / 8);
104 enum sync_output_contents {
106 DAC_BUSY = 0x2, /* dac state machine is not ready */
109 enum sync_output_trigger_contents {
110 SYNC_TRIGGER_BITS = 0x0 /* any value works */
113 enum board_id_contents {
117 static const struct comedi_lrange ao_ranges_1724 = { 4,
126 static const struct comedi_lrange *const ao_range_list_1724[NUM_AO_CHANNELS] = {
127 [0 ... NUM_AO_CHANNELS - 1] = &ao_ranges_1724,
130 /* this structure is for data unique to this hardware driver. */
131 struct adv_pci1724_private {
132 int ao_value[NUM_AO_CHANNELS];
133 int offset_value[NUM_AO_CHANNELS];
134 int gain_value[NUM_AO_CHANNELS];
137 static int wait_for_dac_idle(struct comedi_device *dev)
139 static const int timeout = 10000;
142 for (i = 0; i < timeout; ++i) {
143 if ((inl(dev->iobase + SYNC_OUTPUT_REG) & DAC_BUSY) == 0)
148 comedi_error(dev, "Timed out waiting for dac to become idle.");
154 static int set_dac(struct comedi_device *dev, unsigned mode, unsigned channel,
158 unsigned control_bits;
160 retval = wait_for_dac_idle(dev);
165 control_bits |= dac_channel_and_group_select_bits(channel);
166 control_bits |= dac_data_bits(data);
167 outl(control_bits, dev->iobase + DAC_CONTROL_REG);
171 static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
172 struct comedi_insn *insn, unsigned int *data)
174 struct adv_pci1724_private *devpriv = dev->private;
175 int channel = CR_CHAN(insn->chanspec);
179 /* turn off synchronous mode */
180 outl(0, dev->iobase + SYNC_OUTPUT_REG);
182 for (i = 0; i < insn->n; ++i) {
183 retval = set_dac(dev, DAC_NORMAL_MODE, channel, data[i]);
186 devpriv->ao_value[channel] = data[i];
191 static int ao_readback_insn(struct comedi_device *dev,
192 struct comedi_subdevice *s,
193 struct comedi_insn *insn, unsigned int *data)
195 struct adv_pci1724_private *devpriv = dev->private;
196 int channel = CR_CHAN(insn->chanspec);
199 if (devpriv->ao_value[channel] < 0) {
201 "Cannot read back channels which have not yet been written to.");
204 for (i = 0; i < insn->n; i++)
205 data[i] = devpriv->ao_value[channel];
210 static int offset_write_insn(struct comedi_device *dev,
211 struct comedi_subdevice *s,
212 struct comedi_insn *insn, unsigned int *data)
214 struct adv_pci1724_private *devpriv = dev->private;
215 int channel = CR_CHAN(insn->chanspec);
219 /* turn off synchronous mode */
220 outl(0, dev->iobase + SYNC_OUTPUT_REG);
222 for (i = 0; i < insn->n; ++i) {
223 retval = set_dac(dev, DAC_OFFSET_MODE, channel, data[i]);
226 devpriv->offset_value[channel] = data[i];
232 static int offset_read_insn(struct comedi_device *dev,
233 struct comedi_subdevice *s,
234 struct comedi_insn *insn, unsigned int *data)
236 struct adv_pci1724_private *devpriv = dev->private;
237 unsigned int channel = CR_CHAN(insn->chanspec);
240 if (devpriv->offset_value[channel] < 0) {
242 "Cannot read back channels which have not yet been written to.");
245 for (i = 0; i < insn->n; i++)
246 data[i] = devpriv->offset_value[channel];
251 static int gain_write_insn(struct comedi_device *dev,
252 struct comedi_subdevice *s,
253 struct comedi_insn *insn, unsigned int *data)
255 struct adv_pci1724_private *devpriv = dev->private;
256 int channel = CR_CHAN(insn->chanspec);
260 /* turn off synchronous mode */
261 outl(0, dev->iobase + SYNC_OUTPUT_REG);
263 for (i = 0; i < insn->n; ++i) {
264 retval = set_dac(dev, DAC_GAIN_MODE, channel, data[i]);
267 devpriv->gain_value[channel] = data[i];
273 static int gain_read_insn(struct comedi_device *dev,
274 struct comedi_subdevice *s, struct comedi_insn *insn,
277 struct adv_pci1724_private *devpriv = dev->private;
278 unsigned int channel = CR_CHAN(insn->chanspec);
281 if (devpriv->gain_value[channel] < 0) {
283 "Cannot read back channels which have not yet been written to.");
286 for (i = 0; i < insn->n; i++)
287 data[i] = devpriv->gain_value[channel];
292 /* Allocate and initialize the subdevice structures.
294 static int setup_subdevices(struct comedi_device *dev)
296 struct comedi_subdevice *s;
299 ret = comedi_alloc_subdevices(dev, 3);
303 /* analog output subdevice */
304 s = &dev->subdevices[0];
305 s->type = COMEDI_SUBD_AO;
306 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND;
307 s->n_chan = NUM_AO_CHANNELS;
309 s->range_table_list = ao_range_list_1724;
310 s->insn_read = ao_readback_insn;
311 s->insn_write = ao_winsn;
313 /* offset calibration */
314 s = &dev->subdevices[1];
315 s->type = COMEDI_SUBD_CALIB;
316 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
317 s->n_chan = NUM_AO_CHANNELS;
318 s->insn_read = offset_read_insn;
319 s->insn_write = offset_write_insn;
322 /* gain calibration */
323 s = &dev->subdevices[2];
324 s->type = COMEDI_SUBD_CALIB;
325 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
326 s->n_chan = NUM_AO_CHANNELS;
327 s->insn_read = gain_read_insn;
328 s->insn_write = gain_write_insn;
334 static int adv_pci1724_auto_attach(struct comedi_device *dev,
335 unsigned long context_unused)
337 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
338 struct adv_pci1724_private *devpriv;
341 unsigned int board_id;
343 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
346 dev->private = devpriv;
348 /* init software copies of output values to indicate we don't know
349 * what the output value is since it has never been written. */
350 for (i = 0; i < NUM_AO_CHANNELS; ++i) {
351 devpriv->ao_value[i] = -1;
352 devpriv->offset_value[i] = -1;
353 devpriv->gain_value[i] = -1;
356 retval = comedi_pci_enable(dev);
360 dev->iobase = pci_resource_start(pcidev, 2);
361 board_id = inl(dev->iobase + BOARD_ID_REG) & BOARD_ID_MASK;
362 dev_info(dev->class_dev, "board id: %d\n", board_id);
364 retval = setup_subdevices(dev);
368 dev_info(dev->class_dev, "%s (pci %s) attached, board id: %u\n",
369 dev->board_name, pci_name(pcidev), board_id);
373 static struct comedi_driver adv_pci1724_driver = {
374 .driver_name = "adv_pci1724",
375 .module = THIS_MODULE,
376 .auto_attach = adv_pci1724_auto_attach,
377 .detach = comedi_pci_disable,
380 static int adv_pci1724_pci_probe(struct pci_dev *dev,
381 const struct pci_device_id *id)
383 return comedi_pci_auto_config(dev, &adv_pci1724_driver,
387 static DEFINE_PCI_DEVICE_TABLE(adv_pci1724_pci_table) = {
388 { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1724) },
391 MODULE_DEVICE_TABLE(pci, adv_pci1724_pci_table);
393 static struct pci_driver adv_pci1724_pci_driver = {
394 .name = "adv_pci1724",
395 .id_table = adv_pci1724_pci_table,
396 .probe = adv_pci1724_pci_probe,
397 .remove = comedi_pci_auto_unconfig,
400 module_comedi_pci_driver(adv_pci1724_driver, adv_pci1724_pci_driver);
402 MODULE_AUTHOR("Frank Mori Hess <fmh6jj@gmail.com>");
403 MODULE_DESCRIPTION("Advantech PCI-1724U Comedi driver");
404 MODULE_LICENSE("GPL");