]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - drivers/staging/comedi/drivers/ni_670x.c
staging: comedi_pci: make comedi_pci_disable() safe to call
[mirror_ubuntu-hirsute-kernel.git] / drivers / staging / comedi / drivers / ni_670x.c
CommitLineData
3c5e1722
BJ
1/*
2 comedi/drivers/ni_670x.c
3 Hardware driver for NI 670x devices
4
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 1997-2001 David A. Schleef <ds@schleef.org>
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22*/
23/*
24Driver: ni_670x
25Description: National Instruments 670x
26Author: Bart Joris <bjoris@advalvas.be>
27Updated: Wed, 11 Dec 2002 18:25:35 -0800
28Devices: [National Instruments] PCI-6703 (ni_670x), PCI-6704
29Status: unknown
30
31Commands are not supported.
32*/
33
34/*
35 Bart Joris <bjoris@advalvas.be> Last updated on 20/08/2001
36
37 Manuals:
38
39 322110a.pdf PCI/PXI-6704 User Manual
40 322110b.pdf PCI/PXI-6703/6704 User Manual
41
42*/
43
33782dd5 44#include <linux/pci.h>
25436dc9 45#include <linux/interrupt.h>
5a0e3ad6 46#include <linux/slab.h>
33782dd5 47
3c5e1722
BJ
48#include "../comedidev.h"
49
50#include "mite.h"
51
3c5e1722
BJ
52#define AO_VALUE_OFFSET 0x00
53#define AO_CHAN_OFFSET 0x0c
54#define AO_STATUS_OFFSET 0x10
55#define AO_CONTROL_OFFSET 0x10
56#define DIO_PORT0_DIR_OFFSET 0x20
57#define DIO_PORT0_DATA_OFFSET 0x24
58#define DIO_PORT1_DIR_OFFSET 0x28
59#define DIO_PORT1_DATA_OFFSET 0x2c
60#define MISC_STATUS_OFFSET 0x14
61#define MISC_CONTROL_OFFSET 0x14
62
3bac78ca
HS
63enum ni_670x_boardid {
64 BOARD_PCI6703,
65 BOARD_PXI6704,
66 BOARD_PCI6704,
67};
3c5e1722 68
43313b07 69struct ni_670x_board {
3c5e1722
BJ
70 const char *name;
71 unsigned short ao_chans;
43313b07
BP
72};
73
74static const struct ni_670x_board ni_670x_boards[] = {
3bac78ca 75 [BOARD_PCI6703] = {
04b136b6 76 .name = "PCI-6703",
04b136b6 77 .ao_chans = 16,
3bac78ca
HS
78 },
79 [BOARD_PXI6704] = {
04b136b6 80 .name = "PXI-6704",
04b136b6 81 .ao_chans = 32,
3bac78ca
HS
82 },
83 [BOARD_PCI6704] = {
04b136b6 84 .name = "PCI-6704",
04b136b6 85 .ao_chans = 32,
04b136b6 86 },
3c5e1722
BJ
87};
88
8ce8a1ff
BP
89struct ni_670x_private {
90
3c5e1722
BJ
91 struct mite_struct *mite;
92 int boardtype;
93 int dio;
790c5541 94 unsigned int ao_readback[32];
8ce8a1ff
BP
95};
96
9ced1de6 97static struct comedi_lrange range_0_20mA = { 1, {RANGE_mA(0, 20)} };
3c5e1722 98
0a85b6f0
MT
99static int ni_670x_ao_winsn(struct comedi_device *dev,
100 struct comedi_subdevice *s,
101 struct comedi_insn *insn, unsigned int *data)
3c5e1722 102{
289a4033 103 struct ni_670x_private *devpriv = dev->private;
3c5e1722
BJ
104 int i;
105 int chan = CR_CHAN(insn->chanspec);
106
107 /* Channel number mapping :
108
109 NI 6703/ NI 6704 | NI 6704 Only
110 ----------------------------------------------------
111 vch(0) : 0 | ich(16) : 1
112 vch(1) : 2 | ich(17) : 3
113 . : . | . .
114 . : . | . .
115 . : . | . .
116 vch(15) : 30 | ich(31) : 31 */
117
118 for (i = 0; i < insn->n; i++) {
c733110a
BA
119 /* First write in channel register which channel to use */
120 writel(((chan & 15) << 1) | ((chan & 16) >> 4),
121 devpriv->mite->daq_io_addr + AO_CHAN_OFFSET);
122 /* write channel value */
123 writel(data[i], devpriv->mite->daq_io_addr + AO_VALUE_OFFSET);
3c5e1722
BJ
124 devpriv->ao_readback[chan] = data[i];
125 }
126
127 return i;
128}
129
0a85b6f0
MT
130static int ni_670x_ao_rinsn(struct comedi_device *dev,
131 struct comedi_subdevice *s,
132 struct comedi_insn *insn, unsigned int *data)
3c5e1722 133{
289a4033 134 struct ni_670x_private *devpriv = dev->private;
3c5e1722
BJ
135 int i;
136 int chan = CR_CHAN(insn->chanspec);
137
138 for (i = 0; i < insn->n; i++)
139 data[i] = devpriv->ao_readback[chan];
140
141 return i;
142}
143
0a85b6f0
MT
144static int ni_670x_dio_insn_bits(struct comedi_device *dev,
145 struct comedi_subdevice *s,
146 struct comedi_insn *insn, unsigned int *data)
3c5e1722 147{
289a4033 148 struct ni_670x_private *devpriv = dev->private;
ebbc0979
HS
149 void __iomem *io_addr = devpriv->mite->daq_io_addr +
150 DIO_PORT0_DATA_OFFSET;
151 unsigned int mask = data[0];
152 unsigned int bits = data[1];
289a4033 153
ebbc0979
HS
154 if (mask) {
155 s->state &= ~mask;
156 s->state |= (bits & mask);
157
158 writel(s->state, io_addr);
3c5e1722
BJ
159 }
160
ebbc0979 161 data[1] = readl(io_addr);
3c5e1722 162
a2714e3e 163 return insn->n;
3c5e1722
BJ
164}
165
0a85b6f0
MT
166static int ni_670x_dio_insn_config(struct comedi_device *dev,
167 struct comedi_subdevice *s,
168 struct comedi_insn *insn, unsigned int *data)
3c5e1722 169{
289a4033 170 struct ni_670x_private *devpriv = dev->private;
3c5e1722
BJ
171 int chan = CR_CHAN(insn->chanspec);
172
173 switch (data[0]) {
174 case INSN_CONFIG_DIO_OUTPUT:
175 s->io_bits |= 1 << chan;
176 break;
177 case INSN_CONFIG_DIO_INPUT:
178 s->io_bits &= ~(1 << chan);
179 break;
180 case INSN_CONFIG_DIO_QUERY:
181 data[1] =
0a85b6f0 182 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
3c5e1722
BJ
183 return insn->n;
184 break;
185 default:
186 return -EINVAL;
187 break;
188 }
189 writel(s->io_bits, devpriv->mite->daq_io_addr + DIO_PORT0_DIR_OFFSET);
190
191 return insn->n;
192}
193
a690b7e5 194static int ni_670x_auto_attach(struct comedi_device *dev,
3bac78ca 195 unsigned long context)
944a2f11 196{
750af5e5 197 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
3bac78ca 198 const struct ni_670x_board *thisboard = NULL;
289a4033 199 struct ni_670x_private *devpriv;
944a2f11
HS
200 struct comedi_subdevice *s;
201 int ret;
202 int i;
203
3bac78ca
HS
204 if (context < ARRAY_SIZE(ni_670x_boards))
205 thisboard = &ni_670x_boards[context];
206 if (!thisboard)
207 return -ENODEV;
208 dev->board_ptr = thisboard;
209 dev->board_name = thisboard->name;
210
c34fa261
HS
211 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
212 if (!devpriv)
213 return -ENOMEM;
214 dev->private = devpriv;
9a1a6cf8 215
6bc42c22 216 devpriv->mite = mite_alloc(pcidev);
c33e23f7 217 if (!devpriv->mite)
6bc42c22 218 return -ENOMEM;
944a2f11
HS
219
220 ret = mite_setup(devpriv->mite);
221 if (ret < 0) {
b48ad330 222 dev_warn(dev->class_dev, "error setting up mite\n");
944a2f11
HS
223 return ret;
224 }
7f072f54 225 dev->iobase = 1;
944a2f11
HS
226
227 ret = comedi_alloc_subdevices(dev, 2);
228 if (ret)
229 return ret;
230
fc041c20 231 s = &dev->subdevices[0];
944a2f11
HS
232 /* analog output subdevice */
233 s->type = COMEDI_SUBD_AO;
234 s->subdev_flags = SDF_WRITABLE;
235 s->n_chan = thisboard->ao_chans;
236 s->maxdata = 0xffff;
237 if (s->n_chan == 32) {
238 const struct comedi_lrange **range_table_list;
239
240 range_table_list = kmalloc(sizeof(struct comedi_lrange *) * 32,
241 GFP_KERNEL);
242 if (!range_table_list)
243 return -ENOMEM;
244 s->range_table_list = range_table_list;
245 for (i = 0; i < 16; i++) {
246 range_table_list[i] = &range_bipolar10;
247 range_table_list[16 + i] = &range_0_20mA;
248 }
249 } else {
250 s->range_table = &range_bipolar10;
251 }
252 s->insn_write = &ni_670x_ao_winsn;
253 s->insn_read = &ni_670x_ao_rinsn;
254
fc041c20 255 s = &dev->subdevices[1];
944a2f11
HS
256 /* digital i/o subdevice */
257 s->type = COMEDI_SUBD_DIO;
258 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
259 s->n_chan = 8;
260 s->maxdata = 1;
261 s->range_table = &range_digital;
262 s->insn_bits = ni_670x_dio_insn_bits;
263 s->insn_config = ni_670x_dio_insn_config;
264
265 /* Config of misc registers */
266 writel(0x10, devpriv->mite->daq_io_addr + MISC_CONTROL_OFFSET);
267 /* Config of ao registers */
268 writel(0x00, devpriv->mite->daq_io_addr + AO_CONTROL_OFFSET);
269
b48ad330
HS
270 dev_info(dev->class_dev, "%s: %s attached\n",
271 dev->driver->driver_name, dev->board_name);
944a2f11 272
464c9451 273 return 0;
944a2f11
HS
274}
275
276static void ni_670x_detach(struct comedi_device *dev)
277{
289a4033 278 struct ni_670x_private *devpriv = dev->private;
70fcd1b7 279 struct comedi_subdevice *s;
289a4033 280
70fcd1b7 281 if (dev->n_subdevices) {
fc041c20 282 s = &dev->subdevices[0];
70fcd1b7
HS
283 if (s)
284 kfree(s->range_table_list);
285 }
6bc42c22 286 if (devpriv && devpriv->mite) {
944a2f11 287 mite_unsetup(devpriv->mite);
6bc42c22
IA
288 mite_free(devpriv->mite);
289 }
7f072f54 290 comedi_pci_disable(dev);
944a2f11
HS
291}
292
6f37e288
HS
293static struct comedi_driver ni_670x_driver = {
294 .driver_name = "ni_670x",
295 .module = THIS_MODULE,
750af5e5 296 .auto_attach = ni_670x_auto_attach,
6f37e288
HS
297 .detach = ni_670x_detach,
298};
299
a690b7e5 300static int ni_670x_pci_probe(struct pci_dev *dev,
b8f4ac23 301 const struct pci_device_id *id)
6f37e288 302{
b8f4ac23 303 return comedi_pci_auto_config(dev, &ni_670x_driver, id->driver_data);
6f37e288
HS
304}
305
6f37e288 306static DEFINE_PCI_DEVICE_TABLE(ni_670x_pci_table) = {
3bac78ca
HS
307 { PCI_VDEVICE(NI, 0x1290), BOARD_PCI6704 },
308 { PCI_VDEVICE(NI, 0x1920), BOARD_PXI6704 },
309 { PCI_VDEVICE(NI, 0x2c90), BOARD_PCI6703 },
6f37e288
HS
310 { 0 }
311};
312MODULE_DEVICE_TABLE(pci, ni_670x_pci_table);
313
314static struct pci_driver ni_670x_pci_driver = {
9e4edd57 315 .name = "ni_670x",
6f37e288
HS
316 .id_table = ni_670x_pci_table,
317 .probe = ni_670x_pci_probe,
9901a4d7 318 .remove = comedi_pci_auto_unconfig,
6f37e288
HS
319};
320module_comedi_pci_driver(ni_670x_driver, ni_670x_pci_driver);
321
3c323c01
IA
322MODULE_AUTHOR("Comedi http://www.comedi.org");
323MODULE_DESCRIPTION("Comedi low-level driver");
324MODULE_LICENSE("GPL");