staging: comedi: cb_pcimdas: support analog input programmable ranges
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / cb_pcimdas.c
1 /*
2  * comedi/drivers/cb_pcimdas.c
3  * Comedi driver for Computer Boards PCIM-DAS1602/16 and PCIe-DAS1602/16
4  *
5  * COMEDI - Linux Control and Measurement Device Interface
6  * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  */
18
19 /*
20  * Driver: cb_pcimdas
21  * Description: Measurement Computing PCI Migration series boards
22  * Devices: [ComputerBoards] PCIM-DAS1602/16 (cb_pcimdas), PCIe-DAS1602/16
23  * Author: Richard Bytheway
24  * Updated: Mon, 13 Oct 2014 11:57:39 +0000
25  * Status: experimental
26  *
27  * Written to support the PCIM-DAS1602/16 and PCIe-DAS1602/16.
28  *
29  * Configuration Options:
30  *   none
31  *
32  * Manual configuration of PCI(e) cards is not supported; they are configured
33  * automatically.
34  *
35  * Developed from cb_pcidas and skel by Richard Bytheway (mocelet@sucs.org).
36  * Only supports DIO, AO and simple AI in it's present form.
37  * No interrupts, multi channel or FIFO AI,
38  * although the card looks like it could support this.
39  *
40  * http://www.mccdaq.com/PDFs/Manuals/pcim-das1602-16.pdf
41  * http://www.mccdaq.com/PDFs/Manuals/pcie-das1602-16.pdf
42  */
43
44 #include <linux/module.h>
45 #include <linux/pci.h>
46 #include <linux/interrupt.h>
47
48 #include "../comedidev.h"
49
50 #include "plx9052.h"
51 #include "8255.h"
52
53 /* Registers for the PCIM-DAS1602/16 and PCIe-DAS1602/16 */
54
55 /* DAC Offsets */
56 #define ADC_TRIG 0
57 #define DAC0_OFFSET 2
58 #define DAC1_OFFSET 4
59
60 /* AI and Counter Constants */
61 #define MUX_LIMITS 0
62 #define MAIN_CONN_DIO 1
63 #define ADC_STAT 2
64 #define ADC_CONV_STAT 3
65 #define ADC_INT 4
66 #define ADC_PACER 5
67 #define BURST_MODE 6
68 #define PROG_GAIN 7
69 #define CLK8254_1_DATA 8
70 #define CLK8254_2_DATA 9
71 #define CLK8254_3_DATA 10
72 #define CLK8254_CONTROL 11
73 #define USER_COUNTER 12
74 #define RESID_COUNT_H 13
75 #define RESID_COUNT_L 14
76
77 static const struct comedi_lrange cb_pcimdas_ai_bip_range = {
78         4, {
79                 BIP_RANGE(10),
80                 BIP_RANGE(5),
81                 BIP_RANGE(2.5),
82                 BIP_RANGE(1.25)
83         }
84 };
85
86 static const struct comedi_lrange cb_pcimdas_ai_uni_range = {
87         4, {
88                 UNI_RANGE(10),
89                 UNI_RANGE(5),
90                 UNI_RANGE(2.5),
91                 UNI_RANGE(1.25)
92         }
93 };
94
95 /*
96  * this structure is for data unique to this hardware driver.  If
97  * several hardware drivers keep similar information in this structure,
98  * feel free to suggest moving the variable to the struct comedi_device
99  * struct.
100  */
101 struct cb_pcimdas_private {
102         /* base addresses */
103         unsigned long daqio;
104         unsigned long BADR3;
105 };
106
107 static int cb_pcimdas_ai_eoc(struct comedi_device *dev,
108                              struct comedi_subdevice *s,
109                              struct comedi_insn *insn,
110                              unsigned long context)
111 {
112         struct cb_pcimdas_private *devpriv = dev->private;
113         unsigned int status;
114
115         status = inb(devpriv->BADR3 + 2);
116         if ((status & 0x80) == 0)
117                 return 0;
118         return -EBUSY;
119 }
120
121 static int cb_pcimdas_ai_rinsn(struct comedi_device *dev,
122                                struct comedi_subdevice *s,
123                                struct comedi_insn *insn, unsigned int *data)
124 {
125         struct cb_pcimdas_private *devpriv = dev->private;
126         unsigned int chan = CR_CHAN(insn->chanspec);
127         unsigned int range = CR_RANGE(insn->chanspec);
128         int n;
129         unsigned int d;
130         unsigned short chanlims;
131         int ret;
132
133         /*  only support sw initiated reads from a single channel */
134
135         /* configure for sw initiated read */
136         d = inb(devpriv->BADR3 + 5);
137         if ((d & 0x03) > 0) {   /* only reset if needed. */
138                 d = d & 0xfd;
139                 outb(d, devpriv->BADR3 + 5);
140         }
141
142         /* set bursting off, conversions on */
143         outb(0x01, devpriv->BADR3 + 6);
144
145         /* set range */
146         outb(range, devpriv->BADR3 + 7);
147
148         /*
149          * write channel limits to multiplexer, set Low (bits 0-3) and
150          * High (bits 4-7) channels to chan.
151          */
152         chanlims = chan | (chan << 4);
153         outb(chanlims, devpriv->BADR3 + 0);
154
155         /* convert n samples */
156         for (n = 0; n < insn->n; n++) {
157                 /* trigger conversion */
158                 outw(0, devpriv->daqio + 0);
159
160                 /* wait for conversion to end */
161                 ret = comedi_timeout(dev, s, insn, cb_pcimdas_ai_eoc, 0);
162                 if (ret)
163                         return ret;
164
165                 /* read data */
166                 data[n] = inw(devpriv->daqio + 0);
167         }
168
169         /* return the number of samples read/written */
170         return n;
171 }
172
173 static int cb_pcimdas_ao_insn_write(struct comedi_device *dev,
174                                     struct comedi_subdevice *s,
175                                     struct comedi_insn *insn,
176                                     unsigned int *data)
177 {
178         struct cb_pcimdas_private *devpriv = dev->private;
179         unsigned int chan = CR_CHAN(insn->chanspec);
180         unsigned int val = s->readback[chan];
181         unsigned int reg = (chan) ? DAC1_OFFSET : DAC0_OFFSET;
182         int i;
183
184         for (i = 0; i < insn->n; i++) {
185                 val = data[i];
186                 outw(val, devpriv->daqio + reg);
187         }
188         s->readback[chan] = val;
189
190         return insn->n;
191 }
192
193 static bool cb_pcimdas_is_ai_se(struct comedi_device *dev)
194 {
195         struct cb_pcimdas_private *devpriv = dev->private;
196         unsigned int status;
197
198         /*
199          * The number of Analog Input channels is set with the
200          * Analog Input Mode Switch on the board. The board can
201          * have 16 single-ended or 8 differential channels.
202          */
203         status = inb(devpriv->BADR3 + 2);
204         return status & 0x20;
205 }
206
207 static bool cb_pcimdas_is_ai_uni(struct comedi_device *dev)
208 {
209         struct cb_pcimdas_private *devpriv = dev->private;
210         unsigned int status;
211
212         /*
213          * The Analog Input range polarity is set with the
214          * Analog Input Polarity Switch on the board. The
215          * inputs can be set to Unipolar or Bipolar ranges.
216          */
217         status = inb(devpriv->BADR3 + 2);
218         return status & 0x40;
219 }
220
221 static int cb_pcimdas_auto_attach(struct comedi_device *dev,
222                                             unsigned long context_unused)
223 {
224         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
225         struct cb_pcimdas_private *devpriv;
226         struct comedi_subdevice *s;
227         int ret;
228
229         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
230         if (!devpriv)
231                 return -ENOMEM;
232
233         ret = comedi_pci_enable(dev);
234         if (ret)
235                 return ret;
236
237         devpriv->daqio = pci_resource_start(pcidev, 2);
238         devpriv->BADR3 = pci_resource_start(pcidev, 3);
239         dev->iobase = pci_resource_start(pcidev, 4);
240
241         ret = comedi_alloc_subdevices(dev, 3);
242         if (ret)
243                 return ret;
244
245         s = &dev->subdevices[0];
246         /* dev->read_subdev=s; */
247         /*  analog input subdevice */
248         s->type = COMEDI_SUBD_AI;
249         s->subdev_flags = SDF_READABLE;
250         if (cb_pcimdas_is_ai_se(dev)) {
251                 s->subdev_flags |= SDF_GROUND;
252                 s->n_chan       = 16;
253         } else {
254                 s->subdev_flags |= SDF_DIFF;
255                 s->n_chan       = 8;
256         }
257         s->maxdata = 0xffff;
258         s->range_table  = cb_pcimdas_is_ai_uni(dev) ? &cb_pcimdas_ai_uni_range
259                                                     : &cb_pcimdas_ai_bip_range;
260         s->len_chanlist = 1;    /*  This is the maximum chanlist length that */
261         /*  the board can handle */
262         s->insn_read = cb_pcimdas_ai_rinsn;
263
264         s = &dev->subdevices[1];
265         /*  analog output subdevice */
266         s->type = COMEDI_SUBD_AO;
267         s->subdev_flags = SDF_WRITABLE;
268         s->n_chan = 2;
269         s->maxdata = 0xfff;
270         /* ranges are hardware settable, but not software readable. */
271         s->range_table = &range_unknown;
272         s->insn_write = cb_pcimdas_ao_insn_write;
273
274         ret = comedi_alloc_subdev_readback(s);
275         if (ret)
276                 return ret;
277
278         s = &dev->subdevices[2];
279         /* digital i/o subdevice */
280         ret = subdev_8255_init(dev, s, NULL, 0x00);
281         if (ret)
282                 return ret;
283
284         return 0;
285 }
286
287 static struct comedi_driver cb_pcimdas_driver = {
288         .driver_name    = "cb_pcimdas",
289         .module         = THIS_MODULE,
290         .auto_attach    = cb_pcimdas_auto_attach,
291         .detach         = comedi_pci_detach,
292 };
293
294 static int cb_pcimdas_pci_probe(struct pci_dev *dev,
295                                 const struct pci_device_id *id)
296 {
297         return comedi_pci_auto_config(dev, &cb_pcimdas_driver,
298                                       id->driver_data);
299 }
300
301 static const struct pci_device_id cb_pcimdas_pci_table[] = {
302         { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0056) },       /* PCIM-DAS1602/16 */
303         { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0115) },       /* PCIe-DAS1602/16 */
304         { 0 }
305 };
306 MODULE_DEVICE_TABLE(pci, cb_pcimdas_pci_table);
307
308 static struct pci_driver cb_pcimdas_pci_driver = {
309         .name           = "cb_pcimdas",
310         .id_table       = cb_pcimdas_pci_table,
311         .probe          = cb_pcimdas_pci_probe,
312         .remove         = comedi_pci_auto_unconfig,
313 };
314 module_comedi_pci_driver(cb_pcimdas_driver, cb_pcimdas_pci_driver);
315
316 MODULE_AUTHOR("Comedi http://www.comedi.org");
317 MODULE_DESCRIPTION("Comedi driver for PCIM-DAS1602/16 and PCIe-DAS1602/16");
318 MODULE_LICENSE("GPL");