Version:  2.0.40 2.2.26 2.4.37 2.6.39 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15

Linux/drivers/staging/comedi/drivers/adv_pci1724.c

  1 /*
  2     comedi/drivers/adv_pci1724.c
  3     This is a driver for the Advantech PCI-1724U card.
  4 
  5     Author:  Frank Mori Hess <fmh6jj@gmail.com>
  6     Copyright (C) 2013 GnuBIO Inc
  7 
  8     COMEDI - Linux Control and Measurement Device Interface
  9     Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
 10 
 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.
 15 
 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.
 20 */
 21 
 22 /*
 23 
 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)
 30 
 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.
 34 
 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).
 41 
 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.
 46 
 47 Configuration options:
 48    None
 49 
 50 Manual configuration of comedi devices is not supported by this driver;
 51 supported PCI devices are configured as comedi devices automatically.
 52 
 53 */
 54 
 55 #include <linux/module.h>
 56 #include <linux/delay.h>
 57 #include <linux/pci.h>
 58 
 59 #include "../comedidev.h"
 60 
 61 #define PCI_VENDOR_ID_ADVANTECH 0x13fe
 62 
 63 #define NUM_AO_CHANNELS 32
 64 
 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,
 71         BOARD_ID_REG = 0x10
 72 };
 73 
 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
 83 };
 84 
 85 static uint32_t dac_data_bits(uint16_t dac_data)
 86 {
 87         return dac_data & DAC_DATA_MASK;
 88 }
 89 
 90 static uint32_t dac_channel_select_bits(unsigned channel)
 91 {
 92         return (channel << 16) & DAC_CHANNEL_SELECT_MASK;
 93 }
 94 
 95 static uint32_t dac_group_select_bits(unsigned group)
 96 {
 97         return (1 << (20 + group)) & DAC_GROUP_SELECT_MASK;
 98 }
 99 
100 static uint32_t dac_channel_and_group_select_bits(unsigned comedi_channel)
101 {
102         return dac_channel_select_bits(comedi_channel % 8) |
103                 dac_group_select_bits(comedi_channel / 8);
104 }
105 
106 enum sync_output_contents {
107         SYNC_MODE = 0x1,
108         DAC_BUSY = 0x2, /* dac state machine is not ready */
109 };
110 
111 enum sync_output_trigger_contents {
112         SYNC_TRIGGER_BITS = 0x0 /* any value works */
113 };
114 
115 enum board_id_contents {
116         BOARD_ID_MASK = 0xf
117 };
118 
119 static const struct comedi_lrange ao_ranges_1724 = {
120         4, {
121                 BIP_RANGE(10),
122                 RANGE_mA(0, 20),
123                 RANGE_mA(4, 20),
124                 RANGE_unitless(0, 1)
125         }
126 };
127 
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];
133 };
134 
135 static int wait_for_dac_idle(struct comedi_device *dev)
136 {
137         static const int timeout = 10000;
138         int i;
139 
140         for (i = 0; i < timeout; ++i) {
141                 if ((inl(dev->iobase + SYNC_OUTPUT_REG) & DAC_BUSY) == 0)
142                         break;
143                 udelay(1);
144         }
145         if (i == timeout) {
146                 comedi_error(dev, "Timed out waiting for dac to become idle.");
147                 return -EIO;
148         }
149         return 0;
150 }
151 
152 static int set_dac(struct comedi_device *dev, unsigned mode, unsigned channel,
153                    unsigned data)
154 {
155         int retval;
156         unsigned control_bits;
157 
158         retval = wait_for_dac_idle(dev);
159         if (retval < 0)
160                 return retval;
161 
162         control_bits = mode;
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);
166         return 0;
167 }
168 
169 static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
170                     struct comedi_insn *insn, unsigned int *data)
171 {
172         struct adv_pci1724_private *devpriv = dev->private;
173         int channel = CR_CHAN(insn->chanspec);
174         int retval;
175         int i;
176 
177         /* turn off synchronous mode */
178         outl(0, dev->iobase + SYNC_OUTPUT_REG);
179 
180         for (i = 0; i < insn->n; ++i) {
181                 retval = set_dac(dev, DAC_NORMAL_MODE, channel, data[i]);
182                 if (retval < 0)
183                         return retval;
184                 devpriv->ao_value[channel] = data[i];
185         }
186         return insn->n;
187 }
188 
189 static int ao_readback_insn(struct comedi_device *dev,
190                             struct comedi_subdevice *s,
191                             struct comedi_insn *insn, unsigned int *data)
192 {
193         struct adv_pci1724_private *devpriv = dev->private;
194         int channel = CR_CHAN(insn->chanspec);
195         int i;
196 
197         if (devpriv->ao_value[channel] < 0) {
198                 comedi_error(dev,
199                              "Cannot read back channels which have not yet been written to.");
200                 return -EIO;
201         }
202         for (i = 0; i < insn->n; i++)
203                 data[i] = devpriv->ao_value[channel];
204 
205         return insn->n;
206 }
207 
208 static int offset_write_insn(struct comedi_device *dev,
209                              struct comedi_subdevice *s,
210                              struct comedi_insn *insn, unsigned int *data)
211 {
212         struct adv_pci1724_private *devpriv = dev->private;
213         int channel = CR_CHAN(insn->chanspec);
214         int retval;
215         int i;
216 
217         /* turn off synchronous mode */
218         outl(0, dev->iobase + SYNC_OUTPUT_REG);
219 
220         for (i = 0; i < insn->n; ++i) {
221                 retval = set_dac(dev, DAC_OFFSET_MODE, channel, data[i]);
222                 if (retval < 0)
223                         return retval;
224                 devpriv->offset_value[channel] = data[i];
225         }
226 
227         return insn->n;
228 }
229 
230 static int offset_read_insn(struct comedi_device *dev,
231                             struct comedi_subdevice *s,
232                             struct comedi_insn *insn, unsigned int *data)
233 {
234         struct adv_pci1724_private *devpriv = dev->private;
235         unsigned int channel = CR_CHAN(insn->chanspec);
236         int i;
237 
238         if (devpriv->offset_value[channel] < 0) {
239                 comedi_error(dev,
240                              "Cannot read back channels which have not yet been written to.");
241                 return -EIO;
242         }
243         for (i = 0; i < insn->n; i++)
244                 data[i] = devpriv->offset_value[channel];
245 
246         return insn->n;
247 }
248 
249 static int gain_write_insn(struct comedi_device *dev,
250                            struct comedi_subdevice *s,
251                            struct comedi_insn *insn, unsigned int *data)
252 {
253         struct adv_pci1724_private *devpriv = dev->private;
254         int channel = CR_CHAN(insn->chanspec);
255         int retval;
256         int i;
257 
258         /* turn off synchronous mode */
259         outl(0, dev->iobase + SYNC_OUTPUT_REG);
260 
261         for (i = 0; i < insn->n; ++i) {
262                 retval = set_dac(dev, DAC_GAIN_MODE, channel, data[i]);
263                 if (retval < 0)
264                         return retval;
265                 devpriv->gain_value[channel] = data[i];
266         }
267 
268         return insn->n;
269 }
270 
271 static int gain_read_insn(struct comedi_device *dev,
272                           struct comedi_subdevice *s, struct comedi_insn *insn,
273                           unsigned int *data)
274 {
275         struct adv_pci1724_private *devpriv = dev->private;
276         unsigned int channel = CR_CHAN(insn->chanspec);
277         int i;
278 
279         if (devpriv->gain_value[channel] < 0) {
280                 comedi_error(dev,
281                              "Cannot read back channels which have not yet been written to.");
282                 return -EIO;
283         }
284         for (i = 0; i < insn->n; i++)
285                 data[i] = devpriv->gain_value[channel];
286 
287         return insn->n;
288 }
289 
290 /* Allocate and initialize the subdevice structures.
291  */
292 static int setup_subdevices(struct comedi_device *dev)
293 {
294         struct comedi_subdevice *s;
295         int ret;
296 
297         ret = comedi_alloc_subdevices(dev, 3);
298         if (ret)
299                 return ret;
300 
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;
306         s->maxdata = 0x3fff;
307         s->range_table = &ao_ranges_1724;
308         s->insn_read = ao_readback_insn;
309         s->insn_write = ao_winsn;
310 
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;
318         s->maxdata = 0x3fff;
319 
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;
327         s->maxdata = 0x3fff;
328 
329         return 0;
330 }
331 
332 static int adv_pci1724_auto_attach(struct comedi_device *dev,
333                                    unsigned long context_unused)
334 {
335         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
336         struct adv_pci1724_private *devpriv;
337         int i;
338         int retval;
339         unsigned int board_id;
340 
341         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
342         if (!devpriv)
343                 return -ENOMEM;
344 
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;
351         }
352 
353         retval = comedi_pci_enable(dev);
354         if (retval)
355                 return retval;
356 
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);
360 
361         retval = setup_subdevices(dev);
362         if (retval < 0)
363                 return retval;
364 
365         dev_info(dev->class_dev, "%s (pci %s) attached, board id: %u\n",
366                  dev->board_name, pci_name(pcidev), board_id);
367         return 0;
368 }
369 
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,
375 };
376 
377 static int adv_pci1724_pci_probe(struct pci_dev *dev,
378                                  const struct pci_device_id *id)
379 {
380         return comedi_pci_auto_config(dev, &adv_pci1724_driver,
381                                       id->driver_data);
382 }
383 
384 static const struct pci_device_id adv_pci1724_pci_table[] = {
385         { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1724) },
386         { 0 }
387 };
388 MODULE_DEVICE_TABLE(pci, adv_pci1724_pci_table);
389 
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,
395 };
396 
397 module_comedi_pci_driver(adv_pci1724_driver, adv_pci1724_pci_driver);
398 
399 MODULE_AUTHOR("Frank Mori Hess <fmh6jj@gmail.com>");
400 MODULE_DESCRIPTION("Advantech PCI-1724U Comedi driver");
401 MODULE_LICENSE("GPL");
402 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us