]> Pileus Git - ~andy/linux/blob - drivers/staging/comedi/drivers/adl_pci7x3x.c
staging: comedi: conditionally build in PCI driver support
[~andy/linux] / 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  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26  */
27
28 /*
29 Driver: adl_pci7x3x
30 Description: 32/64-Channel Isolated Digital I/O Boards
31 Devices: (ADLink) PCI-7230 [adl_pci7230] - 16 input / 16 output
32          (ADLink) PCI-7233 [adl_pci7233] - 32 input
33          (ADLink) PCI-7234 [adl_pci7234] - 32 output
34          (ADLink) PCI-7432 [adl_pci7432] - 32 input / 32 output
35          (ADLink) PCI-7433 [adl_pci7433] - 64 input
36          (ADLink) PCI-7434 [adl_pci7434] - 64 output
37 Author: H Hartley Sweeten <hsweeten@visionengravers.com>
38 Updated: Thu, 02 Aug 2012 14:27:46 -0700
39 Status: untested
40
41 This driver only attaches using the PCI PnP auto config support
42 in the comedi core. The module parameter 'comedi_autoconfig'
43 must be 1 (default) to enable this feature. The COMEDI_DEVCONFIG
44 ioctl, used by the comedi_config utility, is not supported by
45 this driver.
46
47 The PCI-7230, PCI-7432 and PCI-7433 boards also support external
48 interrupt signals on digital input channels 0 and 1. The PCI-7233
49 has dual-interrupt sources for change-of-state (COS) on any 16
50 digital input channels of LSB and for COS on any 16 digital input
51 lines of MSB. Interrupts are not currently supported by this
52 driver.
53
54 Configuration Options: not applicable
55 */
56
57 #include <linux/pci.h>
58
59 #include "../comedidev.h"
60
61 /*
62  * PCI Device ID's supported by this driver
63  */
64 #define PCI_DEVICE_ID_PCI7230   0x7230
65 #define PCI_DEVICE_ID_PCI7233   0x7233
66 #define PCI_DEVICE_ID_PCI7234   0x7234
67 #define PCI_DEVICE_ID_PCI7432   0x7432
68 #define PCI_DEVICE_ID_PCI7433   0x7433
69 #define PCI_DEVICE_ID_PCI7434   0x7434
70
71 /*
72  * Register I/O map (32-bit access only)
73  */
74 #define PCI7X3X_DIO_REG         0x00
75 #define PCI743X_DIO_REG         0x04
76
77 struct adl_pci7x3x_boardinfo {
78         const char *name;
79         unsigned short device;
80         int nsubdevs;
81         int di_nchan;
82         int do_nchan;
83 };
84
85 static const struct adl_pci7x3x_boardinfo adl_pci7x3x_boards[] = {
86         {
87                 .name           = "adl_pci7230",
88                 .device         = PCI_DEVICE_ID_PCI7230,
89                 .nsubdevs       = 2,
90                 .di_nchan       = 16,
91                 .do_nchan       = 16,
92         }, {
93                 .name           = "adl_pci7233",
94                 .device         = PCI_DEVICE_ID_PCI7233,
95                 .nsubdevs       = 1,
96                 .di_nchan       = 32,
97         }, {
98                 .name           = "adl_pci7234",
99                 .device         = PCI_DEVICE_ID_PCI7234,
100                 .nsubdevs       = 1,
101                 .do_nchan       = 32,
102         }, {
103                 .name           = "adl_pci7432",
104                 .device         = PCI_DEVICE_ID_PCI7432,
105                 .nsubdevs       = 2,
106                 .di_nchan       = 32,
107                 .do_nchan       = 32,
108         }, {
109                 .name           = "adl_pci7433",
110                 .device         = PCI_DEVICE_ID_PCI7433,
111                 .nsubdevs       = 2,
112                 .di_nchan       = 64,
113         }, {
114                 .name           = "adl_pci7434",
115                 .device         = PCI_DEVICE_ID_PCI7434,
116                 .nsubdevs       = 2,
117                 .do_nchan       = 64,
118         }
119 };
120
121 static int adl_pci7x3x_do_insn_bits(struct comedi_device *dev,
122                                     struct comedi_subdevice *s,
123                                     struct comedi_insn *insn,
124                                     unsigned int *data)
125 {
126         unsigned long reg = (unsigned long)s->private;
127         unsigned int mask = data[0];
128         unsigned int bits = data[1];
129
130         if (mask) {
131                 s->state &= ~mask;
132                 s->state |= (bits & mask);
133
134                 outl(s->state, dev->iobase + reg);
135         }
136
137         /*
138          * NOTE: The output register is not readable.
139          * This returned state will not be correct until all the
140          * outputs have been updated.
141          */
142         data[1] = s->state;
143
144         return insn->n;
145 }
146
147 static int adl_pci7x3x_di_insn_bits(struct comedi_device *dev,
148                                     struct comedi_subdevice *s,
149                                     struct comedi_insn *insn,
150                                     unsigned int *data)
151 {
152         unsigned long reg = (unsigned long)s->private;
153
154         data[1] = inl(dev->iobase + reg);
155
156         return insn->n;
157 }
158
159 static const void *adl_pci7x3x_find_boardinfo(struct comedi_device *dev,
160                                               struct pci_dev *pcidev)
161 {
162         const struct adl_pci7x3x_boardinfo *board;
163         int i;
164
165         for (i = 0; i < ARRAY_SIZE(adl_pci7x3x_boards); i++) {
166                 board = &adl_pci7x3x_boards[i];
167                 if (pcidev->device == board->device)
168                         return board;
169         }
170         return NULL;
171 }
172
173 static int adl_pci7x3x_auto_attach(struct comedi_device *dev,
174                                              unsigned long context_unused)
175 {
176         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
177         const struct adl_pci7x3x_boardinfo *board;
178         struct comedi_subdevice *s;
179         int subdev;
180         int nchan;
181         int ret;
182
183         board = adl_pci7x3x_find_boardinfo(dev, pcidev);
184         if (!board)
185                 return -ENODEV;
186         dev->board_ptr = board;
187         dev->board_name = board->name;
188
189         ret = comedi_pci_enable(pcidev, dev->board_name);
190         if (ret)
191                 return ret;
192         dev->iobase = pci_resource_start(pcidev, 2);
193
194         /*
195          * One or two subdevices are setup by this driver depending on
196          * the number of digital inputs and/or outputs provided by the
197          * board. Each subdevice has a maximum of 32 channels.
198          *
199          *      PCI-7230 - 2 subdevices: 0 - 16 input, 1 - 16 output
200          *      PCI-7233 - 1 subdevice: 0 - 32 input
201          *      PCI-7234 - 1 subdevice: 0 - 32 output
202          *      PCI-7432 - 2 subdevices: 0 - 32 input, 1 - 32 output
203          *      PCI-7433 - 2 subdevices: 0 - 32 input, 1 - 32 input
204          *      PCI-7434 - 2 subdevices: 0 - 32 output, 1 - 32 output
205          */
206         ret = comedi_alloc_subdevices(dev, board->nsubdevs);
207         if (ret)
208                 return ret;
209
210         subdev = 0;
211
212         if (board->di_nchan) {
213                 nchan = min(board->di_nchan, 32);
214
215                 s = &dev->subdevices[subdev];
216                 /* Isolated digital inputs 0 to 15/31 */
217                 s->type         = COMEDI_SUBD_DI;
218                 s->subdev_flags = SDF_READABLE;
219                 s->n_chan       = nchan;
220                 s->maxdata      = 1;
221                 s->insn_bits    = adl_pci7x3x_di_insn_bits;
222                 s->range_table  = &range_digital;
223
224                 s->private      = (void *)PCI7X3X_DIO_REG;
225
226                 subdev++;
227
228                 nchan = board->di_nchan - nchan;
229                 if (nchan) {
230                         s = &dev->subdevices[subdev];
231                         /* Isolated digital inputs 32 to 63 */
232                         s->type         = COMEDI_SUBD_DI;
233                         s->subdev_flags = SDF_READABLE;
234                         s->n_chan       = nchan;
235                         s->maxdata      = 1;
236                         s->insn_bits    = adl_pci7x3x_di_insn_bits;
237                         s->range_table  = &range_digital;
238
239                         s->private      = (void *)PCI743X_DIO_REG;
240
241                         subdev++;
242                 }
243         }
244
245         if (board->do_nchan) {
246                 nchan = min(board->do_nchan, 32);
247
248                 s = &dev->subdevices[subdev];
249                 /* Isolated digital outputs 0 to 15/31 */
250                 s->type         = COMEDI_SUBD_DO;
251                 s->subdev_flags = SDF_WRITABLE;
252                 s->n_chan       = nchan;
253                 s->maxdata      = 1;
254                 s->insn_bits    = adl_pci7x3x_do_insn_bits;
255                 s->range_table  = &range_digital;
256
257                 s->private      = (void *)PCI7X3X_DIO_REG;
258
259                 subdev++;
260
261                 nchan = board->do_nchan - nchan;
262                 if (nchan) {
263                         s = &dev->subdevices[subdev];
264                         /* Isolated digital outputs 32 to 63 */
265                         s->type         = COMEDI_SUBD_DO;
266                         s->subdev_flags = SDF_WRITABLE;
267                         s->n_chan       = nchan;
268                         s->maxdata      = 1;
269                         s->insn_bits    = adl_pci7x3x_do_insn_bits;
270                         s->range_table  = &range_digital;
271
272                         s->private      = (void *)PCI743X_DIO_REG;
273
274                         subdev++;
275                 }
276         }
277
278         dev_info(dev->class_dev, "%s attached (%d inputs/%d outputs)\n",
279                 dev->board_name, board->di_nchan, board->do_nchan);
280
281         return 0;
282 }
283
284 static void adl_pci7x3x_detach(struct comedi_device *dev)
285 {
286         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
287
288         if (pcidev) {
289                 if (dev->iobase)
290                         comedi_pci_disable(pcidev);
291         }
292 }
293
294 static struct comedi_driver adl_pci7x3x_driver = {
295         .driver_name    = "adl_pci7x3x",
296         .module         = THIS_MODULE,
297         .auto_attach    = adl_pci7x3x_auto_attach,
298         .detach         = adl_pci7x3x_detach,
299 };
300
301 static int adl_pci7x3x_pci_probe(struct pci_dev *dev,
302                                            const struct pci_device_id *ent)
303 {
304         return comedi_pci_auto_config(dev, &adl_pci7x3x_driver);
305 }
306
307 static DEFINE_PCI_DEVICE_TABLE(adl_pci7x3x_pci_table) = {
308         { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7230) },
309         { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7233) },
310         { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7234) },
311         { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7432) },
312         { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7433) },
313         { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7434) },
314         { 0 }
315 };
316 MODULE_DEVICE_TABLE(pci, adl_pci7x3x_pci_table);
317
318 static struct pci_driver adl_pci7x3x_pci_driver = {
319         .name           = "adl_pci7x3x",
320         .id_table       = adl_pci7x3x_pci_table,
321         .probe          = adl_pci7x3x_pci_probe,
322         .remove         = comedi_pci_auto_unconfig,
323 };
324 module_comedi_pci_driver(adl_pci7x3x_driver, adl_pci7x3x_pci_driver);
325
326 MODULE_DESCRIPTION("ADLINK PCI-723x/743x Isolated Digital I/O boards");
327 MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
328 MODULE_LICENSE("GPL");