static int pci230_auto_attach()

in drivers/amplc_pci230.c [2368:2540]


static int pci230_auto_attach(struct comedi_device *dev,
			      unsigned long context_unused)
{
	struct pci_dev *pci_dev = comedi_to_pci_dev(dev);
	const struct pci230_board *board;
	struct pci230_private *devpriv;
	struct comedi_subdevice *s;
	int rc;

	dev_info(dev->class_dev, "amplc_pci230: attach pci %s\n",
		 pci_name(pci_dev));

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

	spin_lock_init(&devpriv->isr_spinlock);
	spin_lock_init(&devpriv->res_spinlock);
	spin_lock_init(&devpriv->ai_stop_spinlock);
	spin_lock_init(&devpriv->ao_stop_spinlock);

	board = pci230_find_pci_board(pci_dev);
	if (!board) {
		dev_err(dev->class_dev,
			"amplc_pci230: BUG! cannot determine board type!\n");
		return -EINVAL;
	}
	dev->board_ptr = board;
	dev->board_name = board->name;

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

	/*
	 * Read base addresses of the PCI230's two I/O regions from PCI
	 * configuration register.
	 */
	dev->iobase = pci_resource_start(pci_dev, 2);
	devpriv->daqio = pci_resource_start(pci_dev, 3);
	dev_dbg(dev->class_dev,
		"%s I/O region 1 0x%04lx I/O region 2 0x%04lx\n",
		dev->board_name, dev->iobase, devpriv->daqio);
	/* Read bits of DACCON register - only the output range. */
	devpriv->daccon = inw(devpriv->daqio + PCI230_DACCON) &
			  PCI230_DAC_OR_MASK;
	/*
	 * Read hardware version register and set extended function register
	 * if they exist.
	 */
	if (pci_resource_len(pci_dev, 3) >= 32) {
		unsigned short extfunc = 0;

		devpriv->hwver = inw(devpriv->daqio + PCI230P_HWVER);
		if (devpriv->hwver < board->min_hwver) {
			dev_err(dev->class_dev,
				"%s - bad hardware version - got %u, need %u\n",
				dev->board_name, devpriv->hwver,
				board->min_hwver);
			return -EIO;
		}
		if (devpriv->hwver > 0) {
			if (!board->have_dio) {
				/*
				 * No DIO ports.  Route counters' external gates
				 * to the EXTTRIG signal (PCI260+ pin 17).
				 * (Otherwise, they would be routed to DIO
				 * inputs PC0, PC1 and PC2 which don't exist
				 * on PCI260[+].)
				 */
				extfunc |= PCI230P_EXTFUNC_GAT_EXTTRIG;
			}
			if (board->ao_bits && devpriv->hwver >= 2) {
				/* Enable DAC FIFO functionality. */
				extfunc |= PCI230P2_EXTFUNC_DACFIFO;
			}
		}
		outw(extfunc, devpriv->daqio + PCI230P_EXTFUNC);
		if (extfunc & PCI230P2_EXTFUNC_DACFIFO) {
			/*
			 * Temporarily enable DAC FIFO, reset it and disable
			 * FIFO wraparound.
			 */
			outw(devpriv->daccon | PCI230P2_DAC_FIFO_EN |
			     PCI230P2_DAC_FIFO_RESET,
			     devpriv->daqio + PCI230_DACCON);
			/* Clear DAC FIFO channel enable register. */
			outw(0, devpriv->daqio + PCI230P2_DACEN);
			/* Disable DAC FIFO. */
			outw(devpriv->daccon, devpriv->daqio + PCI230_DACCON);
		}
	}
	/* Disable board's interrupts. */
	outb(0, dev->iobase + PCI230_INT_SCE);
	/* Set ADC to a reasonable state. */
	devpriv->adcg = 0;
	devpriv->adccon = PCI230_ADC_TRIG_NONE | PCI230_ADC_IM_SE |
			  PCI230_ADC_IR_BIP;
	outw(BIT(0), devpriv->daqio + PCI230_ADCEN);
	outw(devpriv->adcg, devpriv->daqio + PCI230_ADCG);
	outw(devpriv->adccon | PCI230_ADC_FIFO_RESET,
	     devpriv->daqio + PCI230_ADCCON);

	if (pci_dev->irq) {
		rc = request_irq(pci_dev->irq, pci230_interrupt, IRQF_SHARED,
				 dev->board_name, dev);
		if (rc == 0)
			dev->irq = pci_dev->irq;
	}

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

	rc = comedi_alloc_subdevices(dev, 3);
	if (rc)
		return rc;

	s = &dev->subdevices[0];
	/* analog input subdevice */
	s->type = COMEDI_SUBD_AI;
	s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_GROUND;
	s->n_chan = 16;
	s->maxdata = (1 << board->ai_bits) - 1;
	s->range_table = &pci230_ai_range;
	s->insn_read = pci230_ai_insn_read;
	s->len_chanlist = 256;	/* but there are restrictions. */
	if (dev->irq) {
		dev->read_subdev = s;
		s->subdev_flags |= SDF_CMD_READ;
		s->do_cmd = pci230_ai_cmd;
		s->do_cmdtest = pci230_ai_cmdtest;
		s->cancel = pci230_ai_cancel;
	}

	s = &dev->subdevices[1];
	/* analog output subdevice */
	if (board->ao_bits) {
		s->type = COMEDI_SUBD_AO;
		s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
		s->n_chan = 2;
		s->maxdata = (1 << board->ao_bits) - 1;
		s->range_table = &pci230_ao_range;
		s->insn_write = pci230_ao_insn_write;
		s->len_chanlist = 2;
		if (dev->irq) {
			dev->write_subdev = s;
			s->subdev_flags |= SDF_CMD_WRITE;
			s->do_cmd = pci230_ao_cmd;
			s->do_cmdtest = pci230_ao_cmdtest;
			s->cancel = pci230_ao_cancel;
		}

		rc = comedi_alloc_subdev_readback(s);
		if (rc)
			return rc;
	} else {
		s->type = COMEDI_SUBD_UNUSED;
	}

	s = &dev->subdevices[2];
	/* digital i/o subdevice */
	if (board->have_dio) {
		rc = subdev_8255_init(dev, s, NULL, PCI230_PPI_X_BASE);
		if (rc)
			return rc;
	} else {
		s->type = COMEDI_SUBD_UNUSED;
	}

	return 0;
}