Commit | Line | Data |
---|---|---|
58dd7c0a M |
1 | /* |
2 | comedi/drivers/ni_660x.c | |
3 | Hardware driver for NI 660x devices | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
58dd7c0a M |
14 | */ |
15 | ||
16 | /* | |
71d92fac IA |
17 | * Driver: ni_660x |
18 | * Description: National Instruments 660x counter/timer boards | |
19 | * Devices: [National Instruments] PCI-6601 (ni_660x), PCI-6602, PXI-6602, | |
8bdfefb7 | 20 | * PXI-6608, PXI-6624 |
71d92fac IA |
21 | * Author: J.P. Mellor <jpmellor@rose-hulman.edu>, |
22 | * Herman.Bruyninckx@mech.kuleuven.ac.be, | |
23 | * Wim.Meeussen@mech.kuleuven.ac.be, | |
24 | * Klaas.Gadeyne@mech.kuleuven.ac.be, | |
25 | * Frank Mori Hess <fmhess@users.sourceforge.net> | |
8bdfefb7 | 26 | * Updated: Fri, 15 Mar 2013 10:47:56 +0000 |
71d92fac IA |
27 | * Status: experimental |
28 | * | |
29 | * Encoders work. PulseGeneration (both single pulse and pulse train) | |
30 | * works. Buffered commands work for input but not output. | |
c8c8ff88 | 31 | * |
71d92fac IA |
32 | * References: |
33 | * DAQ 660x Register-Level Programmer Manual (NI 370505A-01) | |
34 | * DAQ 6601/6602 User Manual (NI 322137B-01) | |
35 | */ | |
58dd7c0a | 36 | |
ce157f80 | 37 | #include <linux/module.h> |
25436dc9 | 38 | #include <linux/interrupt.h> |
33782dd5 | 39 | |
a6c760b1 | 40 | #include "../comedi_pci.h" |
33782dd5 | 41 | |
58dd7c0a M |
42 | #include "mite.h" |
43 | #include "ni_tio.h" | |
44 | ||
45 | enum ni_660x_constants { | |
46 | min_counter_pfi_chan = 8, | |
47 | max_dio_pfi_chan = 31, | |
48 | counters_per_chip = 4 | |
49 | }; | |
50 | ||
51 | #define NUM_PFI_CHANNELS 40 | |
900b7808 BA |
52 | /* really there are only up to 3 dma channels, but the register layout allows |
53 | for 4 */ | |
58dd7c0a M |
54 | #define MAX_DMA_CHANNEL 4 |
55 | ||
56 | /* See Register-Level Programmer Manual page 3.1 */ | |
1246f05b HS |
57 | enum ni_660x_register { |
58 | NI660X_G0_INT_ACK, | |
59 | NI660X_G0_STATUS, | |
60 | NI660X_G1_INT_ACK, | |
61 | NI660X_G1_STATUS, | |
62 | NI660X_G01_STATUS, | |
63 | NI660X_G0_CMD, | |
64 | NI660X_STC_DIO_PARALLEL_INPUT, | |
65 | NI660X_G1_CMD, | |
66 | NI660X_G0_HW_SAVE, | |
67 | NI660X_G1_HW_SAVE, | |
68 | NI660X_STC_DIO_OUTPUT, | |
69 | NI660X_STC_DIO_CONTROL, | |
70 | NI660X_G0_SW_SAVE, | |
71 | NI660X_G1_SW_SAVE, | |
72 | NI660X_G0_MODE, | |
73 | NI660X_G01_STATUS1, | |
74 | NI660X_G1_MODE, | |
75 | NI660X_STC_DIO_SERIAL_INPUT, | |
76 | NI660X_G0_LOADA, | |
77 | NI660X_G01_STATUS2, | |
78 | NI660X_G0_LOADB, | |
79 | NI660X_G1_LOADA, | |
80 | NI660X_G1_LOADB, | |
81 | NI660X_G0_INPUT_SEL, | |
82 | NI660X_G1_INPUT_SEL, | |
83 | NI660X_G0_AUTO_INC, | |
84 | NI660X_G1_AUTO_INC, | |
85 | NI660X_G01_RESET, | |
86 | NI660X_G0_INT_ENA, | |
87 | NI660X_G1_INT_ENA, | |
88 | NI660X_G0_CNT_MODE, | |
89 | NI660X_G1_CNT_MODE, | |
90 | NI660X_G0_GATE2, | |
91 | NI660X_G1_GATE2, | |
92 | NI660X_G0_DMA_CFG, | |
93 | NI660X_G0_DMA_STATUS, | |
94 | NI660X_G1_DMA_CFG, | |
95 | NI660X_G1_DMA_STATUS, | |
96 | NI660X_G2_INT_ACK, | |
97 | NI660X_G2_STATUS, | |
98 | NI660X_G3_INT_ACK, | |
99 | NI660X_G3_STATUS, | |
100 | NI660X_G23_STATUS, | |
101 | NI660X_G2_CMD, | |
102 | NI660X_G3_CMD, | |
103 | NI660X_G2_HW_SAVE, | |
104 | NI660X_G3_HW_SAVE, | |
105 | NI660X_G2_SW_SAVE, | |
106 | NI660X_G3_SW_SAVE, | |
107 | NI660X_G2_MODE, | |
108 | NI660X_G23_STATUS1, | |
109 | NI660X_G3_MODE, | |
110 | NI660X_G2_LOADA, | |
111 | NI660X_G23_STATUS2, | |
112 | NI660X_G2_LOADB, | |
113 | NI660X_G3_LOADA, | |
114 | NI660X_G3_LOADB, | |
115 | NI660X_G2_INPUT_SEL, | |
116 | NI660X_G3_INPUT_SEL, | |
117 | NI660X_G2_AUTO_INC, | |
118 | NI660X_G3_AUTO_INC, | |
119 | NI660X_G23_RESET, | |
120 | NI660X_G2_INT_ENA, | |
121 | NI660X_G3_INT_ENA, | |
122 | NI660X_G2_CNT_MODE, | |
123 | NI660X_G3_CNT_MODE, | |
124 | NI660X_G3_GATE2, | |
125 | NI660X_G2_GATE2, | |
126 | NI660X_G2_DMA_CFG, | |
127 | NI660X_G2_DMA_STATUS, | |
128 | NI660X_G3_DMA_CFG, | |
129 | NI660X_G3_DMA_STATUS, | |
130 | NI660X_DIO32_INPUT, | |
131 | NI660X_DIO32_OUTPUT, | |
132 | NI660X_CLK_CFG, | |
133 | NI660X_GLOBAL_INT_STATUS, | |
134 | NI660X_DMA_CFG, | |
135 | NI660X_GLOBAL_INT_CFG, | |
136 | NI660X_IO_CFG_0_1, | |
137 | NI660X_IO_CFG_2_3, | |
138 | NI660X_IO_CFG_4_5, | |
139 | NI660X_IO_CFG_6_7, | |
140 | NI660X_IO_CFG_8_9, | |
141 | NI660X_IO_CFG_10_11, | |
142 | NI660X_IO_CFG_12_13, | |
143 | NI660X_IO_CFG_14_15, | |
144 | NI660X_IO_CFG_16_17, | |
145 | NI660X_IO_CFG_18_19, | |
146 | NI660X_IO_CFG_20_21, | |
147 | NI660X_IO_CFG_22_23, | |
148 | NI660X_IO_CFG_24_25, | |
149 | NI660X_IO_CFG_26_27, | |
150 | NI660X_IO_CFG_28_29, | |
151 | NI660X_IO_CFG_30_31, | |
152 | NI660X_IO_CFG_32_33, | |
153 | NI660X_IO_CFG_34_35, | |
154 | NI660X_IO_CFG_36_37, | |
155 | NI660X_IO_CFG_38_39, | |
156 | NI660X_NUM_REGS, | |
251411cf | 157 | }; |
58dd7c0a M |
158 | |
159 | static inline unsigned IOConfigReg(unsigned pfi_channel) | |
160 | { | |
1246f05b | 161 | unsigned reg = NI660X_IO_CFG_0_1 + pfi_channel / 2; |
c4c2c67a | 162 | |
1246f05b | 163 | BUG_ON(reg > NI660X_IO_CFG_38_39); |
58dd7c0a M |
164 | return reg; |
165 | } | |
166 | ||
167 | enum ni_660x_register_width { | |
168 | DATA_1B, | |
169 | DATA_2B, | |
170 | DATA_4B | |
171 | }; | |
172 | ||
173 | enum ni_660x_register_direction { | |
174 | NI_660x_READ, | |
175 | NI_660x_WRITE, | |
176 | NI_660x_READ_WRITE | |
177 | }; | |
178 | ||
179 | enum ni_660x_pfi_output_select { | |
180 | pfi_output_select_high_Z = 0, | |
181 | pfi_output_select_counter = 1, | |
182 | pfi_output_select_do = 2, | |
183 | num_pfi_output_selects | |
184 | }; | |
185 | ||
186 | enum ni_660x_subdevices { | |
187 | NI_660X_DIO_SUBDEV = 1, | |
188 | NI_660X_GPCT_SUBDEV_0 = 2 | |
189 | }; | |
190 | static inline unsigned NI_660X_GPCT_SUBDEV(unsigned index) | |
191 | { | |
192 | return NI_660X_GPCT_SUBDEV_0 + index; | |
193 | } | |
194 | ||
0cb5e8ff | 195 | struct NI_660xRegisterData { |
2696fb57 BP |
196 | const char *name; /* Register Name */ |
197 | int offset; /* Offset from base address from GPCT chip */ | |
58dd7c0a | 198 | enum ni_660x_register_direction direction; |
b02957d5 | 199 | enum ni_660x_register_width size; /* 1 byte, 2 bytes, or 4 bytes */ |
0cb5e8ff BP |
200 | }; |
201 | ||
1246f05b | 202 | static const struct NI_660xRegisterData registerData[NI660X_NUM_REGS] = { |
58dd7c0a M |
203 | {"G0 Interrupt Acknowledge", 0x004, NI_660x_WRITE, DATA_2B}, |
204 | {"G0 Status Register", 0x004, NI_660x_READ, DATA_2B}, | |
205 | {"G1 Interrupt Acknowledge", 0x006, NI_660x_WRITE, DATA_2B}, | |
206 | {"G1 Status Register", 0x006, NI_660x_READ, DATA_2B}, | |
207 | {"G01 Status Register ", 0x008, NI_660x_READ, DATA_2B}, | |
208 | {"G0 Command Register", 0x00C, NI_660x_WRITE, DATA_2B}, | |
209 | {"STC DIO Parallel Input", 0x00E, NI_660x_READ, DATA_2B}, | |
210 | {"G1 Command Register", 0x00E, NI_660x_WRITE, DATA_2B}, | |
211 | {"G0 HW Save Register", 0x010, NI_660x_READ, DATA_4B}, | |
212 | {"G1 HW Save Register", 0x014, NI_660x_READ, DATA_4B}, | |
213 | {"STC DIO Output", 0x014, NI_660x_WRITE, DATA_2B}, | |
214 | {"STC DIO Control", 0x016, NI_660x_WRITE, DATA_2B}, | |
215 | {"G0 SW Save Register", 0x018, NI_660x_READ, DATA_4B}, | |
216 | {"G1 SW Save Register", 0x01C, NI_660x_READ, DATA_4B}, | |
217 | {"G0 Mode Register", 0x034, NI_660x_WRITE, DATA_2B}, | |
218 | {"G01 Joint Status 1 Register", 0x036, NI_660x_READ, DATA_2B}, | |
219 | {"G1 Mode Register", 0x036, NI_660x_WRITE, DATA_2B}, | |
220 | {"STC DIO Serial Input", 0x038, NI_660x_READ, DATA_2B}, | |
221 | {"G0 Load A Register", 0x038, NI_660x_WRITE, DATA_4B}, | |
222 | {"G01 Joint Status 2 Register", 0x03A, NI_660x_READ, DATA_2B}, | |
223 | {"G0 Load B Register", 0x03C, NI_660x_WRITE, DATA_4B}, | |
224 | {"G1 Load A Register", 0x040, NI_660x_WRITE, DATA_4B}, | |
225 | {"G1 Load B Register", 0x044, NI_660x_WRITE, DATA_4B}, | |
226 | {"G0 Input Select Register", 0x048, NI_660x_WRITE, DATA_2B}, | |
227 | {"G1 Input Select Register", 0x04A, NI_660x_WRITE, DATA_2B}, | |
228 | {"G0 Autoincrement Register", 0x088, NI_660x_WRITE, DATA_2B}, | |
229 | {"G1 Autoincrement Register", 0x08A, NI_660x_WRITE, DATA_2B}, | |
230 | {"G01 Joint Reset Register", 0x090, NI_660x_WRITE, DATA_2B}, | |
231 | {"G0 Interrupt Enable", 0x092, NI_660x_WRITE, DATA_2B}, | |
232 | {"G1 Interrupt Enable", 0x096, NI_660x_WRITE, DATA_2B}, | |
233 | {"G0 Counting Mode Register", 0x0B0, NI_660x_WRITE, DATA_2B}, | |
234 | {"G1 Counting Mode Register", 0x0B2, NI_660x_WRITE, DATA_2B}, | |
235 | {"G0 Second Gate Register", 0x0B4, NI_660x_WRITE, DATA_2B}, | |
236 | {"G1 Second Gate Register", 0x0B6, NI_660x_WRITE, DATA_2B}, | |
237 | {"G0 DMA Config Register", 0x0B8, NI_660x_WRITE, DATA_2B}, | |
238 | {"G0 DMA Status Register", 0x0B8, NI_660x_READ, DATA_2B}, | |
239 | {"G1 DMA Config Register", 0x0BA, NI_660x_WRITE, DATA_2B}, | |
240 | {"G1 DMA Status Register", 0x0BA, NI_660x_READ, DATA_2B}, | |
241 | {"G2 Interrupt Acknowledge", 0x104, NI_660x_WRITE, DATA_2B}, | |
242 | {"G2 Status Register", 0x104, NI_660x_READ, DATA_2B}, | |
243 | {"G3 Interrupt Acknowledge", 0x106, NI_660x_WRITE, DATA_2B}, | |
244 | {"G3 Status Register", 0x106, NI_660x_READ, DATA_2B}, | |
245 | {"G23 Status Register", 0x108, NI_660x_READ, DATA_2B}, | |
246 | {"G2 Command Register", 0x10C, NI_660x_WRITE, DATA_2B}, | |
247 | {"G3 Command Register", 0x10E, NI_660x_WRITE, DATA_2B}, | |
248 | {"G2 HW Save Register", 0x110, NI_660x_READ, DATA_4B}, | |
249 | {"G3 HW Save Register", 0x114, NI_660x_READ, DATA_4B}, | |
250 | {"G2 SW Save Register", 0x118, NI_660x_READ, DATA_4B}, | |
251 | {"G3 SW Save Register", 0x11C, NI_660x_READ, DATA_4B}, | |
252 | {"G2 Mode Register", 0x134, NI_660x_WRITE, DATA_2B}, | |
253 | {"G23 Joint Status 1 Register", 0x136, NI_660x_READ, DATA_2B}, | |
254 | {"G3 Mode Register", 0x136, NI_660x_WRITE, DATA_2B}, | |
255 | {"G2 Load A Register", 0x138, NI_660x_WRITE, DATA_4B}, | |
256 | {"G23 Joint Status 2 Register", 0x13A, NI_660x_READ, DATA_2B}, | |
257 | {"G2 Load B Register", 0x13C, NI_660x_WRITE, DATA_4B}, | |
258 | {"G3 Load A Register", 0x140, NI_660x_WRITE, DATA_4B}, | |
259 | {"G3 Load B Register", 0x144, NI_660x_WRITE, DATA_4B}, | |
260 | {"G2 Input Select Register", 0x148, NI_660x_WRITE, DATA_2B}, | |
261 | {"G3 Input Select Register", 0x14A, NI_660x_WRITE, DATA_2B}, | |
262 | {"G2 Autoincrement Register", 0x188, NI_660x_WRITE, DATA_2B}, | |
263 | {"G3 Autoincrement Register", 0x18A, NI_660x_WRITE, DATA_2B}, | |
264 | {"G23 Joint Reset Register", 0x190, NI_660x_WRITE, DATA_2B}, | |
265 | {"G2 Interrupt Enable", 0x192, NI_660x_WRITE, DATA_2B}, | |
266 | {"G3 Interrupt Enable", 0x196, NI_660x_WRITE, DATA_2B}, | |
267 | {"G2 Counting Mode Register", 0x1B0, NI_660x_WRITE, DATA_2B}, | |
268 | {"G3 Counting Mode Register", 0x1B2, NI_660x_WRITE, DATA_2B}, | |
269 | {"G3 Second Gate Register", 0x1B6, NI_660x_WRITE, DATA_2B}, | |
270 | {"G2 Second Gate Register", 0x1B4, NI_660x_WRITE, DATA_2B}, | |
271 | {"G2 DMA Config Register", 0x1B8, NI_660x_WRITE, DATA_2B}, | |
272 | {"G2 DMA Status Register", 0x1B8, NI_660x_READ, DATA_2B}, | |
273 | {"G3 DMA Config Register", 0x1BA, NI_660x_WRITE, DATA_2B}, | |
274 | {"G3 DMA Status Register", 0x1BA, NI_660x_READ, DATA_2B}, | |
275 | {"32 bit Digital Input", 0x414, NI_660x_READ, DATA_4B}, | |
276 | {"32 bit Digital Output", 0x510, NI_660x_WRITE, DATA_4B}, | |
277 | {"Clock Config Register", 0x73C, NI_660x_WRITE, DATA_4B}, | |
278 | {"Global Interrupt Status Register", 0x754, NI_660x_READ, DATA_4B}, | |
279 | {"DMA Configuration Register", 0x76C, NI_660x_WRITE, DATA_4B}, | |
280 | {"Global Interrupt Config Register", 0x770, NI_660x_WRITE, DATA_4B}, | |
281 | {"IO Config Register 0-1", 0x77C, NI_660x_READ_WRITE, DATA_2B}, | |
282 | {"IO Config Register 2-3", 0x77E, NI_660x_READ_WRITE, DATA_2B}, | |
283 | {"IO Config Register 4-5", 0x780, NI_660x_READ_WRITE, DATA_2B}, | |
284 | {"IO Config Register 6-7", 0x782, NI_660x_READ_WRITE, DATA_2B}, | |
285 | {"IO Config Register 8-9", 0x784, NI_660x_READ_WRITE, DATA_2B}, | |
286 | {"IO Config Register 10-11", 0x786, NI_660x_READ_WRITE, DATA_2B}, | |
287 | {"IO Config Register 12-13", 0x788, NI_660x_READ_WRITE, DATA_2B}, | |
288 | {"IO Config Register 14-15", 0x78A, NI_660x_READ_WRITE, DATA_2B}, | |
289 | {"IO Config Register 16-17", 0x78C, NI_660x_READ_WRITE, DATA_2B}, | |
290 | {"IO Config Register 18-19", 0x78E, NI_660x_READ_WRITE, DATA_2B}, | |
291 | {"IO Config Register 20-21", 0x790, NI_660x_READ_WRITE, DATA_2B}, | |
292 | {"IO Config Register 22-23", 0x792, NI_660x_READ_WRITE, DATA_2B}, | |
293 | {"IO Config Register 24-25", 0x794, NI_660x_READ_WRITE, DATA_2B}, | |
294 | {"IO Config Register 26-27", 0x796, NI_660x_READ_WRITE, DATA_2B}, | |
295 | {"IO Config Register 28-29", 0x798, NI_660x_READ_WRITE, DATA_2B}, | |
296 | {"IO Config Register 30-31", 0x79A, NI_660x_READ_WRITE, DATA_2B}, | |
297 | {"IO Config Register 32-33", 0x79C, NI_660x_READ_WRITE, DATA_2B}, | |
298 | {"IO Config Register 34-35", 0x79E, NI_660x_READ_WRITE, DATA_2B}, | |
299 | {"IO Config Register 36-37", 0x7A0, NI_660x_READ_WRITE, DATA_2B}, | |
300 | {"IO Config Register 38-39", 0x7A2, NI_660x_READ_WRITE, DATA_2B} | |
301 | }; | |
302 | ||
2696fb57 | 303 | /* kind of ENABLE for the second counter */ |
58dd7c0a M |
304 | enum clock_config_register_bits { |
305 | CounterSwap = 0x1 << 21 | |
306 | }; | |
307 | ||
2696fb57 | 308 | /* ioconfigreg */ |
58dd7c0a M |
309 | static inline unsigned ioconfig_bitshift(unsigned pfi_channel) |
310 | { | |
f9558b49 | 311 | return (pfi_channel % 2) ? 0 : 8; |
58dd7c0a | 312 | } |
0a85b6f0 | 313 | |
58dd7c0a M |
314 | static inline unsigned pfi_output_select_mask(unsigned pfi_channel) |
315 | { | |
316 | return 0x3 << ioconfig_bitshift(pfi_channel); | |
317 | } | |
0a85b6f0 | 318 | |
58dd7c0a | 319 | static inline unsigned pfi_output_select_bits(unsigned pfi_channel, |
0a85b6f0 | 320 | unsigned output_select) |
58dd7c0a M |
321 | { |
322 | return (output_select & 0x3) << ioconfig_bitshift(pfi_channel); | |
323 | } | |
0a85b6f0 | 324 | |
58dd7c0a M |
325 | static inline unsigned pfi_input_select_mask(unsigned pfi_channel) |
326 | { | |
327 | return 0x7 << (4 + ioconfig_bitshift(pfi_channel)); | |
328 | } | |
0a85b6f0 | 329 | |
58dd7c0a | 330 | static inline unsigned pfi_input_select_bits(unsigned pfi_channel, |
0a85b6f0 | 331 | unsigned input_select) |
58dd7c0a M |
332 | { |
333 | return (input_select & 0x7) << (4 + ioconfig_bitshift(pfi_channel)); | |
334 | } | |
335 | ||
2696fb57 | 336 | /* dma configuration register bits */ |
58dd7c0a M |
337 | static inline unsigned dma_select_mask(unsigned dma_channel) |
338 | { | |
339 | BUG_ON(dma_channel >= MAX_DMA_CHANNEL); | |
340 | return 0x1f << (8 * dma_channel); | |
341 | } | |
0a85b6f0 | 342 | |
58dd7c0a M |
343 | enum dma_selection { |
344 | dma_selection_none = 0x1f, | |
345 | }; | |
0a85b6f0 | 346 | |
58dd7c0a M |
347 | static inline unsigned dma_select_bits(unsigned dma_channel, unsigned selection) |
348 | { | |
349 | BUG_ON(dma_channel >= MAX_DMA_CHANNEL); | |
350 | return (selection << (8 * dma_channel)) & dma_select_mask(dma_channel); | |
351 | } | |
0a85b6f0 | 352 | |
58dd7c0a M |
353 | static inline unsigned dma_reset_bit(unsigned dma_channel) |
354 | { | |
355 | BUG_ON(dma_channel >= MAX_DMA_CHANNEL); | |
356 | return 0x80 << (8 * dma_channel); | |
357 | } | |
358 | ||
359 | enum global_interrupt_status_register_bits { | |
360 | Counter_0_Int_Bit = 0x100, | |
361 | Counter_1_Int_Bit = 0x200, | |
362 | Counter_2_Int_Bit = 0x400, | |
363 | Counter_3_Int_Bit = 0x800, | |
364 | Cascade_Int_Bit = 0x20000000, | |
365 | Global_Int_Bit = 0x80000000 | |
366 | }; | |
367 | ||
368 | enum global_interrupt_config_register_bits { | |
369 | Cascade_Int_Enable_Bit = 0x20000000, | |
370 | Global_Int_Polarity_Bit = 0x40000000, | |
371 | Global_Int_Enable_Bit = 0x80000000 | |
372 | }; | |
373 | ||
f69b0d64 | 374 | /* Offset of the GPCT chips from the base-address of the card */ |
900b7808 BA |
375 | /* First chip is at base-address + 0x00, etc. */ |
376 | static const unsigned GPCT_OFFSET[2] = { 0x0, 0x800 }; | |
58dd7c0a | 377 | |
97bcce5a HS |
378 | enum ni_660x_boardid { |
379 | BOARD_PCI6601, | |
380 | BOARD_PCI6602, | |
381 | BOARD_PXI6602, | |
382 | BOARD_PXI6608, | |
8bdfefb7 | 383 | BOARD_PXI6624 |
97bcce5a HS |
384 | }; |
385 | ||
50792813 | 386 | struct ni_660x_board { |
58dd7c0a M |
387 | const char *name; |
388 | unsigned n_chips; /* total number of TIO chips */ | |
50792813 | 389 | }; |
58dd7c0a | 390 | |
50792813 | 391 | static const struct ni_660x_board ni_660x_boards[] = { |
97bcce5a | 392 | [BOARD_PCI6601] = { |
e2b8360f HS |
393 | .name = "PCI-6601", |
394 | .n_chips = 1, | |
395 | }, | |
97bcce5a | 396 | [BOARD_PCI6602] = { |
e2b8360f HS |
397 | .name = "PCI-6602", |
398 | .n_chips = 2, | |
399 | }, | |
97bcce5a | 400 | [BOARD_PXI6602] = { |
e2b8360f HS |
401 | .name = "PXI-6602", |
402 | .n_chips = 2, | |
403 | }, | |
97bcce5a | 404 | [BOARD_PXI6608] = { |
e2b8360f HS |
405 | .name = "PXI-6608", |
406 | .n_chips = 2, | |
407 | }, | |
8bdfefb7 IA |
408 | [BOARD_PXI6624] = { |
409 | .name = "PXI-6624", | |
410 | .n_chips = 2, | |
411 | }, | |
58dd7c0a M |
412 | }; |
413 | ||
414 | #define NI_660X_MAX_NUM_CHIPS 2 | |
415 | #define NI_660X_MAX_NUM_COUNTERS (NI_660X_MAX_NUM_CHIPS * counters_per_chip) | |
416 | ||
b3be94ea | 417 | struct ni_660x_private { |
58dd7c0a M |
418 | struct mite_struct *mite; |
419 | struct ni_gpct_device *counter_dev; | |
420 | uint64_t pfi_direction_bits; | |
421 | struct mite_dma_descriptor_ring | |
422 | *mite_rings[NI_660X_MAX_NUM_CHIPS][counters_per_chip]; | |
423 | spinlock_t mite_channel_lock; | |
894db119 FMH |
424 | /* interrupt_lock prevents races between interrupt and comedi_poll */ |
425 | spinlock_t interrupt_lock; | |
58dd7c0a M |
426 | unsigned dma_configuration_soft_copies[NI_660X_MAX_NUM_CHIPS]; |
427 | spinlock_t soft_reg_copy_lock; | |
428 | unsigned short pfi_output_selects[NUM_PFI_CHANNELS]; | |
b3be94ea | 429 | }; |
58dd7c0a | 430 | |
da91b269 | 431 | static inline unsigned ni_660x_num_counters(struct comedi_device *dev) |
58dd7c0a | 432 | { |
da8e2a52 | 433 | const struct ni_660x_board *board = dev->board_ptr; |
9186ccde HS |
434 | |
435 | return board->n_chips * counters_per_chip; | |
58dd7c0a M |
436 | } |
437 | ||
1246f05b | 438 | static enum ni_660x_register ni_gpct_to_660x_register(enum ni_gpct_register reg) |
58dd7c0a | 439 | { |
58dd7c0a | 440 | switch (reg) { |
12375292 | 441 | case NITIO_G0_AUTO_INC: |
1246f05b | 442 | return NI660X_G0_AUTO_INC; |
12375292 | 443 | case NITIO_G1_AUTO_INC: |
1246f05b | 444 | return NI660X_G1_AUTO_INC; |
12375292 | 445 | case NITIO_G2_AUTO_INC: |
1246f05b | 446 | return NI660X_G2_AUTO_INC; |
12375292 | 447 | case NITIO_G3_AUTO_INC: |
1246f05b | 448 | return NI660X_G3_AUTO_INC; |
12375292 | 449 | case NITIO_G0_CMD: |
1246f05b | 450 | return NI660X_G0_CMD; |
12375292 | 451 | case NITIO_G1_CMD: |
1246f05b | 452 | return NI660X_G1_CMD; |
12375292 | 453 | case NITIO_G2_CMD: |
1246f05b | 454 | return NI660X_G2_CMD; |
12375292 | 455 | case NITIO_G3_CMD: |
1246f05b | 456 | return NI660X_G3_CMD; |
12375292 | 457 | case NITIO_G0_HW_SAVE: |
1246f05b | 458 | return NI660X_G0_HW_SAVE; |
12375292 | 459 | case NITIO_G1_HW_SAVE: |
1246f05b | 460 | return NI660X_G1_HW_SAVE; |
12375292 | 461 | case NITIO_G2_HW_SAVE: |
1246f05b | 462 | return NI660X_G2_HW_SAVE; |
12375292 | 463 | case NITIO_G3_HW_SAVE: |
1246f05b | 464 | return NI660X_G3_HW_SAVE; |
12375292 | 465 | case NITIO_G0_SW_SAVE: |
1246f05b | 466 | return NI660X_G0_SW_SAVE; |
12375292 | 467 | case NITIO_G1_SW_SAVE: |
1246f05b | 468 | return NI660X_G1_SW_SAVE; |
12375292 | 469 | case NITIO_G2_SW_SAVE: |
1246f05b | 470 | return NI660X_G2_SW_SAVE; |
12375292 | 471 | case NITIO_G3_SW_SAVE: |
1246f05b | 472 | return NI660X_G3_SW_SAVE; |
12375292 | 473 | case NITIO_G0_MODE: |
1246f05b | 474 | return NI660X_G0_MODE; |
12375292 | 475 | case NITIO_G1_MODE: |
1246f05b | 476 | return NI660X_G1_MODE; |
12375292 | 477 | case NITIO_G2_MODE: |
1246f05b | 478 | return NI660X_G2_MODE; |
12375292 | 479 | case NITIO_G3_MODE: |
1246f05b | 480 | return NI660X_G3_MODE; |
12375292 | 481 | case NITIO_G0_LOADA: |
1246f05b | 482 | return NI660X_G0_LOADA; |
12375292 | 483 | case NITIO_G1_LOADA: |
1246f05b | 484 | return NI660X_G1_LOADA; |
12375292 | 485 | case NITIO_G2_LOADA: |
1246f05b | 486 | return NI660X_G2_LOADA; |
12375292 | 487 | case NITIO_G3_LOADA: |
1246f05b | 488 | return NI660X_G3_LOADA; |
12375292 | 489 | case NITIO_G0_LOADB: |
1246f05b | 490 | return NI660X_G0_LOADB; |
12375292 | 491 | case NITIO_G1_LOADB: |
1246f05b | 492 | return NI660X_G1_LOADB; |
12375292 | 493 | case NITIO_G2_LOADB: |
1246f05b | 494 | return NI660X_G2_LOADB; |
12375292 | 495 | case NITIO_G3_LOADB: |
1246f05b | 496 | return NI660X_G3_LOADB; |
12375292 | 497 | case NITIO_G0_INPUT_SEL: |
1246f05b | 498 | return NI660X_G0_INPUT_SEL; |
12375292 | 499 | case NITIO_G1_INPUT_SEL: |
1246f05b | 500 | return NI660X_G1_INPUT_SEL; |
12375292 | 501 | case NITIO_G2_INPUT_SEL: |
1246f05b | 502 | return NI660X_G2_INPUT_SEL; |
12375292 | 503 | case NITIO_G3_INPUT_SEL: |
1246f05b | 504 | return NI660X_G3_INPUT_SEL; |
12375292 | 505 | case NITIO_G01_STATUS: |
1246f05b | 506 | return NI660X_G01_STATUS; |
12375292 | 507 | case NITIO_G23_STATUS: |
1246f05b | 508 | return NI660X_G23_STATUS; |
12375292 | 509 | case NITIO_G01_RESET: |
1246f05b | 510 | return NI660X_G01_RESET; |
12375292 | 511 | case NITIO_G23_RESET: |
1246f05b | 512 | return NI660X_G23_RESET; |
12375292 | 513 | case NITIO_G01_STATUS1: |
1246f05b | 514 | return NI660X_G01_STATUS1; |
12375292 | 515 | case NITIO_G23_STATUS1: |
1246f05b | 516 | return NI660X_G23_STATUS1; |
12375292 | 517 | case NITIO_G01_STATUS2: |
1246f05b | 518 | return NI660X_G01_STATUS2; |
12375292 | 519 | case NITIO_G23_STATUS2: |
1246f05b | 520 | return NI660X_G23_STATUS2; |
12375292 | 521 | case NITIO_G0_CNT_MODE: |
1246f05b | 522 | return NI660X_G0_CNT_MODE; |
12375292 | 523 | case NITIO_G1_CNT_MODE: |
1246f05b | 524 | return NI660X_G1_CNT_MODE; |
12375292 | 525 | case NITIO_G2_CNT_MODE: |
1246f05b | 526 | return NI660X_G2_CNT_MODE; |
12375292 | 527 | case NITIO_G3_CNT_MODE: |
1246f05b | 528 | return NI660X_G3_CNT_MODE; |
12375292 | 529 | case NITIO_G0_GATE2: |
1246f05b | 530 | return NI660X_G0_GATE2; |
12375292 | 531 | case NITIO_G1_GATE2: |
1246f05b | 532 | return NI660X_G1_GATE2; |
12375292 | 533 | case NITIO_G2_GATE2: |
1246f05b | 534 | return NI660X_G2_GATE2; |
12375292 | 535 | case NITIO_G3_GATE2: |
1246f05b | 536 | return NI660X_G3_GATE2; |
12375292 | 537 | case NITIO_G0_DMA_CFG: |
1246f05b | 538 | return NI660X_G0_DMA_CFG; |
12375292 | 539 | case NITIO_G0_DMA_STATUS: |
1246f05b | 540 | return NI660X_G0_DMA_STATUS; |
12375292 | 541 | case NITIO_G1_DMA_CFG: |
1246f05b | 542 | return NI660X_G1_DMA_CFG; |
12375292 | 543 | case NITIO_G1_DMA_STATUS: |
1246f05b | 544 | return NI660X_G1_DMA_STATUS; |
12375292 | 545 | case NITIO_G2_DMA_CFG: |
1246f05b | 546 | return NI660X_G2_DMA_CFG; |
12375292 | 547 | case NITIO_G2_DMA_STATUS: |
1246f05b | 548 | return NI660X_G2_DMA_STATUS; |
12375292 | 549 | case NITIO_G3_DMA_CFG: |
1246f05b | 550 | return NI660X_G3_DMA_CFG; |
12375292 | 551 | case NITIO_G3_DMA_STATUS: |
1246f05b | 552 | return NI660X_G3_DMA_STATUS; |
12375292 | 553 | case NITIO_G0_INT_ACK: |
1246f05b | 554 | return NI660X_G0_INT_ACK; |
12375292 | 555 | case NITIO_G1_INT_ACK: |
1246f05b | 556 | return NI660X_G1_INT_ACK; |
12375292 | 557 | case NITIO_G2_INT_ACK: |
1246f05b | 558 | return NI660X_G2_INT_ACK; |
12375292 | 559 | case NITIO_G3_INT_ACK: |
1246f05b | 560 | return NI660X_G3_INT_ACK; |
12375292 | 561 | case NITIO_G0_STATUS: |
1246f05b | 562 | return NI660X_G0_STATUS; |
12375292 | 563 | case NITIO_G1_STATUS: |
1246f05b | 564 | return NI660X_G1_STATUS; |
12375292 | 565 | case NITIO_G2_STATUS: |
1246f05b | 566 | return NI660X_G2_STATUS; |
12375292 | 567 | case NITIO_G3_STATUS: |
1246f05b | 568 | return NI660X_G3_STATUS; |
12375292 | 569 | case NITIO_G0_INT_ENA: |
1246f05b | 570 | return NI660X_G0_INT_ENA; |
12375292 | 571 | case NITIO_G1_INT_ENA: |
1246f05b | 572 | return NI660X_G1_INT_ENA; |
12375292 | 573 | case NITIO_G2_INT_ENA: |
1246f05b | 574 | return NI660X_G2_INT_ENA; |
12375292 | 575 | case NITIO_G3_INT_ENA: |
1246f05b | 576 | return NI660X_G3_INT_ENA; |
58dd7c0a | 577 | default: |
58dd7c0a M |
578 | BUG(); |
579 | return 0; | |
58dd7c0a | 580 | } |
58dd7c0a M |
581 | } |
582 | ||
da91b269 | 583 | static inline void ni_660x_write_register(struct comedi_device *dev, |
0c26c7ed | 584 | unsigned chip, unsigned bits, |
1246f05b | 585 | enum ni_660x_register reg) |
58dd7c0a | 586 | { |
5f8a5f4f | 587 | unsigned int addr = GPCT_OFFSET[chip] + registerData[reg].offset; |
58dd7c0a M |
588 | |
589 | switch (registerData[reg].size) { | |
590 | case DATA_2B: | |
5f8a5f4f | 591 | writew(bits, dev->mmio + addr); |
58dd7c0a M |
592 | break; |
593 | case DATA_4B: | |
5f8a5f4f | 594 | writel(bits, dev->mmio + addr); |
58dd7c0a M |
595 | break; |
596 | default: | |
58dd7c0a M |
597 | BUG(); |
598 | break; | |
599 | } | |
600 | } | |
601 | ||
da91b269 | 602 | static inline unsigned ni_660x_read_register(struct comedi_device *dev, |
0c26c7ed | 603 | unsigned chip, |
1246f05b | 604 | enum ni_660x_register reg) |
58dd7c0a | 605 | { |
5f8a5f4f | 606 | unsigned int addr = GPCT_OFFSET[chip] + registerData[reg].offset; |
58dd7c0a M |
607 | |
608 | switch (registerData[reg].size) { | |
609 | case DATA_2B: | |
5f8a5f4f | 610 | return readw(dev->mmio + addr); |
58dd7c0a | 611 | case DATA_4B: |
5f8a5f4f | 612 | return readl(dev->mmio + addr); |
58dd7c0a | 613 | default: |
58dd7c0a M |
614 | BUG(); |
615 | break; | |
616 | } | |
617 | return 0; | |
618 | } | |
619 | ||
620 | static void ni_gpct_write_register(struct ni_gpct *counter, unsigned bits, | |
0a85b6f0 | 621 | enum ni_gpct_register reg) |
58dd7c0a | 622 | { |
71b5f4f1 | 623 | struct comedi_device *dev = counter->counter_dev->dev; |
1246f05b | 624 | enum ni_660x_register ni_660x_register = ni_gpct_to_660x_register(reg); |
0c26c7ed HS |
625 | unsigned chip = counter->chip_index; |
626 | ||
627 | ni_660x_write_register(dev, chip, bits, ni_660x_register); | |
58dd7c0a M |
628 | } |
629 | ||
630 | static unsigned ni_gpct_read_register(struct ni_gpct *counter, | |
0a85b6f0 | 631 | enum ni_gpct_register reg) |
58dd7c0a | 632 | { |
71b5f4f1 | 633 | struct comedi_device *dev = counter->counter_dev->dev; |
1246f05b | 634 | enum ni_660x_register ni_660x_register = ni_gpct_to_660x_register(reg); |
0c26c7ed HS |
635 | unsigned chip = counter->chip_index; |
636 | ||
637 | return ni_660x_read_register(dev, chip, ni_660x_register); | |
58dd7c0a M |
638 | } |
639 | ||
0a85b6f0 MT |
640 | static inline struct mite_dma_descriptor_ring *mite_ring(struct ni_660x_private |
641 | *priv, | |
642 | struct ni_gpct | |
643 | *counter) | |
58dd7c0a | 644 | { |
0c26c7ed HS |
645 | unsigned chip = counter->chip_index; |
646 | ||
647 | return priv->mite_rings[chip][counter->counter_index]; | |
58dd7c0a M |
648 | } |
649 | ||
da91b269 | 650 | static inline void ni_660x_set_dma_channel(struct comedi_device *dev, |
0a85b6f0 MT |
651 | unsigned mite_channel, |
652 | struct ni_gpct *counter) | |
58dd7c0a | 653 | { |
8c12ec26 | 654 | struct ni_660x_private *devpriv = dev->private; |
0c26c7ed | 655 | unsigned chip = counter->chip_index; |
58dd7c0a | 656 | unsigned long flags; |
8c12ec26 HS |
657 | |
658 | spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags); | |
0c26c7ed | 659 | devpriv->dma_configuration_soft_copies[chip] &= |
d783a20e | 660 | ~dma_select_mask(mite_channel); |
0c26c7ed | 661 | devpriv->dma_configuration_soft_copies[chip] |= |
d783a20e | 662 | dma_select_bits(mite_channel, counter->counter_index); |
0c26c7ed HS |
663 | ni_660x_write_register(dev, chip, |
664 | devpriv->dma_configuration_soft_copies[chip] | | |
1246f05b | 665 | dma_reset_bit(mite_channel), NI660X_DMA_CFG); |
58dd7c0a | 666 | mmiowb(); |
8c12ec26 | 667 | spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags); |
58dd7c0a M |
668 | } |
669 | ||
da91b269 | 670 | static inline void ni_660x_unset_dma_channel(struct comedi_device *dev, |
0a85b6f0 MT |
671 | unsigned mite_channel, |
672 | struct ni_gpct *counter) | |
58dd7c0a | 673 | { |
8c12ec26 | 674 | struct ni_660x_private *devpriv = dev->private; |
0c26c7ed | 675 | unsigned chip = counter->chip_index; |
58dd7c0a | 676 | unsigned long flags; |
8c12ec26 HS |
677 | |
678 | spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags); | |
0c26c7ed | 679 | devpriv->dma_configuration_soft_copies[chip] &= |
0a85b6f0 | 680 | ~dma_select_mask(mite_channel); |
0c26c7ed | 681 | devpriv->dma_configuration_soft_copies[chip] |= |
0a85b6f0 | 682 | dma_select_bits(mite_channel, dma_selection_none); |
0c26c7ed HS |
683 | ni_660x_write_register(dev, chip, |
684 | devpriv->dma_configuration_soft_copies[chip], | |
685 | NI660X_DMA_CFG); | |
58dd7c0a | 686 | mmiowb(); |
8c12ec26 | 687 | spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags); |
58dd7c0a M |
688 | } |
689 | ||
da91b269 | 690 | static int ni_660x_request_mite_channel(struct comedi_device *dev, |
0a85b6f0 MT |
691 | struct ni_gpct *counter, |
692 | enum comedi_io_direction direction) | |
58dd7c0a | 693 | { |
8c12ec26 | 694 | struct ni_660x_private *devpriv = dev->private; |
58dd7c0a M |
695 | unsigned long flags; |
696 | struct mite_channel *mite_chan; | |
697 | ||
8c12ec26 | 698 | spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
58dd7c0a | 699 | BUG_ON(counter->mite_chan); |
8c12ec26 HS |
700 | mite_chan = mite_request_channel(devpriv->mite, |
701 | mite_ring(devpriv, counter)); | |
307da4b2 | 702 | if (!mite_chan) { |
8c12ec26 | 703 | spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
22bc059e HS |
704 | dev_err(dev->class_dev, |
705 | "failed to reserve mite dma channel for counter\n"); | |
58dd7c0a M |
706 | return -EBUSY; |
707 | } | |
708 | mite_chan->dir = direction; | |
709 | ni_tio_set_mite_channel(counter, mite_chan); | |
710 | ni_660x_set_dma_channel(dev, mite_chan->channel, counter); | |
8c12ec26 | 711 | spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
58dd7c0a M |
712 | return 0; |
713 | } | |
714 | ||
ce48a911 HS |
715 | static void ni_660x_release_mite_channel(struct comedi_device *dev, |
716 | struct ni_gpct *counter) | |
58dd7c0a | 717 | { |
8c12ec26 | 718 | struct ni_660x_private *devpriv = dev->private; |
58dd7c0a M |
719 | unsigned long flags; |
720 | ||
8c12ec26 | 721 | spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
58dd7c0a M |
722 | if (counter->mite_chan) { |
723 | struct mite_channel *mite_chan = counter->mite_chan; | |
724 | ||
725 | ni_660x_unset_dma_channel(dev, mite_chan->channel, counter); | |
726 | ni_tio_set_mite_channel(counter, NULL); | |
727 | mite_release_channel(mite_chan); | |
728 | } | |
8c12ec26 | 729 | spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
58dd7c0a M |
730 | } |
731 | ||
da91b269 | 732 | static int ni_660x_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
58dd7c0a | 733 | { |
00edbc31 | 734 | struct ni_gpct *counter = s->private; |
58dd7c0a M |
735 | int retval; |
736 | ||
58dd7c0a M |
737 | retval = ni_660x_request_mite_channel(dev, counter, COMEDI_INPUT); |
738 | if (retval) { | |
22bc059e HS |
739 | dev_err(dev->class_dev, |
740 | "no dma channel available for use by counter\n"); | |
58dd7c0a M |
741 | return retval; |
742 | } | |
f8cfd0eb | 743 | ni_tio_acknowledge(counter); |
58dd7c0a | 744 | |
16cc181d | 745 | return ni_tio_cmd(dev, s); |
58dd7c0a M |
746 | } |
747 | ||
da91b269 | 748 | static int ni_660x_cancel(struct comedi_device *dev, struct comedi_subdevice *s) |
58dd7c0a | 749 | { |
00edbc31 | 750 | struct ni_gpct *counter = s->private; |
58dd7c0a M |
751 | int retval; |
752 | ||
753 | retval = ni_tio_cancel(counter); | |
754 | ni_660x_release_mite_channel(dev, counter); | |
755 | return retval; | |
756 | } | |
757 | ||
01b6442b | 758 | static void set_tio_counterswap(struct comedi_device *dev, int chip) |
58dd7c0a | 759 | { |
01b6442b HS |
760 | unsigned bits = 0; |
761 | ||
762 | /* | |
763 | * See P. 3.5 of the Register-Level Programming manual. | |
764 | * The CounterSwap bit has to be set on the second chip, | |
765 | * otherwise it will try to use the same pins as the | |
766 | * first chip. | |
58dd7c0a | 767 | */ |
01b6442b HS |
768 | if (chip) |
769 | bits = CounterSwap; | |
770 | ||
771 | ni_660x_write_register(dev, chip, bits, NI660X_CLK_CFG); | |
58dd7c0a M |
772 | } |
773 | ||
da91b269 | 774 | static void ni_660x_handle_gpct_interrupt(struct comedi_device *dev, |
0a85b6f0 | 775 | struct comedi_subdevice *s) |
58dd7c0a | 776 | { |
00edbc31 HS |
777 | struct ni_gpct *counter = s->private; |
778 | ||
779 | ni_tio_handle_interrupt(counter, s); | |
9e1a0824 | 780 | comedi_handle_events(dev, s); |
58dd7c0a M |
781 | } |
782 | ||
70265d24 | 783 | static irqreturn_t ni_660x_interrupt(int irq, void *d) |
58dd7c0a | 784 | { |
71b5f4f1 | 785 | struct comedi_device *dev = d; |
8c12ec26 | 786 | struct ni_660x_private *devpriv = dev->private; |
34c43922 | 787 | struct comedi_subdevice *s; |
58dd7c0a | 788 | unsigned i; |
894db119 | 789 | unsigned long flags; |
58dd7c0a | 790 | |
a7401cdd | 791 | if (!dev->attached) |
58dd7c0a | 792 | return IRQ_NONE; |
894db119 | 793 | /* lock to avoid race with comedi_poll */ |
8c12ec26 | 794 | spin_lock_irqsave(&devpriv->interrupt_lock, flags); |
58dd7c0a M |
795 | smp_mb(); |
796 | for (i = 0; i < ni_660x_num_counters(dev); ++i) { | |
41e862f3 | 797 | s = &dev->subdevices[NI_660X_GPCT_SUBDEV(i)]; |
58dd7c0a M |
798 | ni_660x_handle_gpct_interrupt(dev, s); |
799 | } | |
8c12ec26 | 800 | spin_unlock_irqrestore(&devpriv->interrupt_lock, flags); |
58dd7c0a M |
801 | return IRQ_HANDLED; |
802 | } | |
803 | ||
894db119 FMH |
804 | static int ni_660x_input_poll(struct comedi_device *dev, |
805 | struct comedi_subdevice *s) | |
806 | { | |
8c12ec26 | 807 | struct ni_660x_private *devpriv = dev->private; |
00edbc31 | 808 | struct ni_gpct *counter = s->private; |
894db119 | 809 | unsigned long flags; |
8c12ec26 | 810 | |
894db119 | 811 | /* lock to avoid race with comedi_poll */ |
8c12ec26 | 812 | spin_lock_irqsave(&devpriv->interrupt_lock, flags); |
74f63db7 | 813 | mite_sync_input_dma(counter->mite_chan, s); |
8c12ec26 | 814 | spin_unlock_irqrestore(&devpriv->interrupt_lock, flags); |
e9edef3a | 815 | return comedi_buf_read_n_available(s); |
894db119 FMH |
816 | } |
817 | ||
0a85b6f0 | 818 | static int ni_660x_buf_change(struct comedi_device *dev, |
d546b896 | 819 | struct comedi_subdevice *s) |
58dd7c0a | 820 | { |
8c12ec26 | 821 | struct ni_660x_private *devpriv = dev->private; |
00edbc31 | 822 | struct ni_gpct *counter = s->private; |
58dd7c0a M |
823 | int ret; |
824 | ||
b74e635d | 825 | ret = mite_buf_change(mite_ring(devpriv, counter), s); |
58dd7c0a M |
826 | if (ret < 0) |
827 | return ret; | |
828 | ||
829 | return 0; | |
830 | } | |
831 | ||
da91b269 | 832 | static int ni_660x_allocate_private(struct comedi_device *dev) |
58dd7c0a | 833 | { |
8c12ec26 | 834 | struct ni_660x_private *devpriv; |
58dd7c0a M |
835 | unsigned i; |
836 | ||
0bdab509 | 837 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
c34fa261 HS |
838 | if (!devpriv) |
839 | return -ENOMEM; | |
c3744138 | 840 | |
8c12ec26 HS |
841 | spin_lock_init(&devpriv->mite_channel_lock); |
842 | spin_lock_init(&devpriv->interrupt_lock); | |
843 | spin_lock_init(&devpriv->soft_reg_copy_lock); | |
900b7808 | 844 | for (i = 0; i < NUM_PFI_CHANNELS; ++i) |
8c12ec26 | 845 | devpriv->pfi_output_selects[i] = pfi_output_select_counter; |
900b7808 | 846 | |
58dd7c0a M |
847 | return 0; |
848 | } | |
849 | ||
da91b269 | 850 | static int ni_660x_alloc_mite_rings(struct comedi_device *dev) |
58dd7c0a | 851 | { |
da8e2a52 | 852 | const struct ni_660x_board *board = dev->board_ptr; |
8c12ec26 | 853 | struct ni_660x_private *devpriv = dev->private; |
58dd7c0a M |
854 | unsigned i; |
855 | unsigned j; | |
856 | ||
9186ccde | 857 | for (i = 0; i < board->n_chips; ++i) { |
58dd7c0a | 858 | for (j = 0; j < counters_per_chip; ++j) { |
8c12ec26 HS |
859 | devpriv->mite_rings[i][j] = |
860 | mite_alloc_ring(devpriv->mite); | |
307da4b2 | 861 | if (!devpriv->mite_rings[i][j]) |
58dd7c0a | 862 | return -ENOMEM; |
58dd7c0a M |
863 | } |
864 | } | |
865 | return 0; | |
866 | } | |
867 | ||
da91b269 | 868 | static void ni_660x_free_mite_rings(struct comedi_device *dev) |
58dd7c0a | 869 | { |
da8e2a52 | 870 | const struct ni_660x_board *board = dev->board_ptr; |
8c12ec26 | 871 | struct ni_660x_private *devpriv = dev->private; |
58dd7c0a M |
872 | unsigned i; |
873 | unsigned j; | |
874 | ||
9186ccde | 875 | for (i = 0; i < board->n_chips; ++i) { |
900b7808 | 876 | for (j = 0; j < counters_per_chip; ++j) |
8c12ec26 | 877 | mite_free_ring(devpriv->mite_rings[i][j]); |
58dd7c0a M |
878 | } |
879 | } | |
880 | ||
da91b269 | 881 | static void init_tio_chip(struct comedi_device *dev, int chipset) |
58dd7c0a | 882 | { |
8c12ec26 | 883 | struct ni_660x_private *devpriv = dev->private; |
58dd7c0a M |
884 | unsigned i; |
885 | ||
2696fb57 | 886 | /* init dma configuration register */ |
8c12ec26 | 887 | devpriv->dma_configuration_soft_copies[chipset] = 0; |
58dd7c0a | 888 | for (i = 0; i < MAX_DMA_CHANNEL; ++i) { |
8c12ec26 | 889 | devpriv->dma_configuration_soft_copies[chipset] |= |
0a85b6f0 | 890 | dma_select_bits(i, dma_selection_none) & dma_select_mask(i); |
58dd7c0a M |
891 | } |
892 | ni_660x_write_register(dev, chipset, | |
8c12ec26 | 893 | devpriv->dma_configuration_soft_copies[chipset], |
1246f05b | 894 | NI660X_DMA_CFG); |
900b7808 | 895 | for (i = 0; i < NUM_PFI_CHANNELS; ++i) |
58dd7c0a | 896 | ni_660x_write_register(dev, chipset, 0, IOConfigReg(i)); |
58dd7c0a M |
897 | } |
898 | ||
da91b269 | 899 | static int ni_660x_dio_insn_bits(struct comedi_device *dev, |
0a85b6f0 MT |
900 | struct comedi_subdevice *s, |
901 | struct comedi_insn *insn, unsigned int *data) | |
58dd7c0a M |
902 | { |
903 | unsigned base_bitfield_channel = CR_CHAN(insn->chanspec); | |
904 | ||
2696fb57 | 905 | /* Check if we have to write some bits */ |
58dd7c0a M |
906 | if (data[0]) { |
907 | s->state &= ~(data[0] << base_bitfield_channel); | |
908 | s->state |= (data[0] & data[1]) << base_bitfield_channel; | |
909 | /* Write out the new digital output lines */ | |
1246f05b | 910 | ni_660x_write_register(dev, 0, s->state, NI660X_DIO32_OUTPUT); |
58dd7c0a M |
911 | } |
912 | /* on return, data[1] contains the value of the digital | |
913 | * input and output lines. */ | |
1246f05b HS |
914 | data[1] = (ni_660x_read_register(dev, 0, NI660X_DIO32_INPUT) >> |
915 | base_bitfield_channel); | |
916 | ||
a2714e3e | 917 | return insn->n; |
58dd7c0a M |
918 | } |
919 | ||
0a85b6f0 MT |
920 | static void ni_660x_select_pfi_output(struct comedi_device *dev, |
921 | unsigned pfi_channel, | |
922 | unsigned output_select) | |
58dd7c0a | 923 | { |
da8e2a52 | 924 | const struct ni_660x_board *board = dev->board_ptr; |
58dd7c0a M |
925 | static const unsigned counter_4_7_first_pfi = 8; |
926 | static const unsigned counter_4_7_last_pfi = 23; | |
927 | unsigned active_chipset = 0; | |
928 | unsigned idle_chipset = 0; | |
929 | unsigned active_bits; | |
930 | unsigned idle_bits; | |
931 | ||
9186ccde | 932 | if (board->n_chips > 1) { |
53106ae6 | 933 | if (output_select == pfi_output_select_counter && |
0a85b6f0 MT |
934 | pfi_channel >= counter_4_7_first_pfi && |
935 | pfi_channel <= counter_4_7_last_pfi) { | |
58dd7c0a M |
936 | active_chipset = 1; |
937 | idle_chipset = 0; | |
0a85b6f0 | 938 | } else { |
58dd7c0a M |
939 | active_chipset = 0; |
940 | idle_chipset = 1; | |
941 | } | |
942 | } | |
943 | ||
53106ae6 | 944 | if (idle_chipset != active_chipset) { |
0a85b6f0 MT |
945 | idle_bits = |
946 | ni_660x_read_register(dev, idle_chipset, | |
947 | IOConfigReg(pfi_channel)); | |
58dd7c0a | 948 | idle_bits &= ~pfi_output_select_mask(pfi_channel); |
0a85b6f0 MT |
949 | idle_bits |= |
950 | pfi_output_select_bits(pfi_channel, | |
951 | pfi_output_select_high_Z); | |
952 | ni_660x_write_register(dev, idle_chipset, idle_bits, | |
953 | IOConfigReg(pfi_channel)); | |
58dd7c0a M |
954 | } |
955 | ||
0a85b6f0 MT |
956 | active_bits = |
957 | ni_660x_read_register(dev, active_chipset, | |
958 | IOConfigReg(pfi_channel)); | |
58dd7c0a M |
959 | active_bits &= ~pfi_output_select_mask(pfi_channel); |
960 | active_bits |= pfi_output_select_bits(pfi_channel, output_select); | |
0a85b6f0 MT |
961 | ni_660x_write_register(dev, active_chipset, active_bits, |
962 | IOConfigReg(pfi_channel)); | |
58dd7c0a M |
963 | } |
964 | ||
da91b269 | 965 | static int ni_660x_set_pfi_routing(struct comedi_device *dev, unsigned chan, |
0a85b6f0 | 966 | unsigned source) |
58dd7c0a | 967 | { |
8c12ec26 HS |
968 | struct ni_660x_private *devpriv = dev->private; |
969 | ||
58dd7c0a M |
970 | if (source > num_pfi_output_selects) |
971 | return -EINVAL; | |
972 | if (source == pfi_output_select_high_Z) | |
973 | return -EINVAL; | |
974 | if (chan < min_counter_pfi_chan) { | |
975 | if (source == pfi_output_select_counter) | |
976 | return -EINVAL; | |
977 | } else if (chan > max_dio_pfi_chan) { | |
978 | if (source == pfi_output_select_do) | |
979 | return -EINVAL; | |
980 | } | |
58dd7c0a | 981 | |
8c12ec26 HS |
982 | devpriv->pfi_output_selects[chan] = source; |
983 | if (devpriv->pfi_direction_bits & (((uint64_t) 1) << chan)) | |
58dd7c0a | 984 | ni_660x_select_pfi_output(dev, chan, |
8c12ec26 | 985 | devpriv->pfi_output_selects[chan]); |
58dd7c0a M |
986 | return 0; |
987 | } | |
988 | ||
da91b269 | 989 | static int ni_660x_dio_insn_config(struct comedi_device *dev, |
0a85b6f0 | 990 | struct comedi_subdevice *s, |
56c645ff HS |
991 | struct comedi_insn *insn, |
992 | unsigned int *data) | |
58dd7c0a | 993 | { |
8c12ec26 | 994 | struct ni_660x_private *devpriv = dev->private; |
56c645ff HS |
995 | unsigned int chan = CR_CHAN(insn->chanspec); |
996 | uint64_t bit = 1ULL << chan; | |
82327aaf | 997 | unsigned int val; |
56c645ff | 998 | int ret; |
58dd7c0a M |
999 | |
1000 | switch (data[0]) { | |
1001 | case INSN_CONFIG_DIO_OUTPUT: | |
56c645ff | 1002 | devpriv->pfi_direction_bits |= bit; |
58dd7c0a | 1003 | ni_660x_select_pfi_output(dev, chan, |
8c12ec26 | 1004 | devpriv->pfi_output_selects[chan]); |
58dd7c0a | 1005 | break; |
56c645ff | 1006 | |
58dd7c0a | 1007 | case INSN_CONFIG_DIO_INPUT: |
56c645ff | 1008 | devpriv->pfi_direction_bits &= ~bit; |
58dd7c0a M |
1009 | ni_660x_select_pfi_output(dev, chan, pfi_output_select_high_Z); |
1010 | break; | |
56c645ff | 1011 | |
58dd7c0a | 1012 | case INSN_CONFIG_DIO_QUERY: |
56c645ff HS |
1013 | data[1] = (devpriv->pfi_direction_bits & bit) ? COMEDI_OUTPUT |
1014 | : COMEDI_INPUT; | |
1015 | break; | |
1016 | ||
58dd7c0a | 1017 | case INSN_CONFIG_SET_ROUTING: |
56c645ff HS |
1018 | ret = ni_660x_set_pfi_routing(dev, chan, data[1]); |
1019 | if (ret) | |
1020 | return ret; | |
58dd7c0a | 1021 | break; |
56c645ff | 1022 | |
58dd7c0a | 1023 | case INSN_CONFIG_GET_ROUTING: |
32bd027d | 1024 | data[1] = devpriv->pfi_output_selects[chan]; |
58dd7c0a | 1025 | break; |
56c645ff | 1026 | |
58dd7c0a | 1027 | case INSN_CONFIG_FILTER: |
82327aaf HS |
1028 | val = ni_660x_read_register(dev, 0, IOConfigReg(chan)); |
1029 | val &= ~pfi_input_select_mask(chan); | |
1030 | val |= pfi_input_select_bits(chan, data[1]); | |
1031 | ni_660x_write_register(dev, 0, val, IOConfigReg(chan)); | |
58dd7c0a | 1032 | break; |
56c645ff | 1033 | |
58dd7c0a M |
1034 | default: |
1035 | return -EINVAL; | |
95cd17c9 | 1036 | } |
56c645ff HS |
1037 | |
1038 | return insn->n; | |
58dd7c0a | 1039 | } |
3c323c01 | 1040 | |
a690b7e5 | 1041 | static int ni_660x_auto_attach(struct comedi_device *dev, |
97bcce5a | 1042 | unsigned long context) |
990b9eed | 1043 | { |
750af5e5 | 1044 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
97bcce5a | 1045 | const struct ni_660x_board *board = NULL; |
990b9eed HS |
1046 | struct ni_660x_private *devpriv; |
1047 | struct comedi_subdevice *s; | |
1048 | int ret; | |
1049 | unsigned i; | |
1050 | unsigned global_interrupt_config_bits; | |
1051 | ||
97bcce5a HS |
1052 | if (context < ARRAY_SIZE(ni_660x_boards)) |
1053 | board = &ni_660x_boards[context]; | |
1054 | if (!board) | |
1055 | return -ENODEV; | |
1056 | dev->board_ptr = board; | |
1057 | dev->board_name = board->name; | |
1058 | ||
818f569f HS |
1059 | ret = comedi_pci_enable(dev); |
1060 | if (ret) | |
1061 | return ret; | |
818f569f | 1062 | |
990b9eed HS |
1063 | ret = ni_660x_allocate_private(dev); |
1064 | if (ret < 0) | |
1065 | return ret; | |
1066 | devpriv = dev->private; | |
1067 | ||
990b9eed HS |
1068 | devpriv->mite = mite_alloc(pcidev); |
1069 | if (!devpriv->mite) | |
1070 | return -ENOMEM; | |
1071 | ||
3bb7c3ab HS |
1072 | ret = mite_setup2(dev, devpriv->mite, true); |
1073 | if (ret < 0) | |
990b9eed | 1074 | return ret; |
990b9eed HS |
1075 | |
1076 | ret = ni_660x_alloc_mite_rings(dev); | |
1077 | if (ret < 0) | |
1078 | return ret; | |
1079 | ||
1080 | ret = comedi_alloc_subdevices(dev, 2 + NI_660X_MAX_NUM_COUNTERS); | |
1081 | if (ret) | |
1082 | return ret; | |
1083 | ||
1084 | s = &dev->subdevices[0]; | |
1085 | /* Old GENERAL-PURPOSE COUNTER/TIME (GPCT) subdevice, no longer used */ | |
1086 | s->type = COMEDI_SUBD_UNUSED; | |
1087 | ||
1088 | s = &dev->subdevices[NI_660X_DIO_SUBDEV]; | |
1089 | /* DIGITAL I/O SUBDEVICE */ | |
1090 | s->type = COMEDI_SUBD_DIO; | |
1091 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
1092 | s->n_chan = NUM_PFI_CHANNELS; | |
1093 | s->maxdata = 1; | |
1094 | s->range_table = &range_digital; | |
1095 | s->insn_bits = ni_660x_dio_insn_bits; | |
1096 | s->insn_config = ni_660x_dio_insn_config; | |
990b9eed HS |
1097 | /* we use the ioconfig registers to control dio direction, so zero |
1098 | output enables in stc dio control reg */ | |
1246f05b | 1099 | ni_660x_write_register(dev, 0, 0, NI660X_STC_DIO_CONTROL); |
990b9eed HS |
1100 | |
1101 | devpriv->counter_dev = ni_gpct_device_construct(dev, | |
1102 | &ni_gpct_write_register, | |
1103 | &ni_gpct_read_register, | |
1104 | ni_gpct_variant_660x, | |
1105 | ni_660x_num_counters | |
1106 | (dev)); | |
307da4b2 | 1107 | if (!devpriv->counter_dev) |
990b9eed HS |
1108 | return -ENOMEM; |
1109 | for (i = 0; i < NI_660X_MAX_NUM_COUNTERS; ++i) { | |
1110 | s = &dev->subdevices[NI_660X_GPCT_SUBDEV(i)]; | |
1111 | if (i < ni_660x_num_counters(dev)) { | |
1112 | s->type = COMEDI_SUBD_COUNTER; | |
56e9bef5 HS |
1113 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE | |
1114 | SDF_LSAMPL | SDF_CMD_READ; | |
990b9eed HS |
1115 | s->n_chan = 3; |
1116 | s->maxdata = 0xffffffff; | |
9014d816 | 1117 | s->insn_read = ni_tio_insn_read; |
10f74377 | 1118 | s->insn_write = ni_tio_insn_write; |
cac04c0f | 1119 | s->insn_config = ni_tio_insn_config; |
990b9eed HS |
1120 | s->do_cmd = &ni_660x_cmd; |
1121 | s->len_chanlist = 1; | |
c3f3b431 | 1122 | s->do_cmdtest = ni_tio_cmdtest; |
990b9eed HS |
1123 | s->cancel = &ni_660x_cancel; |
1124 | s->poll = &ni_660x_input_poll; | |
1125 | s->async_dma_dir = DMA_BIDIRECTIONAL; | |
1126 | s->buf_change = &ni_660x_buf_change; | |
1127 | s->private = &devpriv->counter_dev->counters[i]; | |
1128 | ||
1129 | devpriv->counter_dev->counters[i].chip_index = | |
1130 | i / counters_per_chip; | |
1131 | devpriv->counter_dev->counters[i].counter_index = | |
1132 | i % counters_per_chip; | |
1133 | } else { | |
1134 | s->type = COMEDI_SUBD_UNUSED; | |
1135 | } | |
1136 | } | |
9186ccde | 1137 | for (i = 0; i < board->n_chips; ++i) |
990b9eed HS |
1138 | init_tio_chip(dev, i); |
1139 | ||
1140 | for (i = 0; i < ni_660x_num_counters(dev); ++i) | |
1141 | ni_tio_init_counter(&devpriv->counter_dev->counters[i]); | |
1142 | ||
1143 | for (i = 0; i < NUM_PFI_CHANNELS; ++i) { | |
1144 | if (i < min_counter_pfi_chan) | |
1145 | ni_660x_set_pfi_routing(dev, i, pfi_output_select_do); | |
1146 | else | |
1147 | ni_660x_set_pfi_routing(dev, i, | |
1148 | pfi_output_select_counter); | |
1149 | ni_660x_select_pfi_output(dev, i, pfi_output_select_high_Z); | |
1150 | } | |
1151 | /* to be safe, set counterswap bits on tio chips after all the counter | |
1152 | outputs have been set to high impedance mode */ | |
9186ccde | 1153 | for (i = 0; i < board->n_chips; ++i) |
990b9eed HS |
1154 | set_tio_counterswap(dev, i); |
1155 | ||
71e06874 HS |
1156 | ret = request_irq(pcidev->irq, ni_660x_interrupt, IRQF_SHARED, |
1157 | dev->board_name, dev); | |
990b9eed HS |
1158 | if (ret < 0) { |
1159 | dev_warn(dev->class_dev, " irq not available\n"); | |
1160 | return ret; | |
1161 | } | |
ba9d29fe | 1162 | dev->irq = pcidev->irq; |
990b9eed | 1163 | global_interrupt_config_bits = Global_Int_Enable_Bit; |
9186ccde | 1164 | if (board->n_chips > 1) |
990b9eed HS |
1165 | global_interrupt_config_bits |= Cascade_Int_Enable_Bit; |
1166 | ni_660x_write_register(dev, 0, global_interrupt_config_bits, | |
1246f05b | 1167 | NI660X_GLOBAL_INT_CFG); |
c93999c2 | 1168 | |
990b9eed HS |
1169 | return 0; |
1170 | } | |
1171 | ||
1172 | static void ni_660x_detach(struct comedi_device *dev) | |
1173 | { | |
1174 | struct ni_660x_private *devpriv = dev->private; | |
1175 | ||
1176 | if (dev->irq) | |
1177 | free_irq(dev->irq, dev); | |
1178 | if (devpriv) { | |
1179 | if (devpriv->counter_dev) | |
1180 | ni_gpct_device_destroy(devpriv->counter_dev); | |
b876e985 HS |
1181 | ni_660x_free_mite_rings(dev); |
1182 | mite_detach(devpriv->mite); | |
990b9eed | 1183 | } |
5f8a5f4f HS |
1184 | if (dev->mmio) |
1185 | iounmap(dev->mmio); | |
7f072f54 | 1186 | comedi_pci_disable(dev); |
990b9eed HS |
1187 | } |
1188 | ||
1189 | static struct comedi_driver ni_660x_driver = { | |
1190 | .driver_name = "ni_660x", | |
1191 | .module = THIS_MODULE, | |
750af5e5 | 1192 | .auto_attach = ni_660x_auto_attach, |
990b9eed HS |
1193 | .detach = ni_660x_detach, |
1194 | }; | |
1195 | ||
a690b7e5 | 1196 | static int ni_660x_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 1197 | const struct pci_device_id *id) |
990b9eed | 1198 | { |
b8f4ac23 | 1199 | return comedi_pci_auto_config(dev, &ni_660x_driver, id->driver_data); |
990b9eed HS |
1200 | } |
1201 | ||
41e043fc | 1202 | static const struct pci_device_id ni_660x_pci_table[] = { |
97bcce5a HS |
1203 | { PCI_VDEVICE(NI, 0x1310), BOARD_PCI6602 }, |
1204 | { PCI_VDEVICE(NI, 0x1360), BOARD_PXI6602 }, | |
1205 | { PCI_VDEVICE(NI, 0x2c60), BOARD_PCI6601 }, | |
1206 | { PCI_VDEVICE(NI, 0x2cc0), BOARD_PXI6608 }, | |
8bdfefb7 | 1207 | { PCI_VDEVICE(NI, 0x1e40), BOARD_PXI6624 }, |
97bcce5a | 1208 | { 0 } |
990b9eed HS |
1209 | }; |
1210 | MODULE_DEVICE_TABLE(pci, ni_660x_pci_table); | |
1211 | ||
1212 | static struct pci_driver ni_660x_pci_driver = { | |
1213 | .name = "ni_660x", | |
1214 | .id_table = ni_660x_pci_table, | |
1215 | .probe = ni_660x_pci_probe, | |
9901a4d7 | 1216 | .remove = comedi_pci_auto_unconfig, |
990b9eed HS |
1217 | }; |
1218 | module_comedi_pci_driver(ni_660x_driver, ni_660x_pci_driver); | |
1219 | ||
3c323c01 IA |
1220 | MODULE_AUTHOR("Comedi http://www.comedi.org"); |
1221 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
1222 | MODULE_LICENSE("GPL"); |