2024-06-14 10:25:27

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 00/24] genirq, irqchip: Convert ARM MSI handling to

Hi!

This is version 3 of the series to convert ARM MSI handling over to
per device MSI domains. Version 2 can be found here:

https://lore.kernel.org/lkml/[email protected]/

The conversion aims to replace the existing platform MSI mechanism
and enables ARM to support the future PCI/IMS mechanism.

The infrastructure to replace the platform MSI mechanism is already
upstream and in use by RISC-V and has been tested on various ARM
platforms during the V2 development.

Changes vs. V2:

- Replace the static key to control [un]masking the parent chip
from pci_msi_[un]mask() with a domain feature flag so it can be
decided per MSI parent domain - Marc

- Forwarded to v6.10-rc3

- Minor cleanups and polishing according to review feedback

The series is only lightly tested due to lack of hardware, so we ask
the people who have access to affected machines to help with testing.

The series is also available from git:

git://git.kernel.org/pub/scm/linux/kernel/git/anna-maria/linux-devel.git devmsi-arm

Thanks,
Shiva

Anna-Maria Behnsen (2):
irqdomain: Fix formatting irq_find_matching_fwspec() kerneldoc comment
irqchip/imx-mu-msi: Fix codingstyle in imx_mu_msi_domains_init()

Shivamurthy Shastri (1):
PCI/MSI: Provide MSI_FLAG_PCI_MSI_MASK_PARENT

Thomas Gleixner (21):
irqchip: Provide irq-msi-lib
irqchip/gic-v3-its: Provide MSI parent infrastructure
irqchip/irq-msi-lib: Prepare for PCI MSI/MSIX
irqchip/gic-v3-its: Provide MSI parent for PCI/MSI[-X]
irqchip/irq-msi-lib: Prepare for DEVICE MSI to replace platform MSI
irqchip/mbigen: Prepare for real per device MSI
irqchip/irq-msi-lib: Prepare for DOMAIN_BUS_WIRED_TO_MSI
irqchip/gic-v3-its: Switch platform MSI to MSI parent
irqchip/mbigen: Remove platform_msi_create_device_domain() fallback
genirq/msi: Remove platform_msi_create_device_domain()
genirq/gic-v3-mbi: Remove unused wired MSI mechanics
genirq/gic-v3-mbi: Switch to MSI parent
irqchip/gic-v2m: Switch to device MSI
irqchip/imx-mu-msi: Switch to MSI parent
irqchip/irq-mvebu-icu: Prepare for real per device MSI
irqchip/mvebu-gicp: Switch to MSI parent
irqchip/mvebu-odmi: Switch to parent MSI
irqchip/irq-mvebu-sei: Switch to MSI parent
irqchip/irq-mvebu-icu: Remove platform MSI leftovers
genirq/msi: Remove platform MSI leftovers
genirq/msi: Move msi_device_data to core

drivers/base/platform-msi.c | 350 +-----------------
drivers/irqchip/Kconfig | 8 +
drivers/irqchip/Makefile | 4 +-
drivers/irqchip/irq-gic-common.h | 3 +
drivers/irqchip/irq-gic-v2m.c | 80 ++--
drivers/irqchip/irq-gic-v3-its-msi-parent.c | 210 +++++++++++
drivers/irqchip/irq-gic-v3-its-pci-msi.c | 202 ----------
drivers/irqchip/irq-gic-v3-its-platform-msi.c | 163 --------
drivers/irqchip/irq-gic-v3-its.c | 5 +
drivers/irqchip/irq-gic-v3-mbi.c | 142 ++-----
drivers/irqchip/irq-imx-mu-msi.c | 55 ++-
drivers/irqchip/irq-mbigen.c | 96 ++---
drivers/irqchip/irq-msi-lib.c | 135 +++++++
drivers/irqchip/irq-msi-lib.h | 27 ++
drivers/irqchip/irq-mvebu-gicp.c | 45 +--
drivers/irqchip/irq-mvebu-icu.c | 275 ++++++--------
drivers/irqchip/irq-mvebu-odmi.c | 38 +-
drivers/irqchip/irq-mvebu-sei.c | 53 ++-
drivers/pci/msi/irqdomain.c | 21 ++
include/linux/msi.h | 52 +--
kernel/irq/irqdomain.c | 3 +-
kernel/irq/msi.c | 95 +----
22 files changed, 734 insertions(+), 1328 deletions(-)
create mode 100644 drivers/irqchip/irq-gic-v3-its-msi-parent.c
delete mode 100644 drivers/irqchip/irq-gic-v3-its-pci-msi.c
delete mode 100644 drivers/irqchip/irq-gic-v3-its-platform-msi.c
create mode 100644 drivers/irqchip/irq-msi-lib.c
create mode 100644 drivers/irqchip/irq-msi-lib.h

--
2.34.1



2024-06-14 10:25:37

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 01/24] irqdomain: Fix formatting irq_find_matching_fwspec() kerneldoc comment

From: Anna-Maria Behnsen <[email protected]>

Modify the comment formatting in irq_find_matching_fwspec function to
enhance code readability and maintain consistency.

Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
---
kernel/irq/irqdomain.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index aadc8891cc16..8475b83c5519 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -438,7 +438,8 @@ struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec,
struct fwnode_handle *fwnode = fwspec->fwnode;
int rc;

- /* We might want to match the legacy controller last since
+ /*
+ * We might want to match the legacy controller last since
* it might potentially be set to match all interrupts in
* the absence of a device node. This isn't a problem so far
* yet though...
--
2.34.1


2024-06-14 10:25:49

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 02/24] irqchip/imx-mu-msi: Fix codingstyle in imx_mu_msi_domains_init()

From: Anna-Maria Behnsen <[email protected]>

Fixes the coding style of irq_domain_create_linear() call within
imx_mu_msi_domains_init() for better code readability.

Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
Cc: Marc Zyngier <[email protected]>
Cc: Shawn Guo <[email protected]>
Cc: Sascha Hauer <[email protected]>
Cc: Fabio Estevam <[email protected]>
---
drivers/irqchip/irq-imx-mu-msi.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/irqchip/irq-imx-mu-msi.c b/drivers/irqchip/irq-imx-mu-msi.c
index 90d41c1407ac..1dceda044db9 100644
--- a/drivers/irqchip/irq-imx-mu-msi.c
+++ b/drivers/irqchip/irq-imx-mu-msi.c
@@ -222,10 +222,8 @@ static int imx_mu_msi_domains_init(struct imx_mu_msi *msi_data, struct device *d
struct irq_domain *parent;

/* Initialize MSI domain parent */
- parent = irq_domain_create_linear(fwnodes,
- IMX_MU_CHANS,
- &imx_mu_msi_domain_ops,
- msi_data);
+ parent = irq_domain_create_linear(fwnodes, IMX_MU_CHANS,
+ &imx_mu_msi_domain_ops, msi_data);
if (!parent) {
dev_err(dev, "failed to create IRQ domain\n");
return -ENOMEM;
--
2.34.1


2024-06-14 10:26:59

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 07/24] irqchip/gic-v3-its: Provide MSI parent for PCI/MSI[-X]

From: Thomas Gleixner <[email protected]>

The its_pci_msi_prepare() function from the ITS-PCI/MSI code provides the
'global' PCI/MSI domains. Move this function to the ITS-MSI parent code and
amend the function to use the domain hardware size, which is the MSI[X]
vector count, for allocating the ITS slots for the PCI device.

Enable PCI matching in msi_parent_ops and provide the necessary update to
the ITS specific child domain initialization function so that the prepare
callback gets invoked on allocations.

The latter might be optimized to do the allocation right at the point where
the child domain is initialized, but keep it simple for now.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Marc Zyngier <[email protected]>
---
v3: adopted to IMS->MSI rename
---
drivers/irqchip/Makefile | 1 -
drivers/irqchip/irq-gic-v3-its-msi-parent.c | 108 +++++++++++
drivers/irqchip/irq-gic-v3-its-pci-msi.c | 202 --------------------
3 files changed, 108 insertions(+), 203 deletions(-)
delete mode 100644 drivers/irqchip/irq-gic-v3-its-pci-msi.c

diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 6e4f7715206d..c5316634637f 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -33,7 +33,6 @@ 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-v3-its-platform-msi.o irq-gic-v4.o irq-gic-v3-its-msi-parent.o
-obj-$(CONFIG_ARM_GIC_V3_ITS_PCI) += irq-gic-v3-its-pci-msi.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_HISILICON_IRQ_MBIGEN) += irq-mbigen.o
diff --git a/drivers/irqchip/irq-gic-v3-its-msi-parent.c b/drivers/irqchip/irq-gic-v3-its-msi-parent.c
index cdc0844229b5..e81fefa428e2 100644
--- a/drivers/irqchip/irq-gic-v3-its-msi-parent.c
+++ b/drivers/irqchip/irq-gic-v3-its-msi-parent.c
@@ -1,7 +1,11 @@
// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2013-2015 ARM Limited, All Rights Reserved.
+// Author: Marc Zyngier <[email protected]>
// Copyright (C) 2022 Linutronix GmbH
// Copyright (C) 2022 Intel

+#include <linux/pci.h>
+
#include "irq-gic-common.h"
#include "irq-msi-lib.h"

@@ -13,12 +17,115 @@
MSI_FLAG_MULTI_PCI_MSI | \
MSI_FLAG_PCI_MSI_MASK_PARENT)

+#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, pdev);
+
+ /*
+ * @domain->msi_domain_info->hwsize contains the size of the
+ * MSI[-X] domain, but vector allocation happens one by one. This
+ * needs some thought when MSI comes into play as the size of MSI
+ * might be unknown at domain creation time and therefore set to
+ * MSI_MAX_INDEX.
+ */
+ msi_info = msi_get_domain_info(domain);
+ if (msi_info->hwsize > nvec)
+ nvec = msi_info->hwsize;
+
+ /*
+ * 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 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;
+ break;
+ default:
+ /* Confused. How did the lib return true? */
+ WARN_ON_ONCE(1);
+ return false;
+ }
+
return true;
}

@@ -26,6 +133,7 @@ const struct msi_parent_ops gic_v3_its_msi_parent_ops = {
.supported_flags = ITS_MSI_FLAGS_SUPPORTED,
.required_flags = ITS_MSI_FLAGS_REQUIRED,
.bus_select_token = DOMAIN_BUS_NEXUS,
+ .bus_select_mask = MATCH_PCI_MSI,
.prefix = "ITS-",
.init_dev_msi_info = its_init_dev_msi_info,
};
diff --git a/drivers/irqchip/irq-gic-v3-its-pci-msi.c b/drivers/irqchip/irq-gic-v3-its-pci-msi.c
deleted file mode 100644
index 93f77a8196da..000000000000
--- a/drivers/irqchip/irq-gic-v3-its-pci-msi.c
+++ /dev/null
@@ -1,202 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2013-2015 ARM Limited, All Rights Reserved.
- * Author: Marc Zyngier <[email protected]>
- */
-
-#include <linux/acpi_iort.h>
-#include <linux/pci.h>
-#include <linux/msi.h>
-#include <linux/of.h>
-#include <linux/of_irq.h>
-#include <linux/of_pci.h>
-
-static void its_mask_msi_irq(struct irq_data *d)
-{
- pci_msi_mask_irq(d);
- irq_chip_mask_parent(d);
-}
-
-static void its_unmask_msi_irq(struct irq_data *d)
-{
- pci_msi_unmask_irq(d);
- irq_chip_unmask_parent(d);
-}
-
-static struct irq_chip its_msi_irq_chip = {
- .name = "ITS-MSI",
- .irq_unmask = its_unmask_msi_irq,
- .irq_mask = its_mask_msi_irq,
- .irq_eoi = irq_chip_eoi_parent,
-};
-
-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;
-
- msi_info = msi_get_domain_info(domain->parent);
-
- 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, 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));
- return msi_info->ops->msi_prepare(domain->parent, dev, nvec, info);
-}
-
-static struct msi_domain_ops its_pci_msi_ops = {
- .msi_prepare = its_pci_msi_prepare,
-};
-
-static struct msi_domain_info its_pci_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 = &its_pci_msi_ops,
- .chip = &its_msi_irq_chip,
-};
-
-static struct of_device_id its_device_id[] = {
- { .compatible = "arm,gic-v3-its", },
- {},
-};
-
-static int __init its_pci_msi_init_one(struct fwnode_handle *handle,
- const char *name)
-{
- struct irq_domain *parent;
-
- parent = irq_find_matching_fwnode(handle, DOMAIN_BUS_NEXUS);
- if (!parent || !msi_get_domain_info(parent)) {
- pr_err("%s: Unable to locate ITS domain\n", name);
- return -ENXIO;
- }
-
- if (!pci_msi_create_irq_domain(handle, &its_pci_msi_domain_info,
- parent)) {
- pr_err("%s: Unable to create PCI domain\n", name);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static int __init its_pci_of_msi_init(void)
-{
- struct device_node *np;
-
- for (np = of_find_matching_node(NULL, its_device_id); np;
- np = of_find_matching_node(np, its_device_id)) {
- if (!of_device_is_available(np))
- continue;
- if (!of_property_read_bool(np, "msi-controller"))
- continue;
-
- if (its_pci_msi_init_one(of_node_to_fwnode(np), np->full_name))
- continue;
-
- pr_info("PCI/MSI: %pOF domain created\n", np);
- }
-
- return 0;
-}
-
-#ifdef CONFIG_ACPI
-
-static int __init
-its_pci_msi_parse_madt(union acpi_subtable_headers *header,
- const unsigned long end)
-{
- struct acpi_madt_generic_translator *its_entry;
- struct fwnode_handle *dom_handle;
- const char *node_name;
- int err = -ENXIO;
-
- its_entry = (struct acpi_madt_generic_translator *)header;
- node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx",
- (long)its_entry->base_address);
- dom_handle = iort_find_domain_token(its_entry->translation_id);
- if (!dom_handle) {
- pr_err("%s: Unable to locate ITS domain handle\n", node_name);
- goto out;
- }
-
- err = its_pci_msi_init_one(dom_handle, node_name);
- if (!err)
- pr_info("PCI/MSI: %s domain created\n", node_name);
-
-out:
- kfree(node_name);
- return err;
-}
-
-static int __init its_pci_acpi_msi_init(void)
-{
- acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
- its_pci_msi_parse_madt, 0);
- return 0;
-}
-#else
-static int __init its_pci_acpi_msi_init(void)
-{
- return 0;
-}
-#endif
-
-static int __init its_pci_msi_init(void)
-{
- its_pci_of_msi_init();
- its_pci_acpi_msi_init();
-
- return 0;
-}
-early_initcall(its_pci_msi_init);
--
2.34.1


2024-06-14 10:28:02

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 11/24] irqchip/gic-v3-its: Switch platform MSI to MSI parent

From: Thomas Gleixner <[email protected]>

Similar to the previous conversion of the PCI/MSI support lift the
prepare() callback from the existing platform MSI code and enable
platform MSI and the related device domain bus tokens in select
and the child domain initialization code.

All platform MSI users are automatically using the new per device MSI model
now.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
---
v3: adopted to IMS->MSI rename
---
drivers/irqchip/Makefile | 2 +-
drivers/irqchip/irq-gic-v3-its-msi-parent.c | 73 +++++++-
drivers/irqchip/irq-gic-v3-its-platform-msi.c | 163 ------------------
3 files changed, 73 insertions(+), 165 deletions(-)
delete mode 100644 drivers/irqchip/irq-gic-v3-its-platform-msi.c

diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index c5316634637f..afc44f4709a6 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -32,7 +32,7 @@ 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-v3-its-platform-msi.o irq-gic-v4.o irq-gic-v3-its-msi-parent.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_V3_ITS_FSL_MC) += irq-gic-v3-its-fsl-mc-msi.o
obj-$(CONFIG_PARTITION_PERCPU) += irq-partition-percpu.o
obj-$(CONFIG_HISILICON_IRQ_MBIGEN) += irq-mbigen.o
diff --git a/drivers/irqchip/irq-gic-v3-its-msi-parent.c b/drivers/irqchip/irq-gic-v3-its-msi-parent.c
index e81fefa428e2..21daa452ffa6 100644
--- a/drivers/irqchip/irq-gic-v3-its-msi-parent.c
+++ b/drivers/irqchip/irq-gic-v3-its-msi-parent.c
@@ -4,6 +4,7 @@
// Copyright (C) 2022 Linutronix GmbH
// Copyright (C) 2022 Intel

+#include <linux/acpi_iort.h>
#include <linux/pci.h>

#include "irq-gic-common.h"
@@ -96,6 +97,68 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
#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);
+
+ 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, 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;
+
+ /*
+ * @domain->msi_domain_info->hwsize contains the size of the device
+ * domain, but vector allocation happens one by one.
+ */
+ msi_info = msi_get_domain_info(domain);
+ if (msi_info->hwsize > nvec)
+ nvec = msi_info->hwsize;
+
+ /* 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 bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
struct irq_domain *real_parent, struct msi_domain_info *info)
{
@@ -120,6 +183,14 @@ static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
*/
info->ops->msi_prepare = its_pci_msi_prepare;
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;
+ break;
default:
/* Confused. How did the lib return true? */
WARN_ON_ONCE(1);
@@ -133,7 +204,7 @@ const struct msi_parent_ops gic_v3_its_msi_parent_ops = {
.supported_flags = ITS_MSI_FLAGS_SUPPORTED,
.required_flags = ITS_MSI_FLAGS_REQUIRED,
.bus_select_token = DOMAIN_BUS_NEXUS,
- .bus_select_mask = MATCH_PCI_MSI,
+ .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-v3-its-platform-msi.c b/drivers/irqchip/irq-gic-v3-its-platform-msi.c
deleted file mode 100644
index daa6d5053bc3..000000000000
--- a/drivers/irqchip/irq-gic-v3-its-platform-msi.c
+++ /dev/null
@@ -1,163 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2013-2015 ARM Limited, All Rights Reserved.
- * Author: Marc Zyngier <[email protected]>
- */
-
-#include <linux/acpi_iort.h>
-#include <linux/device.h>
-#include <linux/msi.h>
-#include <linux/of.h>
-#include <linux/of_irq.h>
-
-static struct irq_chip its_pmsi_irq_chip = {
- .name = "ITS-pMSI",
-};
-
-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);
-
- 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;
-
- msi_info = msi_get_domain_info(domain->parent);
-
- if (dev->of_node)
- ret = of_pmsi_get_dev_id(domain, 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));
- return msi_info->ops->msi_prepare(domain->parent,
- dev, nvec, info);
-}
-
-static struct msi_domain_ops its_pmsi_ops = {
- .msi_prepare = its_pmsi_prepare,
-};
-
-static struct msi_domain_info its_pmsi_domain_info = {
- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
- .ops = &its_pmsi_ops,
- .chip = &its_pmsi_irq_chip,
-};
-
-static const struct of_device_id its_device_id[] = {
- { .compatible = "arm,gic-v3-its", },
- {},
-};
-
-static int __init its_pmsi_init_one(struct fwnode_handle *fwnode,
- const char *name)
-{
- struct irq_domain *parent;
-
- parent = irq_find_matching_fwnode(fwnode, DOMAIN_BUS_NEXUS);
- if (!parent || !msi_get_domain_info(parent)) {
- pr_err("%s: unable to locate ITS domain\n", name);
- return -ENXIO;
- }
-
- if (!platform_msi_create_irq_domain(fwnode, &its_pmsi_domain_info,
- parent)) {
- pr_err("%s: unable to create platform domain\n", name);
- return -ENXIO;
- }
-
- pr_info("Platform MSI: %s domain created\n", name);
- return 0;
-}
-
-#ifdef CONFIG_ACPI
-static int __init
-its_pmsi_parse_madt(union acpi_subtable_headers *header,
- const unsigned long end)
-{
- struct acpi_madt_generic_translator *its_entry;
- struct fwnode_handle *domain_handle;
- const char *node_name;
- int err = -ENXIO;
-
- its_entry = (struct acpi_madt_generic_translator *)header;
- node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx",
- (long)its_entry->base_address);
- domain_handle = iort_find_domain_token(its_entry->translation_id);
- if (!domain_handle) {
- pr_err("%s: Unable to locate ITS domain handle\n", node_name);
- goto out;
- }
-
- err = its_pmsi_init_one(domain_handle, node_name);
-
-out:
- kfree(node_name);
- return err;
-}
-
-static void __init its_pmsi_acpi_init(void)
-{
- acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
- its_pmsi_parse_madt, 0);
-}
-#else
-static inline void its_pmsi_acpi_init(void) { }
-#endif
-
-static void __init its_pmsi_of_init(void)
-{
- struct device_node *np;
-
- for (np = of_find_matching_node(NULL, its_device_id); np;
- np = of_find_matching_node(np, its_device_id)) {
- if (!of_device_is_available(np))
- continue;
- if (!of_property_read_bool(np, "msi-controller"))
- continue;
-
- its_pmsi_init_one(of_node_to_fwnode(np), np->full_name);
- }
-}
-
-static int __init its_pmsi_init(void)
-{
- its_pmsi_of_init();
- its_pmsi_acpi_init();
- return 0;
-}
-early_initcall(its_pmsi_init);
--
2.34.1


2024-06-14 10:28:13

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 12/24] irqchip/mbigen: Remove platform_msi_create_device_domain() fallback

From: Thomas Gleixner <[email protected]>

Now that ITS provides the MSI parent domain, remove the unused fallback
code.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
---
drivers/irqchip/irq-mbigen.c | 74 ++----------------------------------
1 file changed, 4 insertions(+), 70 deletions(-)

diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c
index db0fa80330d9..093fd42893a7 100644
--- a/drivers/irqchip/irq-mbigen.c
+++ b/drivers/irqchip/irq-mbigen.c
@@ -180,64 +180,6 @@ static int mbigen_domain_translate(struct irq_domain *d, struct irq_fwspec *fwsp
return -EINVAL;
}

-/* The following section will go away once ITS provides a MSI parent */
-
-static struct irq_chip mbigen_irq_chip = {
- .name = "mbigen-v2",
- .irq_mask = irq_chip_mask_parent,
- .irq_unmask = irq_chip_unmask_parent,
- .irq_eoi = mbigen_eoi_irq,
- .irq_set_type = mbigen_set_type,
- .irq_set_affinity = irq_chip_set_affinity_parent,
-};
-
-static int mbigen_irq_domain_alloc(struct irq_domain *domain,
- unsigned int virq,
- unsigned int nr_irqs,
- void *args)
-{
- struct irq_fwspec *fwspec = args;
- irq_hw_number_t hwirq;
- unsigned int type;
- struct mbigen_device *mgn_chip;
- int i, err;
-
- err = mbigen_domain_translate(domain, fwspec, &hwirq, &type);
- if (err)
- return err;
-
- err = platform_msi_device_domain_alloc(domain, virq, nr_irqs);
- if (err)
- return err;
-
- mgn_chip = platform_msi_get_host_data(domain);
-
- for (i = 0; i < nr_irqs; i++)
- irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
- &mbigen_irq_chip, mgn_chip->base);
-
- return 0;
-}
-
-static void mbigen_irq_domain_free(struct irq_domain *domain, unsigned int virq,
- unsigned int nr_irqs)
-{
- platform_msi_device_domain_free(domain, virq, nr_irqs);
-}
-
-static const struct irq_domain_ops mbigen_domain_ops = {
- .translate = mbigen_domain_translate,
- .alloc = mbigen_irq_domain_alloc,
- .free = mbigen_irq_domain_free,
-};
-
-static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg)
-{
- mbigen_write_msi_msg(irq_get_irq_data(desc->irq), msg);
-}
-
-/* End of to be removed section */
-
static void mbigen_domain_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
{
arg->desc = desc;
@@ -268,20 +210,12 @@ static const struct msi_domain_template mbigen_msi_template = {
static bool mbigen_create_device_domain(struct device *dev, unsigned int size,
struct mbigen_device *mgn_chip)
{
- struct irq_domain *domain = dev->msi.domain;
-
- if (WARN_ON_ONCE(!domain))
+ if (WARN_ON_ONCE(!dev->msi.domain))
return false;

- if (irq_domain_is_msi_parent(domain)) {
- return msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN,
- &mbigen_msi_template, size,
- NULL, mgn_chip->base);
- }
-
- /* Remove once ITS provides MSI parent */
- return !!platform_msi_create_device_domain(dev, size, mbigen_write_msg,
- &mbigen_domain_ops, mgn_chip);
+ return msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN,
+ &mbigen_msi_template, size,
+ NULL, mgn_chip->base);
}

static int mbigen_of_create_domain(struct platform_device *pdev,
--
2.34.1


2024-06-14 10:29:50

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 16/24] irqchip/gic-v2m: Switch to device MSI

From: Thomas Gleixner <[email protected]>

All platform MSI users and the PCI/MSI code handle per device MSI domains
when the irqdomain associated to the device provides MSI parent
functionality.

Remove the "global" PCI/MSI and platform domain related code and provide
the MSI parent functionality by filling in msi_parent_ops.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Marc Zyngier <[email protected]>
---
v3: enabled MSI_FLAG_PCI_MSI_MASK_PARENT in msi_parent_ops::supported_flags
---
drivers/irqchip/Kconfig | 1 +
drivers/irqchip/irq-gic-v2m.c | 80 +++++++++++------------------------
2 files changed, 25 insertions(+), 56 deletions(-)

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index b51863fa9b38..2104b8727b1a 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -26,6 +26,7 @@ config ARM_GIC_V2M
bool
depends on PCI
select ARM_GIC
+ select IRQ_MSI_LIB
select PCI_MSI

config GIC_NON_BANKED
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c
index f2ff4387870d..428132aa26cc 100644
--- a/drivers/irqchip/irq-gic-v2m.c
+++ b/drivers/irqchip/irq-gic-v2m.c
@@ -26,6 +26,8 @@
#include <linux/irqchip/arm-gic.h>
#include <linux/irqchip/arm-gic-common.h>

+#include "irq-msi-lib.h"
+
/*
* MSI_TYPER:
* [31:26] Reserved
@@ -72,31 +74,6 @@ struct v2m_data {
u32 flags; /* v2m flags for specific implementation */
};

-static void gicv2m_mask_msi_irq(struct irq_data *d)
-{
- pci_msi_mask_irq(d);
- irq_chip_mask_parent(d);
-}
-
-static void gicv2m_unmask_msi_irq(struct irq_data *d)
-{
- pci_msi_unmask_irq(d);
- irq_chip_unmask_parent(d);
-}
-
-static struct irq_chip gicv2m_msi_irq_chip = {
- .name = "MSI",
- .irq_mask = gicv2m_mask_msi_irq,
- .irq_unmask = gicv2m_unmask_msi_irq,
- .irq_eoi = irq_chip_eoi_parent,
-};
-
-static struct msi_domain_info gicv2m_msi_domain_info = {
- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
- MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI),
- .chip = &gicv2m_msi_irq_chip,
-};
-
static phys_addr_t gicv2m_get_msi_addr(struct v2m_data *v2m, int hwirq)
{
if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)
@@ -230,6 +207,7 @@ static void gicv2m_irq_domain_free(struct irq_domain *domain,
}

static const struct irq_domain_ops gicv2m_domain_ops = {
+ .select = msi_lib_irq_domain_select,
.alloc = gicv2m_irq_domain_alloc,
.free = gicv2m_irq_domain_free,
};
@@ -250,19 +228,6 @@ static bool is_msi_spi_valid(u32 base, u32 num)
return true;
}

-static struct irq_chip gicv2m_pmsi_irq_chip = {
- .name = "pMSI",
-};
-
-static struct msi_domain_ops gicv2m_pmsi_ops = {
-};
-
-static struct msi_domain_info gicv2m_pmsi_domain_info = {
- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
- .ops = &gicv2m_pmsi_ops,
- .chip = &gicv2m_pmsi_irq_chip,
-};
-
static void __init gicv2m_teardown(void)
{
struct v2m_data *v2m, *tmp;
@@ -278,9 +243,26 @@ static void __init gicv2m_teardown(void)
}
}

+#define GICV2M_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
+ MSI_FLAG_USE_DEF_CHIP_OPS)
+
+#define GICV2M_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \
+ MSI_FLAG_PCI_MSIX | \
+ MSI_FLAG_MULTI_PCI_MSI | \
+ MSI_FLAG_PCI_MSI_MASK_PARENT)
+
+static struct msi_parent_ops gicv2m_msi_parent_ops = {
+ .supported_flags = GICV2M_MSI_FLAGS_SUPPORTED,
+ .required_flags = GICV2M_MSI_FLAGS_REQUIRED,
+ .bus_select_token = DOMAIN_BUS_NEXUS,
+ .bus_select_mask = MATCH_PCI_MSI | MATCH_PLATFORM_MSI,
+ .prefix = "GICv2m-",
+ .init_dev_msi_info = msi_lib_init_dev_msi_info,
+};
+
static __init int gicv2m_allocate_domains(struct irq_domain *parent)
{
- struct irq_domain *inner_domain, *pci_domain, *plat_domain;
+ struct irq_domain *inner_domain;
struct v2m_data *v2m;

v2m = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry);
@@ -295,22 +277,8 @@ static __init int gicv2m_allocate_domains(struct irq_domain *parent)
}

irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS);
- pci_domain = pci_msi_create_irq_domain(v2m->fwnode,
- &gicv2m_msi_domain_info,
- inner_domain);
- plat_domain = platform_msi_create_irq_domain(v2m->fwnode,
- &gicv2m_pmsi_domain_info,
- inner_domain);
- if (!pci_domain || !plat_domain) {
- pr_err("Failed to create MSI domains\n");
- if (plat_domain)
- irq_domain_remove(plat_domain);
- if (pci_domain)
- irq_domain_remove(pci_domain);
- irq_domain_remove(inner_domain);
- return -ENOMEM;
- }
-
+ inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
+ inner_domain->msi_parent_ops = &gicv2m_msi_parent_ops;
return 0;
}

@@ -511,7 +479,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header,
pr_info("applying Amazon Graviton quirk\n");
res.end = res.start + SZ_8K - 1;
flags |= GICV2M_GRAVITON_ADDRESS_ONLY;
- gicv2m_msi_domain_info.flags &= ~MSI_FLAG_MULTI_PCI_MSI;
+ gicv2m_msi_parent_ops.supported_flags &= ~MSI_FLAG_MULTI_PCI_MSI;
}

if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) {
--
2.34.1


2024-06-14 10:30:10

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 18/24] irqchip/irq-mvebu-icu: Prepare for real per device MSI

From: Thomas Gleixner <[email protected]>

The core infrastructure has everything in place to switch ICU to per
device MSI domains and avoid the convoluted construct of the existing
platform-MSI layering violation.

The new infrastructure provides a wired interrupt specific interface in the
MSI core which converts the 'hardware interrupt number + trigger type'
allocation which is required for wired interrupts in the regular irqdomain
code to a normal MSI allocation.

The hardware interrupt number and the trigger type are stored in the MSI
descriptor device cookie by the core code so the ICU specific code can
retrieve them.

The new per device domain is only instantiated when the irqdomain which is
associated to the ICU device provides MSI parent functionality. Up to
that point it invokes the existing code. Once the parent is converted the
code for the current platform-MSI mechanism is removed.

The new domain shares the interrupt chip callbacks and the translation
function. The only new functionality aside of filling out the
msi_domain_templates is a domain specific set_desc() callback, which will go
away once all platform-MSI code has been converted.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
Cc: Andrew Lunn <[email protected]>
Cc: Gregory Clement <[email protected]>
Cc: Sebastian Hesselbarth <[email protected]>
---
drivers/irqchip/irq-mvebu-icu.c | 181 ++++++++++++++++++++++++++++++--
1 file changed, 170 insertions(+), 11 deletions(-)

diff --git a/drivers/irqchip/irq-mvebu-icu.c b/drivers/irqchip/irq-mvebu-icu.c
index 3c77acc7ec6a..2a210cd9482e 100644
--- a/drivers/irqchip/irq-mvebu-icu.c
+++ b/drivers/irqchip/irq-mvebu-icu.c
@@ -20,6 +20,8 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>

+#include "irq-msi-lib.h"
+
#include <dt-bindings/interrupt-controller/mvebu-icu.h>

/* ICU registers */
@@ -60,14 +62,52 @@ struct mvebu_icu_msi_data {
const struct mvebu_icu_subset_data *subset_data;
};

-struct mvebu_icu_irq_data {
- struct mvebu_icu *icu;
- unsigned int icu_group;
- unsigned int type;
-};
-
static DEFINE_STATIC_KEY_FALSE(legacy_bindings);

+static int mvebu_icu_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
+ unsigned long *hwirq, unsigned int *type)
+{
+ unsigned int param_count = static_branch_unlikely(&legacy_bindings) ? 3 : 2;
+ struct mvebu_icu_msi_data *msi_data = d->host_data;
+ struct mvebu_icu *icu = msi_data->icu;
+
+ /* Check the count of the parameters in dt */
+ if (WARN_ON(fwspec->param_count != param_count)) {
+ dev_err(icu->dev, "wrong ICU parameter count %d\n",
+ fwspec->param_count);
+ return -EINVAL;
+ }
+
+ if (static_branch_unlikely(&legacy_bindings)) {
+ *hwirq = fwspec->param[1];
+ *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+ if (fwspec->param[0] != ICU_GRP_NSR) {
+ dev_err(icu->dev, "wrong ICU group type %x\n",
+ fwspec->param[0]);
+ return -EINVAL;
+ }
+ } else {
+ *hwirq = fwspec->param[0];
+ *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+
+ /*
+ * The ICU receives level interrupts. While the NSR are also
+ * level interrupts, SEI are edge interrupts. Force the type
+ * here in this case. Please note that this makes the interrupt
+ * handling unreliable.
+ */
+ if (msi_data->subset_data->icu_group == ICU_GRP_SEI)
+ *type = IRQ_TYPE_EDGE_RISING;
+ }
+
+ if (*hwirq >= ICU_MAX_IRQS) {
+ dev_err(icu->dev, "invalid interrupt number %ld\n", *hwirq);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static void mvebu_icu_init(struct mvebu_icu *icu,
struct mvebu_icu_msi_data *msi_data,
struct msi_msg *msg)
@@ -89,6 +129,14 @@ static void mvebu_icu_init(struct mvebu_icu *icu,
writel_relaxed(msg[1].address_lo, icu->base + subset->offset_clr_al);
}

+/* Start of area to be removed once all parent chips provide MSI parent */
+
+struct mvebu_icu_irq_data {
+ struct mvebu_icu *icu;
+ unsigned int icu_group;
+ unsigned int type;
+};
+
static void mvebu_icu_write_msg(struct msi_desc *desc, struct msi_msg *msg)
{
struct irq_data *d = irq_get_irq_data(desc->irq);
@@ -269,6 +317,109 @@ static const struct irq_domain_ops mvebu_icu_domain_ops = {
.free = mvebu_icu_irq_domain_free,
};

+/* End of removal area */
+
+static int mvebu_icu_msi_init(struct irq_domain *domain, struct msi_domain_info *info,
+ unsigned int virq, irq_hw_number_t hwirq, msi_alloc_info_t *arg)
+{
+ irq_domain_set_hwirq_and_chip(domain, virq, hwirq, info->chip, info->chip_data);
+ return irq_set_irqchip_state(virq, IRQCHIP_STATE_PENDING, false);
+}
+
+static void mvebu_icu_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
+{
+ arg->desc = desc;
+ arg->hwirq = (u32)desc->data.icookie.value;
+}
+
+static void mvebu_icu_write_msi_msg(struct irq_data *d, struct msi_msg *msg)
+{
+ struct mvebu_icu_msi_data *msi_data = d->chip_data;
+ unsigned int icu_group = msi_data->subset_data->icu_group;
+ struct msi_desc *desc = irq_data_get_msi_desc(d);
+ struct mvebu_icu *icu = msi_data->icu;
+ unsigned int type;
+ u32 icu_int;
+
+ if (msg->address_lo || msg->address_hi) {
+ /* One off initialization per domain */
+ mvebu_icu_init(icu, msi_data, msg);
+ /* Configure the ICU with irq number & type */
+ icu_int = msg->data | ICU_INT_ENABLE;
+ type = (unsigned int)(desc->data.icookie.value >> 32);
+ if (type & IRQ_TYPE_EDGE_RISING)
+ icu_int |= ICU_IS_EDGE;
+ icu_int |= icu_group << ICU_GROUP_SHIFT;
+ } else {
+ /* De-configure the ICU */
+ icu_int = 0;
+ }
+
+ writel_relaxed(icu_int, icu->base + ICU_INT_CFG(d->hwirq));
+
+ /*
+ * The SATA unit has 2 ports, and a dedicated ICU entry per
+ * port. The ahci sata driver supports only one irq interrupt
+ * per SATA unit. To solve this conflict, we configure the 2
+ * SATA wired interrupts in the south bridge into 1 GIC
+ * interrupt in the north bridge. Even if only a single port
+ * is enabled, if sata node is enabled, both interrupts are
+ * configured (regardless of which port is actually in use).
+ */
+ if (d->hwirq == ICU_SATA0_ICU_ID || d->hwirq == ICU_SATA1_ICU_ID) {
+ writel_relaxed(icu_int, icu->base + ICU_INT_CFG(ICU_SATA0_ICU_ID));
+ writel_relaxed(icu_int, icu->base + ICU_INT_CFG(ICU_SATA1_ICU_ID));
+ }
+}
+
+static const struct msi_domain_template mvebu_icu_nsr_msi_template = {
+ .chip = {
+ .name = "ICU-NSR",
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_set_type = irq_chip_set_type_parent,
+ .irq_write_msi_msg = mvebu_icu_write_msi_msg,
+ .flags = IRQCHIP_SUPPORTS_LEVEL_MSI,
+ },
+
+ .ops = {
+ .msi_translate = mvebu_icu_translate,
+ .msi_init = mvebu_icu_msi_init,
+ .set_desc = mvebu_icu_set_desc,
+ },
+
+ .info = {
+ .bus_token = DOMAIN_BUS_WIRED_TO_MSI,
+ .flags = MSI_FLAG_LEVEL_CAPABLE |
+ MSI_FLAG_USE_DEV_FWNODE,
+ },
+};
+
+static const struct msi_domain_template mvebu_icu_sei_msi_template = {
+ .chip = {
+ .name = "ICU-SEI",
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_ack = irq_chip_ack_parent,
+ .irq_set_type = irq_chip_set_type_parent,
+ .irq_write_msi_msg = mvebu_icu_write_msi_msg,
+ .flags = IRQCHIP_SUPPORTS_LEVEL_MSI,
+ },
+
+ .ops = {
+ .msi_translate = mvebu_icu_translate,
+ .msi_init = mvebu_icu_msi_init,
+ .set_desc = mvebu_icu_set_desc,
+ },
+
+ .info = {
+ .bus_token = DOMAIN_BUS_WIRED_TO_MSI,
+ .flags = MSI_FLAG_LEVEL_CAPABLE |
+ MSI_FLAG_USE_DEV_FWNODE,
+ },
+};
+
static const struct mvebu_icu_subset_data mvebu_icu_nsr_subset_data = {
.icu_group = ICU_GRP_NSR,
.offset_set_ah = ICU_SETSPI_NSR_AH,
@@ -298,7 +449,6 @@ static const struct of_device_id mvebu_icu_subset_of_match[] = {
static int mvebu_icu_subset_probe(struct platform_device *pdev)
{
struct mvebu_icu_msi_data *msi_data;
- struct device_node *msi_parent_dn;
struct device *dev = &pdev->dev;
struct irq_domain *irq_domain;

@@ -314,15 +464,24 @@ static int mvebu_icu_subset_probe(struct platform_device *pdev)
msi_data->subset_data = of_device_get_match_data(dev);
}

- dev->msi.domain = of_msi_get_domain(dev, dev->of_node,
- DOMAIN_BUS_PLATFORM_MSI);
+ dev->msi.domain = of_msi_get_domain(dev, dev->of_node, DOMAIN_BUS_PLATFORM_MSI);
if (!dev->msi.domain)
return -EPROBE_DEFER;

- msi_parent_dn = irq_domain_get_of_node(dev->msi.domain);
- if (!msi_parent_dn)
+ if (!irq_domain_get_of_node(dev->msi.domain))
return -ENODEV;

+ if (irq_domain_is_msi_parent(dev->msi.domain)) {
+ bool sei = msi_data->subset_data->icu_group == ICU_GRP_SEI;
+ const struct msi_domain_template *tmpl;
+
+ tmpl = sei ? &mvebu_icu_sei_msi_template : &mvebu_icu_nsr_msi_template;
+
+ if (!msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN, tmpl,
+ ICU_MAX_IRQS, NULL, msi_data))
+ return -ENOMEM;
+ }
+
irq_domain = platform_msi_create_device_tree_domain(dev, ICU_MAX_IRQS,
mvebu_icu_write_msg,
&mvebu_icu_domain_ops,
--
2.34.1


2024-06-14 10:30:33

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 20/24] irqchip/mvebu-odmi: Switch to parent MSI

From: Thomas Gleixner <[email protected]>

All platform MSI users and the PCI/MSI code handle per device MSI domains
when the irqdomain associated to the device provides MSI parent
functionality.

Remove the "global" platform domain related code and provide the MSI parent
functionality by filling in msi_parent_ops.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
Cc: Andrew Lunn <[email protected]>
Cc: Gregory Clement <[email protected]>
Cc: Sebastian Hesselbarth <[email protected]>
---
v3: enabled MSI_FLAG_PCI_MSI_MASK_PARENT in msi_parent_ops::supported_flags
---
drivers/irqchip/Kconfig | 1 +
drivers/irqchip/irq-mvebu-odmi.c | 38 ++++++++++++++++----------------
2 files changed, 20 insertions(+), 19 deletions(-)

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index a0fa59928913..4def6dc76fa6 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -379,6 +379,7 @@ config MVEBU_ICU

config MVEBU_ODMI
bool
+ select IRQ_MSI_LIB
select GENERIC_MSI_IRQ

config MVEBU_PIC
diff --git a/drivers/irqchip/irq-mvebu-odmi.c b/drivers/irqchip/irq-mvebu-odmi.c
index 108091533e10..78e23fcdff8d 100644
--- a/drivers/irqchip/irq-mvebu-odmi.c
+++ b/drivers/irqchip/irq-mvebu-odmi.c
@@ -17,6 +17,9 @@
#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/slab.h>
+
+#include "irq-msi-lib.h"
+
#include <dt-bindings/interrupt-controller/arm-gic.h>

#define GICP_ODMIN_SET 0x40
@@ -141,27 +144,30 @@ static void odmi_irq_domain_free(struct irq_domain *domain,
}

static const struct irq_domain_ops odmi_domain_ops = {
+ .select = msi_lib_irq_domain_select,
.alloc = odmi_irq_domain_alloc,
.free = odmi_irq_domain_free,
};

-static struct irq_chip odmi_msi_irq_chip = {
- .name = "ODMI",
-};
+#define ODMI_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
+ MSI_FLAG_USE_DEF_CHIP_OPS)

-static struct msi_domain_ops odmi_msi_ops = {
-};
+#define ODMI_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \
+ MSI_FLAG_PCI_MSI_MASK_PARENT)

-static struct msi_domain_info odmi_msi_domain_info = {
- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
- .ops = &odmi_msi_ops,
- .chip = &odmi_msi_irq_chip,
+static const struct msi_parent_ops odmi_msi_parent_ops = {
+ .supported_flags = ODMI_MSI_FLAGS_SUPPORTED,
+ .required_flags = ODMI_MSI_FLAGS_REQUIRED,
+ .bus_select_token = DOMAIN_BUS_GENERIC_MSI,
+ .bus_select_mask = MATCH_PLATFORM_MSI,
+ .prefix = "ODMI-",
+ .init_dev_msi_info = msi_lib_init_dev_msi_info,
};

static int __init mvebu_odmi_init(struct device_node *node,
struct device_node *parent)
{
- struct irq_domain *parent_domain, *inner_domain, *plat_domain;
+ struct irq_domain *parent_domain, *inner_domain;
int ret, i;

if (of_property_read_u32(node, "marvell,odmi-frames", &odmis_count))
@@ -208,18 +214,12 @@ static int __init mvebu_odmi_init(struct device_node *node,
goto err_unmap;
}

- plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node),
- &odmi_msi_domain_info,
- inner_domain);
- if (!plat_domain) {
- ret = -ENOMEM;
- goto err_remove_inner;
- }
+ irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_GENERIC_MSI);
+ inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
+ inner_domain->msi_parent_ops = &odmi_msi_parent_ops;

return 0;

-err_remove_inner:
- irq_domain_remove(inner_domain);
err_unmap:
for (i = 0; i < odmis_count; i++) {
struct odmi_data *odmi = &odmis[i];
--
2.34.1


2024-06-14 10:30:47

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 21/24] irqchip/irq-mvebu-sei: Switch to MSI parent

From: Thomas Gleixner <[email protected]>

All platform MSI users and the PCI/MSI code handle per device MSI domains
when the irqdomain associated to the device provides MSI parent
functionality.

Remove the "global" platform domain related code and provide the MSI parent
functionality by filling in msi_parent_ops.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
Cc: Andrew Lunn <[email protected]>
Cc: Gregory Clement <[email protected]>
Cc: Sebastian Hesselbarth <[email protected]>
---
v3: enabled MSI_FLAG_PCI_MSI_MASK_PARENT in msi_parent_ops::supported_flags
---
drivers/irqchip/irq-mvebu-sei.c | 53 +++++++++++++--------------------
1 file changed, 21 insertions(+), 32 deletions(-)

diff --git a/drivers/irqchip/irq-mvebu-sei.c b/drivers/irqchip/irq-mvebu-sei.c
index a48dbe91b036..b52007f884d0 100644
--- a/drivers/irqchip/irq-mvebu-sei.c
+++ b/drivers/irqchip/irq-mvebu-sei.c
@@ -14,6 +14,8 @@
#include <linux/of_irq.h>
#include <linux/of_platform.h>

+#include "irq-msi-lib.h"
+
/* Cause register */
#define GICP_SECR(idx) (0x0 + ((idx) * 0x4))
/* Mask register */
@@ -190,6 +192,7 @@ static void mvebu_sei_domain_free(struct irq_domain *domain, unsigned int virq,
}

static const struct irq_domain_ops mvebu_sei_domain_ops = {
+ .select = msi_lib_irq_domain_select,
.alloc = mvebu_sei_domain_alloc,
.free = mvebu_sei_domain_free,
};
@@ -307,21 +310,6 @@ static const struct irq_domain_ops mvebu_sei_cp_domain_ops = {
.free = mvebu_sei_cp_domain_free,
};

-static struct irq_chip mvebu_sei_msi_irq_chip = {
- .name = "SEI pMSI",
- .irq_ack = irq_chip_ack_parent,
- .irq_set_type = irq_chip_set_type_parent,
-};
-
-static struct msi_domain_ops mvebu_sei_msi_ops = {
-};
-
-static struct msi_domain_info mvebu_sei_msi_domain_info = {
- .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS,
- .ops = &mvebu_sei_msi_ops,
- .chip = &mvebu_sei_msi_irq_chip,
-};
-
static void mvebu_sei_handle_cascade_irq(struct irq_desc *desc)
{
struct mvebu_sei *sei = irq_desc_get_handler_data(desc);
@@ -360,10 +348,24 @@ static void mvebu_sei_reset(struct mvebu_sei *sei)
}
}

+#define SEI_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
+ MSI_FLAG_USE_DEF_CHIP_OPS)
+
+#define SEI_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \
+ MSI_FLAG_PCI_MSI_MASK_PARENT)
+
+static const struct msi_parent_ops sei_msi_parent_ops = {
+ .supported_flags = SEI_MSI_FLAGS_SUPPORTED,
+ .required_flags = SEI_MSI_FLAGS_REQUIRED,
+ .bus_select_mask = MATCH_PLATFORM_MSI,
+ .bus_select_token = DOMAIN_BUS_GENERIC_MSI,
+ .prefix = "SEI-",
+ .init_dev_msi_info = msi_lib_init_dev_msi_info,
+};
+
static int mvebu_sei_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
- struct irq_domain *plat_domain;
struct mvebu_sei *sei;
u32 parent_irq;
int ret;
@@ -440,33 +442,20 @@ static int mvebu_sei_probe(struct platform_device *pdev)
}

irq_domain_update_bus_token(sei->cp_domain, DOMAIN_BUS_GENERIC_MSI);
-
- plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node),
- &mvebu_sei_msi_domain_info,
- sei->cp_domain);
- if (!plat_domain) {
- pr_err("Failed to create CPs MSI domain\n");
- ret = -ENOMEM;
- goto remove_cp_domain;
- }
+ sei->cp_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
+ sei->cp_domain->msi_parent_ops = &sei_msi_parent_ops;

mvebu_sei_reset(sei);

- irq_set_chained_handler_and_data(parent_irq,
- mvebu_sei_handle_cascade_irq,
- sei);
-
+ irq_set_chained_handler_and_data(parent_irq, mvebu_sei_handle_cascade_irq, sei);
return 0;

-remove_cp_domain:
- irq_domain_remove(sei->cp_domain);
remove_ap_domain:
irq_domain_remove(sei->ap_domain);
remove_sei_domain:
irq_domain_remove(sei->sei_domain);
dispose_irq:
irq_dispose_mapping(parent_irq);
-
return ret;
}

--
2.34.1


2024-06-14 10:31:07

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 22/24] irqchip/irq-mvebu-icu: Remove platform MSI leftovers

From: Thomas Gleixner <[email protected]>

All related domains provide MSI parent functionality, so the fallback code
to the original platform MSI implementation is not longer required.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
Cc: Andrew Lunn <[email protected]>
Cc: Gregory Clement <[email protected]>
Cc: Sebastian Hesselbarth <[email protected]>
---
drivers/irqchip/irq-mvebu-icu.c | 212 +-------------------------------
1 file changed, 6 insertions(+), 206 deletions(-)

diff --git a/drivers/irqchip/irq-mvebu-icu.c b/drivers/irqchip/irq-mvebu-icu.c
index 2a210cd9482e..b337f6c05f18 100644
--- a/drivers/irqchip/irq-mvebu-icu.c
+++ b/drivers/irqchip/irq-mvebu-icu.c
@@ -129,196 +129,6 @@ static void mvebu_icu_init(struct mvebu_icu *icu,
writel_relaxed(msg[1].address_lo, icu->base + subset->offset_clr_al);
}

-/* Start of area to be removed once all parent chips provide MSI parent */
-
-struct mvebu_icu_irq_data {
- struct mvebu_icu *icu;
- unsigned int icu_group;
- unsigned int type;
-};
-
-static void mvebu_icu_write_msg(struct msi_desc *desc, struct msi_msg *msg)
-{
- struct irq_data *d = irq_get_irq_data(desc->irq);
- struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(d->domain);
- struct mvebu_icu_irq_data *icu_irqd = d->chip_data;
- struct mvebu_icu *icu = icu_irqd->icu;
- unsigned int icu_int;
-
- if (msg->address_lo || msg->address_hi) {
- /* One off initialization per domain */
- mvebu_icu_init(icu, msi_data, msg);
- /* Configure the ICU with irq number & type */
- icu_int = msg->data | ICU_INT_ENABLE;
- if (icu_irqd->type & IRQ_TYPE_EDGE_RISING)
- icu_int |= ICU_IS_EDGE;
- icu_int |= icu_irqd->icu_group << ICU_GROUP_SHIFT;
- } else {
- /* De-configure the ICU */
- icu_int = 0;
- }
-
- writel_relaxed(icu_int, icu->base + ICU_INT_CFG(d->hwirq));
-
- /*
- * The SATA unit has 2 ports, and a dedicated ICU entry per
- * port. The ahci sata driver supports only one irq interrupt
- * per SATA unit. To solve this conflict, we configure the 2
- * SATA wired interrupts in the south bridge into 1 GIC
- * interrupt in the north bridge. Even if only a single port
- * is enabled, if sata node is enabled, both interrupts are
- * configured (regardless of which port is actually in use).
- */
- if (d->hwirq == ICU_SATA0_ICU_ID || d->hwirq == ICU_SATA1_ICU_ID) {
- writel_relaxed(icu_int,
- icu->base + ICU_INT_CFG(ICU_SATA0_ICU_ID));
- writel_relaxed(icu_int,
- icu->base + ICU_INT_CFG(ICU_SATA1_ICU_ID));
- }
-}
-
-static struct irq_chip mvebu_icu_nsr_chip = {
- .name = "ICU-NSR",
- .irq_mask = irq_chip_mask_parent,
- .irq_unmask = irq_chip_unmask_parent,
- .irq_eoi = irq_chip_eoi_parent,
- .irq_set_type = irq_chip_set_type_parent,
- .irq_set_affinity = irq_chip_set_affinity_parent,
-};
-
-static struct irq_chip mvebu_icu_sei_chip = {
- .name = "ICU-SEI",
- .irq_ack = irq_chip_ack_parent,
- .irq_mask = irq_chip_mask_parent,
- .irq_unmask = irq_chip_unmask_parent,
- .irq_set_type = irq_chip_set_type_parent,
- .irq_set_affinity = irq_chip_set_affinity_parent,
-};
-
-static int
-mvebu_icu_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
- unsigned long *hwirq, unsigned int *type)
-{
- unsigned int param_count = static_branch_unlikely(&legacy_bindings) ? 3 : 2;
- struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(d);
- struct mvebu_icu *icu = msi_data->icu;
-
- /* Check the count of the parameters in dt */
- if (WARN_ON(fwspec->param_count != param_count)) {
- dev_err(icu->dev, "wrong ICU parameter count %d\n",
- fwspec->param_count);
- return -EINVAL;
- }
-
- if (static_branch_unlikely(&legacy_bindings)) {
- *hwirq = fwspec->param[1];
- *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
- if (fwspec->param[0] != ICU_GRP_NSR) {
- dev_err(icu->dev, "wrong ICU group type %x\n",
- fwspec->param[0]);
- return -EINVAL;
- }
- } else {
- *hwirq = fwspec->param[0];
- *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
-
- /*
- * The ICU receives level interrupts. While the NSR are also
- * level interrupts, SEI are edge interrupts. Force the type
- * here in this case. Please note that this makes the interrupt
- * handling unreliable.
- */
- if (msi_data->subset_data->icu_group == ICU_GRP_SEI)
- *type = IRQ_TYPE_EDGE_RISING;
- }
-
- if (*hwirq >= ICU_MAX_IRQS) {
- dev_err(icu->dev, "invalid interrupt number %ld\n", *hwirq);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int
-mvebu_icu_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
- unsigned int nr_irqs, void *args)
-{
- int err;
- unsigned long hwirq;
- struct irq_fwspec *fwspec = args;
- struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(domain);
- struct mvebu_icu *icu = msi_data->icu;
- struct mvebu_icu_irq_data *icu_irqd;
- struct irq_chip *chip = &mvebu_icu_nsr_chip;
-
- icu_irqd = kmalloc(sizeof(*icu_irqd), GFP_KERNEL);
- if (!icu_irqd)
- return -ENOMEM;
-
- err = mvebu_icu_irq_domain_translate(domain, fwspec, &hwirq,
- &icu_irqd->type);
- if (err) {
- dev_err(icu->dev, "failed to translate ICU parameters\n");
- goto free_irqd;
- }
-
- if (static_branch_unlikely(&legacy_bindings))
- icu_irqd->icu_group = fwspec->param[0];
- else
- icu_irqd->icu_group = msi_data->subset_data->icu_group;
- icu_irqd->icu = icu;
-
- err = platform_msi_device_domain_alloc(domain, virq, nr_irqs);
- if (err) {
- dev_err(icu->dev, "failed to allocate ICU interrupt in parent domain\n");
- goto free_irqd;
- }
-
- /* Make sure there is no interrupt left pending by the firmware */
- err = irq_set_irqchip_state(virq, IRQCHIP_STATE_PENDING, false);
- if (err)
- goto free_msi;
-
- if (icu_irqd->icu_group == ICU_GRP_SEI)
- chip = &mvebu_icu_sei_chip;
-
- err = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
- chip, icu_irqd);
- if (err) {
- dev_err(icu->dev, "failed to set the data to IRQ domain\n");
- goto free_msi;
- }
-
- return 0;
-
-free_msi:
- platform_msi_device_domain_free(domain, virq, nr_irqs);
-free_irqd:
- kfree(icu_irqd);
- return err;
-}
-
-static void
-mvebu_icu_irq_domain_free(struct irq_domain *domain, unsigned int virq,
- unsigned int nr_irqs)
-{
- struct irq_data *d = irq_get_irq_data(virq);
- struct mvebu_icu_irq_data *icu_irqd = d->chip_data;
-
- kfree(icu_irqd);
-
- platform_msi_device_domain_free(domain, virq, nr_irqs);
-}
-
-static const struct irq_domain_ops mvebu_icu_domain_ops = {
- .translate = mvebu_icu_irq_domain_translate,
- .alloc = mvebu_icu_irq_domain_alloc,
- .free = mvebu_icu_irq_domain_free,
-};
-
-/* End of removal area */
-
static int mvebu_icu_msi_init(struct irq_domain *domain, struct msi_domain_info *info,
unsigned int virq, irq_hw_number_t hwirq, msi_alloc_info_t *arg)
{
@@ -448,9 +258,10 @@ static const struct of_device_id mvebu_icu_subset_of_match[] = {

static int mvebu_icu_subset_probe(struct platform_device *pdev)
{
+ const struct msi_domain_template *tmpl;
struct mvebu_icu_msi_data *msi_data;
struct device *dev = &pdev->dev;
- struct irq_domain *irq_domain;
+ bool sei;

msi_data = devm_kzalloc(dev, sizeof(*msi_data), GFP_KERNEL);
if (!msi_data)
@@ -471,22 +282,11 @@ static int mvebu_icu_subset_probe(struct platform_device *pdev)
if (!irq_domain_get_of_node(dev->msi.domain))
return -ENODEV;

- if (irq_domain_is_msi_parent(dev->msi.domain)) {
- bool sei = msi_data->subset_data->icu_group == ICU_GRP_SEI;
- const struct msi_domain_template *tmpl;
-
- tmpl = sei ? &mvebu_icu_sei_msi_template : &mvebu_icu_nsr_msi_template;
-
- if (!msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN, tmpl,
- ICU_MAX_IRQS, NULL, msi_data))
- return -ENOMEM;
- }
+ sei = msi_data->subset_data->icu_group == ICU_GRP_SEI;
+ tmpl = sei ? &mvebu_icu_sei_msi_template : &mvebu_icu_nsr_msi_template;

- irq_domain = platform_msi_create_device_tree_domain(dev, ICU_MAX_IRQS,
- mvebu_icu_write_msg,
- &mvebu_icu_domain_ops,
- msi_data);
- if (!irq_domain) {
+ if (!msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN, tmpl,
+ ICU_MAX_IRQS, NULL, msi_data)) {
dev_err(dev, "Failed to create ICU MSI domain\n");
return -ENOMEM;
}
--
2.34.1


2024-06-14 10:37:25

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 05/24] irqchip/gic-v3-its: Provide MSI parent infrastructure

From: Thomas Gleixner <[email protected]>

To support per device MSI domains the ITS must provide MSI parent domain
functionality.

Provide the basic skeleton for this:

- msi_parent_ops
- child domain init callback
- the MSI parent flag set in irqdomain::flags

This does not make ITS a functional parent domain as there is no bit set in
the bus_select_mask yet, but it provides the base to implement PCI and
platform MSI support gradually on top.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Marc Zyngier <[email protected]>
---
v3: enabled MSI_FLAG_PCI_MSI_MASK_PARENT in msi_parent_ops::supported_flags
---
drivers/irqchip/Kconfig | 1 +
drivers/irqchip/Makefile | 2 +-
drivers/irqchip/irq-gic-common.h | 3 ++
drivers/irqchip/irq-gic-v3-its-msi-parent.c | 31 +++++++++++++++++++++
drivers/irqchip/irq-gic-v3-its.c | 5 ++++
5 files changed, 41 insertions(+), 1 deletion(-)
create mode 100644 drivers/irqchip/irq-gic-v3-its-msi-parent.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 2bf8d940504c..b51863fa9b38 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -41,6 +41,7 @@ config ARM_GIC_V3
config ARM_GIC_V3_ITS
bool
select GENERIC_MSI_IRQ
+ select IRQ_MSI_LIB
default ARM_GIC_V3

config ARM_GIC_V3_ITS_PCI
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 72c7f6289411..6e4f7715206d 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -32,7 +32,7 @@ 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-v3-its-platform-msi.o irq-gic-v4.o
+obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v3-its-platform-msi.o irq-gic-v4.o irq-gic-v3-its-msi-parent.o
obj-$(CONFIG_ARM_GIC_V3_ITS_PCI) += irq-gic-v3-its-pci-msi.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
diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h
index f407cce9ecaa..eb4a220dd6ad 100644
--- a/drivers/irqchip/irq-gic-common.h
+++ b/drivers/irqchip/irq-gic-common.h
@@ -8,6 +8,7 @@

#include <linux/of.h>
#include <linux/irqdomain.h>
+#include <linux/msi.h>
#include <linux/irqchip/arm-gic-common.h>

struct gic_quirk {
@@ -29,6 +30,8 @@ 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-v3-its-msi-parent.c b/drivers/irqchip/irq-gic-v3-its-msi-parent.c
new file mode 100644
index 000000000000..cdc0844229b5
--- /dev/null
+++ b/drivers/irqchip/irq-gic-v3-its-msi-parent.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2022 Linutronix GmbH
+// Copyright (C) 2022 Intel
+
+#include "irq-gic-common.h"
+#include "irq-msi-lib.h"
+
+#define ITS_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
+ MSI_FLAG_USE_DEF_CHIP_OPS)
+
+#define ITS_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \
+ MSI_FLAG_PCI_MSIX | \
+ MSI_FLAG_MULTI_PCI_MSI | \
+ MSI_FLAG_PCI_MSI_MASK_PARENT)
+
+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;
+
+ 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,
+ .bus_select_token = DOMAIN_BUS_NEXUS,
+ .prefix = "ITS-",
+ .init_dev_msi_info = its_init_dev_msi_info,
+};
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 3c755d5dad6e..d770d6aedb29 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -38,6 +38,7 @@
#include <asm/exception.h>

#include "irq-gic-common.h"
+#include "irq-msi-lib.h"

#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0)
#define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1)
@@ -3688,6 +3689,7 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
}

static const struct irq_domain_ops its_domain_ops = {
+ .select = msi_lib_irq_domain_select,
.alloc = its_irq_domain_alloc,
.free = its_irq_domain_free,
.activate = its_irq_domain_activate,
@@ -4993,6 +4995,9 @@ static int its_init_domain(struct its_node *its)

irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS);

+ inner_domain->msi_parent_ops = &gic_v3_its_msi_parent_ops;
+ inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
+
return 0;
}

--
2.34.1


2024-06-14 10:37:37

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 06/24] irqchip/irq-msi-lib: Prepare for PCI MSI/MSIX

From: Thomas Gleixner <[email protected]>

Add the bus tokens for DOMAIN_BUS_PCI_DEVICE_MSI and
DOMAIN_BUS_PCI_DEVICE_MSIX to the common child init
function.

Provide the match mask which can be used by parent domain
implementation so the bitmask based child bus token match
works.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
---
v3: removed pci_device_msi_mask_unmask_parent_enable call as there is no
more global static key.
---
drivers/irqchip/irq-msi-lib.c | 6 ++++++
drivers/irqchip/irq-msi-lib.h | 6 ++++++
2 files changed, 12 insertions(+)

diff --git a/drivers/irqchip/irq-msi-lib.c b/drivers/irqchip/irq-msi-lib.c
index acbccf8f7f5b..0b359c5d8c6c 100644
--- a/drivers/irqchip/irq-msi-lib.c
+++ b/drivers/irqchip/irq-msi-lib.c
@@ -48,6 +48,12 @@ bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain,

/* Is the target domain bus token supported? */
switch(info->bus_token) {
+ case DOMAIN_BUS_PCI_DEVICE_MSI:
+ case DOMAIN_BUS_PCI_DEVICE_MSIX:
+ if (WARN_ON_ONCE(!IS_ENABLED(CONFIG_PCI_MSI)))
+ return false;
+
+ break;
default:
/*
* This should never be reached. See
diff --git a/drivers/irqchip/irq-msi-lib.h b/drivers/irqchip/irq-msi-lib.h
index f0706cc28264..525aa5284a99 100644
--- a/drivers/irqchip/irq-msi-lib.h
+++ b/drivers/irqchip/irq-msi-lib.h
@@ -9,6 +9,12 @@
#include <linux/irqdomain.h>
#include <linux/msi.h>

+#ifdef CONFIG_PCI_MSI
+#define MATCH_PCI_MSI BIT(DOMAIN_BUS_PCI_MSI)
+#else
+#define MATCH_PCI_MSI (0)
+#endif
+
int msi_lib_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
enum irq_domain_bus_token bus_token);

--
2.34.1


2024-06-14 10:39:11

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 08/24] irqchip/irq-msi-lib: Prepare for DEVICE MSI to replace platform MSI

From: Thomas Gleixner <[email protected]>

Add the prerequisites for DEVICE MSI into the shared select() and child
domain init function. These domains are really trivial and just provide a
custom irq chip callback to write the MSI message.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
---
v3: adopted to IMS->MSI rename
---
drivers/irqchip/irq-msi-lib.c | 13 +++++++++++++
drivers/irqchip/irq-msi-lib.h | 2 ++
2 files changed, 15 insertions(+)

diff --git a/drivers/irqchip/irq-msi-lib.c b/drivers/irqchip/irq-msi-lib.c
index 0b359c5d8c6c..6aa4974d2d12 100644
--- a/drivers/irqchip/irq-msi-lib.c
+++ b/drivers/irqchip/irq-msi-lib.c
@@ -53,6 +53,19 @@ bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
if (WARN_ON_ONCE(!IS_ENABLED(CONFIG_PCI_MSI)))
return false;

+ break;
+ case DOMAIN_BUS_DEVICE_MSI:
+ /*
+ * Per device MSI should never have any MSI feature bits
+ * set. It's sole purpose is to create a dumb interrupt
+ * chip which has a device specific irq_write_msi_msg()
+ * callback.
+ */
+ if (WARN_ON_ONCE(info->flags))
+ return false;
+
+ /* Core managed MSI descriptors */
+ info->flags = MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS | MSI_FLAG_FREE_MSI_DESCS;
break;
default:
/*
diff --git a/drivers/irqchip/irq-msi-lib.h b/drivers/irqchip/irq-msi-lib.h
index 525aa5284a99..681ceabb7bc7 100644
--- a/drivers/irqchip/irq-msi-lib.h
+++ b/drivers/irqchip/irq-msi-lib.h
@@ -15,6 +15,8 @@
#define MATCH_PCI_MSI (0)
#endif

+#define MATCH_PLATFORM_MSI BIT(DOMAIN_BUS_PLATFORM_MSI)
+
int msi_lib_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
enum irq_domain_bus_token bus_token);

--
2.34.1


2024-06-14 10:40:39

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 14/24] genirq/gic-v3-mbi: Remove unused wired MSI mechanics

From: Thomas Gleixner <[email protected]>

Nothing builds a platform_device MSI domain for wire to MSI on top of
this. The "regular" users of the platform MSI domain just provide their own
irq_write_msi_msg() callback.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Marc Zyngier <[email protected]>
---
drivers/irqchip/irq-gic-v3-mbi.c | 17 +----------------
1 file changed, 1 insertion(+), 16 deletions(-)

diff --git a/drivers/irqchip/irq-gic-v3-mbi.c b/drivers/irqchip/irq-gic-v3-mbi.c
index dbb8b1efda44..19298cc6c2ee 100644
--- a/drivers/irqchip/irq-gic-v3-mbi.c
+++ b/drivers/irqchip/irq-gic-v3-mbi.c
@@ -199,31 +199,16 @@ static int mbi_allocate_pci_domain(struct irq_domain *nexus_domain,
}
#endif

-static void mbi_compose_mbi_msg(struct irq_data *data, struct msi_msg *msg)
-{
- mbi_compose_msi_msg(data, msg);
-
- msg[1].address_hi = upper_32_bits(mbi_phys_base + GICD_CLRSPI_NSR);
- msg[1].address_lo = lower_32_bits(mbi_phys_base + GICD_CLRSPI_NSR);
- msg[1].data = data->parent_data->hwirq;
-
- iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), &msg[1]);
-}
-
/* Platform-MSI specific irqchip */
static struct irq_chip mbi_pmsi_irq_chip = {
.name = "pMSI",
- .irq_set_type = irq_chip_set_type_parent,
- .irq_compose_msi_msg = mbi_compose_mbi_msg,
- .flags = IRQCHIP_SUPPORTS_LEVEL_MSI,
};

static struct msi_domain_ops mbi_pmsi_ops = {
};

static struct msi_domain_info mbi_pmsi_domain_info = {
- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
- MSI_FLAG_LEVEL_CAPABLE),
+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
.ops = &mbi_pmsi_ops,
.chip = &mbi_pmsi_irq_chip,
};
--
2.34.1


2024-06-14 10:40:51

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 15/24] genirq/gic-v3-mbi: Switch to MSI parent

From: Thomas Gleixner <[email protected]>

All platform MSI users and the PCI/MSI code handle per device MSI domains
when the irqdomain associated to the device provides MSI parent
functionality.

Remove the "global" PCI/MSI and platform domain related code and provide
the MSI parent functionality by filling in msi_parent_ops.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Marc Zyngier <[email protected]>
---
v3: enabled MSI_FLAG_PCI_MSI_MASK_PARENT in msi_parent_ops::supported_flags
---
drivers/irqchip/irq-gic-v3-mbi.c | 127 +++++++++----------------------
1 file changed, 35 insertions(+), 92 deletions(-)

diff --git a/drivers/irqchip/irq-gic-v3-mbi.c b/drivers/irqchip/irq-gic-v3-mbi.c
index 19298cc6c2ee..26b95027934e 100644
--- a/drivers/irqchip/irq-gic-v3-mbi.c
+++ b/drivers/irqchip/irq-gic-v3-mbi.c
@@ -18,6 +18,8 @@

#include <linux/irqchip/arm-gic-v3.h>

+#include "irq-msi-lib.h"
+
struct mbi_range {
u32 spi_start;
u32 nr_spis;
@@ -29,6 +31,15 @@ static phys_addr_t mbi_phys_base;
static struct mbi_range *mbi_ranges;
static unsigned int mbi_range_nr;

+static void mbi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+ msg[0].address_hi = upper_32_bits(mbi_phys_base + GICD_SETSPI_NSR);
+ msg[0].address_lo = lower_32_bits(mbi_phys_base + GICD_SETSPI_NSR);
+ msg[0].data = data->hwirq;
+
+ iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg);
+}
+
static struct irq_chip mbi_irq_chip = {
.name = "MBI",
.irq_mask = irq_chip_mask_parent,
@@ -36,11 +47,11 @@ static struct irq_chip mbi_irq_chip = {
.irq_eoi = irq_chip_eoi_parent,
.irq_set_type = irq_chip_set_type_parent,
.irq_set_affinity = irq_chip_set_affinity_parent,
+ .irq_compose_msi_msg = mbi_compose_msi_msg,
};

-static int mbi_irq_gic_domain_alloc(struct irq_domain *domain,
- unsigned int virq,
- irq_hw_number_t hwirq)
+static int mbi_irq_gic_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ irq_hw_number_t hwirq)
{
struct irq_fwspec fwspec;
struct irq_data *d;
@@ -138,85 +149,31 @@ static void mbi_irq_domain_free(struct irq_domain *domain,
}

static const struct irq_domain_ops mbi_domain_ops = {
+ .select = msi_lib_irq_domain_select,
.alloc = mbi_irq_domain_alloc,
.free = mbi_irq_domain_free,
};

-static void mbi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
-{
- msg[0].address_hi = upper_32_bits(mbi_phys_base + GICD_SETSPI_NSR);
- msg[0].address_lo = lower_32_bits(mbi_phys_base + GICD_SETSPI_NSR);
- msg[0].data = data->parent_data->hwirq;
-
- iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg);
-}
-
-#ifdef CONFIG_PCI_MSI
-/* PCI-specific irqchip */
-static void mbi_mask_msi_irq(struct irq_data *d)
-{
- pci_msi_mask_irq(d);
- irq_chip_mask_parent(d);
-}
-
-static void mbi_unmask_msi_irq(struct irq_data *d)
-{
- pci_msi_unmask_irq(d);
- irq_chip_unmask_parent(d);
-}
-
-static struct irq_chip mbi_msi_irq_chip = {
- .name = "MSI",
- .irq_mask = mbi_mask_msi_irq,
- .irq_unmask = mbi_unmask_msi_irq,
- .irq_eoi = irq_chip_eoi_parent,
- .irq_compose_msi_msg = mbi_compose_msi_msg,
-};
-
-static struct msi_domain_info mbi_msi_domain_info = {
- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
- MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI),
- .chip = &mbi_msi_irq_chip,
+#define MBI_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
+ MSI_FLAG_USE_DEF_CHIP_OPS)
+
+#define MBI_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \
+ MSI_FLAG_PCI_MSIX | \
+ MSI_FLAG_MULTI_PCI_MSI | \
+ MSI_FLAG_PCI_MSI_MASK_PARENT)
+
+static const struct msi_parent_ops gic_v3_mbi_msi_parent_ops = {
+ .supported_flags = MBI_MSI_FLAGS_SUPPORTED,
+ .required_flags = MBI_MSI_FLAGS_REQUIRED,
+ .bus_select_token = DOMAIN_BUS_NEXUS,
+ .bus_select_mask = MATCH_PCI_MSI | MATCH_PLATFORM_MSI,
+ .prefix = "MBI-",
+ .init_dev_msi_info = msi_lib_init_dev_msi_info,
};

-static int mbi_allocate_pci_domain(struct irq_domain *nexus_domain,
- struct irq_domain **pci_domain)
+static int mbi_allocate_domain(struct irq_domain *parent)
{
- *pci_domain = pci_msi_create_irq_domain(nexus_domain->parent->fwnode,
- &mbi_msi_domain_info,
- nexus_domain);
- if (!*pci_domain)
- return -ENOMEM;
-
- return 0;
-}
-#else
-static int mbi_allocate_pci_domain(struct irq_domain *nexus_domain,
- struct irq_domain **pci_domain)
-{
- *pci_domain = NULL;
- return 0;
-}
-#endif
-
-/* Platform-MSI specific irqchip */
-static struct irq_chip mbi_pmsi_irq_chip = {
- .name = "pMSI",
-};
-
-static struct msi_domain_ops mbi_pmsi_ops = {
-};
-
-static struct msi_domain_info mbi_pmsi_domain_info = {
- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
- .ops = &mbi_pmsi_ops,
- .chip = &mbi_pmsi_irq_chip,
-};
-
-static int mbi_allocate_domains(struct irq_domain *parent)
-{
- struct irq_domain *nexus_domain, *pci_domain, *plat_domain;
- int err;
+ struct irq_domain *nexus_domain;

nexus_domain = irq_domain_create_hierarchy(parent, 0, 0, parent->fwnode,
&mbi_domain_ops, NULL);
@@ -224,22 +181,8 @@ static int mbi_allocate_domains(struct irq_domain *parent)
return -ENOMEM;

irq_domain_update_bus_token(nexus_domain, DOMAIN_BUS_NEXUS);
-
- err = mbi_allocate_pci_domain(nexus_domain, &pci_domain);
-
- plat_domain = platform_msi_create_irq_domain(parent->fwnode,
- &mbi_pmsi_domain_info,
- nexus_domain);
-
- if (err || !plat_domain) {
- if (plat_domain)
- irq_domain_remove(plat_domain);
- if (pci_domain)
- irq_domain_remove(pci_domain);
- irq_domain_remove(nexus_domain);
- return -ENOMEM;
- }
-
+ nexus_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
+ nexus_domain->msi_parent_ops = &gic_v3_mbi_msi_parent_ops;
return 0;
}

@@ -302,7 +245,7 @@ int __init mbi_init(struct fwnode_handle *fwnode, struct irq_domain *parent)

pr_info("Using MBI frame %pa\n", &mbi_phys_base);

- ret = mbi_allocate_domains(parent);
+ ret = mbi_allocate_domain(parent);
if (ret)
goto err_free_mbi;

--
2.34.1


2024-06-14 10:41:09

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 17/24] irqchip/imx-mu-msi: Switch to MSI parent

From: Thomas Gleixner <[email protected]>

All platform MSI users and the PCI/MSI code handle per device MSI domains
when the irqdomain associated to the device provides MSI parent
functionality.

Remove the "global" platform domain related code and provide the MSI parent
functionality by filling in msi_parent_ops.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Marc Zyngier <[email protected]>
Cc: Shawn Guo <[email protected]>
Cc: Sascha Hauer <[email protected]>
Cc: Fabio Estevam <[email protected]>
---
v3: enabled MSI_FLAG_PCI_MSI_MASK_PARENT in msi_parent_ops::supported_flags
---
drivers/irqchip/Kconfig | 1 +
drivers/irqchip/irq-imx-mu-msi.c | 49 ++++++++++++++------------------
drivers/irqchip/irq-msi-lib.c | 2 ++
3 files changed, 25 insertions(+), 27 deletions(-)

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 2104b8727b1a..e7a57b3dc4b1 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -492,6 +492,7 @@ config IMX_MU_MSI
select IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY
select GENERIC_MSI_IRQ
+ select IRQ_MSI_LIB
help
Provide a driver for the i.MX Messaging Unit block used as a
CPU-to-CPU MSI controller. This requires a specially crafted DT
diff --git a/drivers/irqchip/irq-imx-mu-msi.c b/drivers/irqchip/irq-imx-mu-msi.c
index 1dceda044db9..87ecf949550c 100644
--- a/drivers/irqchip/irq-imx-mu-msi.c
+++ b/drivers/irqchip/irq-imx-mu-msi.c
@@ -24,6 +24,8 @@
#include <linux/pm_domain.h>
#include <linux/spinlock.h>

+#include "irq-msi-lib.h"
+
#define IMX_MU_CHANS 4

enum imx_mu_xcr {
@@ -114,20 +116,6 @@ static void imx_mu_msi_parent_ack_irq(struct irq_data *data)
imx_mu_read(msi_data, msi_data->cfg->xRR + data->hwirq * 4);
}

-static struct irq_chip imx_mu_msi_irq_chip = {
- .name = "MU-MSI",
- .irq_ack = irq_chip_ack_parent,
-};
-
-static struct msi_domain_ops imx_mu_msi_irq_ops = {
-};
-
-static struct msi_domain_info imx_mu_msi_domain_info = {
- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
- .ops = &imx_mu_msi_irq_ops,
- .chip = &imx_mu_msi_irq_chip,
-};
-
static void imx_mu_msi_parent_compose_msg(struct irq_data *data,
struct msi_msg *msg)
{
@@ -195,6 +183,7 @@ static void imx_mu_msi_domain_irq_free(struct irq_domain *domain,
}

static const struct irq_domain_ops imx_mu_msi_domain_ops = {
+ .select = msi_lib_irq_domain_select,
.alloc = imx_mu_msi_domain_irq_alloc,
.free = imx_mu_msi_domain_irq_free,
};
@@ -216,6 +205,22 @@ static void imx_mu_msi_irq_handler(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}

+#define IMX_MU_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
+ MSI_FLAG_USE_DEF_CHIP_OPS | \
+ MSI_FLAG_PARENT_PM_DEV)
+
+#define IMX_MU_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \
+ MSI_FLAG_PCI_MSI_MASK_PARENT)
+
+static const struct msi_parent_ops imx_mu_msi_parent_ops = {
+ .supported_flags = IMX_MU_MSI_FLAGS_SUPPORTED,
+ .required_flags = IMX_MU_MSI_FLAGS_REQUIRED,
+ .bus_select_token = DOMAIN_BUS_NEXUS,
+ .bus_select_mask = MATCH_PLATFORM_MSI,
+ .prefix = "MU-MSI-",
+ .init_dev_msi_info = msi_lib_init_dev_msi_info,
+};
+
static int imx_mu_msi_domains_init(struct imx_mu_msi *msi_data, struct device *dev)
{
struct fwnode_handle *fwnodes = dev_fwnode(dev);
@@ -230,19 +235,9 @@ static int imx_mu_msi_domains_init(struct imx_mu_msi *msi_data, struct device *d
}

irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS);
-
- msi_data->msi_domain = platform_msi_create_irq_domain(fwnodes,
- &imx_mu_msi_domain_info,
- parent);
-
- if (!msi_data->msi_domain) {
- dev_err(dev, "failed to create MSI domain\n");
- irq_domain_remove(parent);
- return -ENOMEM;
- }
-
- irq_domain_set_pm_device(msi_data->msi_domain, dev);
-
+ parent->dev = parent->pm_dev = dev;
+ parent->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
+ parent->msi_parent_ops = &imx_mu_msi_parent_ops;
return 0;
}

diff --git a/drivers/irqchip/irq-msi-lib.c b/drivers/irqchip/irq-msi-lib.c
index 40c19087d719..15f92ce4d1e8 100644
--- a/drivers/irqchip/irq-msi-lib.c
+++ b/drivers/irqchip/irq-msi-lib.c
@@ -89,6 +89,8 @@ bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
/* Chip updates for all child bus types */
if (!info->chip->irq_eoi)
info->chip->irq_eoi = irq_chip_eoi_parent;
+ if (!info->chip->irq_ack)
+ info->chip->irq_ack = irq_chip_ack_parent;

/*
* The device MSI domain can never have a set affinity callback. It
--
2.34.1


2024-06-14 10:42:46

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 24/24] genirq/msi: Move msi_device_data to core

From: Thomas Gleixner <[email protected]>

Now that the platform MSI hack is gone, nothing needs to know about struct
msi_device_data outside of the core code.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
---
v3: fixed warning by kernel test robot
warning: cannot understand function prototype: 'struct msi_device_data '
---
include/linux/msi.h | 18 ------------------
kernel/irq/msi.c | 20 ++++++++++++++++++--
2 files changed, 18 insertions(+), 20 deletions(-)

diff --git a/include/linux/msi.h b/include/linux/msi.h
index 4c3462a6a97b..369367ecae5e 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -21,11 +21,7 @@
#include <linux/irqdomain_defs.h>
#include <linux/cpumask.h>
#include <linux/msi_api.h>
-#include <linux/xarray.h>
-#include <linux/mutex.h>
-#include <linux/list.h>
#include <linux/irq.h>
-#include <linux/bits.h>

#include <asm/msi.h>

@@ -227,20 +223,6 @@ struct msi_dev_domain {
struct irq_domain *domain;
};

-/**
- * msi_device_data - MSI per device data
- * @properties: MSI properties which are interesting to drivers
- * @mutex: Mutex protecting the MSI descriptor store
- * @__domains: Internal data for per device MSI domains
- * @__iter_idx: Index to search the next entry for iterators
- */
-struct msi_device_data {
- unsigned long properties;
- struct mutex mutex;
- struct msi_dev_domain __domains[MSI_MAX_DEVICE_IRQDOMAINS];
- unsigned long __iter_idx;
-};
-
int msi_setup_device_data(struct device *dev);

void msi_lock_descs(struct device *dev);
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index 8314b1d4a903..5fa0547ece0c 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -8,17 +8,33 @@
* This file contains common code to support Message Signaled Interrupts for
* PCI compatible and non PCI compatible devices.
*/
-#include <linux/types.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/msi.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
-#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/xarray.h>

#include "internals.h"

+/**
+ * struct msi_device_data - MSI per device data
+ * @properties: MSI properties which are interesting to drivers
+ * @mutex: Mutex protecting the MSI descriptor store
+ * @__domains: Internal data for per device MSI domains
+ * @__iter_idx: Index to search the next entry for iterators
+ */
+struct msi_device_data {
+ unsigned long properties;
+ struct mutex mutex;
+ struct msi_dev_domain __domains[MSI_MAX_DEVICE_IRQDOMAINS];
+ unsigned long __iter_idx;
+};
+
/**
* struct msi_ctrl - MSI internal management control structure
* @domid: ID of the domain on which management operations should be done
--
2.34.1


2024-06-14 11:00:11

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 03/24] PCI/MSI: Provide MSI_FLAG_PCI_MSI_MASK_PARENT

Most ARM(64) PCI/MSI domains mask and unmask in the parent domain after or
before the PCI mask/unmask operation takes place. So there are more than a
dozen of the same wrapper implementation all over the place.

Don't make the same mistake with the new per device PCI/MSI domains and
provide a new MSI feature flag, which lets the domain implementation
enable this sequence in the PCI/MSI code.

Signed-off-by: Shivamurthy Shastri <[email protected]>
---
v3: new patch to replace the global static key - Marc Zyngier
---
drivers/pci/msi/irqdomain.c | 21 +++++++++++++++++++++
include/linux/msi.h | 2 ++
2 files changed, 23 insertions(+)

diff --git a/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c
index 03d2dd25790d..112c2ff3035c 100644
--- a/drivers/pci/msi/irqdomain.c
+++ b/drivers/pci/msi/irqdomain.c
@@ -148,17 +148,35 @@ static void pci_device_domain_set_desc(msi_alloc_info_t *arg, struct msi_desc *d
arg->hwirq = desc->msi_index;
}

+static __always_inline void cond_mask_parent(struct irq_data *data)
+{
+ struct msi_domain_info *info = data->domain->host_data;
+
+ if (unlikely(info->flags & MSI_FLAG_PCI_MSI_MASK_PARENT))
+ irq_chip_mask_parent(data);
+}
+
+static __always_inline void cond_unmask_parent(struct irq_data *data)
+{
+ struct msi_domain_info *info = data->domain->host_data;
+
+ if (unlikely(info->flags & MSI_FLAG_PCI_MSI_MASK_PARENT))
+ irq_chip_unmask_parent(data);
+}
+
static void pci_irq_mask_msi(struct irq_data *data)
{
struct msi_desc *desc = irq_data_get_msi_desc(data);

pci_msi_mask(desc, BIT(data->irq - desc->irq));
+ cond_mask_parent(data);
}

static void pci_irq_unmask_msi(struct irq_data *data)
{
struct msi_desc *desc = irq_data_get_msi_desc(data);

+ cond_unmask_parent(data);
pci_msi_unmask(desc, BIT(data->irq - desc->irq));
}

@@ -170,6 +188,7 @@ static void pci_irq_unmask_msi(struct irq_data *data)

#define MSI_COMMON_FLAGS (MSI_FLAG_FREE_MSI_DESCS | \
MSI_FLAG_ACTIVATE_EARLY | \
+ MSI_FLAG_PCI_MSI_MASK_PARENT | \
MSI_FLAG_DEV_SYSFS | \
MSI_REACTIVATE)

@@ -195,10 +214,12 @@ static const struct msi_domain_template pci_msi_template = {
static void pci_irq_mask_msix(struct irq_data *data)
{
pci_msix_mask(irq_data_get_msi_desc(data));
+ cond_mask_parent(data);
}

static void pci_irq_unmask_msix(struct irq_data *data)
{
+ cond_unmask_parent(data);
pci_msix_unmask(irq_data_get_msi_desc(data));
}

diff --git a/include/linux/msi.h b/include/linux/msi.h
index dc27cf3903d5..04f33e7f6f8b 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -556,6 +556,8 @@ enum {
MSI_FLAG_USE_DEV_FWNODE = (1 << 7),
/* Set parent->dev into domain->pm_dev on device domain creation */
MSI_FLAG_PARENT_PM_DEV = (1 << 8),
+ /* Support for parent mask/unmask */
+ MSI_FLAG_PCI_MSI_MASK_PARENT = (1 << 9),

/* Mask for the generic functionality */
MSI_GENERIC_FLAGS_MASK = GENMASK(15, 0),
--
2.34.1


2024-06-14 11:00:33

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 04/24] irqchip: Provide irq-msi-lib

From: Thomas Gleixner <[email protected]>

All irqdomains which provide MSI parent domain functionality for per device
MSI domains need to provide a select() callback for the irqdomain and a
function to initialize the child domain.

Most of these functions would just be copy&paste with minimal
modifications, so provide a library function which implements the required
functionality and is customizable via parent_domain::msi_parent_ops. The
check for the supported bus tokens in msi_lib_init_dev_msi_info() is
expanded step by step within the next patches.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
---
v3: renamed file and functions as the library is independent of gic -
Frank Li
---
drivers/irqchip/Kconfig | 3 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-msi-lib.c | 112 ++++++++++++++++++++++++++++++++++
drivers/irqchip/irq-msi-lib.h | 19 ++++++
4 files changed, 135 insertions(+)
create mode 100644 drivers/irqchip/irq-msi-lib.c
create mode 100644 drivers/irqchip/irq-msi-lib.h

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 14464716bacb..2bf8d940504c 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -74,6 +74,9 @@ config ARM_VIC_NR
The maximum number of VICs available in the system, for
power management.

+config IRQ_MSI_LIB
+ bool
+
config ARMADA_370_XP_IRQ
bool
select GENERIC_IRQ_CHIP
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index d9dc3d99aaa8..72c7f6289411 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o
obj-$(CONFIG_ARM_GIC_PM) += irq-gic-pm.o
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-v3-its-platform-msi.o irq-gic-v4.o
diff --git a/drivers/irqchip/irq-msi-lib.c b/drivers/irqchip/irq-msi-lib.c
new file mode 100644
index 000000000000..acbccf8f7f5b
--- /dev/null
+++ b/drivers/irqchip/irq-msi-lib.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2022 Linutronix GmbH
+// Copyright (C) 2022 Intel
+
+#include <linux/export.h>
+
+#include "irq-msi-lib.h"
+
+/**
+ * msi_lib_init_dev_msi_info - Domain info setup for MSI domains
+ * @dev: The device for which the domain is created for
+ * @domain: The domain providing this callback
+ * @real_parent: The real parent domain of the domain to be initialized
+ * which might be a domain built on top of @domain or
+ * @domain itself
+ * @info: The domain info for the domain to be initialize
+ *
+ * This function is to be used for all types of MSI domains above the root
+ * parent domain and any intermediates. The topmost parent domain specific
+ * functionality is determined via @real_parent.
+ *
+ * All intermediate domains between the root and the device domain must
+ * have either msi_parent_ops.init_dev_msi_info = msi_parent_init_dev_msi_info
+ * or invoke it down the line.
+ */
+bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
+ struct irq_domain *real_parent,
+ struct msi_domain_info *info)
+{
+ const struct msi_parent_ops *pops = real_parent->msi_parent_ops;
+
+ /*
+ * MSI parent domain specific settings. For now there is only the
+ * root parent domain, e.g. NEXUS, acting as a MSI parent, but it is
+ * possible to stack MSI parents. See x86 vector -> irq remapping
+ */
+ if (domain->bus_token == pops->bus_select_token) {
+ if (WARN_ON_ONCE(domain != real_parent))
+ return false;
+ } else {
+ WARN_ON_ONCE(1);
+ return false;
+ }
+
+ /* Parent ops available? */
+ if (WARN_ON_ONCE(!pops))
+ return false;
+
+ /* Is the target domain bus token supported? */
+ switch(info->bus_token) {
+ default:
+ /*
+ * This should never be reached. See
+ * msi_lib_irq_domain_select()
+ */
+ WARN_ON_ONCE(1);
+ return false;
+ }
+
+ /*
+ * Mask out the domain specific MSI feature flags which are not
+ * supported by the real parent.
+ */
+ info->flags &= pops->supported_flags;
+ /* Enforce the required flags */
+ info->flags |= pops->required_flags;
+
+ /* Chip updates for all child bus types */
+ if (!info->chip->irq_eoi)
+ info->chip->irq_eoi = irq_chip_eoi_parent;
+
+ /*
+ * The device MSI domain can never have a set affinity callback. It
+ * always has to rely on the parent domain to handle affinity
+ * settings. The device MSI domain just has to write the resulting
+ * MSI message into the hardware which is the whole purpose of the
+ * device MSI domain aside of mask/unmask which is provided e.g. by
+ * PCI/MSI device domains.
+ */
+ info->chip->irq_set_affinity = msi_domain_set_affinity;
+ return true;
+}
+EXPORT_SYMBOL_GPL(msi_lib_init_dev_msi_info);
+
+/**
+ * msi_lib_irq_domain_select - Shared select function for NEXUS domains
+ * @d: Pointer to the irq domain on which select is invoked
+ * @fwspec: Firmware spec describing what is searched
+ * @bus_token: The bus token for which a matching irq domain is looked up
+ *
+ * Returns: %0 if @d is not what is being looked for
+ *
+ * %1 if @d is either the domain which is directly searched for or
+ * if @d is providing the parent MSI domain for the functionality
+ * requested with @bus_token.
+ */
+int msi_lib_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
+ enum irq_domain_bus_token bus_token)
+{
+ const struct msi_parent_ops *ops = d->msi_parent_ops;
+ u32 busmask = BIT(bus_token);
+
+ if (fwspec->fwnode != d->fwnode || fwspec->param_count != 0)
+ return 0;
+
+ /* Handle pure domain searches */
+ if (bus_token == ops->bus_select_token)
+ return 1;
+
+ return ops && !!(ops->bus_select_mask & busmask);
+}
+EXPORT_SYMBOL_GPL(msi_lib_irq_domain_select);
diff --git a/drivers/irqchip/irq-msi-lib.h b/drivers/irqchip/irq-msi-lib.h
new file mode 100644
index 000000000000..f0706cc28264
--- /dev/null
+++ b/drivers/irqchip/irq-msi-lib.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2022 Linutronix GmbH
+// Copyright (C) 2022 Intel
+
+#ifndef _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H
+#define _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H
+
+#include <linux/bits.h>
+#include <linux/irqdomain.h>
+#include <linux/msi.h>
+
+int msi_lib_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
+ enum irq_domain_bus_token bus_token);
+
+bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
+ struct irq_domain *real_parent,
+ struct msi_domain_info *info);
+
+#endif /* _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H */
--
2.34.1


2024-06-14 11:04:28

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 09/24] irqchip/mbigen: Prepare for real per device MSI

From: Thomas Gleixner <[email protected]>

The core infrastructure has everything in place to switch MBIGEN to per
device MSI domains and avoid the convoluted construct of the existing
platform-MSI layering violation.

The new infrastructure provides a wired interrupt specific interface in the
MSI core which converts the 'hardware interrupt number + trigger type'
allocation which is required for wired interrupts in the regular irqdomain
code to a normal MSI allocation.

The hardware interrupt number and the trigger type are stored in the MSI
descriptor device cookie by the core code so the MBIGEN specific code can
retrieve them.

The new per device domain is only instantiated when the irqdomain which is
associated to the MBIGEN device provides MSI parent functionality. Up to
that point it invokes the existing code. Once the parent is converted the
code for the current platform-MSI mechanism is removed.

The new domain shares the interrupt chip callbacks and the translation
function. The only new functionality aside of filling out the
msi_domain_template is a domain specific set_desc() callback, which will go
away once all platform-MSI code has been converted.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Marc Zyngier <[email protected]>
Cc: Shameerali Kolothum Thodi <[email protected]>
Cc: Zenghui Yu <[email protected]>
---
v3: removed unused variable 'parent' in the function
'mbigen_of_create_domain'
---
drivers/irqchip/irq-mbigen.c | 98 +++++++++++++++++++++++++-----------
1 file changed, 70 insertions(+), 28 deletions(-)

diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c
index 58881d313979..db0fa80330d9 100644
--- a/drivers/irqchip/irq-mbigen.c
+++ b/drivers/irqchip/irq-mbigen.c
@@ -135,24 +135,14 @@ static int mbigen_set_type(struct irq_data *data, unsigned int type)
return 0;
}

-static struct irq_chip mbigen_irq_chip = {
- .name = "mbigen-v2",
- .irq_mask = irq_chip_mask_parent,
- .irq_unmask = irq_chip_unmask_parent,
- .irq_eoi = mbigen_eoi_irq,
- .irq_set_type = mbigen_set_type,
- .irq_set_affinity = irq_chip_set_affinity_parent,
-};
-
-static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg)
+static void mbigen_write_msi_msg(struct irq_data *d, struct msi_msg *msg)
{
- struct irq_data *d = irq_get_irq_data(desc->irq);
void __iomem *base = d->chip_data;
u32 val;

if (!msg->address_lo && !msg->address_hi)
return;
-
+
base += get_mbigen_vec_reg(d->hwirq);
val = readl_relaxed(base);

@@ -165,10 +155,8 @@ static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg)
writel_relaxed(val, base);
}

-static int mbigen_domain_translate(struct irq_domain *d,
- struct irq_fwspec *fwspec,
- unsigned long *hwirq,
- unsigned int *type)
+static int mbigen_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
+ unsigned long *hwirq, unsigned int *type)
{
if (is_of_node(fwspec->fwnode) || is_acpi_device_node(fwspec->fwnode)) {
if (fwspec->param_count != 2)
@@ -192,6 +180,17 @@ static int mbigen_domain_translate(struct irq_domain *d,
return -EINVAL;
}

+/* The following section will go away once ITS provides a MSI parent */
+
+static struct irq_chip mbigen_irq_chip = {
+ .name = "mbigen-v2",
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_eoi = mbigen_eoi_irq,
+ .irq_set_type = mbigen_set_type,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+};
+
static int mbigen_irq_domain_alloc(struct irq_domain *domain,
unsigned int virq,
unsigned int nr_irqs,
@@ -232,11 +231,63 @@ static const struct irq_domain_ops mbigen_domain_ops = {
.free = mbigen_irq_domain_free,
};

+static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg)
+{
+ mbigen_write_msi_msg(irq_get_irq_data(desc->irq), msg);
+}
+
+/* End of to be removed section */
+
+static void mbigen_domain_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
+{
+ arg->desc = desc;
+ arg->hwirq = (u32)desc->data.icookie.value;
+}
+
+static const struct msi_domain_template mbigen_msi_template = {
+ .chip = {
+ .name = "mbigen-v2",
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_eoi = mbigen_eoi_irq,
+ .irq_set_type = mbigen_set_type,
+ .irq_write_msi_msg = mbigen_write_msi_msg,
+ },
+
+ .ops = {
+ .set_desc = mbigen_domain_set_desc,
+ .msi_translate = mbigen_domain_translate,
+ },
+
+ .info = {
+ .bus_token = DOMAIN_BUS_WIRED_TO_MSI,
+ .flags = MSI_FLAG_USE_DEV_FWNODE,
+ },
+};
+
+static bool mbigen_create_device_domain(struct device *dev, unsigned int size,
+ struct mbigen_device *mgn_chip)
+{
+ struct irq_domain *domain = dev->msi.domain;
+
+ if (WARN_ON_ONCE(!domain))
+ return false;
+
+ if (irq_domain_is_msi_parent(domain)) {
+ return msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN,
+ &mbigen_msi_template, size,
+ NULL, mgn_chip->base);
+ }
+
+ /* Remove once ITS provides MSI parent */
+ return !!platform_msi_create_device_domain(dev, size, mbigen_write_msg,
+ &mbigen_domain_ops, mgn_chip);
+}
+
static int mbigen_of_create_domain(struct platform_device *pdev,
struct mbigen_device *mgn_chip)
{
struct platform_device *child;
- struct irq_domain *domain;
struct device_node *np;
u32 num_pins;
int ret = 0;
@@ -258,11 +309,7 @@ static int mbigen_of_create_domain(struct platform_device *pdev,
break;
}

- domain = platform_msi_create_device_domain(&child->dev, num_pins,
- mbigen_write_msg,
- &mbigen_domain_ops,
- mgn_chip);
- if (!domain) {
+ if (!mbigen_create_device_domain(&child->dev, num_pins, mgn_chip)) {
ret = -ENOMEM;
break;
}
@@ -284,7 +331,6 @@ MODULE_DEVICE_TABLE(acpi, mbigen_acpi_match);
static int mbigen_acpi_create_domain(struct platform_device *pdev,
struct mbigen_device *mgn_chip)
{
- struct irq_domain *domain;
u32 num_pins = 0;
int ret;

@@ -315,11 +361,7 @@ static int mbigen_acpi_create_domain(struct platform_device *pdev,
if (ret || num_pins == 0)
return -EINVAL;

- domain = platform_msi_create_device_domain(&pdev->dev, num_pins,
- mbigen_write_msg,
- &mbigen_domain_ops,
- mgn_chip);
- if (!domain)
+ if (!mbigen_create_device_domain(&pdev->dev, num_pins, mgn_chip))
return -ENOMEM;

return 0;
--
2.34.1


2024-06-14 11:04:37

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 10/24] irqchip/irq-msi-lib: Prepare for DOMAIN_BUS_WIRED_TO_MSI

From: Thomas Gleixner <[email protected]>

Add the new bus token to the accepted list of child domain tokens.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
---
drivers/irqchip/irq-msi-lib.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/drivers/irqchip/irq-msi-lib.c b/drivers/irqchip/irq-msi-lib.c
index 6aa4974d2d12..40c19087d719 100644
--- a/drivers/irqchip/irq-msi-lib.c
+++ b/drivers/irqchip/irq-msi-lib.c
@@ -67,6 +67,8 @@ bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
/* Core managed MSI descriptors */
info->flags = MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS | MSI_FLAG_FREE_MSI_DESCS;
break;
+ case DOMAIN_BUS_WIRED_TO_MSI:
+ break;
default:
/*
* This should never be reached. See
--
2.34.1


2024-06-14 11:05:39

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 23/24] genirq/msi: Remove platform MSI leftovers

From: Thomas Gleixner <[email protected]>

No more users!

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
---
drivers/base/platform-msi.c | 350 +-----------------------------------
include/linux/msi.h | 30 ----
kernel/irq/msi.c | 75 +-------
3 files changed, 5 insertions(+), 450 deletions(-)

diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c
index 11f5fdf65b9e..0e60dd650b5e 100644
--- a/drivers/base/platform-msi.c
+++ b/drivers/base/platform-msi.c
@@ -4,346 +4,12 @@
*
* Copyright (C) 2015 ARM Limited, All Rights Reserved.
* Author: Marc Zyngier <[email protected]>
+ * Copyright (C) 2022 Linutronix GmbH
*/

#include <linux/device.h>
-#include <linux/idr.h>
-#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/msi.h>
-#include <linux/slab.h>
-
-/* Begin of removal area. Once everything is converted over. Cleanup the includes too! */
-
-#define DEV_ID_SHIFT 21
-#define MAX_DEV_MSIS (1 << (32 - DEV_ID_SHIFT))
-
-/*
- * Internal data structure containing a (made up, but unique) devid
- * and the callback to write the MSI message.
- */
-struct platform_msi_priv_data {
- struct device *dev;
- void *host_data;
- msi_alloc_info_t arg;
- irq_write_msi_msg_t write_msg;
- int devid;
-};
-
-/* The devid allocator */
-static DEFINE_IDA(platform_msi_devid_ida);
-
-#ifdef GENERIC_MSI_DOMAIN_OPS
-/*
- * Convert an msi_desc to a globaly unique identifier (per-device
- * devid + msi_desc position in the msi_list).
- */
-static irq_hw_number_t platform_msi_calc_hwirq(struct msi_desc *desc)
-{
- u32 devid = desc->dev->msi.data->platform_data->devid;
-
- return (devid << (32 - DEV_ID_SHIFT)) | desc->msi_index;
-}
-
-static void platform_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
-{
- arg->desc = desc;
- arg->hwirq = platform_msi_calc_hwirq(desc);
-}
-
-static int platform_msi_init(struct irq_domain *domain,
- struct msi_domain_info *info,
- unsigned int virq, irq_hw_number_t hwirq,
- msi_alloc_info_t *arg)
-{
- return irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
- info->chip, info->chip_data);
-}
-
-static void platform_msi_set_proxy_dev(msi_alloc_info_t *arg)
-{
- arg->flags |= MSI_ALLOC_FLAGS_PROXY_DEVICE;
-}
-#else
-#define platform_msi_set_desc NULL
-#define platform_msi_init NULL
-#define platform_msi_set_proxy_dev(x) do {} while(0)
-#endif
-
-static void platform_msi_update_dom_ops(struct msi_domain_info *info)
-{
- struct msi_domain_ops *ops = info->ops;
-
- BUG_ON(!ops);
-
- if (ops->msi_init == NULL)
- ops->msi_init = platform_msi_init;
- if (ops->set_desc == NULL)
- ops->set_desc = platform_msi_set_desc;
-}
-
-static void platform_msi_write_msg(struct irq_data *data, struct msi_msg *msg)
-{
- struct msi_desc *desc = irq_data_get_msi_desc(data);
-
- desc->dev->msi.data->platform_data->write_msg(desc, msg);
-}
-
-static void platform_msi_update_chip_ops(struct msi_domain_info *info)
-{
- struct irq_chip *chip = info->chip;
-
- BUG_ON(!chip);
- if (!chip->irq_mask)
- chip->irq_mask = irq_chip_mask_parent;
- if (!chip->irq_unmask)
- chip->irq_unmask = irq_chip_unmask_parent;
- if (!chip->irq_eoi)
- chip->irq_eoi = irq_chip_eoi_parent;
- if (!chip->irq_set_affinity)
- chip->irq_set_affinity = msi_domain_set_affinity;
- if (!chip->irq_write_msi_msg)
- chip->irq_write_msi_msg = platform_msi_write_msg;
- if (WARN_ON((info->flags & MSI_FLAG_LEVEL_CAPABLE) &&
- !(chip->flags & IRQCHIP_SUPPORTS_LEVEL_MSI)))
- info->flags &= ~MSI_FLAG_LEVEL_CAPABLE;
-}
-
-/**
- * platform_msi_create_irq_domain - Create a platform MSI interrupt domain
- * @fwnode: Optional fwnode of the interrupt controller
- * @info: MSI domain info
- * @parent: Parent irq domain
- *
- * Updates the domain and chip ops and creates a platform MSI
- * interrupt domain.
- *
- * Returns:
- * A domain pointer or NULL in case of failure.
- */
-struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
- struct msi_domain_info *info,
- struct irq_domain *parent)
-{
- struct irq_domain *domain;
-
- if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
- platform_msi_update_dom_ops(info);
- if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
- platform_msi_update_chip_ops(info);
- info->flags |= MSI_FLAG_DEV_SYSFS | MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS |
- MSI_FLAG_FREE_MSI_DESCS;
-
- domain = msi_create_irq_domain(fwnode, info, parent);
- if (domain)
- irq_domain_update_bus_token(domain, DOMAIN_BUS_PLATFORM_MSI);
-
- return domain;
-}
-EXPORT_SYMBOL_GPL(platform_msi_create_irq_domain);
-
-static int platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec,
- irq_write_msi_msg_t write_msi_msg)
-{
- struct platform_msi_priv_data *datap;
- int err;
-
- /*
- * Limit the number of interrupts to 2048 per device. Should we
- * need to bump this up, DEV_ID_SHIFT should be adjusted
- * accordingly (which would impact the max number of MSI
- * capable devices).
- */
- if (!dev->msi.domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS)
- return -EINVAL;
-
- if (dev->msi.domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) {
- dev_err(dev, "Incompatible msi_domain, giving up\n");
- return -EINVAL;
- }
-
- err = msi_setup_device_data(dev);
- if (err)
- return err;
-
- /* Already initialized? */
- if (dev->msi.data->platform_data)
- return -EBUSY;
-
- datap = kzalloc(sizeof(*datap), GFP_KERNEL);
- if (!datap)
- return -ENOMEM;
-
- datap->devid = ida_alloc_max(&platform_msi_devid_ida,
- (1 << DEV_ID_SHIFT) - 1, GFP_KERNEL);
- if (datap->devid < 0) {
- err = datap->devid;
- kfree(datap);
- return err;
- }
-
- datap->write_msg = write_msi_msg;
- datap->dev = dev;
- dev->msi.data->platform_data = datap;
- return 0;
-}
-
-static void platform_msi_free_priv_data(struct device *dev)
-{
- struct platform_msi_priv_data *data = dev->msi.data->platform_data;
-
- dev->msi.data->platform_data = NULL;
- ida_free(&platform_msi_devid_ida, data->devid);
- kfree(data);
-}
-
-/**
- * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev
- * @dev: The device for which to allocate interrupts
- * @nvec: The number of interrupts to allocate
- * @write_msi_msg: Callback to write an interrupt message for @dev
- *
- * Returns:
- * Zero for success, or an error code in case of failure
- */
-static int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
- irq_write_msi_msg_t write_msi_msg)
-{
- int err;
-
- err = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
- if (err)
- return err;
-
- err = msi_domain_alloc_irqs_range(dev, MSI_DEFAULT_DOMAIN, 0, nvec - 1);
- if (err)
- platform_msi_free_priv_data(dev);
-
- return err;
-}
-
-/**
- * platform_msi_get_host_data - Query the private data associated with
- * a platform-msi domain
- * @domain: The platform-msi domain
- *
- * Return: The private data provided when calling
- * platform_msi_create_device_domain().
- */
-void *platform_msi_get_host_data(struct irq_domain *domain)
-{
- struct platform_msi_priv_data *data = domain->host_data;
-
- return data->host_data;
-}
-
-static struct lock_class_key platform_device_msi_lock_class;
-
-/**
- * __platform_msi_create_device_domain - Create a platform-msi device domain
- *
- * @dev: The device generating the MSIs
- * @nvec: The number of MSIs that need to be allocated
- * @is_tree: flag to indicate tree hierarchy
- * @write_msi_msg: Callback to write an interrupt message for @dev
- * @ops: The hierarchy domain operations to use
- * @host_data: Private data associated to this domain
- *
- * Return: An irqdomain for @nvec interrupts on success, NULL in case of error.
- *
- * This is for interrupt domains which stack on a platform-msi domain
- * created by platform_msi_create_irq_domain(). @dev->msi.domain points to
- * that platform-msi domain which is the parent for the new domain.
- */
-struct irq_domain *
-__platform_msi_create_device_domain(struct device *dev,
- unsigned int nvec,
- bool is_tree,
- irq_write_msi_msg_t write_msi_msg,
- const struct irq_domain_ops *ops,
- void *host_data)
-{
- struct platform_msi_priv_data *data;
- struct irq_domain *domain;
- int err;
-
- err = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
- if (err)
- return NULL;
-
- /*
- * Use a separate lock class for the MSI descriptor mutex on
- * platform MSI device domains because the descriptor mutex nests
- * into the domain mutex. See alloc/free below.
- */
- lockdep_set_class(&dev->msi.data->mutex, &platform_device_msi_lock_class);
-
- data = dev->msi.data->platform_data;
- data->host_data = host_data;
- domain = irq_domain_create_hierarchy(dev->msi.domain, 0,
- is_tree ? 0 : nvec,
- dev->fwnode, ops, data);
- if (!domain)
- goto free_priv;
-
- platform_msi_set_proxy_dev(&data->arg);
- err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg);
- if (err)
- goto free_domain;
-
- return domain;
-
-free_domain:
- irq_domain_remove(domain);
-free_priv:
- platform_msi_free_priv_data(dev);
- return NULL;
-}
-
-/**
- * platform_msi_device_domain_free - Free interrupts associated with a platform-msi
- * device domain
- *
- * @domain: The platform-msi device domain
- * @virq: The base irq from which to perform the free operation
- * @nr_irqs: How many interrupts to free from @virq
- */
-void platform_msi_device_domain_free(struct irq_domain *domain, unsigned int virq,
- unsigned int nr_irqs)
-{
- struct platform_msi_priv_data *data = domain->host_data;
-
- msi_lock_descs(data->dev);
- msi_domain_depopulate_descs(data->dev, virq, nr_irqs);
- irq_domain_free_irqs_common(domain, virq, nr_irqs);
- msi_free_msi_descs_range(data->dev, virq, virq + nr_irqs - 1);
- msi_unlock_descs(data->dev);
-}
-
-/**
- * platform_msi_device_domain_alloc - Allocate interrupts associated with
- * a platform-msi device domain
- *
- * @domain: The platform-msi device domain
- * @virq: The base irq from which to perform the allocate operation
- * @nr_irqs: How many interrupts to allocate from @virq
- *
- * Return 0 on success, or an error code on failure. Must be called
- * with irq_domain_mutex held (which can only be done as part of a
- * top-level interrupt allocation).
- */
-int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int virq,
- unsigned int nr_irqs)
-{
- struct platform_msi_priv_data *data = domain->host_data;
- struct device *dev = data->dev;
-
- return msi_domain_populate_irqs(domain->parent, dev, virq, nr_irqs, &data->arg);
-}
-
-/* End of removal area */
-
-/* Real per device domain interfaces */

/*
* This indirection can go when platform_device_msi_init_and_alloc_irqs()
@@ -357,7 +23,7 @@ static void platform_msi_write_msi_msg(struct irq_data *d, struct msi_msg *msg)
cb(irq_data_get_msi_desc(d), msg);
}

-static void platform_msi_set_desc_byindex(msi_alloc_info_t *arg, struct msi_desc *desc)
+static void platform_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
{
arg->desc = desc;
arg->hwirq = desc->msi_index;
@@ -373,7 +39,7 @@ static const struct msi_domain_template platform_msi_template = {
},

.ops = {
- .set_desc = platform_msi_set_desc_byindex,
+ .set_desc = platform_msi_set_desc,
},

.info = {
@@ -408,10 +74,6 @@ int platform_device_msi_init_and_alloc_irqs(struct device *dev, unsigned int nve
if (!domain || !write_msi_msg)
return -EINVAL;

- /* Migration support. Will go away once everything is converted */
- if (!irq_domain_is_msi_parent(domain))
- return platform_msi_domain_alloc_irqs(dev, nvec, write_msi_msg);
-
/*
* @write_msi_msg is stored in the resulting msi_domain_info::data.
* The underlying domain creation mechanism will assign that
@@ -432,12 +94,6 @@ EXPORT_SYMBOL_GPL(platform_device_msi_init_and_alloc_irqs);
*/
void platform_device_msi_free_irqs_all(struct device *dev)
{
- struct irq_domain *domain = dev->msi.domain;
-
msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN);
-
- /* Migration support. Will go away once everything is converted */
- if (!irq_domain_is_msi_parent(domain))
- platform_msi_free_priv_data(dev);
}
EXPORT_SYMBOL_GPL(platform_device_msi_free_irqs_all);
diff --git a/include/linux/msi.h b/include/linux/msi.h
index 4ae036d0c7db..4c3462a6a97b 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -81,7 +81,6 @@ extern int pci_msi_ignore_mask;
/* Helper functions */
struct msi_desc;
struct pci_dev;
-struct platform_msi_priv_data;
struct device_attribute;
struct irq_domain;
struct irq_affinity_desc;
@@ -231,14 +230,12 @@ struct msi_dev_domain {
/**
* msi_device_data - MSI per device data
* @properties: MSI properties which are interesting to drivers
- * @platform_data: Platform-MSI specific data
* @mutex: Mutex protecting the MSI descriptor store
* @__domains: Internal data for per device MSI domains
* @__iter_idx: Index to search the next entry for iterators
*/
struct msi_device_data {
unsigned long properties;
- struct platform_msi_priv_data *platform_data;
struct mutex mutex;
struct msi_dev_domain __domains[MSI_MAX_DEVICE_IRQDOMAINS];
unsigned long __iter_idx;
@@ -641,33 +638,6 @@ void msi_domain_free_irqs_all(struct device *dev, unsigned int domid);

struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain);

-struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
- struct msi_domain_info *info,
- struct irq_domain *parent);
-
-/* When an MSI domain is used as an intermediate domain */
-int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
- int nvec, msi_alloc_info_t *args);
-int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev,
- int virq, int nvec, msi_alloc_info_t *args);
-void msi_domain_depopulate_descs(struct device *dev, int virq, int nvec);
-
-struct irq_domain *
-__platform_msi_create_device_domain(struct device *dev,
- unsigned int nvec,
- bool is_tree,
- irq_write_msi_msg_t write_msi_msg,
- const struct irq_domain_ops *ops,
- void *host_data);
-
-#define platform_msi_create_device_tree_domain(dev, nvec, write, ops, data) \
- __platform_msi_create_device_domain(dev, nvec, true, write, ops, data)
-
-int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int virq,
- unsigned int nr_irqs);
-void platform_msi_device_domain_free(struct irq_domain *domain, unsigned int virq,
- unsigned int nvec);
-void *platform_msi_get_host_data(struct irq_domain *domain);
/* Per device platform MSI */
int platform_device_msi_init_and_alloc_irqs(struct device *dev, unsigned int nvec,
irq_write_msi_msg_t write_msi_msg);
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index 2024f89baea4..8314b1d4a903 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -1088,8 +1088,8 @@ bool msi_match_device_irq_domain(struct device *dev, unsigned int domid,
return ret;
}

-int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
- int nvec, msi_alloc_info_t *arg)
+static int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
+ int nvec, msi_alloc_info_t *arg)
{
struct msi_domain_info *info = domain->host_data;
struct msi_domain_ops *ops = info->ops;
@@ -1097,77 +1097,6 @@ int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
return ops->msi_prepare(domain, dev, nvec, arg);
}

-int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev,
- int virq_base, int nvec, msi_alloc_info_t *arg)
-{
- struct msi_domain_info *info = domain->host_data;
- struct msi_domain_ops *ops = info->ops;
- struct msi_ctrl ctrl = {
- .domid = MSI_DEFAULT_DOMAIN,
- .first = virq_base,
- .last = virq_base + nvec - 1,
- };
- struct msi_desc *desc;
- struct xarray *xa;
- int ret, virq;
-
- msi_lock_descs(dev);
-
- if (!msi_ctrl_valid(dev, &ctrl)) {
- ret = -EINVAL;
- goto unlock;
- }
-
- ret = msi_domain_add_simple_msi_descs(dev, &ctrl);
- if (ret)
- goto unlock;
-
- xa = &dev->msi.data->__domains[ctrl.domid].store;
-
- for (virq = virq_base; virq < virq_base + nvec; virq++) {
- desc = xa_load(xa, virq);
- desc->irq = virq;
-
- ops->set_desc(arg, desc);
- ret = irq_domain_alloc_irqs_hierarchy(domain, virq, 1, arg);
- if (ret)
- goto fail;
-
- irq_set_msi_desc(virq, desc);
- }
- msi_unlock_descs(dev);
- return 0;
-
-fail:
- for (--virq; virq >= virq_base; virq--) {
- msi_domain_depopulate_descs(dev, virq, 1);
- irq_domain_free_irqs_common(domain, virq, 1);
- }
- msi_domain_free_descs(dev, &ctrl);
-unlock:
- msi_unlock_descs(dev);
- return ret;
-}
-
-void msi_domain_depopulate_descs(struct device *dev, int virq_base, int nvec)
-{
- struct msi_ctrl ctrl = {
- .domid = MSI_DEFAULT_DOMAIN,
- .first = virq_base,
- .last = virq_base + nvec - 1,
- };
- struct msi_desc *desc;
- struct xarray *xa;
- unsigned long idx;
-
- if (!msi_ctrl_valid(dev, &ctrl))
- return;
-
- xa = &dev->msi.data->__domains[ctrl.domid].store;
- xa_for_each_range(xa, idx, desc, ctrl.first, ctrl.last)
- desc->irq = 0;
-}
-
/*
* Carefully check whether the device can use reservation mode. If
* reservation mode is enabled then the early activation will assign a
--
2.34.1


2024-06-14 11:08:06

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 13/24] genirq/msi: Remove platform_msi_create_device_domain()

From: Thomas Gleixner <[email protected]>

No more users.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
---
include/linux/msi.h | 2 --
1 file changed, 2 deletions(-)

diff --git a/include/linux/msi.h b/include/linux/msi.h
index 04f33e7f6f8b..4ae036d0c7db 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -660,8 +660,6 @@ __platform_msi_create_device_domain(struct device *dev,
const struct irq_domain_ops *ops,
void *host_data);

-#define platform_msi_create_device_domain(dev, nvec, write, ops, data) \
- __platform_msi_create_device_domain(dev, nvec, false, write, ops, data)
#define platform_msi_create_device_tree_domain(dev, nvec, write, ops, data) \
__platform_msi_create_device_domain(dev, nvec, true, write, ops, data)

--
2.34.1


2024-06-14 11:08:20

by Shivamurthy Shastri

[permalink] [raw]
Subject: [PATCH v3 19/24] irqchip/mvebu-gicp: Switch to MSI parent

From: Thomas Gleixner <[email protected]>

All platform MSI users and the PCI/MSI code handle per device MSI domains
when the irqdomain associated to the device provides MSI parent
functionality.

Remove the "global" platform domain related code and provide the MSI parent
functionality by filling in msi_parent_ops.

Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Anna-Maria Behnsen <[email protected]>
Signed-off-by: Shivamurthy Shastri <[email protected]>
Cc: Andrew Lunn <[email protected]>
Cc: Gregory Clement <[email protected]>
Cc: Sebastian Hesselbarth <[email protected]>
---
v3: enabled MSI_FLAG_PCI_MSI_MASK_PARENT in msi_parent_ops::supported_flags
---
drivers/irqchip/Kconfig | 1 +
drivers/irqchip/irq-mvebu-gicp.c | 45 ++++++++++++++------------------
2 files changed, 21 insertions(+), 25 deletions(-)

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index e7a57b3dc4b1..a0fa59928913 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -371,6 +371,7 @@ config MSCC_OCELOT_IRQ
select GENERIC_IRQ_CHIP

config MVEBU_GICP
+ select IRQ_MSI_LIB
bool

config MVEBU_ICU
diff --git a/drivers/irqchip/irq-mvebu-gicp.c b/drivers/irqchip/irq-mvebu-gicp.c
index c43a345061d5..aaa01ff89d41 100644
--- a/drivers/irqchip/irq-mvebu-gicp.c
+++ b/drivers/irqchip/irq-mvebu-gicp.c
@@ -17,6 +17,8 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>

+#include "irq-msi-lib.h"
+
#include <dt-bindings/interrupt-controller/arm-gic.h>

#define GICP_SETSPI_NSR_OFFSET 0x0
@@ -145,32 +147,33 @@ static void gicp_irq_domain_free(struct irq_domain *domain,
}

static const struct irq_domain_ops gicp_domain_ops = {
+ .select = msi_lib_irq_domain_select,
.alloc = gicp_irq_domain_alloc,
.free = gicp_irq_domain_free,
};

-static struct irq_chip gicp_msi_irq_chip = {
- .name = "GICP",
- .irq_set_type = irq_chip_set_type_parent,
- .flags = IRQCHIP_SUPPORTS_LEVEL_MSI,
-};
+#define GICP_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
+ MSI_FLAG_USE_DEF_CHIP_OPS)

-static struct msi_domain_ops gicp_msi_ops = {
-};
+#define GICP_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \
+ MSI_FLAG_LEVEL_CAPABLE | \
+ MSI_FLAG_PCI_MSI_MASK_PARENT)

-static struct msi_domain_info gicp_msi_domain_info = {
- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
- MSI_FLAG_LEVEL_CAPABLE),
- .ops = &gicp_msi_ops,
- .chip = &gicp_msi_irq_chip,
+static const struct msi_parent_ops gicp_msi_parent_ops = {
+ .supported_flags = GICP_MSI_FLAGS_SUPPORTED,
+ .required_flags = GICP_MSI_FLAGS_REQUIRED,
+ .bus_select_token = DOMAIN_BUS_GENERIC_MSI,
+ .bus_select_mask = MATCH_PLATFORM_MSI,
+ .prefix = "GICP-",
+ .init_dev_msi_info = msi_lib_init_dev_msi_info,
};

static int mvebu_gicp_probe(struct platform_device *pdev)
{
- struct mvebu_gicp *gicp;
- struct irq_domain *inner_domain, *plat_domain, *parent_domain;
+ struct irq_domain *inner_domain, *parent_domain;
struct device_node *node = pdev->dev.of_node;
struct device_node *irq_parent_dn;
+ struct mvebu_gicp *gicp;
int ret, i;

gicp = devm_kzalloc(&pdev->dev, sizeof(*gicp), GFP_KERNEL);
@@ -234,17 +237,9 @@ static int mvebu_gicp_probe(struct platform_device *pdev)
if (!inner_domain)
return -ENOMEM;

-
- plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node),
- &gicp_msi_domain_info,
- inner_domain);
- if (!plat_domain) {
- irq_domain_remove(inner_domain);
- return -ENOMEM;
- }
-
- platform_set_drvdata(pdev, gicp);
-
+ irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_GENERIC_MSI);
+ inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
+ inner_domain->msi_parent_ops = &gicp_msi_parent_ops;
return 0;
}

--
2.34.1


2024-06-14 16:43:49

by Bjorn Helgaas

[permalink] [raw]
Subject: Re: [PATCH v3 03/24] PCI/MSI: Provide MSI_FLAG_PCI_MSI_MASK_PARENT

On Fri, Jun 14, 2024 at 12:23:42PM +0200, Shivamurthy Shastri wrote:
> Most ARM(64) PCI/MSI domains mask and unmask in the parent domain after or
> before the PCI mask/unmask operation takes place. So there are more than a
> dozen of the same wrapper implementation all over the place.

Is this an opportunity to clean up all these wrappers? If you could
mention an example or two here, maybe somebody would be motivated to
come back and simplify the existing wrappers to take advantage of this
new flag?

> Don't make the same mistake with the new per device PCI/MSI domains and
> provide a new MSI feature flag, which lets the domain implementation
> enable this sequence in the PCI/MSI code.
>
> Signed-off-by: Shivamurthy Shastri <[email protected]>

I assume you'll merge this series via some other tree, so:

Acked-by: Bjorn Helgaas <[email protected]>

> ---
> v3: new patch to replace the global static key - Marc Zyngier
> ---
> drivers/pci/msi/irqdomain.c | 21 +++++++++++++++++++++
> include/linux/msi.h | 2 ++
> 2 files changed, 23 insertions(+)
>
> diff --git a/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c
> index 03d2dd25790d..112c2ff3035c 100644
> --- a/drivers/pci/msi/irqdomain.c
> +++ b/drivers/pci/msi/irqdomain.c
> @@ -148,17 +148,35 @@ static void pci_device_domain_set_desc(msi_alloc_info_t *arg, struct msi_desc *d
> arg->hwirq = desc->msi_index;
> }
>
> +static __always_inline void cond_mask_parent(struct irq_data *data)
> +{
> + struct msi_domain_info *info = data->domain->host_data;
> +
> + if (unlikely(info->flags & MSI_FLAG_PCI_MSI_MASK_PARENT))
> + irq_chip_mask_parent(data);
> +}
> +
> +static __always_inline void cond_unmask_parent(struct irq_data *data)
> +{
> + struct msi_domain_info *info = data->domain->host_data;
> +
> + if (unlikely(info->flags & MSI_FLAG_PCI_MSI_MASK_PARENT))
> + irq_chip_unmask_parent(data);
> +}
> +
> static void pci_irq_mask_msi(struct irq_data *data)
> {
> struct msi_desc *desc = irq_data_get_msi_desc(data);
>
> pci_msi_mask(desc, BIT(data->irq - desc->irq));
> + cond_mask_parent(data);
> }
>
> static void pci_irq_unmask_msi(struct irq_data *data)
> {
> struct msi_desc *desc = irq_data_get_msi_desc(data);
>
> + cond_unmask_parent(data);
> pci_msi_unmask(desc, BIT(data->irq - desc->irq));
> }
>
> @@ -170,6 +188,7 @@ static void pci_irq_unmask_msi(struct irq_data *data)
>
> #define MSI_COMMON_FLAGS (MSI_FLAG_FREE_MSI_DESCS | \
> MSI_FLAG_ACTIVATE_EARLY | \
> + MSI_FLAG_PCI_MSI_MASK_PARENT | \
> MSI_FLAG_DEV_SYSFS | \
> MSI_REACTIVATE)
>
> @@ -195,10 +214,12 @@ static const struct msi_domain_template pci_msi_template = {
> static void pci_irq_mask_msix(struct irq_data *data)
> {
> pci_msix_mask(irq_data_get_msi_desc(data));
> + cond_mask_parent(data);
> }
>
> static void pci_irq_unmask_msix(struct irq_data *data)
> {
> + cond_unmask_parent(data);
> pci_msix_unmask(irq_data_get_msi_desc(data));
> }
>
> diff --git a/include/linux/msi.h b/include/linux/msi.h
> index dc27cf3903d5..04f33e7f6f8b 100644
> --- a/include/linux/msi.h
> +++ b/include/linux/msi.h
> @@ -556,6 +556,8 @@ enum {
> MSI_FLAG_USE_DEV_FWNODE = (1 << 7),
> /* Set parent->dev into domain->pm_dev on device domain creation */
> MSI_FLAG_PARENT_PM_DEV = (1 << 8),
> + /* Support for parent mask/unmask */
> + MSI_FLAG_PCI_MSI_MASK_PARENT = (1 << 9),
>
> /* Mask for the generic functionality */
> MSI_GENERIC_FLAGS_MASK = GENMASK(15, 0),
> --
> 2.34.1
>

2024-06-15 17:24:17

by Marc Zyngier

[permalink] [raw]
Subject: Re: [PATCH v3 14/24] genirq/gic-v3-mbi: Remove unused wired MSI mechanics

On Fri, 14 Jun 2024 11:23:53 +0100,
Shivamurthy Shastri <[email protected]> wrote:
>
> From: Thomas Gleixner <[email protected]>
>
> Nothing builds a platform_device MSI domain for wire to MSI on top of
> this. The "regular" users of the platform MSI domain just provide their own
> irq_write_msi_msg() callback.
>
> Signed-off-by: Thomas Gleixner <[email protected]>
> Signed-off-by: Anna-Maria Behnsen <[email protected]>
> Signed-off-by: Shivamurthy Shastri <[email protected]>
> Cc: Thomas Gleixner <[email protected]>
> Cc: Marc Zyngier <[email protected]>
> ---
> drivers/irqchip/irq-gic-v3-mbi.c | 17 +----------------
> 1 file changed, 1 insertion(+), 16 deletions(-)
>
> diff --git a/drivers/irqchip/irq-gic-v3-mbi.c b/drivers/irqchip/irq-gic-v3-mbi.c
> index dbb8b1efda44..19298cc6c2ee 100644
> --- a/drivers/irqchip/irq-gic-v3-mbi.c
> +++ b/drivers/irqchip/irq-gic-v3-mbi.c
> @@ -199,31 +199,16 @@ static int mbi_allocate_pci_domain(struct irq_domain *nexus_domain,
> }
> #endif
>
> -static void mbi_compose_mbi_msg(struct irq_data *data, struct msi_msg *msg)
> -{
> - mbi_compose_msi_msg(data, msg);
> -
> - msg[1].address_hi = upper_32_bits(mbi_phys_base + GICD_CLRSPI_NSR);
> - msg[1].address_lo = lower_32_bits(mbi_phys_base + GICD_CLRSPI_NSR);
> - msg[1].data = data->parent_data->hwirq;
> -
> - iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), &msg[1]);
> -}
> -
> /* Platform-MSI specific irqchip */
> static struct irq_chip mbi_pmsi_irq_chip = {
> .name = "pMSI",
> - .irq_set_type = irq_chip_set_type_parent,
> - .irq_compose_msi_msg = mbi_compose_mbi_msg,
> - .flags = IRQCHIP_SUPPORTS_LEVEL_MSI,
> };
>
> static struct msi_domain_ops mbi_pmsi_ops = {
> };
>
> static struct msi_domain_info mbi_pmsi_domain_info = {
> - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> - MSI_FLAG_LEVEL_CAPABLE),
> + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
> .ops = &mbi_pmsi_ops,
> .chip = &mbi_pmsi_irq_chip,
> };

This patch doesn't do what it says. It simply kills any form of level
MSI support for *endpoints*, and has nothing to do with any sort of
"wire to MSI".

What replaces it?

M.

--
Without deviation from the norm, progress is not possible.