Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / comedi / drivers / adl_pci7x3x.c
1 /*
2  * COMEDI driver for the ADLINK PCI-723x/743x series boards.
3  * Copyright (C) 2012 H Hartley Sweeten <hsweeten@visionengravers.com>
4  *
5  * Based on the adl_pci7230 driver written by:
6  *      David Fernandez <dfcastelao@gmail.com>
7  * and the adl_pci7432 driver written by:
8  *      Michel Lachaine <mike@mikelachaine.ca>
9  *
10  * COMEDI - Linux Control and Measurement Device Interface
11  * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  */
23
24 /*
25  * Driver: adl_pci7x3x
26  * Description: 32/64-Channel Isolated Digital I/O Boards
27  * Devices: [ADLink] PCI-7230 (adl_pci7230), PCI-7233 (adl_pci7233),
28  *   PCI-7234 (adl_pci7234), PCI-7432 (adl_pci7432), PCI-7433 (adl_pci7433),
29  *   PCI-7434 (adl_pci7434)
30  * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
31  * Updated: Thu, 02 Aug 2012 14:27:46 -0700
32  * Status: untested
33  *
34  * One or two subdevices are setup by this driver depending on
35  * the number of digital inputs and/or outputs provided by the
36  * board. Each subdevice has a maximum of 32 channels.
37  *
38  *      PCI-7230 - 2 subdevices: 0 - 16 input, 1 - 16 output
39  *      PCI-7233 - 1 subdevice: 0 - 32 input
40  *      PCI-7234 - 1 subdevice: 0 - 32 output
41  *      PCI-7432 - 2 subdevices: 0 - 32 input, 1 - 32 output
42  *      PCI-7433 - 2 subdevices: 0 - 32 input, 1 - 32 input
43  *      PCI-7434 - 2 subdevices: 0 - 32 output, 1 - 32 output
44  *
45  * The PCI-7230, PCI-7432 and PCI-7433 boards also support external
46  * interrupt signals on digital input channels 0 and 1. The PCI-7233
47  * has dual-interrupt sources for change-of-state (COS) on any 16
48  * digital input channels of LSB and for COS on any 16 digital input
49  * lines of MSB. Interrupts are not currently supported by this
50  * driver.
51  *
52  * Configuration Options: not applicable, uses comedi PCI auto config
53  */
54
55 #include <linux/module.h>
56
57 #include "../comedi_pci.h"
58
59 /*
60  * Register I/O map (32-bit access only)
61  */
62 #define PCI7X3X_DIO_REG         0x00
63 #define PCI743X_DIO_REG         0x04
64
65 enum apci1516_boardid {
66         BOARD_PCI7230,
67         BOARD_PCI7233,
68         BOARD_PCI7234,
69         BOARD_PCI7432,
70         BOARD_PCI7433,
71         BOARD_PCI7434,
72 };
73
74 struct adl_pci7x3x_boardinfo {
75         const char *name;
76         int nsubdevs;
77         int di_nchan;
78         int do_nchan;
79 };
80
81 static const struct adl_pci7x3x_boardinfo adl_pci7x3x_boards[] = {
82         [BOARD_PCI7230] = {
83                 .name           = "adl_pci7230",
84                 .nsubdevs       = 2,
85                 .di_nchan       = 16,
86                 .do_nchan       = 16,
87         },
88         [BOARD_PCI7233] = {
89                 .name           = "adl_pci7233",
90                 .nsubdevs       = 1,
91                 .di_nchan       = 32,
92         },
93         [BOARD_PCI7234] = {
94                 .name           = "adl_pci7234",
95                 .nsubdevs       = 1,
96                 .do_nchan       = 32,
97         },
98         [BOARD_PCI7432] = {
99                 .name           = "adl_pci7432",
100                 .nsubdevs       = 2,
101                 .di_nchan       = 32,
102                 .do_nchan       = 32,
103         },
104         [BOARD_PCI7433] = {
105                 .name           = "adl_pci7433",
106                 .nsubdevs       = 2,
107                 .di_nchan       = 64,
108         },
109         [BOARD_PCI7434] = {
110                 .name           = "adl_pci7434",
111                 .nsubdevs       = 2,
112                 .do_nchan       = 64,
113         }
114 };
115
116 static int adl_pci7x3x_do_insn_bits(struct comedi_device *dev,
117                                     struct comedi_subdevice *s,
118                                     struct comedi_insn *insn,
119                                     unsigned int *data)
120 {
121         unsigned long reg = (unsigned long)s->private;
122
123         if (comedi_dio_update_state(s, data))
124                 outl(s->state, dev->iobase + reg);
125
126         data[1] = s->state;
127
128         return insn->n;
129 }
130
131 static int adl_pci7x3x_di_insn_bits(struct comedi_device *dev,
132                                     struct comedi_subdevice *s,
133                                     struct comedi_insn *insn,
134                                     unsigned int *data)
135 {
136         unsigned long reg = (unsigned long)s->private;
137
138         data[1] = inl(dev->iobase + reg);
139
140         return insn->n;
141 }
142
143 static int adl_pci7x3x_auto_attach(struct comedi_device *dev,
144                                    unsigned long context)
145 {
146         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
147         const struct adl_pci7x3x_boardinfo *board = NULL;
148         struct comedi_subdevice *s;
149         int subdev;
150         int nchan;
151         int ret;
152
153         if (context < ARRAY_SIZE(adl_pci7x3x_boards))
154                 board = &adl_pci7x3x_boards[context];
155         if (!board)
156                 return -ENODEV;
157         dev->board_ptr = board;
158         dev->board_name = board->name;
159
160         ret = comedi_pci_enable(dev);
161         if (ret)
162                 return ret;
163         dev->iobase = pci_resource_start(pcidev, 2);
164
165         ret = comedi_alloc_subdevices(dev, board->nsubdevs);
166         if (ret)
167                 return ret;
168
169         subdev = 0;
170
171         if (board->di_nchan) {
172                 nchan = min(board->di_nchan, 32);
173
174                 s = &dev->subdevices[subdev];
175                 /* Isolated digital inputs 0 to 15/31 */
176                 s->type         = COMEDI_SUBD_DI;
177                 s->subdev_flags = SDF_READABLE;
178                 s->n_chan       = nchan;
179                 s->maxdata      = 1;
180                 s->insn_bits    = adl_pci7x3x_di_insn_bits;
181                 s->range_table  = &range_digital;
182
183                 s->private      = (void *)PCI7X3X_DIO_REG;
184
185                 subdev++;
186
187                 nchan = board->di_nchan - nchan;
188                 if (nchan) {
189                         s = &dev->subdevices[subdev];
190                         /* Isolated digital inputs 32 to 63 */
191                         s->type         = COMEDI_SUBD_DI;
192                         s->subdev_flags = SDF_READABLE;
193                         s->n_chan       = nchan;
194                         s->maxdata      = 1;
195                         s->insn_bits    = adl_pci7x3x_di_insn_bits;
196                         s->range_table  = &range_digital;
197
198                         s->private      = (void *)PCI743X_DIO_REG;
199
200                         subdev++;
201                 }
202         }
203
204         if (board->do_nchan) {
205                 nchan = min(board->do_nchan, 32);
206
207                 s = &dev->subdevices[subdev];
208                 /* Isolated digital outputs 0 to 15/31 */
209                 s->type         = COMEDI_SUBD_DO;
210                 s->subdev_flags = SDF_WRITABLE;
211                 s->n_chan       = nchan;
212                 s->maxdata      = 1;
213                 s->insn_bits    = adl_pci7x3x_do_insn_bits;
214                 s->range_table  = &range_digital;
215
216                 s->private      = (void *)PCI7X3X_DIO_REG;
217
218                 subdev++;
219
220                 nchan = board->do_nchan - nchan;
221                 if (nchan) {
222                         s = &dev->subdevices[subdev];
223                         /* Isolated digital outputs 32 to 63 */
224                         s->type         = COMEDI_SUBD_DO;
225                         s->subdev_flags = SDF_WRITABLE;
226                         s->n_chan       = nchan;
227                         s->maxdata      = 1;
228                         s->insn_bits    = adl_pci7x3x_do_insn_bits;
229                         s->range_table  = &range_digital;
230
231                         s->private      = (void *)PCI743X_DIO_REG;
232
233                         subdev++;
234                 }
235         }
236
237         return 0;
238 }
239
240 static struct comedi_driver adl_pci7x3x_driver = {
241         .driver_name    = "adl_pci7x3x",
242         .module         = THIS_MODULE,
243         .auto_attach    = adl_pci7x3x_auto_attach,
244         .detach         = comedi_pci_detach,
245 };
246
247 static int adl_pci7x3x_pci_probe(struct pci_dev *dev,
248                                  const struct pci_device_id *id)
249 {
250         return comedi_pci_auto_config(dev, &adl_pci7x3x_driver,
251                                       id->driver_data);
252 }
253
254 static const struct pci_device_id adl_pci7x3x_pci_table[] = {
255         { PCI_VDEVICE(ADLINK, 0x7230), BOARD_PCI7230 },
256         { PCI_VDEVICE(ADLINK, 0x7233), BOARD_PCI7233 },
257         { PCI_VDEVICE(ADLINK, 0x7234), BOARD_PCI7234 },
258         { PCI_VDEVICE(ADLINK, 0x7432), BOARD_PCI7432 },
259         { PCI_VDEVICE(ADLINK, 0x7433), BOARD_PCI7433 },
260         { PCI_VDEVICE(ADLINK, 0x7434), BOARD_PCI7434 },
261         { 0 }
262 };
263 MODULE_DEVICE_TABLE(pci, adl_pci7x3x_pci_table);
264
265 static struct pci_driver adl_pci7x3x_pci_driver = {
266         .name           = "adl_pci7x3x",
267         .id_table       = adl_pci7x3x_pci_table,
268         .probe          = adl_pci7x3x_pci_probe,
269         .remove         = comedi_pci_auto_unconfig,
270 };
271 module_comedi_pci_driver(adl_pci7x3x_driver, adl_pci7x3x_pci_driver);
272
273 MODULE_DESCRIPTION("ADLINK PCI-723x/743x Isolated Digital I/O boards");
274 MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
275 MODULE_LICENSE("GPL");