]>
Commit | Line | Data |
---|---|---|
e184e2be | 1 | // SPDX-License-Identifier: GPL-2.0+ |
58dd7c0a | 2 | /* |
aa36af20 | 3 | * Hardware driver for NI 660x devices |
aa36af20 | 4 | */ |
58dd7c0a M |
5 | |
6 | /* | |
71d92fac IA |
7 | * Driver: ni_660x |
8 | * Description: National Instruments 660x counter/timer boards | |
9 | * Devices: [National Instruments] PCI-6601 (ni_660x), PCI-6602, PXI-6602, | |
51c4ba64 | 10 | * PCI-6608, PXI-6608, PCI-6624, PXI-6624 |
71d92fac IA |
11 | * Author: J.P. Mellor <jpmellor@rose-hulman.edu>, |
12 | * Herman.Bruyninckx@mech.kuleuven.ac.be, | |
13 | * Wim.Meeussen@mech.kuleuven.ac.be, | |
14 | * Klaas.Gadeyne@mech.kuleuven.ac.be, | |
15 | * Frank Mori Hess <fmhess@users.sourceforge.net> | |
0d0ad829 | 16 | * Updated: Mon, 16 Jan 2017 14:00:43 +0000 |
71d92fac IA |
17 | * Status: experimental |
18 | * | |
19 | * Encoders work. PulseGeneration (both single pulse and pulse train) | |
20 | * works. Buffered commands work for input but not output. | |
c8c8ff88 | 21 | * |
71d92fac IA |
22 | * References: |
23 | * DAQ 660x Register-Level Programmer Manual (NI 370505A-01) | |
24 | * DAQ 6601/6602 User Manual (NI 322137B-01) | |
25 | */ | |
58dd7c0a | 26 | |
ce157f80 | 27 | #include <linux/module.h> |
25436dc9 | 28 | #include <linux/interrupt.h> |
33782dd5 | 29 | |
a6c760b1 | 30 | #include "../comedi_pci.h" |
33782dd5 | 31 | |
58dd7c0a M |
32 | #include "mite.h" |
33 | #include "ni_tio.h" | |
347e2448 | 34 | #include "ni_routes.h" |
58dd7c0a | 35 | |
58dd7c0a | 36 | /* See Register-Level Programmer Manual page 3.1 */ |
1246f05b | 37 | enum ni_660x_register { |
4f2c3b20 | 38 | /* see enum ni_gpct_register */ |
242311d1 HS |
39 | NI660X_STC_DIO_PARALLEL_INPUT = NITIO_NUM_REGS, |
40 | NI660X_STC_DIO_OUTPUT, | |
41 | NI660X_STC_DIO_CONTROL, | |
42 | NI660X_STC_DIO_SERIAL_INPUT, | |
1246f05b HS |
43 | NI660X_DIO32_INPUT, |
44 | NI660X_DIO32_OUTPUT, | |
45 | NI660X_CLK_CFG, | |
46 | NI660X_GLOBAL_INT_STATUS, | |
47 | NI660X_DMA_CFG, | |
48 | NI660X_GLOBAL_INT_CFG, | |
49 | NI660X_IO_CFG_0_1, | |
50 | NI660X_IO_CFG_2_3, | |
51 | NI660X_IO_CFG_4_5, | |
52 | NI660X_IO_CFG_6_7, | |
53 | NI660X_IO_CFG_8_9, | |
54 | NI660X_IO_CFG_10_11, | |
55 | NI660X_IO_CFG_12_13, | |
56 | NI660X_IO_CFG_14_15, | |
57 | NI660X_IO_CFG_16_17, | |
58 | NI660X_IO_CFG_18_19, | |
59 | NI660X_IO_CFG_20_21, | |
60 | NI660X_IO_CFG_22_23, | |
61 | NI660X_IO_CFG_24_25, | |
62 | NI660X_IO_CFG_26_27, | |
63 | NI660X_IO_CFG_28_29, | |
64 | NI660X_IO_CFG_30_31, | |
65 | NI660X_IO_CFG_32_33, | |
66 | NI660X_IO_CFG_34_35, | |
67 | NI660X_IO_CFG_36_37, | |
68 | NI660X_IO_CFG_38_39, | |
69 | NI660X_NUM_REGS, | |
251411cf | 70 | }; |
58dd7c0a | 71 | |
502552e1 HS |
72 | #define NI660X_CLK_CFG_COUNTER_SWAP BIT(21) |
73 | ||
41014593 HS |
74 | #define NI660X_GLOBAL_INT_COUNTER0 BIT(8) |
75 | #define NI660X_GLOBAL_INT_COUNTER1 BIT(9) | |
76 | #define NI660X_GLOBAL_INT_COUNTER2 BIT(10) | |
77 | #define NI660X_GLOBAL_INT_COUNTER3 BIT(11) | |
78 | #define NI660X_GLOBAL_INT_CASCADE BIT(29) | |
79 | #define NI660X_GLOBAL_INT_GLOBAL_POL BIT(30) | |
80 | #define NI660X_GLOBAL_INT_GLOBAL BIT(31) | |
81 | ||
fecf4cce HS |
82 | #define NI660X_DMA_CFG_SEL(_c, _s) (((_s) & 0x1f) << (8 * (_c))) |
83 | #define NI660X_DMA_CFG_SEL_MASK(_c) NI660X_DMA_CFG_SEL((_c), 0x1f) | |
84 | #define NI660X_DMA_CFG_SEL_NONE(_c) NI660X_DMA_CFG_SEL((_c), 0x1f) | |
85 | #define NI660X_DMA_CFG_RESET(_c) NI660X_DMA_CFG_SEL((_c), 0x80) | |
86 | ||
01ead0de HS |
87 | #define NI660X_IO_CFG(x) (NI660X_IO_CFG_0_1 + ((x) / 2)) |
88 | #define NI660X_IO_CFG_OUT_SEL(_c, _s) (((_s) & 0x3) << (((_c) % 2) ? 0 : 8)) | |
89 | #define NI660X_IO_CFG_OUT_SEL_MASK(_c) NI660X_IO_CFG_OUT_SEL((_c), 0x3) | |
01ead0de HS |
90 | #define NI660X_IO_CFG_IN_SEL(_c, _s) (((_s) & 0x7) << (((_c) % 2) ? 4 : 12)) |
91 | #define NI660X_IO_CFG_IN_SEL_MASK(_c) NI660X_IO_CFG_IN_SEL((_c), 0x7) | |
58dd7c0a | 92 | |
b38700a2 | 93 | struct ni_660x_register_data { |
2696fb57 | 94 | int offset; /* Offset from base address from GPCT chip */ |
87090141 | 95 | char size; /* 2 or 4 bytes */ |
0cb5e8ff BP |
96 | }; |
97 | ||
b38700a2 | 98 | static const struct ni_660x_register_data ni_660x_reg_data[NI660X_NUM_REGS] = { |
4f2c3b20 HS |
99 | [NITIO_G0_INT_ACK] = { 0x004, 2 }, /* write */ |
100 | [NITIO_G0_STATUS] = { 0x004, 2 }, /* read */ | |
101 | [NITIO_G1_INT_ACK] = { 0x006, 2 }, /* write */ | |
102 | [NITIO_G1_STATUS] = { 0x006, 2 }, /* read */ | |
103 | [NITIO_G01_STATUS] = { 0x008, 2 }, /* read */ | |
104 | [NITIO_G0_CMD] = { 0x00c, 2 }, /* write */ | |
9392e5dd | 105 | [NI660X_STC_DIO_PARALLEL_INPUT] = { 0x00e, 2 }, /* read */ |
4f2c3b20 HS |
106 | [NITIO_G1_CMD] = { 0x00e, 2 }, /* write */ |
107 | [NITIO_G0_HW_SAVE] = { 0x010, 4 }, /* read */ | |
108 | [NITIO_G1_HW_SAVE] = { 0x014, 4 }, /* read */ | |
9392e5dd HS |
109 | [NI660X_STC_DIO_OUTPUT] = { 0x014, 2 }, /* write */ |
110 | [NI660X_STC_DIO_CONTROL] = { 0x016, 2 }, /* write */ | |
4f2c3b20 HS |
111 | [NITIO_G0_SW_SAVE] = { 0x018, 4 }, /* read */ |
112 | [NITIO_G1_SW_SAVE] = { 0x01c, 4 }, /* read */ | |
113 | [NITIO_G0_MODE] = { 0x034, 2 }, /* write */ | |
114 | [NITIO_G01_STATUS1] = { 0x036, 2 }, /* read */ | |
115 | [NITIO_G1_MODE] = { 0x036, 2 }, /* write */ | |
9392e5dd | 116 | [NI660X_STC_DIO_SERIAL_INPUT] = { 0x038, 2 }, /* read */ |
4f2c3b20 HS |
117 | [NITIO_G0_LOADA] = { 0x038, 4 }, /* write */ |
118 | [NITIO_G01_STATUS2] = { 0x03a, 2 }, /* read */ | |
119 | [NITIO_G0_LOADB] = { 0x03c, 4 }, /* write */ | |
120 | [NITIO_G1_LOADA] = { 0x040, 4 }, /* write */ | |
121 | [NITIO_G1_LOADB] = { 0x044, 4 }, /* write */ | |
122 | [NITIO_G0_INPUT_SEL] = { 0x048, 2 }, /* write */ | |
123 | [NITIO_G1_INPUT_SEL] = { 0x04a, 2 }, /* write */ | |
124 | [NITIO_G0_AUTO_INC] = { 0x088, 2 }, /* write */ | |
125 | [NITIO_G1_AUTO_INC] = { 0x08a, 2 }, /* write */ | |
126 | [NITIO_G01_RESET] = { 0x090, 2 }, /* write */ | |
127 | [NITIO_G0_INT_ENA] = { 0x092, 2 }, /* write */ | |
128 | [NITIO_G1_INT_ENA] = { 0x096, 2 }, /* write */ | |
129 | [NITIO_G0_CNT_MODE] = { 0x0b0, 2 }, /* write */ | |
130 | [NITIO_G1_CNT_MODE] = { 0x0b2, 2 }, /* write */ | |
131 | [NITIO_G0_GATE2] = { 0x0b4, 2 }, /* write */ | |
132 | [NITIO_G1_GATE2] = { 0x0b6, 2 }, /* write */ | |
133 | [NITIO_G0_DMA_CFG] = { 0x0b8, 2 }, /* write */ | |
134 | [NITIO_G0_DMA_STATUS] = { 0x0b8, 2 }, /* read */ | |
135 | [NITIO_G1_DMA_CFG] = { 0x0ba, 2 }, /* write */ | |
136 | [NITIO_G1_DMA_STATUS] = { 0x0ba, 2 }, /* read */ | |
137 | [NITIO_G2_INT_ACK] = { 0x104, 2 }, /* write */ | |
138 | [NITIO_G2_STATUS] = { 0x104, 2 }, /* read */ | |
139 | [NITIO_G3_INT_ACK] = { 0x106, 2 }, /* write */ | |
140 | [NITIO_G3_STATUS] = { 0x106, 2 }, /* read */ | |
141 | [NITIO_G23_STATUS] = { 0x108, 2 }, /* read */ | |
142 | [NITIO_G2_CMD] = { 0x10c, 2 }, /* write */ | |
143 | [NITIO_G3_CMD] = { 0x10e, 2 }, /* write */ | |
144 | [NITIO_G2_HW_SAVE] = { 0x110, 4 }, /* read */ | |
145 | [NITIO_G3_HW_SAVE] = { 0x114, 4 }, /* read */ | |
146 | [NITIO_G2_SW_SAVE] = { 0x118, 4 }, /* read */ | |
147 | [NITIO_G3_SW_SAVE] = { 0x11c, 4 }, /* read */ | |
148 | [NITIO_G2_MODE] = { 0x134, 2 }, /* write */ | |
149 | [NITIO_G23_STATUS1] = { 0x136, 2 }, /* read */ | |
150 | [NITIO_G3_MODE] = { 0x136, 2 }, /* write */ | |
151 | [NITIO_G2_LOADA] = { 0x138, 4 }, /* write */ | |
152 | [NITIO_G23_STATUS2] = { 0x13a, 2 }, /* read */ | |
153 | [NITIO_G2_LOADB] = { 0x13c, 4 }, /* write */ | |
154 | [NITIO_G3_LOADA] = { 0x140, 4 }, /* write */ | |
155 | [NITIO_G3_LOADB] = { 0x144, 4 }, /* write */ | |
156 | [NITIO_G2_INPUT_SEL] = { 0x148, 2 }, /* write */ | |
157 | [NITIO_G3_INPUT_SEL] = { 0x14a, 2 }, /* write */ | |
158 | [NITIO_G2_AUTO_INC] = { 0x188, 2 }, /* write */ | |
159 | [NITIO_G3_AUTO_INC] = { 0x18a, 2 }, /* write */ | |
160 | [NITIO_G23_RESET] = { 0x190, 2 }, /* write */ | |
161 | [NITIO_G2_INT_ENA] = { 0x192, 2 }, /* write */ | |
162 | [NITIO_G3_INT_ENA] = { 0x196, 2 }, /* write */ | |
163 | [NITIO_G2_CNT_MODE] = { 0x1b0, 2 }, /* write */ | |
164 | [NITIO_G3_CNT_MODE] = { 0x1b2, 2 }, /* write */ | |
165 | [NITIO_G2_GATE2] = { 0x1b4, 2 }, /* write */ | |
166 | [NITIO_G3_GATE2] = { 0x1b6, 2 }, /* write */ | |
167 | [NITIO_G2_DMA_CFG] = { 0x1b8, 2 }, /* write */ | |
168 | [NITIO_G2_DMA_STATUS] = { 0x1b8, 2 }, /* read */ | |
169 | [NITIO_G3_DMA_CFG] = { 0x1ba, 2 }, /* write */ | |
170 | [NITIO_G3_DMA_STATUS] = { 0x1ba, 2 }, /* read */ | |
9392e5dd HS |
171 | [NI660X_DIO32_INPUT] = { 0x414, 4 }, /* read */ |
172 | [NI660X_DIO32_OUTPUT] = { 0x510, 4 }, /* write */ | |
173 | [NI660X_CLK_CFG] = { 0x73c, 4 }, /* write */ | |
174 | [NI660X_GLOBAL_INT_STATUS] = { 0x754, 4 }, /* read */ | |
175 | [NI660X_DMA_CFG] = { 0x76c, 4 }, /* write */ | |
176 | [NI660X_GLOBAL_INT_CFG] = { 0x770, 4 }, /* write */ | |
177 | [NI660X_IO_CFG_0_1] = { 0x77c, 2 }, /* read/write */ | |
178 | [NI660X_IO_CFG_2_3] = { 0x77e, 2 }, /* read/write */ | |
179 | [NI660X_IO_CFG_4_5] = { 0x780, 2 }, /* read/write */ | |
180 | [NI660X_IO_CFG_6_7] = { 0x782, 2 }, /* read/write */ | |
181 | [NI660X_IO_CFG_8_9] = { 0x784, 2 }, /* read/write */ | |
182 | [NI660X_IO_CFG_10_11] = { 0x786, 2 }, /* read/write */ | |
183 | [NI660X_IO_CFG_12_13] = { 0x788, 2 }, /* read/write */ | |
184 | [NI660X_IO_CFG_14_15] = { 0x78a, 2 }, /* read/write */ | |
185 | [NI660X_IO_CFG_16_17] = { 0x78c, 2 }, /* read/write */ | |
186 | [NI660X_IO_CFG_18_19] = { 0x78e, 2 }, /* read/write */ | |
187 | [NI660X_IO_CFG_20_21] = { 0x790, 2 }, /* read/write */ | |
188 | [NI660X_IO_CFG_22_23] = { 0x792, 2 }, /* read/write */ | |
189 | [NI660X_IO_CFG_24_25] = { 0x794, 2 }, /* read/write */ | |
190 | [NI660X_IO_CFG_26_27] = { 0x796, 2 }, /* read/write */ | |
191 | [NI660X_IO_CFG_28_29] = { 0x798, 2 }, /* read/write */ | |
192 | [NI660X_IO_CFG_30_31] = { 0x79a, 2 }, /* read/write */ | |
193 | [NI660X_IO_CFG_32_33] = { 0x79c, 2 }, /* read/write */ | |
194 | [NI660X_IO_CFG_34_35] = { 0x79e, 2 }, /* read/write */ | |
195 | [NI660X_IO_CFG_36_37] = { 0x7a0, 2 }, /* read/write */ | |
196 | [NI660X_IO_CFG_38_39] = { 0x7a2, 2 } /* read/write */ | |
58dd7c0a M |
197 | }; |
198 | ||
8f266d50 | 199 | #define NI660X_CHIP_OFFSET 0x800 |
58dd7c0a | 200 | |
97bcce5a HS |
201 | enum ni_660x_boardid { |
202 | BOARD_PCI6601, | |
203 | BOARD_PCI6602, | |
204 | BOARD_PXI6602, | |
51c4ba64 | 205 | BOARD_PCI6608, |
97bcce5a | 206 | BOARD_PXI6608, |
0d0ad829 | 207 | BOARD_PCI6624, |
8bdfefb7 | 208 | BOARD_PXI6624 |
97bcce5a HS |
209 | }; |
210 | ||
50792813 | 211 | struct ni_660x_board { |
58dd7c0a | 212 | const char *name; |
2225320e | 213 | unsigned int n_chips; /* total number of TIO chips */ |
50792813 | 214 | }; |
58dd7c0a | 215 | |
50792813 | 216 | static const struct ni_660x_board ni_660x_boards[] = { |
97bcce5a | 217 | [BOARD_PCI6601] = { |
e2b8360f HS |
218 | .name = "PCI-6601", |
219 | .n_chips = 1, | |
220 | }, | |
97bcce5a | 221 | [BOARD_PCI6602] = { |
e2b8360f HS |
222 | .name = "PCI-6602", |
223 | .n_chips = 2, | |
224 | }, | |
97bcce5a | 225 | [BOARD_PXI6602] = { |
e2b8360f HS |
226 | .name = "PXI-6602", |
227 | .n_chips = 2, | |
228 | }, | |
51c4ba64 SO |
229 | [BOARD_PCI6608] = { |
230 | .name = "PCI-6608", | |
231 | .n_chips = 2, | |
232 | }, | |
97bcce5a | 233 | [BOARD_PXI6608] = { |
e2b8360f HS |
234 | .name = "PXI-6608", |
235 | .n_chips = 2, | |
236 | }, | |
0d0ad829 IA |
237 | [BOARD_PCI6624] = { |
238 | .name = "PCI-6624", | |
239 | .n_chips = 2, | |
240 | }, | |
8bdfefb7 IA |
241 | [BOARD_PXI6624] = { |
242 | .name = "PXI-6624", | |
243 | .n_chips = 2, | |
244 | }, | |
58dd7c0a M |
245 | }; |
246 | ||
ccef0da8 HS |
247 | #define NI660X_NUM_PFI_CHANNELS 40 |
248 | ||
249 | /* there are only up to 3 dma channels, but the register layout allows for 4 */ | |
250 | #define NI660X_MAX_DMA_CHANNEL 4 | |
251 | ||
252 | #define NI660X_COUNTERS_PER_CHIP 4 | |
253 | #define NI660X_MAX_CHIPS 2 | |
254 | #define NI660X_MAX_COUNTERS (NI660X_MAX_CHIPS * \ | |
255 | NI660X_COUNTERS_PER_CHIP) | |
58dd7c0a | 256 | |
b3be94ea | 257 | struct ni_660x_private { |
1a8da31b | 258 | struct mite *mite; |
58dd7c0a | 259 | struct ni_gpct_device *counter_dev; |
19d9212e | 260 | struct mite_ring *ring[NI660X_MAX_CHIPS][NI660X_COUNTERS_PER_CHIP]; |
e8f6e2b9 | 261 | /* protects mite channel request/release */ |
58dd7c0a | 262 | spinlock_t mite_channel_lock; |
e8f6e2b9 | 263 | /* prevents races between interrupt and comedi_poll */ |
894db119 | 264 | spinlock_t interrupt_lock; |
ccef0da8 HS |
265 | unsigned int dma_cfg[NI660X_MAX_CHIPS]; |
266 | unsigned int io_cfg[NI660X_NUM_PFI_CHANNELS]; | |
aa94f288 | 267 | u64 io_dir; |
347e2448 | 268 | struct ni_route_tables routing_tables; |
b3be94ea | 269 | }; |
58dd7c0a | 270 | |
4f2c3b20 HS |
271 | static void ni_660x_write(struct comedi_device *dev, unsigned int chip, |
272 | unsigned int bits, unsigned int reg) | |
58dd7c0a | 273 | { |
8f266d50 HS |
274 | unsigned int addr = (chip * NI660X_CHIP_OFFSET) + |
275 | ni_660x_reg_data[reg].offset; | |
58dd7c0a | 276 | |
b38700a2 | 277 | if (ni_660x_reg_data[reg].size == 2) |
5f8a5f4f | 278 | writew(bits, dev->mmio + addr); |
87090141 | 279 | else |
5f8a5f4f | 280 | writel(bits, dev->mmio + addr); |
58dd7c0a M |
281 | } |
282 | ||
ad98c18c | 283 | static unsigned int ni_660x_read(struct comedi_device *dev, |
4f2c3b20 | 284 | unsigned int chip, unsigned int reg) |
58dd7c0a | 285 | { |
8f266d50 HS |
286 | unsigned int addr = (chip * NI660X_CHIP_OFFSET) + |
287 | ni_660x_reg_data[reg].offset; | |
58dd7c0a | 288 | |
b38700a2 | 289 | if (ni_660x_reg_data[reg].size == 2) |
5f8a5f4f | 290 | return readw(dev->mmio + addr); |
87090141 | 291 | return readl(dev->mmio + addr); |
58dd7c0a M |
292 | } |
293 | ||
dc285820 HS |
294 | static void ni_660x_gpct_write(struct ni_gpct *counter, unsigned int bits, |
295 | enum ni_gpct_register reg) | |
58dd7c0a | 296 | { |
71b5f4f1 | 297 | struct comedi_device *dev = counter->counter_dev->dev; |
0c9434e3 | 298 | |
4f2c3b20 | 299 | ni_660x_write(dev, counter->chip_index, bits, reg); |
58dd7c0a M |
300 | } |
301 | ||
dc285820 | 302 | static unsigned int ni_660x_gpct_read(struct ni_gpct *counter, |
0a85b6f0 | 303 | enum ni_gpct_register reg) |
58dd7c0a | 304 | { |
71b5f4f1 | 305 | struct comedi_device *dev = counter->counter_dev->dev; |
0c26c7ed | 306 | |
4f2c3b20 | 307 | return ni_660x_read(dev, counter->chip_index, reg); |
58dd7c0a M |
308 | } |
309 | ||
da91b269 | 310 | static inline void ni_660x_set_dma_channel(struct comedi_device *dev, |
2225320e | 311 | unsigned int mite_channel, |
0a85b6f0 | 312 | struct ni_gpct *counter) |
58dd7c0a | 313 | { |
8c12ec26 | 314 | struct ni_660x_private *devpriv = dev->private; |
2225320e | 315 | unsigned int chip = counter->chip_index; |
8c12ec26 | 316 | |
fecf4cce HS |
317 | devpriv->dma_cfg[chip] &= ~NI660X_DMA_CFG_SEL_MASK(mite_channel); |
318 | devpriv->dma_cfg[chip] |= NI660X_DMA_CFG_SEL(mite_channel, | |
319 | counter->counter_index); | |
9678b73e HS |
320 | ni_660x_write(dev, chip, devpriv->dma_cfg[chip] | |
321 | NI660X_DMA_CFG_RESET(mite_channel), | |
322 | NI660X_DMA_CFG); | |
58dd7c0a M |
323 | } |
324 | ||
da91b269 | 325 | static inline void ni_660x_unset_dma_channel(struct comedi_device *dev, |
2225320e | 326 | unsigned int mite_channel, |
0a85b6f0 | 327 | struct ni_gpct *counter) |
58dd7c0a | 328 | { |
8c12ec26 | 329 | struct ni_660x_private *devpriv = dev->private; |
2225320e | 330 | unsigned int chip = counter->chip_index; |
8c12ec26 | 331 | |
fecf4cce HS |
332 | devpriv->dma_cfg[chip] &= ~NI660X_DMA_CFG_SEL_MASK(mite_channel); |
333 | devpriv->dma_cfg[chip] |= NI660X_DMA_CFG_SEL_NONE(mite_channel); | |
9678b73e | 334 | ni_660x_write(dev, chip, devpriv->dma_cfg[chip], NI660X_DMA_CFG); |
58dd7c0a M |
335 | } |
336 | ||
da91b269 | 337 | static int ni_660x_request_mite_channel(struct comedi_device *dev, |
0a85b6f0 MT |
338 | struct ni_gpct *counter, |
339 | enum comedi_io_direction direction) | |
58dd7c0a | 340 | { |
8c12ec26 | 341 | struct ni_660x_private *devpriv = dev->private; |
19d9212e | 342 | struct mite_ring *ring; |
58dd7c0a | 343 | struct mite_channel *mite_chan; |
515b5e6b | 344 | unsigned long flags; |
58dd7c0a | 345 | |
8c12ec26 | 346 | spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
19d9212e | 347 | ring = devpriv->ring[counter->chip_index][counter->counter_index]; |
515b5e6b | 348 | mite_chan = mite_request_channel(devpriv->mite, ring); |
307da4b2 | 349 | if (!mite_chan) { |
8c12ec26 | 350 | spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
22bc059e HS |
351 | dev_err(dev->class_dev, |
352 | "failed to reserve mite dma channel for counter\n"); | |
58dd7c0a M |
353 | return -EBUSY; |
354 | } | |
355 | mite_chan->dir = direction; | |
356 | ni_tio_set_mite_channel(counter, mite_chan); | |
357 | ni_660x_set_dma_channel(dev, mite_chan->channel, counter); | |
8c12ec26 | 358 | spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
58dd7c0a M |
359 | return 0; |
360 | } | |
361 | ||
ce48a911 HS |
362 | static void ni_660x_release_mite_channel(struct comedi_device *dev, |
363 | struct ni_gpct *counter) | |
58dd7c0a | 364 | { |
8c12ec26 | 365 | struct ni_660x_private *devpriv = dev->private; |
58dd7c0a M |
366 | unsigned long flags; |
367 | ||
8c12ec26 | 368 | spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
58dd7c0a M |
369 | if (counter->mite_chan) { |
370 | struct mite_channel *mite_chan = counter->mite_chan; | |
371 | ||
372 | ni_660x_unset_dma_channel(dev, mite_chan->channel, counter); | |
373 | ni_tio_set_mite_channel(counter, NULL); | |
374 | mite_release_channel(mite_chan); | |
375 | } | |
8c12ec26 | 376 | spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
58dd7c0a M |
377 | } |
378 | ||
da91b269 | 379 | static int ni_660x_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
58dd7c0a | 380 | { |
00edbc31 | 381 | struct ni_gpct *counter = s->private; |
58dd7c0a M |
382 | int retval; |
383 | ||
58dd7c0a M |
384 | retval = ni_660x_request_mite_channel(dev, counter, COMEDI_INPUT); |
385 | if (retval) { | |
22bc059e HS |
386 | dev_err(dev->class_dev, |
387 | "no dma channel available for use by counter\n"); | |
58dd7c0a M |
388 | return retval; |
389 | } | |
f8cfd0eb | 390 | ni_tio_acknowledge(counter); |
58dd7c0a | 391 | |
16cc181d | 392 | return ni_tio_cmd(dev, s); |
58dd7c0a M |
393 | } |
394 | ||
da91b269 | 395 | static int ni_660x_cancel(struct comedi_device *dev, struct comedi_subdevice *s) |
58dd7c0a | 396 | { |
00edbc31 | 397 | struct ni_gpct *counter = s->private; |
58dd7c0a M |
398 | int retval; |
399 | ||
400 | retval = ni_tio_cancel(counter); | |
401 | ni_660x_release_mite_channel(dev, counter); | |
402 | return retval; | |
403 | } | |
404 | ||
01b6442b | 405 | static void set_tio_counterswap(struct comedi_device *dev, int chip) |
58dd7c0a | 406 | { |
2225320e | 407 | unsigned int bits = 0; |
01b6442b HS |
408 | |
409 | /* | |
410 | * See P. 3.5 of the Register-Level Programming manual. | |
411 | * The CounterSwap bit has to be set on the second chip, | |
412 | * otherwise it will try to use the same pins as the | |
413 | * first chip. | |
58dd7c0a | 414 | */ |
01b6442b | 415 | if (chip) |
502552e1 | 416 | bits = NI660X_CLK_CFG_COUNTER_SWAP; |
01b6442b | 417 | |
9678b73e | 418 | ni_660x_write(dev, chip, bits, NI660X_CLK_CFG); |
58dd7c0a M |
419 | } |
420 | ||
da91b269 | 421 | static void ni_660x_handle_gpct_interrupt(struct comedi_device *dev, |
0a85b6f0 | 422 | struct comedi_subdevice *s) |
58dd7c0a | 423 | { |
00edbc31 HS |
424 | struct ni_gpct *counter = s->private; |
425 | ||
426 | ni_tio_handle_interrupt(counter, s); | |
9e1a0824 | 427 | comedi_handle_events(dev, s); |
58dd7c0a M |
428 | } |
429 | ||
70265d24 | 430 | static irqreturn_t ni_660x_interrupt(int irq, void *d) |
58dd7c0a | 431 | { |
71b5f4f1 | 432 | struct comedi_device *dev = d; |
8c12ec26 | 433 | struct ni_660x_private *devpriv = dev->private; |
34c43922 | 434 | struct comedi_subdevice *s; |
2225320e | 435 | unsigned int i; |
894db119 | 436 | unsigned long flags; |
58dd7c0a | 437 | |
a7401cdd | 438 | if (!dev->attached) |
58dd7c0a | 439 | return IRQ_NONE; |
5262d035 HS |
440 | /* make sure dev->attached is checked before doing anything else */ |
441 | smp_mb(); | |
442 | ||
894db119 | 443 | /* lock to avoid race with comedi_poll */ |
8c12ec26 | 444 | spin_lock_irqsave(&devpriv->interrupt_lock, flags); |
520e6191 HS |
445 | for (i = 0; i < dev->n_subdevices; ++i) { |
446 | s = &dev->subdevices[i]; | |
447 | if (s->type == COMEDI_SUBD_COUNTER) | |
448 | ni_660x_handle_gpct_interrupt(dev, s); | |
58dd7c0a | 449 | } |
8c12ec26 | 450 | spin_unlock_irqrestore(&devpriv->interrupt_lock, flags); |
58dd7c0a M |
451 | return IRQ_HANDLED; |
452 | } | |
453 | ||
894db119 FMH |
454 | static int ni_660x_input_poll(struct comedi_device *dev, |
455 | struct comedi_subdevice *s) | |
456 | { | |
8c12ec26 | 457 | struct ni_660x_private *devpriv = dev->private; |
00edbc31 | 458 | struct ni_gpct *counter = s->private; |
894db119 | 459 | unsigned long flags; |
8c12ec26 | 460 | |
894db119 | 461 | /* lock to avoid race with comedi_poll */ |
8c12ec26 | 462 | spin_lock_irqsave(&devpriv->interrupt_lock, flags); |
51d43005 | 463 | mite_sync_dma(counter->mite_chan, s); |
8c12ec26 | 464 | spin_unlock_irqrestore(&devpriv->interrupt_lock, flags); |
e9edef3a | 465 | return comedi_buf_read_n_available(s); |
894db119 FMH |
466 | } |
467 | ||
0a85b6f0 | 468 | static int ni_660x_buf_change(struct comedi_device *dev, |
d546b896 | 469 | struct comedi_subdevice *s) |
58dd7c0a | 470 | { |
8c12ec26 | 471 | struct ni_660x_private *devpriv = dev->private; |
00edbc31 | 472 | struct ni_gpct *counter = s->private; |
19d9212e | 473 | struct mite_ring *ring; |
58dd7c0a M |
474 | int ret; |
475 | ||
19d9212e | 476 | ring = devpriv->ring[counter->chip_index][counter->counter_index]; |
515b5e6b | 477 | ret = mite_buf_change(ring, s); |
58dd7c0a M |
478 | if (ret < 0) |
479 | return ret; | |
480 | ||
481 | return 0; | |
482 | } | |
483 | ||
da91b269 | 484 | static int ni_660x_allocate_private(struct comedi_device *dev) |
58dd7c0a | 485 | { |
8c12ec26 | 486 | struct ni_660x_private *devpriv; |
2225320e | 487 | unsigned int i; |
58dd7c0a | 488 | |
0bdab509 | 489 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
c34fa261 HS |
490 | if (!devpriv) |
491 | return -ENOMEM; | |
c3744138 | 492 | |
8c12ec26 HS |
493 | spin_lock_init(&devpriv->mite_channel_lock); |
494 | spin_lock_init(&devpriv->interrupt_lock); | |
ccef0da8 | 495 | for (i = 0; i < NI660X_NUM_PFI_CHANNELS; ++i) |
aa94f288 | 496 | devpriv->io_cfg[i] = NI_660X_PFI_OUTPUT_COUNTER; |
900b7808 | 497 | |
58dd7c0a M |
498 | return 0; |
499 | } | |
500 | ||
da91b269 | 501 | static int ni_660x_alloc_mite_rings(struct comedi_device *dev) |
58dd7c0a | 502 | { |
da8e2a52 | 503 | const struct ni_660x_board *board = dev->board_ptr; |
8c12ec26 | 504 | struct ni_660x_private *devpriv = dev->private; |
2225320e HS |
505 | unsigned int i; |
506 | unsigned int j; | |
58dd7c0a | 507 | |
9186ccde | 508 | for (i = 0; i < board->n_chips; ++i) { |
ccef0da8 | 509 | for (j = 0; j < NI660X_COUNTERS_PER_CHIP; ++j) { |
19d9212e HS |
510 | devpriv->ring[i][j] = mite_alloc_ring(devpriv->mite); |
511 | if (!devpriv->ring[i][j]) | |
58dd7c0a | 512 | return -ENOMEM; |
58dd7c0a M |
513 | } |
514 | } | |
515 | return 0; | |
516 | } | |
517 | ||
da91b269 | 518 | static void ni_660x_free_mite_rings(struct comedi_device *dev) |
58dd7c0a | 519 | { |
da8e2a52 | 520 | const struct ni_660x_board *board = dev->board_ptr; |
8c12ec26 | 521 | struct ni_660x_private *devpriv = dev->private; |
2225320e HS |
522 | unsigned int i; |
523 | unsigned int j; | |
58dd7c0a | 524 | |
9186ccde | 525 | for (i = 0; i < board->n_chips; ++i) { |
ccef0da8 | 526 | for (j = 0; j < NI660X_COUNTERS_PER_CHIP; ++j) |
19d9212e | 527 | mite_free_ring(devpriv->ring[i][j]); |
58dd7c0a M |
528 | } |
529 | } | |
530 | ||
da91b269 | 531 | static int ni_660x_dio_insn_bits(struct comedi_device *dev, |
0a85b6f0 | 532 | struct comedi_subdevice *s, |
826f783a HS |
533 | struct comedi_insn *insn, |
534 | unsigned int *data) | |
58dd7c0a | 535 | { |
826f783a HS |
536 | unsigned int shift = CR_CHAN(insn->chanspec); |
537 | unsigned int mask = data[0] << shift; | |
538 | unsigned int bits = data[1] << shift; | |
58dd7c0a | 539 | |
826f783a HS |
540 | /* |
541 | * There are 40 channels in this subdevice but only 32 are usable | |
542 | * as DIO. The shift adjusts the mask/bits to account for the base | |
543 | * channel in insn->chanspec. The state update can then be handled | |
544 | * normally for the 32 usable channels. | |
545 | */ | |
546 | if (mask) { | |
547 | s->state &= ~mask; | |
548 | s->state |= (bits & mask); | |
9678b73e | 549 | ni_660x_write(dev, 0, s->state, NI660X_DIO32_OUTPUT); |
58dd7c0a | 550 | } |
826f783a HS |
551 | |
552 | /* | |
553 | * Return the input channels, shifted back to account for the base | |
554 | * channel. | |
555 | */ | |
556 | data[1] = ni_660x_read(dev, 0, NI660X_DIO32_INPUT) >> shift; | |
1246f05b | 557 | |
a2714e3e | 558 | return insn->n; |
58dd7c0a M |
559 | } |
560 | ||
0a85b6f0 | 561 | static void ni_660x_select_pfi_output(struct comedi_device *dev, |
518d3842 | 562 | unsigned int chan, unsigned int out_sel) |
58dd7c0a | 563 | { |
da8e2a52 | 564 | const struct ni_660x_board *board = dev->board_ptr; |
518d3842 HS |
565 | unsigned int active_chip = 0; |
566 | unsigned int idle_chip = 0; | |
567 | unsigned int bits; | |
58dd7c0a | 568 | |
fa86c007 SO |
569 | if (chan >= NI_PFI(0)) |
570 | /* allow new and old names of pfi channels to work. */ | |
571 | chan -= NI_PFI(0); | |
572 | ||
9186ccde | 573 | if (board->n_chips > 1) { |
aa94f288 | 574 | if (out_sel == NI_660X_PFI_OUTPUT_COUNTER && |
518d3842 HS |
575 | chan >= 8 && chan <= 23) { |
576 | /* counters 4-7 pfi channels */ | |
577 | active_chip = 1; | |
578 | idle_chip = 0; | |
0a85b6f0 | 579 | } else { |
518d3842 HS |
580 | /* counters 0-3 pfi channels */ |
581 | active_chip = 0; | |
582 | idle_chip = 1; | |
58dd7c0a M |
583 | } |
584 | } | |
585 | ||
518d3842 HS |
586 | if (idle_chip != active_chip) { |
587 | /* set the pfi channel to high-z on the inactive chip */ | |
588 | bits = ni_660x_read(dev, idle_chip, NI660X_IO_CFG(chan)); | |
589 | bits &= ~NI660X_IO_CFG_OUT_SEL_MASK(chan); | |
aa94f288 | 590 | bits |= NI660X_IO_CFG_OUT_SEL(chan, 0); /* high-z */ |
518d3842 | 591 | ni_660x_write(dev, idle_chip, bits, NI660X_IO_CFG(chan)); |
58dd7c0a M |
592 | } |
593 | ||
518d3842 HS |
594 | /* set the pfi channel output on the active chip */ |
595 | bits = ni_660x_read(dev, active_chip, NI660X_IO_CFG(chan)); | |
596 | bits &= ~NI660X_IO_CFG_OUT_SEL_MASK(chan); | |
597 | bits |= NI660X_IO_CFG_OUT_SEL(chan, out_sel); | |
598 | ni_660x_write(dev, active_chip, bits, NI660X_IO_CFG(chan)); | |
58dd7c0a M |
599 | } |
600 | ||
a0c5e846 SO |
601 | static void ni_660x_set_pfi_direction(struct comedi_device *dev, |
602 | unsigned int chan, | |
603 | unsigned int direction) | |
604 | { | |
605 | struct ni_660x_private *devpriv = dev->private; | |
606 | u64 bit; | |
607 | ||
fa86c007 SO |
608 | if (chan >= NI_PFI(0)) |
609 | /* allow new and old names of pfi channels to work. */ | |
610 | chan -= NI_PFI(0); | |
611 | ||
a0c5e846 SO |
612 | bit = 1ULL << chan; |
613 | ||
614 | if (direction == COMEDI_OUTPUT) { | |
615 | devpriv->io_dir |= bit; | |
616 | /* reset the output to currently assigned output value */ | |
617 | ni_660x_select_pfi_output(dev, chan, devpriv->io_cfg[chan]); | |
618 | } else { | |
619 | devpriv->io_dir &= ~bit; | |
620 | /* set pin to high-z; do not change currently assigned route */ | |
621 | ni_660x_select_pfi_output(dev, chan, 0); | |
622 | } | |
623 | } | |
624 | ||
625 | static unsigned int ni_660x_get_pfi_direction(struct comedi_device *dev, | |
626 | unsigned int chan) | |
627 | { | |
628 | struct ni_660x_private *devpriv = dev->private; | |
629 | u64 bit; | |
630 | ||
fa86c007 SO |
631 | if (chan >= NI_PFI(0)) |
632 | /* allow new and old names of pfi channels to work. */ | |
633 | chan -= NI_PFI(0); | |
634 | ||
a0c5e846 SO |
635 | bit = 1ULL << chan; |
636 | ||
637 | return (devpriv->io_dir & bit) ? COMEDI_OUTPUT : COMEDI_INPUT; | |
638 | } | |
639 | ||
2225320e HS |
640 | static int ni_660x_set_pfi_routing(struct comedi_device *dev, |
641 | unsigned int chan, unsigned int source) | |
58dd7c0a | 642 | { |
8c12ec26 HS |
643 | struct ni_660x_private *devpriv = dev->private; |
644 | ||
fa86c007 SO |
645 | if (chan >= NI_PFI(0)) |
646 | /* allow new and old names of pfi channels to work. */ | |
647 | chan -= NI_PFI(0); | |
648 | ||
aa94f288 HS |
649 | switch (source) { |
650 | case NI_660X_PFI_OUTPUT_COUNTER: | |
651 | if (chan < 8) | |
58dd7c0a | 652 | return -EINVAL; |
aa94f288 HS |
653 | break; |
654 | case NI_660X_PFI_OUTPUT_DIO: | |
655 | if (chan > 31) | |
58dd7c0a | 656 | return -EINVAL; |
479826cc | 657 | break; |
aa94f288 HS |
658 | default: |
659 | return -EINVAL; | |
58dd7c0a | 660 | } |
58dd7c0a | 661 | |
aa94f288 | 662 | devpriv->io_cfg[chan] = source; |
a0c5e846 | 663 | if (ni_660x_get_pfi_direction(dev, chan) == COMEDI_OUTPUT) |
aa94f288 | 664 | ni_660x_select_pfi_output(dev, chan, devpriv->io_cfg[chan]); |
58dd7c0a M |
665 | return 0; |
666 | } | |
667 | ||
a0c5e846 SO |
668 | static int ni_660x_get_pfi_routing(struct comedi_device *dev, unsigned int chan) |
669 | { | |
670 | struct ni_660x_private *devpriv = dev->private; | |
671 | ||
fa86c007 SO |
672 | if (chan >= NI_PFI(0)) |
673 | /* allow new and old names of pfi channels to work. */ | |
674 | chan -= NI_PFI(0); | |
675 | ||
a0c5e846 SO |
676 | return devpriv->io_cfg[chan]; |
677 | } | |
678 | ||
679 | static void ni_660x_set_pfi_filter(struct comedi_device *dev, | |
680 | unsigned int chan, unsigned int value) | |
681 | { | |
682 | unsigned int val; | |
683 | ||
fa86c007 SO |
684 | if (chan >= NI_PFI(0)) |
685 | /* allow new and old names of pfi channels to work. */ | |
686 | chan -= NI_PFI(0); | |
687 | ||
a0c5e846 SO |
688 | val = ni_660x_read(dev, 0, NI660X_IO_CFG(chan)); |
689 | val &= ~NI660X_IO_CFG_IN_SEL_MASK(chan); | |
690 | val |= NI660X_IO_CFG_IN_SEL(chan, value); | |
691 | ni_660x_write(dev, 0, val, NI660X_IO_CFG(chan)); | |
692 | } | |
693 | ||
da91b269 | 694 | static int ni_660x_dio_insn_config(struct comedi_device *dev, |
0a85b6f0 | 695 | struct comedi_subdevice *s, |
56c645ff HS |
696 | struct comedi_insn *insn, |
697 | unsigned int *data) | |
58dd7c0a | 698 | { |
56c645ff | 699 | unsigned int chan = CR_CHAN(insn->chanspec); |
56c645ff | 700 | int ret; |
58dd7c0a M |
701 | |
702 | switch (data[0]) { | |
703 | case INSN_CONFIG_DIO_OUTPUT: | |
a0c5e846 | 704 | ni_660x_set_pfi_direction(dev, chan, COMEDI_OUTPUT); |
58dd7c0a | 705 | break; |
56c645ff | 706 | |
58dd7c0a | 707 | case INSN_CONFIG_DIO_INPUT: |
a0c5e846 | 708 | ni_660x_set_pfi_direction(dev, chan, COMEDI_INPUT); |
58dd7c0a | 709 | break; |
56c645ff | 710 | |
58dd7c0a | 711 | case INSN_CONFIG_DIO_QUERY: |
a0c5e846 | 712 | data[1] = ni_660x_get_pfi_direction(dev, chan); |
56c645ff HS |
713 | break; |
714 | ||
58dd7c0a | 715 | case INSN_CONFIG_SET_ROUTING: |
56c645ff HS |
716 | ret = ni_660x_set_pfi_routing(dev, chan, data[1]); |
717 | if (ret) | |
718 | return ret; | |
58dd7c0a | 719 | break; |
56c645ff | 720 | |
58dd7c0a | 721 | case INSN_CONFIG_GET_ROUTING: |
a0c5e846 | 722 | data[1] = ni_660x_get_pfi_routing(dev, chan); |
58dd7c0a | 723 | break; |
56c645ff | 724 | |
58dd7c0a | 725 | case INSN_CONFIG_FILTER: |
a0c5e846 | 726 | ni_660x_set_pfi_filter(dev, chan, data[1]); |
58dd7c0a | 727 | break; |
56c645ff | 728 | |
58dd7c0a M |
729 | default: |
730 | return -EINVAL; | |
95cd17c9 | 731 | } |
56c645ff HS |
732 | |
733 | return insn->n; | |
58dd7c0a | 734 | } |
3c323c01 | 735 | |
fa86c007 SO |
736 | static unsigned int _ni_get_valid_routes(struct comedi_device *dev, |
737 | unsigned int n_pairs, | |
738 | unsigned int *pair_data) | |
739 | { | |
740 | struct ni_660x_private *devpriv = dev->private; | |
741 | ||
742 | return ni_get_valid_routes(&devpriv->routing_tables, n_pairs, | |
743 | pair_data); | |
744 | } | |
745 | ||
746 | /* | |
747 | * Retrieves the current source of the output selector for the given | |
748 | * destination. If the terminal for the destination is not already configured | |
749 | * as an output, this function returns -EINVAL as error. | |
750 | * | |
751 | * Return: The register value of the destination output selector; | |
752 | * -EINVAL if terminal is not configured for output. | |
753 | */ | |
754 | static inline int get_output_select_source(int dest, struct comedi_device *dev) | |
755 | { | |
756 | struct ni_660x_private *devpriv = dev->private; | |
757 | int reg = -1; | |
758 | ||
759 | if (channel_is_pfi(dest)) { | |
760 | if (ni_660x_get_pfi_direction(dev, dest) == COMEDI_OUTPUT) | |
761 | reg = ni_660x_get_pfi_routing(dev, dest); | |
762 | } else if (channel_is_rtsi(dest)) { | |
763 | dev_dbg(dev->class_dev, | |
764 | "%s: unhandled rtsi destination (%d) queried\n", | |
765 | __func__, dest); | |
766 | /* | |
767 | * The following can be enabled when RTSI routing info is | |
768 | * determined (not currently documented): | |
769 | * if (ni_get_rtsi_direction(dev, dest) == COMEDI_OUTPUT) { | |
770 | * reg = ni_get_rtsi_routing(dev, dest); | |
771 | ||
772 | * if (reg == NI_RTSI_OUTPUT_RGOUT0) { | |
773 | * dest = NI_RGOUT0; ** prepare for lookup below ** | |
774 | * reg = get_rgout0_reg(dev); | |
775 | * } else if (reg >= NI_RTSI_OUTPUT_RTSI_BRD(0) && | |
776 | * reg <= NI_RTSI_OUTPUT_RTSI_BRD(3)) { | |
777 | * const int i = reg - NI_RTSI_OUTPUT_RTSI_BRD(0); | |
778 | ||
779 | * dest = NI_RTSI_BRD(i); ** prepare for lookup ** | |
780 | * reg = get_ith_rtsi_brd_reg(i, dev); | |
781 | * } | |
782 | * } | |
783 | */ | |
784 | } else if (channel_is_ctr(dest)) { | |
785 | reg = ni_tio_get_routing(devpriv->counter_dev, dest); | |
786 | } else { | |
787 | dev_dbg(dev->class_dev, | |
788 | "%s: unhandled destination (%d) queried\n", | |
789 | __func__, dest); | |
790 | } | |
791 | ||
792 | if (reg >= 0) | |
793 | return ni_find_route_source(CR_CHAN(reg), dest, | |
794 | &devpriv->routing_tables); | |
795 | return -EINVAL; | |
796 | } | |
797 | ||
798 | /* | |
799 | * Test a route: | |
800 | * | |
801 | * Return: -1 if not connectible; | |
802 | * 0 if connectible and not connected; | |
803 | * 1 if connectible and connected. | |
804 | */ | |
805 | static inline int test_route(unsigned int src, unsigned int dest, | |
806 | struct comedi_device *dev) | |
807 | { | |
808 | struct ni_660x_private *devpriv = dev->private; | |
809 | s8 reg = ni_route_to_register(CR_CHAN(src), dest, | |
810 | &devpriv->routing_tables); | |
811 | ||
812 | if (reg < 0) | |
813 | return -1; | |
814 | if (get_output_select_source(dest, dev) != CR_CHAN(src)) | |
815 | return 0; | |
816 | return 1; | |
817 | } | |
818 | ||
819 | /* Connect the actual route. */ | |
820 | static inline int connect_route(unsigned int src, unsigned int dest, | |
821 | struct comedi_device *dev) | |
822 | { | |
823 | struct ni_660x_private *devpriv = dev->private; | |
824 | s8 reg = ni_route_to_register(CR_CHAN(src), dest, | |
825 | &devpriv->routing_tables); | |
826 | s8 current_src; | |
827 | ||
828 | if (reg < 0) | |
829 | /* route is not valid */ | |
830 | return -EINVAL; | |
831 | ||
832 | current_src = get_output_select_source(dest, dev); | |
833 | if (current_src == CR_CHAN(src)) | |
834 | return -EALREADY; | |
835 | if (current_src >= 0) | |
836 | /* destination mux is already busy. complain, don't overwrite */ | |
837 | return -EBUSY; | |
838 | ||
839 | /* The route is valid and available. Now connect... */ | |
840 | if (channel_is_pfi(CR_CHAN(dest))) { | |
841 | /* | |
842 | * set routing and then direction so that the output does not | |
843 | * first get generated with the wrong pin | |
844 | */ | |
845 | ni_660x_set_pfi_routing(dev, dest, reg); | |
846 | ni_660x_set_pfi_direction(dev, dest, COMEDI_OUTPUT); | |
847 | } else if (channel_is_rtsi(CR_CHAN(dest))) { | |
848 | dev_dbg(dev->class_dev, "%s: unhandled rtsi destination (%d)\n", | |
849 | __func__, dest); | |
850 | return -EINVAL; | |
851 | /* | |
852 | * The following can be enabled when RTSI routing info is | |
853 | * determined (not currently documented): | |
854 | * if (reg == NI_RTSI_OUTPUT_RGOUT0) { | |
855 | * int ret = incr_rgout0_src_use(src, dev); | |
856 | ||
857 | * if (ret < 0) | |
858 | * return ret; | |
859 | * } else if (ni_rtsi_route_requires_mux(reg)) { | |
860 | * ** Attempt to allocate and route (src->brd) ** | |
861 | * int brd = incr_rtsi_brd_src_use(src, dev); | |
862 | ||
863 | * if (brd < 0) | |
864 | * return brd; | |
865 | ||
866 | * ** Now lookup the register value for (brd->dest) ** | |
867 | * reg = ni_lookup_route_register(brd, CR_CHAN(dest), | |
868 | * &devpriv->routing_tables); | |
869 | * } | |
870 | ||
871 | * ni_set_rtsi_direction(dev, dest, COMEDI_OUTPUT); | |
872 | * ni_set_rtsi_routing(dev, dest, reg); | |
873 | */ | |
874 | } else if (channel_is_ctr(CR_CHAN(dest))) { | |
875 | /* | |
876 | * we are adding back the channel modifier info to set | |
877 | * invert/edge info passed by the user | |
878 | */ | |
879 | ni_tio_set_routing(devpriv->counter_dev, dest, | |
880 | reg | (src & ~CR_CHAN(-1))); | |
881 | } else { | |
882 | return -EINVAL; | |
883 | } | |
884 | return 0; | |
885 | } | |
886 | ||
887 | static inline int disconnect_route(unsigned int src, unsigned int dest, | |
888 | struct comedi_device *dev) | |
889 | { | |
890 | struct ni_660x_private *devpriv = dev->private; | |
891 | s8 reg = ni_route_to_register(CR_CHAN(src), CR_CHAN(dest), | |
892 | &devpriv->routing_tables); | |
893 | ||
894 | if (reg < 0) | |
895 | /* route is not valid */ | |
896 | return -EINVAL; | |
897 | if (get_output_select_source(dest, dev) != CR_CHAN(src)) | |
898 | /* cannot disconnect something not connected */ | |
899 | return -EINVAL; | |
900 | ||
901 | /* The route is valid and is connected. Now disconnect... */ | |
902 | if (channel_is_pfi(CR_CHAN(dest))) { | |
903 | unsigned int source = ((CR_CHAN(dest) - NI_PFI(0)) < 8) | |
904 | ? NI_660X_PFI_OUTPUT_DIO | |
905 | : NI_660X_PFI_OUTPUT_COUNTER; | |
906 | ||
907 | /* set the pfi to high impedance, and disconnect */ | |
908 | ni_660x_set_pfi_direction(dev, dest, COMEDI_INPUT); | |
909 | ni_660x_set_pfi_routing(dev, dest, source); | |
910 | } else if (channel_is_rtsi(CR_CHAN(dest))) { | |
911 | dev_dbg(dev->class_dev, "%s: unhandled rtsi destination (%d)\n", | |
912 | __func__, dest); | |
913 | return -EINVAL; | |
914 | /* | |
915 | * The following can be enabled when RTSI routing info is | |
916 | * determined (not currently documented): | |
917 | * if (reg == NI_RTSI_OUTPUT_RGOUT0) { | |
918 | * int ret = decr_rgout0_src_use(src, dev); | |
919 | ||
920 | * if (ret < 0) | |
921 | * return ret; | |
922 | * } else if (ni_rtsi_route_requires_mux(reg)) { | |
923 | * ** find which RTSI_BRD line is source for rtsi pin ** | |
924 | * int brd = ni_find_route_source( | |
925 | * ni_get_rtsi_routing(dev, dest), CR_CHAN(dest), | |
926 | * &devpriv->routing_tables); | |
927 | ||
928 | * if (brd < 0) | |
929 | * return brd; | |
930 | ||
931 | * ** decrement/disconnect RTSI_BRD line from source ** | |
932 | * decr_rtsi_brd_src_use(src, brd, dev); | |
933 | * } | |
934 | ||
935 | * ** set rtsi output selector to default state ** | |
936 | * reg = default_rtsi_routing[CR_CHAN(dest) - TRIGGER_LINE(0)]; | |
937 | * ni_set_rtsi_direction(dev, dest, COMEDI_INPUT); | |
938 | * ni_set_rtsi_routing(dev, dest, reg); | |
939 | */ | |
940 | } else if (channel_is_ctr(CR_CHAN(dest))) { | |
941 | ni_tio_unset_routing(devpriv->counter_dev, dest); | |
942 | } else { | |
943 | return -EINVAL; | |
944 | } | |
945 | return 0; | |
946 | } | |
947 | ||
948 | static int ni_global_insn_config(struct comedi_device *dev, | |
949 | struct comedi_insn *insn, | |
950 | unsigned int *data) | |
951 | { | |
952 | switch (data[0]) { | |
953 | case INSN_DEVICE_CONFIG_TEST_ROUTE: | |
954 | data[0] = test_route(data[1], data[2], dev); | |
955 | return 2; | |
956 | case INSN_DEVICE_CONFIG_CONNECT_ROUTE: | |
957 | return connect_route(data[1], data[2], dev); | |
958 | case INSN_DEVICE_CONFIG_DISCONNECT_ROUTE: | |
959 | return disconnect_route(data[1], data[2], dev); | |
960 | /* | |
961 | * This case is already handled one level up. | |
962 | * case INSN_DEVICE_CONFIG_GET_ROUTES: | |
963 | */ | |
964 | default: | |
965 | return -EINVAL; | |
966 | } | |
967 | return 1; | |
968 | } | |
969 | ||
2363cbf0 HS |
970 | static void ni_660x_init_tio_chips(struct comedi_device *dev, |
971 | unsigned int n_chips) | |
972 | { | |
973 | struct ni_660x_private *devpriv = dev->private; | |
974 | unsigned int chip; | |
975 | unsigned int chan; | |
976 | ||
977 | /* | |
978 | * We use the ioconfig registers to control dio direction, so zero | |
979 | * output enables in stc dio control reg. | |
980 | */ | |
981 | ni_660x_write(dev, 0, 0, NI660X_STC_DIO_CONTROL); | |
982 | ||
983 | for (chip = 0; chip < n_chips; ++chip) { | |
984 | /* init dma configuration register */ | |
985 | devpriv->dma_cfg[chip] = 0; | |
986 | for (chan = 0; chan < NI660X_MAX_DMA_CHANNEL; ++chan) | |
987 | devpriv->dma_cfg[chip] |= NI660X_DMA_CFG_SEL_NONE(chan); | |
988 | ni_660x_write(dev, chip, devpriv->dma_cfg[chip], | |
989 | NI660X_DMA_CFG); | |
990 | ||
991 | /* init ioconfig registers */ | |
992 | for (chan = 0; chan < NI660X_NUM_PFI_CHANNELS; ++chan) | |
993 | ni_660x_write(dev, chip, 0, NI660X_IO_CFG(chan)); | |
994 | } | |
995 | } | |
996 | ||
a690b7e5 | 997 | static int ni_660x_auto_attach(struct comedi_device *dev, |
97bcce5a | 998 | unsigned long context) |
990b9eed | 999 | { |
750af5e5 | 1000 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
97bcce5a | 1001 | const struct ni_660x_board *board = NULL; |
990b9eed HS |
1002 | struct ni_660x_private *devpriv; |
1003 | struct comedi_subdevice *s; | |
32f89d8e | 1004 | struct ni_gpct_device *gpct_dev; |
b15f5069 | 1005 | unsigned int n_counters; |
520e6191 | 1006 | int subdev; |
990b9eed | 1007 | int ret; |
2225320e HS |
1008 | unsigned int i; |
1009 | unsigned int global_interrupt_config_bits; | |
990b9eed | 1010 | |
97bcce5a HS |
1011 | if (context < ARRAY_SIZE(ni_660x_boards)) |
1012 | board = &ni_660x_boards[context]; | |
1013 | if (!board) | |
1014 | return -ENODEV; | |
1015 | dev->board_ptr = board; | |
1016 | dev->board_name = board->name; | |
1017 | ||
818f569f HS |
1018 | ret = comedi_pci_enable(dev); |
1019 | if (ret) | |
1020 | return ret; | |
818f569f | 1021 | |
990b9eed HS |
1022 | ret = ni_660x_allocate_private(dev); |
1023 | if (ret < 0) | |
1024 | return ret; | |
1025 | devpriv = dev->private; | |
1026 | ||
48f2c1ae | 1027 | devpriv->mite = mite_attach(dev, true); /* use win1 */ |
990b9eed HS |
1028 | if (!devpriv->mite) |
1029 | return -ENOMEM; | |
1030 | ||
990b9eed HS |
1031 | ret = ni_660x_alloc_mite_rings(dev); |
1032 | if (ret < 0) | |
1033 | return ret; | |
1034 | ||
2363cbf0 HS |
1035 | ni_660x_init_tio_chips(dev, board->n_chips); |
1036 | ||
347e2448 | 1037 | /* prepare the device for globally-named routes. */ |
e3b7ce73 | 1038 | if (ni_assign_device_routes("ni_660x", board->name, NULL, |
347e2448 SO |
1039 | &devpriv->routing_tables) < 0) { |
1040 | dev_warn(dev->class_dev, "%s: %s device has no signal routing table.\n", | |
1041 | __func__, board->name); | |
1042 | dev_warn(dev->class_dev, "%s: High level NI signal names will not be available for this %s board.\n", | |
1043 | __func__, board->name); | |
fa86c007 SO |
1044 | } else { |
1045 | /* | |
1046 | * only(?) assign insn_device_config if we have global names for | |
1047 | * this device. | |
1048 | */ | |
1049 | dev->insn_device_config = ni_global_insn_config; | |
1050 | dev->get_valid_routes = _ni_get_valid_routes; | |
347e2448 SO |
1051 | } |
1052 | ||
f229594a HS |
1053 | n_counters = board->n_chips * NI660X_COUNTERS_PER_CHIP; |
1054 | gpct_dev = ni_gpct_device_construct(dev, | |
1055 | ni_660x_gpct_write, | |
1056 | ni_660x_gpct_read, | |
1057 | ni_gpct_variant_660x, | |
347e2448 SO |
1058 | n_counters, |
1059 | NI660X_COUNTERS_PER_CHIP, | |
1060 | &devpriv->routing_tables); | |
f229594a HS |
1061 | if (!gpct_dev) |
1062 | return -ENOMEM; | |
1063 | devpriv->counter_dev = gpct_dev; | |
1064 | ||
ccef0da8 | 1065 | ret = comedi_alloc_subdevices(dev, 2 + NI660X_MAX_COUNTERS); |
990b9eed HS |
1066 | if (ret) |
1067 | return ret; | |
1068 | ||
520e6191 HS |
1069 | subdev = 0; |
1070 | ||
1071 | s = &dev->subdevices[subdev++]; | |
990b9eed HS |
1072 | /* Old GENERAL-PURPOSE COUNTER/TIME (GPCT) subdevice, no longer used */ |
1073 | s->type = COMEDI_SUBD_UNUSED; | |
1074 | ||
7e906186 HS |
1075 | /* |
1076 | * Digital I/O subdevice | |
1077 | * | |
1078 | * There are 40 channels but only the first 32 can be digital I/Os. | |
1079 | * The last 8 are dedicated to counters 0 and 1. | |
1080 | * | |
1081 | * Counter 0-3 signals are from the first TIO chip. | |
1082 | * Counter 4-7 signals are from the second TIO chip. | |
1083 | * | |
1084 | * Comedi External | |
1085 | * PFI Chan DIO Chan Counter Signal | |
1086 | * ------- -------- -------------- | |
1087 | * 0 0 | |
1088 | * 1 1 | |
1089 | * 2 2 | |
1090 | * 3 3 | |
1091 | * 4 4 | |
1092 | * 5 5 | |
1093 | * 6 6 | |
1094 | * 7 7 | |
1095 | * 8 8 CTR 7 OUT | |
1096 | * 9 9 CTR 7 AUX | |
1097 | * 10 10 CTR 7 GATE | |
1098 | * 11 11 CTR 7 SOURCE | |
1099 | * 12 12 CTR 6 OUT | |
1100 | * 13 13 CTR 6 AUX | |
1101 | * 14 14 CTR 6 GATE | |
1102 | * 15 15 CTR 6 SOURCE | |
1103 | * 16 16 CTR 5 OUT | |
1104 | * 17 17 CTR 5 AUX | |
1105 | * 18 18 CTR 5 GATE | |
1106 | * 19 19 CTR 5 SOURCE | |
1107 | * 20 20 CTR 4 OUT | |
1108 | * 21 21 CTR 4 AUX | |
1109 | * 22 22 CTR 4 GATE | |
1110 | * 23 23 CTR 4 SOURCE | |
1111 | * 24 24 CTR 3 OUT | |
1112 | * 25 25 CTR 3 AUX | |
1113 | * 26 26 CTR 3 GATE | |
1114 | * 27 27 CTR 3 SOURCE | |
1115 | * 28 28 CTR 2 OUT | |
1116 | * 29 29 CTR 2 AUX | |
1117 | * 30 30 CTR 2 GATE | |
1118 | * 31 31 CTR 2 SOURCE | |
1119 | * 32 CTR 1 OUT | |
1120 | * 33 CTR 1 AUX | |
1121 | * 34 CTR 1 GATE | |
1122 | * 35 CTR 1 SOURCE | |
1123 | * 36 CTR 0 OUT | |
1124 | * 37 CTR 0 AUX | |
1125 | * 38 CTR 0 GATE | |
1126 | * 39 CTR 0 SOURCE | |
1127 | */ | |
520e6191 | 1128 | s = &dev->subdevices[subdev++]; |
7e906186 HS |
1129 | s->type = COMEDI_SUBD_DIO; |
1130 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
ccef0da8 | 1131 | s->n_chan = NI660X_NUM_PFI_CHANNELS; |
7e906186 HS |
1132 | s->maxdata = 1; |
1133 | s->range_table = &range_digital; | |
1134 | s->insn_bits = ni_660x_dio_insn_bits; | |
1135 | s->insn_config = ni_660x_dio_insn_config; | |
6d40805b | 1136 | |
34b7f1f8 HS |
1137 | /* |
1138 | * Default the DIO channels as: | |
1139 | * chan 0-7: DIO inputs | |
1140 | * chan 8-39: counter signal inputs | |
1141 | */ | |
1142 | for (i = 0; i < s->n_chan; ++i) { | |
1143 | unsigned int source = (i < 8) ? NI_660X_PFI_OUTPUT_DIO | |
1144 | : NI_660X_PFI_OUTPUT_COUNTER; | |
1145 | ||
1146 | ni_660x_set_pfi_routing(dev, i, source); | |
a0c5e846 | 1147 | ni_660x_set_pfi_direction(dev, i, COMEDI_INPUT);/* high-z */ |
34b7f1f8 HS |
1148 | } |
1149 | ||
32f89d8e | 1150 | /* Counter subdevices (4 NI TIO General Purpose Counters per chip) */ |
ccef0da8 | 1151 | for (i = 0; i < NI660X_MAX_COUNTERS; ++i) { |
520e6191 | 1152 | s = &dev->subdevices[subdev++]; |
b15f5069 | 1153 | if (i < n_counters) { |
32f89d8e HS |
1154 | struct ni_gpct *counter = &gpct_dev->counters[i]; |
1155 | ||
32f89d8e HS |
1156 | s->type = COMEDI_SUBD_COUNTER; |
1157 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE | | |
56e9bef5 | 1158 | SDF_LSAMPL | SDF_CMD_READ; |
32f89d8e HS |
1159 | s->n_chan = 3; |
1160 | s->maxdata = 0xffffffff; | |
1161 | s->insn_read = ni_tio_insn_read; | |
1162 | s->insn_write = ni_tio_insn_write; | |
1163 | s->insn_config = ni_tio_insn_config; | |
1164 | s->len_chanlist = 1; | |
1165 | s->do_cmd = ni_660x_cmd; | |
1166 | s->do_cmdtest = ni_tio_cmdtest; | |
1167 | s->cancel = ni_660x_cancel; | |
1168 | s->poll = ni_660x_input_poll; | |
1169 | s->buf_change = ni_660x_buf_change; | |
990b9eed | 1170 | s->async_dma_dir = DMA_BIDIRECTIONAL; |
32f89d8e | 1171 | s->private = counter; |
90ad57be HS |
1172 | |
1173 | ni_tio_init_counter(counter); | |
990b9eed | 1174 | } else { |
32f89d8e | 1175 | s->type = COMEDI_SUBD_UNUSED; |
990b9eed HS |
1176 | } |
1177 | } | |
32f89d8e | 1178 | |
6d40805b HS |
1179 | /* |
1180 | * To be safe, set counterswap bits on tio chips after all the counter | |
1181 | * outputs have been set to high impedance mode. | |
1182 | */ | |
9186ccde | 1183 | for (i = 0; i < board->n_chips; ++i) |
990b9eed HS |
1184 | set_tio_counterswap(dev, i); |
1185 | ||
71e06874 HS |
1186 | ret = request_irq(pcidev->irq, ni_660x_interrupt, IRQF_SHARED, |
1187 | dev->board_name, dev); | |
990b9eed HS |
1188 | if (ret < 0) { |
1189 | dev_warn(dev->class_dev, " irq not available\n"); | |
1190 | return ret; | |
1191 | } | |
ba9d29fe | 1192 | dev->irq = pcidev->irq; |
41014593 | 1193 | global_interrupt_config_bits = NI660X_GLOBAL_INT_GLOBAL; |
9186ccde | 1194 | if (board->n_chips > 1) |
41014593 | 1195 | global_interrupt_config_bits |= NI660X_GLOBAL_INT_CASCADE; |
9678b73e HS |
1196 | ni_660x_write(dev, 0, global_interrupt_config_bits, |
1197 | NI660X_GLOBAL_INT_CFG); | |
c93999c2 | 1198 | |
990b9eed HS |
1199 | return 0; |
1200 | } | |
1201 | ||
1202 | static void ni_660x_detach(struct comedi_device *dev) | |
1203 | { | |
1204 | struct ni_660x_private *devpriv = dev->private; | |
1205 | ||
78d514fa HS |
1206 | if (dev->irq) { |
1207 | ni_660x_write(dev, 0, 0, NI660X_GLOBAL_INT_CFG); | |
990b9eed | 1208 | free_irq(dev->irq, dev); |
78d514fa | 1209 | } |
990b9eed | 1210 | if (devpriv) { |
26a0fe3f | 1211 | ni_gpct_device_destroy(devpriv->counter_dev); |
b876e985 HS |
1212 | ni_660x_free_mite_rings(dev); |
1213 | mite_detach(devpriv->mite); | |
990b9eed | 1214 | } |
5f8a5f4f HS |
1215 | if (dev->mmio) |
1216 | iounmap(dev->mmio); | |
7f072f54 | 1217 | comedi_pci_disable(dev); |
990b9eed HS |
1218 | } |
1219 | ||
1220 | static struct comedi_driver ni_660x_driver = { | |
1221 | .driver_name = "ni_660x", | |
1222 | .module = THIS_MODULE, | |
750af5e5 | 1223 | .auto_attach = ni_660x_auto_attach, |
990b9eed HS |
1224 | .detach = ni_660x_detach, |
1225 | }; | |
1226 | ||
a690b7e5 | 1227 | static int ni_660x_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 1228 | const struct pci_device_id *id) |
990b9eed | 1229 | { |
b8f4ac23 | 1230 | return comedi_pci_auto_config(dev, &ni_660x_driver, id->driver_data); |
990b9eed HS |
1231 | } |
1232 | ||
41e043fc | 1233 | static const struct pci_device_id ni_660x_pci_table[] = { |
97bcce5a HS |
1234 | { PCI_VDEVICE(NI, 0x1310), BOARD_PCI6602 }, |
1235 | { PCI_VDEVICE(NI, 0x1360), BOARD_PXI6602 }, | |
1236 | { PCI_VDEVICE(NI, 0x2c60), BOARD_PCI6601 }, | |
51c4ba64 | 1237 | { PCI_VDEVICE(NI, 0x2db0), BOARD_PCI6608 }, |
97bcce5a | 1238 | { PCI_VDEVICE(NI, 0x2cc0), BOARD_PXI6608 }, |
0d0ad829 | 1239 | { PCI_VDEVICE(NI, 0x1e30), BOARD_PCI6624 }, |
8bdfefb7 | 1240 | { PCI_VDEVICE(NI, 0x1e40), BOARD_PXI6624 }, |
97bcce5a | 1241 | { 0 } |
990b9eed HS |
1242 | }; |
1243 | MODULE_DEVICE_TABLE(pci, ni_660x_pci_table); | |
1244 | ||
1245 | static struct pci_driver ni_660x_pci_driver = { | |
1246 | .name = "ni_660x", | |
1247 | .id_table = ni_660x_pci_table, | |
1248 | .probe = ni_660x_pci_probe, | |
9901a4d7 | 1249 | .remove = comedi_pci_auto_unconfig, |
990b9eed HS |
1250 | }; |
1251 | module_comedi_pci_driver(ni_660x_driver, ni_660x_pci_driver); | |
1252 | ||
3659743d | 1253 | MODULE_AUTHOR("Comedi https://www.comedi.org"); |
47470216 | 1254 | MODULE_DESCRIPTION("Comedi driver for NI 660x counter/timer boards"); |
3c323c01 | 1255 | MODULE_LICENSE("GPL"); |