GNU Linux-libre 4.14.266-gnu1
[releases.git] / drivers / staging / comedi / drivers / ni_670x.c
1 /*
2  * Comedi driver for NI 670x devices
3  *
4  * COMEDI - Linux Control and Measurement Device Interface
5  * Copyright (C) 1997-2001 David A. Schleef <ds@schleef.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  */
17
18 /*
19  * Driver: ni_670x
20  * Description: National Instruments 670x
21  * Author: Bart Joris <bjoris@advalvas.be>
22  * Updated: Wed, 11 Dec 2002 18:25:35 -0800
23  * Devices: [National Instruments] PCI-6703 (ni_670x), PCI-6704
24  * Status: unknown
25  *
26  * Commands are not supported.
27  *
28  * Manuals:
29  *   322110a.pdf        PCI/PXI-6704 User Manual
30  *   322110b.pdf        PCI/PXI-6703/6704 User Manual
31  */
32
33 #include <linux/module.h>
34 #include <linux/interrupt.h>
35 #include <linux/slab.h>
36
37 #include "../comedi_pci.h"
38
39 #define AO_VALUE_OFFSET                 0x00
40 #define AO_CHAN_OFFSET                  0x0c
41 #define AO_STATUS_OFFSET                0x10
42 #define AO_CONTROL_OFFSET               0x10
43 #define DIO_PORT0_DIR_OFFSET    0x20
44 #define DIO_PORT0_DATA_OFFSET   0x24
45 #define DIO_PORT1_DIR_OFFSET    0x28
46 #define DIO_PORT1_DATA_OFFSET   0x2c
47 #define MISC_STATUS_OFFSET              0x14
48 #define MISC_CONTROL_OFFSET             0x14
49
50 enum ni_670x_boardid {
51         BOARD_PCI6703,
52         BOARD_PXI6704,
53         BOARD_PCI6704,
54 };
55
56 struct ni_670x_board {
57         const char *name;
58         unsigned short ao_chans;
59 };
60
61 static const struct ni_670x_board ni_670x_boards[] = {
62         [BOARD_PCI6703] = {
63                 .name           = "PCI-6703",
64                 .ao_chans       = 16,
65         },
66         [BOARD_PXI6704] = {
67                 .name           = "PXI-6704",
68                 .ao_chans       = 32,
69         },
70         [BOARD_PCI6704] = {
71                 .name           = "PCI-6704",
72                 .ao_chans       = 32,
73         },
74 };
75
76 struct ni_670x_private {
77         int boardtype;
78         int dio;
79 };
80
81 static int ni_670x_ao_insn_write(struct comedi_device *dev,
82                                  struct comedi_subdevice *s,
83                                  struct comedi_insn *insn,
84                                  unsigned int *data)
85 {
86         unsigned int chan = CR_CHAN(insn->chanspec);
87         unsigned int val = s->readback[chan];
88         int i;
89
90         /*
91          * Channel number mapping:
92          *
93          * NI 6703/ NI 6704 | NI 6704 Only
94          * -------------------------------
95          * vch(0)  :  0     | ich(16) :  1
96          * vch(1)  :  2     | ich(17) :  3
97          * ...              | ...
98          * vch(15) : 30     | ich(31) : 31
99          */
100         for (i = 0; i < insn->n; i++) {
101                 val = data[i];
102                 /* First write in channel register which channel to use */
103                 writel(((chan & 15) << 1) | ((chan & 16) >> 4),
104                        dev->mmio + AO_CHAN_OFFSET);
105                 /* write channel value */
106                 writel(val, dev->mmio + AO_VALUE_OFFSET);
107         }
108         s->readback[chan] = val;
109
110         return insn->n;
111 }
112
113 static int ni_670x_dio_insn_bits(struct comedi_device *dev,
114                                  struct comedi_subdevice *s,
115                                  struct comedi_insn *insn,
116                                  unsigned int *data)
117 {
118         if (comedi_dio_update_state(s, data))
119                 writel(s->state, dev->mmio + DIO_PORT0_DATA_OFFSET);
120
121         data[1] = readl(dev->mmio + DIO_PORT0_DATA_OFFSET);
122
123         return insn->n;
124 }
125
126 static int ni_670x_dio_insn_config(struct comedi_device *dev,
127                                    struct comedi_subdevice *s,
128                                    struct comedi_insn *insn,
129                                    unsigned int *data)
130 {
131         int ret;
132
133         ret = comedi_dio_insn_config(dev, s, insn, data, 0);
134         if (ret)
135                 return ret;
136
137         writel(s->io_bits, dev->mmio + DIO_PORT0_DIR_OFFSET);
138
139         return insn->n;
140 }
141
142 /* ripped from mite.h and mite_setup2() to avoid mite dependency */
143 #define MITE_IODWBSR    0xc0     /* IO Device Window Base Size Register */
144 #define WENAB           BIT(7) /* window enable */
145
146 static int ni_670x_mite_init(struct pci_dev *pcidev)
147 {
148         void __iomem *mite_base;
149         u32 main_phys_addr;
150
151         /* ioremap the MITE registers (BAR 0) temporarily */
152         mite_base = pci_ioremap_bar(pcidev, 0);
153         if (!mite_base)
154                 return -ENOMEM;
155
156         /* set data window to main registers (BAR 1) */
157         main_phys_addr = pci_resource_start(pcidev, 1);
158         writel(main_phys_addr | WENAB, mite_base + MITE_IODWBSR);
159
160         /* finished with MITE registers */
161         iounmap(mite_base);
162         return 0;
163 }
164
165 static int ni_670x_auto_attach(struct comedi_device *dev,
166                                unsigned long context)
167 {
168         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
169         const struct ni_670x_board *board = NULL;
170         struct ni_670x_private *devpriv;
171         struct comedi_subdevice *s;
172         int ret;
173         int i;
174
175         if (context < ARRAY_SIZE(ni_670x_boards))
176                 board = &ni_670x_boards[context];
177         if (!board)
178                 return -ENODEV;
179         dev->board_ptr = board;
180         dev->board_name = board->name;
181
182         ret = comedi_pci_enable(dev);
183         if (ret)
184                 return ret;
185
186         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
187         if (!devpriv)
188                 return -ENOMEM;
189
190         ret = ni_670x_mite_init(pcidev);
191         if (ret)
192                 return ret;
193
194         dev->mmio = pci_ioremap_bar(pcidev, 1);
195         if (!dev->mmio)
196                 return -ENOMEM;
197
198         ret = comedi_alloc_subdevices(dev, 2);
199         if (ret)
200                 return ret;
201
202         s = &dev->subdevices[0];
203         /* analog output subdevice */
204         s->type = COMEDI_SUBD_AO;
205         s->subdev_flags = SDF_WRITABLE;
206         s->n_chan = board->ao_chans;
207         s->maxdata = 0xffff;
208         if (s->n_chan == 32) {
209                 const struct comedi_lrange **range_table_list;
210
211                 range_table_list = kmalloc_array(32,
212                                                  sizeof(struct comedi_lrange *),
213                                                  GFP_KERNEL);
214                 if (!range_table_list)
215                         return -ENOMEM;
216                 s->range_table_list = range_table_list;
217                 for (i = 0; i < 16; i++) {
218                         range_table_list[i] = &range_bipolar10;
219                         range_table_list[16 + i] = &range_0_20mA;
220                 }
221         } else {
222                 s->range_table = &range_bipolar10;
223         }
224         s->insn_write = ni_670x_ao_insn_write;
225
226         ret = comedi_alloc_subdev_readback(s);
227         if (ret)
228                 return ret;
229
230         s = &dev->subdevices[1];
231         /* digital i/o subdevice */
232         s->type = COMEDI_SUBD_DIO;
233         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
234         s->n_chan = 8;
235         s->maxdata = 1;
236         s->range_table = &range_digital;
237         s->insn_bits = ni_670x_dio_insn_bits;
238         s->insn_config = ni_670x_dio_insn_config;
239
240         /* Config of misc registers */
241         writel(0x10, dev->mmio + MISC_CONTROL_OFFSET);
242         /* Config of ao registers */
243         writel(0x00, dev->mmio + AO_CONTROL_OFFSET);
244
245         return 0;
246 }
247
248 static void ni_670x_detach(struct comedi_device *dev)
249 {
250         struct comedi_subdevice *s;
251
252         comedi_pci_detach(dev);
253         if (dev->n_subdevices) {
254                 s = &dev->subdevices[0];
255                 if (s)
256                         kfree(s->range_table_list);
257         }
258 }
259
260 static struct comedi_driver ni_670x_driver = {
261         .driver_name    = "ni_670x",
262         .module         = THIS_MODULE,
263         .auto_attach    = ni_670x_auto_attach,
264         .detach         = ni_670x_detach,
265 };
266
267 static int ni_670x_pci_probe(struct pci_dev *dev,
268                              const struct pci_device_id *id)
269 {
270         return comedi_pci_auto_config(dev, &ni_670x_driver, id->driver_data);
271 }
272
273 static const struct pci_device_id ni_670x_pci_table[] = {
274         { PCI_VDEVICE(NI, 0x1290), BOARD_PCI6704 },
275         { PCI_VDEVICE(NI, 0x1920), BOARD_PXI6704 },
276         { PCI_VDEVICE(NI, 0x2c90), BOARD_PCI6703 },
277         { 0 }
278 };
279 MODULE_DEVICE_TABLE(pci, ni_670x_pci_table);
280
281 static struct pci_driver ni_670x_pci_driver = {
282         .name           = "ni_670x",
283         .id_table       = ni_670x_pci_table,
284         .probe          = ni_670x_pci_probe,
285         .remove         = comedi_pci_auto_unconfig,
286 };
287 module_comedi_pci_driver(ni_670x_driver, ni_670x_pci_driver);
288
289 MODULE_AUTHOR("Comedi http://www.comedi.org");
290 MODULE_DESCRIPTION("Comedi low-level driver");
291 MODULE_LICENSE("GPL");