Skip to content

Commit a5f3d2c

Browse files
legoatermpe
authored andcommitted
powerpc/pseries/pci: Add MSI domains
Two IRQ domains are added on top of default machine IRQ domain. First, the top level "pSeries-PCI-MSI" domain deals with the MSI specificities. In this domain, the HW IRQ numbers are generated by the PCI MSI layer, they compose a unique ID for an MSI source with the PCI device identifier and the MSI vector number. These numbers can be quite large on a pSeries machine running under the IBM Hypervisor and /sys/kernel/irq/ and /proc/interrupts will require small fixes to show them correctly. Second domain is the in-the-middle "pSeries-MSI" domain which acts as a proxy between the PCI MSI subsystem and the machine IRQ subsystem. It usually allocate the MSI vector numbers but, on pSeries machines, this is done by the RTAS FW and RTAS returns IRQ numbers in the IRQ number space of the machine. This is why the in-the-middle "pSeries-MSI" domain has the same HW IRQ numbers as its parent domain. Only the XIVE (P9/P10) parent domain is supported for now. We still need to add support for IRQ domain hierarchy under XICS. Signed-off-by: Cédric Le Goater <[email protected]> Signed-off-by: Michael Ellerman <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 6c2ab2a commit a5f3d2c

File tree

5 files changed

+199
-0
lines changed

5 files changed

+199
-0
lines changed

arch/powerpc/include/asm/pci-bridge.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ struct pci_controller {
126126
#endif /* CONFIG_PPC64 */
127127

128128
void *private_data;
129+
130+
/* IRQ domain hierarchy */
131+
struct irq_domain *dev_domain;
132+
struct irq_domain *msi_domain;
133+
struct fwnode_handle *fwnode;
129134
};
130135

131136
/* These are used for config access before all the PCI probing

arch/powerpc/kernel/pci-common.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <linux/slab.h>
3030
#include <linux/vgaarb.h>
3131
#include <linux/numa.h>
32+
#include <linux/msi.h>
3233

3334
#include <asm/processor.h>
3435
#include <asm/io.h>
@@ -1060,11 +1061,16 @@ void pcibios_bus_add_device(struct pci_dev *dev)
10601061

10611062
int pcibios_add_device(struct pci_dev *dev)
10621063
{
1064+
struct irq_domain *d;
1065+
10631066
#ifdef CONFIG_PCI_IOV
10641067
if (ppc_md.pcibios_fixup_sriov)
10651068
ppc_md.pcibios_fixup_sriov(dev);
10661069
#endif /* CONFIG_PCI_IOV */
10671070

1071+
d = dev_get_msi_domain(&dev->bus->dev);
1072+
if (d)
1073+
dev_set_msi_domain(&dev->dev, d);
10681074
return 0;
10691075
}
10701076

arch/powerpc/platforms/pseries/msi.c

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <asm/hw_irq.h>
1414
#include <asm/ppc-pci.h>
1515
#include <asm/machdep.h>
16+
#include <asm/xive.h>
1617

1718
#include "pseries.h"
1819

@@ -518,6 +519,190 @@ static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type)
518519
return 0;
519520
}
520521

522+
static int pseries_msi_ops_prepare(struct irq_domain *domain, struct device *dev,
523+
int nvec, msi_alloc_info_t *arg)
524+
{
525+
struct pci_dev *pdev = to_pci_dev(dev);
526+
struct msi_desc *desc = first_pci_msi_entry(pdev);
527+
int type = desc->msi_attrib.is_msix ? PCI_CAP_ID_MSIX : PCI_CAP_ID_MSI;
528+
529+
return rtas_prepare_msi_irqs(pdev, nvec, type, arg);
530+
}
531+
532+
static struct msi_domain_ops pseries_pci_msi_domain_ops = {
533+
.msi_prepare = pseries_msi_ops_prepare,
534+
};
535+
536+
static void pseries_msi_shutdown(struct irq_data *d)
537+
{
538+
d = d->parent_data;
539+
if (d->chip->irq_shutdown)
540+
d->chip->irq_shutdown(d);
541+
}
542+
543+
static void pseries_msi_mask(struct irq_data *d)
544+
{
545+
pci_msi_mask_irq(d);
546+
irq_chip_mask_parent(d);
547+
}
548+
549+
static void pseries_msi_unmask(struct irq_data *d)
550+
{
551+
pci_msi_unmask_irq(d);
552+
irq_chip_unmask_parent(d);
553+
}
554+
555+
static struct irq_chip pseries_pci_msi_irq_chip = {
556+
.name = "pSeries-PCI-MSI",
557+
.irq_shutdown = pseries_msi_shutdown,
558+
.irq_mask = pseries_msi_mask,
559+
.irq_unmask = pseries_msi_unmask,
560+
.irq_eoi = irq_chip_eoi_parent,
561+
};
562+
563+
static struct msi_domain_info pseries_msi_domain_info = {
564+
.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
565+
MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX),
566+
.ops = &pseries_pci_msi_domain_ops,
567+
.chip = &pseries_pci_msi_irq_chip,
568+
};
569+
570+
static void pseries_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
571+
{
572+
__pci_read_msi_msg(irq_data_get_msi_desc(data), msg);
573+
}
574+
575+
static struct irq_chip pseries_msi_irq_chip = {
576+
.name = "pSeries-MSI",
577+
.irq_shutdown = pseries_msi_shutdown,
578+
.irq_mask = irq_chip_mask_parent,
579+
.irq_unmask = irq_chip_unmask_parent,
580+
.irq_eoi = irq_chip_eoi_parent,
581+
.irq_set_affinity = irq_chip_set_affinity_parent,
582+
.irq_compose_msi_msg = pseries_msi_compose_msg,
583+
};
584+
585+
static int pseries_irq_parent_domain_alloc(struct irq_domain *domain, unsigned int virq,
586+
irq_hw_number_t hwirq)
587+
{
588+
struct irq_fwspec parent_fwspec;
589+
int ret;
590+
591+
parent_fwspec.fwnode = domain->parent->fwnode;
592+
parent_fwspec.param_count = 2;
593+
parent_fwspec.param[0] = hwirq;
594+
parent_fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
595+
596+
ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
597+
if (ret)
598+
return ret;
599+
600+
return 0;
601+
}
602+
603+
static int pseries_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
604+
unsigned int nr_irqs, void *arg)
605+
{
606+
struct pci_controller *phb = domain->host_data;
607+
msi_alloc_info_t *info = arg;
608+
struct msi_desc *desc = info->desc;
609+
struct pci_dev *pdev = msi_desc_to_pci_dev(desc);
610+
int hwirq;
611+
int i, ret;
612+
613+
hwirq = rtas_query_irq_number(pci_get_pdn(pdev), desc->msi_attrib.entry_nr);
614+
if (hwirq < 0) {
615+
dev_err(&pdev->dev, "Failed to query HW IRQ: %d\n", hwirq);
616+
return hwirq;
617+
}
618+
619+
dev_dbg(&pdev->dev, "%s bridge %pOF %d/%x #%d\n", __func__,
620+
phb->dn, virq, hwirq, nr_irqs);
621+
622+
for (i = 0; i < nr_irqs; i++) {
623+
ret = pseries_irq_parent_domain_alloc(domain, virq + i, hwirq + i);
624+
if (ret)
625+
goto out;
626+
627+
irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
628+
&pseries_msi_irq_chip, domain->host_data);
629+
}
630+
631+
return 0;
632+
633+
out:
634+
/* TODO: handle RTAS cleanup in ->msi_finish() ? */
635+
irq_domain_free_irqs_parent(domain, virq, i - 1);
636+
return ret;
637+
}
638+
639+
static void pseries_irq_domain_free(struct irq_domain *domain, unsigned int virq,
640+
unsigned int nr_irqs)
641+
{
642+
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
643+
struct pci_controller *phb = irq_data_get_irq_chip_data(d);
644+
645+
pr_debug("%s bridge %pOF %d #%d\n", __func__, phb->dn, virq, nr_irqs);
646+
647+
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
648+
}
649+
650+
static const struct irq_domain_ops pseries_irq_domain_ops = {
651+
.alloc = pseries_irq_domain_alloc,
652+
.free = pseries_irq_domain_free,
653+
};
654+
655+
static int __pseries_msi_allocate_domains(struct pci_controller *phb,
656+
unsigned int count)
657+
{
658+
struct irq_domain *parent = irq_get_default_host();
659+
660+
phb->fwnode = irq_domain_alloc_named_id_fwnode("pSeries-MSI",
661+
phb->global_number);
662+
if (!phb->fwnode)
663+
return -ENOMEM;
664+
665+
phb->dev_domain = irq_domain_create_hierarchy(parent, 0, count,
666+
phb->fwnode,
667+
&pseries_irq_domain_ops, phb);
668+
if (!phb->dev_domain) {
669+
pr_err("PCI: failed to create IRQ domain bridge %pOF (domain %d)\n",
670+
phb->dn, phb->global_number);
671+
irq_domain_free_fwnode(phb->fwnode);
672+
return -ENOMEM;
673+
}
674+
675+
phb->msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(phb->dn),
676+
&pseries_msi_domain_info,
677+
phb->dev_domain);
678+
if (!phb->msi_domain) {
679+
pr_err("PCI: failed to create MSI IRQ domain bridge %pOF (domain %d)\n",
680+
phb->dn, phb->global_number);
681+
irq_domain_free_fwnode(phb->fwnode);
682+
irq_domain_remove(phb->dev_domain);
683+
return -ENOMEM;
684+
}
685+
686+
return 0;
687+
}
688+
689+
int pseries_msi_allocate_domains(struct pci_controller *phb)
690+
{
691+
int count;
692+
693+
/* Only supported by the XIVE driver */
694+
if (!xive_enabled())
695+
return -ENODEV;
696+
697+
if (!__find_pe_total_msi(phb->dn, &count)) {
698+
pr_err("PCI: failed to find MSIs for bridge %pOF (domain %d)\n",
699+
phb->dn, phb->global_number);
700+
return -ENOSPC;
701+
}
702+
703+
return __pseries_msi_allocate_domains(phb, count);
704+
}
705+
521706
static void rtas_msi_pci_irq_fixup(struct pci_dev *pdev)
522707
{
523708
/* No LSI -> leave MSIs (if any) configured */

arch/powerpc/platforms/pseries/pseries.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ struct pci_host_bridge;
8585
int pseries_root_bridge_prepare(struct pci_host_bridge *bridge);
8686

8787
extern struct pci_controller_ops pseries_pci_controller_ops;
88+
int pseries_msi_allocate_domains(struct pci_controller *phb);
8889

8990
unsigned long pseries_memory_block_size(void);
9091

arch/powerpc/platforms/pseries/setup.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,8 @@ static void __init pSeries_discover_phbs(void)
486486

487487
/* create pci_dn's for DT nodes under this PHB */
488488
pci_devs_phb_init_dynamic(phb);
489+
490+
pseries_msi_allocate_domains(phb);
489491
}
490492

491493
of_node_put(root);

0 commit comments

Comments
 (0)