irqchip/gic-v3: Rename GICv3 ITS MSI parent
authorLorenzo Pieralisi <lpieralisi@kernel.org>
Thu, 3 Jul 2025 10:25:16 +0000 (12:25 +0200)
committerMarc Zyngier <maz@kernel.org>
Tue, 8 Jul 2025 17:35:52 +0000 (18:35 +0100)
The GICv5 ITS will reuse some GICv3 ITS MSI parent functions therefore
it makes sense to keep the code functionality in a compilation unit
shared by the two drivers.

Rename the GICv3 ITS MSI parent file and update the related
Kconfig/Makefile entries to pave the way for code sharing.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Reviewed-by: Marc Zyngier <maz@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20250703-gicv5-host-v7-26-12e71f1b3528@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
drivers/irqchip/Kconfig
drivers/irqchip/Makefile
drivers/irqchip/irq-gic-common.h
drivers/irqchip/irq-gic-its-msi-parent.c [new file with mode: 0644]
drivers/irqchip/irq-gic-its-msi-parent.h [new file with mode: 0644]
drivers/irqchip/irq-gic-v3-its-msi-parent.c [deleted file]
drivers/irqchip/irq-gic-v3-its.c

index 3e4fb08b7a4d1a31911ecbf4b7204b8fb2218ce6..f9eae1a645c980f48eca4f66133811e61846daa7 100644 (file)
@@ -41,10 +41,14 @@ config ARM_GIC_V3
        select HAVE_ARM_SMCCC_DISCOVERY
        select IRQ_MSI_IOMMU
 
+config ARM_GIC_ITS_PARENT
+       bool
+
 config ARM_GIC_V3_ITS
        bool
        select GENERIC_MSI_IRQ
        select IRQ_MSI_LIB
+       select ARM_GIC_ITS_PARENT
        default ARM_GIC_V3
        select IRQ_MSI_IOMMU
 
index 7a0e6cee09e1ed1af4c846f191bf256e601c63a5..3ce6ea9a371bf96fc3e2e111e5e762f5f3249baf 100644 (file)
@@ -33,7 +33,8 @@ obj-$(CONFIG_ARCH_REALVIEW)           += irq-gic-realview.o
 obj-$(CONFIG_IRQ_MSI_LIB)              += irq-msi-lib.o
 obj-$(CONFIG_ARM_GIC_V2M)              += irq-gic-v2m.o
 obj-$(CONFIG_ARM_GIC_V3)               += irq-gic-v3.o irq-gic-v3-mbi.o irq-gic-common.o
-obj-$(CONFIG_ARM_GIC_V3_ITS)           += irq-gic-v3-its.o irq-gic-v4.o irq-gic-v3-its-msi-parent.o
+obj-$(CONFIG_ARM_GIC_ITS_PARENT)       += irq-gic-its-msi-parent.o
+obj-$(CONFIG_ARM_GIC_V3_ITS)           += irq-gic-v3-its.o irq-gic-v4.o
 obj-$(CONFIG_ARM_GIC_V3_ITS_FSL_MC)    += irq-gic-v3-its-fsl-mc-msi.o
 obj-$(CONFIG_PARTITION_PERCPU)         += irq-partition-percpu.o
 obj-$(CONFIG_ARM_GIC_V5)               += irq-gic-v5.o irq-gic-v5-irs.o
index 020ecdf16901c9720e5746aec4d0b5b39d3625ed..710cab61d9195a0bd64d57e03c60852c4cd6ff8e 100644 (file)
@@ -29,8 +29,6 @@ void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
 void gic_enable_of_quirks(const struct device_node *np,
                          const struct gic_quirk *quirks, void *data);
 
-extern const struct msi_parent_ops gic_v3_its_msi_parent_ops;
-
 #define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING    (1 << 0)
 #define RDIST_FLAGS_RD_TABLES_PREALLOCATED     (1 << 1)
 #define RDIST_FLAGS_FORCE_NON_SHAREABLE        (1 << 2)
diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c
new file mode 100644 (file)
index 0000000..8beecfe
--- /dev/null
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2013-2015 ARM Limited, All Rights Reserved.
+// Author: Marc Zyngier <marc.zyngier@arm.com>
+// Copyright (C) 2022 Linutronix GmbH
+// Copyright (C) 2022 Intel
+
+#include <linux/acpi_iort.h>
+#include <linux/pci.h>
+
+#include "irq-gic-its-msi-parent.h"
+#include <linux/irqchip/irq-msi-lib.h>
+
+#define ITS_MSI_FLAGS_REQUIRED  (MSI_FLAG_USE_DEF_DOM_OPS |    \
+                                MSI_FLAG_USE_DEF_CHIP_OPS |    \
+                                MSI_FLAG_PCI_MSI_MASK_PARENT)
+
+#define ITS_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK |      \
+                                MSI_FLAG_PCI_MSIX      |       \
+                                MSI_FLAG_MULTI_PCI_MSI)
+
+#ifdef CONFIG_PCI_MSI
+static int its_pci_msi_vec_count(struct pci_dev *pdev, void *data)
+{
+       int msi, msix, *count = data;
+
+       msi = max(pci_msi_vec_count(pdev), 0);
+       msix = max(pci_msix_vec_count(pdev), 0);
+       *count += max(msi, msix);
+
+       return 0;
+}
+
+static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
+{
+       struct pci_dev **alias_dev = data;
+
+       *alias_dev = pdev;
+
+       return 0;
+}
+
+static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
+                              int nvec, msi_alloc_info_t *info)
+{
+       struct pci_dev *pdev, *alias_dev;
+       struct msi_domain_info *msi_info;
+       int alias_count = 0, minnvec = 1;
+
+       if (!dev_is_pci(dev))
+               return -EINVAL;
+
+       pdev = to_pci_dev(dev);
+       /*
+        * If pdev is downstream of any aliasing bridges, take an upper
+        * bound of how many other vectors could map to the same DevID.
+        * Also tell the ITS that the signalling will come from a proxy
+        * device, and that special allocation rules apply.
+        */
+       pci_for_each_dma_alias(pdev, its_get_pci_alias, &alias_dev);
+       if (alias_dev != pdev) {
+               if (alias_dev->subordinate)
+                       pci_walk_bus(alias_dev->subordinate,
+                                    its_pci_msi_vec_count, &alias_count);
+               info->flags |= MSI_ALLOC_FLAGS_PROXY_DEVICE;
+       }
+
+       /* ITS specific DeviceID, as the core ITS ignores dev. */
+       info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain->parent, pdev);
+
+       /*
+        * Always allocate a power of 2, and special case device 0 for
+        * broken systems where the DevID is not wired (and all devices
+        * appear as DevID 0). For that reason, we generously allocate a
+        * minimum of 32 MSIs for DevID 0. If you want more because all
+        * your devices are aliasing to DevID 0, consider fixing your HW.
+        */
+       nvec = max(nvec, alias_count);
+       if (!info->scratchpad[0].ul)
+               minnvec = 32;
+       nvec = max_t(int, minnvec, roundup_pow_of_two(nvec));
+
+       msi_info = msi_get_domain_info(domain->parent);
+       return msi_info->ops->msi_prepare(domain->parent, dev, nvec, info);
+}
+#else /* CONFIG_PCI_MSI */
+#define its_pci_msi_prepare    NULL
+#endif /* !CONFIG_PCI_MSI */
+
+static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev,
+                                 u32 *dev_id)
+{
+       int ret, index = 0;
+
+       /* Suck the DeviceID out of the msi-parent property */
+       do {
+               struct of_phandle_args args;
+
+               ret = of_parse_phandle_with_args(dev->of_node,
+                                                "msi-parent", "#msi-cells",
+                                                index, &args);
+               if (args.np == irq_domain_get_of_node(domain)) {
+                       if (WARN_ON(args.args_count != 1))
+                               return -EINVAL;
+                       *dev_id = args.args[0];
+                       break;
+               }
+               index++;
+       } while (!ret);
+
+       if (ret) {
+               struct device_node *np = NULL;
+
+               ret = of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &np, dev_id);
+               if (np)
+                       of_node_put(np);
+       }
+
+       return ret;
+}
+
+int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
+{
+       return -1;
+}
+
+static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
+                           int nvec, msi_alloc_info_t *info)
+{
+       struct msi_domain_info *msi_info;
+       u32 dev_id;
+       int ret;
+
+       if (dev->of_node)
+               ret = of_pmsi_get_dev_id(domain->parent, dev, &dev_id);
+       else
+               ret = iort_pmsi_get_dev_id(dev, &dev_id);
+       if (ret)
+               return ret;
+
+       /* ITS specific DeviceID, as the core ITS ignores dev. */
+       info->scratchpad[0].ul = dev_id;
+
+       /* Allocate at least 32 MSIs, and always as a power of 2 */
+       nvec = max_t(int, 32, roundup_pow_of_two(nvec));
+
+       msi_info = msi_get_domain_info(domain->parent);
+       return msi_info->ops->msi_prepare(domain->parent,
+                                         dev, nvec, info);
+}
+
+static void its_msi_teardown(struct irq_domain *domain, msi_alloc_info_t *info)
+{
+       struct msi_domain_info *msi_info;
+
+       msi_info = msi_get_domain_info(domain->parent);
+       msi_info->ops->msi_teardown(domain->parent, info);
+}
+
+static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
+                                 struct irq_domain *real_parent, struct msi_domain_info *info)
+{
+       if (!msi_lib_init_dev_msi_info(dev, domain, real_parent, info))
+               return false;
+
+       switch(info->bus_token) {
+       case DOMAIN_BUS_PCI_DEVICE_MSI:
+       case DOMAIN_BUS_PCI_DEVICE_MSIX:
+               /*
+                * FIXME: This probably should be done after a (not yet
+                * existing) post domain creation callback once to make
+                * support for dynamic post-enable MSI-X allocations
+                * work without having to reevaluate the domain size
+                * over and over. It is known already at allocation
+                * time via info->hwsize.
+                *
+                * That should work perfectly fine for MSI/MSI-X but needs
+                * some thoughts for purely software managed MSI domains
+                * where the index space is only limited artificially via
+                * %MSI_MAX_INDEX.
+                */
+               info->ops->msi_prepare = its_pci_msi_prepare;
+               info->ops->msi_teardown = its_msi_teardown;
+               break;
+       case DOMAIN_BUS_DEVICE_MSI:
+       case DOMAIN_BUS_WIRED_TO_MSI:
+               /*
+                * FIXME: See the above PCI prepare comment. The domain
+                * size is also known at domain creation time.
+                */
+               info->ops->msi_prepare = its_pmsi_prepare;
+               info->ops->msi_teardown = its_msi_teardown;
+               break;
+       default:
+               /* Confused. How did the lib return true? */
+               WARN_ON_ONCE(1);
+               return false;
+       }
+
+       return true;
+}
+
+const struct msi_parent_ops gic_v3_its_msi_parent_ops = {
+       .supported_flags        = ITS_MSI_FLAGS_SUPPORTED,
+       .required_flags         = ITS_MSI_FLAGS_REQUIRED,
+       .chip_flags             = MSI_CHIP_FLAG_SET_EOI,
+       .bus_select_token       = DOMAIN_BUS_NEXUS,
+       .bus_select_mask        = MATCH_PCI_MSI | MATCH_PLATFORM_MSI,
+       .prefix                 = "ITS-",
+       .init_dev_msi_info      = its_init_dev_msi_info,
+};
diff --git a/drivers/irqchip/irq-gic-its-msi-parent.h b/drivers/irqchip/irq-gic-its-msi-parent.h
new file mode 100644 (file)
index 0000000..75e223e
--- /dev/null
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024 ARM Limited, All Rights Reserved.
+ */
+
+#ifndef _IRQ_GIC_ITS_MSI_PARENT_H
+#define _IRQ_GIC_ITS_MSI_PARENT_H
+
+extern const struct msi_parent_ops gic_v3_its_msi_parent_ops;
+
+#endif /* _IRQ_GIC_ITS_MSI_PARENT_H */
diff --git a/drivers/irqchip/irq-gic-v3-its-msi-parent.c b/drivers/irqchip/irq-gic-v3-its-msi-parent.c
deleted file mode 100644 (file)
index a5e110f..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-// Copyright (C) 2013-2015 ARM Limited, All Rights Reserved.
-// Author: Marc Zyngier <marc.zyngier@arm.com>
-// Copyright (C) 2022 Linutronix GmbH
-// Copyright (C) 2022 Intel
-
-#include <linux/acpi_iort.h>
-#include <linux/pci.h>
-
-#include "irq-gic-common.h"
-#include <linux/irqchip/irq-msi-lib.h>
-
-#define ITS_MSI_FLAGS_REQUIRED  (MSI_FLAG_USE_DEF_DOM_OPS |    \
-                                MSI_FLAG_USE_DEF_CHIP_OPS |    \
-                                MSI_FLAG_PCI_MSI_MASK_PARENT)
-
-#define ITS_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK |      \
-                                MSI_FLAG_PCI_MSIX      |       \
-                                MSI_FLAG_MULTI_PCI_MSI)
-
-#ifdef CONFIG_PCI_MSI
-static int its_pci_msi_vec_count(struct pci_dev *pdev, void *data)
-{
-       int msi, msix, *count = data;
-
-       msi = max(pci_msi_vec_count(pdev), 0);
-       msix = max(pci_msix_vec_count(pdev), 0);
-       *count += max(msi, msix);
-
-       return 0;
-}
-
-static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
-{
-       struct pci_dev **alias_dev = data;
-
-       *alias_dev = pdev;
-
-       return 0;
-}
-
-static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
-                              int nvec, msi_alloc_info_t *info)
-{
-       struct pci_dev *pdev, *alias_dev;
-       struct msi_domain_info *msi_info;
-       int alias_count = 0, minnvec = 1;
-
-       if (!dev_is_pci(dev))
-               return -EINVAL;
-
-       pdev = to_pci_dev(dev);
-       /*
-        * If pdev is downstream of any aliasing bridges, take an upper
-        * bound of how many other vectors could map to the same DevID.
-        * Also tell the ITS that the signalling will come from a proxy
-        * device, and that special allocation rules apply.
-        */
-       pci_for_each_dma_alias(pdev, its_get_pci_alias, &alias_dev);
-       if (alias_dev != pdev) {
-               if (alias_dev->subordinate)
-                       pci_walk_bus(alias_dev->subordinate,
-                                    its_pci_msi_vec_count, &alias_count);
-               info->flags |= MSI_ALLOC_FLAGS_PROXY_DEVICE;
-       }
-
-       /* ITS specific DeviceID, as the core ITS ignores dev. */
-       info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain->parent, pdev);
-
-       /*
-        * Always allocate a power of 2, and special case device 0 for
-        * broken systems where the DevID is not wired (and all devices
-        * appear as DevID 0). For that reason, we generously allocate a
-        * minimum of 32 MSIs for DevID 0. If you want more because all
-        * your devices are aliasing to DevID 0, consider fixing your HW.
-        */
-       nvec = max(nvec, alias_count);
-       if (!info->scratchpad[0].ul)
-               minnvec = 32;
-       nvec = max_t(int, minnvec, roundup_pow_of_two(nvec));
-
-       msi_info = msi_get_domain_info(domain->parent);
-       return msi_info->ops->msi_prepare(domain->parent, dev, nvec, info);
-}
-#else /* CONFIG_PCI_MSI */
-#define its_pci_msi_prepare    NULL
-#endif /* !CONFIG_PCI_MSI */
-
-static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev,
-                                 u32 *dev_id)
-{
-       int ret, index = 0;
-
-       /* Suck the DeviceID out of the msi-parent property */
-       do {
-               struct of_phandle_args args;
-
-               ret = of_parse_phandle_with_args(dev->of_node,
-                                                "msi-parent", "#msi-cells",
-                                                index, &args);
-               if (args.np == irq_domain_get_of_node(domain)) {
-                       if (WARN_ON(args.args_count != 1))
-                               return -EINVAL;
-                       *dev_id = args.args[0];
-                       break;
-               }
-               index++;
-       } while (!ret);
-
-       if (ret) {
-               struct device_node *np = NULL;
-
-               ret = of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &np, dev_id);
-               if (np)
-                       of_node_put(np);
-       }
-
-       return ret;
-}
-
-int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
-{
-       return -1;
-}
-
-static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
-                           int nvec, msi_alloc_info_t *info)
-{
-       struct msi_domain_info *msi_info;
-       u32 dev_id;
-       int ret;
-
-       if (dev->of_node)
-               ret = of_pmsi_get_dev_id(domain->parent, dev, &dev_id);
-       else
-               ret = iort_pmsi_get_dev_id(dev, &dev_id);
-       if (ret)
-               return ret;
-
-       /* ITS specific DeviceID, as the core ITS ignores dev. */
-       info->scratchpad[0].ul = dev_id;
-
-       /* Allocate at least 32 MSIs, and always as a power of 2 */
-       nvec = max_t(int, 32, roundup_pow_of_two(nvec));
-
-       msi_info = msi_get_domain_info(domain->parent);
-       return msi_info->ops->msi_prepare(domain->parent,
-                                         dev, nvec, info);
-}
-
-static void its_msi_teardown(struct irq_domain *domain, msi_alloc_info_t *info)
-{
-       struct msi_domain_info *msi_info;
-
-       msi_info = msi_get_domain_info(domain->parent);
-       msi_info->ops->msi_teardown(domain->parent, info);
-}
-
-static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
-                                 struct irq_domain *real_parent, struct msi_domain_info *info)
-{
-       if (!msi_lib_init_dev_msi_info(dev, domain, real_parent, info))
-               return false;
-
-       switch(info->bus_token) {
-       case DOMAIN_BUS_PCI_DEVICE_MSI:
-       case DOMAIN_BUS_PCI_DEVICE_MSIX:
-               /*
-                * FIXME: This probably should be done after a (not yet
-                * existing) post domain creation callback once to make
-                * support for dynamic post-enable MSI-X allocations
-                * work without having to reevaluate the domain size
-                * over and over. It is known already at allocation
-                * time via info->hwsize.
-                *
-                * That should work perfectly fine for MSI/MSI-X but needs
-                * some thoughts for purely software managed MSI domains
-                * where the index space is only limited artificially via
-                * %MSI_MAX_INDEX.
-                */
-               info->ops->msi_prepare = its_pci_msi_prepare;
-               info->ops->msi_teardown = its_msi_teardown;
-               break;
-       case DOMAIN_BUS_DEVICE_MSI:
-       case DOMAIN_BUS_WIRED_TO_MSI:
-               /*
-                * FIXME: See the above PCI prepare comment. The domain
-                * size is also known at domain creation time.
-                */
-               info->ops->msi_prepare = its_pmsi_prepare;
-               info->ops->msi_teardown = its_msi_teardown;
-               break;
-       default:
-               /* Confused. How did the lib return true? */
-               WARN_ON_ONCE(1);
-               return false;
-       }
-
-       return true;
-}
-
-const struct msi_parent_ops gic_v3_its_msi_parent_ops = {
-       .supported_flags        = ITS_MSI_FLAGS_SUPPORTED,
-       .required_flags         = ITS_MSI_FLAGS_REQUIRED,
-       .chip_flags             = MSI_CHIP_FLAG_SET_EOI,
-       .bus_select_token       = DOMAIN_BUS_NEXUS,
-       .bus_select_mask        = MATCH_PCI_MSI | MATCH_PLATFORM_MSI,
-       .prefix                 = "ITS-",
-       .init_dev_msi_info      = its_init_dev_msi_info,
-};
index d54fa0638dc442efd7f3597ecf96529f9c17ce17..467cb78435a9b64423cecad982ad1cc69482a737 100644 (file)
@@ -41,6 +41,7 @@
 #include <asm/exception.h>
 
 #include "irq-gic-common.h"
+#include "irq-gic-its-msi-parent.h"
 #include <linux/irqchip/irq-msi-lib.h>
 
 #define ITS_FLAGS_CMDQ_NEEDS_FLUSHING          (1ULL << 0)