staging: comedi: ni_660x: add device-global routing

Provides the device-global routing interface for ni_660x devices.  Using
the device-global names in comedi_cmd structures for commands was already
supported through the ni_tio module.

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:12 -06:00 committed by Greg Kroah-Hartman
parent a0c5e84600
commit fa86c00799
1 changed files with 265 additions and 0 deletions

View File

@ -568,6 +568,10 @@ static void ni_660x_select_pfi_output(struct comedi_device *dev,
unsigned int idle_chip = 0;
unsigned int bits;
if (chan >= NI_PFI(0))
/* allow new and old names of pfi channels to work. */
chan -= NI_PFI(0);
if (board->n_chips > 1) {
if (out_sel == NI_660X_PFI_OUTPUT_COUNTER &&
chan >= 8 && chan <= 23) {
@ -603,6 +607,10 @@ static void ni_660x_set_pfi_direction(struct comedi_device *dev,
struct ni_660x_private *devpriv = dev->private;
u64 bit;
if (chan >= NI_PFI(0))
/* allow new and old names of pfi channels to work. */
chan -= NI_PFI(0);
bit = 1ULL << chan;
if (direction == COMEDI_OUTPUT) {
@ -622,6 +630,10 @@ static unsigned int ni_660x_get_pfi_direction(struct comedi_device *dev,
struct ni_660x_private *devpriv = dev->private;
u64 bit;
if (chan >= NI_PFI(0))
/* allow new and old names of pfi channels to work. */
chan -= NI_PFI(0);
bit = 1ULL << chan;
return (devpriv->io_dir & bit) ? COMEDI_OUTPUT : COMEDI_INPUT;
@ -632,6 +644,10 @@ static int ni_660x_set_pfi_routing(struct comedi_device *dev,
{
struct ni_660x_private *devpriv = dev->private;
if (chan >= NI_PFI(0))
/* allow new and old names of pfi channels to work. */
chan -= NI_PFI(0);
switch (source) {
case NI_660X_PFI_OUTPUT_COUNTER:
if (chan < 8)
@ -654,6 +670,10 @@ static int ni_660x_get_pfi_routing(struct comedi_device *dev, unsigned int chan)
{
struct ni_660x_private *devpriv = dev->private;
if (chan >= NI_PFI(0))
/* allow new and old names of pfi channels to work. */
chan -= NI_PFI(0);
return devpriv->io_cfg[chan];
}
@ -662,6 +682,10 @@ static void ni_660x_set_pfi_filter(struct comedi_device *dev,
{
unsigned int val;
if (chan >= NI_PFI(0))
/* allow new and old names of pfi channels to work. */
chan -= NI_PFI(0);
val = ni_660x_read(dev, 0, NI660X_IO_CFG(chan));
val &= ~NI660X_IO_CFG_IN_SEL_MASK(chan);
val |= NI660X_IO_CFG_IN_SEL(chan, value);
@ -710,6 +734,240 @@ static int ni_660x_dio_insn_config(struct comedi_device *dev,
return insn->n;
}
static unsigned int _ni_get_valid_routes(struct comedi_device *dev,
unsigned int n_pairs,
unsigned int *pair_data)
{
struct ni_660x_private *devpriv = dev->private;
return ni_get_valid_routes(&devpriv->routing_tables, n_pairs,
pair_data);
}
/*
* Retrieves 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.
*/
static inline int get_output_select_source(int dest, struct comedi_device *dev)
{
struct ni_660x_private *devpriv = dev->private;
int reg = -1;
if (channel_is_pfi(dest)) {
if (ni_660x_get_pfi_direction(dev, dest) == COMEDI_OUTPUT)
reg = ni_660x_get_pfi_routing(dev, dest);
} else if (channel_is_rtsi(dest)) {
dev_dbg(dev->class_dev,
"%s: unhandled rtsi destination (%d) queried\n",
__func__, dest);
/*
* The following can be enabled when RTSI routing info is
* determined (not currently documented):
* if (ni_get_rtsi_direction(dev, dest) == COMEDI_OUTPUT) {
* reg = ni_get_rtsi_routing(dev, dest);
* if (reg == NI_RTSI_OUTPUT_RGOUT0) {
* dest = NI_RGOUT0; ** prepare for lookup below **
* reg = get_rgout0_reg(dev);
* } else if (reg >= NI_RTSI_OUTPUT_RTSI_BRD(0) &&
* reg <= NI_RTSI_OUTPUT_RTSI_BRD(3)) {
* const int i = reg - NI_RTSI_OUTPUT_RTSI_BRD(0);
* dest = NI_RTSI_BRD(i); ** prepare for lookup **
* reg = get_ith_rtsi_brd_reg(i, dev);
* }
* }
*/
} else if (channel_is_ctr(dest)) {
reg = ni_tio_get_routing(devpriv->counter_dev, dest);
} else {
dev_dbg(dev->class_dev,
"%s: unhandled destination (%d) queried\n",
__func__, dest);
}
if (reg >= 0)
return ni_find_route_source(CR_CHAN(reg), dest,
&devpriv->routing_tables);
return -EINVAL;
}
/*
* Test a route:
*
* Return: -1 if not connectible;
* 0 if connectible and not connected;
* 1 if connectible and connected.
*/
static inline int test_route(unsigned int src, unsigned int dest,
struct comedi_device *dev)
{
struct ni_660x_private *devpriv = dev->private;
s8 reg = ni_route_to_register(CR_CHAN(src), dest,
&devpriv->routing_tables);
if (reg < 0)
return -1;
if (get_output_select_source(dest, dev) != CR_CHAN(src))
return 0;
return 1;
}
/* Connect the actual route. */
static inline int connect_route(unsigned int src, unsigned int dest,
struct comedi_device *dev)
{
struct ni_660x_private *devpriv = dev->private;
s8 reg = ni_route_to_register(CR_CHAN(src), dest,
&devpriv->routing_tables);
s8 current_src;
if (reg < 0)
/* route is not valid */
return -EINVAL;
current_src = get_output_select_source(dest, dev);
if (current_src == CR_CHAN(src))
return -EALREADY;
if (current_src >= 0)
/* destination mux is already busy. complain, don't overwrite */
return -EBUSY;
/* The route is valid and available. Now connect... */
if (channel_is_pfi(CR_CHAN(dest))) {
/*
* set routing and then direction so that the output does not
* first get generated with the wrong pin
*/
ni_660x_set_pfi_routing(dev, dest, reg);
ni_660x_set_pfi_direction(dev, dest, COMEDI_OUTPUT);
} else if (channel_is_rtsi(CR_CHAN(dest))) {
dev_dbg(dev->class_dev, "%s: unhandled rtsi destination (%d)\n",
__func__, dest);
return -EINVAL;
/*
* The following can be enabled when RTSI routing info is
* determined (not currently documented):
* if (reg == NI_RTSI_OUTPUT_RGOUT0) {
* int ret = incr_rgout0_src_use(src, dev);
* if (ret < 0)
* return ret;
* } else if (ni_rtsi_route_requires_mux(reg)) {
* ** Attempt to allocate and route (src->brd) **
* int brd = incr_rtsi_brd_src_use(src, dev);
* if (brd < 0)
* return brd;
* ** Now lookup the register value for (brd->dest) **
* reg = ni_lookup_route_register(brd, CR_CHAN(dest),
* &devpriv->routing_tables);
* }
* ni_set_rtsi_direction(dev, dest, COMEDI_OUTPUT);
* ni_set_rtsi_routing(dev, dest, reg);
*/
} else if (channel_is_ctr(CR_CHAN(dest))) {
/*
* we are adding back the channel modifier info to set
* invert/edge info passed by the user
*/
ni_tio_set_routing(devpriv->counter_dev, dest,
reg | (src & ~CR_CHAN(-1)));
} else {
return -EINVAL;
}
return 0;
}
static inline int disconnect_route(unsigned int src, unsigned int dest,
struct comedi_device *dev)
{
struct ni_660x_private *devpriv = dev->private;
s8 reg = ni_route_to_register(CR_CHAN(src), CR_CHAN(dest),
&devpriv->routing_tables);
if (reg < 0)
/* route is not valid */
return -EINVAL;
if (get_output_select_source(dest, dev) != CR_CHAN(src))
/* cannot disconnect something not connected */
return -EINVAL;
/* The route is valid and is connected. Now disconnect... */
if (channel_is_pfi(CR_CHAN(dest))) {
unsigned int source = ((CR_CHAN(dest) - NI_PFI(0)) < 8)
? NI_660X_PFI_OUTPUT_DIO
: NI_660X_PFI_OUTPUT_COUNTER;
/* set the pfi to high impedance, and disconnect */
ni_660x_set_pfi_direction(dev, dest, COMEDI_INPUT);
ni_660x_set_pfi_routing(dev, dest, source);
} else if (channel_is_rtsi(CR_CHAN(dest))) {
dev_dbg(dev->class_dev, "%s: unhandled rtsi destination (%d)\n",
__func__, dest);
return -EINVAL;
/*
* The following can be enabled when RTSI routing info is
* determined (not currently documented):
* if (reg == NI_RTSI_OUTPUT_RGOUT0) {
* int ret = decr_rgout0_src_use(src, dev);
* if (ret < 0)
* return ret;
* } else if (ni_rtsi_route_requires_mux(reg)) {
* ** find which RTSI_BRD line is source for rtsi pin **
* int brd = ni_find_route_source(
* ni_get_rtsi_routing(dev, dest), CR_CHAN(dest),
* &devpriv->routing_tables);
* if (brd < 0)
* return brd;
* ** decrement/disconnect RTSI_BRD line from source **
* decr_rtsi_brd_src_use(src, brd, dev);
* }
* ** set rtsi output selector to default state **
* reg = default_rtsi_routing[CR_CHAN(dest) - TRIGGER_LINE(0)];
* ni_set_rtsi_direction(dev, dest, COMEDI_INPUT);
* ni_set_rtsi_routing(dev, dest, reg);
*/
} else if (channel_is_ctr(CR_CHAN(dest))) {
ni_tio_unset_routing(devpriv->counter_dev, dest);
} else {
return -EINVAL;
}
return 0;
}
static int ni_global_insn_config(struct comedi_device *dev,
struct comedi_insn *insn,
unsigned int *data)
{
switch (data[0]) {
case INSN_DEVICE_CONFIG_TEST_ROUTE:
data[0] = test_route(data[1], data[2], dev);
return 2;
case INSN_DEVICE_CONFIG_CONNECT_ROUTE:
return connect_route(data[1], data[2], dev);
case INSN_DEVICE_CONFIG_DISCONNECT_ROUTE:
return disconnect_route(data[1], data[2], dev);
/*
* This case is already handled one level up.
* case INSN_DEVICE_CONFIG_GET_ROUTES:
*/
default:
return -EINVAL;
}
return 1;
}
static void ni_660x_init_tio_chips(struct comedi_device *dev,
unsigned int n_chips)
{
@ -784,6 +1042,13 @@ static int ni_660x_auto_attach(struct comedi_device *dev,
__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);
} else {
/*
* only(?) assign insn_device_config if we have global names for
* this device.
*/
dev->insn_device_config = ni_global_insn_config;
dev->get_valid_routes = _ni_get_valid_routes;
}
n_counters = board->n_chips * NI660X_COUNTERS_PER_CHIP;