]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/adv_pci1724.c
Merge branch 'drm-nouveau-next' of git://anongit.freedesktop.org/git/nouveau/linux-2.6
[karo-tx-linux.git] / 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/pci.h>
56
57 #include "../comedidev.h"
58
59 #define PCI_VENDOR_ID_ADVANTECH 0x13fe
60
61 #define NUM_AO_CHANNELS 32
62
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
70 };
71
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
81 };
82
83 static uint32_t dac_data_bits(uint16_t dac_data)
84 {
85         return dac_data & DAC_DATA_MASK;
86 }
87
88 static uint32_t dac_channel_select_bits(unsigned channel)
89 {
90         return (channel << 16) & DAC_CHANNEL_SELECT_MASK;
91 }
92
93 static uint32_t dac_group_select_bits(unsigned group)
94 {
95         return (1 << (20 + group)) & DAC_GROUP_SELECT_MASK;
96 }
97
98 static uint32_t dac_channel_and_group_select_bits(unsigned comedi_channel)
99 {
100         return dac_channel_select_bits(comedi_channel % 8) |
101                 dac_group_select_bits(comedi_channel / 8);
102 }
103
104 enum sync_output_contents {
105         SYNC_MODE = 0x1,
106         DAC_BUSY = 0x2, /* dac state machine is not ready */
107 };
108
109 enum sync_output_trigger_contents {
110         SYNC_TRIGGER_BITS = 0x0 /* any value works */
111 };
112
113 enum board_id_contents {
114         BOARD_ID_MASK = 0xf
115 };
116
117 static const struct comedi_lrange ao_ranges_1724 = { 4,
118         {
119                 BIP_RANGE(10),
120                 RANGE_mA(0, 20),
121                 RANGE_mA(4, 20),
122                 RANGE_unitless(0, 1)
123         }
124 };
125
126 static const struct comedi_lrange *const ao_range_list_1724[NUM_AO_CHANNELS] = {
127         [0 ... NUM_AO_CHANNELS - 1] = &ao_ranges_1724,
128 };
129
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];
135 };
136
137 static int wait_for_dac_idle(struct comedi_device *dev)
138 {
139         static const int timeout = 10000;
140         int i;
141
142         for (i = 0; i < timeout; ++i) {
143                 if ((inl(dev->iobase + SYNC_OUTPUT_REG) & DAC_BUSY) == 0)
144                         break;
145                 udelay(1);
146         }
147         if (i == timeout) {
148                 comedi_error(dev, "Timed out waiting for dac to become idle.");
149                 return -EIO;
150         }
151         return 0;
152 }
153
154 static int set_dac(struct comedi_device *dev, unsigned mode, unsigned channel,
155                    unsigned data)
156 {
157         int retval;
158         unsigned control_bits;
159
160         retval = wait_for_dac_idle(dev);
161         if (retval < 0)
162                 return retval;
163
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;
169 }
170
171 static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
172                     struct comedi_insn *insn, unsigned int *data)
173 {
174         struct adv_pci1724_private *devpriv = dev->private;
175         int channel = CR_CHAN(insn->chanspec);
176         int retval;
177         int i;
178
179         /* turn off synchronous mode */
180         outl(0, dev->iobase + SYNC_OUTPUT_REG);
181
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];
187         }
188         return insn->n;
189 }
190
191 static int ao_readback_insn(struct comedi_device *dev,
192                             struct comedi_subdevice *s,
193                             struct comedi_insn *insn, unsigned int *data)
194 {
195         struct adv_pci1724_private *devpriv = dev->private;
196         int channel = CR_CHAN(insn->chanspec);
197         int i;
198
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;
203         }
204         for (i = 0; i < insn->n; i++)
205                 data[i] = devpriv->ao_value[channel];
206
207         return insn->n;
208 }
209
210 static int offset_write_insn(struct comedi_device *dev,
211                              struct comedi_subdevice *s,
212                              struct comedi_insn *insn, unsigned int *data)
213 {
214         struct adv_pci1724_private *devpriv = dev->private;
215         int channel = CR_CHAN(insn->chanspec);
216         int retval;
217         int i;
218
219         /* turn off synchronous mode */
220         outl(0, dev->iobase + SYNC_OUTPUT_REG);
221
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];
227         }
228
229         return insn->n;
230 }
231
232 static int offset_read_insn(struct comedi_device *dev,
233                             struct comedi_subdevice *s,
234                             struct comedi_insn *insn, unsigned int *data)
235 {
236         struct adv_pci1724_private *devpriv = dev->private;
237         unsigned int channel = CR_CHAN(insn->chanspec);
238         int i;
239
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;
244         }
245         for (i = 0; i < insn->n; i++)
246                 data[i] = devpriv->offset_value[channel];
247
248         return insn->n;
249 }
250
251 static int gain_write_insn(struct comedi_device *dev,
252                            struct comedi_subdevice *s,
253                            struct comedi_insn *insn, unsigned int *data)
254 {
255         struct adv_pci1724_private *devpriv = dev->private;
256         int channel = CR_CHAN(insn->chanspec);
257         int retval;
258         int i;
259
260         /* turn off synchronous mode */
261         outl(0, dev->iobase + SYNC_OUTPUT_REG);
262
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];
268         }
269
270         return insn->n;
271 }
272
273 static int gain_read_insn(struct comedi_device *dev,
274                           struct comedi_subdevice *s, struct comedi_insn *insn,
275                           unsigned int *data)
276 {
277         struct adv_pci1724_private *devpriv = dev->private;
278         unsigned int channel = CR_CHAN(insn->chanspec);
279         int i;
280
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;
285         }
286         for (i = 0; i < insn->n; i++)
287                 data[i] = devpriv->gain_value[channel];
288
289         return insn->n;
290 }
291
292 /* Allocate and initialize the subdevice structures.
293  */
294 static int setup_subdevices(struct comedi_device *dev)
295 {
296         struct comedi_subdevice *s;
297         int ret;
298
299         ret = comedi_alloc_subdevices(dev, 3);
300         if (ret)
301                 return ret;
302
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;
312
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;
321
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;
330
331         return 0;
332 }
333
334 static int adv_pci1724_auto_attach(struct comedi_device *dev,
335                                    unsigned long context_unused)
336 {
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;
342
343         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
344         if (!devpriv)
345                 return -ENOMEM;
346         dev->private = devpriv;
347
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;
354         }
355
356         retval = comedi_pci_enable(dev);
357         if (retval)
358                 return retval;
359
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);
363
364         retval = setup_subdevices(dev);
365         if (retval < 0)
366                 return retval;
367
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;
371 }
372
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,
378 };
379
380 static int adv_pci1724_pci_probe(struct pci_dev *dev,
381                                  const struct pci_device_id *id)
382 {
383         return comedi_pci_auto_config(dev, &adv_pci1724_driver,
384                                       id->driver_data);
385 }
386
387 static DEFINE_PCI_DEVICE_TABLE(adv_pci1724_pci_table) = {
388         { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1724) },
389         { 0 }
390 };
391 MODULE_DEVICE_TABLE(pci, adv_pci1724_pci_table);
392
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,
398 };
399
400 module_comedi_pci_driver(adv_pci1724_driver, adv_pci1724_pci_driver);
401
402 MODULE_AUTHOR("Frank Mori Hess <fmh6jj@gmail.com>");
403 MODULE_DESCRIPTION("Advantech PCI-1724U Comedi driver");
404 MODULE_LICENSE("GPL");