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