Commit fa86c007 authored by Spencer E. Olson's avatar Spencer E. Olson Committed by Greg Kroah-Hartman
Browse files

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: default avatarSpencer E. Olson <olsonse@umich.edu>
Reviewed-by: default avatarIan Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a0c5e846
Loading
Loading
Loading
Loading
+265 −0
Original line number Diff line number Diff line
@@ -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;