mirror of
https://github.com/torvalds/linux.git
synced 2026-06-03 20:14:06 +02:00
powerpc/powernv/pci: Add MSI domains
This is very similar to the MSI domains of the pSeries platform. The MSI allocator is directly handled under the Linux PHB in the in-the-middle "PNV-MSI" domain. Only the XIVE (P9/P10) parent domain is supported for now. Support for XICS will come later. Signed-off-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20210701132750.1475580-13-clg@kaod.org
This commit is contained in:
parent
2c50d7e99e
commit
0fcfe2247e
|
|
@ -36,6 +36,7 @@
|
||||||
#include <asm/firmware.h>
|
#include <asm/firmware.h>
|
||||||
#include <asm/pnv-pci.h>
|
#include <asm/pnv-pci.h>
|
||||||
#include <asm/mmzone.h>
|
#include <asm/mmzone.h>
|
||||||
|
#include <asm/xive.h>
|
||||||
|
|
||||||
#include <misc/cxl-base.h>
|
#include <misc/cxl-base.h>
|
||||||
|
|
||||||
|
|
@ -2100,6 +2101,189 @@ static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The msi_free() op is called before irq_domain_free_irqs_top() when
|
||||||
|
* the handler data is still available. Use that to clear the XIVE
|
||||||
|
* controller.
|
||||||
|
*/
|
||||||
|
static void pnv_msi_ops_msi_free(struct irq_domain *domain,
|
||||||
|
struct msi_domain_info *info,
|
||||||
|
unsigned int irq)
|
||||||
|
{
|
||||||
|
if (xive_enabled())
|
||||||
|
xive_irq_free_data(irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct msi_domain_ops pnv_pci_msi_domain_ops = {
|
||||||
|
.msi_free = pnv_msi_ops_msi_free,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pnv_msi_shutdown(struct irq_data *d)
|
||||||
|
{
|
||||||
|
d = d->parent_data;
|
||||||
|
if (d->chip->irq_shutdown)
|
||||||
|
d->chip->irq_shutdown(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pnv_msi_mask(struct irq_data *d)
|
||||||
|
{
|
||||||
|
pci_msi_mask_irq(d);
|
||||||
|
irq_chip_mask_parent(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pnv_msi_unmask(struct irq_data *d)
|
||||||
|
{
|
||||||
|
pci_msi_unmask_irq(d);
|
||||||
|
irq_chip_unmask_parent(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_chip pnv_pci_msi_irq_chip = {
|
||||||
|
.name = "PNV-PCI-MSI",
|
||||||
|
.irq_shutdown = pnv_msi_shutdown,
|
||||||
|
.irq_mask = pnv_msi_mask,
|
||||||
|
.irq_unmask = pnv_msi_unmask,
|
||||||
|
.irq_eoi = irq_chip_eoi_parent,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct msi_domain_info pnv_msi_domain_info = {
|
||||||
|
.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
|
||||||
|
MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX),
|
||||||
|
.ops = &pnv_pci_msi_domain_ops,
|
||||||
|
.chip = &pnv_pci_msi_irq_chip,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pnv_msi_compose_msg(struct irq_data *d, struct msi_msg *msg)
|
||||||
|
{
|
||||||
|
struct msi_desc *entry = irq_data_get_msi_desc(d);
|
||||||
|
struct pci_dev *pdev = msi_desc_to_pci_dev(entry);
|
||||||
|
struct pci_controller *hose = irq_data_get_irq_chip_data(d);
|
||||||
|
struct pnv_phb *phb = hose->private_data;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = __pnv_pci_ioda_msi_setup(phb, pdev, d->hwirq,
|
||||||
|
entry->msi_attrib.is_64, msg);
|
||||||
|
if (rc)
|
||||||
|
dev_err(&pdev->dev, "Failed to setup %s-bit MSI #%ld : %d\n",
|
||||||
|
entry->msi_attrib.is_64 ? "64" : "32", d->hwirq, rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_chip pnv_msi_irq_chip = {
|
||||||
|
.name = "PNV-MSI",
|
||||||
|
.irq_shutdown = pnv_msi_shutdown,
|
||||||
|
.irq_mask = irq_chip_mask_parent,
|
||||||
|
.irq_unmask = irq_chip_unmask_parent,
|
||||||
|
.irq_eoi = irq_chip_eoi_parent,
|
||||||
|
.irq_set_affinity = irq_chip_set_affinity_parent,
|
||||||
|
.irq_compose_msi_msg = pnv_msi_compose_msg,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int pnv_irq_parent_domain_alloc(struct irq_domain *domain,
|
||||||
|
unsigned int virq, int hwirq)
|
||||||
|
{
|
||||||
|
struct irq_fwspec parent_fwspec;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
parent_fwspec.fwnode = domain->parent->fwnode;
|
||||||
|
parent_fwspec.param_count = 2;
|
||||||
|
parent_fwspec.param[0] = hwirq;
|
||||||
|
parent_fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
|
||||||
|
|
||||||
|
ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pnv_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||||
|
unsigned int nr_irqs, void *arg)
|
||||||
|
{
|
||||||
|
struct pci_controller *hose = domain->host_data;
|
||||||
|
struct pnv_phb *phb = hose->private_data;
|
||||||
|
msi_alloc_info_t *info = arg;
|
||||||
|
struct pci_dev *pdev = msi_desc_to_pci_dev(info->desc);
|
||||||
|
int hwirq;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
hwirq = msi_bitmap_alloc_hwirqs(&phb->msi_bmp, nr_irqs);
|
||||||
|
if (hwirq < 0) {
|
||||||
|
dev_warn(&pdev->dev, "failed to find a free MSI\n");
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "%s bridge %pOF %d/%x #%d\n", __func__,
|
||||||
|
hose->dn, virq, hwirq, nr_irqs);
|
||||||
|
|
||||||
|
for (i = 0; i < nr_irqs; i++) {
|
||||||
|
ret = pnv_irq_parent_domain_alloc(domain, virq + i,
|
||||||
|
phb->msi_base + hwirq + i);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
|
||||||
|
&pnv_msi_irq_chip, hose);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
irq_domain_free_irqs_parent(domain, virq, i - 1);
|
||||||
|
msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq, nr_irqs);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pnv_irq_domain_free(struct irq_domain *domain, unsigned int virq,
|
||||||
|
unsigned int nr_irqs)
|
||||||
|
{
|
||||||
|
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
|
||||||
|
struct pci_controller *hose = irq_data_get_irq_chip_data(d);
|
||||||
|
struct pnv_phb *phb = hose->private_data;
|
||||||
|
|
||||||
|
pr_debug("%s bridge %pOF %d/%lx #%d\n", __func__, hose->dn,
|
||||||
|
virq, d->hwirq, nr_irqs);
|
||||||
|
|
||||||
|
msi_bitmap_free_hwirqs(&phb->msi_bmp, d->hwirq, nr_irqs);
|
||||||
|
/* XIVE domain is cleared through ->msi_free() */
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct irq_domain_ops pnv_irq_domain_ops = {
|
||||||
|
.alloc = pnv_irq_domain_alloc,
|
||||||
|
.free = pnv_irq_domain_free,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int pnv_msi_allocate_domains(struct pci_controller *hose, unsigned int count)
|
||||||
|
{
|
||||||
|
struct pnv_phb *phb = hose->private_data;
|
||||||
|
struct irq_domain *parent = irq_get_default_host();
|
||||||
|
|
||||||
|
hose->fwnode = irq_domain_alloc_named_id_fwnode("PNV-MSI", phb->opal_id);
|
||||||
|
if (!hose->fwnode)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
hose->dev_domain = irq_domain_create_hierarchy(parent, 0, count,
|
||||||
|
hose->fwnode,
|
||||||
|
&pnv_irq_domain_ops, hose);
|
||||||
|
if (!hose->dev_domain) {
|
||||||
|
pr_err("PCI: failed to create IRQ domain bridge %pOF (domain %d)\n",
|
||||||
|
hose->dn, hose->global_number);
|
||||||
|
irq_domain_free_fwnode(hose->fwnode);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
hose->msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(hose->dn),
|
||||||
|
&pnv_msi_domain_info,
|
||||||
|
hose->dev_domain);
|
||||||
|
if (!hose->msi_domain) {
|
||||||
|
pr_err("PCI: failed to create MSI IRQ domain bridge %pOF (domain %d)\n",
|
||||||
|
hose->dn, hose->global_number);
|
||||||
|
irq_domain_free_fwnode(hose->fwnode);
|
||||||
|
irq_domain_remove(hose->dev_domain);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void pnv_pci_init_ioda_msis(struct pnv_phb *phb)
|
static void pnv_pci_init_ioda_msis(struct pnv_phb *phb)
|
||||||
{
|
{
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
|
|
@ -2124,6 +2308,10 @@ static void pnv_pci_init_ioda_msis(struct pnv_phb *phb)
|
||||||
phb->msi32_support = 1;
|
phb->msi32_support = 1;
|
||||||
pr_info(" Allocated bitmap for %d MSIs (base IRQ 0x%x)\n",
|
pr_info(" Allocated bitmap for %d MSIs (base IRQ 0x%x)\n",
|
||||||
count, phb->msi_base);
|
count, phb->msi_base);
|
||||||
|
|
||||||
|
/* Only supported by the XIVE driver */
|
||||||
|
if (xive_enabled())
|
||||||
|
pnv_msi_allocate_domains(phb->hose, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pnv_ioda_setup_pe_res(struct pnv_ioda_pe *pe,
|
static void pnv_ioda_setup_pe_res(struct pnv_ioda_pe *pe,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user