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:
parent
a0c5e84600
commit
fa86c00799
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user