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