]>
Commit | Line | Data |
---|---|---|
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. | |
3c5e1722 BJ |
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 | ||
29 | /* | |
30 | Bart Joris <bjoris@advalvas.be> Last updated on 20/08/2001 | |
31 | ||
32 | Manuals: | |
33 | ||
34 | 322110a.pdf PCI/PXI-6704 User Manual | |
35 | 322110b.pdf PCI/PXI-6703/6704 User Manual | |
36 | ||
37 | */ | |
38 | ||
ce157f80 | 39 | #include <linux/module.h> |
33782dd5 | 40 | #include <linux/pci.h> |
25436dc9 | 41 | #include <linux/interrupt.h> |
bda230cf | 42 | #include <linux/slab.h> |
33782dd5 | 43 | |
3c5e1722 BJ |
44 | #include "../comedidev.h" |
45 | ||
3c5e1722 BJ |
46 | #define AO_VALUE_OFFSET 0x00 |
47 | #define AO_CHAN_OFFSET 0x0c | |
48 | #define AO_STATUS_OFFSET 0x10 | |
49 | #define AO_CONTROL_OFFSET 0x10 | |
50 | #define DIO_PORT0_DIR_OFFSET 0x20 | |
51 | #define DIO_PORT0_DATA_OFFSET 0x24 | |
52 | #define DIO_PORT1_DIR_OFFSET 0x28 | |
53 | #define DIO_PORT1_DATA_OFFSET 0x2c | |
54 | #define MISC_STATUS_OFFSET 0x14 | |
55 | #define MISC_CONTROL_OFFSET 0x14 | |
56 | ||
3bac78ca HS |
57 | enum ni_670x_boardid { |
58 | BOARD_PCI6703, | |
59 | BOARD_PXI6704, | |
60 | BOARD_PCI6704, | |
61 | }; | |
3c5e1722 | 62 | |
43313b07 | 63 | struct ni_670x_board { |
3c5e1722 BJ |
64 | const char *name; |
65 | unsigned short ao_chans; | |
43313b07 BP |
66 | }; |
67 | ||
68 | static const struct ni_670x_board ni_670x_boards[] = { | |
3bac78ca | 69 | [BOARD_PCI6703] = { |
04b136b6 | 70 | .name = "PCI-6703", |
04b136b6 | 71 | .ao_chans = 16, |
3bac78ca HS |
72 | }, |
73 | [BOARD_PXI6704] = { | |
04b136b6 | 74 | .name = "PXI-6704", |
04b136b6 | 75 | .ao_chans = 32, |
3bac78ca HS |
76 | }, |
77 | [BOARD_PCI6704] = { | |
04b136b6 | 78 | .name = "PCI-6704", |
04b136b6 | 79 | .ao_chans = 32, |
04b136b6 | 80 | }, |
3c5e1722 BJ |
81 | }; |
82 | ||
8ce8a1ff | 83 | struct ni_670x_private { |
3c5e1722 BJ |
84 | int boardtype; |
85 | int dio; | |
8ce8a1ff BP |
86 | }; |
87 | ||
d34b3d41 HS |
88 | static int ni_670x_ao_insn_write(struct comedi_device *dev, |
89 | struct comedi_subdevice *s, | |
90 | struct comedi_insn *insn, | |
91 | unsigned int *data) | |
3c5e1722 | 92 | { |
d34b3d41 | 93 | unsigned int chan = CR_CHAN(insn->chanspec); |
1d0750ce | 94 | unsigned int val = s->readback[chan]; |
3c5e1722 | 95 | int i; |
3c5e1722 | 96 | |
d34b3d41 HS |
97 | /* |
98 | * Channel number mapping: | |
99 | * | |
100 | * NI 6703/ NI 6704 | NI 6704 Only | |
101 | * ------------------------------- | |
102 | * vch(0) : 0 | ich(16) : 1 | |
103 | * vch(1) : 2 | ich(17) : 3 | |
104 | * ... | ... | |
105 | * vch(15) : 30 | ich(31) : 31 | |
106 | */ | |
3c5e1722 | 107 | for (i = 0; i < insn->n; i++) { |
d34b3d41 | 108 | val = data[i]; |
c733110a BA |
109 | /* First write in channel register which channel to use */ |
110 | writel(((chan & 15) << 1) | ((chan & 16) >> 4), | |
176db588 | 111 | dev->mmio + AO_CHAN_OFFSET); |
c733110a | 112 | /* write channel value */ |
d34b3d41 | 113 | writel(val, dev->mmio + AO_VALUE_OFFSET); |
3c5e1722 | 114 | } |
1d0750ce | 115 | s->readback[chan] = val; |
3c5e1722 | 116 | |
d34b3d41 | 117 | return insn->n; |
3c5e1722 BJ |
118 | } |
119 | ||
0a85b6f0 MT |
120 | static int ni_670x_dio_insn_bits(struct comedi_device *dev, |
121 | struct comedi_subdevice *s, | |
97f4289a HS |
122 | struct comedi_insn *insn, |
123 | unsigned int *data) | |
3c5e1722 | 124 | { |
97f4289a | 125 | if (comedi_dio_update_state(s, data)) |
176db588 | 126 | writel(s->state, dev->mmio + DIO_PORT0_DATA_OFFSET); |
3c5e1722 | 127 | |
176db588 | 128 | data[1] = readl(dev->mmio + DIO_PORT0_DATA_OFFSET); |
3c5e1722 | 129 | |
a2714e3e | 130 | return insn->n; |
3c5e1722 BJ |
131 | } |
132 | ||
0a85b6f0 MT |
133 | static int ni_670x_dio_insn_config(struct comedi_device *dev, |
134 | struct comedi_subdevice *s, | |
ddf62f2c HS |
135 | struct comedi_insn *insn, |
136 | unsigned int *data) | |
3c5e1722 | 137 | { |
ddf62f2c HS |
138 | int ret; |
139 | ||
140 | ret = comedi_dio_insn_config(dev, s, insn, data, 0); | |
141 | if (ret) | |
142 | return ret; | |
3c5e1722 | 143 | |
176db588 | 144 | writel(s->io_bits, dev->mmio + DIO_PORT0_DIR_OFFSET); |
3c5e1722 BJ |
145 | |
146 | return insn->n; | |
147 | } | |
148 | ||
39798450 HS |
149 | /* ripped from mite.h and mite_setup2() to avoid mite dependancy */ |
150 | #define MITE_IODWBSR 0xc0 /* IO Device Window Base Size Register */ | |
151 | #define WENAB (1 << 7) /* window enable */ | |
152 | ||
153 | static int ni_670x_mite_init(struct pci_dev *pcidev) | |
154 | { | |
155 | void __iomem *mite_base; | |
156 | u32 main_phys_addr; | |
157 | ||
158 | /* ioremap the MITE registers (BAR 0) temporarily */ | |
159 | mite_base = pci_ioremap_bar(pcidev, 0); | |
160 | if (!mite_base) | |
161 | return -ENOMEM; | |
162 | ||
163 | /* set data window to main registers (BAR 1) */ | |
164 | main_phys_addr = pci_resource_start(pcidev, 1); | |
165 | writel(main_phys_addr | WENAB, mite_base + MITE_IODWBSR); | |
166 | ||
167 | /* finished with MITE registers */ | |
168 | iounmap(mite_base); | |
169 | return 0; | |
170 | } | |
171 | ||
a690b7e5 | 172 | static int ni_670x_auto_attach(struct comedi_device *dev, |
3bac78ca | 173 | unsigned long context) |
944a2f11 | 174 | { |
750af5e5 | 175 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
3bac78ca | 176 | const struct ni_670x_board *thisboard = NULL; |
289a4033 | 177 | struct ni_670x_private *devpriv; |
944a2f11 HS |
178 | struct comedi_subdevice *s; |
179 | int ret; | |
180 | int i; | |
181 | ||
3bac78ca HS |
182 | if (context < ARRAY_SIZE(ni_670x_boards)) |
183 | thisboard = &ni_670x_boards[context]; | |
184 | if (!thisboard) | |
185 | return -ENODEV; | |
186 | dev->board_ptr = thisboard; | |
187 | dev->board_name = thisboard->name; | |
188 | ||
818f569f HS |
189 | ret = comedi_pci_enable(dev); |
190 | if (ret) | |
191 | return ret; | |
818f569f | 192 | |
0bdab509 | 193 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
c34fa261 HS |
194 | if (!devpriv) |
195 | return -ENOMEM; | |
9a1a6cf8 | 196 | |
39798450 HS |
197 | ret = ni_670x_mite_init(pcidev); |
198 | if (ret) | |
944a2f11 | 199 | return ret; |
39798450 | 200 | |
176db588 HS |
201 | dev->mmio = pci_ioremap_bar(pcidev, 1); |
202 | if (!dev->mmio) | |
39798450 | 203 | return -ENOMEM; |
944a2f11 HS |
204 | |
205 | ret = comedi_alloc_subdevices(dev, 2); | |
206 | if (ret) | |
207 | return ret; | |
208 | ||
fc041c20 | 209 | s = &dev->subdevices[0]; |
944a2f11 HS |
210 | /* analog output subdevice */ |
211 | s->type = COMEDI_SUBD_AO; | |
212 | s->subdev_flags = SDF_WRITABLE; | |
213 | s->n_chan = thisboard->ao_chans; | |
214 | s->maxdata = 0xffff; | |
215 | if (s->n_chan == 32) { | |
216 | const struct comedi_lrange **range_table_list; | |
217 | ||
218 | range_table_list = kmalloc(sizeof(struct comedi_lrange *) * 32, | |
219 | GFP_KERNEL); | |
220 | if (!range_table_list) | |
221 | return -ENOMEM; | |
222 | s->range_table_list = range_table_list; | |
223 | for (i = 0; i < 16; i++) { | |
224 | range_table_list[i] = &range_bipolar10; | |
225 | range_table_list[16 + i] = &range_0_20mA; | |
226 | } | |
227 | } else { | |
228 | s->range_table = &range_bipolar10; | |
229 | } | |
d34b3d41 | 230 | s->insn_write = ni_670x_ao_insn_write; |
1d0750ce HS |
231 | s->insn_read = comedi_readback_insn_read; |
232 | ||
233 | ret = comedi_alloc_subdev_readback(s); | |
234 | if (ret) | |
235 | return ret; | |
944a2f11 | 236 | |
fc041c20 | 237 | s = &dev->subdevices[1]; |
944a2f11 HS |
238 | /* digital i/o subdevice */ |
239 | s->type = COMEDI_SUBD_DIO; | |
240 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
241 | s->n_chan = 8; | |
242 | s->maxdata = 1; | |
243 | s->range_table = &range_digital; | |
244 | s->insn_bits = ni_670x_dio_insn_bits; | |
245 | s->insn_config = ni_670x_dio_insn_config; | |
246 | ||
247 | /* Config of misc registers */ | |
176db588 | 248 | writel(0x10, dev->mmio + MISC_CONTROL_OFFSET); |
944a2f11 | 249 | /* Config of ao registers */ |
176db588 | 250 | writel(0x00, dev->mmio + AO_CONTROL_OFFSET); |
944a2f11 | 251 | |
464c9451 | 252 | return 0; |
944a2f11 HS |
253 | } |
254 | ||
255 | static void ni_670x_detach(struct comedi_device *dev) | |
256 | { | |
70fcd1b7 | 257 | struct comedi_subdevice *s; |
289a4033 | 258 | |
aac307f9 | 259 | comedi_pci_detach(dev); |
70fcd1b7 | 260 | if (dev->n_subdevices) { |
fc041c20 | 261 | s = &dev->subdevices[0]; |
70fcd1b7 HS |
262 | if (s) |
263 | kfree(s->range_table_list); | |
264 | } | |
944a2f11 HS |
265 | } |
266 | ||
6f37e288 HS |
267 | static struct comedi_driver ni_670x_driver = { |
268 | .driver_name = "ni_670x", | |
269 | .module = THIS_MODULE, | |
750af5e5 | 270 | .auto_attach = ni_670x_auto_attach, |
6f37e288 HS |
271 | .detach = ni_670x_detach, |
272 | }; | |
273 | ||
a690b7e5 | 274 | static int ni_670x_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 275 | const struct pci_device_id *id) |
6f37e288 | 276 | { |
b8f4ac23 | 277 | return comedi_pci_auto_config(dev, &ni_670x_driver, id->driver_data); |
6f37e288 HS |
278 | } |
279 | ||
41e043fc | 280 | static const struct pci_device_id ni_670x_pci_table[] = { |
3bac78ca HS |
281 | { PCI_VDEVICE(NI, 0x1290), BOARD_PCI6704 }, |
282 | { PCI_VDEVICE(NI, 0x1920), BOARD_PXI6704 }, | |
283 | { PCI_VDEVICE(NI, 0x2c90), BOARD_PCI6703 }, | |
6f37e288 HS |
284 | { 0 } |
285 | }; | |
286 | MODULE_DEVICE_TABLE(pci, ni_670x_pci_table); | |
287 | ||
288 | static struct pci_driver ni_670x_pci_driver = { | |
9e4edd57 | 289 | .name = "ni_670x", |
6f37e288 HS |
290 | .id_table = ni_670x_pci_table, |
291 | .probe = ni_670x_pci_probe, | |
9901a4d7 | 292 | .remove = comedi_pci_auto_unconfig, |
6f37e288 HS |
293 | }; |
294 | module_comedi_pci_driver(ni_670x_driver, ni_670x_pci_driver); | |
295 | ||
3c323c01 IA |
296 | MODULE_AUTHOR("Comedi http://www.comedi.org"); |
297 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
298 | MODULE_LICENSE("GPL"); |