Version:  2.0.40 2.2.26 2.4.37 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 3.16 3.17 3.18

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                 dev_err(dev->class_dev,
147                         "Timed out waiting for dac to become idle\n");
148                 return -EIO;
149         }
150         return 0;
151 }
152 
153 static int set_dac(struct comedi_device *dev, unsigned mode, unsigned channel,
154                    unsigned data)
155 {
156         int retval;
157         unsigned control_bits;
158 
159         retval = wait_for_dac_idle(dev);
160         if (retval < 0)
161                 return retval;
162 
163         control_bits = mode;
164         control_bits |= dac_channel_and_group_select_bits(channel);
165         control_bits |= dac_data_bits(data);
166         outl(control_bits, dev->iobase + DAC_CONTROL_REG);
167         return 0;
168 }
169 
170 static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
171                     struct comedi_insn *insn, unsigned int *data)
172 {
173         struct adv_pci1724_private *devpriv = dev->private;
174         int channel = CR_CHAN(insn->chanspec);
175         int retval;
176         int i;
177 
178         /* turn off synchronous mode */
179         outl(0, dev->iobase + SYNC_OUTPUT_REG);
180 
181         for (i = 0; i < insn->n; ++i) {
182                 retval = set_dac(dev, DAC_NORMAL_MODE, channel, data[i]);
183                 if (retval < 0)
184                         return retval;
185                 devpriv->ao_value[channel] = data[i];
186         }
187         return insn->n;
188 }
189 
190 static int ao_readback_insn(struct comedi_device *dev,
191                             struct comedi_subdevice *s,
192                             struct comedi_insn *insn, unsigned int *data)
193 {
194         struct adv_pci1724_private *devpriv = dev->private;
195         int channel = CR_CHAN(insn->chanspec);
196         int i;
197 
198         if (devpriv->ao_value[channel] < 0) {
199                 dev_err(dev->class_dev,
200                         "Cannot read back channels which have not yet been written to\n");
201                 return -EIO;
202         }
203         for (i = 0; i < insn->n; i++)
204                 data[i] = devpriv->ao_value[channel];
205 
206         return insn->n;
207 }
208 
209 static int offset_write_insn(struct comedi_device *dev,
210                              struct comedi_subdevice *s,
211                              struct comedi_insn *insn, unsigned int *data)
212 {
213         struct adv_pci1724_private *devpriv = dev->private;
214         int channel = CR_CHAN(insn->chanspec);
215         int retval;
216         int i;
217 
218         /* turn off synchronous mode */
219         outl(0, dev->iobase + SYNC_OUTPUT_REG);
220 
221         for (i = 0; i < insn->n; ++i) {
222                 retval = set_dac(dev, DAC_OFFSET_MODE, channel, data[i]);
223                 if (retval < 0)
224                         return retval;
225                 devpriv->offset_value[channel] = data[i];
226         }
227 
228         return insn->n;
229 }
230 
231 static int offset_read_insn(struct comedi_device *dev,
232                             struct comedi_subdevice *s,
233                             struct comedi_insn *insn, unsigned int *data)
234 {
235         struct adv_pci1724_private *devpriv = dev->private;
236         unsigned int channel = CR_CHAN(insn->chanspec);
237         int i;
238 
239         if (devpriv->offset_value[channel] < 0) {
240                 dev_err(dev->class_dev,
241                         "Cannot read back channels which have not yet been written to\n");
242                 return -EIO;
243         }
244         for (i = 0; i < insn->n; i++)
245                 data[i] = devpriv->offset_value[channel];
246 
247         return insn->n;
248 }
249 
250 static int gain_write_insn(struct comedi_device *dev,
251                            struct comedi_subdevice *s,
252                            struct comedi_insn *insn, unsigned int *data)
253 {
254         struct adv_pci1724_private *devpriv = dev->private;
255         int channel = CR_CHAN(insn->chanspec);
256         int retval;
257         int i;
258 
259         /* turn off synchronous mode */
260         outl(0, dev->iobase + SYNC_OUTPUT_REG);
261 
262         for (i = 0; i < insn->n; ++i) {
263                 retval = set_dac(dev, DAC_GAIN_MODE, channel, data[i]);
264                 if (retval < 0)
265                         return retval;
266                 devpriv->gain_value[channel] = data[i];
267         }
268 
269         return insn->n;
270 }
271 
272 static int gain_read_insn(struct comedi_device *dev,
273                           struct comedi_subdevice *s, struct comedi_insn *insn,
274                           unsigned int *data)
275 {
276         struct adv_pci1724_private *devpriv = dev->private;
277         unsigned int channel = CR_CHAN(insn->chanspec);
278         int i;
279 
280         if (devpriv->gain_value[channel] < 0) {
281                 dev_err(dev->class_dev,
282                         "Cannot read back channels which have not yet been written to\n");
283                 return -EIO;
284         }
285         for (i = 0; i < insn->n; i++)
286                 data[i] = devpriv->gain_value[channel];
287 
288         return insn->n;
289 }
290 
291 /* Allocate and initialize the subdevice structures.
292  */
293 static int setup_subdevices(struct comedi_device *dev)
294 {
295         struct comedi_subdevice *s;
296         int ret;
297 
298         ret = comedi_alloc_subdevices(dev, 3);
299         if (ret)
300                 return ret;
301 
302         /* analog output subdevice */
303         s = &dev->subdevices[0];
304         s->type = COMEDI_SUBD_AO;
305         s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND;
306         s->n_chan = NUM_AO_CHANNELS;
307         s->maxdata = 0x3fff;
308         s->range_table = &ao_ranges_1724;
309         s->insn_read = ao_readback_insn;
310         s->insn_write = ao_winsn;
311 
312         /* offset calibration */
313         s = &dev->subdevices[1];
314         s->type = COMEDI_SUBD_CALIB;
315         s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
316         s->n_chan = NUM_AO_CHANNELS;
317         s->insn_read = offset_read_insn;
318         s->insn_write = offset_write_insn;
319         s->maxdata = 0x3fff;
320 
321         /* gain calibration */
322         s = &dev->subdevices[2];
323         s->type = COMEDI_SUBD_CALIB;
324         s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
325         s->n_chan = NUM_AO_CHANNELS;
326         s->insn_read = gain_read_insn;
327         s->insn_write = gain_write_insn;
328         s->maxdata = 0x3fff;
329 
330         return 0;
331 }
332 
333 static int adv_pci1724_auto_attach(struct comedi_device *dev,
334                                    unsigned long context_unused)
335 {
336         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
337         struct adv_pci1724_private *devpriv;
338         int i;
339         int retval;
340         unsigned int board_id;
341 
342         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
343         if (!devpriv)
344                 return -ENOMEM;
345 
346         /* init software copies of output values to indicate we don't know
347          * what the output value is since it has never been written. */
348         for (i = 0; i < NUM_AO_CHANNELS; ++i) {
349                 devpriv->ao_value[i] = -1;
350                 devpriv->offset_value[i] = -1;
351                 devpriv->gain_value[i] = -1;
352         }
353 
354         retval = comedi_pci_enable(dev);
355         if (retval)
356                 return retval;
357 
358         dev->iobase = pci_resource_start(pcidev, 2);
359         board_id = inl(dev->iobase + BOARD_ID_REG) & BOARD_ID_MASK;
360         dev_info(dev->class_dev, "board id: %d\n", board_id);
361 
362         retval = setup_subdevices(dev);
363         if (retval < 0)
364                 return retval;
365 
366         dev_info(dev->class_dev, "%s (pci %s) attached, board id: %u\n",
367                  dev->board_name, pci_name(pcidev), board_id);
368         return 0;
369 }
370 
371 static struct comedi_driver adv_pci1724_driver = {
372         .driver_name = "adv_pci1724",
373         .module = THIS_MODULE,
374         .auto_attach = adv_pci1724_auto_attach,
375         .detach = comedi_pci_detach,
376 };
377 
378 static int adv_pci1724_pci_probe(struct pci_dev *dev,
379                                  const struct pci_device_id *id)
380 {
381         return comedi_pci_auto_config(dev, &adv_pci1724_driver,
382                                       id->driver_data);
383 }
384 
385 static const struct pci_device_id adv_pci1724_pci_table[] = {
386         { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1724) },
387         { 0 }
388 };
389 MODULE_DEVICE_TABLE(pci, adv_pci1724_pci_table);
390 
391 static struct pci_driver adv_pci1724_pci_driver = {
392         .name = "adv_pci1724",
393         .id_table = adv_pci1724_pci_table,
394         .probe = adv_pci1724_pci_probe,
395         .remove = comedi_pci_auto_unconfig,
396 };
397 
398 module_comedi_pci_driver(adv_pci1724_driver, adv_pci1724_pci_driver);
399 
400 MODULE_AUTHOR("Frank Mori Hess <fmh6jj@gmail.com>");
401 MODULE_DESCRIPTION("Advantech PCI-1724U Comedi driver");
402 MODULE_LICENSE("GPL");
403 

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