Commit | Line | Data |
---|---|---|
3c5e1722 | 1 | /* |
15203e5a HS |
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 | */ | |
3c5e1722 BJ |
17 | |
18 | /* | |
15203e5a HS |
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 | */ | |
3c5e1722 | 32 | |
ce157f80 | 33 | #include <linux/module.h> |
25436dc9 | 34 | #include <linux/interrupt.h> |
bda230cf | 35 | #include <linux/slab.h> |
33782dd5 | 36 | |
29321b55 | 37 | #include "../comedi_pci.h" |
3c5e1722 | 38 | |
3c5e1722 BJ |
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 | ||
3bac78ca HS |
50 | enum ni_670x_boardid { |
51 | BOARD_PCI6703, | |
52 | BOARD_PXI6704, | |
53 | BOARD_PCI6704, | |
54 | }; | |
3c5e1722 | 55 | |
43313b07 | 56 | struct ni_670x_board { |
3c5e1722 BJ |
57 | const char *name; |
58 | unsigned short ao_chans; | |
43313b07 BP |
59 | }; |
60 | ||
61 | static const struct ni_670x_board ni_670x_boards[] = { | |
3bac78ca | 62 | [BOARD_PCI6703] = { |
04b136b6 | 63 | .name = "PCI-6703", |
04b136b6 | 64 | .ao_chans = 16, |
3bac78ca HS |
65 | }, |
66 | [BOARD_PXI6704] = { | |
04b136b6 | 67 | .name = "PXI-6704", |
04b136b6 | 68 | .ao_chans = 32, |
3bac78ca HS |
69 | }, |
70 | [BOARD_PCI6704] = { | |
04b136b6 | 71 | .name = "PCI-6704", |
04b136b6 | 72 | .ao_chans = 32, |
04b136b6 | 73 | }, |
3c5e1722 BJ |
74 | }; |
75 | ||
8ce8a1ff | 76 | struct ni_670x_private { |
3c5e1722 BJ |
77 | int boardtype; |
78 | int dio; | |
8ce8a1ff BP |
79 | }; |
80 | ||
d34b3d41 HS |
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) | |
3c5e1722 | 85 | { |
d34b3d41 | 86 | unsigned int chan = CR_CHAN(insn->chanspec); |
1d0750ce | 87 | unsigned int val = s->readback[chan]; |
3c5e1722 | 88 | int i; |
3c5e1722 | 89 | |
d34b3d41 HS |
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 | */ | |
3c5e1722 | 100 | for (i = 0; i < insn->n; i++) { |
d34b3d41 | 101 | val = data[i]; |
c733110a BA |
102 | /* First write in channel register which channel to use */ |
103 | writel(((chan & 15) << 1) | ((chan & 16) >> 4), | |
176db588 | 104 | dev->mmio + AO_CHAN_OFFSET); |
c733110a | 105 | /* write channel value */ |
d34b3d41 | 106 | writel(val, dev->mmio + AO_VALUE_OFFSET); |
3c5e1722 | 107 | } |
1d0750ce | 108 | s->readback[chan] = val; |
3c5e1722 | 109 | |
d34b3d41 | 110 | return insn->n; |
3c5e1722 BJ |
111 | } |
112 | ||
0a85b6f0 MT |
113 | static int ni_670x_dio_insn_bits(struct comedi_device *dev, |
114 | struct comedi_subdevice *s, | |
97f4289a HS |
115 | struct comedi_insn *insn, |
116 | unsigned int *data) | |
3c5e1722 | 117 | { |
97f4289a | 118 | if (comedi_dio_update_state(s, data)) |
176db588 | 119 | writel(s->state, dev->mmio + DIO_PORT0_DATA_OFFSET); |
3c5e1722 | 120 | |
176db588 | 121 | data[1] = readl(dev->mmio + DIO_PORT0_DATA_OFFSET); |
3c5e1722 | 122 | |
a2714e3e | 123 | return insn->n; |
3c5e1722 BJ |
124 | } |
125 | ||
0a85b6f0 MT |
126 | static int ni_670x_dio_insn_config(struct comedi_device *dev, |
127 | struct comedi_subdevice *s, | |
ddf62f2c HS |
128 | struct comedi_insn *insn, |
129 | unsigned int *data) | |
3c5e1722 | 130 | { |
ddf62f2c HS |
131 | int ret; |
132 | ||
133 | ret = comedi_dio_insn_config(dev, s, insn, data, 0); | |
134 | if (ret) | |
135 | return ret; | |
3c5e1722 | 136 | |
176db588 | 137 | writel(s->io_bits, dev->mmio + DIO_PORT0_DIR_OFFSET); |
3c5e1722 BJ |
138 | |
139 | return insn->n; | |
140 | } | |
141 | ||
9949595c | 142 | /* ripped from mite.h and mite_setup2() to avoid mite dependency */ |
39798450 HS |
143 | #define MITE_IODWBSR 0xc0 /* IO Device Window Base Size Register */ |
144 | #define WENAB (1 << 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 | ||
a690b7e5 | 165 | static int ni_670x_auto_attach(struct comedi_device *dev, |
3bac78ca | 166 | unsigned long context) |
944a2f11 | 167 | { |
750af5e5 | 168 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
6e2b126d | 169 | const struct ni_670x_board *board = NULL; |
289a4033 | 170 | struct ni_670x_private *devpriv; |
944a2f11 HS |
171 | struct comedi_subdevice *s; |
172 | int ret; | |
173 | int i; | |
174 | ||
3bac78ca | 175 | if (context < ARRAY_SIZE(ni_670x_boards)) |
6e2b126d HS |
176 | board = &ni_670x_boards[context]; |
177 | if (!board) | |
3bac78ca | 178 | return -ENODEV; |
6e2b126d HS |
179 | dev->board_ptr = board; |
180 | dev->board_name = board->name; | |
3bac78ca | 181 | |
818f569f HS |
182 | ret = comedi_pci_enable(dev); |
183 | if (ret) | |
184 | return ret; | |
818f569f | 185 | |
0bdab509 | 186 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
c34fa261 HS |
187 | if (!devpriv) |
188 | return -ENOMEM; | |
9a1a6cf8 | 189 | |
39798450 HS |
190 | ret = ni_670x_mite_init(pcidev); |
191 | if (ret) | |
944a2f11 | 192 | return ret; |
39798450 | 193 | |
176db588 HS |
194 | dev->mmio = pci_ioremap_bar(pcidev, 1); |
195 | if (!dev->mmio) | |
39798450 | 196 | return -ENOMEM; |
944a2f11 HS |
197 | |
198 | ret = comedi_alloc_subdevices(dev, 2); | |
199 | if (ret) | |
200 | return ret; | |
201 | ||
fc041c20 | 202 | s = &dev->subdevices[0]; |
944a2f11 HS |
203 | /* analog output subdevice */ |
204 | s->type = COMEDI_SUBD_AO; | |
205 | s->subdev_flags = SDF_WRITABLE; | |
6e2b126d | 206 | s->n_chan = board->ao_chans; |
944a2f11 HS |
207 | s->maxdata = 0xffff; |
208 | if (s->n_chan == 32) { | |
209 | const struct comedi_lrange **range_table_list; | |
210 | ||
04226e40 GT |
211 | range_table_list = kmalloc_array(32, |
212 | sizeof(struct comedi_lrange *), | |
213 | GFP_KERNEL); | |
944a2f11 HS |
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 | } | |
d34b3d41 | 224 | s->insn_write = ni_670x_ao_insn_write; |
1d0750ce HS |
225 | |
226 | ret = comedi_alloc_subdev_readback(s); | |
227 | if (ret) | |
228 | return ret; | |
944a2f11 | 229 | |
fc041c20 | 230 | s = &dev->subdevices[1]; |
944a2f11 HS |
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 */ | |
176db588 | 241 | writel(0x10, dev->mmio + MISC_CONTROL_OFFSET); |
944a2f11 | 242 | /* Config of ao registers */ |
176db588 | 243 | writel(0x00, dev->mmio + AO_CONTROL_OFFSET); |
944a2f11 | 244 | |
464c9451 | 245 | return 0; |
944a2f11 HS |
246 | } |
247 | ||
248 | static void ni_670x_detach(struct comedi_device *dev) | |
249 | { | |
70fcd1b7 | 250 | struct comedi_subdevice *s; |
289a4033 | 251 | |
aac307f9 | 252 | comedi_pci_detach(dev); |
70fcd1b7 | 253 | if (dev->n_subdevices) { |
fc041c20 | 254 | s = &dev->subdevices[0]; |
70fcd1b7 HS |
255 | if (s) |
256 | kfree(s->range_table_list); | |
257 | } | |
944a2f11 HS |
258 | } |
259 | ||
6f37e288 HS |
260 | static struct comedi_driver ni_670x_driver = { |
261 | .driver_name = "ni_670x", | |
262 | .module = THIS_MODULE, | |
750af5e5 | 263 | .auto_attach = ni_670x_auto_attach, |
6f37e288 HS |
264 | .detach = ni_670x_detach, |
265 | }; | |
266 | ||
a690b7e5 | 267 | static int ni_670x_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 268 | const struct pci_device_id *id) |
6f37e288 | 269 | { |
b8f4ac23 | 270 | return comedi_pci_auto_config(dev, &ni_670x_driver, id->driver_data); |
6f37e288 HS |
271 | } |
272 | ||
41e043fc | 273 | static const struct pci_device_id ni_670x_pci_table[] = { |
3bac78ca HS |
274 | { PCI_VDEVICE(NI, 0x1290), BOARD_PCI6704 }, |
275 | { PCI_VDEVICE(NI, 0x1920), BOARD_PXI6704 }, | |
276 | { PCI_VDEVICE(NI, 0x2c90), BOARD_PCI6703 }, | |
6f37e288 HS |
277 | { 0 } |
278 | }; | |
279 | MODULE_DEVICE_TABLE(pci, ni_670x_pci_table); | |
280 | ||
281 | static struct pci_driver ni_670x_pci_driver = { | |
9e4edd57 | 282 | .name = "ni_670x", |
6f37e288 HS |
283 | .id_table = ni_670x_pci_table, |
284 | .probe = ni_670x_pci_probe, | |
9901a4d7 | 285 | .remove = comedi_pci_auto_unconfig, |
6f37e288 HS |
286 | }; |
287 | module_comedi_pci_driver(ni_670x_driver, ni_670x_pci_driver); | |
288 | ||
3c323c01 IA |
289 | MODULE_AUTHOR("Comedi http://www.comedi.org"); |
290 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
291 | MODULE_LICENSE("GPL"); |