diff options
Diffstat (limited to 'drivers/staging/comedi')
-rw-r--r-- | drivers/staging/comedi/drivers/ni_660x.c | 18 | ||||
-rw-r--r-- | drivers/staging/comedi/drivers/ni_mio_common.c | 6 | ||||
-rw-r--r-- | drivers/staging/comedi/drivers/ni_tio.c | 457 | ||||
-rw-r--r-- | drivers/staging/comedi/drivers/ni_tio.h | 42 | ||||
-rw-r--r-- | drivers/staging/comedi/drivers/ni_tio_internal.h | 2 | ||||
-rw-r--r-- | drivers/staging/comedi/drivers/ni_tiocmd.c | 66 |
6 files changed, 476 insertions, 115 deletions
diff --git a/drivers/staging/comedi/drivers/ni_660x.c b/drivers/staging/comedi/drivers/ni_660x.c index e521ed9d0887..498b2868c957 100644 --- a/drivers/staging/comedi/drivers/ni_660x.c +++ b/drivers/staging/comedi/drivers/ni_660x.c @@ -31,6 +31,7 @@ #include "mite.h" #include "ni_tio.h" +#include "ni_routes.h" /* See Register-Level Programmer Manual page 3.1 */ enum ni_660x_register { @@ -259,6 +260,7 @@ struct ni_660x_private { unsigned int dma_cfg[NI660X_MAX_CHIPS]; unsigned int io_cfg[NI660X_NUM_PFI_CHANNELS]; u64 io_dir; + struct ni_route_tables routing_tables; }; static void ni_660x_write(struct comedi_device *dev, unsigned int chip, @@ -730,12 +732,23 @@ static int ni_660x_auto_attach(struct comedi_device *dev, ni_660x_init_tio_chips(dev, board->n_chips); + /* prepare the device for globally-named routes. */ + if (ni_assign_device_routes("ni_660x", board->name, + &devpriv->routing_tables) < 0) { + dev_warn(dev->class_dev, "%s: %s device has no signal routing table.\n", + __func__, board->name); + dev_warn(dev->class_dev, "%s: High level NI signal names will not be available for this %s board.\n", + __func__, board->name); + } + n_counters = board->n_chips * NI660X_COUNTERS_PER_CHIP; gpct_dev = ni_gpct_device_construct(dev, ni_660x_gpct_write, ni_660x_gpct_read, ni_gpct_variant_660x, - n_counters); + n_counters, + NI660X_COUNTERS_PER_CHIP, + &devpriv->routing_tables); if (!gpct_dev) return -ENOMEM; devpriv->counter_dev = gpct_dev; @@ -831,9 +844,6 @@ static int ni_660x_auto_attach(struct comedi_device *dev, if (i < n_counters) { struct ni_gpct *counter = &gpct_dev->counters[i]; - counter->chip_index = i / NI660X_COUNTERS_PER_CHIP; - counter->counter_index = i % NI660X_COUNTERS_PER_CHIP; - s->type = COMEDI_SUBD_COUNTER; s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL | SDF_CMD_READ; diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c index d0c403a9a226..addea2446197 100644 --- a/drivers/staging/comedi/drivers/ni_mio_common.c +++ b/drivers/staging/comedi/drivers/ni_mio_common.c @@ -6213,7 +6213,9 @@ static int ni_E_init(struct comedi_device *dev, (devpriv->is_m_series) ? ni_gpct_variant_m_series : ni_gpct_variant_e_series, - NUM_GPCT); + NUM_GPCT, + NUM_GPCT, + &devpriv->routing_tables); if (!devpriv->counter_dev) return -ENOMEM; @@ -6222,8 +6224,6 @@ static int ni_E_init(struct comedi_device *dev, struct ni_gpct *gpct = &devpriv->counter_dev->counters[i]; /* setup and initialize the counter */ - gpct->chip_index = 0; - gpct->counter_index = i; ni_tio_init_counter(gpct); s = &dev->subdevices[NI_GPCT_SUBDEV(i)]; diff --git a/drivers/staging/comedi/drivers/ni_tio.c b/drivers/staging/comedi/drivers/ni_tio.c index ef919b21b7d9..838614ee64d6 100644 --- a/drivers/staging/comedi/drivers/ni_tio.c +++ b/drivers/staging/comedi/drivers/ni_tio.c @@ -818,10 +818,79 @@ static int ni_tio_get_clock_src(struct ni_gpct *counter, return 0; } +static inline void ni_tio_set_gate_raw(struct ni_gpct *counter, + unsigned int gate_source) +{ + ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(counter->counter_index), + GI_GATE_SEL_MASK, GI_GATE_SEL(gate_source)); +} + +static inline void ni_tio_set_gate2_raw(struct ni_gpct *counter, + unsigned int gate_source) +{ + ni_tio_set_bits(counter, NITIO_GATE2_REG(counter->counter_index), + GI_GATE2_SEL_MASK, GI_GATE2_SEL(gate_source)); +} + +/* Set the mode bits for gate. */ +static inline void ni_tio_set_gate_mode(struct ni_gpct *counter, + unsigned int src) +{ + unsigned int mode_bits = 0; + + if (CR_CHAN(src) & NI_GPCT_DISABLED_GATE_SELECT) { + /* + * Allowing bitwise comparison here to allow non-zero raw + * register value to be used for channel when disabling. + */ + mode_bits = GI_GATING_DISABLED; + } else { + if (src & CR_INVERT) + mode_bits |= GI_GATE_POL_INVERT; + if (src & CR_EDGE) + mode_bits |= GI_RISING_EDGE_GATING; + else + mode_bits |= GI_LEVEL_GATING; + } + ni_tio_set_bits(counter, NITIO_MODE_REG(counter->counter_index), + GI_GATE_POL_INVERT | GI_GATING_MODE_MASK, + mode_bits); +} + +/* + * Set the mode bits for gate2. + * + * Previously, the code this function represents did not actually write anything + * to the register. Rather, writing to this register was reserved for the code + * ni ni_tio_set_gate2_raw. + */ +static inline void ni_tio_set_gate2_mode(struct ni_gpct *counter, + unsigned int src) +{ + /* + * The GI_GATE2_MODE bit was previously set in the code that also sets + * the gate2 source. + * We'll set mode bits _after_ source bits now, and thus, this function + * will effectively enable the second gate after all bits are set. + */ + unsigned int mode_bits = GI_GATE2_MODE; + + if (CR_CHAN(src) & NI_GPCT_DISABLED_GATE_SELECT) + /* + * Allowing bitwise comparison here to allow non-zero raw + * register value to be used for channel when disabling. + */ + mode_bits = GI_GATING_DISABLED; + if (src & CR_INVERT) + mode_bits |= GI_GATE2_POL_INVERT; + + ni_tio_set_bits(counter, NITIO_GATE2_REG(counter->counter_index), + GI_GATE2_POL_INVERT | GI_GATE2_MODE, mode_bits); +} + static int ni_660x_set_gate(struct ni_gpct *counter, unsigned int gate_source) { unsigned int chan = CR_CHAN(gate_source); - unsigned int cidx = counter->counter_index; unsigned int gate_sel; unsigned int i; @@ -854,15 +923,13 @@ static int ni_660x_set_gate(struct ni_gpct *counter, unsigned int gate_source) break; return -EINVAL; } - ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx), - GI_GATE_SEL_MASK, GI_GATE_SEL(gate_sel)); + ni_tio_set_gate_raw(counter, 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 int cidx = counter->counter_index; unsigned int gate_sel; unsigned int i; @@ -896,17 +963,13 @@ static int ni_m_set_gate(struct ni_gpct *counter, unsigned int gate_source) break; return -EINVAL; } - ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx), - GI_GATE_SEL_MASK, GI_GATE_SEL(gate_sel)); + ni_tio_set_gate_raw(counter, 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 int cidx = counter->counter_index; unsigned int chan = CR_CHAN(gate_source); - unsigned int gate2_reg = NITIO_GATE2_REG(cidx); unsigned int gate2_sel; unsigned int i; @@ -940,94 +1003,102 @@ static int ni_660x_set_gate2(struct ni_gpct *counter, unsigned int gate_source) 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); - ni_tio_write(counter, counter_dev->regs[gate2_reg], gate2_reg); + ni_tio_set_gate2_raw(counter, gate2_sel); 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 int cidx = counter->counter_index; - unsigned int chan = CR_CHAN(gate_source); - unsigned int gate2_reg = NITIO_GATE2_REG(cidx); - unsigned int 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; + ni_tio_set_gate2_raw(counter, gate_source); + return 0; +} + +int ni_tio_set_gate_src_raw(struct ni_gpct *counter, + unsigned int gate, unsigned int src) +{ + struct ni_gpct_device *counter_dev = counter->counter_dev; + + switch (gate) { + case 0: + /* 1. start by disabling gate */ + ni_tio_set_gate_mode(counter, NI_GPCT_DISABLED_GATE_SELECT); + /* 2. set the requested gate source */ + ni_tio_set_gate_raw(counter, src); + /* 3. reenable & set mode to starts things back up */ + ni_tio_set_gate_mode(counter, src); + break; + case 1: + if (!ni_tio_has_gate2_registers(counter_dev)) + return -EINVAL; + + /* 1. start by disabling gate */ + ni_tio_set_gate2_mode(counter, NI_GPCT_DISABLED_GATE_SELECT); + /* 2. set the requested gate source */ + ni_tio_set_gate2_raw(counter, src); + /* 3. reenable & set mode to starts things back up */ + ni_tio_set_gate2_mode(counter, src); break; + default: + 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); - ni_tio_write(counter, counter_dev->regs[gate2_reg], gate2_reg); return 0; } +EXPORT_SYMBOL_GPL(ni_tio_set_gate_src_raw); int ni_tio_set_gate_src(struct ni_gpct *counter, unsigned int gate, unsigned int src) { struct ni_gpct_device *counter_dev = counter->counter_dev; - unsigned int cidx = counter->counter_index; - unsigned int chan = CR_CHAN(src); - unsigned int gate2_reg = NITIO_GATE2_REG(cidx); - unsigned int mode = 0; + /* + * mask off disable flag. This high bit still passes CR_CHAN. + * Doing this allows one to both set the gate as disabled, but also + * change the route value of the gate. + */ + int chan = CR_CHAN(src) & (~NI_GPCT_DISABLED_GATE_SELECT); + int ret; switch (gate) { 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 (src & CR_INVERT) - mode |= GI_GATE_POL_INVERT; - if (src & 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); + /* 1. start by disabling gate */ + ni_tio_set_gate_mode(counter, NI_GPCT_DISABLED_GATE_SELECT); + /* 2. set the requested gate source */ switch (counter_dev->variant) { case ni_gpct_variant_e_series: case ni_gpct_variant_m_series: - default: - return ni_m_set_gate(counter, src); + ret = ni_m_set_gate(counter, chan); case ni_gpct_variant_660x: - return ni_660x_set_gate(counter, src); + ret = ni_660x_set_gate(counter, chan); + default: + return -EINVAL; } + if (ret) + return ret; + /* 3. reenable & set mode to starts things back up */ + ni_tio_set_gate_mode(counter, src); 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; - ni_tio_write(counter, counter_dev->regs[gate2_reg], - gate2_reg); - return 0; - } - if (src & CR_INVERT) - counter_dev->regs[gate2_reg] |= GI_GATE2_POL_INVERT; - else - counter_dev->regs[gate2_reg] &= ~GI_GATE2_POL_INVERT; + /* 1. start by disabling gate */ + ni_tio_set_gate2_mode(counter, NI_GPCT_DISABLED_GATE_SELECT); + /* 2. set the requested gate source */ switch (counter_dev->variant) { case ni_gpct_variant_m_series: - return ni_m_set_gate2(counter, src); + ret = ni_m_set_gate2(counter, chan); case ni_gpct_variant_660x: - return ni_660x_set_gate2(counter, src); + ret = ni_660x_set_gate2(counter, chan); default: return -EINVAL; } + if (ret) + return ret; + /* 3. reenable & set mode to starts things back up */ + ni_tio_set_gate2_mode(counter, src); break; default: return -EINVAL; @@ -1047,19 +1118,21 @@ static int ni_tio_set_other_src(struct ni_gpct *counter, unsigned int index, return -EINVAL; abz_reg = NITIO_ABZ_REG(cidx); - switch (index) { - case NI_GPCT_SOURCE_ENCODER_A: + + /* allow for new device-global names */ + if (index == NI_GPCT_SOURCE_ENCODER_A || + (index >= NI_CtrA(0) && index <= NI_CtrA(-1))) { shift = 10; - break; - case NI_GPCT_SOURCE_ENCODER_B: + } else if (index == NI_GPCT_SOURCE_ENCODER_B || + (index >= NI_CtrB(0) && index <= NI_CtrB(-1))) { shift = 5; - break; - case NI_GPCT_SOURCE_ENCODER_Z: + } else if (index == NI_GPCT_SOURCE_ENCODER_Z || + (index >= NI_CtrZ(0) && index <= NI_CtrZ(-1))) { shift = 0; - break; - default: + } else { return -EINVAL; } + mask = 0x1f << shift; if (source > 0x1f) source = 0x1f; /* Disable gate */ @@ -1070,6 +1143,39 @@ static int ni_tio_set_other_src(struct ni_gpct *counter, unsigned int index, return 0; } +static int ni_tio_get_other_src(struct ni_gpct *counter, unsigned int index, + unsigned int *source) +{ + struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned int cidx = counter->counter_index; + unsigned int abz_reg, shift, mask; + + if (counter_dev->variant != ni_gpct_variant_m_series) + /* A,B,Z only valid for m-series */ + return -EINVAL; + + abz_reg = NITIO_ABZ_REG(cidx); + + /* allow for new device-global names */ + if (index == NI_GPCT_SOURCE_ENCODER_A || + (index >= NI_CtrA(0) && index <= NI_CtrA(-1))) { + shift = 10; + } else if (index == NI_GPCT_SOURCE_ENCODER_B || + (index >= NI_CtrB(0) && index <= NI_CtrB(-1))) { + shift = 5; + } else if (index == NI_GPCT_SOURCE_ENCODER_Z || + (index >= NI_CtrZ(0) && index <= NI_CtrZ(-1))) { + shift = 0; + } else { + return -EINVAL; + } + + mask = 0x1f; + + *source = (ni_tio_get_soft_copy(counter, abz_reg) >> shift) & mask; + return 0; +} + static int ni_660x_gate_to_generic_gate(unsigned int gate, unsigned int *src) { unsigned int source; @@ -1112,7 +1218,7 @@ static int ni_660x_gate_to_generic_gate(unsigned int gate, unsigned int *src) } *src = source; return 0; -}; +} static int ni_m_gate_to_generic_gate(unsigned int gate, unsigned int *src) { @@ -1165,7 +1271,7 @@ static int ni_m_gate_to_generic_gate(unsigned int gate, unsigned int *src) } *src = source; return 0; -}; +} static int ni_660x_gate2_to_generic_gate(unsigned int gate, unsigned int *src) { @@ -1212,7 +1318,7 @@ static int ni_660x_gate2_to_generic_gate(unsigned int gate, unsigned int *src) } *src = source; return 0; -}; +} static int ni_m_gate2_to_generic_gate(unsigned int gate, unsigned int *src) { @@ -1222,32 +1328,60 @@ static int ni_m_gate2_to_generic_gate(unsigned int gate, unsigned int *src) */ *src = gate; return 0; -}; +} + +static inline unsigned int ni_tio_get_gate_mode(struct ni_gpct *counter) +{ + unsigned int mode = ni_tio_get_soft_copy( + counter, NITIO_MODE_REG(counter->counter_index)); + unsigned int ret = 0; + + if ((mode & GI_GATING_MODE_MASK) == GI_GATING_DISABLED) + ret |= NI_GPCT_DISABLED_GATE_SELECT; + if (mode & GI_GATE_POL_INVERT) + ret |= CR_INVERT; + if ((mode & GI_GATING_MODE_MASK) != GI_LEVEL_GATING) + ret |= CR_EDGE; + + return ret; +} + +static inline unsigned int ni_tio_get_gate2_mode(struct ni_gpct *counter) +{ + unsigned int mode = ni_tio_get_soft_copy( + counter, NITIO_GATE2_REG(counter->counter_index)); + unsigned int ret = 0; + + if (!(mode & GI_GATE2_MODE)) + ret |= NI_GPCT_DISABLED_GATE_SELECT; + if (mode & GI_GATE2_POL_INVERT) + ret |= CR_INVERT; + + return ret; +} + +static inline unsigned int ni_tio_get_gate_val(struct ni_gpct *counter) +{ + return GI_BITS_TO_GATE(ni_tio_get_soft_copy(counter, + NITIO_INPUT_SEL_REG(counter->counter_index))); +} + +static inline unsigned int ni_tio_get_gate2_val(struct ni_gpct *counter) +{ + return GI_BITS_TO_GATE2(ni_tio_get_soft_copy(counter, + NITIO_GATE2_REG(counter->counter_index))); +} static int ni_tio_get_gate_src(struct ni_gpct *counter, unsigned int gate_index, unsigned int *gate_source) { - struct ni_gpct_device *counter_dev = counter->counter_dev; - unsigned int cidx = counter->counter_index; - unsigned int mode; - unsigned int reg; unsigned int gate; int ret; - mode = ni_tio_get_soft_copy(counter, NITIO_MODE_REG(cidx)); - if (((mode & GI_GATING_MODE_MASK) == GI_GATING_DISABLED) || - (gate_index == 1 && - !(counter_dev->regs[NITIO_GATE2_REG(cidx)] & GI_GATE2_MODE))) { - *gate_source = NI_GPCT_DISABLED_GATE_SELECT; - return 0; - } - switch (gate_index) { case 0: - reg = NITIO_INPUT_SEL_REG(cidx); - gate = GI_BITS_TO_GATE(ni_tio_get_soft_copy(counter, reg)); - - switch (counter_dev->variant) { + gate = ni_tio_get_gate_val(counter); + switch (counter->counter_dev->variant) { case ni_gpct_variant_e_series: case ni_gpct_variant_m_series: default: @@ -1259,16 +1393,11 @@ static int ni_tio_get_gate_src(struct ni_gpct *counter, unsigned int gate_index, } if (ret) return ret; - if (mode & GI_GATE_POL_INVERT) - *gate_source |= CR_INVERT; - if ((mode & GI_GATING_MODE_MASK) != GI_LEVEL_GATING) - *gate_source |= CR_EDGE; + *gate_source |= ni_tio_get_gate_mode(counter); break; case 1: - reg = NITIO_GATE2_REG(cidx); - gate = GI_BITS_TO_GATE2(counter_dev->regs[reg]); - - switch (counter_dev->variant) { + gate = ni_tio_get_gate2_val(counter); + switch (counter->counter_dev->variant) { case ni_gpct_variant_e_series: case ni_gpct_variant_m_series: default: @@ -1280,11 +1409,26 @@ static int ni_tio_get_gate_src(struct ni_gpct *counter, unsigned int gate_index, } if (ret) return ret; - if (counter_dev->regs[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; + *gate_source |= ni_tio_get_gate2_mode(counter); + break; + default: + return -EINVAL; + } + return 0; +} + +static int ni_tio_get_gate_src_raw(struct ni_gpct *counter, + unsigned int gate_index, + unsigned int *gate_source) +{ + switch (gate_index) { + case 0: + *gate_source = ni_tio_get_gate_mode(counter) + | ni_tio_get_gate_val(counter); + break; + case 1: + *gate_source = ni_tio_get_gate2_mode(counter) + | ni_tio_get_gate2_val(counter); break; default: return -EINVAL; @@ -1347,6 +1491,107 @@ int ni_tio_insn_config(struct comedi_device *dev, } EXPORT_SYMBOL_GPL(ni_tio_insn_config); +/** + * Retrieves the register value of the current source of the output selector for + * the given destination. + * + * If the terminal for the destination is not already configured as an output, + * this function returns -EINVAL as error. + * + * Return: the register value of the destination output selector; + * -EINVAL if terminal is not configured for output. + */ +int ni_tio_get_routing(struct ni_gpct_device *counter_dev, unsigned int dest) +{ + /* we need to know the actual counter below... */ + int ctr_index = (dest - NI_COUNTER_NAMES_BASE) % NI_MAX_COUNTERS; + struct ni_gpct *counter = &counter_dev->counters[ctr_index]; + int ret = 1; + unsigned int reg; + + if (dest >= NI_CtrA(0) && dest <= NI_CtrZ(-1)) { + ret = ni_tio_get_other_src(counter, dest, ®); + } else if (dest >= NI_CtrGate(0) && dest <= NI_CtrGate(-1)) { + ret = ni_tio_get_gate_src_raw(counter, 0, ®); + } else if (dest >= NI_CtrAux(0) && dest <= NI_CtrAux(-1)) { + ret = ni_tio_get_gate_src_raw(counter, 1, ®); + /* + * This case is not possible through this interface. A user must use + * INSN_CONFIG_SET_CLOCK_SRC instead. + * } else if (dest >= NI_CtrSource(0) && dest <= NI_CtrSource(-1)) { + * ret = ni_tio_set_clock_src(counter, ®, &period_ns); + */ + } + + if (ret) + return -EINVAL; + + return reg; +} +EXPORT_SYMBOL_GPL(ni_tio_get_routing); + +/** + * Sets the register value of the selector MUX for the given destination. + * @counter_dev:Pointer to general counter device. + * @destination:Device-global identifier of route destination. + * @register_value: + * The first several bits of this value should store the desired + * value to write to the register. All other bits are for + * transmitting information that modify the mode of the particular + * destination/gate. These mode bits might include a bitwise or of + * CR_INVERT and CR_EDGE. Note that the calling function should + * have already validated the correctness of this value. + */ +int ni_tio_set_routing(struct ni_gpct_device *counter_dev, unsigned int dest, + unsigned int reg) +{ + /* we need to know the actual counter below... */ + int ctr_index = (dest - NI_COUNTER_NAMES_BASE) % NI_MAX_COUNTERS; + struct ni_gpct *counter = &counter_dev->counters[ctr_index]; + int ret; + + if (dest >= NI_CtrA(0) && dest <= NI_CtrZ(-1)) { + ret = ni_tio_set_other_src(counter, dest, reg); + } else if (dest >= NI_CtrGate(0) && dest <= NI_CtrGate(-1)) { + ret = ni_tio_set_gate_src_raw(counter, 0, reg); + } else if (dest >= NI_CtrAux(0) && dest <= NI_CtrAux(-1)) { + ret = ni_tio_set_gate_src_raw(counter, 1, reg); + /* + * This case is not possible through this interface. A user must use + * INSN_CONFIG_SET_CLOCK_SRC instead. + * } else if (dest >= NI_CtrSource(0) && dest <= NI_CtrSource(-1)) { + * ret = ni_tio_set_clock_src(counter, reg, period_ns); + */ + } else { + return -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL_GPL(ni_tio_set_routing); + +/** + * Sets the given destination MUX to its default value or disable it. + * + * Return: 0 if successful; -EINVAL if terminal is unknown. + */ +int ni_tio_unset_routing(struct ni_gpct_device *counter_dev, unsigned int dest) +{ + if (dest >= NI_GATES_NAMES_BASE && dest <= NI_GATES_NAMES_MAX) + /* Disable gate (via mode bits) and set to default 0-value */ + return ni_tio_set_routing(counter_dev, dest, + NI_GPCT_DISABLED_GATE_SELECT); + /* + * This case is not possible through this interface. A user must use + * INSN_CONFIG_SET_CLOCK_SRC instead. + * if (dest >= NI_CtrSource(0) && dest <= NI_CtrSource(-1)) + * return ni_tio_set_clock_src(counter, reg, period_ns); + */ + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(ni_tio_unset_routing); + static unsigned int ni_tio_read_sw_save_reg(struct comedi_device *dev, struct comedi_subdevice *s) { @@ -1504,13 +1749,15 @@ ni_gpct_device_construct(struct comedi_device *dev, unsigned int (*read)(struct ni_gpct *counter, enum ni_gpct_register reg), enum ni_gpct_variant variant, - unsigned int num_counters) + unsigned int num_counters, + unsigned int counters_per_chip, + const struct ni_route_tables *routing_tables) { struct ni_gpct_device *counter_dev; struct ni_gpct *counter; unsigned int i; - if (num_counters == 0) + if (num_counters == 0 || counters_per_chip == 0) return NULL; counter_dev = kzalloc(sizeof(*counter_dev), GFP_KERNEL); @@ -1521,6 +1768,7 @@ ni_gpct_device_construct(struct comedi_device *dev, counter_dev->write = write; counter_dev->read = read; counter_dev->variant = variant; + counter_dev->routing_tables = routing_tables; spin_lock_init(&counter_dev->regs_lock); @@ -1534,9 +1782,12 @@ ni_gpct_device_construct(struct comedi_device *dev, for (i = 0; i < num_counters; ++i) { counter = &counter_dev->counters[i]; counter->counter_dev = counter_dev; + counter->chip_index = i / counters_per_chip; + counter->counter_index = i % counters_per_chip; spin_lock_init(&counter->lock); } counter_dev->num_counters = num_counters; + counter_dev->counters_per_chip = counters_per_chip; return counter_dev; } diff --git a/drivers/staging/comedi/drivers/ni_tio.h b/drivers/staging/comedi/drivers/ni_tio.h index 23221cead8ca..340d63c74467 100644 --- a/drivers/staging/comedi/drivers/ni_tio.h +++ b/drivers/staging/comedi/drivers/ni_tio.h @@ -107,8 +107,10 @@ struct ni_gpct_device { enum ni_gpct_variant variant; struct ni_gpct *counters; unsigned int num_counters; + unsigned int counters_per_chip; unsigned int regs[NITIO_NUM_REGS]; spinlock_t regs_lock; /* protects 'regs' */ + const struct ni_route_tables *routing_tables; /* link to routes */ }; struct ni_gpct_device * @@ -119,7 +121,9 @@ ni_gpct_device_construct(struct comedi_device *dev, unsigned int (*read)(struct ni_gpct *counter, enum ni_gpct_register), enum ni_gpct_variant, - unsigned int num_counters); + unsigned int num_counters, + unsigned int counters_per_chip, + const struct ni_route_tables *routing_tables); void ni_gpct_device_destroy(struct ni_gpct_device *counter_dev); void ni_tio_init_counter(struct ni_gpct *counter); int ni_tio_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, @@ -138,4 +142,40 @@ void ni_tio_set_mite_channel(struct ni_gpct *counter, struct mite_channel *mite_chan); void ni_tio_acknowledge(struct ni_gpct *counter); +/* + * Retrieves the register value of the current source of the output selector for + * the given destination. + * + * If the terminal for the destination is not already configured as an output, + * this function returns -EINVAL as error. + * + * Return: the register value of the destination output selector; + * -EINVAL if terminal is not configured for output. + */ +int ni_tio_get_routing(struct ni_gpct_device *counter_dev, + unsigned int destination); + +/* + * Sets the register value of the selector MUX for the given destination. + * @counter_dev:Pointer to general counter device. + * @destination:Device-global identifier of route destination. + * @register_value: + * The first several bits of this value should store the desired + * value to write to the register. All other bits are for + * transmitting information that modify the mode of the particular + * destination/gate. These mode bits might include a bitwise or of + * CR_INVERT and CR_EDGE. Note that the calling function should + * have already validated the correctness of this value. + */ +int ni_tio_set_routing(struct ni_gpct_device *counter_dev, + unsigned int destination, unsigned int register_value); + +/* + * Sets the given destination MUX to its default value or disable it. + * + * Return: 0 if successful; -EINVAL if terminal is unknown. + */ +int ni_tio_unset_routing(struct ni_gpct_device *counter_dev, + unsigned int destination); + #endif /* _COMEDI_NI_TIO_H */ diff --git a/drivers/staging/comedi/drivers/ni_tio_internal.h b/drivers/staging/comedi/drivers/ni_tio_internal.h index f4d99d78208a..652a28990132 100644 --- a/drivers/staging/comedi/drivers/ni_tio_internal.h +++ b/drivers/staging/comedi/drivers/ni_tio_internal.h @@ -170,5 +170,7 @@ unsigned int ni_tio_get_soft_copy(const struct ni_gpct *counter, int ni_tio_arm(struct ni_gpct *counter, bool arm, unsigned int start_trigger); int ni_tio_set_gate_src(struct ni_gpct *counter, unsigned int gate, unsigned int src); +int ni_tio_set_gate_src_raw(struct ni_gpct *counter, unsigned int gate, + unsigned int src); #endif /* _COMEDI_NI_TIO_INTERNAL_H */ diff --git a/drivers/staging/comedi/drivers/ni_tiocmd.c b/drivers/staging/comedi/drivers/ni_tiocmd.c index 050bee0b9515..2a9f7e9821a7 100644 --- a/drivers/staging/comedi/drivers/ni_tiocmd.c +++ b/drivers/staging/comedi/drivers/ni_tiocmd.c @@ -33,6 +33,7 @@ #include <linux/module.h> #include "ni_tio_internal.h" #include "mite.h" +#include "ni_routes.h" static void ni_tio_configure_dma(struct ni_gpct *counter, bool enable, bool read) @@ -100,6 +101,8 @@ static int ni_tio_input_cmd(struct comedi_subdevice *s) { struct ni_gpct *counter = s->private; struct ni_gpct_device *counter_dev = counter->counter_dev; + const struct ni_route_tables *routing_tables = + counter_dev->routing_tables; unsigned int cidx = counter->counter_index; struct comedi_async *async = s->async; struct comedi_cmd *cmd = &async->cmd; @@ -128,8 +131,19 @@ static int ni_tio_input_cmd(struct comedi_subdevice *s) if (cmd->start_src == TRIG_NOW) ret = ni_tio_arm(counter, true, NI_GPCT_ARM_IMMEDIATE); - else if (cmd->start_src == TRIG_EXT) - ret = ni_tio_arm(counter, true, cmd->start_arg); + else if (cmd->start_src == TRIG_EXT) { + int reg = CR_CHAN(cmd->start_arg); + + if (reg >= NI_NAMES_BASE) { + /* using a device-global name. lookup reg */ + reg = ni_get_reg_value(reg, + NI_CtrArmStartTrigger(cidx), + routing_tables); + /* mark this as a raw register value */ + reg |= NI_GPCT_HW_ARM; + } + ret = ni_tio_arm(counter, true, reg); + } } return ret; } @@ -148,6 +162,8 @@ static int ni_tio_cmd_setup(struct comedi_subdevice *s) struct comedi_cmd *cmd = &s->async->cmd; struct ni_gpct *counter = s->private; unsigned int cidx = counter->counter_index; + const struct ni_route_tables *routing_tables = + counter->counter_dev->routing_tables; int set_gate_source = 0; unsigned int gate_source; int retval = 0; @@ -159,8 +175,24 @@ static int ni_tio_cmd_setup(struct comedi_subdevice *s) set_gate_source = 1; gate_source = cmd->convert_arg; } - if (set_gate_source) - retval = ni_tio_set_gate_src(counter, 0, gate_source); + if (set_gate_source) { + if (CR_CHAN(gate_source) >= NI_NAMES_BASE) { + /* Lookup and use the real register values */ + int reg = ni_get_reg_value(CR_CHAN(gate_source), + NI_CtrGate(cidx), + routing_tables); + if (reg < 0) + return -EINVAL; + retval = ni_tio_set_gate_src_raw(counter, 0, reg); + } else { + /* + * This function must be used separately since it does + * not expect real register values and attempts to + * convert these to real register values. + */ + retval = ni_tio_set_gate_src(counter, 0, gate_source); + } + } if (cmd->flags & CMDF_WAKE_EOS) { ni_tio_set_bits(counter, NITIO_INT_ENA_REG(cidx), GI_GATE_INTERRUPT_ENABLE(cidx), @@ -203,6 +235,9 @@ int ni_tio_cmdtest(struct comedi_device *dev, struct comedi_cmd *cmd) { struct ni_gpct *counter = s->private; + unsigned int cidx = counter->counter_index; + const struct ni_route_tables *routing_tables = + counter->counter_dev->routing_tables; int err = 0; unsigned int sources; @@ -247,14 +282,37 @@ int ni_tio_cmdtest(struct comedi_device *dev, break; case TRIG_EXT: /* start_arg is the start_trigger passed to ni_tio_arm() */ + /* + * This should be done, but we don't yet know the actual + * register values. These should be tested and then documented + * in the ni_route_values/ni_*.csv files, with indication of + * who/when/which/how these these were tested. + * When at least a e/m/660x series have been tested, this code + * should be uncommented: + * + * err |= ni_check_trigger_arg(CR_CHAN(cmd->start_arg), + * NI_CtrArmStartTrigger(cidx), + * routing_tables); + */ break; } + /* + * It seems that convention is to allow either scan_begin_arg or + * convert_arg to specify the Gate source, with scan_begin_arg taking + * precedence. + */ if (cmd->scan_begin_src != TRIG_EXT) err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); + else + err |= ni_check_trigger_arg(CR_CHAN(cmd->scan_begin_arg), + NI_CtrGate(cidx), routing_tables); if (cmd->convert_src != TRIG_EXT) err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); + else + err |= ni_check_trigger_arg(CR_CHAN(cmd->convert_arg), + NI_CtrGate(cidx), routing_tables); err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); |