int arch_setup_msi_irqs()

in pci/pci_irq.c [265:351]


int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
{
	struct zpci_dev *zdev = to_zpci(pdev);
	unsigned int hwirq, msi_vecs, cpu;
	unsigned long bit;
	struct msi_desc *msi;
	struct msi_msg msg;
	int cpu_addr;
	int rc, irq;

	zdev->aisb = -1UL;
	zdev->msi_first_bit = -1U;
	if (type == PCI_CAP_ID_MSI && nvec > 1)
		return 1;
	msi_vecs = min_t(unsigned int, nvec, zdev->max_msi);

	if (irq_delivery == DIRECTED) {
		/* Allocate cpu vector bits */
		bit = airq_iv_alloc(zpci_ibv[0], msi_vecs);
		if (bit == -1UL)
			return -EIO;
	} else {
		/* Allocate adapter summary indicator bit */
		bit = airq_iv_alloc_bit(zpci_sbv);
		if (bit == -1UL)
			return -EIO;
		zdev->aisb = bit;

		/* Create adapter interrupt vector */
		zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA | AIRQ_IV_BITLOCK);
		if (!zdev->aibv)
			return -ENOMEM;

		/* Wire up shortcut pointer */
		zpci_ibv[bit] = zdev->aibv;
		/* Each function has its own interrupt vector */
		bit = 0;
	}

	/* Request MSI interrupts */
	hwirq = bit;
	msi_for_each_desc(msi, &pdev->dev, MSI_DESC_NOTASSOCIATED) {
		rc = -EIO;
		if (hwirq - bit >= msi_vecs)
			break;
		irq = __irq_alloc_descs(-1, 0, 1, 0, THIS_MODULE,
				(irq_delivery == DIRECTED) ?
				msi->affinity : NULL);
		if (irq < 0)
			return -ENOMEM;
		rc = irq_set_msi_desc(irq, msi);
		if (rc)
			return rc;
		irq_set_chip_and_handler(irq, &zpci_irq_chip,
					 handle_percpu_irq);
		msg.data = hwirq - bit;
		if (irq_delivery == DIRECTED) {
			if (msi->affinity)
				cpu = cpumask_first(&msi->affinity->mask);
			else
				cpu = 0;
			cpu_addr = smp_cpu_get_cpu_address(cpu);

			msg.address_lo = zdev->msi_addr & 0xff0000ff;
			msg.address_lo |= (cpu_addr << 8);

			for_each_possible_cpu(cpu) {
				airq_iv_set_data(zpci_ibv[cpu], hwirq, irq);
			}
		} else {
			msg.address_lo = zdev->msi_addr & 0xffffffff;
			airq_iv_set_data(zdev->aibv, hwirq, irq);
		}
		msg.address_hi = zdev->msi_addr >> 32;
		pci_write_msi_msg(irq, &msg);
		hwirq++;
	}

	zdev->msi_first_bit = bit;
	zdev->msi_nr_irqs = msi_vecs;

	rc = zpci_set_irq(zdev);
	if (rc)
		return rc;

	return (msi_vecs == nvec) ? 0 : msi_vecs;
}