* [PATCH v2 0/4] irqchip: add EyeQ5 PCIe MSI controller
@ 2026-11-02 16:50 Gregory CLEMENT
2026-11-02 16:50 ` [PATCH v2 1/4] dt-bindings: interrupt-controller: add Mobileye " Gregory CLEMENT
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Gregory CLEMENT @ 2026-11-02 16:50 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 on two EyeQ5 SoCs that have a PCIe link. One SoC
acted as the host and the other SoC acted as the endpoint. The
pci_endpoint_test kselftest passed the MSI-X and DOORBELL_TEST cases.
The MSI test uses one vector because the controller does not support
standard Multi-MSI. An additional test made sure that each MSI-X vector
uses its target CPU (using affinity).
Grégory
Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com>
---
Changes in v2:
- Address review comments from Krzysztof Kozlowski, Thomas Gleixner and
sashiko.
- dt-bindings: drop the num-vectors property (all ports have four
vectors) and simplify the example.
- irqchip: use the fixed four-vector count, add lockdep assertions, and
dispatch through generic_handle_demux_domain_irq() with a redirect-based
irq_set_affinity() callback.
- irqchip: drop MSI_FLAG_MULTI_PCI_MSI (standard Multi-MSI cannot work
with the address-based vector decoding) and MSI_FLAG_NO_AFFINITY; add
MSI_FLAG_PCI_MSI_MASK_PARENT.
- irqchip: keep vectors masked during allocation, mask all vectors and
clear pending status before installing the chained handler, and reject
of_irq_get() returning 0.
- MIPS: dts: drop the num-vectors property from both MSI nodes.
- Reformat the driver for the 100-column limit.
- Link to v1: https://patch.msgid.link/20261028-eyeq5_msi-v1-0-c464752fb55f@bootlin.com
To: Vladimir Kondratiev <vladimir.kondratiev@mobileye.com>
To: Benoît Monin <benoit.monin@bootlin.com>
To: Gregory CLEMENT <gregory.clement@bootlin.com>
To: Théo Lebrun <theo.lebrun@bootlin.com>
To: Thomas Gleixner <tglx@kernel.org>
To: Radu Rendec <radu@rendec.net>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Cc: linux-mips@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: devicetree@vger.kernel.org
---
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 | 90 +++++
MAINTAINERS | 2 +
arch/mips/Kconfig | 1 +
arch/mips/boot/dts/mobileye/eyeq5.dtsi | 28 ++
drivers/irqchip/Kconfig | 5 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-eyeq5-msi.c | 439 +++++++++++++++++++++
7 files changed, 566 insertions(+)
---
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
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] 5+ messages in thread
* [PATCH v2 1/4] dt-bindings: interrupt-controller: add Mobileye EyeQ5 PCIe MSI controller
2026-11-02 16:50 [PATCH v2 0/4] irqchip: add EyeQ5 PCIe MSI controller Gregory CLEMENT
@ 2026-11-02 16:50 ` Gregory CLEMENT
2026-11-02 16:50 ` [PATCH v2 2/4] irqchip: irq-eyeq5-msi: add EyeQ5 PCIe MSI controller driver Gregory CLEMENT
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Gregory CLEMENT @ 2026-11-02 16:50 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 | 90 ++++++++++++++++++++++
1 file changed, 90 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..b86698e4f82c7
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/mobileye,eyeq5-msi.yaml
@@ -0,0 +1,90 @@
+# 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.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - interrupt-controller
+ - '#interrupt-cells'
+ - msi-controller
+ - '#msi-cells'
+ - memory-region
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/mips-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ 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>;
+ };
+ };
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 2/4] irqchip: irq-eyeq5-msi: add EyeQ5 PCIe MSI controller driver
2026-11-02 16:50 [PATCH v2 0/4] irqchip: add EyeQ5 PCIe MSI controller Gregory CLEMENT
2026-11-02 16:50 ` [PATCH v2 1/4] dt-bindings: interrupt-controller: add Mobileye " Gregory CLEMENT
@ 2026-11-02 16:50 ` Gregory CLEMENT
2026-11-02 16:50 ` [PATCH v2 3/4] MIPS: dts: eyeq5: add MSI controllers Gregory CLEMENT
2026-11-02 16:50 ` [PATCH v2 4/4] MAINTAINERS: add EyeQ5 MSI controller entry Gregory CLEMENT
3 siblings, 0 replies; 5+ messages in thread
From: Gregory CLEMENT @ 2026-11-02 16:50 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 | 439 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 446 insertions(+)
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index ed17c9b1624ca..19dbf24684b23 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -632,6 +632,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 136bab41f8011..3874dee5db97b 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -813,6 +813,11 @@ config APPLE_AIC
Support for the Apple Interrupt Controller found on Apple Silicon SoCs,
such as the M1.
+config EYEQ5_MSI
+ bool
+ select IRQ_DOMAIN
+ select IRQ_MSI_LIB
+
config MCHP_EIC
bool "Microchip External Interrupt Controller"
depends on ARCH_AT91 || COMPILE_TEST
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 16a5fd126c9a9..5bc276fd82cab 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -132,6 +132,7 @@ obj-$(CONFIG_MACH_REALTEK_RTL) += irq-realtek-rtl.o
obj-$(CONFIG_WPCM450_AIC) += irq-wpcm450-aic.o
obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o
obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o
+obj-$(CONFIG_EYEQ5_MSI) += irq-eyeq5-msi.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
diff --git a/drivers/irqchip/irq-eyeq5-msi.c b/drivers/irqchip/irq-eyeq5-msi.c
new file mode 100644
index 0000000000000..f56e130393cb2
--- /dev/null
+++ b/drivers/irqchip/irq-eyeq5-msi.c
@@ -0,0 +1,439 @@
+// 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;
+ int irq;
+};
+
+static void __eyeq_msi_mask_irq(struct eyeq_msi *msi, unsigned int bit)
+{
+ u16 value;
+
+ lockdep_assert_held(&msi->lock);
+
+ 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_unmask_irq(struct eyeq_msi *msi, unsigned int bit)
+{
+ u16 value;
+
+ lockdep_assert_held(&msi->lock);
+
+ 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,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = irq_chip_redirect_set_affinity,
+#endif
+};
+
+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;
+
+ 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, EYEQ_MSI_MAX_VECTORS) {
+ if (!generic_handle_demux_domain_irq(msi->msi_domain, bit))
+ 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)
+{
+ DECLARE_BITMAP(reserved, EYEQ_MSI_MAX_VECTORS);
+ struct eyeq_msi *msi = domain->host_data;
+ 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, EYEQ_MSI_MAX_VECTORS, bit);
+ if (bit >= EYEQ_MSI_MAX_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);
+ 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)
+{
+ for (unsigned int i = 0; i < nr_irqs; i++) {
+ irq_hw_number_t hwirq;
+ struct irq_data *data;
+ struct eyeq_msi *msi;
+
+ 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_PCI_MSI_MASK_PARENT)
+
+#define EYEQ_MSI_FLAGS_SUPPORTED \
+ (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 = EYEQ_MSI_MAX_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 = 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)
+{
+ u16 control, value;
+ u32 match_bar;
+
+ guard(raw_spinlock_irqsave)(&msi->lock);
+ value = readw(msi->gcsr_base + GCSR_IRQMASK);
+ value &= ~GCSR_IRQMASK_MASK;
+ writew(value, msi->gcsr_base + GCSR_IRQMASK);
+ writew(GCSR_IRQSTATUS_MASK, msi->gcsr_base + GCSR_IRQSTATUS);
+
+ /*
+ * 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 ?: -EINVAL;
+ 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", EYEQ_MSI_MAX_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] 5+ messages in thread
* [PATCH v2 3/4] MIPS: dts: eyeq5: add MSI controllers
2026-11-02 16:50 [PATCH v2 0/4] irqchip: add EyeQ5 PCIe MSI controller Gregory CLEMENT
2026-11-02 16:50 ` [PATCH v2 1/4] dt-bindings: interrupt-controller: add Mobileye " Gregory CLEMENT
2026-11-02 16:50 ` [PATCH v2 2/4] irqchip: irq-eyeq5-msi: add EyeQ5 PCIe MSI controller driver Gregory CLEMENT
@ 2026-11-02 16:50 ` Gregory CLEMENT
2026-11-02 16:50 ` [PATCH v2 4/4] MAINTAINERS: add EyeQ5 MSI controller entry Gregory CLEMENT
3 siblings, 0 replies; 5+ messages in thread
From: Gregory CLEMENT @ 2026-11-02 16:50 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 | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/arch/mips/boot/dts/mobileye/eyeq5.dtsi b/arch/mips/boot/dts/mobileye/eyeq5.dtsi
index cec5ad8752285..39549cef998ea 100644
--- a/arch/mips/boot/dts/mobileye/eyeq5.dtsi
+++ b/arch/mips/boot/dts/mobileye/eyeq5.dtsi
@@ -309,6 +309,34 @@ 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>;
+ };
+
+ 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>;
+ };
+
iocu-bus {
compatible = "simple-bus";
#address-cells = <2>;
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 4/4] MAINTAINERS: add EyeQ5 MSI controller entry
2026-11-02 16:50 [PATCH v2 0/4] irqchip: add EyeQ5 PCIe MSI controller Gregory CLEMENT
` (2 preceding siblings ...)
2026-11-02 16:50 ` [PATCH v2 3/4] MIPS: dts: eyeq5: add MSI controllers Gregory CLEMENT
@ 2026-11-02 16:50 ` Gregory CLEMENT
3 siblings, 0 replies; 5+ messages in thread
From: Gregory CLEMENT @ 2026-11-02 16:50 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 93abbb315065d..ca3a32790261d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18769,12 +18769,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] 5+ messages in thread
end of thread, other threads:[~2026-11-02 16:51 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-02 16:50 [PATCH v2 0/4] irqchip: add EyeQ5 PCIe MSI controller Gregory CLEMENT
2026-11-02 16:50 ` [PATCH v2 1/4] dt-bindings: interrupt-controller: add Mobileye " Gregory CLEMENT
2026-11-02 16:50 ` [PATCH v2 2/4] irqchip: irq-eyeq5-msi: add EyeQ5 PCIe MSI controller driver Gregory CLEMENT
2026-11-02 16:50 ` [PATCH v2 3/4] MIPS: dts: eyeq5: add MSI controllers Gregory CLEMENT
2026-11-02 16:50 ` [PATCH v2 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®