]>
Commit | Line | Data |
---|---|---|
e184e2be | 1 | // SPDX-License-Identifier: GPL-2.0+ |
5211d97c | 2 | /* |
96470b68 HS |
3 | * ni_65xx.c |
4 | * Comedi driver for National Instruments PCI-65xx static dio boards | |
5 | * | |
6 | * Copyright (C) 2006 Jon Grierson <jd@renko.co.uk> | |
7 | * Copyright (C) 2006 Frank Mori Hess <fmhess@users.sourceforge.net> | |
8 | * | |
9 | * COMEDI - Linux Control and Measurement Device Interface | |
10 | * Copyright (C) 1999,2002,2003 David A. Schleef <ds@schleef.org> | |
96470b68 | 11 | */ |
5211d97c | 12 | |
5211d97c | 13 | /* |
96470b68 HS |
14 | * Driver: ni_65xx |
15 | * Description: National Instruments 65xx static dio boards | |
16 | * Author: Jon Grierson <jd@renko.co.uk>, | |
17 | * Frank Mori Hess <fmhess@users.sourceforge.net> | |
18 | * Status: testing | |
fe8fd0c8 IA |
19 | * Devices: [National Instruments] PCI-6509 (pci-6509), PXI-6509 (pxi-6509), |
20 | * PCI-6510 (pci-6510), PCI-6511 (pci-6511), PXI-6511 (pxi-6511), | |
21 | * PCI-6512 (pci-6512), PXI-6512 (pxi-6512), PCI-6513 (pci-6513), | |
22 | * PXI-6513 (pxi-6513), PCI-6514 (pci-6514), PXI-6514 (pxi-6514), | |
23 | * PCI-6515 (pxi-6515), PXI-6515 (pxi-6515), PCI-6516 (pci-6516), | |
24 | * PCI-6517 (pci-6517), PCI-6518 (pci-6518), PCI-6519 (pci-6519), | |
25 | * PCI-6520 (pci-6520), PCI-6521 (pci-6521), PXI-6521 (pxi-6521), | |
26 | * PCI-6528 (pci-6528), PXI-6528 (pxi-6528) | |
d484e337 | 27 | * Updated: Mon, 21 Jul 2014 12:49:58 +0000 |
96470b68 HS |
28 | * |
29 | * Configuration Options: not applicable, uses PCI auto config | |
30 | * | |
31 | * Based on the PCI-6527 driver by ds. | |
32 | * The interrupt subdevice (subdevice 3) is probably broken for all | |
33 | * boards except maybe the 6514. | |
d484e337 IA |
34 | * |
35 | * This driver previously inverted the outputs on PCI-6513 through to | |
36 | * PCI-6519 and on PXI-6513 through to PXI-6515. It no longer inverts | |
37 | * outputs on those cards by default as it didn't make much sense. If | |
38 | * you require the outputs to be inverted on those cards for legacy | |
39 | * reasons, set the module parameter "legacy_invert_outputs=true" when | |
40 | * loading the module, or set "ni_65xx.legacy_invert_outputs=true" on | |
41 | * the kernel command line if the driver is built in to the kernel. | |
96470b68 | 42 | */ |
5211d97c JG |
43 | |
44 | /* | |
96470b68 HS |
45 | * Manuals (available from ftp://ftp.natinst.com/support/manuals) |
46 | * | |
47 | * 370106b.pdf 6514 Register Level Programmer Manual | |
5211d97c JG |
48 | */ |
49 | ||
ce157f80 | 50 | #include <linux/module.h> |
25436dc9 | 51 | #include <linux/interrupt.h> |
33782dd5 | 52 | |
7b9c3d14 | 53 | #include "../comedi_pci.h" |
5211d97c | 54 | |
8f587d35 HS |
55 | /* |
56 | * PCI BAR1 Register Map | |
57 | */ | |
58 | ||
59 | /* Non-recurring Registers (8-bit except where noted) */ | |
60 | #define NI_65XX_ID_REG 0x00 | |
61 | #define NI_65XX_CLR_REG 0x01 | |
ecbbf6d3 RT |
62 | #define NI_65XX_CLR_WDOG_INT BIT(6) |
63 | #define NI_65XX_CLR_WDOG_PING BIT(5) | |
64 | #define NI_65XX_CLR_WDOG_EXP BIT(4) | |
65 | #define NI_65XX_CLR_EDGE_INT BIT(3) | |
66 | #define NI_65XX_CLR_OVERFLOW_INT BIT(2) | |
8f587d35 | 67 | #define NI_65XX_STATUS_REG 0x02 |
ecbbf6d3 RT |
68 | #define NI_65XX_STATUS_WDOG_INT BIT(5) |
69 | #define NI_65XX_STATUS_FALL_EDGE BIT(4) | |
70 | #define NI_65XX_STATUS_RISE_EDGE BIT(3) | |
71 | #define NI_65XX_STATUS_INT BIT(2) | |
72 | #define NI_65XX_STATUS_OVERFLOW_INT BIT(1) | |
73 | #define NI_65XX_STATUS_EDGE_INT BIT(0) | |
8f587d35 | 74 | #define NI_65XX_CTRL_REG 0x03 |
ecbbf6d3 RT |
75 | #define NI_65XX_CTRL_WDOG_ENA BIT(5) |
76 | #define NI_65XX_CTRL_FALL_EDGE_ENA BIT(4) | |
77 | #define NI_65XX_CTRL_RISE_EDGE_ENA BIT(3) | |
78 | #define NI_65XX_CTRL_INT_ENA BIT(2) | |
79 | #define NI_65XX_CTRL_OVERFLOW_ENA BIT(1) | |
80 | #define NI_65XX_CTRL_EDGE_ENA BIT(0) | |
8f587d35 HS |
81 | #define NI_65XX_REV_REG 0x04 /* 32-bit */ |
82 | #define NI_65XX_FILTER_REG 0x08 /* 32-bit */ | |
83 | #define NI_65XX_RTSI_ROUTE_REG 0x0c /* 16-bit */ | |
84 | #define NI_65XX_RTSI_EDGE_REG 0x0e /* 16-bit */ | |
85 | #define NI_65XX_RTSI_WDOG_REG 0x10 /* 16-bit */ | |
86 | #define NI_65XX_RTSI_TRIG_REG 0x12 /* 16-bit */ | |
87 | #define NI_65XX_AUTO_CLK_SEL_REG 0x14 /* PXI-6528 only */ | |
ecbbf6d3 RT |
88 | #define NI_65XX_AUTO_CLK_SEL_STATUS BIT(1) |
89 | #define NI_65XX_AUTO_CLK_SEL_DISABLE BIT(0) | |
8f587d35 | 90 | #define NI_65XX_WDOG_CTRL_REG 0x15 |
ecbbf6d3 | 91 | #define NI_65XX_WDOG_CTRL_ENA BIT(0) |
8f587d35 | 92 | #define NI_65XX_RTSI_CFG_REG 0x16 |
ecbbf6d3 RT |
93 | #define NI_65XX_RTSI_CFG_RISE_SENSE BIT(2) |
94 | #define NI_65XX_RTSI_CFG_FALL_SENSE BIT(1) | |
95 | #define NI_65XX_RTSI_CFG_SYNC_DETECT BIT(0) | |
8f587d35 | 96 | #define NI_65XX_WDOG_STATUS_REG 0x17 |
ecbbf6d3 | 97 | #define NI_65XX_WDOG_STATUS_EXP BIT(0) |
8f587d35 | 98 | #define NI_65XX_WDOG_INTERVAL_REG 0x18 /* 32-bit */ |
5211d97c | 99 | |
632b9ad3 HS |
100 | /* Recurring port registers (8-bit) */ |
101 | #define NI_65XX_PORT(x) ((x) * 0x10) | |
102 | #define NI_65XX_IO_DATA_REG(x) (0x40 + NI_65XX_PORT(x)) | |
103 | #define NI_65XX_IO_SEL_REG(x) (0x41 + NI_65XX_PORT(x)) | |
ecbbf6d3 RT |
104 | #define NI_65XX_IO_SEL_OUTPUT 0 |
105 | #define NI_65XX_IO_SEL_INPUT BIT(0) | |
632b9ad3 HS |
106 | #define NI_65XX_RISE_EDGE_ENA_REG(x) (0x42 + NI_65XX_PORT(x)) |
107 | #define NI_65XX_FALL_EDGE_ENA_REG(x) (0x43 + NI_65XX_PORT(x)) | |
108 | #define NI_65XX_FILTER_ENA(x) (0x44 + NI_65XX_PORT(x)) | |
109 | #define NI_65XX_WDOG_HIZ_REG(x) (0x46 + NI_65XX_PORT(x)) | |
110 | #define NI_65XX_WDOG_ENA(x) (0x47 + NI_65XX_PORT(x)) | |
111 | #define NI_65XX_WDOG_HI_LO_REG(x) (0x48 + NI_65XX_PORT(x)) | |
112 | #define NI_65XX_RTSI_ENA(x) (0x49 + NI_65XX_PORT(x)) | |
113 | ||
1b094a42 HS |
114 | #define NI_65XX_PORT_TO_CHAN(x) ((x) * 8) |
115 | #define NI_65XX_CHAN_TO_PORT(x) ((x) / 8) | |
116 | #define NI_65XX_CHAN_TO_MASK(x) (1 << ((x) % 8)) | |
5211d97c | 117 | |
b4a69035 HS |
118 | enum ni_65xx_boardid { |
119 | BOARD_PCI6509, | |
120 | BOARD_PXI6509, | |
121 | BOARD_PCI6510, | |
122 | BOARD_PCI6511, | |
123 | BOARD_PXI6511, | |
124 | BOARD_PCI6512, | |
125 | BOARD_PXI6512, | |
126 | BOARD_PCI6513, | |
127 | BOARD_PXI6513, | |
128 | BOARD_PCI6514, | |
129 | BOARD_PXI6514, | |
130 | BOARD_PCI6515, | |
131 | BOARD_PXI6515, | |
132 | BOARD_PCI6516, | |
133 | BOARD_PCI6517, | |
134 | BOARD_PCI6518, | |
135 | BOARD_PCI6519, | |
136 | BOARD_PCI6520, | |
137 | BOARD_PCI6521, | |
138 | BOARD_PXI6521, | |
139 | BOARD_PCI6528, | |
140 | BOARD_PXI6528, | |
141 | }; | |
142 | ||
125edf55 | 143 | struct ni_65xx_board { |
5211d97c | 144 | const char *name; |
f7ede00d NE |
145 | unsigned int num_dio_ports; |
146 | unsigned int num_di_ports; | |
147 | unsigned int num_do_ports; | |
148 | unsigned int legacy_invert:1; | |
125edf55 BP |
149 | }; |
150 | ||
151 | static const struct ni_65xx_board ni_65xx_boards[] = { | |
b4a69035 | 152 | [BOARD_PCI6509] = { |
e4f0e130 HS |
153 | .name = "pci-6509", |
154 | .num_dio_ports = 12, | |
155 | }, | |
b4a69035 | 156 | [BOARD_PXI6509] = { |
e4f0e130 HS |
157 | .name = "pxi-6509", |
158 | .num_dio_ports = 12, | |
159 | }, | |
b4a69035 | 160 | [BOARD_PCI6510] = { |
e4f0e130 HS |
161 | .name = "pci-6510", |
162 | .num_di_ports = 4, | |
163 | }, | |
b4a69035 | 164 | [BOARD_PCI6511] = { |
e4f0e130 HS |
165 | .name = "pci-6511", |
166 | .num_di_ports = 8, | |
167 | }, | |
b4a69035 | 168 | [BOARD_PXI6511] = { |
e4f0e130 HS |
169 | .name = "pxi-6511", |
170 | .num_di_ports = 8, | |
171 | }, | |
b4a69035 | 172 | [BOARD_PCI6512] = { |
e4f0e130 HS |
173 | .name = "pci-6512", |
174 | .num_do_ports = 8, | |
175 | }, | |
b4a69035 | 176 | [BOARD_PXI6512] = { |
e4f0e130 HS |
177 | .name = "pxi-6512", |
178 | .num_do_ports = 8, | |
179 | }, | |
b4a69035 | 180 | [BOARD_PCI6513] = { |
e4f0e130 HS |
181 | .name = "pci-6513", |
182 | .num_do_ports = 8, | |
d484e337 | 183 | .legacy_invert = 1, |
e4f0e130 | 184 | }, |
b4a69035 | 185 | [BOARD_PXI6513] = { |
e4f0e130 HS |
186 | .name = "pxi-6513", |
187 | .num_do_ports = 8, | |
d484e337 | 188 | .legacy_invert = 1, |
e4f0e130 | 189 | }, |
b4a69035 | 190 | [BOARD_PCI6514] = { |
e4f0e130 HS |
191 | .name = "pci-6514", |
192 | .num_di_ports = 4, | |
193 | .num_do_ports = 4, | |
d484e337 | 194 | .legacy_invert = 1, |
e4f0e130 | 195 | }, |
b4a69035 | 196 | [BOARD_PXI6514] = { |
e4f0e130 HS |
197 | .name = "pxi-6514", |
198 | .num_di_ports = 4, | |
199 | .num_do_ports = 4, | |
d484e337 | 200 | .legacy_invert = 1, |
e4f0e130 | 201 | }, |
b4a69035 | 202 | [BOARD_PCI6515] = { |
e4f0e130 HS |
203 | .name = "pci-6515", |
204 | .num_di_ports = 4, | |
205 | .num_do_ports = 4, | |
d484e337 | 206 | .legacy_invert = 1, |
e4f0e130 | 207 | }, |
b4a69035 | 208 | [BOARD_PXI6515] = { |
e4f0e130 HS |
209 | .name = "pxi-6515", |
210 | .num_di_ports = 4, | |
211 | .num_do_ports = 4, | |
d484e337 | 212 | .legacy_invert = 1, |
e4f0e130 | 213 | }, |
b4a69035 | 214 | [BOARD_PCI6516] = { |
e4f0e130 HS |
215 | .name = "pci-6516", |
216 | .num_do_ports = 4, | |
d484e337 | 217 | .legacy_invert = 1, |
e4f0e130 | 218 | }, |
b4a69035 | 219 | [BOARD_PCI6517] = { |
e4f0e130 HS |
220 | .name = "pci-6517", |
221 | .num_do_ports = 4, | |
d484e337 | 222 | .legacy_invert = 1, |
e4f0e130 | 223 | }, |
b4a69035 | 224 | [BOARD_PCI6518] = { |
e4f0e130 HS |
225 | .name = "pci-6518", |
226 | .num_di_ports = 2, | |
227 | .num_do_ports = 2, | |
d484e337 | 228 | .legacy_invert = 1, |
e4f0e130 | 229 | }, |
b4a69035 | 230 | [BOARD_PCI6519] = { |
e4f0e130 HS |
231 | .name = "pci-6519", |
232 | .num_di_ports = 2, | |
233 | .num_do_ports = 2, | |
d484e337 | 234 | .legacy_invert = 1, |
e4f0e130 | 235 | }, |
b4a69035 | 236 | [BOARD_PCI6520] = { |
e4f0e130 HS |
237 | .name = "pci-6520", |
238 | .num_di_ports = 1, | |
239 | .num_do_ports = 1, | |
240 | }, | |
b4a69035 | 241 | [BOARD_PCI6521] = { |
e4f0e130 HS |
242 | .name = "pci-6521", |
243 | .num_di_ports = 1, | |
244 | .num_do_ports = 1, | |
245 | }, | |
b4a69035 | 246 | [BOARD_PXI6521] = { |
e4f0e130 HS |
247 | .name = "pxi-6521", |
248 | .num_di_ports = 1, | |
249 | .num_do_ports = 1, | |
250 | }, | |
b4a69035 | 251 | [BOARD_PCI6528] = { |
e4f0e130 HS |
252 | .name = "pci-6528", |
253 | .num_di_ports = 3, | |
254 | .num_do_ports = 3, | |
255 | }, | |
b4a69035 | 256 | [BOARD_PXI6528] = { |
e4f0e130 HS |
257 | .name = "pxi-6528", |
258 | .num_di_ports = 3, | |
259 | .num_do_ports = 3, | |
260 | }, | |
5211d97c JG |
261 | }; |
262 | ||
d484e337 IA |
263 | static bool ni_65xx_legacy_invert_outputs; |
264 | module_param_named(legacy_invert_outputs, ni_65xx_legacy_invert_outputs, | |
265 | bool, 0444); | |
266 | MODULE_PARM_DESC(legacy_invert_outputs, | |
267 | "invert outputs of PCI/PXI-6513/6514/6515/6516/6517/6518/6519 for compatibility with old user code"); | |
268 | ||
de35c941 | 269 | static unsigned int ni_65xx_num_ports(struct comedi_device *dev) |
9233c8db | 270 | { |
369f87fa | 271 | const struct ni_65xx_board *board = dev->board_ptr; |
de35c941 IA |
272 | |
273 | return board->num_dio_ports + board->num_di_ports + board->num_do_ports; | |
274 | } | |
275 | ||
276 | static void ni_65xx_disable_input_filters(struct comedi_device *dev) | |
277 | { | |
de35c941 | 278 | unsigned int num_ports = ni_65xx_num_ports(dev); |
9233c8db HS |
279 | int i; |
280 | ||
9233c8db HS |
281 | /* disable input filtering on all ports */ |
282 | for (i = 0; i < num_ports; ++i) | |
f7be5ad9 | 283 | writeb(0x00, dev->mmio + NI_65XX_FILTER_ENA(i)); |
9233c8db HS |
284 | |
285 | /* set filter interval to 0 (32bit reg) */ | |
f7be5ad9 | 286 | writel(0x00000000, dev->mmio + NI_65XX_FILTER_REG); |
9233c8db HS |
287 | } |
288 | ||
a5e7d93d IA |
289 | /* updates edge detection for base_chan to base_chan+31 */ |
290 | static void ni_65xx_update_edge_detection(struct comedi_device *dev, | |
291 | unsigned int base_chan, | |
292 | unsigned int rising, | |
293 | unsigned int falling) | |
294 | { | |
a5e7d93d IA |
295 | unsigned int num_ports = ni_65xx_num_ports(dev); |
296 | unsigned int port; | |
297 | ||
298 | if (base_chan >= NI_65XX_PORT_TO_CHAN(num_ports)) | |
299 | return; | |
300 | ||
301 | for (port = NI_65XX_CHAN_TO_PORT(base_chan); port < num_ports; port++) { | |
302 | int bitshift = (int)(NI_65XX_PORT_TO_CHAN(port) - base_chan); | |
303 | unsigned int port_mask, port_rising, port_falling; | |
304 | ||
305 | if (bitshift >= 32) | |
306 | break; | |
307 | ||
308 | if (bitshift >= 0) { | |
309 | port_mask = ~0U >> bitshift; | |
310 | port_rising = rising >> bitshift; | |
311 | port_falling = falling >> bitshift; | |
312 | } else { | |
313 | port_mask = ~0U << -bitshift; | |
314 | port_rising = rising << -bitshift; | |
315 | port_falling = falling << -bitshift; | |
316 | } | |
317 | if (port_mask & 0xff) { | |
318 | if (~port_mask & 0xff) { | |
319 | port_rising |= | |
f7be5ad9 | 320 | readb(dev->mmio + |
a5e7d93d IA |
321 | NI_65XX_RISE_EDGE_ENA_REG(port)) & |
322 | ~port_mask; | |
323 | port_falling |= | |
f7be5ad9 | 324 | readb(dev->mmio + |
a5e7d93d IA |
325 | NI_65XX_FALL_EDGE_ENA_REG(port)) & |
326 | ~port_mask; | |
327 | } | |
328 | writeb(port_rising & 0xff, | |
f7be5ad9 | 329 | dev->mmio + NI_65XX_RISE_EDGE_ENA_REG(port)); |
a5e7d93d | 330 | writeb(port_falling & 0xff, |
f7be5ad9 | 331 | dev->mmio + NI_65XX_FALL_EDGE_ENA_REG(port)); |
a5e7d93d IA |
332 | } |
333 | } | |
334 | } | |
335 | ||
3443d1c6 IA |
336 | static void ni_65xx_disable_edge_detection(struct comedi_device *dev) |
337 | { | |
338 | /* clear edge detection for channels 0 to 31 */ | |
339 | ni_65xx_update_edge_detection(dev, 0, 0, 0); | |
340 | /* clear edge detection for channels 32 to 63 */ | |
341 | ni_65xx_update_edge_detection(dev, 32, 0, 0); | |
342 | /* clear edge detection for channels 64 to 95 */ | |
343 | ni_65xx_update_edge_detection(dev, 64, 0, 0); | |
344 | } | |
345 | ||
0a85b6f0 MT |
346 | static int ni_65xx_dio_insn_config(struct comedi_device *dev, |
347 | struct comedi_subdevice *s, | |
8405e40e HS |
348 | struct comedi_insn *insn, |
349 | unsigned int *data) | |
5211d97c | 350 | { |
98e9f679 | 351 | unsigned long base_port = (unsigned long)s->private; |
8405e40e | 352 | unsigned int chan = CR_CHAN(insn->chanspec); |
1b094a42 | 353 | unsigned int chan_mask = NI_65XX_CHAN_TO_MASK(chan); |
f7ede00d | 354 | unsigned int port = base_port + NI_65XX_CHAN_TO_PORT(chan); |
8405e40e HS |
355 | unsigned int interval; |
356 | unsigned int val; | |
5211d97c | 357 | |
5211d97c JG |
358 | switch (data[0]) { |
359 | case INSN_CONFIG_FILTER: | |
8405e40e HS |
360 | /* |
361 | * The deglitch filter interval is specified in nanoseconds. | |
362 | * The hardware supports intervals in 200ns increments. Round | |
363 | * the user values up and return the actual interval. | |
364 | */ | |
365 | interval = (data[1] + 100) / 200; | |
366 | if (interval > 0xfffff) | |
367 | interval = 0xfffff; | |
368 | data[1] = interval * 200; | |
369 | ||
370 | /* | |
371 | * Enable/disable the channel for deglitch filtering. Note | |
372 | * that the filter interval is never set to '0'. This is done | |
373 | * because other channels might still be enabled for filtering. | |
374 | */ | |
f7be5ad9 | 375 | val = readb(dev->mmio + NI_65XX_FILTER_ENA(port)); |
8405e40e | 376 | if (interval) { |
f7be5ad9 | 377 | writel(interval, dev->mmio + NI_65XX_FILTER_REG); |
8405e40e HS |
378 | val |= chan_mask; |
379 | } else { | |
380 | val &= ~chan_mask; | |
381 | } | |
f7be5ad9 | 382 | writeb(val, dev->mmio + NI_65XX_FILTER_ENA(port)); |
11d892cc | 383 | break; |
8405e40e | 384 | |
5211d97c JG |
385 | case INSN_CONFIG_DIO_OUTPUT: |
386 | if (s->type != COMEDI_SUBD_DIO) | |
387 | return -EINVAL; | |
632b9ad3 | 388 | writeb(NI_65XX_IO_SEL_OUTPUT, |
f7be5ad9 | 389 | dev->mmio + NI_65XX_IO_SEL_REG(port)); |
5211d97c | 390 | break; |
11d892cc | 391 | |
5211d97c JG |
392 | case INSN_CONFIG_DIO_INPUT: |
393 | if (s->type != COMEDI_SUBD_DIO) | |
394 | return -EINVAL; | |
632b9ad3 | 395 | writeb(NI_65XX_IO_SEL_INPUT, |
f7be5ad9 | 396 | dev->mmio + NI_65XX_IO_SEL_REG(port)); |
5211d97c | 397 | break; |
11d892cc | 398 | |
5211d97c JG |
399 | case INSN_CONFIG_DIO_QUERY: |
400 | if (s->type != COMEDI_SUBD_DIO) | |
401 | return -EINVAL; | |
f7be5ad9 | 402 | val = readb(dev->mmio + NI_65XX_IO_SEL_REG(port)); |
11d892cc HS |
403 | data[1] = (val == NI_65XX_IO_SEL_INPUT) ? COMEDI_INPUT |
404 | : COMEDI_OUTPUT; | |
5211d97c | 405 | break; |
11d892cc | 406 | |
5211d97c | 407 | default: |
11d892cc | 408 | return -EINVAL; |
5211d97c | 409 | } |
11d892cc HS |
410 | |
411 | return insn->n; | |
5211d97c JG |
412 | } |
413 | ||
0a85b6f0 MT |
414 | static int ni_65xx_dio_insn_bits(struct comedi_device *dev, |
415 | struct comedi_subdevice *s, | |
1b094a42 HS |
416 | struct comedi_insn *insn, |
417 | unsigned int *data) | |
5211d97c | 418 | { |
1b094a42 HS |
419 | unsigned long base_port = (unsigned long)s->private; |
420 | unsigned int base_chan = CR_CHAN(insn->chanspec); | |
421 | int last_port_offset = NI_65XX_CHAN_TO_PORT(s->n_chan - 1); | |
f7ede00d | 422 | unsigned int read_bits = 0; |
677a3156 | 423 | int port_offset; |
52070660 | 424 | |
1b094a42 | 425 | for (port_offset = NI_65XX_CHAN_TO_PORT(base_chan); |
677a3156 | 426 | port_offset <= last_port_offset; port_offset++) { |
f7ede00d | 427 | unsigned int port = base_port + port_offset; |
1b094a42 | 428 | int base_port_channel = NI_65XX_PORT_TO_CHAN(port_offset); |
f7ede00d | 429 | unsigned int port_mask, port_data, bits; |
1b094a42 | 430 | int bitshift = base_port_channel - base_chan; |
677a3156 IA |
431 | |
432 | if (bitshift >= 32) | |
5211d97c | 433 | break; |
5211d97c JG |
434 | port_mask = data[0]; |
435 | port_data = data[1]; | |
5211d97c JG |
436 | if (bitshift > 0) { |
437 | port_mask >>= bitshift; | |
438 | port_data >>= bitshift; | |
439 | } else { | |
440 | port_mask <<= -bitshift; | |
441 | port_data <<= -bitshift; | |
442 | } | |
443 | port_mask &= 0xff; | |
444 | port_data &= 0xff; | |
5c70b536 HS |
445 | |
446 | /* update the outputs */ | |
5211d97c | 447 | if (port_mask) { |
f7be5ad9 | 448 | bits = readb(dev->mmio + NI_65XX_IO_DATA_REG(port)); |
800453dc | 449 | bits ^= s->io_bits; /* invert if necessary */ |
5c70b536 HS |
450 | bits &= ~port_mask; |
451 | bits |= (port_data & port_mask); | |
800453dc | 452 | bits ^= s->io_bits; /* invert back */ |
f7be5ad9 | 453 | writeb(bits, dev->mmio + NI_65XX_IO_DATA_REG(port)); |
5211d97c | 454 | } |
5c70b536 HS |
455 | |
456 | /* read back the actual state */ | |
f7be5ad9 | 457 | bits = readb(dev->mmio + NI_65XX_IO_DATA_REG(port)); |
800453dc | 458 | bits ^= s->io_bits; /* invert if necessary */ |
d8dfb388 | 459 | if (bitshift > 0) |
5c70b536 | 460 | bits <<= bitshift; |
d8dfb388 | 461 | else |
5c70b536 | 462 | bits >>= -bitshift; |
d8dfb388 | 463 | |
5c70b536 | 464 | read_bits |= bits; |
5211d97c JG |
465 | } |
466 | data[1] = read_bits; | |
467 | return insn->n; | |
468 | } | |
469 | ||
70265d24 | 470 | static irqreturn_t ni_65xx_interrupt(int irq, void *d) |
5211d97c | 471 | { |
71b5f4f1 | 472 | struct comedi_device *dev = d; |
33524ed1 | 473 | struct comedi_subdevice *s = dev->read_subdev; |
5211d97c JG |
474 | unsigned int status; |
475 | ||
f7be5ad9 | 476 | status = readb(dev->mmio + NI_65XX_STATUS_REG); |
8f587d35 | 477 | if ((status & NI_65XX_STATUS_INT) == 0) |
5211d97c | 478 | return IRQ_NONE; |
8f587d35 | 479 | if ((status & NI_65XX_STATUS_EDGE_INT) == 0) |
5211d97c JG |
480 | return IRQ_NONE; |
481 | ||
8f587d35 | 482 | writeb(NI_65XX_CLR_EDGE_INT | NI_65XX_CLR_OVERFLOW_INT, |
f7be5ad9 | 483 | dev->mmio + NI_65XX_CLR_REG); |
5211d97c | 484 | |
562de49c | 485 | comedi_buf_write_samples(s, &s->state, 1); |
9207b6b0 | 486 | comedi_handle_events(dev, s); |
562de49c | 487 | |
5211d97c JG |
488 | return IRQ_HANDLED; |
489 | } | |
490 | ||
0a85b6f0 MT |
491 | static int ni_65xx_intr_cmdtest(struct comedi_device *dev, |
492 | struct comedi_subdevice *s, | |
493 | struct comedi_cmd *cmd) | |
5211d97c JG |
494 | { |
495 | int err = 0; | |
5211d97c | 496 | |
27020ffe | 497 | /* Step 1 : check if triggers are trivially valid */ |
5211d97c | 498 | |
4e9782c1 IA |
499 | err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); |
500 | err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_OTHER); | |
501 | err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW); | |
502 | err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); | |
503 | err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT); | |
5211d97c JG |
504 | |
505 | if (err) | |
506 | return 1; | |
507 | ||
27020ffe HS |
508 | /* Step 2a : make sure trigger sources are unique */ |
509 | /* Step 2b : and mutually compatible */ | |
5211d97c | 510 | |
5f93c88f | 511 | /* Step 3: check if arguments are trivially valid */ |
5211d97c | 512 | |
4e9782c1 IA |
513 | err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); |
514 | err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); | |
515 | err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); | |
516 | err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, | |
517 | cmd->chanlist_len); | |
518 | err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); | |
5211d97c JG |
519 | |
520 | if (err) | |
521 | return 3; | |
522 | ||
f8ee13c6 | 523 | /* Step 4: fix up any arguments */ |
5211d97c | 524 | |
f8ee13c6 | 525 | /* Step 5: check channel list if it exists */ |
5211d97c JG |
526 | |
527 | return 0; | |
528 | } | |
529 | ||
0a85b6f0 MT |
530 | static int ni_65xx_intr_cmd(struct comedi_device *dev, |
531 | struct comedi_subdevice *s) | |
5211d97c | 532 | { |
8f587d35 | 533 | writeb(NI_65XX_CLR_EDGE_INT | NI_65XX_CLR_OVERFLOW_INT, |
f7be5ad9 | 534 | dev->mmio + NI_65XX_CLR_REG); |
8f587d35 HS |
535 | writeb(NI_65XX_CTRL_FALL_EDGE_ENA | NI_65XX_CTRL_RISE_EDGE_ENA | |
536 | NI_65XX_CTRL_INT_ENA | NI_65XX_CTRL_EDGE_ENA, | |
f7be5ad9 | 537 | dev->mmio + NI_65XX_CTRL_REG); |
5211d97c JG |
538 | |
539 | return 0; | |
540 | } | |
541 | ||
0a85b6f0 MT |
542 | static int ni_65xx_intr_cancel(struct comedi_device *dev, |
543 | struct comedi_subdevice *s) | |
5211d97c | 544 | { |
f7be5ad9 | 545 | writeb(0x00, dev->mmio + NI_65XX_CTRL_REG); |
5211d97c JG |
546 | |
547 | return 0; | |
548 | } | |
549 | ||
0a85b6f0 MT |
550 | static int ni_65xx_intr_insn_bits(struct comedi_device *dev, |
551 | struct comedi_subdevice *s, | |
b44ce32d HS |
552 | struct comedi_insn *insn, |
553 | unsigned int *data) | |
5211d97c | 554 | { |
5211d97c | 555 | data[1] = 0; |
a2714e3e | 556 | return insn->n; |
5211d97c JG |
557 | } |
558 | ||
0a85b6f0 MT |
559 | static int ni_65xx_intr_insn_config(struct comedi_device *dev, |
560 | struct comedi_subdevice *s, | |
561 | struct comedi_insn *insn, | |
562 | unsigned int *data) | |
5211d97c | 563 | { |
b3e33cd3 HS |
564 | switch (data[0]) { |
565 | case INSN_CONFIG_CHANGE_NOTIFY: | |
566 | /* add instruction to check_insn_config_length() */ | |
567 | if (insn->n != 3) | |
568 | return -EINVAL; | |
5211d97c | 569 | |
3443d1c6 | 570 | /* update edge detection for channels 0 to 31 */ |
a5e7d93d | 571 | ni_65xx_update_edge_detection(dev, 0, data[1], data[2]); |
3443d1c6 IA |
572 | /* clear edge detection for channels 32 to 63 */ |
573 | ni_65xx_update_edge_detection(dev, 32, 0, 0); | |
574 | /* clear edge detection for channels 64 to 95 */ | |
575 | ni_65xx_update_edge_detection(dev, 64, 0, 0); | |
576 | break; | |
577 | case INSN_CONFIG_DIGITAL_TRIG: | |
578 | /* check trigger number */ | |
579 | if (data[1] != 0) | |
580 | return -EINVAL; | |
581 | /* check digital trigger operation */ | |
582 | switch (data[2]) { | |
583 | case COMEDI_DIGITAL_TRIG_DISABLE: | |
584 | ni_65xx_disable_edge_detection(dev); | |
585 | break; | |
586 | case COMEDI_DIGITAL_TRIG_ENABLE_EDGES: | |
587 | /* | |
588 | * update edge detection for channels data[3] | |
589 | * to (data[3] + 31) | |
590 | */ | |
591 | ni_65xx_update_edge_detection(dev, data[3], | |
592 | data[4], data[5]); | |
593 | break; | |
594 | default: | |
595 | return -EINVAL; | |
596 | } | |
b3e33cd3 HS |
597 | break; |
598 | default: | |
599 | return -EINVAL; | |
600 | } | |
5211d97c | 601 | |
b3e33cd3 | 602 | return insn->n; |
5211d97c JG |
603 | } |
604 | ||
9949595c | 605 | /* ripped from mite.h and mite_setup2() to avoid mite dependency */ |
b965b77f | 606 | #define MITE_IODWBSR 0xc0 /* IO Device Window Base Size Register */ |
ecbbf6d3 | 607 | #define WENAB BIT(7) /* window enable */ |
b965b77f HS |
608 | |
609 | static int ni_65xx_mite_init(struct pci_dev *pcidev) | |
610 | { | |
611 | void __iomem *mite_base; | |
612 | u32 main_phys_addr; | |
613 | ||
614 | /* ioremap the MITE registers (BAR 0) temporarily */ | |
615 | mite_base = pci_ioremap_bar(pcidev, 0); | |
616 | if (!mite_base) | |
617 | return -ENOMEM; | |
618 | ||
619 | /* set data window to main registers (BAR 1) */ | |
620 | main_phys_addr = pci_resource_start(pcidev, 1); | |
621 | writel(main_phys_addr | WENAB, mite_base + MITE_IODWBSR); | |
622 | ||
623 | /* finished with MITE registers */ | |
624 | iounmap(mite_base); | |
625 | return 0; | |
626 | } | |
627 | ||
a690b7e5 | 628 | static int ni_65xx_auto_attach(struct comedi_device *dev, |
b4a69035 | 629 | unsigned long context) |
5211d97c | 630 | { |
750af5e5 | 631 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
b4a69035 | 632 | const struct ni_65xx_board *board = NULL; |
34c43922 | 633 | struct comedi_subdevice *s; |
f7ede00d | 634 | unsigned int i; |
5211d97c JG |
635 | int ret; |
636 | ||
b4a69035 HS |
637 | if (context < ARRAY_SIZE(ni_65xx_boards)) |
638 | board = &ni_65xx_boards[context]; | |
639 | if (!board) | |
640 | return -ENODEV; | |
641 | dev->board_ptr = board; | |
642 | dev->board_name = board->name; | |
643 | ||
818f569f HS |
644 | ret = comedi_pci_enable(dev); |
645 | if (ret) | |
646 | return ret; | |
818f569f | 647 | |
b965b77f HS |
648 | ret = ni_65xx_mite_init(pcidev); |
649 | if (ret) | |
5211d97c | 650 | return ret; |
b965b77f | 651 | |
f7be5ad9 HS |
652 | dev->mmio = pci_ioremap_bar(pcidev, 1); |
653 | if (!dev->mmio) | |
b965b77f | 654 | return -ENOMEM; |
5211d97c | 655 | |
33998d67 | 656 | writeb(NI_65XX_CLR_EDGE_INT | NI_65XX_CLR_OVERFLOW_INT, |
f7be5ad9 HS |
657 | dev->mmio + NI_65XX_CLR_REG); |
658 | writeb(0x00, dev->mmio + NI_65XX_CTRL_REG); | |
33998d67 HS |
659 | |
660 | if (pcidev->irq) { | |
661 | ret = request_irq(pcidev->irq, ni_65xx_interrupt, IRQF_SHARED, | |
662 | dev->board_name, dev); | |
663 | if (ret == 0) | |
664 | dev->irq = pcidev->irq; | |
665 | } | |
666 | ||
0e5789f5 | 667 | dev_info(dev->class_dev, "board: %s, ID=0x%02x", dev->board_name, |
6c7d2c8b | 668 | readb(dev->mmio + NI_65XX_ID_REG)); |
5211d97c | 669 | |
2f0b9d08 | 670 | ret = comedi_alloc_subdevices(dev, 4); |
8b6c5694 | 671 | if (ret) |
5211d97c JG |
672 | return ret; |
673 | ||
a9c6f0bb | 674 | s = &dev->subdevices[0]; |
b4a69035 | 675 | if (board->num_di_ports) { |
feb919dc HS |
676 | s->type = COMEDI_SUBD_DI; |
677 | s->subdev_flags = SDF_READABLE; | |
678 | s->n_chan = NI_65XX_PORT_TO_CHAN(board->num_di_ports); | |
679 | s->maxdata = 1; | |
680 | s->range_table = &range_digital; | |
681 | s->insn_bits = ni_65xx_dio_insn_bits; | |
682 | s->insn_config = ni_65xx_dio_insn_config; | |
98e9f679 HS |
683 | |
684 | /* the input ports always start at port 0 */ | |
685 | s->private = (void *)0; | |
5211d97c | 686 | } else { |
feb919dc | 687 | s->type = COMEDI_SUBD_UNUSED; |
5211d97c JG |
688 | } |
689 | ||
a9c6f0bb | 690 | s = &dev->subdevices[1]; |
b4a69035 | 691 | if (board->num_do_ports) { |
feb919dc HS |
692 | s->type = COMEDI_SUBD_DO; |
693 | s->subdev_flags = SDF_WRITABLE; | |
694 | s->n_chan = NI_65XX_PORT_TO_CHAN(board->num_do_ports); | |
695 | s->maxdata = 1; | |
696 | s->range_table = &range_digital; | |
697 | s->insn_bits = ni_65xx_dio_insn_bits; | |
98e9f679 HS |
698 | |
699 | /* the output ports always start after the input ports */ | |
700 | s->private = (void *)(unsigned long)board->num_di_ports; | |
800453dc | 701 | |
d484e337 IA |
702 | /* |
703 | * Use the io_bits to handle the inverted outputs. Inverted | |
704 | * outputs are only supported if the "legacy_invert_outputs" | |
705 | * module parameter is set to "true". | |
706 | */ | |
707 | if (ni_65xx_legacy_invert_outputs && board->legacy_invert) | |
708 | s->io_bits = 0xff; | |
dd269473 HS |
709 | |
710 | /* reset all output ports to comedi '0' */ | |
711 | for (i = 0; i < board->num_do_ports; ++i) { | |
712 | writeb(s->io_bits, /* inverted if necessary */ | |
f7be5ad9 | 713 | dev->mmio + |
dd269473 HS |
714 | NI_65XX_IO_DATA_REG(board->num_di_ports + i)); |
715 | } | |
5211d97c | 716 | } else { |
feb919dc | 717 | s->type = COMEDI_SUBD_UNUSED; |
5211d97c JG |
718 | } |
719 | ||
a9c6f0bb | 720 | s = &dev->subdevices[2]; |
b4a69035 | 721 | if (board->num_dio_ports) { |
feb919dc HS |
722 | s->type = COMEDI_SUBD_DIO; |
723 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
724 | s->n_chan = NI_65XX_PORT_TO_CHAN(board->num_dio_ports); | |
725 | s->maxdata = 1; | |
726 | s->range_table = &range_digital; | |
727 | s->insn_bits = ni_65xx_dio_insn_bits; | |
728 | s->insn_config = ni_65xx_dio_insn_config; | |
98e9f679 HS |
729 | |
730 | /* the input/output ports always start at port 0 */ | |
731 | s->private = (void *)0; | |
732 | ||
632b9ad3 | 733 | /* configure all ports for input */ |
b4a69035 | 734 | for (i = 0; i < board->num_dio_ports; ++i) { |
632b9ad3 | 735 | writeb(NI_65XX_IO_SEL_INPUT, |
f7be5ad9 | 736 | dev->mmio + NI_65XX_IO_SEL_REG(i)); |
5211d97c JG |
737 | } |
738 | } else { | |
feb919dc | 739 | s->type = COMEDI_SUBD_UNUSED; |
5211d97c JG |
740 | } |
741 | ||
a9c6f0bb | 742 | s = &dev->subdevices[3]; |
33998d67 HS |
743 | s->type = COMEDI_SUBD_DI; |
744 | s->subdev_flags = SDF_READABLE; | |
745 | s->n_chan = 1; | |
746 | s->maxdata = 1; | |
747 | s->range_table = &range_digital; | |
748 | s->insn_bits = ni_65xx_intr_insn_bits; | |
749 | if (dev->irq) { | |
750 | dev->read_subdev = s; | |
751 | s->subdev_flags |= SDF_CMD_READ; | |
752 | s->len_chanlist = 1; | |
753 | s->insn_config = ni_65xx_intr_insn_config; | |
754 | s->do_cmdtest = ni_65xx_intr_cmdtest; | |
755 | s->do_cmd = ni_65xx_intr_cmd; | |
756 | s->cancel = ni_65xx_intr_cancel; | |
757 | } | |
5211d97c | 758 | |
9233c8db | 759 | ni_65xx_disable_input_filters(dev); |
31adc05f | 760 | ni_65xx_disable_edge_detection(dev); |
5211d97c | 761 | |
5211d97c JG |
762 | return 0; |
763 | } | |
764 | ||
484ecc95 | 765 | static void ni_65xx_detach(struct comedi_device *dev) |
5211d97c | 766 | { |
aac307f9 | 767 | if (dev->mmio) |
f7be5ad9 | 768 | writeb(0x00, dev->mmio + NI_65XX_CTRL_REG); |
aac307f9 | 769 | comedi_pci_detach(dev); |
5211d97c JG |
770 | } |
771 | ||
2927eda6 | 772 | static struct comedi_driver ni_65xx_driver = { |
f5b6ebd6 HS |
773 | .driver_name = "ni_65xx", |
774 | .module = THIS_MODULE, | |
775 | .auto_attach = ni_65xx_auto_attach, | |
776 | .detach = ni_65xx_detach, | |
2927eda6 IA |
777 | }; |
778 | ||
a690b7e5 | 779 | static int ni_65xx_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 780 | const struct pci_device_id *id) |
727b286b | 781 | { |
b8f4ac23 | 782 | return comedi_pci_auto_config(dev, &ni_65xx_driver, id->driver_data); |
727b286b AT |
783 | } |
784 | ||
41e043fc | 785 | static const struct pci_device_id ni_65xx_pci_table[] = { |
b4a69035 HS |
786 | { PCI_VDEVICE(NI, 0x1710), BOARD_PXI6509 }, |
787 | { PCI_VDEVICE(NI, 0x7085), BOARD_PCI6509 }, | |
788 | { PCI_VDEVICE(NI, 0x7086), BOARD_PXI6528 }, | |
789 | { PCI_VDEVICE(NI, 0x7087), BOARD_PCI6515 }, | |
790 | { PCI_VDEVICE(NI, 0x7088), BOARD_PCI6514 }, | |
791 | { PCI_VDEVICE(NI, 0x70a9), BOARD_PCI6528 }, | |
792 | { PCI_VDEVICE(NI, 0x70c3), BOARD_PCI6511 }, | |
793 | { PCI_VDEVICE(NI, 0x70c8), BOARD_PCI6513 }, | |
794 | { PCI_VDEVICE(NI, 0x70c9), BOARD_PXI6515 }, | |
795 | { PCI_VDEVICE(NI, 0x70cc), BOARD_PCI6512 }, | |
796 | { PCI_VDEVICE(NI, 0x70cd), BOARD_PXI6514 }, | |
797 | { PCI_VDEVICE(NI, 0x70d1), BOARD_PXI6513 }, | |
798 | { PCI_VDEVICE(NI, 0x70d2), BOARD_PXI6512 }, | |
799 | { PCI_VDEVICE(NI, 0x70d3), BOARD_PXI6511 }, | |
800 | { PCI_VDEVICE(NI, 0x7124), BOARD_PCI6510 }, | |
801 | { PCI_VDEVICE(NI, 0x7125), BOARD_PCI6516 }, | |
802 | { PCI_VDEVICE(NI, 0x7126), BOARD_PCI6517 }, | |
803 | { PCI_VDEVICE(NI, 0x7127), BOARD_PCI6518 }, | |
804 | { PCI_VDEVICE(NI, 0x7128), BOARD_PCI6519 }, | |
805 | { PCI_VDEVICE(NI, 0x718b), BOARD_PCI6521 }, | |
806 | { PCI_VDEVICE(NI, 0x718c), BOARD_PXI6521 }, | |
807 | { PCI_VDEVICE(NI, 0x71c5), BOARD_PCI6520 }, | |
a2fa439d HS |
808 | { 0 } |
809 | }; | |
810 | MODULE_DEVICE_TABLE(pci, ni_65xx_pci_table); | |
811 | ||
7e661293 | 812 | static struct pci_driver ni_65xx_pci_driver = { |
a2fa439d HS |
813 | .name = "ni_65xx", |
814 | .id_table = ni_65xx_pci_table, | |
815 | .probe = ni_65xx_pci_probe, | |
9901a4d7 | 816 | .remove = comedi_pci_auto_unconfig, |
727b286b | 817 | }; |
7e661293 | 818 | module_comedi_pci_driver(ni_65xx_driver, ni_65xx_pci_driver); |
3c323c01 IA |
819 | |
820 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
68864aab | 821 | MODULE_DESCRIPTION("Comedi driver for NI PCI-65xx static dio boards"); |
3c323c01 | 822 | MODULE_LICENSE("GPL"); |