1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Command support for NI general purpose counters |
4 | * |
5 | * Copyright (C) 2006 Frank Mori Hess <fmhess@users.sourceforge.net> |
6 | */ |
7 | |
8 | /* |
9 | * Module: ni_tiocmd |
10 | * Description: National Instruments general purpose counters command support |
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: Fri, 11 Apr 2008 12:32:35 +0100 |
17 | * Status: works |
18 | * |
19 | * This module is not used directly by end-users. Rather, it |
20 | * is used by other drivers (for example ni_660x and ni_pcimio) |
21 | * to provide command support for NI's general purpose counters. |
22 | * It was originally split out of ni_tio.c to stop the 'ni_tio' |
23 | * module depending on the 'mite' module. |
24 | * |
25 | * References: |
26 | * DAQ 660x Register-Level Programmer Manual (NI 370505A-01) |
27 | * DAQ 6601/6602 User Manual (NI 322137B-01) |
28 | * 340934b.pdf DAQ-STC reference manual |
29 | * |
30 | * TODO: Support use of both banks X and Y |
31 | */ |
32 | |
33 | #include <linux/module.h> |
34 | #include "ni_tio_internal.h" |
35 | #include "mite.h" |
36 | #include "ni_routes.h" |
37 | |
38 | static void ni_tio_configure_dma(struct ni_gpct *counter, |
39 | bool enable, bool read) |
40 | { |
41 | struct ni_gpct_device *counter_dev = counter->counter_dev; |
42 | unsigned int cidx = counter->counter_index; |
43 | unsigned int mask; |
44 | unsigned int bits; |
45 | |
46 | mask = GI_READ_ACKS_IRQ | GI_WRITE_ACKS_IRQ; |
47 | bits = 0; |
48 | |
49 | if (enable) { |
50 | if (read) |
51 | bits |= GI_READ_ACKS_IRQ; |
52 | else |
53 | bits |= GI_WRITE_ACKS_IRQ; |
54 | } |
55 | ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx), mask, value: bits); |
56 | |
57 | switch (counter_dev->variant) { |
58 | case ni_gpct_variant_e_series: |
59 | break; |
60 | case ni_gpct_variant_m_series: |
61 | case ni_gpct_variant_660x: |
62 | mask = GI_DMA_ENABLE | GI_DMA_INT_ENA | GI_DMA_WRITE; |
63 | bits = 0; |
64 | |
65 | if (enable) |
66 | bits |= GI_DMA_ENABLE | GI_DMA_INT_ENA; |
67 | if (!read) |
68 | bits |= GI_DMA_WRITE; |
69 | ni_tio_set_bits(counter, NITIO_DMA_CFG_REG(cidx), mask, value: bits); |
70 | break; |
71 | } |
72 | } |
73 | |
74 | static int ni_tio_input_inttrig(struct comedi_device *dev, |
75 | struct comedi_subdevice *s, |
76 | unsigned int trig_num) |
77 | { |
78 | struct ni_gpct *counter = s->private; |
79 | struct comedi_cmd *cmd = &s->async->cmd; |
80 | unsigned long flags; |
81 | int ret = 0; |
82 | |
83 | if (trig_num != cmd->start_arg) |
84 | return -EINVAL; |
85 | |
86 | spin_lock_irqsave(&counter->lock, flags); |
87 | if (counter->mite_chan) |
88 | mite_dma_arm(mite_chan: counter->mite_chan); |
89 | else |
90 | ret = -EIO; |
91 | spin_unlock_irqrestore(lock: &counter->lock, flags); |
92 | if (ret < 0) |
93 | return ret; |
94 | ret = ni_tio_arm(counter, arm: true, start_trigger: NI_GPCT_ARM_IMMEDIATE); |
95 | s->async->inttrig = NULL; |
96 | |
97 | return ret; |
98 | } |
99 | |
100 | static int ni_tio_input_cmd(struct comedi_subdevice *s) |
101 | { |
102 | struct ni_gpct *counter = s->private; |
103 | struct ni_gpct_device *counter_dev = counter->counter_dev; |
104 | const struct ni_route_tables *routing_tables = |
105 | counter_dev->routing_tables; |
106 | unsigned int cidx = counter->counter_index; |
107 | struct comedi_async *async = s->async; |
108 | struct comedi_cmd *cmd = &async->cmd; |
109 | int ret = 0; |
110 | |
111 | /* write alloc the entire buffer */ |
112 | comedi_buf_write_alloc(s, n: async->prealloc_bufsz); |
113 | counter->mite_chan->dir = COMEDI_INPUT; |
114 | switch (counter_dev->variant) { |
115 | case ni_gpct_variant_m_series: |
116 | case ni_gpct_variant_660x: |
117 | mite_prep_dma(mite_chan: counter->mite_chan, num_device_bits: 32, num_memory_bits: 32); |
118 | break; |
119 | case ni_gpct_variant_e_series: |
120 | mite_prep_dma(mite_chan: counter->mite_chan, num_device_bits: 16, num_memory_bits: 32); |
121 | break; |
122 | } |
123 | ni_tio_set_bits(counter, NITIO_CMD_REG(cidx), GI_SAVE_TRACE, value: 0); |
124 | ni_tio_configure_dma(counter, enable: true, read: true); |
125 | |
126 | if (cmd->start_src == TRIG_INT) { |
127 | async->inttrig = &ni_tio_input_inttrig; |
128 | } else { /* TRIG_NOW || TRIG_EXT || TRIG_OTHER */ |
129 | async->inttrig = NULL; |
130 | mite_dma_arm(mite_chan: counter->mite_chan); |
131 | |
132 | if (cmd->start_src == TRIG_NOW) |
133 | ret = ni_tio_arm(counter, arm: true, start_trigger: NI_GPCT_ARM_IMMEDIATE); |
134 | else if (cmd->start_src == TRIG_EXT) { |
135 | int reg = CR_CHAN(cmd->start_arg); |
136 | |
137 | if (reg >= NI_NAMES_BASE) { |
138 | /* using a device-global name. lookup reg */ |
139 | reg = ni_get_reg_value(src: reg, |
140 | NI_CtrArmStartTrigger(cidx), |
141 | tables: routing_tables); |
142 | /* mark this as a raw register value */ |
143 | reg |= NI_GPCT_HW_ARM; |
144 | } |
145 | ret = ni_tio_arm(counter, arm: true, start_trigger: reg); |
146 | } |
147 | } |
148 | return ret; |
149 | } |
150 | |
151 | static int ni_tio_output_cmd(struct comedi_subdevice *s) |
152 | { |
153 | struct ni_gpct *counter = s->private; |
154 | |
155 | dev_err(counter->counter_dev->dev->class_dev, |
156 | "output commands not yet implemented.\n" ); |
157 | return -ENOTSUPP; |
158 | } |
159 | |
160 | static int ni_tio_cmd_setup(struct comedi_subdevice *s) |
161 | { |
162 | struct comedi_cmd *cmd = &s->async->cmd; |
163 | struct ni_gpct *counter = s->private; |
164 | unsigned int cidx = counter->counter_index; |
165 | const struct ni_route_tables *routing_tables = |
166 | counter->counter_dev->routing_tables; |
167 | int set_gate_source = 0; |
168 | unsigned int gate_source; |
169 | int retval = 0; |
170 | |
171 | if (cmd->scan_begin_src == TRIG_EXT) { |
172 | set_gate_source = 1; |
173 | gate_source = cmd->scan_begin_arg; |
174 | } else if (cmd->convert_src == TRIG_EXT) { |
175 | set_gate_source = 1; |
176 | gate_source = cmd->convert_arg; |
177 | } |
178 | if (set_gate_source) { |
179 | if (CR_CHAN(gate_source) >= NI_NAMES_BASE) { |
180 | /* Lookup and use the real register values */ |
181 | int reg = ni_get_reg_value(CR_CHAN(gate_source), |
182 | NI_CtrGate(cidx), |
183 | tables: routing_tables); |
184 | if (reg < 0) |
185 | return -EINVAL; |
186 | retval = ni_tio_set_gate_src_raw(counter, gate: 0, src: reg); |
187 | } else { |
188 | /* |
189 | * This function must be used separately since it does |
190 | * not expect real register values and attempts to |
191 | * convert these to real register values. |
192 | */ |
193 | retval = ni_tio_set_gate_src(counter, gate: 0, src: gate_source); |
194 | } |
195 | } |
196 | if (cmd->flags & CMDF_WAKE_EOS) { |
197 | ni_tio_set_bits(counter, NITIO_INT_ENA_REG(cidx), |
198 | GI_GATE_INTERRUPT_ENABLE(cidx), |
199 | GI_GATE_INTERRUPT_ENABLE(cidx)); |
200 | } |
201 | return retval; |
202 | } |
203 | |
204 | int ni_tio_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
205 | { |
206 | struct ni_gpct *counter = s->private; |
207 | struct comedi_async *async = s->async; |
208 | struct comedi_cmd *cmd = &async->cmd; |
209 | int retval = 0; |
210 | unsigned long flags; |
211 | |
212 | spin_lock_irqsave(&counter->lock, flags); |
213 | if (!counter->mite_chan) { |
214 | dev_err(counter->counter_dev->dev->class_dev, |
215 | "commands only supported with DMA. " ); |
216 | dev_err(counter->counter_dev->dev->class_dev, |
217 | "Interrupt-driven commands not yet implemented.\n" ); |
218 | retval = -EIO; |
219 | } else { |
220 | retval = ni_tio_cmd_setup(s); |
221 | if (retval == 0) { |
222 | if (cmd->flags & CMDF_WRITE) |
223 | retval = ni_tio_output_cmd(s); |
224 | else |
225 | retval = ni_tio_input_cmd(s); |
226 | } |
227 | } |
228 | spin_unlock_irqrestore(lock: &counter->lock, flags); |
229 | return retval; |
230 | } |
231 | EXPORT_SYMBOL_GPL(ni_tio_cmd); |
232 | |
233 | int ni_tio_cmdtest(struct comedi_device *dev, |
234 | struct comedi_subdevice *s, |
235 | struct comedi_cmd *cmd) |
236 | { |
237 | struct ni_gpct *counter = s->private; |
238 | unsigned int cidx = counter->counter_index; |
239 | const struct ni_route_tables *routing_tables = |
240 | counter->counter_dev->routing_tables; |
241 | int err = 0; |
242 | unsigned int sources; |
243 | |
244 | /* Step 1 : check if triggers are trivially valid */ |
245 | |
246 | sources = TRIG_NOW | TRIG_INT | TRIG_OTHER; |
247 | if (ni_tio_counting_mode_registers_present(counter_dev: counter->counter_dev)) |
248 | sources |= TRIG_EXT; |
249 | err |= comedi_check_trigger_src(src: &cmd->start_src, flags: sources); |
250 | |
251 | err |= comedi_check_trigger_src(src: &cmd->scan_begin_src, |
252 | TRIG_FOLLOW | TRIG_EXT | TRIG_OTHER); |
253 | err |= comedi_check_trigger_src(src: &cmd->convert_src, |
254 | TRIG_NOW | TRIG_EXT | TRIG_OTHER); |
255 | err |= comedi_check_trigger_src(src: &cmd->scan_end_src, TRIG_COUNT); |
256 | err |= comedi_check_trigger_src(src: &cmd->stop_src, TRIG_NONE); |
257 | |
258 | if (err) |
259 | return 1; |
260 | |
261 | /* Step 2a : make sure trigger sources are unique */ |
262 | |
263 | err |= comedi_check_trigger_is_unique(src: cmd->start_src); |
264 | err |= comedi_check_trigger_is_unique(src: cmd->scan_begin_src); |
265 | err |= comedi_check_trigger_is_unique(src: cmd->convert_src); |
266 | |
267 | /* Step 2b : and mutually compatible */ |
268 | |
269 | if (cmd->convert_src != TRIG_NOW && cmd->scan_begin_src != TRIG_FOLLOW) |
270 | err |= -EINVAL; |
271 | |
272 | if (err) |
273 | return 2; |
274 | |
275 | /* Step 3: check if arguments are trivially valid */ |
276 | |
277 | switch (cmd->start_src) { |
278 | case TRIG_NOW: |
279 | case TRIG_INT: |
280 | case TRIG_OTHER: |
281 | err |= comedi_check_trigger_arg_is(arg: &cmd->start_arg, val: 0); |
282 | break; |
283 | case TRIG_EXT: |
284 | /* start_arg is the start_trigger passed to ni_tio_arm() */ |
285 | /* |
286 | * This should be done, but we don't yet know the actual |
287 | * register values. These should be tested and then documented |
288 | * in the ni_route_values/ni_*.csv files, with indication of |
289 | * who/when/which/how these were tested. |
290 | * When at least a e/m/660x series have been tested, this code |
291 | * should be uncommented: |
292 | * |
293 | * err |= ni_check_trigger_arg(CR_CHAN(cmd->start_arg), |
294 | * NI_CtrArmStartTrigger(cidx), |
295 | * routing_tables); |
296 | */ |
297 | break; |
298 | } |
299 | |
300 | /* |
301 | * It seems that convention is to allow either scan_begin_arg or |
302 | * convert_arg to specify the Gate source, with scan_begin_arg taking |
303 | * precedence. |
304 | */ |
305 | if (cmd->scan_begin_src != TRIG_EXT) |
306 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_begin_arg, val: 0); |
307 | else |
308 | err |= ni_check_trigger_arg(CR_CHAN(cmd->scan_begin_arg), |
309 | NI_CtrGate(cidx), tables: routing_tables); |
310 | |
311 | if (cmd->convert_src != TRIG_EXT) |
312 | err |= comedi_check_trigger_arg_is(arg: &cmd->convert_arg, val: 0); |
313 | else |
314 | err |= ni_check_trigger_arg(CR_CHAN(cmd->convert_arg), |
315 | NI_CtrGate(cidx), tables: routing_tables); |
316 | |
317 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_end_arg, |
318 | val: cmd->chanlist_len); |
319 | err |= comedi_check_trigger_arg_is(arg: &cmd->stop_arg, val: 0); |
320 | |
321 | if (err) |
322 | return 3; |
323 | |
324 | /* Step 4: fix up any arguments */ |
325 | |
326 | /* Step 5: check channel list if it exists */ |
327 | |
328 | return 0; |
329 | } |
330 | EXPORT_SYMBOL_GPL(ni_tio_cmdtest); |
331 | |
332 | int ni_tio_cancel(struct ni_gpct *counter) |
333 | { |
334 | unsigned int cidx = counter->counter_index; |
335 | unsigned long flags; |
336 | |
337 | ni_tio_arm(counter, arm: false, start_trigger: 0); |
338 | spin_lock_irqsave(&counter->lock, flags); |
339 | if (counter->mite_chan) |
340 | mite_dma_disarm(mite_chan: counter->mite_chan); |
341 | spin_unlock_irqrestore(lock: &counter->lock, flags); |
342 | ni_tio_configure_dma(counter, enable: false, read: false); |
343 | |
344 | ni_tio_set_bits(counter, NITIO_INT_ENA_REG(cidx), |
345 | GI_GATE_INTERRUPT_ENABLE(cidx), value: 0x0); |
346 | return 0; |
347 | } |
348 | EXPORT_SYMBOL_GPL(ni_tio_cancel); |
349 | |
350 | static int should_ack_gate(struct ni_gpct *counter) |
351 | { |
352 | unsigned long flags; |
353 | int retval = 0; |
354 | |
355 | switch (counter->counter_dev->variant) { |
356 | case ni_gpct_variant_m_series: |
357 | case ni_gpct_variant_660x: |
358 | /* |
359 | * not sure if 660x really supports gate interrupts |
360 | * (the bits are not listed in register-level manual) |
361 | */ |
362 | return 1; |
363 | case ni_gpct_variant_e_series: |
364 | /* |
365 | * During buffered input counter operation for e-series, |
366 | * the gate interrupt is acked automatically by the dma |
367 | * controller, due to the Gi_Read/Write_Acknowledges_IRQ |
368 | * bits in the input select register. |
369 | */ |
370 | spin_lock_irqsave(&counter->lock, flags); |
371 | { |
372 | if (!counter->mite_chan || |
373 | counter->mite_chan->dir != COMEDI_INPUT || |
374 | (mite_done(mite_chan: counter->mite_chan))) { |
375 | retval = 1; |
376 | } |
377 | } |
378 | spin_unlock_irqrestore(lock: &counter->lock, flags); |
379 | break; |
380 | } |
381 | return retval; |
382 | } |
383 | |
384 | static void ni_tio_acknowledge_and_confirm(struct ni_gpct *counter, |
385 | int *gate_error, |
386 | int *tc_error, |
387 | int *perm_stale_data) |
388 | { |
389 | unsigned int cidx = counter->counter_index; |
390 | const unsigned short gxx_status = ni_tio_read(counter, |
391 | NITIO_SHARED_STATUS_REG(cidx)); |
392 | const unsigned short gi_status = ni_tio_read(counter, |
393 | NITIO_STATUS_REG(cidx)); |
394 | unsigned int ack = 0; |
395 | |
396 | if (gate_error) |
397 | *gate_error = 0; |
398 | if (tc_error) |
399 | *tc_error = 0; |
400 | if (perm_stale_data) |
401 | *perm_stale_data = 0; |
402 | |
403 | if (gxx_status & GI_GATE_ERROR(cidx)) { |
404 | ack |= GI_GATE_ERROR_CONFIRM(cidx); |
405 | if (gate_error) { |
406 | /* |
407 | * 660x don't support automatic acknowledgment |
408 | * of gate interrupt via dma read/write |
409 | * and report bogus gate errors |
410 | */ |
411 | if (counter->counter_dev->variant != |
412 | ni_gpct_variant_660x) |
413 | *gate_error = 1; |
414 | } |
415 | } |
416 | if (gxx_status & GI_TC_ERROR(cidx)) { |
417 | ack |= GI_TC_ERROR_CONFIRM(cidx); |
418 | if (tc_error) |
419 | *tc_error = 1; |
420 | } |
421 | if (gi_status & GI_TC) |
422 | ack |= GI_TC_INTERRUPT_ACK; |
423 | if (gi_status & GI_GATE_INTERRUPT) { |
424 | if (should_ack_gate(counter)) |
425 | ack |= GI_GATE_INTERRUPT_ACK; |
426 | } |
427 | if (ack) |
428 | ni_tio_write(counter, value: ack, NITIO_INT_ACK_REG(cidx)); |
429 | if (ni_tio_get_soft_copy(counter, NITIO_MODE_REG(cidx)) & |
430 | GI_LOADING_ON_GATE) { |
431 | if (ni_tio_read(counter, NITIO_STATUS2_REG(cidx)) & |
432 | GI_PERMANENT_STALE(cidx)) { |
433 | dev_info(counter->counter_dev->dev->class_dev, |
434 | "%s: Gi_Permanent_Stale_Data detected.\n" , |
435 | __func__); |
436 | if (perm_stale_data) |
437 | *perm_stale_data = 1; |
438 | } |
439 | } |
440 | } |
441 | |
442 | void ni_tio_acknowledge(struct ni_gpct *counter) |
443 | { |
444 | ni_tio_acknowledge_and_confirm(counter, NULL, NULL, NULL); |
445 | } |
446 | EXPORT_SYMBOL_GPL(ni_tio_acknowledge); |
447 | |
448 | void ni_tio_handle_interrupt(struct ni_gpct *counter, |
449 | struct comedi_subdevice *s) |
450 | { |
451 | unsigned int cidx = counter->counter_index; |
452 | unsigned long flags; |
453 | int gate_error; |
454 | int tc_error; |
455 | int perm_stale_data; |
456 | |
457 | ni_tio_acknowledge_and_confirm(counter, gate_error: &gate_error, tc_error: &tc_error, |
458 | perm_stale_data: &perm_stale_data); |
459 | if (gate_error) { |
460 | dev_notice(counter->counter_dev->dev->class_dev, |
461 | "%s: Gi_Gate_Error detected.\n" , __func__); |
462 | s->async->events |= COMEDI_CB_OVERFLOW; |
463 | } |
464 | if (perm_stale_data) |
465 | s->async->events |= COMEDI_CB_ERROR; |
466 | switch (counter->counter_dev->variant) { |
467 | case ni_gpct_variant_m_series: |
468 | case ni_gpct_variant_660x: |
469 | if (ni_tio_read(counter, NITIO_DMA_STATUS_REG(cidx)) & |
470 | GI_DRQ_ERROR) { |
471 | dev_notice(counter->counter_dev->dev->class_dev, |
472 | "%s: Gi_DRQ_Error detected.\n" , __func__); |
473 | s->async->events |= COMEDI_CB_OVERFLOW; |
474 | } |
475 | break; |
476 | case ni_gpct_variant_e_series: |
477 | break; |
478 | } |
479 | spin_lock_irqsave(&counter->lock, flags); |
480 | if (counter->mite_chan) |
481 | mite_ack_linkc(mite_chan: counter->mite_chan, s, sync: true); |
482 | spin_unlock_irqrestore(lock: &counter->lock, flags); |
483 | } |
484 | EXPORT_SYMBOL_GPL(ni_tio_handle_interrupt); |
485 | |
486 | void ni_tio_set_mite_channel(struct ni_gpct *counter, |
487 | struct mite_channel *mite_chan) |
488 | { |
489 | unsigned long flags; |
490 | |
491 | spin_lock_irqsave(&counter->lock, flags); |
492 | counter->mite_chan = mite_chan; |
493 | spin_unlock_irqrestore(lock: &counter->lock, flags); |
494 | } |
495 | EXPORT_SYMBOL_GPL(ni_tio_set_mite_channel); |
496 | |
497 | static int __init ni_tiocmd_init_module(void) |
498 | { |
499 | return 0; |
500 | } |
501 | module_init(ni_tiocmd_init_module); |
502 | |
503 | static void __exit ni_tiocmd_cleanup_module(void) |
504 | { |
505 | } |
506 | module_exit(ni_tiocmd_cleanup_module); |
507 | |
508 | MODULE_AUTHOR("Comedi <comedi@comedi.org>" ); |
509 | MODULE_DESCRIPTION("Comedi command support for NI general-purpose counters" ); |
510 | MODULE_LICENSE("GPL" ); |
511 | |