These changes are a raw update to a vanilla kernel 4.1.10, with the
[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                 unsigned int val = s->state;
125
126                 if (s->n_chan == 16) {
127                         /*
128                          * It seems the PCI-7230 needs the 16-bit DO state
129                          * to be shifted left by 16 bits before being written
130                          * to the 32-bit register.  Set the value in both
131                          * halves of the register to be sure.
132                          */
133                         val |= val << 16;
134                 }
135                 outl(val, dev->iobase + reg);
136         }
137
138         data[1] = s->state;
139
140         return insn->n;
141 }
142
143 static int adl_pci7x3x_di_insn_bits(struct comedi_device *dev,
144                                     struct comedi_subdevice *s,
145                                     struct comedi_insn *insn,
146                                     unsigned int *data)
147 {
148         unsigned long reg = (unsigned long)s->private;
149
150         data[1] = inl(dev->iobase + reg);
151
152         return insn->n;
153 }
154
155 static int adl_pci7x3x_auto_attach(struct comedi_device *dev,
156                                    unsigned long context)
157 {
158         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
159         const struct adl_pci7x3x_boardinfo *board = NULL;
160         struct comedi_subdevice *s;
161         int subdev;
162         int nchan;
163         int ret;
164
165         if (context < ARRAY_SIZE(adl_pci7x3x_boards))
166                 board = &adl_pci7x3x_boards[context];
167         if (!board)
168                 return -ENODEV;
169         dev->board_ptr = board;
170         dev->board_name = board->name;
171
172         ret = comedi_pci_enable(dev);
173         if (ret)
174                 return ret;
175         dev->iobase = pci_resource_start(pcidev, 2);
176
177         ret = comedi_alloc_subdevices(dev, board->nsubdevs);
178         if (ret)
179                 return ret;
180
181         subdev = 0;
182
183         if (board->di_nchan) {
184                 nchan = min(board->di_nchan, 32);
185
186                 s = &dev->subdevices[subdev];
187                 /* Isolated digital inputs 0 to 15/31 */
188                 s->type         = COMEDI_SUBD_DI;
189                 s->subdev_flags = SDF_READABLE;
190                 s->n_chan       = nchan;
191                 s->maxdata      = 1;
192                 s->insn_bits    = adl_pci7x3x_di_insn_bits;
193                 s->range_table  = &range_digital;
194
195                 s->private      = (void *)PCI7X3X_DIO_REG;
196
197                 subdev++;
198
199                 nchan = board->di_nchan - nchan;
200                 if (nchan) {
201                         s = &dev->subdevices[subdev];
202                         /* Isolated digital inputs 32 to 63 */
203                         s->type         = COMEDI_SUBD_DI;
204                         s->subdev_flags = SDF_READABLE;
205                         s->n_chan       = nchan;
206                         s->maxdata      = 1;
207                         s->insn_bits    = adl_pci7x3x_di_insn_bits;
208                         s->range_table  = &range_digital;
209
210                         s->private      = (void *)PCI743X_DIO_REG;
211
212                         subdev++;
213                 }
214         }
215
216         if (board->do_nchan) {
217                 nchan = min(board->do_nchan, 32);
218
219                 s = &dev->subdevices[subdev];
220                 /* Isolated digital outputs 0 to 15/31 */
221                 s->type         = COMEDI_SUBD_DO;
222                 s->subdev_flags = SDF_WRITABLE;
223                 s->n_chan       = nchan;
224                 s->maxdata      = 1;
225                 s->insn_bits    = adl_pci7x3x_do_insn_bits;
226                 s->range_table  = &range_digital;
227
228                 s->private      = (void *)PCI7X3X_DIO_REG;
229
230                 subdev++;
231
232                 nchan = board->do_nchan - nchan;
233                 if (nchan) {
234                         s = &dev->subdevices[subdev];
235                         /* Isolated digital outputs 32 to 63 */
236                         s->type         = COMEDI_SUBD_DO;
237                         s->subdev_flags = SDF_WRITABLE;
238                         s->n_chan       = nchan;
239                         s->maxdata      = 1;
240                         s->insn_bits    = adl_pci7x3x_do_insn_bits;
241                         s->range_table  = &range_digital;
242
243                         s->private      = (void *)PCI743X_DIO_REG;
244
245                         subdev++;
246                 }
247         }
248
249         return 0;
250 }
251
252 static struct comedi_driver adl_pci7x3x_driver = {
253         .driver_name    = "adl_pci7x3x",
254         .module         = THIS_MODULE,
255         .auto_attach    = adl_pci7x3x_auto_attach,
256         .detach         = comedi_pci_detach,
257 };
258
259 static int adl_pci7x3x_pci_probe(struct pci_dev *dev,
260                                  const struct pci_device_id *id)
261 {
262         return comedi_pci_auto_config(dev, &adl_pci7x3x_driver,
263                                       id->driver_data);
264 }
265
266 static const struct pci_device_id adl_pci7x3x_pci_table[] = {
267         { PCI_VDEVICE(ADLINK, 0x7230), BOARD_PCI7230 },
268         { PCI_VDEVICE(ADLINK, 0x7233), BOARD_PCI7233 },
269         { PCI_VDEVICE(ADLINK, 0x7234), BOARD_PCI7234 },
270         { PCI_VDEVICE(ADLINK, 0x7432), BOARD_PCI7432 },
271         { PCI_VDEVICE(ADLINK, 0x7433), BOARD_PCI7433 },
272         { PCI_VDEVICE(ADLINK, 0x7434), BOARD_PCI7434 },
273         { 0 }
274 };
275 MODULE_DEVICE_TABLE(pci, adl_pci7x3x_pci_table);
276
277 static struct pci_driver adl_pci7x3x_pci_driver = {
278         .name           = "adl_pci7x3x",
279         .id_table       = adl_pci7x3x_pci_table,
280         .probe          = adl_pci7x3x_pci_probe,
281         .remove         = comedi_pci_auto_unconfig,
282 };
283 module_comedi_pci_driver(adl_pci7x3x_driver, adl_pci7x3x_pci_driver);
284
285 MODULE_DESCRIPTION("ADLINK PCI-723x/743x Isolated Digital I/O boards");
286 MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
287 MODULE_LICENSE("GPL");