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