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