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.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 ************************************************************************/
30 Description: Advantech PCI-1724U
31 Author: Frank Mori Hess <fmh6jj@gmail.com>
34 Devices: [Advantech] PCI-1724U (adv_pci1724)
36 Subdevice 0 is the analog output.
37 Subdevice 1 is the offset calibration for the analog output.
38 Subdevice 2 is the gain calibration for the analog output.
40 The calibration offset and gains have quite a large effect
41 on the analog output, so it is possible to adjust the analog output to
42 have an output range significantly different from the board's
43 nominal output ranges. For a calibrated +/- 10V range, the analog
44 output's offset will be set somewhere near mid-range (0x2000) and its
45 gain will be near maximum (0x3fff).
47 There is really no difference between the board's documented 0-20mA
48 versus 4-20mA output ranges. To pick one or the other is simply a matter
49 of adjusting the offset and gain calibration until the board outputs in
52 Configuration options:
55 Manual configuration of comedi devices is not supported by this driver;
56 supported PCI devices are configured as comedi devices automatically.
60 #include <linux/pci.h>
62 #include "../comedidev.h"
64 #define PCI_VENDOR_ID_ADVANTECH 0x13fe
66 #define NUM_AO_CHANNELS 32
68 /* register offsets */
69 enum board_registers {
70 DAC_CONTROL_REG = 0x0,
71 SYNC_OUTPUT_REG = 0x4,
72 EEPROM_CONTROL_REG = 0x8,
73 SYNC_OUTPUT_TRIGGER_REG = 0xc,
77 /* bit definitions for registers */
78 enum dac_control_contents {
79 DAC_DATA_MASK = 0x3fff,
80 DAC_DESTINATION_MASK = 0xc000,
81 DAC_NORMAL_MODE = 0xc000,
82 DAC_OFFSET_MODE = 0x8000,
83 DAC_GAIN_MODE = 0x4000,
84 DAC_CHANNEL_SELECT_MASK = 0xf0000,
85 DAC_GROUP_SELECT_MASK = 0xf00000
88 static uint32_t dac_data_bits(uint16_t dac_data)
90 return dac_data & DAC_DATA_MASK;
93 static uint32_t dac_channel_select_bits(unsigned channel)
95 return (channel << 16) & DAC_CHANNEL_SELECT_MASK;
98 static uint32_t dac_group_select_bits(unsigned group)
100 return (1 << (20 + group)) & DAC_GROUP_SELECT_MASK;
103 static uint32_t dac_channel_and_group_select_bits(unsigned comedi_channel)
105 return dac_channel_select_bits(comedi_channel % 8) |
106 dac_group_select_bits(comedi_channel / 8);
109 enum sync_output_contents {
111 DAC_BUSY = 0x2, /* dac state machine is not ready */
114 enum sync_output_trigger_contents {
115 SYNC_TRIGGER_BITS = 0x0 /* any value works */
118 enum board_id_contents {
122 static const struct comedi_lrange ao_ranges_1724 = { 4,
131 static const struct comedi_lrange *const ao_range_list_1724[NUM_AO_CHANNELS] = {
132 [0 ... NUM_AO_CHANNELS - 1] = &ao_ranges_1724,
135 /* this structure is for data unique to this hardware driver. */
136 struct adv_pci1724_private {
137 int ao_value[NUM_AO_CHANNELS];
138 int offset_value[NUM_AO_CHANNELS];
139 int gain_value[NUM_AO_CHANNELS];
142 static int wait_for_dac_idle(struct comedi_device *dev)
144 static const int timeout = 10000;
147 for (i = 0; i < timeout; ++i) {
148 if ((inl(dev->iobase + SYNC_OUTPUT_REG) & DAC_BUSY) == 0)
153 comedi_error(dev, "Timed out waiting for dac to become idle.");
159 static int set_dac(struct comedi_device *dev, unsigned mode, unsigned channel,
163 unsigned control_bits;
165 retval = wait_for_dac_idle(dev);
170 control_bits |= dac_channel_and_group_select_bits(channel);
171 control_bits |= dac_data_bits(data);
172 outl(control_bits, dev->iobase + DAC_CONTROL_REG);
176 static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
177 struct comedi_insn *insn, unsigned int *data)
179 struct adv_pci1724_private *devpriv = dev->private;
180 int channel = CR_CHAN(insn->chanspec);
184 /* turn off synchronous mode */
185 outl(0, dev->iobase + SYNC_OUTPUT_REG);
187 for (i = 0; i < insn->n; ++i) {
188 retval = set_dac(dev, DAC_NORMAL_MODE, channel, data[i]);
191 devpriv->ao_value[channel] = data[i];
196 static int ao_readback_insn(struct comedi_device *dev,
197 struct comedi_subdevice *s,
198 struct comedi_insn *insn, unsigned int *data)
200 struct adv_pci1724_private *devpriv = dev->private;
201 int channel = CR_CHAN(insn->chanspec);
204 if (devpriv->ao_value[channel] < 0) {
206 "Cannot read back channels which have not yet been written to.");
209 for (i = 0; i < insn->n; i++)
210 data[i] = devpriv->ao_value[channel];
215 static int offset_write_insn(struct comedi_device *dev,
216 struct comedi_subdevice *s,
217 struct comedi_insn *insn, unsigned int *data)
219 struct adv_pci1724_private *devpriv = dev->private;
220 int channel = CR_CHAN(insn->chanspec);
224 /* turn off synchronous mode */
225 outl(0, dev->iobase + SYNC_OUTPUT_REG);
227 for (i = 0; i < insn->n; ++i) {
228 retval = set_dac(dev, DAC_OFFSET_MODE, channel, data[i]);
231 devpriv->offset_value[channel] = data[i];
237 static int offset_read_insn(struct comedi_device *dev,
238 struct comedi_subdevice *s,
239 struct comedi_insn *insn, unsigned int *data)
241 struct adv_pci1724_private *devpriv = dev->private;
242 unsigned int channel = CR_CHAN(insn->chanspec);
245 if (devpriv->offset_value[channel] < 0) {
247 "Cannot read back channels which have not yet been written to.");
250 for (i = 0; i < insn->n; i++)
251 data[i] = devpriv->offset_value[channel];
256 static int gain_write_insn(struct comedi_device *dev,
257 struct comedi_subdevice *s,
258 struct comedi_insn *insn, unsigned int *data)
260 struct adv_pci1724_private *devpriv = dev->private;
261 int channel = CR_CHAN(insn->chanspec);
265 /* turn off synchronous mode */
266 outl(0, dev->iobase + SYNC_OUTPUT_REG);
268 for (i = 0; i < insn->n; ++i) {
269 retval = set_dac(dev, DAC_GAIN_MODE, channel, data[i]);
272 devpriv->gain_value[channel] = data[i];
278 static int gain_read_insn(struct comedi_device *dev,
279 struct comedi_subdevice *s, struct comedi_insn *insn,
282 struct adv_pci1724_private *devpriv = dev->private;
283 unsigned int channel = CR_CHAN(insn->chanspec);
286 if (devpriv->gain_value[channel] < 0) {
288 "Cannot read back channels which have not yet been written to.");
291 for (i = 0; i < insn->n; i++)
292 data[i] = devpriv->gain_value[channel];
297 /* Allocate and initialize the subdevice structures.
299 static int setup_subdevices(struct comedi_device *dev)
301 struct comedi_subdevice *s;
304 ret = comedi_alloc_subdevices(dev, 3);
308 /* analog output subdevice */
309 s = &dev->subdevices[0];
310 s->type = COMEDI_SUBD_AO;
311 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND;
312 s->n_chan = NUM_AO_CHANNELS;
314 s->range_table_list = ao_range_list_1724;
315 s->insn_read = ao_readback_insn;
316 s->insn_write = ao_winsn;
318 /* offset calibration */
319 s = &dev->subdevices[1];
320 s->type = COMEDI_SUBD_CALIB;
321 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
322 s->n_chan = NUM_AO_CHANNELS;
323 s->insn_read = offset_read_insn;
324 s->insn_write = offset_write_insn;
327 /* gain calibration */
328 s = &dev->subdevices[2];
329 s->type = COMEDI_SUBD_CALIB;
330 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
331 s->n_chan = NUM_AO_CHANNELS;
332 s->insn_read = gain_read_insn;
333 s->insn_write = gain_write_insn;
339 static int adv_pci1724_auto_attach(struct comedi_device *dev,
340 unsigned long context_unused)
342 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
343 struct adv_pci1724_private *devpriv;
346 unsigned int board_id;
348 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
351 dev->private = devpriv;
353 /* init software copies of output values to indicate we don't know
354 * what the output value is since it has never been written. */
355 for (i = 0; i < NUM_AO_CHANNELS; ++i) {
356 devpriv->ao_value[i] = -1;
357 devpriv->offset_value[i] = -1;
358 devpriv->gain_value[i] = -1;
361 retval = comedi_pci_enable(dev);
365 dev->iobase = pci_resource_start(pcidev, 2);
366 board_id = inl(dev->iobase + BOARD_ID_REG) & BOARD_ID_MASK;
367 dev_info(dev->class_dev, "board id: %d\n", board_id);
369 retval = setup_subdevices(dev);
373 dev_info(dev->class_dev, "%s (pci %s) attached, board id: %u\n",
374 dev->board_name, pci_name(pcidev), board_id);
378 static struct comedi_driver adv_pci1724_driver = {
379 .driver_name = "adv_pci1724",
380 .module = THIS_MODULE,
381 .auto_attach = adv_pci1724_auto_attach,
382 .detach = comedi_pci_disable,
385 static int adv_pci1724_pci_probe(struct pci_dev *dev,
386 const struct pci_device_id *id)
388 return comedi_pci_auto_config(dev, &adv_pci1724_driver,
392 static DEFINE_PCI_DEVICE_TABLE(adv_pci1724_pci_table) = {
393 { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1724) },
396 MODULE_DEVICE_TABLE(pci, adv_pci1724_pci_table);
398 static struct pci_driver adv_pci1724_pci_driver = {
399 .name = "adv_pci1724",
400 .id_table = adv_pci1724_pci_table,
401 .probe = adv_pci1724_pci_probe,
402 .remove = comedi_pci_auto_unconfig,
405 module_comedi_pci_driver(adv_pci1724_driver, adv_pci1724_pci_driver);
407 MODULE_AUTHOR("Frank Mori Hess <fmh6jj@gmail.com>");
408 MODULE_DESCRIPTION("Advantech PCI-1724U Comedi driver");
409 MODULE_LICENSE("GPL");