From 281b26433b46eff0b32370d79d20060677d33e42 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 27 Oct 2023 21:12:54 +0530 Subject: [PATCH 01/20] of: property: Add fw_devlink support for msi-parent ANBZ: #20644 commit c5e4ce9db635fef5ebffdfba1e7d80710474b176 upstream. This allows fw_devlink to create device links between consumers of a MSI and the supplier of the MSI. Signed-off-by: Anup Patel Acked-by: Rob Herring Reviewed-by: Saravana Kannan Link: https://lore.kernel.org/r/20231027154254.355853-3-apatel@ventanamicro.com Signed-off-by: Palmer Dabbelt Signed-off-by: Zhang Anmeng --- drivers/of/property.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/of/property.c b/drivers/of/property.c index b3f0285e401c..15d115684325 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1238,6 +1238,7 @@ DEFINE_SIMPLE_PROP(resets, "resets", "#reset-cells") DEFINE_SIMPLE_PROP(leds, "leds", NULL) DEFINE_SIMPLE_PROP(backlight, "backlight", NULL) DEFINE_SIMPLE_PROP(panel, "panel", NULL) +DEFINE_SIMPLE_PROP(msi_parent, "msi-parent", "#msi-cells") DEFINE_SUFFIX_PROP(regulators, "-supply", NULL) DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells") @@ -1341,6 +1342,7 @@ static const struct supplier_bindings of_supplier_bindings[] = { { .parse_prop = parse_leds, }, { .parse_prop = parse_backlight, }, { .parse_prop = parse_panel, }, + { .parse_prop = parse_msi_parent, }, { .parse_prop = parse_gpio_compat, }, { .parse_prop = parse_interrupts, }, { .parse_prop = parse_regulators, }, -- Gitee From 6d45a87d29722b7f35c73e0bdc59c7d7395e775a Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Thu, 9 May 2024 17:38:20 +0530 Subject: [PATCH 02/20] of: property: Add fw_devlink support for interrupt-map property ANBZ: #20644 commit d976c6f4b32c2d273d44ff9ad7099efe16279a39 upstream. Some of the PCI host controllers (such as generic PCI host controller) use "interrupt-map" DT property to describe the mapping between PCI endpoints and PCI interrupt pins. This is the only case where the interrupts are not described in DT. Currently, there is no fw_devlink created based on "interrupt-map" DT property so interrupt controller is not guaranteed to be probed before the PCI host controller. This affects every platform where both PCI host controller and interrupt controllers are probed as regular platform devices. This creates fw_devlink between consumers (PCI host controller) and supplier (interrupt controller) based on "interrupt-map" DT property. Signed-off-by: Anup Patel Reviewed-by: Saravana Kannan Link: https://lore.kernel.org/r/20240509120820.1430587-1-apatel@ventanamicro.com Signed-off-by: Rob Herring (Arm) Signed-off-by: Zhang Anmeng --- drivers/of/property.c | 52 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/drivers/of/property.c b/drivers/of/property.c index 15d115684325..7c4ddcad03f1 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1298,6 +1298,57 @@ static struct device_node *parse_interrupts(struct device_node *np, return of_irq_parse_one(np, index, &sup_args) ? NULL : sup_args.np; } +static struct device_node *parse_interrupt_map(struct device_node *np, + const char *prop_name, int index) +{ + const __be32 *imap, *imap_end, *addr; + struct of_phandle_args sup_args; + u32 addrcells, intcells; + int i, imaplen; + + if (!IS_ENABLED(CONFIG_OF_IRQ)) + return NULL; + + if (strcmp(prop_name, "interrupt-map")) + return NULL; + + if (of_property_read_u32(np, "#interrupt-cells", &intcells)) + return NULL; + addrcells = of_bus_n_addr_cells(np); + + imap = of_get_property(np, "interrupt-map", &imaplen); + if (!imap || imaplen <= (addrcells + intcells)) + return NULL; + imap_end = imap + imaplen; + + while (imap < imap_end) { + addr = imap; + imap += addrcells; + + sup_args.np = np; + sup_args.args_count = intcells; + for (i = 0; i < intcells; i++) + sup_args.args[i] = be32_to_cpu(imap[i]); + imap += intcells; + + /* + * Upon success, the function of_irq_parse_raw() returns + * interrupt controller DT node pointer in sup_args.np. + */ + if (of_irq_parse_raw(addr, &sup_args)) + return NULL; + + if (!index) + return sup_args.np; + + of_node_put(sup_args.np); + imap += sup_args.args_count + 1; + index--; + } + + return NULL; +} + static struct device_node *parse_remote_endpoint(struct device_node *np, const char *prop_name, int index) @@ -1345,6 +1396,7 @@ static const struct supplier_bindings of_supplier_bindings[] = { { .parse_prop = parse_msi_parent, }, { .parse_prop = parse_gpio_compat, }, { .parse_prop = parse_interrupts, }, + { .parse_prop = parse_interrupt_map, }, { .parse_prop = parse_regulators, }, { .parse_prop = parse_gpio, }, { .parse_prop = parse_gpios, }, -- Gitee From 8bec68a4f13a1f842fb21160ea77d83f2a628232 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 29 May 2024 14:59:21 -0500 Subject: [PATCH 03/20] of: property: Fix fw_devlink handling of interrupt-map ANBZ: #20644 commit e7985f43609c782132f8f5794ee6cc4cdb66ca75 upstream. Commit d976c6f4b32c ("of: property: Add fw_devlink support for interrupt-map property") tried to do what it says on the tin, but failed on a couple of points: - it confuses bytes and cells. Not a huge deal, except when it comes to pointer arithmetic - it doesn't really handle anything but interrupt-maps that have their parent #address-cells set to 0 The combinations of the two leads to some serious fun on my M1 box, with plenty of WARN-ON() firing all over the shop, and amusing values being generated for interrupt specifiers. Having 2 versions of parsing code for "interrupt-map" was a bad idea. Now that the common parsing parts have been refactored into of_irq_parse_imap_parent(), rework the code here to use it instead and fix the pointer arithmetic. Note that the dependency will be a bit different than the original code when the interrupt-map points to another interrupt-map. In this case, the original code would resolve to the final interrupt controller. Now the dependency is the parent interrupt-map (which itself should have a dependency to the parent). It is possible that a node with an interrupt-map has no driver. Fixes: d976c6f4b32c ("of: property: Add fw_devlink support for interrupt-map property") Signed-off-by: Marc Zyngier Co-developed-by: Rob Herring (Arm) Cc: Saravana Kannan Tested-by: Marc Zyngier Tested-by: Anup Patel Link: https://lore.kernel.org/r/20240529-dt-interrupt-map-fix-v2-2-ef86dc5bcd2a@kernel.org Signed-off-by: Rob Herring (Arm) Signed-off-by: Zhang Anmeng --- drivers/of/property.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/drivers/of/property.c b/drivers/of/property.c index 7c4ddcad03f1..369a6f05e264 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1301,10 +1301,10 @@ static struct device_node *parse_interrupts(struct device_node *np, static struct device_node *parse_interrupt_map(struct device_node *np, const char *prop_name, int index) { - const __be32 *imap, *imap_end, *addr; + const __be32 *imap, *imap_end; struct of_phandle_args sup_args; u32 addrcells, intcells; - int i, imaplen; + int imaplen; if (!IS_ENABLED(CONFIG_OF_IRQ)) return NULL; @@ -1317,33 +1317,23 @@ static struct device_node *parse_interrupt_map(struct device_node *np, addrcells = of_bus_n_addr_cells(np); imap = of_get_property(np, "interrupt-map", &imaplen); - if (!imap || imaplen <= (addrcells + intcells)) + imaplen /= sizeof(*imap); + if (!imap) return NULL; - imap_end = imap + imaplen; - while (imap < imap_end) { - addr = imap; - imap += addrcells; + imap_end = imap + imaplen; - sup_args.np = np; - sup_args.args_count = intcells; - for (i = 0; i < intcells; i++) - sup_args.args[i] = be32_to_cpu(imap[i]); - imap += intcells; + for (int i = 0; imap + addrcells + intcells + 1 < imap_end; i++) { + imap += addrcells + intcells; - /* - * Upon success, the function of_irq_parse_raw() returns - * interrupt controller DT node pointer in sup_args.np. - */ - if (of_irq_parse_raw(addr, &sup_args)) + imap = of_irq_parse_imap_parent(imap, imap_end - imap, &sup_args); + if (!imap) return NULL; - if (!index) + if (i == index) return sup_args.np; of_node_put(sup_args.np); - imap += sup_args.args_count + 1; - index--; } return NULL; -- Gitee From 0e1ed983aeb99c1e40d58cc68364141c07258c93 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 Jan 2024 21:47:29 +0530 Subject: [PATCH 04/20] irqchip/gic-v3: Make gic_irq_domain_select() robust for zero parameter count ANBZ: #20644 commit 15137825100422c4c393c87af5aa5a8fa297b1f3 upstream. Currently the irqdomain select callback is only invoked when the parameter count of the fwspec arguments is not zero. That makes sense because then the match is on the firmware node and eventually on the bus_token, which is already handled in the core code. The upcoming support for per device MSI domains requires to do real bus token specific checks in the MSI parent domains with a zero parameter count. Make the gic-v3 select() callback handle that case. Signed-off-by: Thomas Gleixner Signed-off-by: Anup Patel Signed-off-by: Thomas Gleixner Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20240127161753.114685-2-apatel@ventanamicro.com Signed-off-by: Zhang Anmeng --- drivers/irqchip/irq-gic-v3.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index c13bbe48bae9..2a61bbd49195 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -1697,9 +1697,13 @@ static int gic_irq_domain_select(struct irq_domain *d, irq_hw_number_t hwirq; /* Not for us */ - if (fwspec->fwnode != d->fwnode) + if (fwspec->fwnode != d->fwnode) return 0; + /* Handle pure domain searches */ + if (!fwspec->param_count) + return d->bus_token == bus_token; + /* If this is not DT, then we have a single domain */ if (!is_of_node(fwspec->fwnode)) return 1; -- Gitee From 80535e4c91b7150fec25eafca412f6c3bc584fbb Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 Jan 2024 21:47:30 +0530 Subject: [PATCH 05/20] genirq/irqdomain: Remove the param count restriction from select() ANBZ: #20644 commit de1ff306dcf4546d6a8863b1f956335e0d3fbb9b upstream. Now that the GIC-v3 callback can handle invocation with a fwspec parameter count of 0 lift the restriction in the core code and invoke select() unconditionally when the domain provides it. Preparatory change for per device MSI domains. Signed-off-by: Thomas Gleixner Signed-off-by: Anup Patel Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240127161753.114685-3-apatel@ventanamicro.com Signed-off-by: Zhang Anmeng --- kernel/irq/irqdomain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 1826790096ae..a1a57dcd6f83 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -445,7 +445,7 @@ struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec, */ mutex_lock(&irq_domain_mutex); list_for_each_entry(h, &irq_domain_list, link) { - if (h->ops->select && fwspec->param_count) + if (h->ops->select) rc = h->ops->select(h, fwspec, bus_token); else if (h->ops->match) rc = h->ops->match(h, to_of_node(fwnode), bus_token); -- Gitee From d529bcb5a83287fc48bce298aa5dc31849078a93 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 20 Feb 2024 11:47:31 +0000 Subject: [PATCH 06/20] genirq/irqdomain: Don't call ops->select for DOMAIN_BUS_ANY tokens ANBZ: #20644 commit 5aa3c0cf5bba6437c9e63a56f684f61de8b503d6 upstream. Users of the IRQCHIP_PLATFORM_DRIVER_{BEGIN,END} helpers rely on a fwspec containing only the fwnode (and crucially a number of parameters set to 0) together with a DOMAIN_BUS_ANY token to check whether a parent irqchip has probed and registered a domain. Since de1ff306dcf4 ("genirq/irqdomain: Remove the param count restriction from select()"), ops->select() is called unconditionally, meaning that irqchips implementing select() now need to handle ANY as a match. Instead of adding more esoteric checks to the individual drivers, add that condition to irq_find_matching_fwspec(), and let it handle the corner case, as per the comment in the function. This restores the functionality of the above helpers. Fixes: de1ff306dcf4 ("genirq/irqdomain: Remove the param count restriction from select()") Reported-by: Dmitry Baryshkov Reported-by: Biju Das Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Tested-by: Dmitry Baryshkov Tested-by: Biju Das Link: https://lore.kernel.org/r/20240220114731.1898534-1-maz@kernel.org Link: https://lore.kernel.org/r/20240219-gic-fix-child-domain-v1-1-09f8fd2d9a8f@linaro.org Signed-off-by: Zhang Anmeng --- kernel/irq/irqdomain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index a1a57dcd6f83..a3dae87f2c52 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -445,7 +445,7 @@ struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec, */ mutex_lock(&irq_domain_mutex); list_for_each_entry(h, &irq_domain_list, link) { - if (h->ops->select) + if (h->ops->select && bus_token != DOMAIN_BUS_ANY) rc = h->ops->select(h, fwspec, bus_token); else if (h->ops->match) rc = h->ops->match(h, to_of_node(fwnode), bus_token); -- Gitee From 5d943462408098ffdee1efe0d8cfa49c01a3764b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 20 Feb 2024 09:46:19 +0100 Subject: [PATCH 07/20] irqchip/imx-intmux: Handle pure domain searches correctly ANBZ: #20644 commit 34da27aa8956d3a75c7556a59c9c7cfd0b3f18ab upstream. The removal of the paremeter count restriction in the core code to allow pure domain token based select() decisions broke the IMX intmux select callback as that unconditioally expects that there is a parameter. Add the missing check for zero parameter count and the token match. Fixes: de1ff306dcf4 ("genirq/irqdomain: Remove the param count restriction from select()") Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/87ttm3ikok.ffs@tglx Signed-off-by: Zhang Anmeng --- drivers/irqchip/irq-imx-intmux.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/irqchip/irq-imx-intmux.c b/drivers/irqchip/irq-imx-intmux.c index 6d9a08238c9d..091127017c0b 100644 --- a/drivers/irqchip/irq-imx-intmux.c +++ b/drivers/irqchip/irq-imx-intmux.c @@ -166,6 +166,10 @@ static int imx_intmux_irq_select(struct irq_domain *d, struct irq_fwspec *fwspec if (fwspec->fwnode != d->fwnode) return false; + /* Handle pure domain searches */ + if (!fwspec->param_count) + return d->bus_token == bus_token; + return irqchip_data->chanidx == fwspec->param[1]; } -- Gitee From fd1ba05fa95aea58e9ffa0a9ea35307a8063f9fc Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 Jan 2024 21:47:31 +0530 Subject: [PATCH 08/20] genirq/msi: Extend msi_parent_ops ANBZ: #20644 commit ac81e94ab001c2882e89c9b61417caea64b800df upstream. Supporting per device MSI domains on ARM64, RISC-V and the zoo of interrupt mechanisms needs a bit more information than what the initial x86 implementation provides. Add the following fields: - required_flags: The flags which a parent domain requires to be set - bus_select_token: The bus token of the parent domain for select() - bus_select_mask: A bitmask of supported child domain bus types This allows to provide library functions which can be shared between various interrupt chip implementations and avoids replicating mostly similar code all over the place. Signed-off-by: Thomas Gleixner Signed-off-by: Anup Patel Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240127161753.114685-4-apatel@ventanamicro.com Signed-off-by: Zhang Anmeng --- include/linux/msi.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/linux/msi.h b/include/linux/msi.h index e82058180004..392bc1436277 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -575,6 +575,11 @@ enum { * struct msi_parent_ops - MSI parent domain callbacks and configuration info * * @supported_flags: Required: The supported MSI flags of the parent domain + * @required_flags: Optional: The required MSI flags of the parent MSI domain + * @bus_select_token: Optional: The bus token of the real parent domain for + * irq_domain::select() + * @bus_select_mask: Optional: A mask of supported BUS_DOMAINs for + * irq_domain::select() * @prefix: Optional: Prefix for the domain and chip name * @init_dev_msi_info: Required: Callback for MSI parent domains to setup parent * domain specific domain flags, domain ops and interrupt chip @@ -582,6 +587,9 @@ enum { */ struct msi_parent_ops { u32 supported_flags; + u32 required_flags; + u32 bus_select_token; + u32 bus_select_mask; const char *prefix; bool (*init_dev_msi_info)(struct device *dev, struct irq_domain *domain, struct irq_domain *msi_parent_domain, -- Gitee From 0b1a6b0eddc387f366a2dac202ad87b755f1c3df Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 Jan 2024 21:47:32 +0530 Subject: [PATCH 09/20] genirq/irqdomain: Add DOMAIN_BUS_DEVICE_MSI ANBZ: #20644 commit 6516d5a295356f8fd5827a1c0954d7ed5b2324dd upstream. Add a new domain bus token to prepare for device MSI which aims to replace the existing platform MSI maze. Signed-off-by: Thomas Gleixner Signed-off-by: Anup Patel Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240127161753.114685-5-apatel@ventanamicro.com Signed-off-by: Zhang Anmeng --- include/linux/irqdomain_defs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/irqdomain_defs.h b/include/linux/irqdomain_defs.h index c29921fd8cd1..a7dea0c8c5e0 100644 --- a/include/linux/irqdomain_defs.h +++ b/include/linux/irqdomain_defs.h @@ -26,6 +26,7 @@ enum irq_domain_bus_token { DOMAIN_BUS_DMAR, DOMAIN_BUS_AMDVI, DOMAIN_BUS_PCI_DEVICE_IMS, + DOMAIN_BUS_DEVICE_MSI, }; #endif /* _LINUX_IRQDOMAIN_DEFS_H */ -- Gitee From 9355edc9da5218a2594160f3cbfb59107f7994b9 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 Jan 2024 21:47:33 +0530 Subject: [PATCH 10/20] platform-msi: Prepare for real per device domains ANBZ: #20644 commit c88f9110bfbca5975a8dee4c9792ba12684c7bca upstream. Provide functions to create and remove per device MSI domains which replace the platform-MSI domains. The new model is that each of the devices which utilize platform-MSI gets now its private MSI domain which is "customized" in size and with a device specific function to write the MSI message into the device. This is the same functionality as platform-MSI but it avoids all the down sides of platform MSI, i.e. the extra ID book keeping, the special data structure in the msi descriptor. Further the domains are only created when the devices are really in use, so the burden is on the usage and not on the infrastructure. Fill in the domain template and provide two functions to init/allocate and remove a per device MSI domain. Until all users and parent domain providers are converted, the init/alloc function invokes the original platform-MSI code when the irqdomain which is associated to the device does not provide MSI parent functionality yet. Signed-off-by: Thomas Gleixner Signed-off-by: Anup Patel Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240127161753.114685-6-apatel@ventanamicro.com Signed-off-by: Zhang Anmeng --- drivers/base/platform-msi.c | 103 ++++++++++++++++++++++++++++++++++++ include/linux/msi.h | 4 ++ 2 files changed, 107 insertions(+) diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c index f37ad34c80ec..b56e919acabb 100644 --- a/drivers/base/platform-msi.c +++ b/drivers/base/platform-msi.c @@ -13,6 +13,8 @@ #include #include +/* 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)) @@ -350,3 +352,104 @@ int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int vir 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() + * is switched to a proper irq_chip::irq_write_msi_msg() callback. Keep it + * simple for now. + */ +static void platform_msi_write_msi_msg(struct irq_data *d, struct msi_msg *msg) +{ + irq_write_msi_msg_t cb = d->chip_data; + + cb(irq_data_get_msi_desc(d), msg); +} + +static void platform_msi_set_desc_byindex(msi_alloc_info_t *arg, struct msi_desc *desc) +{ + arg->desc = desc; + arg->hwirq = desc->msi_index; +} + +static const struct msi_domain_template platform_msi_template = { + .chip = { + .name = "pMSI", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_write_msi_msg = platform_msi_write_msi_msg, + /* The rest is filled in by the platform MSI parent */ + }, + + .ops = { + .set_desc = platform_msi_set_desc_byindex, + }, + + .info = { + .bus_token = DOMAIN_BUS_DEVICE_MSI, + }, +}; + +/** + * platform_device_msi_init_and_alloc_irqs - Initialize platform device MSI + * and allocate 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 + * + * This creates a MSI domain on @dev which has @dev->msi.domain as + * 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) +{ + struct irq_domain *domain = dev->msi.domain; + + 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 + * callback to the resulting irq chip. + */ + if (!msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN, + &platform_msi_template, + nvec, NULL, write_msi_msg)) + return -ENODEV; + + return msi_domain_alloc_irqs_range(dev, MSI_DEFAULT_DOMAIN, 0, nvec - 1); +} +EXPORT_SYMBOL_GPL(platform_device_msi_init_and_alloc_irqs); + +/** + * platform_device_msi_free_irqs_all - Free all interrupts for @dev + * @dev: The device for which to free interrupts + */ +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 392bc1436277..892d8cce5988 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -667,6 +667,10 @@ int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int vir 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); +void platform_device_msi_free_irqs_all(struct device *dev); bool msi_device_has_isolated_msi(struct device *dev); #else /* CONFIG_GENERIC_MSI_IRQ */ -- Gitee From e0c95e68e79feb9fb6b5f6a980016ea8ae3e186d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 Jan 2024 21:47:34 +0530 Subject: [PATCH 11/20] irqchip: Convert all platform MSI users to the new API ANBZ: #20644 commit 14fd06c776b5289a43c91cdc64bac3bdbc7b397e upstream. Switch all the users of the platform MSI domain over to invoke the new interfaces which branch to the original platform MSI functions when the irqdomain associated to the caller device does not yet provide MSI parent functionality. No functional change. Signed-off-by: Thomas Gleixner Signed-off-by: Anup Patel Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240127161753.114685-7-apatel@ventanamicro.com Signed-off-by: Zhang Anmeng --- drivers/dma/mv_xor_v2.c | 8 ++++---- drivers/dma/qcom/hidma.c | 6 +++--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 5 +++-- drivers/mailbox/bcm-flexrm-mailbox.c | 8 ++++---- drivers/perf/arm_smmuv3_pmu.c | 4 ++-- drivers/ufs/host/ufs-qcom.c | 8 ++++---- 6 files changed, 20 insertions(+), 19 deletions(-) diff --git a/drivers/dma/mv_xor_v2.c b/drivers/dma/mv_xor_v2.c index 0e1e9ca1c005..cd2b9a6ab621 100644 --- a/drivers/dma/mv_xor_v2.c +++ b/drivers/dma/mv_xor_v2.c @@ -747,8 +747,8 @@ static int mv_xor_v2_probe(struct platform_device *pdev) if (IS_ERR(xor_dev->clk)) return PTR_ERR(xor_dev->clk); - ret = platform_msi_domain_alloc_irqs(&pdev->dev, 1, - mv_xor_v2_set_msi_msg); + ret = platform_device_msi_init_and_alloc_irqs(&pdev->dev, 1, + mv_xor_v2_set_msi_msg); if (ret) return ret; @@ -851,7 +851,7 @@ static int mv_xor_v2_probe(struct platform_device *pdev) xor_dev->desc_size * MV_XOR_V2_DESC_NUM, xor_dev->hw_desq_virt, xor_dev->hw_desq); free_msi_irqs: - platform_msi_domain_free_irqs(&pdev->dev); + platform_device_msi_free_irqs_all(&pdev->dev); return ret; } @@ -867,7 +867,7 @@ static int mv_xor_v2_remove(struct platform_device *pdev) devm_free_irq(&pdev->dev, xor_dev->irq, xor_dev); - platform_msi_domain_free_irqs(&pdev->dev); + platform_device_msi_free_irqs_all(&pdev->dev); tasklet_kill(&xor_dev->irq_tasklet); diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c index 834ae519c15d..f2b299c23b1e 100644 --- a/drivers/dma/qcom/hidma.c +++ b/drivers/dma/qcom/hidma.c @@ -696,7 +696,7 @@ static void hidma_free_msis(struct hidma_dev *dmadev) devm_free_irq(dev, virq, &dmadev->lldev); } - platform_msi_domain_free_irqs(dev); + platform_device_msi_free_irqs_all(dev); #endif } @@ -706,8 +706,8 @@ static int hidma_request_msi(struct hidma_dev *dmadev, #ifdef CONFIG_GENERIC_MSI_IRQ int rc, i, virq; - rc = platform_msi_domain_alloc_irqs(&pdev->dev, HIDMA_MSI_INTS, - hidma_write_msi_msg); + rc = platform_device_msi_init_and_alloc_irqs(&pdev->dev, HIDMA_MSI_INTS, + hidma_write_msi_msg); if (rc) return rc; diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index 58d45b27bd0d..798d07a04107 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -3141,7 +3141,8 @@ static int arm_smmu_update_gbpa(struct arm_smmu_device *smmu, u32 set, u32 clr) static void arm_smmu_free_msis(void *data) { struct device *dev = data; - platform_msi_domain_free_irqs(dev); + + platform_device_msi_free_irqs_all(dev); } static void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) @@ -3182,7 +3183,7 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu) } /* Allocate MSIs for evtq, gerror and priq. Ignore cmdq */ - ret = platform_msi_domain_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg); + ret = platform_device_msi_init_and_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg); if (ret) { dev_warn(dev, "failed to allocate MSIs - falling back to wired irqs\n"); return; diff --git a/drivers/mailbox/bcm-flexrm-mailbox.c b/drivers/mailbox/bcm-flexrm-mailbox.c index a2b8839d4e7c..7094d44869a8 100644 --- a/drivers/mailbox/bcm-flexrm-mailbox.c +++ b/drivers/mailbox/bcm-flexrm-mailbox.c @@ -1587,8 +1587,8 @@ static int flexrm_mbox_probe(struct platform_device *pdev) } /* Allocate platform MSIs for each ring */ - ret = platform_msi_domain_alloc_irqs(dev, mbox->num_rings, - flexrm_mbox_msi_write); + ret = platform_device_msi_init_and_alloc_irqs(dev, mbox->num_rings, + flexrm_mbox_msi_write); if (ret) goto fail_destroy_cmpl_pool; @@ -1641,7 +1641,7 @@ static int flexrm_mbox_probe(struct platform_device *pdev) fail_free_debugfs_root: debugfs_remove_recursive(mbox->root); - platform_msi_domain_free_irqs(dev); + platform_device_msi_free_irqs_all(dev); fail_destroy_cmpl_pool: dma_pool_destroy(mbox->cmpl_pool); fail_destroy_bd_pool: @@ -1657,7 +1657,7 @@ static int flexrm_mbox_remove(struct platform_device *pdev) debugfs_remove_recursive(mbox->root); - platform_msi_domain_free_irqs(dev); + platform_device_msi_free_irqs_all(dev); dma_pool_destroy(mbox->cmpl_pool); dma_pool_destroy(mbox->bd_pool); diff --git a/drivers/perf/arm_smmuv3_pmu.c b/drivers/perf/arm_smmuv3_pmu.c index 31e491e7f206..2946422539fb 100644 --- a/drivers/perf/arm_smmuv3_pmu.c +++ b/drivers/perf/arm_smmuv3_pmu.c @@ -719,7 +719,7 @@ static void smmu_pmu_free_msis(void *data) { struct device *dev = data; - platform_msi_domain_free_irqs(dev); + platform_device_msi_free_irqs_all(dev); } static void smmu_pmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) @@ -749,7 +749,7 @@ static void smmu_pmu_setup_msi(struct smmu_pmu *pmu) if (!(readl(pmu->reg_base + SMMU_PMCG_CFGR) & SMMU_PMCG_CFGR_MSI)) return; - ret = platform_msi_domain_alloc_irqs(dev, 1, smmu_pmu_write_msi_msg); + ret = platform_device_msi_init_and_alloc_irqs(dev, 1, smmu_pmu_write_msi_msg); if (ret) { dev_warn(dev, "failed to allocate MSIs\n"); return; diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index 51ed40529f9a..e692d91c7ce8 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -1812,8 +1812,8 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba) * 2. Poll queues do not need ESI. */ nr_irqs = hba->nr_hw_queues - hba->nr_queues[HCTX_TYPE_POLL]; - ret = platform_msi_domain_alloc_irqs(hba->dev, nr_irqs, - ufs_qcom_write_msi_msg); + ret = platform_device_msi_init_and_alloc_irqs(hba->dev, nr_irqs, + ufs_qcom_write_msi_msg); if (ret) { dev_err(hba->dev, "Failed to request Platform MSI %d\n", ret); goto out; @@ -1842,7 +1842,7 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba) devm_free_irq(hba->dev, desc->irq, hba); } msi_unlock_descs(hba->dev); - platform_msi_domain_free_irqs(hba->dev); + platform_device_msi_free_irqs_all(hba->dev); } else { if (host->hw_ver.major == 6 && host->hw_ver.minor == 0 && host->hw_ver.step == 0) { @@ -1923,7 +1923,7 @@ static int ufs_qcom_remove(struct platform_device *pdev) pm_runtime_get_sync(&(pdev)->dev); ufshcd_remove(hba); if (host->esi_enabled) - platform_msi_domain_free_irqs(hba->dev); + platform_device_msi_free_irqs_all(hba->dev); return 0; } -- Gitee From f80e4629eee7cd7b4499e5098eefd4e637b61ba2 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 15 Feb 2024 16:35:43 +0100 Subject: [PATCH 12/20] platform-msi: Remove unused interfaces ANBZ: #20644 commit 1a4671ff7a903e87e4e76213e200bb8bcfa942e4 upstream. Signed-off-by: Thomas Gleixner Signed-off-by: Zhang Anmeng --- drivers/base/platform-msi.c | 16 ++-------------- include/linux/msi.h | 3 --- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c index b56e919acabb..0d01890160f3 100644 --- a/drivers/base/platform-msi.c +++ b/drivers/base/platform-msi.c @@ -206,8 +206,8 @@ static void platform_msi_free_priv_data(struct device *dev) * Returns: * Zero for success, or an error code in case of failure */ -int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, - irq_write_msi_msg_t write_msi_msg) +static int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, + irq_write_msi_msg_t write_msi_msg) { int err; @@ -221,18 +221,6 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, return err; } -EXPORT_SYMBOL_GPL(platform_msi_domain_alloc_irqs); - -/** - * platform_msi_domain_free_irqs - Free MSI interrupts for @dev - * @dev: The device for which to free interrupts - */ -void platform_msi_domain_free_irqs(struct device *dev) -{ - msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN); - platform_msi_free_priv_data(dev); -} -EXPORT_SYMBOL_GPL(platform_msi_domain_free_irqs); /** * platform_msi_get_host_data - Query the private data associated with diff --git a/include/linux/msi.h b/include/linux/msi.h index 892d8cce5988..8998a0c3146d 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -638,9 +638,6 @@ 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); -int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, - irq_write_msi_msg_t write_msi_msg); -void platform_msi_domain_free_irqs(struct device *dev); /* When an MSI domain is used as an intermediate domain */ int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev, -- Gitee From f46ddf1fecb1c46d5a4d109dfc1c28edc3b1dcde Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 Jan 2024 21:47:35 +0530 Subject: [PATCH 13/20] genirq/msi: Provide optional translation op ANBZ: #20644 commit 9c78c1a85c04bdfbccc5a50588e001087d942b08 upstream. irq_create_fwspec_mapping() requires translation of the firmware spec to a hardware interrupt number and the trigger type information. Wired interrupts which are connected to a wire to MSI bridge, like MBIGEN are allocated that way. So far MBIGEN provides a regular irqdomain which then hooks backwards into the MSI infrastructure. That's an unholy mess and will be replaced with per device MSI domains which are regular MSI domains. Interrupts on MSI domains are not supported by irq_create_fwspec_mapping(), but for making the wire to MSI bridges sane it makes sense to provide a special allocation/free interface in the MSI infrastructure. That avoids the backdoors into the core MSI allocation code and just shares all the regular MSI infrastructure. Provide an optional translation callback in msi_domain_ops which can be utilized by these wire to MSI bridges. No other MSI domain should provide a translation callback. The default translation callback of the MSI irqdomains will warn when it is invoked on a non-prepared MSI domain. Signed-off-by: Thomas Gleixner Signed-off-by: Anup Patel Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240127161753.114685-8-apatel@ventanamicro.com Signed-off-by: Zhang Anmeng --- include/linux/msi.h | 5 +++++ kernel/irq/msi.c | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/linux/msi.h b/include/linux/msi.h index 8998a0c3146d..9375be39f396 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -415,6 +415,7 @@ bool arch_restore_msi_irqs(struct pci_dev *dev); struct irq_domain; struct irq_domain_ops; struct irq_chip; +struct irq_fwspec; struct device_node; struct fwnode_handle; struct msi_domain_info; @@ -434,6 +435,8 @@ struct msi_domain_info; * function. * @msi_post_free: Optional function which is invoked after freeing * all interrupts. + * @msi_translate: Optional translate callback to support the odd wire to + * MSI bridges, e.g. MBIGEN * * @get_hwirq, @msi_init and @msi_free are callbacks used by the underlying * irqdomain. @@ -471,6 +474,8 @@ struct msi_domain_ops { struct device *dev); void (*msi_post_free)(struct irq_domain *domain, struct device *dev); + int (*msi_translate)(struct irq_domain *domain, struct irq_fwspec *fwspec, + irq_hw_number_t *hwirq, unsigned int *type); }; /** diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 79b4a58ba9c3..c0e73788e878 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -726,11 +726,26 @@ static void msi_domain_free(struct irq_domain *domain, unsigned int virq, irq_domain_free_irqs_top(domain, virq, nr_irqs); } +static int msi_domain_translate(struct irq_domain *domain, struct irq_fwspec *fwspec, + irq_hw_number_t *hwirq, unsigned int *type) +{ + struct msi_domain_info *info = domain->host_data; + + /* + * This will catch allocations through the regular irqdomain path except + * for MSI domains which really support this, e.g. MBIGEN. + */ + if (!info->ops->msi_translate) + return -ENOTSUPP; + return info->ops->msi_translate(domain, fwspec, hwirq, type); +} + static const struct irq_domain_ops msi_domain_ops = { .alloc = msi_domain_alloc, .free = msi_domain_free, .activate = msi_domain_activate, .deactivate = msi_domain_deactivate, + .translate = msi_domain_translate, }; static irq_hw_number_t msi_domain_ops_get_hwirq(struct msi_domain_info *info, -- Gitee From c80859fb7b2132c5c289d569345f5d68aebd5db6 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 Jan 2024 21:47:36 +0530 Subject: [PATCH 14/20] genirq/msi: Split msi_domain_alloc_irq_at() ANBZ: #20644 commit 3095cc0d5b2c246ddfcb18f54ed5557640224b6a upstream. In preparation for providing a special allocation function for wired interrupts which are connected to a wire to MSI bridge, split the inner workings of msi_domain_alloc_irq_at() out into a helper function so the code can be shared. No functional change. Signed-off-by: Thomas Gleixner Signed-off-by: Anup Patel Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240127161753.114685-9-apatel@ventanamicro.com Signed-off-by: Zhang Anmeng --- kernel/irq/msi.c | 76 +++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index c0e73788e878..8d463901c864 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -1446,34 +1446,10 @@ int msi_domain_alloc_irqs_all_locked(struct device *dev, unsigned int domid, int return msi_domain_alloc_locked(dev, &ctrl); } -/** - * msi_domain_alloc_irq_at - Allocate an interrupt from a MSI interrupt domain at - * a given index - or at the next free index - * - * @dev: Pointer to device struct of the device for which the interrupts - * are allocated - * @domid: Id of the interrupt domain to operate on - * @index: Index for allocation. If @index == %MSI_ANY_INDEX the allocation - * uses the next free index. - * @affdesc: Optional pointer to an interrupt affinity descriptor structure - * @icookie: Optional pointer to a domain specific per instance cookie. If - * non-NULL the content of the cookie is stored in msi_desc::data. - * Must be NULL for MSI-X allocations - * - * This requires a MSI interrupt domain which lets the core code manage the - * MSI descriptors. - * - * Return: struct msi_map - * - * On success msi_map::index contains the allocated index number and - * msi_map::virq the corresponding Linux interrupt number - * - * On failure msi_map::index contains the error code and msi_map::virq - * is %0. - */ -struct msi_map msi_domain_alloc_irq_at(struct device *dev, unsigned int domid, unsigned int index, - const struct irq_affinity_desc *affdesc, - union msi_instance_cookie *icookie) +static struct msi_map __msi_domain_alloc_irq_at(struct device *dev, unsigned int domid, + unsigned int index, + const struct irq_affinity_desc *affdesc, + union msi_instance_cookie *icookie) { struct msi_ctrl ctrl = { .domid = domid, .nirqs = 1, }; struct irq_domain *domain; @@ -1481,17 +1457,16 @@ struct msi_map msi_domain_alloc_irq_at(struct device *dev, unsigned int domid, u struct msi_desc *desc; int ret; - msi_lock_descs(dev); domain = msi_get_device_domain(dev, domid); if (!domain) { map.index = -ENODEV; - goto unlock; + return map; } desc = msi_alloc_desc(dev, 1, affdesc); if (!desc) { map.index = -ENOMEM; - goto unlock; + return map; } if (icookie) @@ -1500,7 +1475,7 @@ struct msi_map msi_domain_alloc_irq_at(struct device *dev, unsigned int domid, u ret = msi_insert_desc(dev, desc, domid, index); if (ret) { map.index = ret; - goto unlock; + return map; } ctrl.first = ctrl.last = desc->msi_index; @@ -1513,7 +1488,42 @@ struct msi_map msi_domain_alloc_irq_at(struct device *dev, unsigned int domid, u map.index = desc->msi_index; map.virq = desc->irq; } -unlock: + return map; +} + +/** + * msi_domain_alloc_irq_at - Allocate an interrupt from a MSI interrupt domain at + * a given index - or at the next free index + * + * @dev: Pointer to device struct of the device for which the interrupts + * are allocated + * @domid: Id of the interrupt domain to operate on + * @index: Index for allocation. If @index == %MSI_ANY_INDEX the allocation + * uses the next free index. + * @affdesc: Optional pointer to an interrupt affinity descriptor structure + * @icookie: Optional pointer to a domain specific per instance cookie. If + * non-NULL the content of the cookie is stored in msi_desc::data. + * Must be NULL for MSI-X allocations + * + * This requires a MSI interrupt domain which lets the core code manage the + * MSI descriptors. + * + * Return: struct msi_map + * + * On success msi_map::index contains the allocated index number and + * msi_map::virq the corresponding Linux interrupt number + * + * On failure msi_map::index contains the error code and msi_map::virq + * is %0. + */ +struct msi_map msi_domain_alloc_irq_at(struct device *dev, unsigned int domid, unsigned int index, + const struct irq_affinity_desc *affdesc, + union msi_instance_cookie *icookie) +{ + struct msi_map map; + + msi_lock_descs(dev); + map = __msi_domain_alloc_irq_at(dev, domid, index, affdesc, icookie); msi_unlock_descs(dev); return map; } -- Gitee From c32e0f0e2591c58292ef4669a8881ac7e16b0e42 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 Jan 2024 21:47:37 +0530 Subject: [PATCH 15/20] genirq/msi: Provide DOMAIN_BUS_WIRED_TO_MSI ANBZ: #20644 commit 2d566a498d6483ba986dadc496f64a20b032608f upstream. Provide a domain bus token for the upcoming support for wire to MSI device domains so the domain can be distinguished from regular device MSI domains. Signed-off-by: Thomas Gleixner Signed-off-by: Anup Patel Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240127161753.114685-10-apatel@ventanamicro.com Signed-off-by: Zhang Anmeng --- include/linux/irqdomain_defs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/irqdomain_defs.h b/include/linux/irqdomain_defs.h index a7dea0c8c5e0..5c1fe6f1fcde 100644 --- a/include/linux/irqdomain_defs.h +++ b/include/linux/irqdomain_defs.h @@ -27,6 +27,7 @@ enum irq_domain_bus_token { DOMAIN_BUS_AMDVI, DOMAIN_BUS_PCI_DEVICE_IMS, DOMAIN_BUS_DEVICE_MSI, + DOMAIN_BUS_WIRED_TO_MSI, }; #endif /* _LINUX_IRQDOMAIN_DEFS_H */ -- Gitee From d7a98cf86d2fd64bb14e2e3c3a96c1ea5bde90e8 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 Jan 2024 21:47:38 +0530 Subject: [PATCH 16/20] genirq/msi: Optionally use dev->fwnode for device domain ANBZ: #20644 commit 9d1c58c8004653b37721dd7b16f4360216778c94 upstream. To support wire to MSI domains via the MSI infrastructure it is required to use the firmware node of the device which implements this for creating the MSI domain. Otherwise the existing firmware match mechanisms to find the correct irqdomain for a wired interrupt which is connected to a wire to MSI bridge would fail. This cannot be used for the general case because not all devices provide firmware nodes and all regular per device MSI domains are directly associated to the device and have not be searched for. Signed-off-by: Thomas Gleixner Signed-off-by: Anup Patel Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240127161753.114685-11-apatel@ventanamicro.com Signed-off-by: Zhang Anmeng --- include/linux/msi.h | 2 ++ kernel/irq/msi.c | 20 ++++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/include/linux/msi.h b/include/linux/msi.h index 9375be39f396..3fed4372ad53 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -555,6 +555,8 @@ enum { MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS = (1 << 5), /* Free MSI descriptors */ MSI_FLAG_FREE_MSI_DESCS = (1 << 6), + /* Use dev->fwnode for MSI device domain creation */ + MSI_FLAG_USE_DEV_FWNODE = (1 << 7), /* Mask for the generic functionality */ MSI_GENERIC_FLAGS_MASK = GENMASK(15, 0), diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 8d463901c864..5289fc2c7630 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -960,9 +960,9 @@ bool msi_create_device_irq_domain(struct device *dev, unsigned int domid, void *chip_data) { struct irq_domain *domain, *parent = dev->msi.domain; - const struct msi_parent_ops *pops; + struct fwnode_handle *fwnode, *fwnalloced = NULL; struct msi_domain_template *bundle; - struct fwnode_handle *fwnode; + const struct msi_parent_ops *pops; if (!irq_domain_is_msi_parent(parent)) return false; @@ -985,7 +985,19 @@ bool msi_create_device_irq_domain(struct device *dev, unsigned int domid, pops->prefix ? : "", bundle->chip.name, dev_name(dev)); bundle->chip.name = bundle->name; - fwnode = irq_domain_alloc_named_fwnode(bundle->name); + /* + * Using the device firmware node is required for wire to MSI + * device domains so that the existing firmware results in a domain + * match. + * All other device domains like PCI/MSI use the named firmware + * node as they are not guaranteed to have a fwnode. They are never + * looked up and always handled in the context of the device. + */ + if (bundle->info.flags & MSI_FLAG_USE_DEV_FWNODE) + fwnode = dev->fwnode; + else + fwnode = fwnalloced = irq_domain_alloc_named_fwnode(bundle->name); + if (!fwnode) goto free_bundle; @@ -1012,7 +1024,7 @@ bool msi_create_device_irq_domain(struct device *dev, unsigned int domid, fail: msi_unlock_descs(dev); free_fwnode: - irq_domain_free_fwnode(fwnode); + irq_domain_free_fwnode(fwnalloced); free_bundle: kfree(bundle); return false; -- Gitee From 04b137bef30b07b115981719ba82f862e27fd325 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 Jan 2024 21:47:39 +0530 Subject: [PATCH 17/20] genirq/msi: Provide allocation/free functions for "wired" MSI interrupts ANBZ: #20644 commit 0ee1578b00bcf5ef8e7955f0c6f02a624443eb29 upstream. To support wire to MSI bridges proper in the MSI core infrastructure it is required to have separate allocation/free interfaces which can be invoked from the regular irqdomain allocaton/free functions. The mechanism for allocation is: - Allocate the next free MSI descriptor index in the domain - Store the hardware interrupt number and the trigger type which was extracted by the irqdomain core from the firmware spec in the MSI descriptor device cookie so it can be retrieved by the underlying interrupt domain and interrupt chip - Use the regular MSI allocation mechanism for the newly allocated index which returns a fully initialized Linux interrupt on succes This works because: - the domains have a fixed size - each hardware interrupt is only allocated once - the underlying domain does not care about the MSI index it only cares about the hardware interrupt number and the trigger type The free function looks up the MSI index in the MSI descriptor of the provided Linux interrupt number and uses the regular index based free functions of the MSI core. Signed-off-by: Thomas Gleixner Signed-off-by: Anup Patel Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240127161753.114685-12-apatel@ventanamicro.com Signed-off-by: Zhang Anmeng --- include/linux/irqdomain.h | 17 ++++++++++ kernel/irq/msi.c | 68 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index a57e45f80986..33843ae1098f 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -625,6 +625,23 @@ static inline bool irq_domain_is_msi_device(struct irq_domain *domain) #endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ +#ifdef CONFIG_GENERIC_MSI_IRQ +int msi_device_domain_alloc_wired(struct irq_domain *domain, unsigned int hwirq, + unsigned int type); +void msi_device_domain_free_wired(struct irq_domain *domain, unsigned int virq); +#else +static inline int msi_device_domain_alloc_wired(struct irq_domain *domain, unsigned int hwirq, + unsigned int type) +{ + WARN_ON_ONCE(1); + return -EINVAL; +} +static inline void msi_device_domain_free_wired(struct irq_domain *domain, unsigned int virq) +{ + WARN_ON_ONCE(1); +} +#endif + #else /* CONFIG_IRQ_DOMAIN */ static inline void irq_dispose_mapping(unsigned int virq) { } static inline struct irq_domain *irq_find_matching_fwnode( diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 5289fc2c7630..07e9daaf0657 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -1540,6 +1540,50 @@ struct msi_map msi_domain_alloc_irq_at(struct device *dev, unsigned int domid, u return map; } +/** + * msi_device_domain_alloc_wired - Allocate a "wired" interrupt on @domain + * @domain: The domain to allocate on + * @hwirq: The hardware interrupt number to allocate for + * @type: The interrupt type + * + * This weirdness supports wire to MSI controllers like MBIGEN. + * + * @hwirq is the hardware interrupt number which is handed in from + * irq_create_fwspec_mapping(). As the wire to MSI domain is sparse, but + * sized in firmware, the hardware interrupt number cannot be used as MSI + * index. For the underlying irq chip the MSI index is irrelevant and + * all it needs is the hardware interrupt number. + * + * To handle this the MSI index is allocated with MSI_ANY_INDEX and the + * hardware interrupt number is stored along with the type information in + * msi_desc::cookie so the underlying interrupt chip and domain code can + * retrieve it. + * + * Return: The Linux interrupt number (> 0) or an error code + */ +int msi_device_domain_alloc_wired(struct irq_domain *domain, unsigned int hwirq, + unsigned int type) +{ + unsigned int domid = MSI_DEFAULT_DOMAIN; + union msi_instance_cookie icookie = { }; + struct device *dev = domain->dev; + struct msi_map map = { }; + + if (WARN_ON_ONCE(!dev || domain->bus_token != DOMAIN_BUS_WIRED_TO_MSI)) + return -EINVAL; + + icookie.value = ((u64)type << 32) | hwirq; + + msi_lock_descs(dev); + if (WARN_ON_ONCE(msi_get_device_domain(dev, domid) != domain)) + map.index = -EINVAL; + else + map = __msi_domain_alloc_irq_at(dev, domid, MSI_ANY_INDEX, NULL, &icookie); + msi_unlock_descs(dev); + + return map.index >= 0 ? map.virq : map.index; +} + static void __msi_domain_free_irqs(struct device *dev, struct irq_domain *domain, struct msi_ctrl *ctrl) { @@ -1665,6 +1709,30 @@ void msi_domain_free_irqs_all(struct device *dev, unsigned int domid) msi_unlock_descs(dev); } +/** + * msi_device_domain_free_wired - Free a wired interrupt in @domain + * @domain: The domain to free the interrupt on + * @virq: The Linux interrupt number to free + * + * This is the counterpart of msi_device_domain_alloc_wired() for the + * weird wired to MSI converting domains. + */ +void msi_device_domain_free_wired(struct irq_domain *domain, unsigned int virq) +{ + struct msi_desc *desc = irq_get_msi_desc(virq); + struct device *dev = domain->dev; + + if (WARN_ON_ONCE(!dev || !desc || domain->bus_token != DOMAIN_BUS_WIRED_TO_MSI)) + return; + + msi_lock_descs(dev); + if (!WARN_ON_ONCE(msi_get_device_domain(dev, MSI_DEFAULT_DOMAIN) != domain)) { + msi_domain_free_irqs_range_locked(dev, MSI_DEFAULT_DOMAIN, desc->msi_index, + desc->msi_index); + } + msi_unlock_descs(dev); +} + /** * msi_get_domain_info - Get the MSI interrupt domain info for @domain * @domain: The interrupt domain to retrieve data from -- Gitee From 511d2e32e22dcba56b1dd31d18b2b8556d61a3b1 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 Jan 2024 21:47:41 +0530 Subject: [PATCH 18/20] genirq/msi: Provide MSI_FLAG_PARENT_PM_DEV ANBZ: #20644 commit 9bbe13a5d414a7f8208dba64b54d2b6e4f7086bd upstream. Some platform-MSI implementations require that power management is redirected to the underlying interrupt chip device. To make this work with per device MSI domains provide a new feature flag and let the core code handle the setup of dev->pm_dev when set during device MSI domain creation. Signed-off-by: Thomas Gleixner Signed-off-by: Anup Patel Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240127161753.114685-14-apatel@ventanamicro.com Signed-off-by: Zhang Anmeng --- include/linux/msi.h | 2 ++ kernel/irq/msi.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/linux/msi.h b/include/linux/msi.h index 3fed4372ad53..4d260cbd8fa8 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -557,6 +557,8 @@ enum { MSI_FLAG_FREE_MSI_DESCS = (1 << 6), /* Use dev->fwnode for MSI device domain creation */ 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), /* Mask for the generic functionality */ MSI_GENERIC_FLAGS_MASK = GENMASK(15, 0), diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 07e9daaf0657..f90952ebc494 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -845,8 +845,11 @@ static struct irq_domain *__msi_create_irq_domain(struct fwnode_handle *fwnode, domain = irq_domain_create_hierarchy(parent, flags | IRQ_DOMAIN_FLAG_MSI, 0, fwnode, &msi_domain_ops, info); - if (domain) + if (domain) { irq_domain_update_bus_token(domain, info->bus_token); + if (info->flags & MSI_FLAG_PARENT_PM_DEV) + domain->pm_dev = parent->pm_dev; + } return domain; } -- Gitee From a42f6dc493ac623e6c29eb1e0845288f722b8d52 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 Jan 2024 21:47:40 +0530 Subject: [PATCH 19/20] genirq/irqdomain: Reroute device MSI create_mapping ANBZ: #20644 commit e49312fe09df36cc4eae0cd6e1b08b563a91e1bc upstream. Reroute interrupt allocation in irq_create_fwspec_mapping() if the domain is a MSI device domain. This is required to convert the support for wire to MSI bridges to per device MSI domains. Signed-off-by: Thomas Gleixner Signed-off-by: Anup Patel Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240127161753.114685-13-apatel@ventanamicro.com Signed-off-by: Zhang Anmeng --- kernel/irq/irqdomain.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index a3dae87f2c52..589c13aaa158 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -29,6 +29,7 @@ static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base, unsigned int nr_irqs, int node, void *arg, bool realloc, const struct irq_affinity_desc *affinity); static void irq_domain_check_hierarchy(struct irq_domain *domain); +static void irq_domain_free_one_irq(struct irq_domain *domain, unsigned int virq); struct irqchip_fwid { struct fwnode_handle fwnode; @@ -855,8 +856,13 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec) } if (irq_domain_is_hierarchy(domain)) { - virq = irq_domain_alloc_irqs_locked(domain, -1, 1, NUMA_NO_NODE, - fwspec, false, NULL); + if (irq_domain_is_msi_device(domain)) { + mutex_unlock(&domain->root->mutex); + virq = msi_device_domain_alloc_wired(domain, hwirq, type); + mutex_lock(&domain->root->mutex); + } else + virq = irq_domain_alloc_irqs_locked(domain, -1, 1, NUMA_NO_NODE, + fwspec, false, NULL); if (virq <= 0) { virq = 0; goto out; @@ -911,7 +917,7 @@ void irq_dispose_mapping(unsigned int virq) return; if (irq_domain_is_hierarchy(domain)) { - irq_domain_free_irqs(virq, 1); + irq_domain_free_one_irq(domain, virq); } else { irq_domain_disassociate(domain, virq); irq_free_desc(virq); @@ -1752,6 +1758,14 @@ void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs) irq_free_descs(virq, nr_irqs); } +static void irq_domain_free_one_irq(struct irq_domain *domain, unsigned int virq) +{ + if (irq_domain_is_msi_device(domain)) + msi_device_domain_free_wired(domain, virq); + else + irq_domain_free_irqs(virq, 1); +} + /** * irq_domain_alloc_irqs_parent - Allocate interrupts from parent domain * @domain: Domain below which interrupts must be allocated @@ -1906,9 +1920,9 @@ static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base, return -EINVAL; } -static void irq_domain_check_hierarchy(struct irq_domain *domain) -{ -} +static void irq_domain_check_hierarchy(struct irq_domain *domain) { } +static void irq_domain_free_one_irq(struct irq_domain *domain, unsigned int virq) { } + #endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ #ifdef CONFIG_GENERIC_IRQ_DEBUGFS -- Gitee From dc46873c0c4afafdd7635c29207149f4903d8251 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 25 Feb 2024 16:56:12 +0100 Subject: [PATCH 20/20] x86/apic/msi: Use DOMAIN_BUS_GENERIC_MSI for HPET/IO-APIC domain search ANBZ: #20644 commit c147e1ef59d4751a60687074e4268ecc0ef31b5c upstream. The recent restriction to invoke irqdomain_ops::select() only when the domain bus token is not DOMAIN_BUS_ANY breaks the search for the parent MSI domain of HPET and IO-APIC. The latter causes a full boot fail. The restriction itself makes sense to avoid adding DOMAIN_BUS_ANY matches into the various ARM specific select() callbacks. Reverting this change would obviously break ARM platforms again and require DOMAIN_BUS_ANY matches added to various places. A simpler solution is to use the DOMAIN_BUS_GENERIC_MSI token for the HPET and IO-APIC parent domain search. This works out of the box because the affected parent domains check only for the firmware specification content and not for the bus token. Fixes: 5aa3c0cf5bba ("genirq/irqdomain: Don't call ops->select for DOMAIN_BUS_ANY tokens") Reported-by: Borislav Petkov (AMD) Signed-off-by: Thomas Gleixner Tested-by: Borislav Petkov (AMD) Reviewed-by: Marc Zyngier Link: https://lore.kernel.org/r/878r38cy8n.ffs@tglx Signed-off-by: Zhang Anmeng --- arch/x86/kernel/apic/io_apic.c | 2 +- arch/x86/kernel/hpet.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index d0c5325d1751..3a93bbc6bb65 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c @@ -2345,7 +2345,7 @@ static int mp_irqdomain_create(int ioapic) fwspec.param_count = 1; fwspec.param[0] = mpc_ioapic_id(ioapic); - parent = irq_find_matching_fwspec(&fwspec, DOMAIN_BUS_ANY); + parent = irq_find_matching_fwspec(&fwspec, DOMAIN_BUS_GENERIC_MSI); if (!parent) { if (!cfg->dev) irq_domain_free_fwnode(fn); diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 2626fa052b45..cf9611956edd 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -568,7 +568,7 @@ static struct irq_domain *hpet_create_irq_domain(int hpet_id) fwspec.param_count = 1; fwspec.param[0] = hpet_id; - parent = irq_find_matching_fwspec(&fwspec, DOMAIN_BUS_ANY); + parent = irq_find_matching_fwspec(&fwspec, DOMAIN_BUS_GENERIC_MSI); if (!parent) { irq_domain_free_fwnode(fn); kfree(domain_info); -- Gitee