From 1fb7392ee3408494d4d62c09a8c3e5f5934caba7 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 24 Feb 2026 10:09:31 +0000 Subject: [PATCH 01/11] fsl-mc: Remove MSI domain propagation to sub-devices Only the root device generates MSIs (it is the only one talking to the ITS), so propagating the domain is pretty pointless. Remove it. Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei # LX2160ARDB, LS2088ARDB Tested-by: Sascha Bischoff Signed-off-by: Marc Zyngier Acked-by: Thomas Gleixner Link: https://lore.kernel.org/r/20260224100936.3752303-2-maz@kernel.org Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/bus/fsl-mc/fsl-mc-bus.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c index c117745cf206..1a6c8e3c0b27 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -854,14 +854,12 @@ int fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc, } else { /* * A non-DPRC object has to be a child of a DPRC, use the - * parent's ICID and interrupt domain. + * parent's ICID. */ mc_dev->icid = parent_mc_dev->icid; mc_dev->dma_mask = FSL_MC_DEFAULT_DMA_MASK; mc_dev->dev.dma_mask = &mc_dev->dma_mask; mc_dev->dev.coherent_dma_mask = mc_dev->dma_mask; - dev_set_msi_domain(&mc_dev->dev, - dev_get_msi_domain(&parent_mc_dev->dev)); } /* From 0c9f522f2d41c7e055a602a0d2c41dc7af01010b Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 24 Feb 2026 10:09:32 +0000 Subject: [PATCH 02/11] fsl-mc: Add minimal infrastructure to use platform MSI Add the tiny bit of infrastructure required to use platform MSI instead of the current hack. This means providing a write_msi_msg callback, as well as irq domain and devid retrieval helpers. Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei # LX2160ARDB, LS2088ARDB Tested-by: Sascha Bischoff Signed-off-by: Marc Zyngier Acked-by: Thomas Gleixner Link: https://lore.kernel.org/r/20260224100936.3752303-3-maz@kernel.org Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/bus/fsl-mc/fsl-mc-msi.c | 50 ++++++++++++++++++++++++----- drivers/bus/fsl-mc/fsl-mc-private.h | 1 + include/linux/fsl/mc.h | 2 ++ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/drivers/bus/fsl-mc/fsl-mc-msi.c b/drivers/bus/fsl-mc/fsl-mc-msi.c index 82cd69f7884c..c9f50969e88c 100644 --- a/drivers/bus/fsl-mc/fsl-mc-msi.c +++ b/drivers/bus/fsl-mc/fsl-mc-msi.c @@ -110,13 +110,8 @@ static void __fsl_mc_msi_write_msg(struct fsl_mc_device *mc_bus_dev, } } -/* - * NOTE: This function is invoked with interrupts disabled - */ -static void fsl_mc_msi_write_msg(struct irq_data *irq_data, - struct msi_msg *msg) +static void fsl_mc_write_msi_msg(struct msi_desc *msi_desc, struct msi_msg *msg) { - struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data); struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(msi_desc->dev); struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); struct fsl_mc_device_irq *mc_dev_irq = @@ -130,6 +125,17 @@ static void fsl_mc_msi_write_msg(struct irq_data *irq_data, __fsl_mc_msi_write_msg(mc_bus_dev, mc_dev_irq, msi_desc); } +/* + * NOTE: This function is invoked with interrupts disabled + */ +static void fsl_mc_msi_write_msg(struct irq_data *irq_data, + struct msi_msg *msg) +{ + struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data); + + fsl_mc_write_msi_msg(msi_desc, msg); +} + static void fsl_mc_msi_update_chip_ops(struct msi_domain_info *info) { struct irq_chip *chip = info->chip; @@ -209,6 +215,20 @@ struct irq_domain *fsl_mc_find_msi_domain(struct device *dev) return msi_domain; } +struct irq_domain *fsl_mc_get_msi_parent(struct device *dev) +{ + struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); + struct device *root_dprc_dev; + struct device *bus_dev; + + fsl_mc_get_root_dprc(dev, &root_dprc_dev); + bus_dev = root_dprc_dev->parent; + + return (bus_dev->of_node ? + of_msi_get_domain(bus_dev, bus_dev->of_node, DOMAIN_BUS_NEXUS) : + iort_get_device_domain(bus_dev, mc_dev->icid, DOMAIN_BUS_NEXUS)); +} + int fsl_mc_msi_domain_alloc_irqs(struct device *dev, unsigned int irq_count) { int error = msi_setup_device_data(dev); @@ -220,8 +240,10 @@ int fsl_mc_msi_domain_alloc_irqs(struct device *dev, unsigned int irq_count) * NOTE: Calling this function will trigger the invocation of the * its_fsl_mc_msi_prepare() callback */ - error = msi_domain_alloc_irqs_range(dev, MSI_DEFAULT_DOMAIN, 0, irq_count - 1); - + if (!irq_domain_is_msi_parent(dev_get_msi_domain(dev))) + error = msi_domain_alloc_irqs_range(dev, MSI_DEFAULT_DOMAIN, 0, irq_count - 1); + else + error = platform_device_msi_init_and_alloc_irqs(dev, irq_count, fsl_mc_write_msi_msg); if (error) dev_err(dev, "Failed to allocate IRQs\n"); return error; @@ -231,3 +253,15 @@ void fsl_mc_msi_domain_free_irqs(struct device *dev) { msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN); } + +u32 fsl_mc_get_msi_id(struct device *dev) +{ + struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); + struct device *root_dprc_dev; + + fsl_mc_get_root_dprc(dev, &root_dprc_dev); + + return (root_dprc_dev->parent->of_node ? + of_msi_xlate(dev, NULL, mc_dev->icid) : + iort_msi_map_id(dev, mc_dev->icid)); +} diff --git a/drivers/bus/fsl-mc/fsl-mc-private.h b/drivers/bus/fsl-mc/fsl-mc-private.h index beed4c53533d..44868f874fd6 100644 --- a/drivers/bus/fsl-mc/fsl-mc-private.h +++ b/drivers/bus/fsl-mc/fsl-mc-private.h @@ -642,6 +642,7 @@ int fsl_mc_msi_domain_alloc_irqs(struct device *dev, void fsl_mc_msi_domain_free_irqs(struct device *dev); struct irq_domain *fsl_mc_find_msi_domain(struct device *dev); +struct irq_domain *fsl_mc_get_msi_parent(struct device *dev); int __must_check fsl_create_mc_io(struct device *dev, phys_addr_t mc_portal_phys_addr, diff --git a/include/linux/fsl/mc.h b/include/linux/fsl/mc.h index 897d6211c163..bcc38c0fc230 100644 --- a/include/linux/fsl/mc.h +++ b/include/linux/fsl/mc.h @@ -361,9 +361,11 @@ int mc_send_command(struct fsl_mc_io *mc_io, struct fsl_mc_command *cmd); #ifdef CONFIG_FSL_MC_BUS #define dev_is_fsl_mc(_dev) ((_dev)->bus == &fsl_mc_bus_type) +u32 fsl_mc_get_msi_id(struct device *dev); #else /* If fsl-mc bus is not present device cannot belong to fsl-mc bus */ #define dev_is_fsl_mc(_dev) (0) +#define fsl_mc_get_msi_id(_dev) (0) #endif /* Macro to check if a device is a container device */ From cf3179b4e53f527aba9f0c6c3b921619c8adf761 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 24 Feb 2026 10:09:33 +0000 Subject: [PATCH 03/11] irqchip/gic-v3-its: Add fsl_mc device plumbing to the msi-parent handling Make the ITS code aware of fsl_mc devices by plumbing the devid retrieval primitive. Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei # LX2160ARDB, LS2088ARDB Tested-by: Sascha Bischoff Signed-off-by: Marc Zyngier Acked-by: Thomas Gleixner Link: https://lore.kernel.org/r/20260224100936.3752303-4-maz@kernel.org Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/irqchip/irq-gic-its-msi-parent.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c index a832cdb2e697..d36b278ae66c 100644 --- a/drivers/irqchip/irq-gic-its-msi-parent.c +++ b/drivers/irqchip/irq-gic-its-msi-parent.c @@ -5,6 +5,7 @@ // Copyright (C) 2022 Intel #include +#include #include #include @@ -187,9 +188,11 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, { struct msi_domain_info *msi_info; u32 dev_id; - int ret; + int ret = 0; - if (dev->of_node) + if (dev_is_fsl_mc(dev)) + dev_id = fsl_mc_get_msi_id(dev); + else if (dev->of_node) ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, NULL); else ret = iort_pmsi_get_msi_info(dev, &dev_id, NULL); From 4a958e47c246fa3fb8954f4303e0da15ab3d026d Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 24 Feb 2026 10:09:34 +0000 Subject: [PATCH 04/11] fsl-mc: Switch over to per-device platform MSI Obtain the msi-parent irqdomain instead of the fsl_mc domain, which magically engages the per-device infrastructure. Additionally, simplify the overly complicated error handling. Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei # LX2160ARDB, LS2088ARDB Tested-by: Sascha Bischoff Signed-off-by: Marc Zyngier Acked-by: Thomas Gleixner Link: https://lore.kernel.org/r/20260224100936.3752303-5-maz@kernel.org Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/bus/fsl-mc/dprc-driver.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c index db67442addad..a85706826fa0 100644 --- a/drivers/bus/fsl-mc/dprc-driver.c +++ b/drivers/bus/fsl-mc/dprc-driver.c @@ -609,9 +609,8 @@ int dprc_setup(struct fsl_mc_device *mc_dev) { struct device *parent_dev = mc_dev->dev.parent; struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); - struct irq_domain *mc_msi_domain; + struct irq_domain *mc_msi_domain = NULL; bool mc_io_created = false; - bool msi_domain_set = false; bool uapi_created = false; u16 major_ver, minor_ver; size_t region_size; @@ -652,14 +651,12 @@ int dprc_setup(struct fsl_mc_device *mc_dev) uapi_created = true; } - mc_msi_domain = fsl_mc_find_msi_domain(&mc_dev->dev); - if (!mc_msi_domain) { + mc_msi_domain = fsl_mc_get_msi_parent(&mc_dev->dev); + if (!mc_msi_domain) dev_warn(&mc_dev->dev, "WARNING: MC bus without interrupt support\n"); - } else { + else dev_set_msi_domain(&mc_dev->dev, mc_msi_domain); - msi_domain_set = true; - } error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, &mc_dev->mc_handle); @@ -699,8 +696,7 @@ int dprc_setup(struct fsl_mc_device *mc_dev) (void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); error_cleanup_msi_domain: - if (msi_domain_set) - dev_set_msi_domain(&mc_dev->dev, NULL); + dev_set_msi_domain(&mc_dev->dev, NULL); if (mc_io_created) { fsl_destroy_mc_io(mc_dev->mc_io); From 14b1cbcc6cec0b02298f4adf717646cd943b7ef6 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 24 Feb 2026 10:09:35 +0000 Subject: [PATCH 05/11] fsl-mc: Remove legacy MSI implementation Get rid of most of the fsl_mc MSI infrastructure, which is now replaced by common code. Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei # LX2160ARDB, LS2088ARDB Tested-by: Sascha Bischoff Signed-off-by: Marc Zyngier Acked-by: Thomas Gleixner Link: https://lore.kernel.org/r/20260224100936.3752303-6-maz@kernel.org Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/bus/fsl-mc/fsl-mc-msi.c | 166 +------------------ drivers/bus/fsl-mc/fsl-mc-private.h | 1 - drivers/irqchip/Kconfig | 6 - drivers/irqchip/Makefile | 1 - drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c | 168 -------------------- include/linux/fsl/mc.h | 4 - include/linux/irqdomain_defs.h | 1 - 7 files changed, 8 insertions(+), 339 deletions(-) delete mode 100644 drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c diff --git a/drivers/bus/fsl-mc/fsl-mc-msi.c b/drivers/bus/fsl-mc/fsl-mc-msi.c index c9f50969e88c..be38b43803de 100644 --- a/drivers/bus/fsl-mc/fsl-mc-msi.c +++ b/drivers/bus/fsl-mc/fsl-mc-msi.c @@ -15,53 +15,16 @@ #include "fsl-mc-private.h" -#ifdef GENERIC_MSI_DOMAIN_OPS -/* - * Generate a unique ID identifying the interrupt (only used within the MSI - * irqdomain. Combine the icid with the interrupt index. - */ -static irq_hw_number_t fsl_mc_domain_calc_hwirq(struct fsl_mc_device *dev, - struct msi_desc *desc) +static void fsl_mc_write_msi_msg(struct msi_desc *msi_desc, struct msi_msg *msg) { - /* - * Make the base hwirq value for ICID*10000 so it is readable - * as a decimal value in /proc/interrupts. - */ - return (irq_hw_number_t)(desc->msi_index + (dev->icid * 10000)); -} - -static void fsl_mc_msi_set_desc(msi_alloc_info_t *arg, - struct msi_desc *desc) -{ - arg->desc = desc; - arg->hwirq = fsl_mc_domain_calc_hwirq(to_fsl_mc_device(desc->dev), - desc); -} -#else -#define fsl_mc_msi_set_desc NULL -#endif - -static void fsl_mc_msi_update_dom_ops(struct msi_domain_info *info) -{ - struct msi_domain_ops *ops = info->ops; - - if (!ops) - return; - - /* - * set_desc should not be set by the caller - */ - if (!ops->set_desc) - ops->set_desc = fsl_mc_msi_set_desc; -} - -static void __fsl_mc_msi_write_msg(struct fsl_mc_device *mc_bus_dev, - struct fsl_mc_device_irq *mc_dev_irq, - struct msi_desc *msi_desc) -{ - int error; + struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(msi_desc->dev); + struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); + struct fsl_mc_device_irq *mc_dev_irq = &mc_bus->irq_resources[msi_desc->msi_index]; struct fsl_mc_device *owner_mc_dev = mc_dev_irq->mc_dev; struct dprc_irq_cfg irq_cfg; + int error; + + msi_desc->msg = *msg; /* * msi_desc->msg.address is 0x0 when this function is invoked in @@ -110,111 +73,6 @@ static void __fsl_mc_msi_write_msg(struct fsl_mc_device *mc_bus_dev, } } -static void fsl_mc_write_msi_msg(struct msi_desc *msi_desc, struct msi_msg *msg) -{ - struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(msi_desc->dev); - struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); - struct fsl_mc_device_irq *mc_dev_irq = - &mc_bus->irq_resources[msi_desc->msi_index]; - - msi_desc->msg = *msg; - - /* - * Program the MSI (paddr, value) pair in the device: - */ - __fsl_mc_msi_write_msg(mc_bus_dev, mc_dev_irq, msi_desc); -} - -/* - * NOTE: This function is invoked with interrupts disabled - */ -static void fsl_mc_msi_write_msg(struct irq_data *irq_data, - struct msi_msg *msg) -{ - struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data); - - fsl_mc_write_msi_msg(msi_desc, msg); -} - -static void fsl_mc_msi_update_chip_ops(struct msi_domain_info *info) -{ - struct irq_chip *chip = info->chip; - - if (!chip) - return; - - /* - * irq_write_msi_msg should not be set by the caller - */ - if (!chip->irq_write_msi_msg) - chip->irq_write_msi_msg = fsl_mc_msi_write_msg; -} - -/** - * fsl_mc_msi_create_irq_domain - Create a fsl-mc MSI interrupt domain - * @fwnode: Optional firmware node of the interrupt controller - * @info: MSI domain info - * @parent: Parent irq domain - * - * Updates the domain and chip ops and creates a fsl-mc MSI - * interrupt domain. - * - * Returns: - * A domain pointer or NULL in case of failure. - */ -struct irq_domain *fsl_mc_msi_create_irq_domain(struct fwnode_handle *fwnode, - struct msi_domain_info *info, - struct irq_domain *parent) -{ - struct irq_domain *domain; - - if (WARN_ON((info->flags & MSI_FLAG_LEVEL_CAPABLE))) - info->flags &= ~MSI_FLAG_LEVEL_CAPABLE; - if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) - fsl_mc_msi_update_dom_ops(info); - if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) - fsl_mc_msi_update_chip_ops(info); - info->flags |= 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_FSL_MC_MSI); - - return domain; -} - -struct irq_domain *fsl_mc_find_msi_domain(struct device *dev) -{ - struct device *root_dprc_dev; - struct device *bus_dev; - struct irq_domain *msi_domain; - struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); - - fsl_mc_get_root_dprc(dev, &root_dprc_dev); - bus_dev = root_dprc_dev->parent; - - if (bus_dev->of_node) { - msi_domain = of_msi_map_get_device_domain(dev, - mc_dev->icid, - DOMAIN_BUS_FSL_MC_MSI); - - /* - * if the msi-map property is missing assume that all the - * child containers inherit the domain from the parent - */ - if (!msi_domain) - - msi_domain = of_msi_get_domain(bus_dev, - bus_dev->of_node, - DOMAIN_BUS_FSL_MC_MSI); - } else { - msi_domain = iort_get_device_domain(dev, mc_dev->icid, - DOMAIN_BUS_FSL_MC_MSI); - } - - return msi_domain; -} - struct irq_domain *fsl_mc_get_msi_parent(struct device *dev) { struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); @@ -232,18 +90,10 @@ struct irq_domain *fsl_mc_get_msi_parent(struct device *dev) int fsl_mc_msi_domain_alloc_irqs(struct device *dev, unsigned int irq_count) { int error = msi_setup_device_data(dev); - if (error) return error; - /* - * NOTE: Calling this function will trigger the invocation of the - * its_fsl_mc_msi_prepare() callback - */ - if (!irq_domain_is_msi_parent(dev_get_msi_domain(dev))) - error = msi_domain_alloc_irqs_range(dev, MSI_DEFAULT_DOMAIN, 0, irq_count - 1); - else - error = platform_device_msi_init_and_alloc_irqs(dev, irq_count, fsl_mc_write_msi_msg); + error = platform_device_msi_init_and_alloc_irqs(dev, irq_count, fsl_mc_write_msi_msg); if (error) dev_err(dev, "Failed to allocate IRQs\n"); return error; diff --git a/drivers/bus/fsl-mc/fsl-mc-private.h b/drivers/bus/fsl-mc/fsl-mc-private.h index 44868f874fd6..197edcc8cde4 100644 --- a/drivers/bus/fsl-mc/fsl-mc-private.h +++ b/drivers/bus/fsl-mc/fsl-mc-private.h @@ -641,7 +641,6 @@ int fsl_mc_msi_domain_alloc_irqs(struct device *dev, void fsl_mc_msi_domain_free_irqs(struct device *dev); -struct irq_domain *fsl_mc_find_msi_domain(struct device *dev); struct irq_domain *fsl_mc_get_msi_parent(struct device *dev); int __must_check fsl_create_mc_io(struct device *dev, diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index f07b00d7fef9..3e5b2040cb4e 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -51,12 +51,6 @@ config ARM_GIC_V3_ITS default ARM_GIC_V3 select IRQ_MSI_IOMMU -config ARM_GIC_V3_ITS_FSL_MC - bool - depends on ARM_GIC_V3_ITS - depends on FSL_MC_BUS - default ARM_GIC_V3_ITS - config ARM_GIC_V5 bool select IRQ_DOMAIN_HIERARCHY diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 26aa3b6ec99f..d5a28cee0d8e 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -35,7 +35,6 @@ 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_ITS_PARENT) += irq-gic-its-msi-parent.o obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v4.o -obj-$(CONFIG_ARM_GIC_V3_ITS_FSL_MC) += irq-gic-v3-its-fsl-mc-msi.o obj-$(CONFIG_ARM_GIC_V5) += irq-gic-v5.o irq-gic-v5-irs.o irq-gic-v5-its.o \ irq-gic-v5-iwb.o obj-$(CONFIG_HISILICON_IRQ_MBIGEN) += irq-mbigen.o diff --git a/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c b/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c deleted file mode 100644 index b5785472765a..000000000000 --- a/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c +++ /dev/null @@ -1,168 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Freescale Management Complex (MC) bus driver MSI support - * - * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. - * Author: German Rivera - * - */ - -#include -#include -#include -#include -#include -#include -#include - -static struct irq_chip its_msi_irq_chip = { - .name = "ITS-fMSI", - .irq_mask = irq_chip_mask_parent, - .irq_unmask = irq_chip_unmask_parent, - .irq_eoi = irq_chip_eoi_parent, - .irq_set_affinity = msi_domain_set_affinity -}; - -static u32 fsl_mc_msi_domain_get_msi_id(struct irq_domain *domain, - struct fsl_mc_device *mc_dev) -{ - struct device_node *of_node; - u32 out_id; - - of_node = irq_domain_get_of_node(domain); - out_id = of_node ? of_msi_xlate(&mc_dev->dev, &of_node, mc_dev->icid) : - iort_msi_map_id(&mc_dev->dev, mc_dev->icid); - - return out_id; -} - -static int its_fsl_mc_msi_prepare(struct irq_domain *msi_domain, - struct device *dev, - int nvec, msi_alloc_info_t *info) -{ - struct fsl_mc_device *mc_bus_dev; - struct msi_domain_info *msi_info; - - if (!dev_is_fsl_mc(dev)) - return -EINVAL; - - mc_bus_dev = to_fsl_mc_device(dev); - if (!(mc_bus_dev->flags & FSL_MC_IS_DPRC)) - return -EINVAL; - - /* - * Set the device Id to be passed to the GIC-ITS: - * - * NOTE: This device id corresponds to the IOMMU stream ID - * associated with the DPRC object (ICID). - */ - info->scratchpad[0].ul = fsl_mc_msi_domain_get_msi_id(msi_domain, - mc_bus_dev); - msi_info = msi_get_domain_info(msi_domain->parent); - - /* 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(msi_domain->parent, dev, nvec, info); -} - -static struct msi_domain_ops its_fsl_mc_msi_ops __ro_after_init = { - .msi_prepare = its_fsl_mc_msi_prepare, -}; - -static struct msi_domain_info its_fsl_mc_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), - .ops = &its_fsl_mc_msi_ops, - .chip = &its_msi_irq_chip, -}; - -static const struct of_device_id its_device_id[] = { - { .compatible = "arm,gic-v3-its", }, - {}, -}; - -static void __init its_fsl_mc_msi_init_one(struct fwnode_handle *handle, - const char *name) -{ - struct irq_domain *parent; - struct irq_domain *mc_msi_domain; - - 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; - } - - mc_msi_domain = fsl_mc_msi_create_irq_domain(handle, - &its_fsl_mc_msi_domain_info, - parent); - if (!mc_msi_domain) { - pr_err("%s: unable to create fsl-mc domain\n", name); - return; - } - - pr_info("fsl-mc MSI: %s domain created\n", name); -} - -#ifdef CONFIG_ACPI -static int __init -its_fsl_mc_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 = 0; - - 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); - err = -ENXIO; - goto out; - } - - its_fsl_mc_msi_init_one(dom_handle, node_name); - -out: - kfree(node_name); - return err; -} - - -static void __init its_fsl_mc_acpi_msi_init(void) -{ - acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR, - its_fsl_mc_msi_parse_madt, 0); -} -#else -static inline void its_fsl_mc_acpi_msi_init(void) { } -#endif - -static void __init its_fsl_mc_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; - - its_fsl_mc_msi_init_one(of_fwnode_handle(np), - np->full_name); - } -} - -static int __init its_fsl_mc_msi_init(void) -{ - its_fsl_mc_of_msi_init(); - its_fsl_mc_acpi_msi_init(); - - return 0; -} - -early_initcall(its_fsl_mc_msi_init); diff --git a/include/linux/fsl/mc.h b/include/linux/fsl/mc.h index bcc38c0fc230..e9522233f9b5 100644 --- a/include/linux/fsl/mc.h +++ b/include/linux/fsl/mc.h @@ -425,10 +425,6 @@ int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev, void fsl_mc_object_free(struct fsl_mc_device *mc_adev); -struct irq_domain *fsl_mc_msi_create_irq_domain(struct fwnode_handle *fwnode, - struct msi_domain_info *info, - struct irq_domain *parent); - int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev); void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev); diff --git a/include/linux/irqdomain_defs.h b/include/linux/irqdomain_defs.h index 36653e2ee1c9..3a03bdfeeee9 100644 --- a/include/linux/irqdomain_defs.h +++ b/include/linux/irqdomain_defs.h @@ -17,7 +17,6 @@ enum irq_domain_bus_token { DOMAIN_BUS_PLATFORM_MSI, DOMAIN_BUS_NEXUS, DOMAIN_BUS_IPI, - DOMAIN_BUS_FSL_MC_MSI, DOMAIN_BUS_TI_SCI_INTA_MSI, DOMAIN_BUS_WAKEUP, DOMAIN_BUS_VMD_MSI, From f0a2eac6a597268034fd40d92c1469182438b53d Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 24 Feb 2026 10:09:36 +0000 Subject: [PATCH 06/11] platform-msi: Remove stale comment The backward compatibility code for the previous incarnation of platform MSI was removed in e9894248994ca ("genirq/msi: Remove platform MSI leftovers"), but the comment about that removal is still present. Remove it. Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei # LX2160ARDB, LS2088ARDB Tested-by: Sascha Bischoff Signed-off-by: Marc Zyngier Acked-by: Thomas Gleixner Link: https://lore.kernel.org/r/20260224100936.3752303-7-maz@kernel.org Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/base/platform-msi.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c index 70db08f3ac6f..69eed058eb20 100644 --- a/drivers/base/platform-msi.c +++ b/drivers/base/platform-msi.c @@ -61,10 +61,6 @@ static const struct msi_domain_template platform_msi_template = { * parent. The parent domain sets up the new domain. The domain has * a fixed size of @nvec. The domain is managed by devres and will * be removed when the device is removed. - * - * Note: For migration purposes this falls back to the original platform_msi code - * up to the point where all platforms have been converted to the MSI - * parent model. */ int platform_device_msi_init_and_alloc_irqs(struct device *dev, unsigned int nvec, irq_write_msi_msg_t write_msi_msg) From cf8e9203cc653f0a82639f7ce8089fa92afe6739 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 26 Feb 2026 14:09:42 -0800 Subject: [PATCH 07/11] virt: fsl_hypervisor: fix header kernel-doc warnings Correct struct member names to placate kernel-doc warnings: Warning: include/uapi/linux/fsl_hypervisor.h:148 struct member 'local_vaddr' not described in 'fsl_hv_ioctl_memcpy' Warning: include/uapi/linux/fsl_hypervisor.h:148 struct member 'remote_paddr' not described in 'fsl_hv_ioctl_memcpy' Fixes: 6db7199407ca ("drivers/virt: introduce Freescale hypervisor management driver") Signed-off-by: Randy Dunlap Link: https://lore.kernel.org/r/20260226220942.1035295-1-rdunlap@infradead.org Signed-off-by: Christophe Leroy (CS GROUP) --- include/uapi/linux/fsl_hypervisor.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/fsl_hypervisor.h b/include/uapi/linux/fsl_hypervisor.h index 1e237fba951f..ab4388441e80 100644 --- a/include/uapi/linux/fsl_hypervisor.h +++ b/include/uapi/linux/fsl_hypervisor.h @@ -114,9 +114,9 @@ struct fsl_hv_ioctl_stop { * @target: the partition ID of the target partition, or -1 for this * partition * @reserved: reserved, must be set to 0 - * @local_addr: user-space virtual address of a buffer in the local + * @local_vaddr: user-space virtual address of a buffer in the local * partition - * @remote_addr: guest physical address of a buffer in the + * @remote_paddr: guest physical address of a buffer in the * remote partition * @count: the number of bytes to copy. Both the local and remote * buffers must be at least 'count' bytes long From 7dad18a179741dbad9f40799e549fa9111987c0c Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 10 Mar 2026 00:25:30 +0800 Subject: [PATCH 08/11] soc: fsl: qe_ports_ic: Add missing cleanup on device removal Add a devm action handler to properly clean up the irq_domain and chained handler when the device is removed. Fixes: f0bcd784e1b7 ("soc: fsl: qe: Add an interrupt controller for QUICC Engine Ports") Signed-off-by: Felix Gu Link: https://lore.kernel.org/r/20260310-qe_ports_ic-v1-1-608293026561@gmail.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/qe_ports_ic.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c index 8e2107e2cde5..5e3fae19f314 100644 --- a/drivers/soc/fsl/qe/qe_ports_ic.c +++ b/drivers/soc/fsl/qe/qe_ports_ic.c @@ -17,6 +17,7 @@ struct qepic_data { void __iomem *reg; struct irq_domain *host; + int irq; }; static void qepic_mask(struct irq_data *d) @@ -92,11 +93,18 @@ static const struct irq_domain_ops qepic_host_ops = { .map = qepic_host_map, }; +static void qepic_remove(void *res) +{ + struct qepic_data *data = res; + + irq_set_chained_handler_and_data(data->irq, NULL, NULL); + irq_domain_remove(data->host); +} + static int qepic_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct qepic_data *data; - int irq; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -106,17 +114,18 @@ static int qepic_probe(struct platform_device *pdev) if (IS_ERR(data->reg)) return PTR_ERR(data->reg); - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + data->irq = platform_get_irq(pdev, 0); + if (data->irq < 0) + return data->irq; data->host = irq_domain_add_linear(dev->of_node, 32, &qepic_host_ops, data); if (!data->host) return -ENODEV; - irq_set_chained_handler_and_data(irq, qepic_cascade, data); + irq_set_chained_handler_and_data(data->irq, qepic_cascade, data); + + return devm_add_action_or_reset(dev, qepic_remove, data); - return 0; } static const struct of_device_id qepic_match[] = { From 7b9233b25131fa54ef9c64ebbe95aef6ad3de47c Mon Sep 17 00:00:00 2001 From: "Christophe Leroy (CS GROUP)" Date: Tue, 24 Mar 2026 23:34:16 +0100 Subject: [PATCH 09/11] soc: fsl: qe_ports_ic: switch to irq_domain_create_linear() irq_domain_add_linear() is about to be removed, replace by the more generic irq_domain_create_linear(), see commit 42b8b16fe56c ("irqdomain: Drop irq_domain_add_*() functions") for details. Link: https://lore.kernel.org/r/a9de2f351ea71e4b794baaea8d9d790fbfac8d26.1774391374.git.chleroy@kernel.org Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/qe_ports_ic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c index 5e3fae19f314..9b0bba64e91e 100644 --- a/drivers/soc/fsl/qe/qe_ports_ic.c +++ b/drivers/soc/fsl/qe/qe_ports_ic.c @@ -118,7 +118,7 @@ static int qepic_probe(struct platform_device *pdev) if (data->irq < 0) return data->irq; - data->host = irq_domain_add_linear(dev->of_node, 32, &qepic_host_ops, data); + data->host = irq_domain_create_linear(dev_fwnode(dev), 32, &qepic_host_ops, data); if (!data->host) return -ENODEV; From a0fe29d20e7822182e12324905af5115c1b3aed3 Mon Sep 17 00:00:00 2001 From: Wang Jun <1742789905@qq.com> Date: Fri, 27 Mar 2026 08:12:25 +0800 Subject: [PATCH 10/11] soc: fsl: qe: panic on ioremap() failure in qe_reset() When ioremap() fails in qe_reset(), the global pointer qe_immr remains NULL, leading to a subsequent NULL pointer dereference when the pointer is accessed. Since this happens early in the boot process, a failure to map a few bytes of I/O memory indicates a fatal error from which the system cannot recover. Follow the same pattern as qe_sdma_init() and panic immediately when ioremap() fails. This avoids a silent NULL pointer dereference later and makes the error explicit. Fixes: 986585385131 ("[POWERPC] Add QUICC Engine (QE) infrastructure") Cc: stable@vger.kernel.org Signed-off-by: Wang Jun <1742789905@qq.com> Link: https://lore.kernel.org/r/tencent_FED49CF5331CC0C7910618883332A08E2606@qq.com [chleroy: Rearranged change to reduce churn] Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/qe.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soc/fsl/qe/qe.c b/drivers/soc/fsl/qe/qe.c index 70b6eddb867b..3ecc4ce9cfa2 100644 --- a/drivers/soc/fsl/qe/qe.c +++ b/drivers/soc/fsl/qe/qe.c @@ -89,6 +89,9 @@ void qe_reset(void) if (qe_immr == NULL) qe_immr = ioremap(get_qe_base(), QE_IMMAP_SIZE); + if (!qe_immr) + panic("QE:ioremap failed!"); + qe_snums_init(); qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID, From 208858b1b48eba83d073542372329cf8ed606526 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Wed, 1 Apr 2026 17:45:08 +0300 Subject: [PATCH 11/11] bus: fsl-mc: wait for the MC firmware to complete its boot There are use cases in which the Management Complex firmware boot process is started by the bootloader which does not wait for the boot to complete. This is mainly done in order to reduce the overall boot time of a DPAA2 based SoC. In this kind of circumstance, the fsl-mc bus driver needs to make sure that the MC firmware boot process is finished before proceeding to the usual operations such as interrogating the firmware to gather all existent DPAA2 objects, creating the fsl-mc devices on the bus etc. Add this kind of check early in the boot process of the fsl-mc bus and defer the probe in case the firmware is still in its boot process. Signed-off-by: Ioana Ciornei Link: https://lore.kernel.org/r/20260401144508.3062019-1-ioana.ciornei@nxp.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/bus/fsl-mc/fsl-mc-bus.c | 46 +++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c index 1a6c8e3c0b27..994834a87b2f 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -66,6 +66,13 @@ struct fsl_mc_addr_translation_range { #define GCR1_P1_STOP BIT(31) #define GCR1_P2_STOP BIT(30) +#define FSL_MC_GSR 0x8 +#define FSL_MC_GSR_BOOT_DONE BIT(0) +#define FSL_MC_GSR_MCS_MASK GENMASK(7, 0) +#define FSL_MC_GSR_MCS_ERR_MASK GENMASK(7, 1) +#define FSL_MC_GSR_BC_MASK GENMASK(15, 8) +#define FSL_MC_GSR_BC_SHIFT 8 + #define FSL_MC_FAPR 0x28 #define MC_FAPR_PL BIT(18) #define MC_FAPR_BMT BIT(17) @@ -1019,6 +1026,41 @@ static int get_mc_addr_translation_ranges(struct device *dev, return 0; } +static u32 fsl_mc_read_gsr(struct fsl_mc *mc) +{ + return readl(mc->fsl_mc_regs + FSL_MC_GSR); +} + +static int fsl_mc_firmware_check(struct platform_device *pdev) +{ + struct fsl_mc *mc = platform_get_drvdata(pdev); + u32 gsr, boot_done, boot_code, mcs; + + gsr = fsl_mc_read_gsr(mc); + boot_code = (gsr & FSL_MC_GSR_BC_MASK) >> FSL_MC_GSR_BC_SHIFT; + if (boot_code == 0xDD) { + dev_err(&pdev->dev, + "fsl-mc: DPL processing was not started, DPAA2 will not work!\n"); + return -EOPNOTSUPP; + } + + boot_done = gsr & FSL_MC_GSR_BOOT_DONE; + if (!boot_done) { + dev_dbg(&pdev->dev, + "fsl-mc: DPL processing in progress, defer probe\n"); + return -EPROBE_DEFER; + } + + mcs = gsr & FSL_MC_GSR_MCS_MASK; + if (mcs & FSL_MC_GSR_MCS_ERR_MASK) { + dev_err(&pdev->dev, + "fsl-mc: MC boot completed with error 0x%x\n", mcs); + return -EINVAL; + } + + return 0; +} + /* * fsl_mc_bus_probe - callback invoked when the root MC bus is being * added @@ -1083,6 +1125,10 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) mc->fsl_mc_regs + FSL_MC_GCR1); } + error = fsl_mc_firmware_check(pdev); + if (error) + return error; + /* * Get physical address of MC portal for the root DPRC: */