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