* [PATCH 0/4] irqchip: add EyeQ5 PCIe MSI controller
@ 2026-10-28 15:58 Gregory CLEMENT
2026-10-28 15:58 ` [PATCH 1/4] dt-bindings: interrupt-controller: add Mobileye " Gregory CLEMENT
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Gregory CLEMENT @ 2026-10-28 15:58 UTC (permalink / raw)
To: Vladimir Kondratiev, Benoît Monin, Théo Lebrun,
Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Thomas Bogendoerfer
Cc: Thomas Petazzoni, linux-mips, linux-kernel, devicetree, Gregory CLEMENT
Hello,
This series adds support for the MSI controller present in the
Mobileye EyeQ5 PCIe ports.
Although the driver uses the standard MSI library, the EyeQ5 hardware
has several specific features. First, each PCIe port has its own MSI
controller, which snoops the MSI write TLPs that the port receives. A
port supports a maximum of 4 MSI vectors, and the memory-region
property gives the base address of the doorbell window. In addition,
the GCSR mode selects the role: in root complex mode the driver
registers a PCI MSI parent, while in endpoint mode it registers a
platform MSI parent. The last specific point is that the driver uses
the snooper domain as a chained controller on one GIC interrupt.
The series adds the binding document, the irqchip driver, the EyeQ5
DTS changes, and the MAINTAINERS entry. In particular, the DTS changes
add the two MSI controllers to the EyeQ5 SoC.
I tested the series with two EyeQ5 SoCs connected by a PCIe link, with
one SoC acting as the host and the other as the endpoint. The
pci_endpoint_test kselftest passed the MSIX_TEST, MSI_TEST, and
DOORBELL_TEST cases.
Grégory
Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com>
---
Gregory CLEMENT (4):
dt-bindings: interrupt-controller: add Mobileye EyeQ5 PCIe MSI controller
irqchip: irq-eyeq5-msi: add EyeQ5 PCIe MSI controller driver
MIPS: dts: eyeq5: add MSI controllers
MAINTAINERS: add EyeQ5 MSI controller entry
.../interrupt-controller/mobileye,eyeq5-msi.yaml | 120 ++++++
MAINTAINERS | 2 +
arch/mips/Kconfig | 1 +
arch/mips/boot/dts/mobileye/eyeq5.dtsi | 30 ++
drivers/irqchip/Kconfig | 5 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-eyeq5-msi.c | 474 +++++++++++++++++++++
7 files changed, 633 insertions(+)
---
base-commit: c03c1a14507dfaf8b856c2608f9ac216021fc222
change-id: 20261028-eyeq5_msi-86aa1d590684
Best regards,
--
Grégory CLEMENT, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 1/4] dt-bindings: interrupt-controller: add Mobileye EyeQ5 PCIe MSI controller 2026-10-28 15:58 [PATCH 0/4] irqchip: add EyeQ5 PCIe MSI controller Gregory CLEMENT @ 2026-10-28 15:58 ` Gregory CLEMENT 2026-10-28 15:58 ` [PATCH 2/4] irqchip: irq-eyeq5-msi: add EyeQ5 PCIe MSI controller driver Gregory CLEMENT ` (2 subsequent siblings) 3 siblings, 0 replies; 6+ messages in thread From: Gregory CLEMENT @ 2026-10-28 15:58 UTC (permalink / raw) To: Vladimir Kondratiev, Benoît Monin, Théo Lebrun, Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thomas Bogendoerfer Cc: Thomas Petazzoni, linux-mips, linux-kernel, devicetree, Gregory CLEMENT Add binding for the Mobileye EyeQ5 PCIe MSI controller. Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> --- .../interrupt-controller/mobileye,eyeq5-msi.yaml | 120 +++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/Documentation/devicetree/bindings/interrupt-controller/mobileye,eyeq5-msi.yaml b/Documentation/devicetree/bindings/interrupt-controller/mobileye,eyeq5-msi.yaml new file mode 100644 index 0000000000000..919af55fc8a60 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/mobileye,eyeq5-msi.yaml @@ -0,0 +1,120 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/mobileye,eyeq5-msi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mobileye EyeQ5 PCIe MSI controller + +maintainers: + - Gregory CLEMENT <gregory.clement@bootlin.com> + +description: | + The Mobileye EyeQ5 PCIe MSI controller snoops the MSI write TLPs of a + PCIe port. The controller signals the parent interrupt controller when a + vector is pending. The memory-region property gives the base address of + the doorbell window. A port supports a maximum of 4 MSI vectors. + + This node is both an MSI parent for the PCIe port and a chained interrupt + controller. + +allOf: + - $ref: /schemas/interrupt-controller/msi-controller.yaml# + +properties: + compatible: + const: mobileye,eyeq5-msi + + reg: + items: + - description: GCSR block of the PCIe port + - description: MSI vector control block + + interrupts: + maxItems: 1 + + interrupt-controller: true + + '#interrupt-cells': + const: 1 + + msi-controller: true + + '#msi-cells': + const: 0 + + label: + $ref: /schemas/types.yaml#/definitions/string + description: Name of the MSI domain. If omitted, the node name is used. + + memory-region: + maxItems: 1 + description: + Reserved-memory region. The hardware uses its base address as the MSI + doorbell window. + + num-vectors: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of MSI vectors that the port supports. + minimum: 1 + maximum: 4 + +required: + - compatible + - reg + - interrupts + - interrupt-controller + - '#interrupt-cells' + - msi-controller + - '#msi-cells' + - memory-region + - num-vectors + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/mips-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + + / { + #address-cells = <2>; + #size-cells = <2>; + + gic: interrupt-controller { + compatible = "mti,cpu-interrupt-controller"; + interrupt-controller; + #interrupt-cells = <3>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + pci0_msi_reserved: pci0-msi@806000000 { + reg = <0x8 0x06000000 0x0 0x100000>; + }; + }; + + soc { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + msi0: msi-controller@5840000 { + compatible = "mobileye,eyeq5-msi"; + reg = <0x0 0x05840000 0x0 0x10>, + <0x0 0x05840040 0x0 0x20>; + label = "port0-msi"; + interrupt-parent = <&gic>; + interrupts = <GIC_SHARED 7 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; + #interrupt-cells = <1>; + msi-controller; + #msi-cells = <0>; + memory-region = <&pci0_msi_reserved>; + num-vectors = <4>; + }; + }; + }; -- 2.53.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/4] irqchip: irq-eyeq5-msi: add EyeQ5 PCIe MSI controller driver 2026-10-28 15:58 [PATCH 0/4] irqchip: add EyeQ5 PCIe MSI controller Gregory CLEMENT 2026-10-28 15:58 ` [PATCH 1/4] dt-bindings: interrupt-controller: add Mobileye " Gregory CLEMENT @ 2026-10-28 15:58 ` Gregory CLEMENT 2026-10-28 20:05 ` Thomas Gleixner 2026-10-28 15:58 ` [PATCH 3/4] MIPS: dts: eyeq5: add MSI controllers Gregory CLEMENT 2026-10-28 15:58 ` [PATCH 4/4] MAINTAINERS: add EyeQ5 MSI controller entry Gregory CLEMENT 3 siblings, 1 reply; 6+ messages in thread From: Gregory CLEMENT @ 2026-10-28 15:58 UTC (permalink / raw) To: Vladimir Kondratiev, Benoît Monin, Théo Lebrun, Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thomas Bogendoerfer Cc: Thomas Petazzoni, linux-mips, linux-kernel, devicetree, Gregory CLEMENT Each EyeQ5 PCIe port has its own MSI controller. The controller snoops the MSI write TLPs that the port receives. It raises an interrupt when a write targets the doorbell window. A port supports a maximum of 4 MSI vectors. The driver supports two roles, selected by the GCSR mode. In root complex mode, it registers a PCI MSI parent. In endpoint mode, it registers a platform MSI parent. The driver chains the snooper domain to one GIC interrupt. Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> --- arch/mips/Kconfig | 1 + drivers/irqchip/Kconfig | 5 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-eyeq5-msi.c | 474 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 481 insertions(+) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index d7c67cebe065f..98571ee5a20dd 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -633,6 +633,7 @@ config EYEQ select CPU_MIPSR2_IRQ_VI select CSRC_R4K select DMA_NONCOHERENT + select EYEQ5_MSI select HAVE_PCI select IRQ_MIPS_CPU select MIPS_AUTO_PFN_OFFSET diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 20b77fbc51ee0..e92134d1b1b97 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -840,4 +840,9 @@ config SUNPLUS_SP7021_INTC chained controller, routing all interrupt source in P-Chip to the primary controller on C-Chip. +config EYEQ5_MSI + bool + select IRQ_DOMAIN + select IRQ_MSI_LIB + endmenu diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index ab33cccd84719..5e6ab5c2c50fc 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -135,3 +135,4 @@ obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o obj-$(CONFIG_MCHP_EIC) += irq-mchp-eic.o obj-$(CONFIG_SOPHGO_SG2042_MSI) += irq-sg2042-msi.o obj-$(CONFIG_SUNPLUS_SP7021_INTC) += irq-sp7021-intc.o +obj-$(CONFIG_EYEQ5_MSI) += irq-eyeq5-msi.o diff --git a/drivers/irqchip/irq-eyeq5-msi.c b/drivers/irqchip/irq-eyeq5-msi.c new file mode 100644 index 0000000000000..883b6d63c9882 --- /dev/null +++ b/drivers/irqchip/irq-eyeq5-msi.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Mobileye EyeQ5 MSI interrupt controller + * + * Copyright Mobileye (2026) + */ + +#define pr_fmt(fmt) "eyeq5-msi: " fmt + +#include <linux/bitfield.h> +#include <linux/bitmap.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqchip/irq-msi-lib.h> +#include <linux/irqdomain.h> +#include <linux/msi.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +/* + * The snooper decodes the vector number from TLP address bits [4:3], so a + * port can serve at most 4 vectors. + */ +#define EYEQ_MSI_MAX_VECTORS 4 + +/* + * Vector n is raised by a write to the doorbell base + n * 8. + */ +#define EYEQ_MSI_VECTOR_STRIDE 8 + +#define GCSR_CONFIG 0x00 +#define GCSR_CONFIG_RC BIT(0) + +#define GCSR_IRQSTATUS 0x0a +#define GCSR_IRQSTATUS_MASK GENMASK(15, 12) + +#define GCSR_IRQMASK 0x0c +#define GCSR_IRQMASK_MASK GENMASK(15, 12) + +#define MSI_BASE_LO 0x00 +#define MSI_BASE_HI 0x02 + +#define MSI_CTRL 0x04 +#define MSI_CTRL_EN_OVERRIDE BIT(15) +#define MSI_CTRL_BAR_MATCH_MODE BIT(10) +#define MSI_CTRL_BAR_COMPARE GENMASK(9, 7) + +#define VECTOR_ADDR_LO GENMASK_ULL(20, 5) +#define VECTOR_ADDR_HI GENMASK_ULL(36, 21) + +struct eyeq_msi { + raw_spinlock_t lock; + struct irq_domain *msi_domain; + DECLARE_BITMAP(used, EYEQ_MSI_MAX_VECTORS); + + void __iomem *gcsr_base; + void __iomem *msi_base; + + phys_addr_t vector_phys; + + const char *label; + u32 num_vectors; + int irq; +}; + +/* + * The caller must hold msi->lock. + */ +static void __eyeq_msi_mask_irq(struct eyeq_msi *msi, unsigned int bit) +{ + u16 value; + + value = readw(msi->gcsr_base + GCSR_IRQMASK); + value &= ~FIELD_PREP(GCSR_IRQMASK_MASK, BIT(bit)); + writew(value, msi->gcsr_base + GCSR_IRQMASK); +} + +/* + * The caller must hold msi->lock. + */ +static void __eyeq_msi_unmask_irq(struct eyeq_msi *msi, unsigned int bit) +{ + u16 value; + + value = readw(msi->gcsr_base + GCSR_IRQMASK); + value |= FIELD_PREP(GCSR_IRQMASK_MASK, BIT(bit)); + writew(value, msi->gcsr_base + GCSR_IRQMASK); +} + +static void eyeq_msi_mask_irq(struct irq_data *data) +{ + struct eyeq_msi *msi = irq_data_get_irq_chip_data(data); + + guard(raw_spinlock_irqsave)(&msi->lock); + __eyeq_msi_mask_irq(msi, data->hwirq); +} + +static void eyeq_msi_unmask_irq(struct irq_data *data) +{ + struct eyeq_msi *msi = irq_data_get_irq_chip_data(data); + + guard(raw_spinlock_irqsave)(&msi->lock); + __eyeq_msi_unmask_irq(msi, data->hwirq); +} + +static void eyeq_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct eyeq_msi *msi = irq_data_get_irq_chip_data(data); + phys_addr_t address; + + memset(msg, 0, sizeof(*msg)); + + address = msi->vector_phys + data->hwirq * EYEQ_MSI_VECTOR_STRIDE; + + msg->address_lo = lower_32_bits(address); + msg->address_hi = upper_32_bits(address); + msg->data = data->hwirq; +} + +static struct irq_chip eyeq_msi_irq_chip = { + .name = "EyeQ5-MSI", + .irq_mask = eyeq_msi_mask_irq, + .irq_unmask = eyeq_msi_unmask_irq, + .irq_compose_msi_msg = eyeq_msi_compose_msg, +}; + +static void eyeq_msi_isr(struct irq_desc *desc) +{ + struct irq_chip *parent_chip = irq_desc_get_chip(desc); + struct eyeq_msi *msi = irq_desc_get_handler_data(desc); + void __iomem *status_reg; + unsigned long pending; + unsigned int bit; + int ret; + + chained_irq_enter(parent_chip, desc); + + status_reg = msi->gcsr_base + GCSR_IRQSTATUS; + + while (true) { + pending = FIELD_GET(GCSR_IRQSTATUS_MASK, + readw(status_reg)); + if (!pending) + break; + + /* + * Write-one-to-clear status register. + */ + writew(FIELD_PREP(GCSR_IRQSTATUS_MASK, pending), + status_reg); + + for_each_set_bit(bit, &pending, msi->num_vectors) { + ret = generic_handle_domain_irq(msi->msi_domain, bit); + if (ret) + pr_err_ratelimited("unmapped MSI interrupt %u\n", + bit); + } + } + + chained_irq_exit(parent_chip, desc); +} + +static int eyeq_msi_domain_alloc(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs, + void *args) +{ + struct eyeq_msi *msi = domain->host_data; + DECLARE_BITMAP(reserved, EYEQ_MSI_MAX_VECTORS); + unsigned long bit = 0; + unsigned int i; + + bitmap_zero(reserved, EYEQ_MSI_MAX_VECTORS); + + scoped_guard(raw_spinlock_irqsave, &msi->lock) { + for (i = 0; i < nr_irqs; i++) { + bit = find_next_zero_bit(msi->used, msi->num_vectors, bit); + if (bit >= msi->num_vectors) { + /* + * Roll back this call's allocations: on error the + * core does not call .free(). + */ + for_each_set_bit(bit, reserved, + EYEQ_MSI_MAX_VECTORS) { + clear_bit(bit, msi->used); + __eyeq_msi_mask_irq(msi, bit); + } + + return -ENOSPC; + } + + set_bit(bit, msi->used); + __eyeq_msi_unmask_irq(msi, bit); + set_bit(bit, reserved); + bit++; + } + } + + i = 0; + for_each_set_bit(bit, reserved, EYEQ_MSI_MAX_VECTORS) + irq_domain_set_info(domain, virq + i++, bit, + &eyeq_msi_irq_chip, msi, + handle_simple_irq, NULL, NULL); + + return 0; +} + +static void eyeq_msi_domain_free(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *data; + struct eyeq_msi *msi; + irq_hw_number_t hwirq; + unsigned int i; + + for (i = 0; i < nr_irqs; i++) { + data = irq_domain_get_irq_data(domain, virq + i); + if (WARN_ON_ONCE(!data)) + continue; + + msi = irq_data_get_irq_chip_data(data); + hwirq = data->hwirq; + + guard(raw_spinlock_irqsave)(&msi->lock); + + if (!test_and_clear_bit(hwirq, msi->used)) { + pr_warn("attempt to free unused hwirq %lu\n", + (unsigned long)hwirq); + continue; + } + + __eyeq_msi_mask_irq(msi, hwirq); + } +} + +/* + * The same callbacks serve the PCI MSI parent (root complex mode) and the + * platform MSI parent (endpoint mode). + */ +static const struct irq_domain_ops eyeq_msi_domain_ops = { + .alloc = eyeq_msi_domain_alloc, + .free = eyeq_msi_domain_free, + .select = msi_lib_irq_domain_select, +}; + +#define EYEQ_MSI_FLAGS_REQUIRED \ + (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS | \ + MSI_FLAG_NO_AFFINITY) + +#define EYEQ_MSI_FLAGS_SUPPORTED \ + (MSI_FLAG_MULTI_PCI_MSI | \ + MSI_FLAG_PCI_MSIX | \ + MSI_GENERIC_FLAGS_MASK) + +static const struct msi_parent_ops eyeq_msi_parent_ops = { + .required_flags = EYEQ_MSI_FLAGS_REQUIRED, + .supported_flags = EYEQ_MSI_FLAGS_SUPPORTED, + .bus_select_token = DOMAIN_BUS_NEXUS, + .bus_select_mask = MATCH_PCI_MSI, + .prefix = "EYEQ-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; + +/* + * In endpoint mode the snooper is a platform MSI controller: the host writes + * the doorbell and the snooper raises a local interrupt. Expose it as a + * platform MSI parent so endpoint functions can allocate doorbells. + */ +static const struct msi_parent_ops eyeq_msi_platform_parent_ops = { + .required_flags = EYEQ_MSI_FLAGS_REQUIRED, + .supported_flags = EYEQ_MSI_FLAGS_SUPPORTED, + .bus_select_token = DOMAIN_BUS_NEXUS, + .bus_select_mask = MATCH_PLATFORM_MSI, + .prefix = "EYEQ-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; + +static int eyeq_msi_create_domain(struct eyeq_msi *msi, + struct device_node *node, bool is_rc) +{ + const struct msi_parent_ops *parent_ops = is_rc ? + &eyeq_msi_parent_ops : &eyeq_msi_platform_parent_ops; + struct irq_domain_info info = { + .fwnode = of_fwnode_handle(node), + .ops = &eyeq_msi_domain_ops, + .host_data = msi, + .size = msi->num_vectors, + /* + * The composed doorbell address does not depend on affinity, + * as required by platform MSI doorbell consumers. + */ + .domain_flags = is_rc ? 0 : IRQ_DOMAIN_FLAG_MSI_IMMUTABLE, + }; + + msi->msi_domain = msi_create_parent_irq_domain(&info, parent_ops); + if (!msi->msi_domain) + return -ENOMEM; + + return 0; +} + +static int eyeq_msi_parse_vector_region(struct eyeq_msi *msi, + struct device_node *node) +{ + struct device_node *memory_node; + struct resource resource; + int ret; + + memory_node = of_parse_phandle(node, "memory-region", 0); + if (!memory_node) { + pr_err("missing memory-region property\n"); + return -EINVAL; + } + + ret = of_address_to_resource(memory_node, 0, &resource); + of_node_put(memory_node); + + if (ret) { + pr_err("failed to parse MSI vector memory region: %d\n", + ret); + return ret; + } + + msi->vector_phys = resource.start; + + return 0; +} + +static int eyeq_msi_parse_dt(struct eyeq_msi *msi, + struct device_node *node) +{ + int ret; + + ret = of_property_read_u32(node, "num-vectors", + &msi->num_vectors); + if (ret) { + pr_err("missing or invalid num-vectors property\n"); + return ret; + } + + if (!msi->num_vectors || + msi->num_vectors > EYEQ_MSI_MAX_VECTORS) { + pr_err("invalid number of vectors: %u\n", + msi->num_vectors); + return -EINVAL; + } + + ret = of_property_read_string(node, "label", &msi->label); + if (ret) + msi->label = node->name; + + return eyeq_msi_parse_vector_region(msi, node); +} + +static void eyeq_msi_program_hardware(struct eyeq_msi *msi, bool is_rc) +{ + u32 match_bar; + u16 control; + + /* + * The register holds address bits 5..36. + */ + writew(FIELD_GET(VECTOR_ADDR_LO, msi->vector_phys), + msi->msi_base + MSI_BASE_LO); + + writew(FIELD_GET(VECTOR_ADDR_HI, msi->vector_phys), + msi->msi_base + MSI_BASE_HI); + + /* + * RC mode uses BAR 2 as the no-translation BAR, endpoint mode uses + * BAR 3 as the MSI BAR. + */ + match_bar = is_rc ? 2 : 3; + + /* + * MSI_ADDR_MASK is left to zero: the vector region only differs from + * its base on address bits [4:3], which hardware excludes from the + * comparison since they hold the vector number. + */ + control = MSI_CTRL_EN_OVERRIDE | + MSI_CTRL_BAR_MATCH_MODE | + FIELD_PREP(MSI_CTRL_BAR_COMPARE, match_bar); + + writew(control, msi->msi_base + MSI_CTRL); +} + +static int __init eyeq_msi_of_init(struct device_node *node, + struct device_node *parent) +{ + struct eyeq_msi *msi; + bool is_rc; + int ret; + + msi = kzalloc_obj(struct eyeq_msi); + if (!msi) + return -ENOMEM; + + raw_spin_lock_init(&msi->lock); + bitmap_zero(msi->used, EYEQ_MSI_MAX_VECTORS); + msi->irq = -1; + + msi->gcsr_base = of_iomap(node, 0); + if (!msi->gcsr_base) { + pr_err("failed to map GCSR registers\n"); + ret = -ENOMEM; + goto err_free; + } + + msi->msi_base = of_iomap(node, 1); + if (!msi->msi_base) { + pr_err("failed to map MSI registers\n"); + ret = -ENOMEM; + goto err_unmap_gcsr; + } + + ret = eyeq_msi_parse_dt(msi, node); + if (ret) + goto err_unmap_msi; + + msi->irq = of_irq_get(node, 0); + if (msi->irq < 0) { + ret = msi->irq; + pr_err("failed to obtain parent IRQ: %d\n", ret); + goto err_unmap_msi; + } + + is_rc = FIELD_GET(GCSR_CONFIG_RC, + readw(msi->gcsr_base + GCSR_CONFIG)); + + ret = eyeq_msi_create_domain(msi, node, is_rc); + if (ret) { + pr_err("failed to create IRQ domain: %d\n", ret); + goto err_dispose_irq; + } + + eyeq_msi_program_hardware(msi, is_rc); + + /* + * Install the chained handler only once the domain and hardware are + * ready, otherwise an interrupt could arrive unhandled. + */ + irq_set_chained_handler_and_data(msi->irq, + eyeq_msi_isr, msi); + + pr_info("%s: initialized in %s mode with %u vectors, parent IRQ %d\n", + msi->label, is_rc ? "root-complex" : "endpoint", + msi->num_vectors, msi->irq); + + return 0; + +err_dispose_irq: + irq_dispose_mapping(msi->irq); + +err_unmap_msi: + iounmap(msi->msi_base); + +err_unmap_gcsr: + iounmap(msi->gcsr_base); + +err_free: + kfree(msi); + + return ret; +} + +IRQCHIP_DECLARE(eyeq5_msi, "mobileye,eyeq5-msi", + eyeq_msi_of_init); -- 2.53.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/4] irqchip: irq-eyeq5-msi: add EyeQ5 PCIe MSI controller driver 2026-10-28 15:58 ` [PATCH 2/4] irqchip: irq-eyeq5-msi: add EyeQ5 PCIe MSI controller driver Gregory CLEMENT @ 2026-10-28 20:05 ` Thomas Gleixner 0 siblings, 0 replies; 6+ messages in thread From: Thomas Gleixner @ 2026-10-28 20:05 UTC (permalink / raw) To: Gregory CLEMENT, Vladimir Kondratiev, Benoît Monin, Théo Lebrun, Radu Rendec, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thomas Bogendoerfer Cc: Thomas Petazzoni, linux-mips, linux-kernel, devicetree, Gregory CLEMENT On Wed, Oct 28 2026 at 16:58, Gregory CLEMENT wrote: > +/* > + * The caller must hold msi->lock. Instead of a non-enforcable comment can you please put a lockdep_assert_held() into the code. > + */ > +static void __eyeq_msi_mask_irq(struct eyeq_msi *msi, unsigned int bit) > +{ > + u16 value; > + > + value = readw(msi->gcsr_base + GCSR_IRQMASK); > + value &= ~FIELD_PREP(GCSR_IRQMASK_MASK, BIT(bit)); > + writew(value, msi->gcsr_base + GCSR_IRQMASK); > +} > +static struct irq_chip eyeq_msi_irq_chip = { > + .name = "EyeQ5-MSI", > + .irq_mask = eyeq_msi_mask_irq, > + .irq_unmask = eyeq_msi_unmask_irq, > + .irq_compose_msi_msg = eyeq_msi_compose_msg, > +}; > + > +static void eyeq_msi_isr(struct irq_desc *desc) > +{ > + struct irq_chip *parent_chip = irq_desc_get_chip(desc); > + struct eyeq_msi *msi = irq_desc_get_handler_data(desc); > + void __iomem *status_reg; > + unsigned long pending; > + unsigned int bit; > + int ret; > + > + chained_irq_enter(parent_chip, desc); > + > + status_reg = msi->gcsr_base + GCSR_IRQSTATUS; > + > + while (true) { > + pending = FIELD_GET(GCSR_IRQSTATUS_MASK, > + readw(status_reg)); > + if (!pending) > + break; > + > + /* > + * Write-one-to-clear status register. > + */ > + writew(FIELD_PREP(GCSR_IRQSTATUS_MASK, pending), > + status_reg); > + > + for_each_set_bit(bit, &pending, msi->num_vectors) { > + ret = generic_handle_domain_irq(msi->msi_domain, bit); You want to look at generic_handle_demux_domain_irq() and related functionality so you can actually provide a irq_set_affinity() callback for these demultiplexed interrupts. > + if (ret) > + pr_err_ratelimited("unmapped MSI interrupt %u\n", > + bit); No line break required. You have 100 characters. Otherwise this needs curly brackets around the "two-liner". > + } > + } > + > + chained_irq_exit(parent_chip, desc); > +} > + > +static int eyeq_msi_domain_alloc(struct irq_domain *domain, > + unsigned int virq, > + unsigned int nr_irqs, > + void *args) 100 characters... All over the place > +{ > + struct eyeq_msi *msi = domain->host_data; > + DECLARE_BITMAP(reserved, EYEQ_MSI_MAX_VECTORS); > + unsigned long bit = 0; > + unsigned int i; > + > + bitmap_zero(reserved, EYEQ_MSI_MAX_VECTORS); > + > + scoped_guard(raw_spinlock_irqsave, &msi->lock) { > + for (i = 0; i < nr_irqs; i++) { > + bit = find_next_zero_bit(msi->used, msi->num_vectors, bit); > + if (bit >= msi->num_vectors) { > + /* > + * Roll back this call's allocations: on error the > + * core does not call .free(). > + */ > + for_each_set_bit(bit, reserved, > + EYEQ_MSI_MAX_VECTORS) { > + clear_bit(bit, msi->used); > + __eyeq_msi_mask_irq(msi, bit); > + } > + > + return -ENOSPC; > + } > + > + set_bit(bit, msi->used); > + __eyeq_msi_unmask_irq(msi, bit); > + set_bit(bit, reserved); > + bit++; > + } > + } > + > + i = 0; > + for_each_set_bit(bit, reserved, EYEQ_MSI_MAX_VECTORS) for....() { > + irq_domain_set_info(domain, virq + i++, bit, > + &eyeq_msi_irq_chip, msi, > + handle_simple_irq, NULL, NULL); > + } See documentation. > + return 0; > +} > + > +static void eyeq_msi_domain_free(struct irq_domain *domain, > + unsigned int virq, > + unsigned int nr_irqs) .... > +{ > + struct irq_data *data; > + struct eyeq_msi *msi; > + irq_hw_number_t hwirq; Reverse fir tree ordering please. See documentation. And please move these declarations into the scope where they are used. > + unsigned int i; > + > + for (i = 0; i < nr_irqs; i++) { for (unsigned int i = 0; ...) > + data = irq_domain_get_irq_data(domain, virq + i); > + if (WARN_ON_ONCE(!data)) > + continue; > + > +static int eyeq_msi_parse_dt(struct eyeq_msi *msi, > + struct device_node *node) > +{ > + int ret; > + > + ret = of_property_read_u32(node, "num-vectors", > + &msi->num_vectors); int ret = of....(); > + if (ret) { > + pr_err("missing or invalid num-vectors property\n"); > + return ret; > + } > + Thanks, tglx ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 3/4] MIPS: dts: eyeq5: add MSI controllers 2026-10-28 15:58 [PATCH 0/4] irqchip: add EyeQ5 PCIe MSI controller Gregory CLEMENT 2026-10-28 15:58 ` [PATCH 1/4] dt-bindings: interrupt-controller: add Mobileye " Gregory CLEMENT 2026-10-28 15:58 ` [PATCH 2/4] irqchip: irq-eyeq5-msi: add EyeQ5 PCIe MSI controller driver Gregory CLEMENT @ 2026-10-28 15:58 ` Gregory CLEMENT 2026-10-28 15:58 ` [PATCH 4/4] MAINTAINERS: add EyeQ5 MSI controller entry Gregory CLEMENT 3 siblings, 0 replies; 6+ messages in thread From: Gregory CLEMENT @ 2026-10-28 15:58 UTC (permalink / raw) To: Vladimir Kondratiev, Benoît Monin, Théo Lebrun, Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thomas Bogendoerfer Cc: Thomas Petazzoni, linux-mips, linux-kernel, devicetree, Gregory CLEMENT Add the two EyeQ5 MSI controllers. They use reserved-memory regions as their doorbell windows. Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> --- arch/mips/boot/dts/mobileye/eyeq5.dtsi | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/arch/mips/boot/dts/mobileye/eyeq5.dtsi b/arch/mips/boot/dts/mobileye/eyeq5.dtsi index cec5ad8752285..be5239d890239 100644 --- a/arch/mips/boot/dts/mobileye/eyeq5.dtsi +++ b/arch/mips/boot/dts/mobileye/eyeq5.dtsi @@ -309,6 +309,36 @@ gpio1: gpio@1500000 { resets = <&olb 0 26>; }; + msi0: msi-controller@5840000 { + compatible = "mobileye,eyeq5-msi"; + reg = <0x0 0x05840000 0x0 0x10>, + <0x0 0x05840040 0x0 0x20>; + label = "port0-msi"; + interrupt-parent = <&gic>; + interrupt-controller; + #interrupt-cells = <1>; + msi-controller; + #msi-cells = <0>; + interrupts = <GIC_SHARED 7 IRQ_TYPE_LEVEL_HIGH>; + memory-region = <&pci0_msi_reserved>; + num-vectors = <4>; + }; + + msi1: msi-controller@5840010 { + compatible = "mobileye,eyeq5-msi"; + reg = <0x0 0x05840010 0x0 0x10>, + <0x0 0x05840060 0x0 0x20>; + label = "port1-msi"; + interrupt-parent = <&gic>; + interrupt-controller; + #interrupt-cells = <1>; + msi-controller; + #msi-cells = <0>; + interrupts = <GIC_SHARED 48 IRQ_TYPE_LEVEL_HIGH>; + memory-region = <&pci1_msi_reserved>; + num-vectors = <4>; + }; + iocu-bus { compatible = "simple-bus"; #address-cells = <2>; -- 2.53.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 4/4] MAINTAINERS: add EyeQ5 MSI controller entry 2026-10-28 15:58 [PATCH 0/4] irqchip: add EyeQ5 PCIe MSI controller Gregory CLEMENT ` (2 preceding siblings ...) 2026-10-28 15:58 ` [PATCH 3/4] MIPS: dts: eyeq5: add MSI controllers Gregory CLEMENT @ 2026-10-28 15:58 ` Gregory CLEMENT 3 siblings, 0 replies; 6+ messages in thread From: Gregory CLEMENT @ 2026-10-28 15:58 UTC (permalink / raw) To: Vladimir Kondratiev, Benoît Monin, Théo Lebrun, Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thomas Bogendoerfer Cc: Thomas Petazzoni, linux-mips, linux-kernel, devicetree, Gregory CLEMENT Add the EyeQ5 MSI controller binding document and driver. Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 782389bd5cd3d..737a9fe66a3de 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18348,12 +18348,14 @@ M: Gregory CLEMENT <gregory.clement@bootlin.com> M: Théo Lebrun <theo.lebrun@bootlin.com> L: linux-mips@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/interrupt-controller/mobileye,eyeq5-msi.yaml F: Documentation/devicetree/bindings/mips/mobileye.yaml F: Documentation/devicetree/bindings/soc/mobileye/ F: arch/mips/boot/dts/mobileye/ F: arch/mips/configs/eyeq*_defconfig F: arch/mips/mobileye/board-epm5.its.S F: drivers/clk/clk-eyeq.c +F: drivers/irqchip/irq-eyeq5-msi.c F: drivers/phy/phy-eyeq5-eth.c F: drivers/pinctrl/pinctrl-eyeq5.c F: drivers/reset/reset-eyeq.c -- 2.53.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-28 20:05 UTC | newest] Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-28 15:58 [PATCH 0/4] irqchip: add EyeQ5 PCIe MSI controller Gregory CLEMENT 2026-10-28 15:58 ` [PATCH 1/4] dt-bindings: interrupt-controller: add Mobileye " Gregory CLEMENT 2026-10-28 15:58 ` [PATCH 2/4] irqchip: irq-eyeq5-msi: add EyeQ5 PCIe MSI controller driver Gregory CLEMENT 2026-10-28 20:05 ` Thomas Gleixner 2026-10-28 15:58 ` [PATCH 3/4] MIPS: dts: eyeq5: add MSI controllers Gregory CLEMENT 2026-10-28 15:58 ` [PATCH 4/4] MAINTAINERS: add EyeQ5 MSI controller entry Gregory CLEMENT
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®