static int ni_660x_auto_attach()

in drivers/ni_660x.c [996:1199]


static int ni_660x_auto_attach(struct comedi_device *dev,
			       unsigned long context)
{
	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
	const struct ni_660x_board *board = NULL;
	struct ni_660x_private *devpriv;
	struct comedi_subdevice *s;
	struct ni_gpct_device *gpct_dev;
	unsigned int n_counters;
	int subdev;
	int ret;
	unsigned int i;
	unsigned int global_interrupt_config_bits;

	if (context < ARRAY_SIZE(ni_660x_boards))
		board = &ni_660x_boards[context];
	if (!board)
		return -ENODEV;
	dev->board_ptr = board;
	dev->board_name = board->name;

	ret = comedi_pci_enable(dev);
	if (ret)
		return ret;

	ret = ni_660x_allocate_private(dev);
	if (ret < 0)
		return ret;
	devpriv = dev->private;

	devpriv->mite = mite_attach(dev, true);		/* use win1 */
	if (!devpriv->mite)
		return -ENOMEM;

	ret = ni_660x_alloc_mite_rings(dev);
	if (ret < 0)
		return ret;

	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, NULL,
				    &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);
	} 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;
	gpct_dev = ni_gpct_device_construct(dev,
					    ni_660x_gpct_write,
					    ni_660x_gpct_read,
					    ni_gpct_variant_660x,
					    n_counters,
					    NI660X_COUNTERS_PER_CHIP,
					    &devpriv->routing_tables);
	if (!gpct_dev)
		return -ENOMEM;
	devpriv->counter_dev = gpct_dev;

	ret = comedi_alloc_subdevices(dev, 2 + NI660X_MAX_COUNTERS);
	if (ret)
		return ret;

	subdev = 0;

	s = &dev->subdevices[subdev++];
	/* Old GENERAL-PURPOSE COUNTER/TIME (GPCT) subdevice, no longer used */
	s->type = COMEDI_SUBD_UNUSED;

	/*
	 * Digital I/O subdevice
	 *
	 * There are 40 channels but only the first 32 can be digital I/Os.
	 * The last 8 are dedicated to counters 0 and 1.
	 *
	 * Counter 0-3 signals are from the first TIO chip.
	 * Counter 4-7 signals are from the second TIO chip.
	 *
	 * Comedi	External
	 * PFI Chan	DIO Chan        Counter Signal
	 * -------	--------	--------------
	 *     0	    0
	 *     1	    1
	 *     2	    2
	 *     3	    3
	 *     4	    4
	 *     5	    5
	 *     6	    6
	 *     7	    7
	 *     8	    8		CTR 7 OUT
	 *     9	    9		CTR 7 AUX
	 *    10	   10		CTR 7 GATE
	 *    11	   11		CTR 7 SOURCE
	 *    12	   12		CTR 6 OUT
	 *    13	   13		CTR 6 AUX
	 *    14	   14		CTR 6 GATE
	 *    15	   15		CTR 6 SOURCE
	 *    16	   16		CTR 5 OUT
	 *    17	   17		CTR 5 AUX
	 *    18	   18		CTR 5 GATE
	 *    19	   19		CTR 5 SOURCE
	 *    20	   20		CTR 4 OUT
	 *    21	   21		CTR 4 AUX
	 *    22	   22		CTR 4 GATE
	 *    23	   23		CTR 4 SOURCE
	 *    24	   24		CTR 3 OUT
	 *    25	   25		CTR 3 AUX
	 *    26	   26		CTR 3 GATE
	 *    27	   27		CTR 3 SOURCE
	 *    28	   28		CTR 2 OUT
	 *    29	   29		CTR 2 AUX
	 *    30	   30		CTR 2 GATE
	 *    31	   31		CTR 2 SOURCE
	 *    32			CTR 1 OUT
	 *    33			CTR 1 AUX
	 *    34			CTR 1 GATE
	 *    35			CTR 1 SOURCE
	 *    36			CTR 0 OUT
	 *    37			CTR 0 AUX
	 *    38			CTR 0 GATE
	 *    39			CTR 0 SOURCE
	 */
	s = &dev->subdevices[subdev++];
	s->type		= COMEDI_SUBD_DIO;
	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
	s->n_chan	= NI660X_NUM_PFI_CHANNELS;
	s->maxdata	= 1;
	s->range_table	= &range_digital;
	s->insn_bits	= ni_660x_dio_insn_bits;
	s->insn_config	= ni_660x_dio_insn_config;

	 /*
	  * Default the DIO channels as:
	  *   chan 0-7:  DIO inputs
	  *   chan 8-39: counter signal inputs
	  */
	for (i = 0; i < s->n_chan; ++i) {
		unsigned int source = (i < 8) ? NI_660X_PFI_OUTPUT_DIO
					      : NI_660X_PFI_OUTPUT_COUNTER;

		ni_660x_set_pfi_routing(dev, i, source);
		ni_660x_set_pfi_direction(dev, i, COMEDI_INPUT);/* high-z */
	}

	/* Counter subdevices (4 NI TIO General Purpose Counters per chip) */
	for (i = 0; i < NI660X_MAX_COUNTERS; ++i) {
		s = &dev->subdevices[subdev++];
		if (i < n_counters) {
			struct ni_gpct *counter = &gpct_dev->counters[i];

			s->type		= COMEDI_SUBD_COUNTER;
			s->subdev_flags	= SDF_READABLE | SDF_WRITABLE |
					  SDF_LSAMPL | SDF_CMD_READ;
			s->n_chan	= 3;
			s->maxdata	= 0xffffffff;
			s->insn_read	= ni_tio_insn_read;
			s->insn_write	= ni_tio_insn_write;
			s->insn_config	= ni_tio_insn_config;
			s->len_chanlist	= 1;
			s->do_cmd	= ni_660x_cmd;
			s->do_cmdtest	= ni_tio_cmdtest;
			s->cancel	= ni_660x_cancel;
			s->poll		= ni_660x_input_poll;
			s->buf_change	= ni_660x_buf_change;
			s->async_dma_dir = DMA_BIDIRECTIONAL;
			s->private	= counter;

			ni_tio_init_counter(counter);
		} else {
			s->type		= COMEDI_SUBD_UNUSED;
		}
	}

	/*
	 * To be safe, set counterswap bits on tio chips after all the counter
	 * outputs have been set to high impedance mode.
	 */
	for (i = 0; i < board->n_chips; ++i)
		set_tio_counterswap(dev, i);

	ret = request_irq(pcidev->irq, ni_660x_interrupt, IRQF_SHARED,
			  dev->board_name, dev);
	if (ret < 0) {
		dev_warn(dev->class_dev, " irq not available\n");
		return ret;
	}
	dev->irq = pcidev->irq;
	global_interrupt_config_bits = NI660X_GLOBAL_INT_GLOBAL;
	if (board->n_chips > 1)
		global_interrupt_config_bits |= NI660X_GLOBAL_INT_CASCADE;
	ni_660x_write(dev, 0, global_interrupt_config_bits,
		      NI660X_GLOBAL_INT_CFG);

	return 0;
}