static int cb_pcidas_auto_attach()

in drivers/cb_pcidas.c [1246:1446]


static int cb_pcidas_auto_attach(struct comedi_device *dev,
				 unsigned long context)
{
	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
	const struct cb_pcidas_board *board = NULL;
	struct cb_pcidas_private *devpriv;
	struct comedi_subdevice *s;
	int i;
	int ret;

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

	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
	if (!devpriv)
		return -ENOMEM;

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

	devpriv->amcc = pci_resource_start(pcidev, 0);
	devpriv->pcibar1 = pci_resource_start(pcidev, 1);
	devpriv->pcibar2 = pci_resource_start(pcidev, 2);
	dev->iobase = pci_resource_start(pcidev, 3);
	if (board->has_ao)
		devpriv->pcibar4 = pci_resource_start(pcidev, 4);

	/*  disable and clear interrupts on amcc s5933 */
	outl(INTCSR_INBOX_INTR_STATUS,
	     devpriv->amcc + AMCC_OP_REG_INTCSR);

	ret = request_irq(pcidev->irq, cb_pcidas_interrupt, IRQF_SHARED,
			  "cb_pcidas", dev);
	if (ret) {
		dev_dbg(dev->class_dev, "unable to allocate irq %d\n",
			pcidev->irq);
		return ret;
	}
	dev->irq = pcidev->irq;

	dev->pacer = comedi_8254_init(dev->iobase + PCIDAS_AI_8254_BASE,
				      I8254_OSC_BASE_10MHZ, I8254_IO8, 0);
	if (!dev->pacer)
		return -ENOMEM;

	devpriv->ao_pacer = comedi_8254_init(dev->iobase + PCIDAS_AO_8254_BASE,
					     I8254_OSC_BASE_10MHZ,
					     I8254_IO8, 0);
	if (!devpriv->ao_pacer)
		return -ENOMEM;

	ret = comedi_alloc_subdevices(dev, 7);
	if (ret)
		return ret;

	/* Analog Input subdevice */
	s = &dev->subdevices[0];
	s->type		= COMEDI_SUBD_AI;
	s->subdev_flags	= SDF_READABLE | SDF_GROUND | SDF_DIFF;
	s->n_chan	= 16;
	s->maxdata	= board->is_16bit ? 0xffff : 0x0fff;
	s->range_table	= board->use_alt_range ? &cb_pcidas_alt_ranges
					       : &cb_pcidas_ranges;
	s->insn_read	= cb_pcidas_ai_insn_read;
	s->insn_config	= cb_pcidas_ai_insn_config;
	if (dev->irq) {
		dev->read_subdev = s;
		s->subdev_flags	|= SDF_CMD_READ;
		s->len_chanlist	= s->n_chan;
		s->do_cmd	= cb_pcidas_ai_cmd;
		s->do_cmdtest	= cb_pcidas_ai_cmdtest;
		s->cancel	= cb_pcidas_ai_cancel;
	}

	/* Analog Output subdevice */
	s = &dev->subdevices[1];
	if (board->has_ao) {
		s->type		= COMEDI_SUBD_AO;
		s->subdev_flags	= SDF_WRITABLE | SDF_GROUND;
		s->n_chan	= 2;
		s->maxdata	= board->is_16bit ? 0xffff : 0x0fff;
		s->range_table	= &cb_pcidas_ao_ranges;
		s->insn_write	= (board->has_ao_fifo)
					? cb_pcidas_ao_fifo_insn_write
					: cb_pcidas_ao_nofifo_insn_write;

		ret = comedi_alloc_subdev_readback(s);
		if (ret)
			return ret;

		if (dev->irq && board->has_ao_fifo) {
			dev->write_subdev = s;
			s->subdev_flags	|= SDF_CMD_WRITE;
			s->len_chanlist	= s->n_chan;
			s->do_cmdtest	= cb_pcidas_ao_cmdtest;
			s->do_cmd	= cb_pcidas_ao_cmd;
			s->cancel	= cb_pcidas_ao_cancel;
		}
	} else {
		s->type		= COMEDI_SUBD_UNUSED;
	}

	/* 8255 */
	s = &dev->subdevices[2];
	ret = subdev_8255_init(dev, s, NULL, PCIDAS_8255_BASE);
	if (ret)
		return ret;

	/* Memory subdevice - serial EEPROM */
	s = &dev->subdevices[3];
	s->type		= COMEDI_SUBD_MEMORY;
	s->subdev_flags	= SDF_READABLE | SDF_INTERNAL;
	s->n_chan	= 256;
	s->maxdata	= 0xff;
	s->insn_read	= cb_pcidas_eeprom_insn_read;

	/* Calibration subdevice - 8800 caldac */
	s = &dev->subdevices[4];
	s->type		= COMEDI_SUBD_CALIB;
	s->subdev_flags	= SDF_WRITABLE | SDF_INTERNAL;
	s->n_chan	= 8;
	s->maxdata	= 0xff;
	s->insn_write	= cb_pcidas_caldac_insn_write;

	ret = comedi_alloc_subdev_readback(s);
	if (ret)
		return ret;

	for (i = 0; i < s->n_chan; i++) {
		unsigned int val = s->maxdata / 2;

		/* write 11-bit channel/value to caldac */
		cb_pcidas_calib_write(dev, (i << 8) | val, 11, false);
		s->readback[i] = val;
	}

	/* Calibration subdevice - trim potentiometer */
	s = &dev->subdevices[5];
	s->type		= COMEDI_SUBD_CALIB;
	s->subdev_flags	= SDF_WRITABLE | SDF_INTERNAL;
	if (board->has_ad8402) {
		/*
		 * pci-das1602/16 have an AD8402 trimpot:
		 *   chan 0 : adc gain
		 *   chan 1 : adc postgain offset
		 */
		s->n_chan	= 2;
		s->maxdata	= 0xff;
	} else {
		/* all other boards have an AD7376 trimpot */
		s->n_chan	= 1;
		s->maxdata	= 0x7f;
	}
	s->insn_write	= cb_pcidas_trimpot_insn_write;

	ret = comedi_alloc_subdev_readback(s);
	if (ret)
		return ret;

	for (i = 0; i < s->n_chan; i++) {
		cb_pcidas_trimpot_write(dev, i, s->maxdata / 2);
		s->readback[i] = s->maxdata / 2;
	}

	/* Calibration subdevice - pci-das1602/16 pregain offset (dac08) */
	s = &dev->subdevices[6];
	if (board->has_dac08) {
		s->type		= COMEDI_SUBD_CALIB;
		s->subdev_flags	= SDF_WRITABLE | SDF_INTERNAL;
		s->n_chan	= 1;
		s->maxdata	= 0xff;
		s->insn_write	= cb_pcidas_dac08_insn_write;

		ret = comedi_alloc_subdev_readback(s);
		if (ret)
			return ret;

		for (i = 0; i < s->n_chan; i++) {
			cb_pcidas_dac08_write(dev, s->maxdata / 2);
			s->readback[i] = s->maxdata / 2;
		}
	} else {
		s->type		= COMEDI_SUBD_UNUSED;
	}

	/*  make sure mailbox 4 is empty */
	inl(devpriv->amcc + AMCC_OP_REG_IMB4);
	/* Set bits to enable incoming mailbox interrupts on amcc s5933. */
	devpriv->amcc_intcsr = INTCSR_INBOX_BYTE(3) | INTCSR_INBOX_SELECT(3) |
			       INTCSR_INBOX_FULL_INT;
	/*  clear and enable interrupt on amcc s5933 */
	outl(devpriv->amcc_intcsr | INTCSR_INBOX_INTR_STATUS,
	     devpriv->amcc + AMCC_OP_REG_INTCSR);

	return 0;
}