Merge branch 'for-3.11' of git://linux-nfs.org/~bfields/linux
[linux-2.6.git] / drivers / staging / comedi / drivers / adv_pci1724.c
blobda7462e01faa9f987092c4e9161364b0ef5d39bf
1 /*
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.
24 Driver: adv_1724
25 Description: Advantech PCI-1724U
26 Author: Frank Mori Hess <fmh6jj@gmail.com>
27 Status: works
28 Updated: 2013-02-09
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
45 the desired range.
47 Configuration options:
48 None
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,
69 BOARD_ID_REG = 0x10
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 {
105 SYNC_MODE = 0x1,
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 {
114 BOARD_ID_MASK = 0xf
117 static const struct comedi_lrange ao_ranges_1724 = { 4,
119 BIP_RANGE(10),
120 RANGE_mA(0, 20),
121 RANGE_mA(4, 20),
122 RANGE_unitless(0, 1)
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;
140 int i;
142 for (i = 0; i < timeout; ++i) {
143 if ((inl(dev->iobase + SYNC_OUTPUT_REG) & DAC_BUSY) == 0)
144 break;
145 udelay(1);
147 if (i == timeout) {
148 comedi_error(dev, "Timed out waiting for dac to become idle.");
149 return -EIO;
151 return 0;
154 static int set_dac(struct comedi_device *dev, unsigned mode, unsigned channel,
155 unsigned data)
157 int retval;
158 unsigned control_bits;
160 retval = wait_for_dac_idle(dev);
161 if (retval < 0)
162 return retval;
164 control_bits = mode;
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);
168 return 0;
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);
176 int retval;
177 int i;
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]);
184 if (retval < 0)
185 return retval;
186 devpriv->ao_value[channel] = data[i];
188 return insn->n;
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);
197 int i;
199 if (devpriv->ao_value[channel] < 0) {
200 comedi_error(dev,
201 "Cannot read back channels which have not yet been written to.");
202 return -EIO;
204 for (i = 0; i < insn->n; i++)
205 data[i] = devpriv->ao_value[channel];
207 return insn->n;
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);
216 int retval;
217 int i;
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]);
224 if (retval < 0)
225 return retval;
226 devpriv->offset_value[channel] = data[i];
229 return insn->n;
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);
238 int i;
240 if (devpriv->offset_value[channel] < 0) {
241 comedi_error(dev,
242 "Cannot read back channels which have not yet been written to.");
243 return -EIO;
245 for (i = 0; i < insn->n; i++)
246 data[i] = devpriv->offset_value[channel];
248 return insn->n;
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);
257 int retval;
258 int i;
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]);
265 if (retval < 0)
266 return retval;
267 devpriv->gain_value[channel] = data[i];
270 return insn->n;
273 static int gain_read_insn(struct comedi_device *dev,
274 struct comedi_subdevice *s, struct comedi_insn *insn,
275 unsigned int *data)
277 struct adv_pci1724_private *devpriv = dev->private;
278 unsigned int channel = CR_CHAN(insn->chanspec);
279 int i;
281 if (devpriv->gain_value[channel] < 0) {
282 comedi_error(dev,
283 "Cannot read back channels which have not yet been written to.");
284 return -EIO;
286 for (i = 0; i < insn->n; i++)
287 data[i] = devpriv->gain_value[channel];
289 return insn->n;
292 /* Allocate and initialize the subdevice structures.
294 static int setup_subdevices(struct comedi_device *dev)
296 struct comedi_subdevice *s;
297 int ret;
299 ret = comedi_alloc_subdevices(dev, 3);
300 if (ret)
301 return ret;
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;
308 s->maxdata = 0x3fff;
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;
320 s->maxdata = 0x3fff;
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;
329 s->maxdata = 0x3fff;
331 return 0;
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;
339 int i;
340 int retval;
341 unsigned int board_id;
343 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
344 if (!devpriv)
345 return -ENOMEM;
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);
357 if (retval)
358 return retval;
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);
365 if (retval < 0)
366 return retval;
368 dev_info(dev->class_dev, "%s (pci %s) attached, board id: %u\n",
369 dev->board_name, pci_name(pcidev), board_id);
370 return 0;
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,
384 id->driver_data);
387 static DEFINE_PCI_DEVICE_TABLE(adv_pci1724_pci_table) = {
388 { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1724) },
389 { 0 }
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");