staging: comedi: tio: implement global tio/ctr routing

Adds ability to use device-global names in command args, in particular
cmd->start_arg (for NI_CtrArmStartTrigger), and cmd->scan_begin_arg or
cmd->convert_arg (either is used to specify NI_CtrGate, with preference
given to cmd->scan_begin_arg, if it is set).

The actual arguments of cmd->start_arg are not fully checked against known
register values for the particular devices because these are not documented
or currently known.  This follows the precedence of prior versions of the
tio driver.  Should these become known, they should be annotated in the
route_values tables and the set of lines in ni_tio_cmdtest should be
uncommented to allow the tests to be made.

This patch also adds interface functions that allow routes for particular
counter route destinations to be made/queried/unmade.  This allows overseer
modules to implement test_route, connect_route, and disconnect_route.  As a
part of these changes, various functions were cleaned up and clarified.

These new interface functions allow direct writing/reading of register
values.  This is an example of exactly what the new device-global access
was intended to solve:  the old interface was not consistent with other
portions of the ni_* drivers--it did not allow full register values to be
given for various MUXes.  Instead, the old interface _did_ abstract away
some of the actual hardware from the underlying devices, but it was not
consistent with any other NI hardware.  Allowing the device-global
identifiers to be used, the new patch provides for consistency across all
ni_* drivers.  One final note:  these changes provide for backwards
compatibility by allowing the older values to still be used in through the
pre-existing kernel interfaces--though not in the new device-global
test/dis/connect/route interfaces.

Signed-off-by: Spencer E. Olson <olsonse@umich.edu>
Reviewed-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Spencer E. Olson 2018-10-03 14:56:08 -06:00 committed by Greg Kroah-Hartman
parent 02d1c6e827
commit 347e244884
6 changed files with 485 additions and 124 deletions

View File

@ -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;

View File

@ -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)];

View File

@ -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;
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);
ni_tio_write(counter, counter_dev->regs[gate2_reg], gate2_reg);
ni_tio_set_gate2_raw(counter, gate_source);
return 0;
}
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)
{
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;
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);
switch (counter_dev->variant) {
case ni_gpct_variant_e_series:
case ni_gpct_variant_m_series:
default:
return ni_m_set_gate(counter, src);
case ni_gpct_variant_660x:
return ni_660x_set_gate(counter, src);
}
/* 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;
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 */
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;
}
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;
/*
* 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:
/* 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:
return ni_m_set_gate2(counter, src);
ret = ni_m_set_gate(counter, chan);
case ni_gpct_variant_660x:
return ni_660x_set_gate2(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;
/* 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:
ret = ni_m_set_gate2(counter, chan);
case ni_gpct_variant_660x:
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, &reg);
} else if (dest >= NI_CtrGate(0) && dest <= NI_CtrGate(-1)) {
ret = ni_tio_get_gate_src_raw(counter, 0, &reg);
} else if (dest >= NI_CtrAux(0) && dest <= NI_CtrAux(-1)) {
ret = ni_tio_get_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);
*/
}
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;
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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);