diff options
Diffstat (limited to 'kernel/drivers/staging/comedi/drivers/ni_tio.c')
-rw-r--r-- | kernel/drivers/staging/comedi/drivers/ni_tio.c | 1436 |
1 files changed, 1436 insertions, 0 deletions
diff --git a/kernel/drivers/staging/comedi/drivers/ni_tio.c b/kernel/drivers/staging/comedi/drivers/ni_tio.c new file mode 100644 index 000000000..c20c51bef --- /dev/null +++ b/kernel/drivers/staging/comedi/drivers/ni_tio.c @@ -0,0 +1,1436 @@ +/* + comedi/drivers/ni_tio.c + Support for NI general purpose counters + + Copyright (C) 2006 Frank Mori Hess <fmhess@users.sourceforge.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +/* + * Module: ni_tio + * Description: National Instruments general purpose counters + * Author: J.P. Mellor <jpmellor@rose-hulman.edu>, + * Herman.Bruyninckx@mech.kuleuven.ac.be, + * Wim.Meeussen@mech.kuleuven.ac.be, + * Klaas.Gadeyne@mech.kuleuven.ac.be, + * Frank Mori Hess <fmhess@users.sourceforge.net> + * Updated: Thu Nov 16 09:50:32 EST 2006 + * Status: works + * + * This module is not used directly by end-users. Rather, it + * is used by other drivers (for example ni_660x and ni_pcimio) + * to provide support for NI's general purpose counters. It was + * originally based on the counter code from ni_660x.c and + * ni_mio_common.c. + * + * References: + * DAQ 660x Register-Level Programmer Manual (NI 370505A-01) + * DAQ 6601/6602 User Manual (NI 322137B-01) + * 340934b.pdf DAQ-STC reference manual + */ + +/* +TODO: + Support use of both banks X and Y +*/ + +#include <linux/module.h> +#include <linux/slab.h> + +#include "ni_tio_internal.h" + +/* + * clock sources for ni e and m series boards, + * get bits with GI_SRC_SEL() + */ +#define NI_M_TIMEBASE_1_CLK 0x0 /* 20MHz */ +#define NI_M_PFI_CLK(x) (((x) < 10) ? (1 + (x)) : (0xb + (x))) +#define NI_M_RTSI_CLK(x) (((x) == 7) ? 0x1b : (0xb + (x))) +#define NI_M_TIMEBASE_2_CLK 0x12 /* 100KHz */ +#define NI_M_NEXT_TC_CLK 0x13 +#define NI_M_NEXT_GATE_CLK 0x14 /* Gi_Src_SubSelect=0 */ +#define NI_M_PXI_STAR_TRIGGER_CLK 0x14 /* Gi_Src_SubSelect=1 */ +#define NI_M_PXI10_CLK 0x1d +#define NI_M_TIMEBASE_3_CLK 0x1e /* 80MHz, Gi_Src_SubSelect=0 */ +#define NI_M_ANALOG_TRIGGER_OUT_CLK 0x1e /* Gi_Src_SubSelect=1 */ +#define NI_M_LOGIC_LOW_CLK 0x1f +#define NI_M_MAX_PFI_CHAN 15 +#define NI_M_MAX_RTSI_CHAN 7 + +/* + * clock sources for ni_660x boards, + * get bits with GI_SRC_SEL() + */ +#define NI_660X_TIMEBASE_1_CLK 0x0 /* 20MHz */ +#define NI_660X_SRC_PIN_I_CLK 0x1 +#define NI_660X_SRC_PIN_CLK(x) (0x2 + (x)) +#define NI_660X_NEXT_GATE_CLK 0xa +#define NI_660X_RTSI_CLK(x) (0xb + (x)) +#define NI_660X_TIMEBASE_2_CLK 0x12 /* 100KHz */ +#define NI_660X_NEXT_TC_CLK 0x13 +#define NI_660X_TIMEBASE_3_CLK 0x1e /* 80MHz */ +#define NI_660X_LOGIC_LOW_CLK 0x1f +#define NI_660X_MAX_SRC_PIN 7 +#define NI_660X_MAX_RTSI_CHAN 6 + +/* ni m series gate_select */ +#define NI_M_TIMESTAMP_MUX_GATE_SEL 0x0 +#define NI_M_PFI_GATE_SEL(x) (((x) < 10) ? (1 + (x)) : (0xb + (x))) +#define NI_M_RTSI_GATE_SEL(x) (((x) == 7) ? 0x1b : (0xb + (x))) +#define NI_M_AI_START2_GATE_SEL 0x12 +#define NI_M_PXI_STAR_TRIGGER_GATE_SEL 0x13 +#define NI_M_NEXT_OUT_GATE_SEL 0x14 +#define NI_M_AI_START1_GATE_SEL 0x1c +#define NI_M_NEXT_SRC_GATE_SEL 0x1d +#define NI_M_ANALOG_TRIG_OUT_GATE_SEL 0x1e +#define NI_M_LOGIC_LOW_GATE_SEL 0x1f + +/* ni_660x gate select */ +#define NI_660X_SRC_PIN_I_GATE_SEL 0x0 +#define NI_660X_GATE_PIN_I_GATE_SEL 0x1 +#define NI_660X_PIN_GATE_SEL(x) (0x2 + (x)) +#define NI_660X_NEXT_SRC_GATE_SEL 0xa +#define NI_660X_RTSI_GATE_SEL(x) (0xb + (x)) +#define NI_660X_NEXT_OUT_GATE_SEL 0x14 +#define NI_660X_LOGIC_LOW_GATE_SEL 0x1f +#define NI_660X_MAX_GATE_PIN 7 + +/* ni_660x second gate select */ +#define NI_660X_SRC_PIN_I_GATE2_SEL 0x0 +#define NI_660X_UD_PIN_I_GATE2_SEL 0x1 +#define NI_660X_UD_PIN_GATE2_SEL(x) (0x2 + (x)) +#define NI_660X_NEXT_SRC_GATE2_SEL 0xa +#define NI_660X_RTSI_GATE2_SEL(x) (0xb + (x)) +#define NI_660X_NEXT_OUT_GATE2_SEL 0x14 +#define NI_660X_SELECTED_GATE2_SEL 0x1e +#define NI_660X_LOGIC_LOW_GATE2_SEL 0x1f +#define NI_660X_MAX_UP_DOWN_PIN 7 + +static inline unsigned GI_ALT_SYNC(enum ni_gpct_variant variant) +{ + switch (variant) { + case ni_gpct_variant_e_series: + default: + return 0; + case ni_gpct_variant_m_series: + return GI_M_ALT_SYNC; + case ni_gpct_variant_660x: + return GI_660X_ALT_SYNC; + } +} + +static inline unsigned GI_PRESCALE_X2(enum ni_gpct_variant variant) +{ + switch (variant) { + case ni_gpct_variant_e_series: + default: + return 0; + case ni_gpct_variant_m_series: + return GI_M_PRESCALE_X2; + case ni_gpct_variant_660x: + return GI_660X_PRESCALE_X2; + } +} + +static inline unsigned GI_PRESCALE_X8(enum ni_gpct_variant variant) +{ + switch (variant) { + case ni_gpct_variant_e_series: + default: + return 0; + case ni_gpct_variant_m_series: + return GI_M_PRESCALE_X8; + case ni_gpct_variant_660x: + return GI_660X_PRESCALE_X8; + } +} + +static inline unsigned GI_HW_ARM_SEL_MASK(enum ni_gpct_variant variant) +{ + switch (variant) { + case ni_gpct_variant_e_series: + default: + return 0; + case ni_gpct_variant_m_series: + return GI_M_HW_ARM_SEL_MASK; + case ni_gpct_variant_660x: + return GI_660X_HW_ARM_SEL_MASK; + } +} + +static int ni_tio_has_gate2_registers(const struct ni_gpct_device *counter_dev) +{ + switch (counter_dev->variant) { + case ni_gpct_variant_e_series: + default: + return 0; + case ni_gpct_variant_m_series: + case ni_gpct_variant_660x: + return 1; + } +} + +static void ni_tio_reset_count_and_disarm(struct ni_gpct *counter) +{ + unsigned cidx = counter->counter_index; + + write_register(counter, GI_RESET(cidx), NITIO_RESET_REG(cidx)); +} + +static uint64_t ni_tio_clock_period_ps(const struct ni_gpct *counter, + unsigned generic_clock_source) +{ + uint64_t clock_period_ps; + + switch (generic_clock_source & NI_GPCT_CLOCK_SRC_SELECT_MASK) { + case NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS: + clock_period_ps = 50000; + break; + case NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS: + clock_period_ps = 10000000; + break; + case NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS: + clock_period_ps = 12500; + break; + case NI_GPCT_PXI10_CLOCK_SRC_BITS: + clock_period_ps = 100000; + break; + default: + /* + * clock period is specified by user with prescaling + * already taken into account. + */ + return counter->clock_period_ps; + } + + switch (generic_clock_source & NI_GPCT_PRESCALE_MODE_CLOCK_SRC_MASK) { + case NI_GPCT_NO_PRESCALE_CLOCK_SRC_BITS: + break; + case NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS: + clock_period_ps *= 2; + break; + case NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS: + clock_period_ps *= 8; + break; + default: + BUG(); + break; + } + return clock_period_ps; +} + +static unsigned ni_tio_clock_src_modifiers(const struct ni_gpct *counter) +{ + struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned cidx = counter->counter_index; + const unsigned counting_mode_bits = + ni_tio_get_soft_copy(counter, NITIO_CNT_MODE_REG(cidx)); + unsigned bits = 0; + + if (ni_tio_get_soft_copy(counter, NITIO_INPUT_SEL_REG(cidx)) & + GI_SRC_POL_INVERT) + bits |= NI_GPCT_INVERT_CLOCK_SRC_BIT; + if (counting_mode_bits & GI_PRESCALE_X2(counter_dev->variant)) + bits |= NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS; + if (counting_mode_bits & GI_PRESCALE_X8(counter_dev->variant)) + bits |= NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS; + return bits; +} + +static unsigned ni_m_series_clock_src_select(const struct ni_gpct *counter) +{ + struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned cidx = counter->counter_index; + const unsigned second_gate_reg = NITIO_GATE2_REG(cidx); + unsigned clock_source = 0; + unsigned src; + unsigned i; + + src = GI_BITS_TO_SRC(ni_tio_get_soft_copy(counter, + NITIO_INPUT_SEL_REG(cidx))); + + switch (src) { + case NI_M_TIMEBASE_1_CLK: + clock_source = NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS; + break; + case NI_M_TIMEBASE_2_CLK: + clock_source = NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS; + break; + case NI_M_TIMEBASE_3_CLK: + if (counter_dev->regs[second_gate_reg] & GI_SRC_SUBSEL) + clock_source = + NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS; + else + clock_source = NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS; + break; + case NI_M_LOGIC_LOW_CLK: + clock_source = NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS; + break; + case NI_M_NEXT_GATE_CLK: + if (counter_dev->regs[second_gate_reg] & GI_SRC_SUBSEL) + clock_source = NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS; + else + clock_source = NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS; + break; + case NI_M_PXI10_CLK: + clock_source = NI_GPCT_PXI10_CLOCK_SRC_BITS; + break; + case NI_M_NEXT_TC_CLK: + clock_source = NI_GPCT_NEXT_TC_CLOCK_SRC_BITS; + break; + default: + for (i = 0; i <= NI_M_MAX_RTSI_CHAN; ++i) { + if (src == NI_M_RTSI_CLK(i)) { + clock_source = NI_GPCT_RTSI_CLOCK_SRC_BITS(i); + break; + } + } + if (i <= NI_M_MAX_RTSI_CHAN) + break; + for (i = 0; i <= NI_M_MAX_PFI_CHAN; ++i) { + if (src == NI_M_PFI_CLK(i)) { + clock_source = NI_GPCT_PFI_CLOCK_SRC_BITS(i); + break; + } + } + if (i <= NI_M_MAX_PFI_CHAN) + break; + BUG(); + break; + } + clock_source |= ni_tio_clock_src_modifiers(counter); + return clock_source; +} + +static unsigned ni_660x_clock_src_select(const struct ni_gpct *counter) +{ + unsigned clock_source = 0; + unsigned cidx = counter->counter_index; + unsigned src; + unsigned i; + + src = GI_BITS_TO_SRC(ni_tio_get_soft_copy(counter, + NITIO_INPUT_SEL_REG(cidx))); + + switch (src) { + case NI_660X_TIMEBASE_1_CLK: + clock_source = NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS; + break; + case NI_660X_TIMEBASE_2_CLK: + clock_source = NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS; + break; + case NI_660X_TIMEBASE_3_CLK: + clock_source = NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS; + break; + case NI_660X_LOGIC_LOW_CLK: + clock_source = NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS; + break; + case NI_660X_SRC_PIN_I_CLK: + clock_source = NI_GPCT_SOURCE_PIN_i_CLOCK_SRC_BITS; + break; + case NI_660X_NEXT_GATE_CLK: + clock_source = NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS; + break; + case NI_660X_NEXT_TC_CLK: + clock_source = NI_GPCT_NEXT_TC_CLOCK_SRC_BITS; + break; + default: + for (i = 0; i <= NI_660X_MAX_RTSI_CHAN; ++i) { + if (src == NI_660X_RTSI_CLK(i)) { + clock_source = NI_GPCT_RTSI_CLOCK_SRC_BITS(i); + break; + } + } + if (i <= NI_660X_MAX_RTSI_CHAN) + break; + for (i = 0; i <= NI_660X_MAX_SRC_PIN; ++i) { + if (src == NI_660X_SRC_PIN_CLK(i)) { + clock_source = + NI_GPCT_SOURCE_PIN_CLOCK_SRC_BITS(i); + break; + } + } + if (i <= NI_660X_MAX_SRC_PIN) + break; + BUG(); + break; + } + clock_source |= ni_tio_clock_src_modifiers(counter); + return clock_source; +} + +static unsigned ni_tio_generic_clock_src_select(const struct ni_gpct *counter) +{ + switch (counter->counter_dev->variant) { + case ni_gpct_variant_e_series: + case ni_gpct_variant_m_series: + default: + return ni_m_series_clock_src_select(counter); + case ni_gpct_variant_660x: + return ni_660x_clock_src_select(counter); + } +} + +static void ni_tio_set_sync_mode(struct ni_gpct *counter, int force_alt_sync) +{ + struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned cidx = counter->counter_index; + const unsigned counting_mode_reg = NITIO_CNT_MODE_REG(cidx); + static const uint64_t min_normal_sync_period_ps = 25000; + unsigned mode; + uint64_t clock_period_ps; + + if (ni_tio_counting_mode_registers_present(counter_dev) == 0) + return; + + mode = ni_tio_get_soft_copy(counter, counting_mode_reg); + switch (mode & GI_CNT_MODE_MASK) { + case GI_CNT_MODE_QUADX1: + case GI_CNT_MODE_QUADX2: + case GI_CNT_MODE_QUADX4: + case GI_CNT_MODE_SYNC_SRC: + force_alt_sync = 1; + break; + default: + break; + } + + clock_period_ps = ni_tio_clock_period_ps(counter, + ni_tio_generic_clock_src_select(counter)); + + /* + * It's not clear what we should do if clock_period is unknown, so we + * are not using the alt sync bit in that case, but allow the caller + * to decide by using the force_alt_sync parameter. + */ + if (force_alt_sync || + (clock_period_ps && clock_period_ps < min_normal_sync_period_ps)) { + ni_tio_set_bits(counter, counting_mode_reg, + GI_ALT_SYNC(counter_dev->variant), + GI_ALT_SYNC(counter_dev->variant)); + } else { + ni_tio_set_bits(counter, counting_mode_reg, + GI_ALT_SYNC(counter_dev->variant), + 0x0); + } +} + +static int ni_tio_set_counter_mode(struct ni_gpct *counter, unsigned mode) +{ + struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned cidx = counter->counter_index; + unsigned mode_reg_mask; + unsigned mode_reg_values; + unsigned input_select_bits = 0; + /* these bits map directly on to the mode register */ + static const unsigned mode_reg_direct_mask = + NI_GPCT_GATE_ON_BOTH_EDGES_BIT | NI_GPCT_EDGE_GATE_MODE_MASK | + NI_GPCT_STOP_MODE_MASK | NI_GPCT_OUTPUT_MODE_MASK | + NI_GPCT_HARDWARE_DISARM_MASK | NI_GPCT_LOADING_ON_TC_BIT | + NI_GPCT_LOADING_ON_GATE_BIT | NI_GPCT_LOAD_B_SELECT_BIT; + + mode_reg_mask = mode_reg_direct_mask | GI_RELOAD_SRC_SWITCHING; + mode_reg_values = mode & mode_reg_direct_mask; + switch (mode & NI_GPCT_RELOAD_SOURCE_MASK) { + case NI_GPCT_RELOAD_SOURCE_FIXED_BITS: + break; + case NI_GPCT_RELOAD_SOURCE_SWITCHING_BITS: + mode_reg_values |= GI_RELOAD_SRC_SWITCHING; + break; + case NI_GPCT_RELOAD_SOURCE_GATE_SELECT_BITS: + input_select_bits |= GI_GATE_SEL_LOAD_SRC; + mode_reg_mask |= GI_GATING_MODE_MASK; + mode_reg_values |= GI_LEVEL_GATING; + break; + default: + break; + } + ni_tio_set_bits(counter, NITIO_MODE_REG(cidx), + mode_reg_mask, mode_reg_values); + + if (ni_tio_counting_mode_registers_present(counter_dev)) { + unsigned bits = 0; + + bits |= GI_CNT_MODE(mode >> NI_GPCT_COUNTING_MODE_SHIFT); + bits |= GI_INDEX_PHASE((mode >> NI_GPCT_INDEX_PHASE_BITSHIFT)); + if (mode & NI_GPCT_INDEX_ENABLE_BIT) + bits |= GI_INDEX_MODE; + ni_tio_set_bits(counter, NITIO_CNT_MODE_REG(cidx), + GI_CNT_MODE_MASK | GI_INDEX_PHASE_MASK | + GI_INDEX_MODE, bits); + ni_tio_set_sync_mode(counter, 0); + } + + ni_tio_set_bits(counter, NITIO_CMD_REG(cidx), GI_CNT_DIR_MASK, + GI_CNT_DIR(mode >> NI_GPCT_COUNTING_DIRECTION_SHIFT)); + + if (mode & NI_GPCT_OR_GATE_BIT) + input_select_bits |= GI_OR_GATE; + if (mode & NI_GPCT_INVERT_OUTPUT_BIT) + input_select_bits |= GI_OUTPUT_POL_INVERT; + ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx), + GI_GATE_SEL_LOAD_SRC | GI_OR_GATE | + GI_OUTPUT_POL_INVERT, input_select_bits); + + return 0; +} + +int ni_tio_arm(struct ni_gpct *counter, int arm, unsigned start_trigger) +{ + struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned cidx = counter->counter_index; + unsigned command_transient_bits = 0; + + if (arm) { + switch (start_trigger) { + case NI_GPCT_ARM_IMMEDIATE: + command_transient_bits |= GI_ARM; + break; + case NI_GPCT_ARM_PAIRED_IMMEDIATE: + command_transient_bits |= GI_ARM | GI_ARM_COPY; + break; + default: + break; + } + if (ni_tio_counting_mode_registers_present(counter_dev)) { + unsigned bits = 0; + unsigned sel_mask; + + sel_mask = GI_HW_ARM_SEL_MASK(counter_dev->variant); + + switch (start_trigger) { + case NI_GPCT_ARM_IMMEDIATE: + case NI_GPCT_ARM_PAIRED_IMMEDIATE: + break; + default: + if (start_trigger & NI_GPCT_ARM_UNKNOWN) { + /* + * pass-through the least significant + * bits so we can figure out what + * select later + */ + bits |= GI_HW_ARM_ENA | + (GI_HW_ARM_SEL(start_trigger) & + sel_mask); + } else { + return -EINVAL; + } + break; + } + ni_tio_set_bits(counter, NITIO_CNT_MODE_REG(cidx), + GI_HW_ARM_ENA | sel_mask, bits); + } + } else { + command_transient_bits |= GI_DISARM; + } + ni_tio_set_bits_transient(counter, NITIO_CMD_REG(cidx), + 0, 0, command_transient_bits); + return 0; +} +EXPORT_SYMBOL_GPL(ni_tio_arm); + +static unsigned ni_660x_clk_src(unsigned int clock_source) +{ + unsigned clk_src = clock_source & NI_GPCT_CLOCK_SRC_SELECT_MASK; + unsigned ni_660x_clock; + unsigned i; + + switch (clk_src) { + case NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS: + ni_660x_clock = NI_660X_TIMEBASE_1_CLK; + break; + case NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS: + ni_660x_clock = NI_660X_TIMEBASE_2_CLK; + break; + case NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS: + ni_660x_clock = NI_660X_TIMEBASE_3_CLK; + break; + case NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS: + ni_660x_clock = NI_660X_LOGIC_LOW_CLK; + break; + case NI_GPCT_SOURCE_PIN_i_CLOCK_SRC_BITS: + ni_660x_clock = NI_660X_SRC_PIN_I_CLK; + break; + case NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS: + ni_660x_clock = NI_660X_NEXT_GATE_CLK; + break; + case NI_GPCT_NEXT_TC_CLOCK_SRC_BITS: + ni_660x_clock = NI_660X_NEXT_TC_CLK; + break; + default: + for (i = 0; i <= NI_660X_MAX_RTSI_CHAN; ++i) { + if (clk_src == NI_GPCT_RTSI_CLOCK_SRC_BITS(i)) { + ni_660x_clock = NI_660X_RTSI_CLK(i); + break; + } + } + if (i <= NI_660X_MAX_RTSI_CHAN) + break; + for (i = 0; i <= NI_660X_MAX_SRC_PIN; ++i) { + if (clk_src == NI_GPCT_SOURCE_PIN_CLOCK_SRC_BITS(i)) { + ni_660x_clock = NI_660X_SRC_PIN_CLK(i); + break; + } + } + if (i <= NI_660X_MAX_SRC_PIN) + break; + ni_660x_clock = 0; + BUG(); + break; + } + return GI_SRC_SEL(ni_660x_clock); +} + +static unsigned ni_m_clk_src(unsigned int clock_source) +{ + unsigned clk_src = clock_source & NI_GPCT_CLOCK_SRC_SELECT_MASK; + unsigned ni_m_series_clock; + unsigned i; + + switch (clk_src) { + case NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS: + ni_m_series_clock = NI_M_TIMEBASE_1_CLK; + break; + case NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS: + ni_m_series_clock = NI_M_TIMEBASE_2_CLK; + break; + case NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS: + ni_m_series_clock = NI_M_TIMEBASE_3_CLK; + break; + case NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS: + ni_m_series_clock = NI_M_LOGIC_LOW_CLK; + break; + case NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS: + ni_m_series_clock = NI_M_NEXT_GATE_CLK; + break; + case NI_GPCT_NEXT_TC_CLOCK_SRC_BITS: + ni_m_series_clock = NI_M_NEXT_TC_CLK; + break; + case NI_GPCT_PXI10_CLOCK_SRC_BITS: + ni_m_series_clock = NI_M_PXI10_CLK; + break; + case NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS: + ni_m_series_clock = NI_M_PXI_STAR_TRIGGER_CLK; + break; + case NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS: + ni_m_series_clock = NI_M_ANALOG_TRIGGER_OUT_CLK; + break; + default: + for (i = 0; i <= NI_M_MAX_RTSI_CHAN; ++i) { + if (clk_src == NI_GPCT_RTSI_CLOCK_SRC_BITS(i)) { + ni_m_series_clock = NI_M_RTSI_CLK(i); + break; + } + } + if (i <= NI_M_MAX_RTSI_CHAN) + break; + for (i = 0; i <= NI_M_MAX_PFI_CHAN; ++i) { + if (clk_src == NI_GPCT_PFI_CLOCK_SRC_BITS(i)) { + ni_m_series_clock = NI_M_PFI_CLK(i); + break; + } + } + if (i <= NI_M_MAX_PFI_CHAN) + break; + pr_err("invalid clock source 0x%lx\n", + (unsigned long)clock_source); + BUG(); + ni_m_series_clock = 0; + break; + } + return GI_SRC_SEL(ni_m_series_clock); +}; + +static void ni_tio_set_source_subselect(struct ni_gpct *counter, + unsigned int clock_source) +{ + struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned cidx = counter->counter_index; + const unsigned second_gate_reg = NITIO_GATE2_REG(cidx); + + if (counter_dev->variant != ni_gpct_variant_m_series) + return; + switch (clock_source & NI_GPCT_CLOCK_SRC_SELECT_MASK) { + /* Gi_Source_Subselect is zero */ + case NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS: + case NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS: + counter_dev->regs[second_gate_reg] &= ~GI_SRC_SUBSEL; + break; + /* Gi_Source_Subselect is one */ + case NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS: + case NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS: + counter_dev->regs[second_gate_reg] |= GI_SRC_SUBSEL; + break; + /* Gi_Source_Subselect doesn't matter */ + default: + return; + } + write_register(counter, counter_dev->regs[second_gate_reg], + second_gate_reg); +} + +static int ni_tio_set_clock_src(struct ni_gpct *counter, + unsigned int clock_source, + unsigned int period_ns) +{ + struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned cidx = counter->counter_index; + unsigned bits = 0; + + /* FIXME: validate clock source */ + switch (counter_dev->variant) { + case ni_gpct_variant_660x: + bits |= ni_660x_clk_src(clock_source); + break; + case ni_gpct_variant_e_series: + case ni_gpct_variant_m_series: + default: + bits |= ni_m_clk_src(clock_source); + break; + } + if (clock_source & NI_GPCT_INVERT_CLOCK_SRC_BIT) + bits |= GI_SRC_POL_INVERT; + ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx), + GI_SRC_SEL_MASK | GI_SRC_POL_INVERT, bits); + ni_tio_set_source_subselect(counter, clock_source); + + if (ni_tio_counting_mode_registers_present(counter_dev)) { + bits = 0; + switch (clock_source & NI_GPCT_PRESCALE_MODE_CLOCK_SRC_MASK) { + case NI_GPCT_NO_PRESCALE_CLOCK_SRC_BITS: + break; + case NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS: + bits |= GI_PRESCALE_X2(counter_dev->variant); + break; + case NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS: + bits |= GI_PRESCALE_X8(counter_dev->variant); + break; + default: + return -EINVAL; + } + ni_tio_set_bits(counter, NITIO_CNT_MODE_REG(cidx), + GI_PRESCALE_X2(counter_dev->variant) | + GI_PRESCALE_X8(counter_dev->variant), bits); + } + counter->clock_period_ps = period_ns * 1000; + ni_tio_set_sync_mode(counter, 0); + return 0; +} + +static void ni_tio_get_clock_src(struct ni_gpct *counter, + unsigned int *clock_source, + unsigned int *period_ns) +{ + uint64_t temp64; + + *clock_source = ni_tio_generic_clock_src_select(counter); + temp64 = ni_tio_clock_period_ps(counter, *clock_source); + do_div(temp64, 1000); /* ps to ns */ + *period_ns = temp64; +} + +static int ni_660x_set_gate(struct ni_gpct *counter, unsigned int gate_source) +{ + unsigned int chan = CR_CHAN(gate_source); + unsigned cidx = counter->counter_index; + unsigned gate_sel; + unsigned i; + + switch (chan) { + case NI_GPCT_NEXT_SOURCE_GATE_SELECT: + gate_sel = NI_660X_NEXT_SRC_GATE_SEL; + break; + case NI_GPCT_NEXT_OUT_GATE_SELECT: + case NI_GPCT_LOGIC_LOW_GATE_SELECT: + case NI_GPCT_SOURCE_PIN_i_GATE_SELECT: + case NI_GPCT_GATE_PIN_i_GATE_SELECT: + gate_sel = chan & 0x1f; + break; + default: + for (i = 0; i <= NI_660X_MAX_RTSI_CHAN; ++i) { + if (chan == NI_GPCT_RTSI_GATE_SELECT(i)) { + gate_sel = chan & 0x1f; + break; + } + } + if (i <= NI_660X_MAX_RTSI_CHAN) + break; + for (i = 0; i <= NI_660X_MAX_GATE_PIN; ++i) { + if (chan == NI_GPCT_GATE_PIN_GATE_SELECT(i)) { + gate_sel = chan & 0x1f; + break; + } + } + if (i <= NI_660X_MAX_GATE_PIN) + break; + return -EINVAL; + } + ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx), + GI_GATE_SEL_MASK, GI_GATE_SEL(gate_sel)); + return 0; +} + +static int ni_m_set_gate(struct ni_gpct *counter, unsigned int gate_source) +{ + unsigned int chan = CR_CHAN(gate_source); + unsigned cidx = counter->counter_index; + unsigned gate_sel; + unsigned i; + + switch (chan) { + case NI_GPCT_TIMESTAMP_MUX_GATE_SELECT: + case NI_GPCT_AI_START2_GATE_SELECT: + case NI_GPCT_PXI_STAR_TRIGGER_GATE_SELECT: + case NI_GPCT_NEXT_OUT_GATE_SELECT: + case NI_GPCT_AI_START1_GATE_SELECT: + case NI_GPCT_NEXT_SOURCE_GATE_SELECT: + case NI_GPCT_ANALOG_TRIGGER_OUT_GATE_SELECT: + case NI_GPCT_LOGIC_LOW_GATE_SELECT: + gate_sel = chan & 0x1f; + break; + default: + for (i = 0; i <= NI_M_MAX_RTSI_CHAN; ++i) { + if (chan == NI_GPCT_RTSI_GATE_SELECT(i)) { + gate_sel = chan & 0x1f; + break; + } + } + if (i <= NI_M_MAX_RTSI_CHAN) + break; + for (i = 0; i <= NI_M_MAX_PFI_CHAN; ++i) { + if (chan == NI_GPCT_PFI_GATE_SELECT(i)) { + gate_sel = chan & 0x1f; + break; + } + } + if (i <= NI_M_MAX_PFI_CHAN) + break; + return -EINVAL; + } + ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx), + GI_GATE_SEL_MASK, GI_GATE_SEL(gate_sel)); + return 0; +} + +static int ni_660x_set_gate2(struct ni_gpct *counter, unsigned int gate_source) +{ + struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned cidx = counter->counter_index; + unsigned int chan = CR_CHAN(gate_source); + unsigned gate2_reg = NITIO_GATE2_REG(cidx); + unsigned gate2_sel; + unsigned i; + + switch (chan) { + case NI_GPCT_SOURCE_PIN_i_GATE_SELECT: + case NI_GPCT_UP_DOWN_PIN_i_GATE_SELECT: + case NI_GPCT_SELECTED_GATE_GATE_SELECT: + case NI_GPCT_NEXT_OUT_GATE_SELECT: + case NI_GPCT_LOGIC_LOW_GATE_SELECT: + gate2_sel = chan & 0x1f; + break; + case NI_GPCT_NEXT_SOURCE_GATE_SELECT: + gate2_sel = NI_660X_NEXT_SRC_GATE2_SEL; + break; + default: + for (i = 0; i <= NI_660X_MAX_RTSI_CHAN; ++i) { + if (chan == NI_GPCT_RTSI_GATE_SELECT(i)) { + gate2_sel = chan & 0x1f; + break; + } + } + if (i <= NI_660X_MAX_RTSI_CHAN) + break; + for (i = 0; i <= NI_660X_MAX_UP_DOWN_PIN; ++i) { + if (chan == NI_GPCT_UP_DOWN_PIN_GATE_SELECT(i)) { + gate2_sel = chan & 0x1f; + break; + } + } + if (i <= NI_660X_MAX_UP_DOWN_PIN) + break; + return -EINVAL; + } + counter_dev->regs[gate2_reg] |= GI_GATE2_MODE; + counter_dev->regs[gate2_reg] &= ~GI_GATE2_SEL_MASK; + counter_dev->regs[gate2_reg] |= GI_GATE2_SEL(gate2_sel); + write_register(counter, counter_dev->regs[gate2_reg], gate2_reg); + return 0; +} + +static int ni_m_set_gate2(struct ni_gpct *counter, unsigned int gate_source) +{ + struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned cidx = counter->counter_index; + unsigned int chan = CR_CHAN(gate_source); + unsigned gate2_reg = NITIO_GATE2_REG(cidx); + unsigned gate2_sel; + + /* + * FIXME: We don't know what the m-series second gate codes are, + * so we'll just pass the bits through for now. + */ + switch (chan) { + default: + gate2_sel = chan & 0x1f; + break; + } + counter_dev->regs[gate2_reg] |= GI_GATE2_MODE; + counter_dev->regs[gate2_reg] &= ~GI_GATE2_SEL_MASK; + counter_dev->regs[gate2_reg] |= GI_GATE2_SEL(gate2_sel); + write_register(counter, counter_dev->regs[gate2_reg], gate2_reg); + return 0; +} + +int ni_tio_set_gate_src(struct ni_gpct *counter, unsigned gate_index, + unsigned int gate_source) +{ + struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned cidx = counter->counter_index; + unsigned int chan = CR_CHAN(gate_source); + unsigned gate2_reg = NITIO_GATE2_REG(cidx); + unsigned mode = 0; + + switch (gate_index) { + case 0: + if (chan == NI_GPCT_DISABLED_GATE_SELECT) { + ni_tio_set_bits(counter, NITIO_MODE_REG(cidx), + GI_GATING_MODE_MASK, + GI_GATING_DISABLED); + return 0; + } + if (gate_source & CR_INVERT) + mode |= GI_GATE_POL_INVERT; + if (gate_source & CR_EDGE) + mode |= GI_RISING_EDGE_GATING; + else + mode |= GI_LEVEL_GATING; + ni_tio_set_bits(counter, NITIO_MODE_REG(cidx), + GI_GATE_POL_INVERT | GI_GATING_MODE_MASK, + mode); + switch (counter_dev->variant) { + case ni_gpct_variant_e_series: + case ni_gpct_variant_m_series: + default: + return ni_m_set_gate(counter, gate_source); + case ni_gpct_variant_660x: + return ni_660x_set_gate(counter, gate_source); + } + break; + case 1: + if (!ni_tio_has_gate2_registers(counter_dev)) + return -EINVAL; + + if (chan == NI_GPCT_DISABLED_GATE_SELECT) { + counter_dev->regs[gate2_reg] &= ~GI_GATE2_MODE; + write_register(counter, counter_dev->regs[gate2_reg], + gate2_reg); + return 0; + } + if (gate_source & CR_INVERT) + counter_dev->regs[gate2_reg] |= GI_GATE2_POL_INVERT; + else + counter_dev->regs[gate2_reg] &= ~GI_GATE2_POL_INVERT; + switch (counter_dev->variant) { + case ni_gpct_variant_m_series: + return ni_m_set_gate2(counter, gate_source); + case ni_gpct_variant_660x: + return ni_660x_set_gate2(counter, gate_source); + default: + BUG(); + break; + } + break; + default: + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(ni_tio_set_gate_src); + +static int ni_tio_set_other_src(struct ni_gpct *counter, unsigned index, + unsigned int source) +{ + struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned cidx = counter->counter_index; + unsigned int abz_reg, shift, mask; + + if (counter_dev->variant != ni_gpct_variant_m_series) + return -EINVAL; + + abz_reg = NITIO_ABZ_REG(cidx); + switch (index) { + case NI_GPCT_SOURCE_ENCODER_A: + shift = 10; + break; + case NI_GPCT_SOURCE_ENCODER_B: + shift = 5; + break; + case NI_GPCT_SOURCE_ENCODER_Z: + shift = 0; + break; + default: + return -EINVAL; + } + mask = 0x1f << shift; + if (source > 0x1f) + source = 0x1f; /* Disable gate */ + + counter_dev->regs[abz_reg] &= ~mask; + counter_dev->regs[abz_reg] |= (source << shift) & mask; + write_register(counter, counter_dev->regs[abz_reg], abz_reg); + return 0; +} + +static unsigned ni_660x_gate_to_generic_gate(unsigned gate) +{ + unsigned i; + + switch (gate) { + case NI_660X_SRC_PIN_I_GATE_SEL: + return NI_GPCT_SOURCE_PIN_i_GATE_SELECT; + case NI_660X_GATE_PIN_I_GATE_SEL: + return NI_GPCT_GATE_PIN_i_GATE_SELECT; + case NI_660X_NEXT_SRC_GATE_SEL: + return NI_GPCT_NEXT_SOURCE_GATE_SELECT; + case NI_660X_NEXT_OUT_GATE_SEL: + return NI_GPCT_NEXT_OUT_GATE_SELECT; + case NI_660X_LOGIC_LOW_GATE_SEL: + return NI_GPCT_LOGIC_LOW_GATE_SELECT; + default: + for (i = 0; i <= NI_660X_MAX_RTSI_CHAN; ++i) { + if (gate == NI_660X_RTSI_GATE_SEL(i)) + return NI_GPCT_RTSI_GATE_SELECT(i); + } + for (i = 0; i <= NI_660X_MAX_GATE_PIN; ++i) { + if (gate == NI_660X_PIN_GATE_SEL(i)) + return NI_GPCT_GATE_PIN_GATE_SELECT(i); + } + BUG(); + break; + } + return 0; +}; + +static unsigned ni_m_gate_to_generic_gate(unsigned gate) +{ + unsigned i; + + switch (gate) { + case NI_M_TIMESTAMP_MUX_GATE_SEL: + return NI_GPCT_TIMESTAMP_MUX_GATE_SELECT; + case NI_M_AI_START2_GATE_SEL: + return NI_GPCT_AI_START2_GATE_SELECT; + case NI_M_PXI_STAR_TRIGGER_GATE_SEL: + return NI_GPCT_PXI_STAR_TRIGGER_GATE_SELECT; + case NI_M_NEXT_OUT_GATE_SEL: + return NI_GPCT_NEXT_OUT_GATE_SELECT; + case NI_M_AI_START1_GATE_SEL: + return NI_GPCT_AI_START1_GATE_SELECT; + case NI_M_NEXT_SRC_GATE_SEL: + return NI_GPCT_NEXT_SOURCE_GATE_SELECT; + case NI_M_ANALOG_TRIG_OUT_GATE_SEL: + return NI_GPCT_ANALOG_TRIGGER_OUT_GATE_SELECT; + case NI_M_LOGIC_LOW_GATE_SEL: + return NI_GPCT_LOGIC_LOW_GATE_SELECT; + default: + for (i = 0; i <= NI_M_MAX_RTSI_CHAN; ++i) { + if (gate == NI_M_RTSI_GATE_SEL(i)) + return NI_GPCT_RTSI_GATE_SELECT(i); + } + for (i = 0; i <= NI_M_MAX_PFI_CHAN; ++i) { + if (gate == NI_M_PFI_GATE_SEL(i)) + return NI_GPCT_PFI_GATE_SELECT(i); + } + BUG(); + break; + } + return 0; +}; + +static unsigned ni_660x_gate2_to_generic_gate(unsigned gate) +{ + unsigned i; + + switch (gate) { + case NI_660X_SRC_PIN_I_GATE2_SEL: + return NI_GPCT_SOURCE_PIN_i_GATE_SELECT; + case NI_660X_UD_PIN_I_GATE2_SEL: + return NI_GPCT_UP_DOWN_PIN_i_GATE_SELECT; + case NI_660X_NEXT_SRC_GATE2_SEL: + return NI_GPCT_NEXT_SOURCE_GATE_SELECT; + case NI_660X_NEXT_OUT_GATE2_SEL: + return NI_GPCT_NEXT_OUT_GATE_SELECT; + case NI_660X_SELECTED_GATE2_SEL: + return NI_GPCT_SELECTED_GATE_GATE_SELECT; + case NI_660X_LOGIC_LOW_GATE2_SEL: + return NI_GPCT_LOGIC_LOW_GATE_SELECT; + default: + for (i = 0; i <= NI_660X_MAX_RTSI_CHAN; ++i) { + if (gate == NI_660X_RTSI_GATE2_SEL(i)) + return NI_GPCT_RTSI_GATE_SELECT(i); + } + for (i = 0; i <= NI_660X_MAX_UP_DOWN_PIN; ++i) { + if (gate == NI_660X_UD_PIN_GATE2_SEL(i)) + return NI_GPCT_UP_DOWN_PIN_GATE_SELECT(i); + } + BUG(); + break; + } + return 0; +}; + +static unsigned ni_m_gate2_to_generic_gate(unsigned gate) +{ + /* + * FIXME: the second gate sources for the m series are undocumented, + * so we just return the raw bits for now. + */ + switch (gate) { + default: + return gate; + } + return 0; +}; + +static int ni_tio_get_gate_src(struct ni_gpct *counter, unsigned gate_index, + unsigned int *gate_source) +{ + struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned cidx = counter->counter_index; + unsigned mode = ni_tio_get_soft_copy(counter, NITIO_MODE_REG(cidx)); + unsigned gate2_reg = NITIO_GATE2_REG(cidx); + unsigned gate; + + switch (gate_index) { + case 0: + if ((mode & GI_GATING_MODE_MASK) == GI_GATING_DISABLED) { + *gate_source = NI_GPCT_DISABLED_GATE_SELECT; + return 0; + } + + gate = GI_BITS_TO_GATE(ni_tio_get_soft_copy(counter, + NITIO_INPUT_SEL_REG(cidx))); + + switch (counter_dev->variant) { + case ni_gpct_variant_e_series: + case ni_gpct_variant_m_series: + default: + *gate_source = ni_m_gate_to_generic_gate(gate); + break; + case ni_gpct_variant_660x: + *gate_source = ni_660x_gate_to_generic_gate(gate); + break; + } + if (mode & GI_GATE_POL_INVERT) + *gate_source |= CR_INVERT; + if ((mode & GI_GATING_MODE_MASK) != GI_LEVEL_GATING) + *gate_source |= CR_EDGE; + break; + case 1: + if ((mode & GI_GATING_MODE_MASK) == GI_GATING_DISABLED || + !(counter_dev->regs[gate2_reg] & GI_GATE2_MODE)) { + *gate_source = NI_GPCT_DISABLED_GATE_SELECT; + return 0; + } + + gate = GI_BITS_TO_GATE2(counter_dev->regs[gate2_reg]); + + switch (counter_dev->variant) { + case ni_gpct_variant_e_series: + case ni_gpct_variant_m_series: + default: + *gate_source = ni_m_gate2_to_generic_gate(gate); + break; + case ni_gpct_variant_660x: + *gate_source = ni_660x_gate2_to_generic_gate(gate); + break; + } + if (counter_dev->regs[gate2_reg] & GI_GATE2_POL_INVERT) + *gate_source |= CR_INVERT; + /* second gate can't have edge/level mode set independently */ + if ((mode & GI_GATING_MODE_MASK) != GI_LEVEL_GATING) + *gate_source |= CR_EDGE; + break; + default: + return -EINVAL; + } + return 0; +} + +int ni_tio_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + struct ni_gpct *counter = s->private; + unsigned cidx = counter->counter_index; + unsigned status; + + switch (data[0]) { + case INSN_CONFIG_SET_COUNTER_MODE: + return ni_tio_set_counter_mode(counter, data[1]); + case INSN_CONFIG_ARM: + return ni_tio_arm(counter, 1, data[1]); + case INSN_CONFIG_DISARM: + ni_tio_arm(counter, 0, 0); + return 0; + case INSN_CONFIG_GET_COUNTER_STATUS: + data[1] = 0; + status = read_register(counter, NITIO_SHARED_STATUS_REG(cidx)); + if (status & GI_ARMED(cidx)) { + data[1] |= COMEDI_COUNTER_ARMED; + if (status & GI_COUNTING(cidx)) + data[1] |= COMEDI_COUNTER_COUNTING; + } + data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING; + return 0; + case INSN_CONFIG_SET_CLOCK_SRC: + return ni_tio_set_clock_src(counter, data[1], data[2]); + case INSN_CONFIG_GET_CLOCK_SRC: + ni_tio_get_clock_src(counter, &data[1], &data[2]); + return 0; + case INSN_CONFIG_SET_GATE_SRC: + return ni_tio_set_gate_src(counter, data[1], data[2]); + case INSN_CONFIG_GET_GATE_SRC: + return ni_tio_get_gate_src(counter, data[1], &data[2]); + case INSN_CONFIG_SET_OTHER_SRC: + return ni_tio_set_other_src(counter, data[1], data[2]); + case INSN_CONFIG_RESET: + ni_tio_reset_count_and_disarm(counter); + return 0; + default: + break; + } + return -EINVAL; +} +EXPORT_SYMBOL_GPL(ni_tio_insn_config); + +static unsigned int ni_tio_read_sw_save_reg(struct comedi_device *dev, + struct comedi_subdevice *s) +{ + struct ni_gpct *counter = s->private; + unsigned cidx = counter->counter_index; + unsigned int val; + + ni_tio_set_bits(counter, NITIO_CMD_REG(cidx), GI_SAVE_TRACE, 0); + ni_tio_set_bits(counter, NITIO_CMD_REG(cidx), + GI_SAVE_TRACE, GI_SAVE_TRACE); + + /* + * The count doesn't get latched until the next clock edge, so it is + * possible the count may change (once) while we are reading. Since + * the read of the SW_Save_Reg isn't atomic (apparently even when it's + * a 32 bit register according to 660x docs), we need to read twice + * and make sure the reading hasn't changed. If it has, a third read + * will be correct since the count value will definitely have latched + * by then. + */ + val = read_register(counter, NITIO_SW_SAVE_REG(cidx)); + if (val != read_register(counter, NITIO_SW_SAVE_REG(cidx))) + val = read_register(counter, NITIO_SW_SAVE_REG(cidx)); + + return val; +} + +int ni_tio_insn_read(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + struct ni_gpct *counter = s->private; + struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned int channel = CR_CHAN(insn->chanspec); + unsigned cidx = counter->counter_index; + int i; + + for (i = 0; i < insn->n; i++) { + switch (channel) { + case 0: + data[i] = ni_tio_read_sw_save_reg(dev, s); + break; + case 1: + data[i] = counter_dev->regs[NITIO_LOADA_REG(cidx)]; + break; + case 2: + data[i] = counter_dev->regs[NITIO_LOADB_REG(cidx)]; + break; + } + } + return insn->n; +} +EXPORT_SYMBOL_GPL(ni_tio_insn_read); + +static unsigned ni_tio_next_load_register(struct ni_gpct *counter) +{ + unsigned cidx = counter->counter_index; + const unsigned bits = + read_register(counter, NITIO_SHARED_STATUS_REG(cidx)); + + return (bits & GI_NEXT_LOAD_SRC(cidx)) + ? NITIO_LOADB_REG(cidx) + : NITIO_LOADA_REG(cidx); +} + +int ni_tio_insn_write(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + struct ni_gpct *counter = s->private; + struct ni_gpct_device *counter_dev = counter->counter_dev; + const unsigned channel = CR_CHAN(insn->chanspec); + unsigned cidx = counter->counter_index; + unsigned load_reg; + + if (insn->n < 1) + return 0; + switch (channel) { + case 0: + /* + * Unsafe if counter is armed. + * Should probably check status and return -EBUSY if armed. + */ + + /* + * Don't disturb load source select, just use whichever + * load register is already selected. + */ + load_reg = ni_tio_next_load_register(counter); + write_register(counter, data[0], load_reg); + ni_tio_set_bits_transient(counter, NITIO_CMD_REG(cidx), + 0, 0, GI_LOAD); + /* restore load reg */ + write_register(counter, counter_dev->regs[load_reg], load_reg); + break; + case 1: + counter_dev->regs[NITIO_LOADA_REG(cidx)] = data[0]; + write_register(counter, data[0], NITIO_LOADA_REG(cidx)); + break; + case 2: + counter_dev->regs[NITIO_LOADB_REG(cidx)] = data[0]; + write_register(counter, data[0], NITIO_LOADB_REG(cidx)); + break; + default: + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(ni_tio_insn_write); + +void ni_tio_init_counter(struct ni_gpct *counter) +{ + struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned cidx = counter->counter_index; + + ni_tio_reset_count_and_disarm(counter); + + /* initialize counter registers */ + counter_dev->regs[NITIO_AUTO_INC_REG(cidx)] = 0x0; + write_register(counter, 0x0, NITIO_AUTO_INC_REG(cidx)); + + ni_tio_set_bits(counter, NITIO_CMD_REG(cidx), + ~0, GI_SYNC_GATE); + + ni_tio_set_bits(counter, NITIO_MODE_REG(cidx), ~0, 0); + + counter_dev->regs[NITIO_LOADA_REG(cidx)] = 0x0; + write_register(counter, 0x0, NITIO_LOADA_REG(cidx)); + + counter_dev->regs[NITIO_LOADB_REG(cidx)] = 0x0; + write_register(counter, 0x0, NITIO_LOADB_REG(cidx)); + + ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx), ~0, 0); + + if (ni_tio_counting_mode_registers_present(counter_dev)) + ni_tio_set_bits(counter, NITIO_CNT_MODE_REG(cidx), ~0, 0); + + if (ni_tio_has_gate2_registers(counter_dev)) { + counter_dev->regs[NITIO_GATE2_REG(cidx)] = 0x0; + write_register(counter, 0x0, NITIO_GATE2_REG(cidx)); + } + + ni_tio_set_bits(counter, NITIO_DMA_CFG_REG(cidx), ~0, 0x0); + + ni_tio_set_bits(counter, NITIO_INT_ENA_REG(cidx), ~0, 0x0); +} +EXPORT_SYMBOL_GPL(ni_tio_init_counter); + +struct ni_gpct_device * +ni_gpct_device_construct(struct comedi_device *dev, + void (*write_register)(struct ni_gpct *counter, + unsigned bits, + enum ni_gpct_register reg), + unsigned (*read_register)(struct ni_gpct *counter, + enum ni_gpct_register reg), + enum ni_gpct_variant variant, + unsigned num_counters) +{ + struct ni_gpct_device *counter_dev; + struct ni_gpct *counter; + unsigned i; + + if (num_counters == 0) + return NULL; + + counter_dev = kzalloc(sizeof(*counter_dev), GFP_KERNEL); + if (!counter_dev) + return NULL; + + counter_dev->dev = dev; + counter_dev->write_register = write_register; + counter_dev->read_register = read_register; + counter_dev->variant = variant; + + spin_lock_init(&counter_dev->regs_lock); + + counter_dev->counters = kcalloc(num_counters, sizeof(*counter), + GFP_KERNEL); + if (!counter_dev->counters) { + kfree(counter_dev); + return NULL; + } + + for (i = 0; i < num_counters; ++i) { + counter = &counter_dev->counters[i]; + counter->counter_dev = counter_dev; + spin_lock_init(&counter->lock); + } + counter_dev->num_counters = num_counters; + + return counter_dev; +} +EXPORT_SYMBOL_GPL(ni_gpct_device_construct); + +void ni_gpct_device_destroy(struct ni_gpct_device *counter_dev) +{ + if (!counter_dev->counters) + return; + kfree(counter_dev->counters); + kfree(counter_dev); +} +EXPORT_SYMBOL_GPL(ni_gpct_device_destroy); + +static int __init ni_tio_init_module(void) +{ + return 0; +} +module_init(ni_tio_init_module); + +static void __exit ni_tio_cleanup_module(void) +{ +} +module_exit(ni_tio_cleanup_module); + +MODULE_AUTHOR("Comedi <comedi@comedi.org>"); +MODULE_DESCRIPTION("Comedi support for NI general-purpose counters"); +MODULE_LICENSE("GPL"); |