mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-13 20:59:37 +02:00
s390/pci: Allow allocation of more than 1 MSI interrupt
On a PCI adapter that provides up to 8 MSI interrupt sources the s390
implementation of PCI interrupts rejected to accommodate them, although
the underlying hardware is able to support that.
For MSI-X it is sufficient to allocate a single irq_desc per msi_desc,
but for MSI multiple irq descriptors are attached to and controlled by
a single msi descriptor. Add the appropriate loops to maintain multiple
irq descriptors and tie/untie them to/from the appropriate AIBV bit, if
a device driver allocates more than 1 MSI interrupt.
Common PCI code passes on requests to allocate a number of interrupt
vectors based on the device drivers' demand and the PCI functions'
capabilities. However, the root-complex of s390 systems support just a
limited number of interrupt vectors per PCI function.
Produce a kernel log message to inform about any architecture-specific
capping that might be done.
With this change, we had a PCI adapter successfully raising
interrupts to its device driver via all 8 sources.
Fixes: a384c8924a
("s390/PCI: Fix single MSI only check")
Signed-off-by: Gerd Bayer <gbayer@linux.ibm.com>
Reviewed-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
This commit is contained in:
parent
5fd11b96b4
commit
ab42fcb511
|
@ -298,8 +298,8 @@ static int __alloc_airq(struct zpci_dev *zdev, int msi_vecs,
|
||||||
|
|
||||||
int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
|
int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
|
||||||
{
|
{
|
||||||
|
unsigned int hwirq, msi_vecs, irqs_per_msi, i, cpu;
|
||||||
struct zpci_dev *zdev = to_zpci(pdev);
|
struct zpci_dev *zdev = to_zpci(pdev);
|
||||||
unsigned int hwirq, msi_vecs, cpu;
|
|
||||||
struct msi_desc *msi;
|
struct msi_desc *msi;
|
||||||
struct msi_msg msg;
|
struct msi_msg msg;
|
||||||
unsigned long bit;
|
unsigned long bit;
|
||||||
|
@ -309,30 +309,46 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
|
||||||
zdev->aisb = -1UL;
|
zdev->aisb = -1UL;
|
||||||
zdev->msi_first_bit = -1U;
|
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);
|
msi_vecs = min_t(unsigned int, nvec, zdev->max_msi);
|
||||||
|
if (msi_vecs < nvec) {
|
||||||
|
pr_info("%s requested %d irqs, allocate system limit of %d",
|
||||||
|
pci_name(pdev), nvec, zdev->max_msi);
|
||||||
|
}
|
||||||
|
|
||||||
rc = __alloc_airq(zdev, msi_vecs, &bit);
|
rc = __alloc_airq(zdev, msi_vecs, &bit);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
/* Request MSI interrupts */
|
/*
|
||||||
|
* Request MSI interrupts:
|
||||||
|
* When using MSI, nvec_used interrupt sources and their irq
|
||||||
|
* descriptors are controlled through one msi descriptor.
|
||||||
|
* Thus the outer loop over msi descriptors shall run only once,
|
||||||
|
* while two inner loops iterate over the interrupt vectors.
|
||||||
|
* When using MSI-X, each interrupt vector/irq descriptor
|
||||||
|
* is bound to exactly one msi descriptor (nvec_used is one).
|
||||||
|
* So the inner loops are executed once, while the outer iterates
|
||||||
|
* over the MSI-X descriptors.
|
||||||
|
*/
|
||||||
hwirq = bit;
|
hwirq = bit;
|
||||||
msi_for_each_desc(msi, &pdev->dev, MSI_DESC_NOTASSOCIATED) {
|
msi_for_each_desc(msi, &pdev->dev, MSI_DESC_NOTASSOCIATED) {
|
||||||
rc = -EIO;
|
|
||||||
if (hwirq - bit >= msi_vecs)
|
if (hwirq - bit >= msi_vecs)
|
||||||
break;
|
break;
|
||||||
irq = __irq_alloc_descs(-1, 0, 1, 0, THIS_MODULE,
|
irqs_per_msi = min_t(unsigned int, msi_vecs, msi->nvec_used);
|
||||||
|
irq = __irq_alloc_descs(-1, 0, irqs_per_msi, 0, THIS_MODULE,
|
||||||
(irq_delivery == DIRECTED) ?
|
(irq_delivery == DIRECTED) ?
|
||||||
msi->affinity : NULL);
|
msi->affinity : NULL);
|
||||||
if (irq < 0)
|
if (irq < 0)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
rc = irq_set_msi_desc(irq, msi);
|
|
||||||
|
for (i = 0; i < irqs_per_msi; i++) {
|
||||||
|
rc = irq_set_msi_desc_off(irq, i, msi);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
irq_set_chip_and_handler(irq, &zpci_irq_chip,
|
irq_set_chip_and_handler(irq + i, &zpci_irq_chip,
|
||||||
handle_percpu_irq);
|
handle_percpu_irq);
|
||||||
|
}
|
||||||
|
|
||||||
msg.data = hwirq - bit;
|
msg.data = hwirq - bit;
|
||||||
if (irq_delivery == DIRECTED) {
|
if (irq_delivery == DIRECTED) {
|
||||||
if (msi->affinity)
|
if (msi->affinity)
|
||||||
|
@ -345,31 +361,35 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
|
||||||
msg.address_lo |= (cpu_addr << 8);
|
msg.address_lo |= (cpu_addr << 8);
|
||||||
|
|
||||||
for_each_possible_cpu(cpu) {
|
for_each_possible_cpu(cpu) {
|
||||||
airq_iv_set_data(zpci_ibv[cpu], hwirq, irq);
|
for (i = 0; i < irqs_per_msi; i++)
|
||||||
|
airq_iv_set_data(zpci_ibv[cpu],
|
||||||
|
hwirq + i, irq + i);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
msg.address_lo = zdev->msi_addr & 0xffffffff;
|
msg.address_lo = zdev->msi_addr & 0xffffffff;
|
||||||
airq_iv_set_data(zdev->aibv, hwirq, irq);
|
for (i = 0; i < irqs_per_msi; i++)
|
||||||
|
airq_iv_set_data(zdev->aibv, hwirq + i, irq + i);
|
||||||
}
|
}
|
||||||
msg.address_hi = zdev->msi_addr >> 32;
|
msg.address_hi = zdev->msi_addr >> 32;
|
||||||
pci_write_msi_msg(irq, &msg);
|
pci_write_msi_msg(irq, &msg);
|
||||||
hwirq++;
|
hwirq += irqs_per_msi;
|
||||||
}
|
}
|
||||||
|
|
||||||
zdev->msi_first_bit = bit;
|
zdev->msi_first_bit = bit;
|
||||||
zdev->msi_nr_irqs = msi_vecs;
|
zdev->msi_nr_irqs = hwirq - bit;
|
||||||
|
|
||||||
rc = zpci_set_irq(zdev);
|
rc = zpci_set_irq(zdev);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
return (msi_vecs == nvec) ? 0 : msi_vecs;
|
return (zdev->msi_nr_irqs == nvec) ? 0 : zdev->msi_nr_irqs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void arch_teardown_msi_irqs(struct pci_dev *pdev)
|
void arch_teardown_msi_irqs(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
struct zpci_dev *zdev = to_zpci(pdev);
|
struct zpci_dev *zdev = to_zpci(pdev);
|
||||||
struct msi_desc *msi;
|
struct msi_desc *msi;
|
||||||
|
unsigned int i;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Disable interrupts */
|
/* Disable interrupts */
|
||||||
|
@ -379,8 +399,10 @@ void arch_teardown_msi_irqs(struct pci_dev *pdev)
|
||||||
|
|
||||||
/* Release MSI interrupts */
|
/* Release MSI interrupts */
|
||||||
msi_for_each_desc(msi, &pdev->dev, MSI_DESC_ASSOCIATED) {
|
msi_for_each_desc(msi, &pdev->dev, MSI_DESC_ASSOCIATED) {
|
||||||
irq_set_msi_desc(msi->irq, NULL);
|
for (i = 0; i < msi->nvec_used; i++) {
|
||||||
irq_free_desc(msi->irq);
|
irq_set_msi_desc(msi->irq + i, NULL);
|
||||||
|
irq_free_desc(msi->irq + i);
|
||||||
|
}
|
||||||
msi->msg.address_lo = 0;
|
msi->msg.address_lo = 0;
|
||||||
msi->msg.address_hi = 0;
|
msi->msg.address_hi = 0;
|
||||||
msi->msg.data = 0;
|
msi->msg.data = 0;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user