OSDN Git Service

Merge tag 'pinctrl-v3.12-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw...
[sagit-ice-cold/kernel_xiaomi_msm8998.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/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 = { 4,
120         {
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 DEFINE_PCI_DEVICE_TABLE(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");