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. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program; if not, write to the Free Software | |
17 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
18 | */ | |
19 | ||
20 | /* | |
21 | Driver: ni_660x | |
22 | Description: National Instruments 660x counter/timer boards | |
23 | Devices: | |
24 | [National Instruments] PCI-6601 (ni_660x), PCI-6602, PXI-6602, | |
25 | PXI-6608 | |
26 | Author: J.P. Mellor <jpmellor@rose-hulman.edu>, | |
27 | Herman.Bruyninckx@mech.kuleuven.ac.be, | |
28 | Wim.Meeussen@mech.kuleuven.ac.be, | |
29 | Klaas.Gadeyne@mech.kuleuven.ac.be, | |
30 | Frank Mori Hess <fmhess@users.sourceforge.net> | |
31 | Updated: Thu Oct 18 12:56:06 EDT 2007 | |
32 | Status: experimental | |
33 | ||
34 | Encoders work. PulseGeneration (both single pulse and pulse train) | |
35 | works. Buffered commands work for input but not output. | |
36 | ||
37 | References: | |
38 | DAQ 660x Register-Level Programmer Manual (NI 370505A-01) | |
39 | DAQ 6601/6602 User Manual (NI 322137B-01) | |
40 | ||
41 | */ | |
42 | ||
25436dc9 | 43 | #include <linux/interrupt.h> |
58dd7c0a M |
44 | #include "../comedidev.h" |
45 | #include "mite.h" | |
46 | #include "ni_tio.h" | |
47 | ||
48 | enum ni_660x_constants { | |
49 | min_counter_pfi_chan = 8, | |
50 | max_dio_pfi_chan = 31, | |
51 | counters_per_chip = 4 | |
52 | }; | |
53 | ||
54 | #define NUM_PFI_CHANNELS 40 | |
900b7808 BA |
55 | /* really there are only up to 3 dma channels, but the register layout allows |
56 | for 4 */ | |
58dd7c0a M |
57 | #define MAX_DMA_CHANNEL 4 |
58 | ||
59 | /* See Register-Level Programmer Manual page 3.1 */ | |
251411cf | 60 | enum NI_660x_Register { |
58dd7c0a M |
61 | G0InterruptAcknowledge, |
62 | G0StatusRegister, | |
63 | G1InterruptAcknowledge, | |
64 | G1StatusRegister, | |
65 | G01StatusRegister, | |
66 | G0CommandRegister, | |
67 | STCDIOParallelInput, | |
68 | G1CommandRegister, | |
69 | G0HWSaveRegister, | |
70 | G1HWSaveRegister, | |
71 | STCDIOOutput, | |
72 | STCDIOControl, | |
73 | G0SWSaveRegister, | |
74 | G1SWSaveRegister, | |
75 | G0ModeRegister, | |
76 | G01JointStatus1Register, | |
77 | G1ModeRegister, | |
78 | STCDIOSerialInput, | |
79 | G0LoadARegister, | |
80 | G01JointStatus2Register, | |
81 | G0LoadBRegister, | |
82 | G1LoadARegister, | |
83 | G1LoadBRegister, | |
84 | G0InputSelectRegister, | |
85 | G1InputSelectRegister, | |
86 | G0AutoincrementRegister, | |
87 | G1AutoincrementRegister, | |
88 | G01JointResetRegister, | |
89 | G0InterruptEnable, | |
90 | G1InterruptEnable, | |
91 | G0CountingModeRegister, | |
92 | G1CountingModeRegister, | |
93 | G0SecondGateRegister, | |
94 | G1SecondGateRegister, | |
95 | G0DMAConfigRegister, | |
96 | G0DMAStatusRegister, | |
97 | G1DMAConfigRegister, | |
98 | G1DMAStatusRegister, | |
99 | G2InterruptAcknowledge, | |
100 | G2StatusRegister, | |
101 | G3InterruptAcknowledge, | |
102 | G3StatusRegister, | |
103 | G23StatusRegister, | |
104 | G2CommandRegister, | |
105 | G3CommandRegister, | |
106 | G2HWSaveRegister, | |
107 | G3HWSaveRegister, | |
108 | G2SWSaveRegister, | |
109 | G3SWSaveRegister, | |
110 | G2ModeRegister, | |
111 | G23JointStatus1Register, | |
112 | G3ModeRegister, | |
113 | G2LoadARegister, | |
114 | G23JointStatus2Register, | |
115 | G2LoadBRegister, | |
116 | G3LoadARegister, | |
117 | G3LoadBRegister, | |
118 | G2InputSelectRegister, | |
119 | G3InputSelectRegister, | |
120 | G2AutoincrementRegister, | |
121 | G3AutoincrementRegister, | |
122 | G23JointResetRegister, | |
123 | G2InterruptEnable, | |
124 | G3InterruptEnable, | |
125 | G2CountingModeRegister, | |
126 | G3CountingModeRegister, | |
127 | G3SecondGateRegister, | |
128 | G2SecondGateRegister, | |
129 | G2DMAConfigRegister, | |
130 | G2DMAStatusRegister, | |
131 | G3DMAConfigRegister, | |
132 | G3DMAStatusRegister, | |
133 | DIO32Input, | |
134 | DIO32Output, | |
135 | ClockConfigRegister, | |
136 | GlobalInterruptStatusRegister, | |
137 | DMAConfigRegister, | |
138 | GlobalInterruptConfigRegister, | |
139 | IOConfigReg0_1, | |
140 | IOConfigReg2_3, | |
141 | IOConfigReg4_5, | |
142 | IOConfigReg6_7, | |
143 | IOConfigReg8_9, | |
144 | IOConfigReg10_11, | |
145 | IOConfigReg12_13, | |
146 | IOConfigReg14_15, | |
147 | IOConfigReg16_17, | |
148 | IOConfigReg18_19, | |
149 | IOConfigReg20_21, | |
150 | IOConfigReg22_23, | |
151 | IOConfigReg24_25, | |
152 | IOConfigReg26_27, | |
153 | IOConfigReg28_29, | |
154 | IOConfigReg30_31, | |
155 | IOConfigReg32_33, | |
156 | IOConfigReg34_35, | |
157 | IOConfigReg36_37, | |
158 | IOConfigReg38_39, | |
159 | NumRegisters, | |
251411cf | 160 | }; |
58dd7c0a M |
161 | |
162 | static inline unsigned IOConfigReg(unsigned pfi_channel) | |
163 | { | |
164 | unsigned reg = IOConfigReg0_1 + pfi_channel / 2; | |
165 | BUG_ON(reg > IOConfigReg38_39); | |
166 | return reg; | |
167 | } | |
168 | ||
169 | enum ni_660x_register_width { | |
170 | DATA_1B, | |
171 | DATA_2B, | |
172 | DATA_4B | |
173 | }; | |
174 | ||
175 | enum ni_660x_register_direction { | |
176 | NI_660x_READ, | |
177 | NI_660x_WRITE, | |
178 | NI_660x_READ_WRITE | |
179 | }; | |
180 | ||
181 | enum ni_660x_pfi_output_select { | |
182 | pfi_output_select_high_Z = 0, | |
183 | pfi_output_select_counter = 1, | |
184 | pfi_output_select_do = 2, | |
185 | num_pfi_output_selects | |
186 | }; | |
187 | ||
188 | enum ni_660x_subdevices { | |
189 | NI_660X_DIO_SUBDEV = 1, | |
190 | NI_660X_GPCT_SUBDEV_0 = 2 | |
191 | }; | |
192 | static inline unsigned NI_660X_GPCT_SUBDEV(unsigned index) | |
193 | { | |
194 | return NI_660X_GPCT_SUBDEV_0 + index; | |
195 | } | |
196 | ||
0cb5e8ff BP |
197 | struct NI_660xRegisterData { |
198 | ||
2696fb57 BP |
199 | const char *name; /* Register Name */ |
200 | int offset; /* Offset from base address from GPCT chip */ | |
58dd7c0a | 201 | enum ni_660x_register_direction direction; |
b02957d5 | 202 | enum ni_660x_register_width size; /* 1 byte, 2 bytes, or 4 bytes */ |
0cb5e8ff BP |
203 | }; |
204 | ||
0cb5e8ff | 205 | static const struct NI_660xRegisterData registerData[NumRegisters] = { |
58dd7c0a M |
206 | {"G0 Interrupt Acknowledge", 0x004, NI_660x_WRITE, DATA_2B}, |
207 | {"G0 Status Register", 0x004, NI_660x_READ, DATA_2B}, | |
208 | {"G1 Interrupt Acknowledge", 0x006, NI_660x_WRITE, DATA_2B}, | |
209 | {"G1 Status Register", 0x006, NI_660x_READ, DATA_2B}, | |
210 | {"G01 Status Register ", 0x008, NI_660x_READ, DATA_2B}, | |
211 | {"G0 Command Register", 0x00C, NI_660x_WRITE, DATA_2B}, | |
212 | {"STC DIO Parallel Input", 0x00E, NI_660x_READ, DATA_2B}, | |
213 | {"G1 Command Register", 0x00E, NI_660x_WRITE, DATA_2B}, | |
214 | {"G0 HW Save Register", 0x010, NI_660x_READ, DATA_4B}, | |
215 | {"G1 HW Save Register", 0x014, NI_660x_READ, DATA_4B}, | |
216 | {"STC DIO Output", 0x014, NI_660x_WRITE, DATA_2B}, | |
217 | {"STC DIO Control", 0x016, NI_660x_WRITE, DATA_2B}, | |
218 | {"G0 SW Save Register", 0x018, NI_660x_READ, DATA_4B}, | |
219 | {"G1 SW Save Register", 0x01C, NI_660x_READ, DATA_4B}, | |
220 | {"G0 Mode Register", 0x034, NI_660x_WRITE, DATA_2B}, | |
221 | {"G01 Joint Status 1 Register", 0x036, NI_660x_READ, DATA_2B}, | |
222 | {"G1 Mode Register", 0x036, NI_660x_WRITE, DATA_2B}, | |
223 | {"STC DIO Serial Input", 0x038, NI_660x_READ, DATA_2B}, | |
224 | {"G0 Load A Register", 0x038, NI_660x_WRITE, DATA_4B}, | |
225 | {"G01 Joint Status 2 Register", 0x03A, NI_660x_READ, DATA_2B}, | |
226 | {"G0 Load B Register", 0x03C, NI_660x_WRITE, DATA_4B}, | |
227 | {"G1 Load A Register", 0x040, NI_660x_WRITE, DATA_4B}, | |
228 | {"G1 Load B Register", 0x044, NI_660x_WRITE, DATA_4B}, | |
229 | {"G0 Input Select Register", 0x048, NI_660x_WRITE, DATA_2B}, | |
230 | {"G1 Input Select Register", 0x04A, NI_660x_WRITE, DATA_2B}, | |
231 | {"G0 Autoincrement Register", 0x088, NI_660x_WRITE, DATA_2B}, | |
232 | {"G1 Autoincrement Register", 0x08A, NI_660x_WRITE, DATA_2B}, | |
233 | {"G01 Joint Reset Register", 0x090, NI_660x_WRITE, DATA_2B}, | |
234 | {"G0 Interrupt Enable", 0x092, NI_660x_WRITE, DATA_2B}, | |
235 | {"G1 Interrupt Enable", 0x096, NI_660x_WRITE, DATA_2B}, | |
236 | {"G0 Counting Mode Register", 0x0B0, NI_660x_WRITE, DATA_2B}, | |
237 | {"G1 Counting Mode Register", 0x0B2, NI_660x_WRITE, DATA_2B}, | |
238 | {"G0 Second Gate Register", 0x0B4, NI_660x_WRITE, DATA_2B}, | |
239 | {"G1 Second Gate Register", 0x0B6, NI_660x_WRITE, DATA_2B}, | |
240 | {"G0 DMA Config Register", 0x0B8, NI_660x_WRITE, DATA_2B}, | |
241 | {"G0 DMA Status Register", 0x0B8, NI_660x_READ, DATA_2B}, | |
242 | {"G1 DMA Config Register", 0x0BA, NI_660x_WRITE, DATA_2B}, | |
243 | {"G1 DMA Status Register", 0x0BA, NI_660x_READ, DATA_2B}, | |
244 | {"G2 Interrupt Acknowledge", 0x104, NI_660x_WRITE, DATA_2B}, | |
245 | {"G2 Status Register", 0x104, NI_660x_READ, DATA_2B}, | |
246 | {"G3 Interrupt Acknowledge", 0x106, NI_660x_WRITE, DATA_2B}, | |
247 | {"G3 Status Register", 0x106, NI_660x_READ, DATA_2B}, | |
248 | {"G23 Status Register", 0x108, NI_660x_READ, DATA_2B}, | |
249 | {"G2 Command Register", 0x10C, NI_660x_WRITE, DATA_2B}, | |
250 | {"G3 Command Register", 0x10E, NI_660x_WRITE, DATA_2B}, | |
251 | {"G2 HW Save Register", 0x110, NI_660x_READ, DATA_4B}, | |
252 | {"G3 HW Save Register", 0x114, NI_660x_READ, DATA_4B}, | |
253 | {"G2 SW Save Register", 0x118, NI_660x_READ, DATA_4B}, | |
254 | {"G3 SW Save Register", 0x11C, NI_660x_READ, DATA_4B}, | |
255 | {"G2 Mode Register", 0x134, NI_660x_WRITE, DATA_2B}, | |
256 | {"G23 Joint Status 1 Register", 0x136, NI_660x_READ, DATA_2B}, | |
257 | {"G3 Mode Register", 0x136, NI_660x_WRITE, DATA_2B}, | |
258 | {"G2 Load A Register", 0x138, NI_660x_WRITE, DATA_4B}, | |
259 | {"G23 Joint Status 2 Register", 0x13A, NI_660x_READ, DATA_2B}, | |
260 | {"G2 Load B Register", 0x13C, NI_660x_WRITE, DATA_4B}, | |
261 | {"G3 Load A Register", 0x140, NI_660x_WRITE, DATA_4B}, | |
262 | {"G3 Load B Register", 0x144, NI_660x_WRITE, DATA_4B}, | |
263 | {"G2 Input Select Register", 0x148, NI_660x_WRITE, DATA_2B}, | |
264 | {"G3 Input Select Register", 0x14A, NI_660x_WRITE, DATA_2B}, | |
265 | {"G2 Autoincrement Register", 0x188, NI_660x_WRITE, DATA_2B}, | |
266 | {"G3 Autoincrement Register", 0x18A, NI_660x_WRITE, DATA_2B}, | |
267 | {"G23 Joint Reset Register", 0x190, NI_660x_WRITE, DATA_2B}, | |
268 | {"G2 Interrupt Enable", 0x192, NI_660x_WRITE, DATA_2B}, | |
269 | {"G3 Interrupt Enable", 0x196, NI_660x_WRITE, DATA_2B}, | |
270 | {"G2 Counting Mode Register", 0x1B0, NI_660x_WRITE, DATA_2B}, | |
271 | {"G3 Counting Mode Register", 0x1B2, NI_660x_WRITE, DATA_2B}, | |
272 | {"G3 Second Gate Register", 0x1B6, NI_660x_WRITE, DATA_2B}, | |
273 | {"G2 Second Gate Register", 0x1B4, NI_660x_WRITE, DATA_2B}, | |
274 | {"G2 DMA Config Register", 0x1B8, NI_660x_WRITE, DATA_2B}, | |
275 | {"G2 DMA Status Register", 0x1B8, NI_660x_READ, DATA_2B}, | |
276 | {"G3 DMA Config Register", 0x1BA, NI_660x_WRITE, DATA_2B}, | |
277 | {"G3 DMA Status Register", 0x1BA, NI_660x_READ, DATA_2B}, | |
278 | {"32 bit Digital Input", 0x414, NI_660x_READ, DATA_4B}, | |
279 | {"32 bit Digital Output", 0x510, NI_660x_WRITE, DATA_4B}, | |
280 | {"Clock Config Register", 0x73C, NI_660x_WRITE, DATA_4B}, | |
281 | {"Global Interrupt Status Register", 0x754, NI_660x_READ, DATA_4B}, | |
282 | {"DMA Configuration Register", 0x76C, NI_660x_WRITE, DATA_4B}, | |
283 | {"Global Interrupt Config Register", 0x770, NI_660x_WRITE, DATA_4B}, | |
284 | {"IO Config Register 0-1", 0x77C, NI_660x_READ_WRITE, DATA_2B}, | |
285 | {"IO Config Register 2-3", 0x77E, NI_660x_READ_WRITE, DATA_2B}, | |
286 | {"IO Config Register 4-5", 0x780, NI_660x_READ_WRITE, DATA_2B}, | |
287 | {"IO Config Register 6-7", 0x782, NI_660x_READ_WRITE, DATA_2B}, | |
288 | {"IO Config Register 8-9", 0x784, NI_660x_READ_WRITE, DATA_2B}, | |
289 | {"IO Config Register 10-11", 0x786, NI_660x_READ_WRITE, DATA_2B}, | |
290 | {"IO Config Register 12-13", 0x788, NI_660x_READ_WRITE, DATA_2B}, | |
291 | {"IO Config Register 14-15", 0x78A, NI_660x_READ_WRITE, DATA_2B}, | |
292 | {"IO Config Register 16-17", 0x78C, NI_660x_READ_WRITE, DATA_2B}, | |
293 | {"IO Config Register 18-19", 0x78E, NI_660x_READ_WRITE, DATA_2B}, | |
294 | {"IO Config Register 20-21", 0x790, NI_660x_READ_WRITE, DATA_2B}, | |
295 | {"IO Config Register 22-23", 0x792, NI_660x_READ_WRITE, DATA_2B}, | |
296 | {"IO Config Register 24-25", 0x794, NI_660x_READ_WRITE, DATA_2B}, | |
297 | {"IO Config Register 26-27", 0x796, NI_660x_READ_WRITE, DATA_2B}, | |
298 | {"IO Config Register 28-29", 0x798, NI_660x_READ_WRITE, DATA_2B}, | |
299 | {"IO Config Register 30-31", 0x79A, NI_660x_READ_WRITE, DATA_2B}, | |
300 | {"IO Config Register 32-33", 0x79C, NI_660x_READ_WRITE, DATA_2B}, | |
301 | {"IO Config Register 34-35", 0x79E, NI_660x_READ_WRITE, DATA_2B}, | |
302 | {"IO Config Register 36-37", 0x7A0, NI_660x_READ_WRITE, DATA_2B}, | |
303 | {"IO Config Register 38-39", 0x7A2, NI_660x_READ_WRITE, DATA_2B} | |
304 | }; | |
305 | ||
2696fb57 | 306 | /* kind of ENABLE for the second counter */ |
58dd7c0a M |
307 | enum clock_config_register_bits { |
308 | CounterSwap = 0x1 << 21 | |
309 | }; | |
310 | ||
2696fb57 | 311 | /* ioconfigreg */ |
58dd7c0a M |
312 | static inline unsigned ioconfig_bitshift(unsigned pfi_channel) |
313 | { | |
314 | if (pfi_channel % 2) | |
315 | return 0; | |
316 | else | |
317 | return 8; | |
318 | } | |
0a85b6f0 | 319 | |
58dd7c0a M |
320 | static inline unsigned pfi_output_select_mask(unsigned pfi_channel) |
321 | { | |
322 | return 0x3 << ioconfig_bitshift(pfi_channel); | |
323 | } | |
0a85b6f0 | 324 | |
58dd7c0a | 325 | static inline unsigned pfi_output_select_bits(unsigned pfi_channel, |
0a85b6f0 | 326 | unsigned output_select) |
58dd7c0a M |
327 | { |
328 | return (output_select & 0x3) << ioconfig_bitshift(pfi_channel); | |
329 | } | |
0a85b6f0 | 330 | |
58dd7c0a M |
331 | static inline unsigned pfi_input_select_mask(unsigned pfi_channel) |
332 | { | |
333 | return 0x7 << (4 + ioconfig_bitshift(pfi_channel)); | |
334 | } | |
0a85b6f0 | 335 | |
58dd7c0a | 336 | static inline unsigned pfi_input_select_bits(unsigned pfi_channel, |
0a85b6f0 | 337 | unsigned input_select) |
58dd7c0a M |
338 | { |
339 | return (input_select & 0x7) << (4 + ioconfig_bitshift(pfi_channel)); | |
340 | } | |
341 | ||
2696fb57 | 342 | /* dma configuration register bits */ |
58dd7c0a M |
343 | static inline unsigned dma_select_mask(unsigned dma_channel) |
344 | { | |
345 | BUG_ON(dma_channel >= MAX_DMA_CHANNEL); | |
346 | return 0x1f << (8 * dma_channel); | |
347 | } | |
0a85b6f0 | 348 | |
58dd7c0a M |
349 | enum dma_selection { |
350 | dma_selection_none = 0x1f, | |
351 | }; | |
352 | static inline unsigned dma_selection_counter(unsigned counter_index) | |
353 | { | |
354 | BUG_ON(counter_index >= counters_per_chip); | |
355 | return counter_index; | |
356 | } | |
0a85b6f0 | 357 | |
58dd7c0a M |
358 | static inline unsigned dma_select_bits(unsigned dma_channel, unsigned selection) |
359 | { | |
360 | BUG_ON(dma_channel >= MAX_DMA_CHANNEL); | |
361 | return (selection << (8 * dma_channel)) & dma_select_mask(dma_channel); | |
362 | } | |
0a85b6f0 | 363 | |
58dd7c0a M |
364 | static inline unsigned dma_reset_bit(unsigned dma_channel) |
365 | { | |
366 | BUG_ON(dma_channel >= MAX_DMA_CHANNEL); | |
367 | return 0x80 << (8 * dma_channel); | |
368 | } | |
369 | ||
370 | enum global_interrupt_status_register_bits { | |
371 | Counter_0_Int_Bit = 0x100, | |
372 | Counter_1_Int_Bit = 0x200, | |
373 | Counter_2_Int_Bit = 0x400, | |
374 | Counter_3_Int_Bit = 0x800, | |
375 | Cascade_Int_Bit = 0x20000000, | |
376 | Global_Int_Bit = 0x80000000 | |
377 | }; | |
378 | ||
379 | enum global_interrupt_config_register_bits { | |
380 | Cascade_Int_Enable_Bit = 0x20000000, | |
381 | Global_Int_Polarity_Bit = 0x40000000, | |
382 | Global_Int_Enable_Bit = 0x80000000 | |
383 | }; | |
384 | ||
f69b0d64 | 385 | /* Offset of the GPCT chips from the base-address of the card */ |
900b7808 BA |
386 | /* First chip is at base-address + 0x00, etc. */ |
387 | static const unsigned GPCT_OFFSET[2] = { 0x0, 0x800 }; | |
58dd7c0a M |
388 | |
389 | /* Board description*/ | |
50792813 | 390 | struct ni_660x_board { |
58dd7c0a M |
391 | unsigned short dev_id; /* `lspci` will show you this */ |
392 | const char *name; | |
393 | unsigned n_chips; /* total number of TIO chips */ | |
50792813 | 394 | }; |
58dd7c0a | 395 | |
50792813 | 396 | static const struct ni_660x_board ni_660x_boards[] = { |
58dd7c0a | 397 | { |
0a85b6f0 MT |
398 | .dev_id = 0x2c60, |
399 | .name = "PCI-6601", | |
400 | .n_chips = 1, | |
401 | }, | |
58dd7c0a | 402 | { |
0a85b6f0 MT |
403 | .dev_id = 0x1310, |
404 | .name = "PCI-6602", | |
405 | .n_chips = 2, | |
406 | }, | |
58dd7c0a | 407 | { |
0a85b6f0 MT |
408 | .dev_id = 0x1360, |
409 | .name = "PXI-6602", | |
410 | .n_chips = 2, | |
411 | }, | |
58dd7c0a | 412 | { |
0a85b6f0 MT |
413 | .dev_id = 0x2cc0, |
414 | .name = "PXI-6608", | |
415 | .n_chips = 2, | |
416 | }, | |
58dd7c0a M |
417 | }; |
418 | ||
419 | #define NI_660X_MAX_NUM_CHIPS 2 | |
420 | #define NI_660X_MAX_NUM_COUNTERS (NI_660X_MAX_NUM_CHIPS * counters_per_chip) | |
421 | ||
422 | static DEFINE_PCI_DEVICE_TABLE(ni_660x_pci_table) = { | |
4e40cee9 GKH |
423 | {PCI_DEVICE(PCI_VENDOR_ID_NI, 0x2c60)}, |
424 | {PCI_DEVICE(PCI_VENDOR_ID_NI, 0x1310)}, | |
425 | {PCI_DEVICE(PCI_VENDOR_ID_NI, 0x1360)}, | |
426 | {PCI_DEVICE(PCI_VENDOR_ID_NI, 0x2cc0)}, | |
427 | {0} | |
58dd7c0a M |
428 | }; |
429 | ||
430 | MODULE_DEVICE_TABLE(pci, ni_660x_pci_table); | |
431 | ||
b3be94ea | 432 | struct ni_660x_private { |
58dd7c0a M |
433 | struct mite_struct *mite; |
434 | struct ni_gpct_device *counter_dev; | |
435 | uint64_t pfi_direction_bits; | |
436 | struct mite_dma_descriptor_ring | |
437 | *mite_rings[NI_660X_MAX_NUM_CHIPS][counters_per_chip]; | |
438 | spinlock_t mite_channel_lock; | |
894db119 FMH |
439 | /* interrupt_lock prevents races between interrupt and comedi_poll */ |
440 | spinlock_t interrupt_lock; | |
58dd7c0a M |
441 | unsigned dma_configuration_soft_copies[NI_660X_MAX_NUM_CHIPS]; |
442 | spinlock_t soft_reg_copy_lock; | |
443 | unsigned short pfi_output_selects[NUM_PFI_CHANNELS]; | |
b3be94ea | 444 | }; |
58dd7c0a | 445 | |
0a85b6f0 | 446 | static inline struct ni_660x_private *private(struct comedi_device *dev) |
58dd7c0a M |
447 | { |
448 | return dev->private; | |
449 | } | |
450 | ||
451 | /* initialized in ni_660x_find_device() */ | |
0a85b6f0 | 452 | static inline const struct ni_660x_board *board(struct comedi_device *dev) |
58dd7c0a M |
453 | { |
454 | return dev->board_ptr; | |
455 | } | |
456 | ||
b6ac1613 | 457 | #define n_ni_660x_boards ARRAY_SIZE(ni_660x_boards) |
58dd7c0a | 458 | |
0a85b6f0 MT |
459 | static int ni_660x_attach(struct comedi_device *dev, |
460 | struct comedi_devconfig *it); | |
da91b269 BP |
461 | static int ni_660x_detach(struct comedi_device *dev); |
462 | static void init_tio_chip(struct comedi_device *dev, int chipset); | |
0a85b6f0 MT |
463 | static void ni_660x_select_pfi_output(struct comedi_device *dev, |
464 | unsigned pfi_channel, | |
465 | unsigned output_select); | |
58dd7c0a | 466 | |
139dfbdf | 467 | static struct comedi_driver driver_ni_660x = { |
68c3dbff BP |
468 | .driver_name = "ni_660x", |
469 | .module = THIS_MODULE, | |
470 | .attach = ni_660x_attach, | |
471 | .detach = ni_660x_detach, | |
58dd7c0a M |
472 | }; |
473 | ||
727b286b AT |
474 | static int __devinit driver_ni_660x_pci_probe(struct pci_dev *dev, |
475 | const struct pci_device_id *ent) | |
476 | { | |
477 | return comedi_pci_auto_config(dev, driver_ni_660x.driver_name); | |
478 | } | |
479 | ||
480 | static void __devexit driver_ni_660x_pci_remove(struct pci_dev *dev) | |
481 | { | |
482 | comedi_pci_auto_unconfig(dev); | |
483 | } | |
484 | ||
485 | static struct pci_driver driver_ni_660x_pci_driver = { | |
486 | .id_table = ni_660x_pci_table, | |
487 | .probe = &driver_ni_660x_pci_probe, | |
488 | .remove = __devexit_p(&driver_ni_660x_pci_remove) | |
489 | }; | |
490 | ||
491 | static int __init driver_ni_660x_init_module(void) | |
492 | { | |
493 | int retval; | |
494 | ||
495 | retval = comedi_driver_register(&driver_ni_660x); | |
496 | if (retval < 0) | |
497 | return retval; | |
498 | ||
499 | driver_ni_660x_pci_driver.name = (char *)driver_ni_660x.driver_name; | |
500 | return pci_register_driver(&driver_ni_660x_pci_driver); | |
501 | } | |
502 | ||
503 | static void __exit driver_ni_660x_cleanup_module(void) | |
504 | { | |
505 | pci_unregister_driver(&driver_ni_660x_pci_driver); | |
506 | comedi_driver_unregister(&driver_ni_660x); | |
507 | } | |
508 | ||
509 | module_init(driver_ni_660x_init_module); | |
510 | module_exit(driver_ni_660x_cleanup_module); | |
58dd7c0a | 511 | |
da91b269 BP |
512 | static int ni_660x_find_device(struct comedi_device *dev, int bus, int slot); |
513 | static int ni_660x_set_pfi_routing(struct comedi_device *dev, unsigned chan, | |
0a85b6f0 | 514 | unsigned source); |
58dd7c0a M |
515 | |
516 | /* Possible instructions for a GPCT */ | |
da91b269 | 517 | static int ni_660x_GPCT_rinsn(struct comedi_device *dev, |
0a85b6f0 MT |
518 | struct comedi_subdevice *s, |
519 | struct comedi_insn *insn, unsigned int *data); | |
da91b269 | 520 | static int ni_660x_GPCT_insn_config(struct comedi_device *dev, |
0a85b6f0 MT |
521 | struct comedi_subdevice *s, |
522 | struct comedi_insn *insn, | |
523 | unsigned int *data); | |
da91b269 | 524 | static int ni_660x_GPCT_winsn(struct comedi_device *dev, |
0a85b6f0 MT |
525 | struct comedi_subdevice *s, |
526 | struct comedi_insn *insn, unsigned int *data); | |
58dd7c0a M |
527 | |
528 | /* Possible instructions for Digital IO */ | |
da91b269 | 529 | static int ni_660x_dio_insn_config(struct comedi_device *dev, |
0a85b6f0 MT |
530 | struct comedi_subdevice *s, |
531 | struct comedi_insn *insn, | |
532 | unsigned int *data); | |
da91b269 | 533 | static int ni_660x_dio_insn_bits(struct comedi_device *dev, |
0a85b6f0 MT |
534 | struct comedi_subdevice *s, |
535 | struct comedi_insn *insn, unsigned int *data); | |
58dd7c0a | 536 | |
da91b269 | 537 | static inline unsigned ni_660x_num_counters(struct comedi_device *dev) |
58dd7c0a M |
538 | { |
539 | return board(dev)->n_chips * counters_per_chip; | |
540 | } | |
541 | ||
251411cf | 542 | static enum NI_660x_Register ni_gpct_to_660x_register(enum ni_gpct_register reg) |
58dd7c0a | 543 | { |
251411cf | 544 | enum NI_660x_Register ni_660x_register; |
58dd7c0a M |
545 | switch (reg) { |
546 | case NITIO_G0_Autoincrement_Reg: | |
547 | ni_660x_register = G0AutoincrementRegister; | |
548 | break; | |
549 | case NITIO_G1_Autoincrement_Reg: | |
550 | ni_660x_register = G1AutoincrementRegister; | |
551 | break; | |
552 | case NITIO_G2_Autoincrement_Reg: | |
553 | ni_660x_register = G2AutoincrementRegister; | |
554 | break; | |
555 | case NITIO_G3_Autoincrement_Reg: | |
556 | ni_660x_register = G3AutoincrementRegister; | |
557 | break; | |
558 | case NITIO_G0_Command_Reg: | |
559 | ni_660x_register = G0CommandRegister; | |
560 | break; | |
561 | case NITIO_G1_Command_Reg: | |
562 | ni_660x_register = G1CommandRegister; | |
563 | break; | |
564 | case NITIO_G2_Command_Reg: | |
565 | ni_660x_register = G2CommandRegister; | |
566 | break; | |
567 | case NITIO_G3_Command_Reg: | |
568 | ni_660x_register = G3CommandRegister; | |
569 | break; | |
570 | case NITIO_G0_HW_Save_Reg: | |
571 | ni_660x_register = G0HWSaveRegister; | |
572 | break; | |
573 | case NITIO_G1_HW_Save_Reg: | |
574 | ni_660x_register = G1HWSaveRegister; | |
575 | break; | |
576 | case NITIO_G2_HW_Save_Reg: | |
577 | ni_660x_register = G2HWSaveRegister; | |
578 | break; | |
579 | case NITIO_G3_HW_Save_Reg: | |
580 | ni_660x_register = G3HWSaveRegister; | |
581 | break; | |
582 | case NITIO_G0_SW_Save_Reg: | |
583 | ni_660x_register = G0SWSaveRegister; | |
584 | break; | |
585 | case NITIO_G1_SW_Save_Reg: | |
586 | ni_660x_register = G1SWSaveRegister; | |
587 | break; | |
588 | case NITIO_G2_SW_Save_Reg: | |
589 | ni_660x_register = G2SWSaveRegister; | |
590 | break; | |
591 | case NITIO_G3_SW_Save_Reg: | |
592 | ni_660x_register = G3SWSaveRegister; | |
593 | break; | |
594 | case NITIO_G0_Mode_Reg: | |
595 | ni_660x_register = G0ModeRegister; | |
596 | break; | |
597 | case NITIO_G1_Mode_Reg: | |
598 | ni_660x_register = G1ModeRegister; | |
599 | break; | |
600 | case NITIO_G2_Mode_Reg: | |
601 | ni_660x_register = G2ModeRegister; | |
602 | break; | |
603 | case NITIO_G3_Mode_Reg: | |
604 | ni_660x_register = G3ModeRegister; | |
605 | break; | |
606 | case NITIO_G0_LoadA_Reg: | |
607 | ni_660x_register = G0LoadARegister; | |
608 | break; | |
609 | case NITIO_G1_LoadA_Reg: | |
610 | ni_660x_register = G1LoadARegister; | |
611 | break; | |
612 | case NITIO_G2_LoadA_Reg: | |
613 | ni_660x_register = G2LoadARegister; | |
614 | break; | |
615 | case NITIO_G3_LoadA_Reg: | |
616 | ni_660x_register = G3LoadARegister; | |
617 | break; | |
618 | case NITIO_G0_LoadB_Reg: | |
619 | ni_660x_register = G0LoadBRegister; | |
620 | break; | |
621 | case NITIO_G1_LoadB_Reg: | |
622 | ni_660x_register = G1LoadBRegister; | |
623 | break; | |
624 | case NITIO_G2_LoadB_Reg: | |
625 | ni_660x_register = G2LoadBRegister; | |
626 | break; | |
627 | case NITIO_G3_LoadB_Reg: | |
628 | ni_660x_register = G3LoadBRegister; | |
629 | break; | |
630 | case NITIO_G0_Input_Select_Reg: | |
631 | ni_660x_register = G0InputSelectRegister; | |
632 | break; | |
633 | case NITIO_G1_Input_Select_Reg: | |
634 | ni_660x_register = G1InputSelectRegister; | |
635 | break; | |
636 | case NITIO_G2_Input_Select_Reg: | |
637 | ni_660x_register = G2InputSelectRegister; | |
638 | break; | |
639 | case NITIO_G3_Input_Select_Reg: | |
640 | ni_660x_register = G3InputSelectRegister; | |
641 | break; | |
642 | case NITIO_G01_Status_Reg: | |
643 | ni_660x_register = G01StatusRegister; | |
644 | break; | |
645 | case NITIO_G23_Status_Reg: | |
646 | ni_660x_register = G23StatusRegister; | |
647 | break; | |
648 | case NITIO_G01_Joint_Reset_Reg: | |
649 | ni_660x_register = G01JointResetRegister; | |
650 | break; | |
651 | case NITIO_G23_Joint_Reset_Reg: | |
652 | ni_660x_register = G23JointResetRegister; | |
653 | break; | |
654 | case NITIO_G01_Joint_Status1_Reg: | |
655 | ni_660x_register = G01JointStatus1Register; | |
656 | break; | |
657 | case NITIO_G23_Joint_Status1_Reg: | |
658 | ni_660x_register = G23JointStatus1Register; | |
659 | break; | |
660 | case NITIO_G01_Joint_Status2_Reg: | |
661 | ni_660x_register = G01JointStatus2Register; | |
662 | break; | |
663 | case NITIO_G23_Joint_Status2_Reg: | |
664 | ni_660x_register = G23JointStatus2Register; | |
665 | break; | |
666 | case NITIO_G0_Counting_Mode_Reg: | |
667 | ni_660x_register = G0CountingModeRegister; | |
668 | break; | |
669 | case NITIO_G1_Counting_Mode_Reg: | |
670 | ni_660x_register = G1CountingModeRegister; | |
671 | break; | |
672 | case NITIO_G2_Counting_Mode_Reg: | |
673 | ni_660x_register = G2CountingModeRegister; | |
674 | break; | |
675 | case NITIO_G3_Counting_Mode_Reg: | |
676 | ni_660x_register = G3CountingModeRegister; | |
677 | break; | |
678 | case NITIO_G0_Second_Gate_Reg: | |
679 | ni_660x_register = G0SecondGateRegister; | |
680 | break; | |
681 | case NITIO_G1_Second_Gate_Reg: | |
682 | ni_660x_register = G1SecondGateRegister; | |
683 | break; | |
684 | case NITIO_G2_Second_Gate_Reg: | |
685 | ni_660x_register = G2SecondGateRegister; | |
686 | break; | |
687 | case NITIO_G3_Second_Gate_Reg: | |
688 | ni_660x_register = G3SecondGateRegister; | |
689 | break; | |
690 | case NITIO_G0_DMA_Config_Reg: | |
691 | ni_660x_register = G0DMAConfigRegister; | |
692 | break; | |
693 | case NITIO_G0_DMA_Status_Reg: | |
694 | ni_660x_register = G0DMAStatusRegister; | |
695 | break; | |
696 | case NITIO_G1_DMA_Config_Reg: | |
697 | ni_660x_register = G1DMAConfigRegister; | |
698 | break; | |
699 | case NITIO_G1_DMA_Status_Reg: | |
700 | ni_660x_register = G1DMAStatusRegister; | |
701 | break; | |
702 | case NITIO_G2_DMA_Config_Reg: | |
703 | ni_660x_register = G2DMAConfigRegister; | |
704 | break; | |
705 | case NITIO_G2_DMA_Status_Reg: | |
706 | ni_660x_register = G2DMAStatusRegister; | |
707 | break; | |
708 | case NITIO_G3_DMA_Config_Reg: | |
709 | ni_660x_register = G3DMAConfigRegister; | |
710 | break; | |
711 | case NITIO_G3_DMA_Status_Reg: | |
712 | ni_660x_register = G3DMAStatusRegister; | |
713 | break; | |
714 | case NITIO_G0_Interrupt_Acknowledge_Reg: | |
715 | ni_660x_register = G0InterruptAcknowledge; | |
716 | break; | |
717 | case NITIO_G1_Interrupt_Acknowledge_Reg: | |
718 | ni_660x_register = G1InterruptAcknowledge; | |
719 | break; | |
720 | case NITIO_G2_Interrupt_Acknowledge_Reg: | |
721 | ni_660x_register = G2InterruptAcknowledge; | |
722 | break; | |
723 | case NITIO_G3_Interrupt_Acknowledge_Reg: | |
724 | ni_660x_register = G3InterruptAcknowledge; | |
725 | break; | |
726 | case NITIO_G0_Status_Reg: | |
727 | ni_660x_register = G0StatusRegister; | |
728 | break; | |
729 | case NITIO_G1_Status_Reg: | |
6c381c57 | 730 | ni_660x_register = G1StatusRegister; |
58dd7c0a M |
731 | break; |
732 | case NITIO_G2_Status_Reg: | |
6c381c57 | 733 | ni_660x_register = G2StatusRegister; |
58dd7c0a M |
734 | break; |
735 | case NITIO_G3_Status_Reg: | |
6c381c57 | 736 | ni_660x_register = G3StatusRegister; |
58dd7c0a M |
737 | break; |
738 | case NITIO_G0_Interrupt_Enable_Reg: | |
739 | ni_660x_register = G0InterruptEnable; | |
740 | break; | |
741 | case NITIO_G1_Interrupt_Enable_Reg: | |
742 | ni_660x_register = G1InterruptEnable; | |
743 | break; | |
744 | case NITIO_G2_Interrupt_Enable_Reg: | |
745 | ni_660x_register = G2InterruptEnable; | |
746 | break; | |
747 | case NITIO_G3_Interrupt_Enable_Reg: | |
748 | ni_660x_register = G3InterruptEnable; | |
749 | break; | |
750 | default: | |
900b7808 | 751 | printk(KERN_WARNING "%s: unhandled register 0x%x in switch.\n", |
0a85b6f0 | 752 | __func__, reg); |
58dd7c0a M |
753 | BUG(); |
754 | return 0; | |
755 | break; | |
756 | } | |
757 | return ni_660x_register; | |
758 | } | |
759 | ||
da91b269 | 760 | static inline void ni_660x_write_register(struct comedi_device *dev, |
0a85b6f0 MT |
761 | unsigned chip_index, unsigned bits, |
762 | enum NI_660x_Register reg) | |
58dd7c0a M |
763 | { |
764 | void *const write_address = | |
0a85b6f0 MT |
765 | private(dev)->mite->daq_io_addr + GPCT_OFFSET[chip_index] + |
766 | registerData[reg].offset; | |
58dd7c0a M |
767 | |
768 | switch (registerData[reg].size) { | |
769 | case DATA_2B: | |
770 | writew(bits, write_address); | |
771 | break; | |
772 | case DATA_4B: | |
773 | writel(bits, write_address); | |
774 | break; | |
775 | default: | |
900b7808 | 776 | printk(KERN_WARNING "%s: %s: bug! unhandled case (reg=0x%x) in switch.\n", |
0a85b6f0 | 777 | __FILE__, __func__, reg); |
58dd7c0a M |
778 | BUG(); |
779 | break; | |
780 | } | |
781 | } | |
782 | ||
da91b269 | 783 | static inline unsigned ni_660x_read_register(struct comedi_device *dev, |
0a85b6f0 MT |
784 | unsigned chip_index, |
785 | enum NI_660x_Register reg) | |
58dd7c0a M |
786 | { |
787 | void *const read_address = | |
0a85b6f0 MT |
788 | private(dev)->mite->daq_io_addr + GPCT_OFFSET[chip_index] + |
789 | registerData[reg].offset; | |
58dd7c0a M |
790 | |
791 | switch (registerData[reg].size) { | |
792 | case DATA_2B: | |
793 | return readw(read_address); | |
794 | break; | |
795 | case DATA_4B: | |
796 | return readl(read_address); | |
797 | break; | |
798 | default: | |
900b7808 | 799 | printk(KERN_WARNING "%s: %s: bug! unhandled case (reg=0x%x) in switch.\n", |
0a85b6f0 | 800 | __FILE__, __func__, reg); |
58dd7c0a M |
801 | BUG(); |
802 | break; | |
803 | } | |
804 | return 0; | |
805 | } | |
806 | ||
807 | static void ni_gpct_write_register(struct ni_gpct *counter, unsigned bits, | |
0a85b6f0 | 808 | enum ni_gpct_register reg) |
58dd7c0a | 809 | { |
71b5f4f1 | 810 | struct comedi_device *dev = counter->counter_dev->dev; |
251411cf | 811 | enum NI_660x_Register ni_660x_register = ni_gpct_to_660x_register(reg); |
58dd7c0a | 812 | ni_660x_write_register(dev, counter->chip_index, bits, |
0a85b6f0 | 813 | ni_660x_register); |
58dd7c0a M |
814 | } |
815 | ||
816 | static unsigned ni_gpct_read_register(struct ni_gpct *counter, | |
0a85b6f0 | 817 | enum ni_gpct_register reg) |
58dd7c0a | 818 | { |
71b5f4f1 | 819 | struct comedi_device *dev = counter->counter_dev->dev; |
251411cf | 820 | enum NI_660x_Register ni_660x_register = ni_gpct_to_660x_register(reg); |
58dd7c0a | 821 | return ni_660x_read_register(dev, counter->chip_index, |
0a85b6f0 | 822 | ni_660x_register); |
58dd7c0a M |
823 | } |
824 | ||
0a85b6f0 MT |
825 | static inline struct mite_dma_descriptor_ring *mite_ring(struct ni_660x_private |
826 | *priv, | |
827 | struct ni_gpct | |
828 | *counter) | |
58dd7c0a M |
829 | { |
830 | return priv->mite_rings[counter->chip_index][counter->counter_index]; | |
831 | } | |
832 | ||
da91b269 | 833 | static inline void ni_660x_set_dma_channel(struct comedi_device *dev, |
0a85b6f0 MT |
834 | unsigned mite_channel, |
835 | struct ni_gpct *counter) | |
58dd7c0a M |
836 | { |
837 | unsigned long flags; | |
5f74ea14 | 838 | spin_lock_irqsave(&private(dev)->soft_reg_copy_lock, flags); |
58dd7c0a | 839 | private(dev)->dma_configuration_soft_copies[counter->chip_index] &= |
0a85b6f0 | 840 | ~dma_select_mask(mite_channel); |
58dd7c0a | 841 | private(dev)->dma_configuration_soft_copies[counter->chip_index] |= |
0a85b6f0 MT |
842 | dma_select_bits(mite_channel, |
843 | dma_selection_counter(counter->counter_index)); | |
58dd7c0a | 844 | ni_660x_write_register(dev, counter->chip_index, |
0a85b6f0 MT |
845 | private(dev)-> |
846 | dma_configuration_soft_copies | |
847 | [counter->chip_index] | | |
848 | dma_reset_bit(mite_channel), DMAConfigRegister); | |
58dd7c0a | 849 | mmiowb(); |
5f74ea14 | 850 | spin_unlock_irqrestore(&private(dev)->soft_reg_copy_lock, flags); |
58dd7c0a M |
851 | } |
852 | ||
da91b269 | 853 | static inline void ni_660x_unset_dma_channel(struct comedi_device *dev, |
0a85b6f0 MT |
854 | unsigned mite_channel, |
855 | struct ni_gpct *counter) | |
58dd7c0a M |
856 | { |
857 | unsigned long flags; | |
5f74ea14 | 858 | spin_lock_irqsave(&private(dev)->soft_reg_copy_lock, flags); |
58dd7c0a | 859 | private(dev)->dma_configuration_soft_copies[counter->chip_index] &= |
0a85b6f0 | 860 | ~dma_select_mask(mite_channel); |
58dd7c0a | 861 | private(dev)->dma_configuration_soft_copies[counter->chip_index] |= |
0a85b6f0 | 862 | dma_select_bits(mite_channel, dma_selection_none); |
58dd7c0a | 863 | ni_660x_write_register(dev, counter->chip_index, |
0a85b6f0 MT |
864 | private(dev)-> |
865 | dma_configuration_soft_copies | |
866 | [counter->chip_index], DMAConfigRegister); | |
58dd7c0a | 867 | mmiowb(); |
5f74ea14 | 868 | spin_unlock_irqrestore(&private(dev)->soft_reg_copy_lock, flags); |
58dd7c0a M |
869 | } |
870 | ||
da91b269 | 871 | static int ni_660x_request_mite_channel(struct comedi_device *dev, |
0a85b6f0 MT |
872 | struct ni_gpct *counter, |
873 | enum comedi_io_direction direction) | |
58dd7c0a M |
874 | { |
875 | unsigned long flags; | |
876 | struct mite_channel *mite_chan; | |
877 | ||
5f74ea14 | 878 | spin_lock_irqsave(&private(dev)->mite_channel_lock, flags); |
58dd7c0a M |
879 | BUG_ON(counter->mite_chan); |
880 | mite_chan = | |
0a85b6f0 MT |
881 | mite_request_channel(private(dev)->mite, mite_ring(private(dev), |
882 | counter)); | |
58dd7c0a | 883 | if (mite_chan == NULL) { |
0a85b6f0 | 884 | spin_unlock_irqrestore(&private(dev)->mite_channel_lock, flags); |
58dd7c0a | 885 | comedi_error(dev, |
0a85b6f0 | 886 | "failed to reserve mite dma channel for counter."); |
58dd7c0a M |
887 | return -EBUSY; |
888 | } | |
889 | mite_chan->dir = direction; | |
890 | ni_tio_set_mite_channel(counter, mite_chan); | |
891 | ni_660x_set_dma_channel(dev, mite_chan->channel, counter); | |
5f74ea14 | 892 | spin_unlock_irqrestore(&private(dev)->mite_channel_lock, flags); |
58dd7c0a M |
893 | return 0; |
894 | } | |
895 | ||
0a85b6f0 MT |
896 | void ni_660x_release_mite_channel(struct comedi_device *dev, |
897 | struct ni_gpct *counter) | |
58dd7c0a M |
898 | { |
899 | unsigned long flags; | |
900 | ||
5f74ea14 | 901 | spin_lock_irqsave(&private(dev)->mite_channel_lock, flags); |
58dd7c0a M |
902 | if (counter->mite_chan) { |
903 | struct mite_channel *mite_chan = counter->mite_chan; | |
904 | ||
905 | ni_660x_unset_dma_channel(dev, mite_chan->channel, counter); | |
906 | ni_tio_set_mite_channel(counter, NULL); | |
907 | mite_release_channel(mite_chan); | |
908 | } | |
5f74ea14 | 909 | spin_unlock_irqrestore(&private(dev)->mite_channel_lock, flags); |
58dd7c0a M |
910 | } |
911 | ||
da91b269 | 912 | static int ni_660x_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
58dd7c0a M |
913 | { |
914 | int retval; | |
915 | ||
916 | struct ni_gpct *counter = subdev_to_counter(s); | |
2696fb57 | 917 | /* const struct comedi_cmd *cmd = &s->async->cmd; */ |
58dd7c0a M |
918 | |
919 | retval = ni_660x_request_mite_channel(dev, counter, COMEDI_INPUT); | |
920 | if (retval) { | |
921 | comedi_error(dev, | |
0a85b6f0 | 922 | "no dma channel available for use by counter"); |
58dd7c0a M |
923 | return retval; |
924 | } | |
925 | ni_tio_acknowledge_and_confirm(counter, NULL, NULL, NULL, NULL); | |
926 | retval = ni_tio_cmd(counter, s->async); | |
927 | ||
928 | return retval; | |
929 | } | |
930 | ||
0a85b6f0 MT |
931 | static int ni_660x_cmdtest(struct comedi_device *dev, |
932 | struct comedi_subdevice *s, struct comedi_cmd *cmd) | |
58dd7c0a M |
933 | { |
934 | struct ni_gpct *counter = subdev_to_counter(s); | |
935 | ||
936 | return ni_tio_cmdtest(counter, cmd); | |
937 | } | |
938 | ||
da91b269 | 939 | static int ni_660x_cancel(struct comedi_device *dev, struct comedi_subdevice *s) |
58dd7c0a M |
940 | { |
941 | struct ni_gpct *counter = subdev_to_counter(s); | |
942 | int retval; | |
943 | ||
944 | retval = ni_tio_cancel(counter); | |
945 | ni_660x_release_mite_channel(dev, counter); | |
946 | return retval; | |
947 | } | |
948 | ||
da91b269 | 949 | static void set_tio_counterswap(struct comedi_device *dev, int chipset) |
58dd7c0a M |
950 | { |
951 | /* See P. 3.5 of the Register-Level Programming manual. The | |
952 | CounterSwap bit has to be set on the second chip, otherwise | |
953 | it will try to use the same pins as the first chip. | |
954 | */ | |
955 | if (chipset) | |
956 | ni_660x_write_register(dev, chipset, CounterSwap, | |
0a85b6f0 | 957 | ClockConfigRegister); |
58dd7c0a M |
958 | else |
959 | ni_660x_write_register(dev, chipset, 0, ClockConfigRegister); | |
960 | } | |
961 | ||
da91b269 | 962 | static void ni_660x_handle_gpct_interrupt(struct comedi_device *dev, |
0a85b6f0 | 963 | struct comedi_subdevice *s) |
58dd7c0a M |
964 | { |
965 | ni_tio_handle_interrupt(subdev_to_counter(s), s); | |
966 | if (s->async->events) { | |
0a85b6f0 MT |
967 | if (s->async->events & (COMEDI_CB_EOA | COMEDI_CB_ERROR | |
968 | COMEDI_CB_OVERFLOW)) { | |
58dd7c0a M |
969 | ni_660x_cancel(dev, s); |
970 | } | |
971 | comedi_event(dev, s); | |
972 | } | |
973 | } | |
974 | ||
70265d24 | 975 | static irqreturn_t ni_660x_interrupt(int irq, void *d) |
58dd7c0a | 976 | { |
71b5f4f1 | 977 | struct comedi_device *dev = d; |
34c43922 | 978 | struct comedi_subdevice *s; |
58dd7c0a | 979 | unsigned i; |
894db119 | 980 | unsigned long flags; |
58dd7c0a M |
981 | |
982 | if (dev->attached == 0) | |
983 | return IRQ_NONE; | |
894db119 | 984 | /* lock to avoid race with comedi_poll */ |
5f74ea14 | 985 | spin_lock_irqsave(&private(dev)->interrupt_lock, flags); |
58dd7c0a M |
986 | smp_mb(); |
987 | for (i = 0; i < ni_660x_num_counters(dev); ++i) { | |
988 | s = dev->subdevices + NI_660X_GPCT_SUBDEV(i); | |
989 | ni_660x_handle_gpct_interrupt(dev, s); | |
990 | } | |
5f74ea14 | 991 | spin_unlock_irqrestore(&private(dev)->interrupt_lock, flags); |
58dd7c0a M |
992 | return IRQ_HANDLED; |
993 | } | |
994 | ||
894db119 FMH |
995 | static int ni_660x_input_poll(struct comedi_device *dev, |
996 | struct comedi_subdevice *s) | |
997 | { | |
998 | unsigned long flags; | |
999 | /* lock to avoid race with comedi_poll */ | |
5f74ea14 | 1000 | spin_lock_irqsave(&private(dev)->interrupt_lock, flags); |
894db119 | 1001 | mite_sync_input_dma(subdev_to_counter(s)->mite_chan, s->async); |
5f74ea14 | 1002 | spin_unlock_irqrestore(&private(dev)->interrupt_lock, flags); |
894db119 FMH |
1003 | return comedi_buf_read_n_available(s->async); |
1004 | } | |
1005 | ||
0a85b6f0 MT |
1006 | static int ni_660x_buf_change(struct comedi_device *dev, |
1007 | struct comedi_subdevice *s, | |
1008 | unsigned long new_size) | |
58dd7c0a M |
1009 | { |
1010 | int ret; | |
1011 | ||
1012 | ret = mite_buf_change(mite_ring(private(dev), subdev_to_counter(s)), | |
0a85b6f0 | 1013 | s->async); |
58dd7c0a M |
1014 | if (ret < 0) |
1015 | return ret; | |
1016 | ||
1017 | return 0; | |
1018 | } | |
1019 | ||
da91b269 | 1020 | static int ni_660x_allocate_private(struct comedi_device *dev) |
58dd7c0a M |
1021 | { |
1022 | int retval; | |
1023 | unsigned i; | |
1024 | ||
c3744138 BP |
1025 | retval = alloc_private(dev, sizeof(struct ni_660x_private)); |
1026 | if (retval < 0) | |
58dd7c0a | 1027 | return retval; |
c3744138 | 1028 | |
58dd7c0a | 1029 | spin_lock_init(&private(dev)->mite_channel_lock); |
894db119 | 1030 | spin_lock_init(&private(dev)->interrupt_lock); |
58dd7c0a | 1031 | spin_lock_init(&private(dev)->soft_reg_copy_lock); |
900b7808 | 1032 | for (i = 0; i < NUM_PFI_CHANNELS; ++i) |
58dd7c0a | 1033 | private(dev)->pfi_output_selects[i] = pfi_output_select_counter; |
900b7808 | 1034 | |
58dd7c0a M |
1035 | return 0; |
1036 | } | |
1037 | ||
da91b269 | 1038 | static int ni_660x_alloc_mite_rings(struct comedi_device *dev) |
58dd7c0a M |
1039 | { |
1040 | unsigned i; | |
1041 | unsigned j; | |
1042 | ||
1043 | for (i = 0; i < board(dev)->n_chips; ++i) { | |
1044 | for (j = 0; j < counters_per_chip; ++j) { | |
1045 | private(dev)->mite_rings[i][j] = | |
0a85b6f0 | 1046 | mite_alloc_ring(private(dev)->mite); |
900b7808 | 1047 | if (private(dev)->mite_rings[i][j] == NULL) |
58dd7c0a | 1048 | return -ENOMEM; |
58dd7c0a M |
1049 | } |
1050 | } | |
1051 | return 0; | |
1052 | } | |
1053 | ||
da91b269 | 1054 | static void ni_660x_free_mite_rings(struct comedi_device *dev) |
58dd7c0a M |
1055 | { |
1056 | unsigned i; | |
1057 | unsigned j; | |
1058 | ||
1059 | for (i = 0; i < board(dev)->n_chips; ++i) { | |
900b7808 | 1060 | for (j = 0; j < counters_per_chip; ++j) |
58dd7c0a | 1061 | mite_free_ring(private(dev)->mite_rings[i][j]); |
58dd7c0a M |
1062 | } |
1063 | } | |
1064 | ||
0a85b6f0 MT |
1065 | static int ni_660x_attach(struct comedi_device *dev, |
1066 | struct comedi_devconfig *it) | |
58dd7c0a | 1067 | { |
34c43922 | 1068 | struct comedi_subdevice *s; |
58dd7c0a M |
1069 | int ret; |
1070 | unsigned i; | |
1071 | unsigned global_interrupt_config_bits; | |
1072 | ||
900b7808 | 1073 | printk(KERN_INFO "comedi%d: ni_660x: ", dev->minor); |
58dd7c0a M |
1074 | |
1075 | ret = ni_660x_allocate_private(dev); | |
1076 | if (ret < 0) | |
1077 | return ret; | |
1078 | ret = ni_660x_find_device(dev, it->options[0], it->options[1]); | |
1079 | if (ret < 0) | |
1080 | return ret; | |
1081 | ||
1082 | dev->board_name = board(dev)->name; | |
1083 | ||
1084 | ret = mite_setup2(private(dev)->mite, 1); | |
1085 | if (ret < 0) { | |
900b7808 | 1086 | printk(KERN_WARNING "error setting up mite\n"); |
58dd7c0a M |
1087 | return ret; |
1088 | } | |
1089 | comedi_set_hw_dev(dev, &private(dev)->mite->pcidev->dev); | |
1090 | ret = ni_660x_alloc_mite_rings(dev); | |
1091 | if (ret < 0) | |
1092 | return ret; | |
1093 | ||
900b7808 | 1094 | printk(KERN_INFO " %s ", dev->board_name); |
58dd7c0a M |
1095 | |
1096 | dev->n_subdevices = 2 + NI_660X_MAX_NUM_COUNTERS; | |
1097 | ||
1098 | if (alloc_subdevices(dev, dev->n_subdevices) < 0) | |
1099 | return -ENOMEM; | |
1100 | ||
1101 | s = dev->subdevices + 0; | |
1102 | /* Old GENERAL-PURPOSE COUNTER/TIME (GPCT) subdevice, no longer used */ | |
1103 | s->type = COMEDI_SUBD_UNUSED; | |
1104 | ||
1105 | s = dev->subdevices + NI_660X_DIO_SUBDEV; | |
1106 | /* DIGITAL I/O SUBDEVICE */ | |
1107 | s->type = COMEDI_SUBD_DIO; | |
1108 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
1109 | s->n_chan = NUM_PFI_CHANNELS; | |
1110 | s->maxdata = 1; | |
1111 | s->range_table = &range_digital; | |
1112 | s->insn_bits = ni_660x_dio_insn_bits; | |
1113 | s->insn_config = ni_660x_dio_insn_config; | |
1114 | s->io_bits = 0; /* all bits default to input */ | |
900b7808 BA |
1115 | /* we use the ioconfig registers to control dio direction, so zero |
1116 | output enables in stc dio control reg */ | |
58dd7c0a M |
1117 | ni_660x_write_register(dev, 0, 0, STCDIOControl); |
1118 | ||
1119 | private(dev)->counter_dev = ni_gpct_device_construct(dev, | |
0054a361 GH |
1120 | &ni_gpct_write_register, |
1121 | &ni_gpct_read_register, | |
1122 | ni_gpct_variant_660x, | |
1123 | ni_660x_num_counters | |
1124 | (dev)); | |
58dd7c0a M |
1125 | if (private(dev)->counter_dev == NULL) |
1126 | return -ENOMEM; | |
1127 | for (i = 0; i < NI_660X_MAX_NUM_COUNTERS; ++i) { | |
1128 | s = dev->subdevices + NI_660X_GPCT_SUBDEV(i); | |
1129 | if (i < ni_660x_num_counters(dev)) { | |
1130 | s->type = COMEDI_SUBD_COUNTER; | |
1131 | s->subdev_flags = | |
0a85b6f0 MT |
1132 | SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL | |
1133 | SDF_CMD_READ /* | SDF_CMD_WRITE */ ; | |
58dd7c0a M |
1134 | s->n_chan = 3; |
1135 | s->maxdata = 0xffffffff; | |
1136 | s->insn_read = ni_660x_GPCT_rinsn; | |
1137 | s->insn_write = ni_660x_GPCT_winsn; | |
1138 | s->insn_config = ni_660x_GPCT_insn_config; | |
1139 | s->do_cmd = &ni_660x_cmd; | |
1140 | s->len_chanlist = 1; | |
1141 | s->do_cmdtest = &ni_660x_cmdtest; | |
1142 | s->cancel = &ni_660x_cancel; | |
894db119 | 1143 | s->poll = &ni_660x_input_poll; |
58dd7c0a M |
1144 | s->async_dma_dir = DMA_BIDIRECTIONAL; |
1145 | s->buf_change = &ni_660x_buf_change; | |
1146 | s->private = &private(dev)->counter_dev->counters[i]; | |
1147 | ||
1148 | private(dev)->counter_dev->counters[i].chip_index = | |
0a85b6f0 | 1149 | i / counters_per_chip; |
58dd7c0a | 1150 | private(dev)->counter_dev->counters[i].counter_index = |
0a85b6f0 | 1151 | i % counters_per_chip; |
58dd7c0a M |
1152 | } else { |
1153 | s->type = COMEDI_SUBD_UNUSED; | |
1154 | } | |
1155 | } | |
900b7808 | 1156 | for (i = 0; i < board(dev)->n_chips; ++i) |
58dd7c0a | 1157 | init_tio_chip(dev, i); |
900b7808 BA |
1158 | |
1159 | for (i = 0; i < ni_660x_num_counters(dev); ++i) | |
58dd7c0a | 1160 | ni_tio_init_counter(&private(dev)->counter_dev->counters[i]); |
900b7808 | 1161 | |
58dd7c0a M |
1162 | for (i = 0; i < NUM_PFI_CHANNELS; ++i) { |
1163 | if (i < min_counter_pfi_chan) | |
1164 | ni_660x_set_pfi_routing(dev, i, pfi_output_select_do); | |
1165 | else | |
1166 | ni_660x_set_pfi_routing(dev, i, | |
0a85b6f0 | 1167 | pfi_output_select_counter); |
58dd7c0a M |
1168 | ni_660x_select_pfi_output(dev, i, pfi_output_select_high_Z); |
1169 | } | |
1170 | /* to be safe, set counterswap bits on tio chips after all the counter | |
1171 | outputs have been set to high impedance mode */ | |
900b7808 | 1172 | for (i = 0; i < board(dev)->n_chips; ++i) |
58dd7c0a | 1173 | set_tio_counterswap(dev, i); |
900b7808 | 1174 | |
5f74ea14 GKH |
1175 | ret = request_irq(mite_irq(private(dev)->mite), ni_660x_interrupt, |
1176 | IRQF_SHARED, "ni_660x", dev); | |
c3744138 | 1177 | if (ret < 0) { |
900b7808 | 1178 | printk(KERN_WARNING " irq not available\n"); |
58dd7c0a M |
1179 | return ret; |
1180 | } | |
1181 | dev->irq = mite_irq(private(dev)->mite); | |
1182 | global_interrupt_config_bits = Global_Int_Enable_Bit; | |
1183 | if (board(dev)->n_chips > 1) | |
1184 | global_interrupt_config_bits |= Cascade_Int_Enable_Bit; | |
1185 | ni_660x_write_register(dev, 0, global_interrupt_config_bits, | |
0a85b6f0 | 1186 | GlobalInterruptConfigRegister); |
900b7808 | 1187 | printk(KERN_INFO "attached\n"); |
58dd7c0a M |
1188 | return 0; |
1189 | } | |
1190 | ||
da91b269 | 1191 | static int ni_660x_detach(struct comedi_device *dev) |
58dd7c0a | 1192 | { |
900b7808 | 1193 | printk(KERN_INFO "comedi%d: ni_660x: remove\n", dev->minor); |
58dd7c0a M |
1194 | |
1195 | /* Free irq */ | |
1196 | if (dev->irq) | |
5f74ea14 | 1197 | free_irq(dev->irq, dev); |
58dd7c0a M |
1198 | |
1199 | if (dev->private) { | |
1200 | if (private(dev)->counter_dev) | |
1201 | ni_gpct_device_destroy(private(dev)->counter_dev); | |
1202 | if (private(dev)->mite) { | |
1203 | ni_660x_free_mite_rings(dev); | |
1204 | mite_unsetup(private(dev)->mite); | |
1205 | } | |
1206 | } | |
1207 | return 0; | |
1208 | } | |
1209 | ||
1210 | static int | |
da91b269 | 1211 | ni_660x_GPCT_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 1212 | struct comedi_insn *insn, unsigned int *data) |
58dd7c0a M |
1213 | { |
1214 | return ni_tio_rinsn(subdev_to_counter(s), insn, data); | |
1215 | } | |
1216 | ||
da91b269 | 1217 | static void init_tio_chip(struct comedi_device *dev, int chipset) |
58dd7c0a M |
1218 | { |
1219 | unsigned i; | |
1220 | ||
2696fb57 | 1221 | /* init dma configuration register */ |
58dd7c0a M |
1222 | private(dev)->dma_configuration_soft_copies[chipset] = 0; |
1223 | for (i = 0; i < MAX_DMA_CHANNEL; ++i) { | |
1224 | private(dev)->dma_configuration_soft_copies[chipset] |= | |
0a85b6f0 | 1225 | dma_select_bits(i, dma_selection_none) & dma_select_mask(i); |
58dd7c0a M |
1226 | } |
1227 | ni_660x_write_register(dev, chipset, | |
0a85b6f0 MT |
1228 | private(dev)-> |
1229 | dma_configuration_soft_copies[chipset], | |
1230 | DMAConfigRegister); | |
900b7808 | 1231 | for (i = 0; i < NUM_PFI_CHANNELS; ++i) |
58dd7c0a | 1232 | ni_660x_write_register(dev, chipset, 0, IOConfigReg(i)); |
58dd7c0a M |
1233 | } |
1234 | ||
1235 | static int | |
da91b269 | 1236 | ni_660x_GPCT_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 1237 | struct comedi_insn *insn, unsigned int *data) |
58dd7c0a M |
1238 | { |
1239 | return ni_tio_insn_config(subdev_to_counter(s), insn, data); | |
1240 | } | |
1241 | ||
da91b269 | 1242 | static int ni_660x_GPCT_winsn(struct comedi_device *dev, |
0a85b6f0 MT |
1243 | struct comedi_subdevice *s, |
1244 | struct comedi_insn *insn, unsigned int *data) | |
58dd7c0a M |
1245 | { |
1246 | return ni_tio_winsn(subdev_to_counter(s), insn, data); | |
1247 | } | |
1248 | ||
da91b269 | 1249 | static int ni_660x_find_device(struct comedi_device *dev, int bus, int slot) |
58dd7c0a M |
1250 | { |
1251 | struct mite_struct *mite; | |
1252 | int i; | |
1253 | ||
1254 | for (mite = mite_devices; mite; mite = mite->next) { | |
1255 | if (mite->used) | |
1256 | continue; | |
1257 | if (bus || slot) { | |
1258 | if (bus != mite->pcidev->bus->number || | |
0a85b6f0 | 1259 | slot != PCI_SLOT(mite->pcidev->devfn)) |
58dd7c0a M |
1260 | continue; |
1261 | } | |
1262 | ||
1263 | for (i = 0; i < n_ni_660x_boards; i++) { | |
1264 | if (mite_device_id(mite) == ni_660x_boards[i].dev_id) { | |
1265 | dev->board_ptr = ni_660x_boards + i; | |
1266 | private(dev)->mite = mite; | |
1267 | return 0; | |
1268 | } | |
1269 | } | |
1270 | } | |
900b7808 | 1271 | printk(KERN_WARNING "no device found\n"); |
58dd7c0a M |
1272 | mite_list_devices(); |
1273 | return -EIO; | |
1274 | } | |
1275 | ||
da91b269 | 1276 | static int ni_660x_dio_insn_bits(struct comedi_device *dev, |
0a85b6f0 MT |
1277 | struct comedi_subdevice *s, |
1278 | struct comedi_insn *insn, unsigned int *data) | |
58dd7c0a M |
1279 | { |
1280 | unsigned base_bitfield_channel = CR_CHAN(insn->chanspec); | |
1281 | ||
2696fb57 | 1282 | /* Check if we have to write some bits */ |
58dd7c0a M |
1283 | if (data[0]) { |
1284 | s->state &= ~(data[0] << base_bitfield_channel); | |
1285 | s->state |= (data[0] & data[1]) << base_bitfield_channel; | |
1286 | /* Write out the new digital output lines */ | |
1287 | ni_660x_write_register(dev, 0, s->state, DIO32Output); | |
1288 | } | |
1289 | /* on return, data[1] contains the value of the digital | |
1290 | * input and output lines. */ | |
1291 | data[1] = | |
0a85b6f0 MT |
1292 | (ni_660x_read_register(dev, 0, |
1293 | DIO32Input) >> base_bitfield_channel); | |
58dd7c0a M |
1294 | return 2; |
1295 | } | |
1296 | ||
0a85b6f0 MT |
1297 | static void ni_660x_select_pfi_output(struct comedi_device *dev, |
1298 | unsigned pfi_channel, | |
1299 | unsigned output_select) | |
58dd7c0a M |
1300 | { |
1301 | static const unsigned counter_4_7_first_pfi = 8; | |
1302 | static const unsigned counter_4_7_last_pfi = 23; | |
1303 | unsigned active_chipset = 0; | |
1304 | unsigned idle_chipset = 0; | |
1305 | unsigned active_bits; | |
1306 | unsigned idle_bits; | |
1307 | ||
0a85b6f0 | 1308 | if (board(dev)->n_chips > 1) { |
53106ae6 | 1309 | if (output_select == pfi_output_select_counter && |
0a85b6f0 MT |
1310 | pfi_channel >= counter_4_7_first_pfi && |
1311 | pfi_channel <= counter_4_7_last_pfi) { | |
58dd7c0a M |
1312 | active_chipset = 1; |
1313 | idle_chipset = 0; | |
0a85b6f0 | 1314 | } else { |
58dd7c0a M |
1315 | active_chipset = 0; |
1316 | idle_chipset = 1; | |
1317 | } | |
1318 | } | |
1319 | ||
53106ae6 | 1320 | if (idle_chipset != active_chipset) { |
0a85b6f0 MT |
1321 | idle_bits = |
1322 | ni_660x_read_register(dev, idle_chipset, | |
1323 | IOConfigReg(pfi_channel)); | |
58dd7c0a | 1324 | idle_bits &= ~pfi_output_select_mask(pfi_channel); |
0a85b6f0 MT |
1325 | idle_bits |= |
1326 | pfi_output_select_bits(pfi_channel, | |
1327 | pfi_output_select_high_Z); | |
1328 | ni_660x_write_register(dev, idle_chipset, idle_bits, | |
1329 | IOConfigReg(pfi_channel)); | |
58dd7c0a M |
1330 | } |
1331 | ||
0a85b6f0 MT |
1332 | active_bits = |
1333 | ni_660x_read_register(dev, active_chipset, | |
1334 | IOConfigReg(pfi_channel)); | |
58dd7c0a M |
1335 | active_bits &= ~pfi_output_select_mask(pfi_channel); |
1336 | active_bits |= pfi_output_select_bits(pfi_channel, output_select); | |
0a85b6f0 MT |
1337 | ni_660x_write_register(dev, active_chipset, active_bits, |
1338 | IOConfigReg(pfi_channel)); | |
58dd7c0a M |
1339 | } |
1340 | ||
da91b269 | 1341 | static int ni_660x_set_pfi_routing(struct comedi_device *dev, unsigned chan, |
0a85b6f0 | 1342 | unsigned source) |
58dd7c0a M |
1343 | { |
1344 | if (source > num_pfi_output_selects) | |
1345 | return -EINVAL; | |
1346 | if (source == pfi_output_select_high_Z) | |
1347 | return -EINVAL; | |
1348 | if (chan < min_counter_pfi_chan) { | |
1349 | if (source == pfi_output_select_counter) | |
1350 | return -EINVAL; | |
1351 | } else if (chan > max_dio_pfi_chan) { | |
1352 | if (source == pfi_output_select_do) | |
1353 | return -EINVAL; | |
1354 | } | |
1355 | BUG_ON(chan >= NUM_PFI_CHANNELS); | |
1356 | ||
1357 | private(dev)->pfi_output_selects[chan] = source; | |
1358 | if (private(dev)->pfi_direction_bits & (((uint64_t) 1) << chan)) | |
1359 | ni_660x_select_pfi_output(dev, chan, | |
0a85b6f0 MT |
1360 | private(dev)-> |
1361 | pfi_output_selects[chan]); | |
58dd7c0a M |
1362 | return 0; |
1363 | } | |
1364 | ||
0a85b6f0 MT |
1365 | static unsigned ni_660x_get_pfi_routing(struct comedi_device *dev, |
1366 | unsigned chan) | |
58dd7c0a M |
1367 | { |
1368 | BUG_ON(chan >= NUM_PFI_CHANNELS); | |
1369 | return private(dev)->pfi_output_selects[chan]; | |
1370 | } | |
1371 | ||
0a85b6f0 MT |
1372 | static void ni660x_config_filter(struct comedi_device *dev, |
1373 | unsigned pfi_channel, | |
1374 | enum ni_gpct_filter_select filter) | |
58dd7c0a M |
1375 | { |
1376 | unsigned bits = ni_660x_read_register(dev, 0, IOConfigReg(pfi_channel)); | |
1377 | bits &= ~pfi_input_select_mask(pfi_channel); | |
1378 | bits |= pfi_input_select_bits(pfi_channel, filter); | |
1379 | ni_660x_write_register(dev, 0, bits, IOConfigReg(pfi_channel)); | |
1380 | } | |
1381 | ||
da91b269 | 1382 | static int ni_660x_dio_insn_config(struct comedi_device *dev, |
0a85b6f0 MT |
1383 | struct comedi_subdevice *s, |
1384 | struct comedi_insn *insn, unsigned int *data) | |
58dd7c0a M |
1385 | { |
1386 | int chan = CR_CHAN(insn->chanspec); | |
1387 | ||
1388 | /* The input or output configuration of each digital line is | |
1389 | * configured by a special insn_config instruction. chanspec | |
1390 | * contains the channel to be changed, and data[0] contains the | |
1391 | * value COMEDI_INPUT or COMEDI_OUTPUT. */ | |
1392 | ||
1393 | switch (data[0]) { | |
1394 | case INSN_CONFIG_DIO_OUTPUT: | |
1395 | private(dev)->pfi_direction_bits |= ((uint64_t) 1) << chan; | |
1396 | ni_660x_select_pfi_output(dev, chan, | |
0a85b6f0 MT |
1397 | private(dev)-> |
1398 | pfi_output_selects[chan]); | |
58dd7c0a M |
1399 | break; |
1400 | case INSN_CONFIG_DIO_INPUT: | |
1401 | private(dev)->pfi_direction_bits &= ~(((uint64_t) 1) << chan); | |
1402 | ni_660x_select_pfi_output(dev, chan, pfi_output_select_high_Z); | |
1403 | break; | |
1404 | case INSN_CONFIG_DIO_QUERY: | |
1405 | data[1] = | |
0a85b6f0 MT |
1406 | (private(dev)->pfi_direction_bits & |
1407 | (((uint64_t) 1) << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT; | |
58dd7c0a M |
1408 | return 0; |
1409 | case INSN_CONFIG_SET_ROUTING: | |
1410 | return ni_660x_set_pfi_routing(dev, chan, data[1]); | |
1411 | break; | |
1412 | case INSN_CONFIG_GET_ROUTING: | |
1413 | data[1] = ni_660x_get_pfi_routing(dev, chan); | |
1414 | break; | |
1415 | case INSN_CONFIG_FILTER: | |
1416 | ni660x_config_filter(dev, chan, data[1]); | |
1417 | break; | |
1418 | default: | |
1419 | return -EINVAL; | |
1420 | break; | |
1421 | }; | |
1422 | return 0; | |
1423 | } |