Merge tag 'hsi-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-hsi
[cascardo/linux.git] / 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] - 16 input / 16 output
28          (ADLink) PCI-7233 [adl_pci7233] - 32 input
29          (ADLink) PCI-7234 [adl_pci7234] - 32 output
30          (ADLink) PCI-7432 [adl_pci7432] - 32 input / 32 output
31          (ADLink) PCI-7433 [adl_pci7433] - 64 input
32          (ADLink) PCI-7434 [adl_pci7434] - 64 output
33 Author: H Hartley Sweeten <hsweeten@visionengravers.com>
34 Updated: Thu, 02 Aug 2012 14:27:46 -0700
35 Status: untested
36
37 The PCI-7230, PCI-7432 and PCI-7433 boards also support external
38 interrupt signals on digital input channels 0 and 1. The PCI-7233
39 has dual-interrupt sources for change-of-state (COS) on any 16
40 digital input channels of LSB and for COS on any 16 digital input
41 lines of MSB. Interrupts are not currently supported by this
42 driver.
43
44 Configuration Options: not applicable, uses comedi PCI auto config
45 */
46
47 #include <linux/module.h>
48 #include <linux/pci.h>
49
50 #include "../comedidev.h"
51
52 /*
53  * Register I/O map (32-bit access only)
54  */
55 #define PCI7X3X_DIO_REG         0x00
56 #define PCI743X_DIO_REG         0x04
57
58 enum apci1516_boardid {
59         BOARD_PCI7230,
60         BOARD_PCI7233,
61         BOARD_PCI7234,
62         BOARD_PCI7432,
63         BOARD_PCI7433,
64         BOARD_PCI7434,
65 };
66
67 struct adl_pci7x3x_boardinfo {
68         const char *name;
69         int nsubdevs;
70         int di_nchan;
71         int do_nchan;
72 };
73
74 static const struct adl_pci7x3x_boardinfo adl_pci7x3x_boards[] = {
75         [BOARD_PCI7230] = {
76                 .name           = "adl_pci7230",
77                 .nsubdevs       = 2,
78                 .di_nchan       = 16,
79                 .do_nchan       = 16,
80         },
81         [BOARD_PCI7233] = {
82                 .name           = "adl_pci7233",
83                 .nsubdevs       = 1,
84                 .di_nchan       = 32,
85         },
86         [BOARD_PCI7234] = {
87                 .name           = "adl_pci7234",
88                 .nsubdevs       = 1,
89                 .do_nchan       = 32,
90         },
91         [BOARD_PCI7432] = {
92                 .name           = "adl_pci7432",
93                 .nsubdevs       = 2,
94                 .di_nchan       = 32,
95                 .do_nchan       = 32,
96         },
97         [BOARD_PCI7433] = {
98                 .name           = "adl_pci7433",
99                 .nsubdevs       = 2,
100                 .di_nchan       = 64,
101         },
102         [BOARD_PCI7434] = {
103                 .name           = "adl_pci7434",
104                 .nsubdevs       = 2,
105                 .do_nchan       = 64,
106         }
107 };
108
109 static int adl_pci7x3x_do_insn_bits(struct comedi_device *dev,
110                                     struct comedi_subdevice *s,
111                                     struct comedi_insn *insn,
112                                     unsigned int *data)
113 {
114         unsigned long reg = (unsigned long)s->private;
115
116         if (comedi_dio_update_state(s, data))
117                 outl(s->state, dev->iobase + reg);
118
119         data[1] = s->state;
120
121         return insn->n;
122 }
123
124 static int adl_pci7x3x_di_insn_bits(struct comedi_device *dev,
125                                     struct comedi_subdevice *s,
126                                     struct comedi_insn *insn,
127                                     unsigned int *data)
128 {
129         unsigned long reg = (unsigned long)s->private;
130
131         data[1] = inl(dev->iobase + reg);
132
133         return insn->n;
134 }
135
136 static int adl_pci7x3x_auto_attach(struct comedi_device *dev,
137                                    unsigned long context)
138 {
139         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
140         const struct adl_pci7x3x_boardinfo *board = NULL;
141         struct comedi_subdevice *s;
142         int subdev;
143         int nchan;
144         int ret;
145
146         if (context < ARRAY_SIZE(adl_pci7x3x_boards))
147                 board = &adl_pci7x3x_boards[context];
148         if (!board)
149                 return -ENODEV;
150         dev->board_ptr = board;
151         dev->board_name = board->name;
152
153         ret = comedi_pci_enable(dev);
154         if (ret)
155                 return ret;
156         dev->iobase = pci_resource_start(pcidev, 2);
157
158         /*
159          * One or two subdevices are setup by this driver depending on
160          * the number of digital inputs and/or outputs provided by the
161          * board. Each subdevice has a maximum of 32 channels.
162          *
163          *      PCI-7230 - 2 subdevices: 0 - 16 input, 1 - 16 output
164          *      PCI-7233 - 1 subdevice: 0 - 32 input
165          *      PCI-7234 - 1 subdevice: 0 - 32 output
166          *      PCI-7432 - 2 subdevices: 0 - 32 input, 1 - 32 output
167          *      PCI-7433 - 2 subdevices: 0 - 32 input, 1 - 32 input
168          *      PCI-7434 - 2 subdevices: 0 - 32 output, 1 - 32 output
169          */
170         ret = comedi_alloc_subdevices(dev, board->nsubdevs);
171         if (ret)
172                 return ret;
173
174         subdev = 0;
175
176         if (board->di_nchan) {
177                 nchan = min(board->di_nchan, 32);
178
179                 s = &dev->subdevices[subdev];
180                 /* Isolated digital inputs 0 to 15/31 */
181                 s->type         = COMEDI_SUBD_DI;
182                 s->subdev_flags = SDF_READABLE;
183                 s->n_chan       = nchan;
184                 s->maxdata      = 1;
185                 s->insn_bits    = adl_pci7x3x_di_insn_bits;
186                 s->range_table  = &range_digital;
187
188                 s->private      = (void *)PCI7X3X_DIO_REG;
189
190                 subdev++;
191
192                 nchan = board->di_nchan - nchan;
193                 if (nchan) {
194                         s = &dev->subdevices[subdev];
195                         /* Isolated digital inputs 32 to 63 */
196                         s->type         = COMEDI_SUBD_DI;
197                         s->subdev_flags = SDF_READABLE;
198                         s->n_chan       = nchan;
199                         s->maxdata      = 1;
200                         s->insn_bits    = adl_pci7x3x_di_insn_bits;
201                         s->range_table  = &range_digital;
202
203                         s->private      = (void *)PCI743X_DIO_REG;
204
205                         subdev++;
206                 }
207         }
208
209         if (board->do_nchan) {
210                 nchan = min(board->do_nchan, 32);
211
212                 s = &dev->subdevices[subdev];
213                 /* Isolated digital outputs 0 to 15/31 */
214                 s->type         = COMEDI_SUBD_DO;
215                 s->subdev_flags = SDF_WRITABLE;
216                 s->n_chan       = nchan;
217                 s->maxdata      = 1;
218                 s->insn_bits    = adl_pci7x3x_do_insn_bits;
219                 s->range_table  = &range_digital;
220
221                 s->private      = (void *)PCI7X3X_DIO_REG;
222
223                 subdev++;
224
225                 nchan = board->do_nchan - nchan;
226                 if (nchan) {
227                         s = &dev->subdevices[subdev];
228                         /* Isolated digital outputs 32 to 63 */
229                         s->type         = COMEDI_SUBD_DO;
230                         s->subdev_flags = SDF_WRITABLE;
231                         s->n_chan       = nchan;
232                         s->maxdata      = 1;
233                         s->insn_bits    = adl_pci7x3x_do_insn_bits;
234                         s->range_table  = &range_digital;
235
236                         s->private      = (void *)PCI743X_DIO_REG;
237
238                         subdev++;
239                 }
240         }
241
242         return 0;
243 }
244
245 static struct comedi_driver adl_pci7x3x_driver = {
246         .driver_name    = "adl_pci7x3x",
247         .module         = THIS_MODULE,
248         .auto_attach    = adl_pci7x3x_auto_attach,
249         .detach         = comedi_pci_disable,
250 };
251
252 static int adl_pci7x3x_pci_probe(struct pci_dev *dev,
253                                  const struct pci_device_id *id)
254 {
255         return comedi_pci_auto_config(dev, &adl_pci7x3x_driver,
256                                       id->driver_data);
257 }
258
259 static const struct pci_device_id adl_pci7x3x_pci_table[] = {
260         { PCI_VDEVICE(ADLINK, 0x7230), BOARD_PCI7230 },
261         { PCI_VDEVICE(ADLINK, 0x7233), BOARD_PCI7233 },
262         { PCI_VDEVICE(ADLINK, 0x7234), BOARD_PCI7234 },
263         { PCI_VDEVICE(ADLINK, 0x7432), BOARD_PCI7432 },
264         { PCI_VDEVICE(ADLINK, 0x7433), BOARD_PCI7433 },
265         { PCI_VDEVICE(ADLINK, 0x7434), BOARD_PCI7434 },
266         { 0 }
267 };
268 MODULE_DEVICE_TABLE(pci, adl_pci7x3x_pci_table);
269
270 static struct pci_driver adl_pci7x3x_pci_driver = {
271         .name           = "adl_pci7x3x",
272         .id_table       = adl_pci7x3x_pci_table,
273         .probe          = adl_pci7x3x_pci_probe,
274         .remove         = comedi_pci_auto_unconfig,
275 };
276 module_comedi_pci_driver(adl_pci7x3x_driver, adl_pci7x3x_pci_driver);
277
278 MODULE_DESCRIPTION("ADLINK PCI-723x/743x Isolated Digital I/O boards");
279 MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
280 MODULE_LICENSE("GPL");