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