From: Tushar Nimkar <tushar.nimkar@amd.com>
To: Thomas Gleixner <tglx@linutronix.de>,
<linux-kernel@vger.kernel.org>,
<linux-arm-kernel@lists.infradead.org>
Cc: Michal Simek <michal.simek@amd.com>,
Anirudha Sarangi <anirudha.sarangi@amd.com>, <git-dev@amd.com>,
Tushar Nimkar <tushar.nimkar@amd.com>
Subject: [PATCH 2/2] irqchip: Add Xilinx Versal NET SMMU CSR interrupt controller driver
Date: Mon, 17 Aug 2026 16:22:51 +0530 [thread overview]
Message-ID: <20260817105251.1557770-1-tushar.nimkar@amd.com> (raw)
From: Anirudha Sarangi <anirudha.sarangi@amd.com>
The Xilinx Versal NET SoC implements a Control and Status Register (CSR)
block in front of the ARM SMMUv3 to manage EVENTQ, PRIQ and GLOBAL
interrupts. Interrupts are enabled, cleared, and acknowledged
through this block by writing to the relevant registers. Once an
interrupt is acknowledged, it is forwarded to the parent interrupt
controller for further processing.
Add an irqchip driver for the CSR block. The driver registers as an
interrupt controller and chains to the upstream SMMUv3 driver, so
that the standard SMMUv3 driver does not require modification while still
supporting Xilinx SoCs.
The CSR block can also gate the SMMUv3 CMDQ_SYNC completion interrupt
(bit 1), but it is intentionally left unsupported. The Linux arm-smmu-v3
driver signals CMDQ_SYNC completion via polling or MSI and never requests
a wired CMD_SYNC interrupt, so there is no in-kernel consumer for it.
Exposing it as a child line would only risk spurious, never-acked
interrupts, and it is omitted from the DT binding for the same reason.
This driver is intended to operate in conjunction with the ARM SMMUv3
driver and the kernel's IOMMU DMA infrastructure. These dependencies
are not selected directly in Kconfig to avoid introducing dependency
recursion.
Signed-off-by: Anirudha Sarangi <anirudha.sarangi@amd.com>
Co-developed-by: Tushar Nimkar <tushar.nimkar@amd.com>
Signed-off-by: Tushar Nimkar <tushar.nimkar@amd.com>
---
MAINTAINERS | 10 ++
drivers/irqchip/Kconfig | 10 ++
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-xilinx-smmu-csr.c | 225 ++++++++++++++++++++++++++
4 files changed, 246 insertions(+)
create mode 100644 drivers/irqchip/irq-xilinx-smmu-csr.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 5c9c9b5cf44a..bf790567e756 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -29881,6 +29881,16 @@ F: Documentation/misc-devices/xilinx_sdfec.rst
F: drivers/misc/xilinx_sdfec.c
F: include/uapi/misc/xilinx_sdfec.h
+XILINX SMMU CSR IRQ DRIVER
+M: Anirudha Sarangi <anirudha.sarangi@amd.com>
+M: Tushar Nimkar <tushar.nimkar@amd.com>
+L: git@amd.com
+L: linux-arm-kernel@lists.infradead.org
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/interrupt-controller/xlnx,versal-net-smmu-csr.yaml
+F: drivers/irqchip/irq-xilinx-smmu-csr.c
+
XILINX TRNG DRIVER
M: Mounika Botcha <mounika.botcha@amd.com>
M: Harsh Jain <h.jain@amd.com>
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 20b77fbc51ee..a08bda407a63 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -840,4 +840,14 @@ config SUNPLUS_SP7021_INTC
chained controller, routing all interrupt source in P-Chip to
the primary controller on C-Chip.
+config IRQCHIP_XILINX_SMMU_CSR
+ bool "Xilinx SMMU CSR Interrupt Driver"
+ depends on ARCH_ZYNQMP
+ select IRQ_DOMAIN
+ help
+ This driver does the initial handling of SMMU interrupts before
+ forwarding them to the ARM SMMU v3 driver. It is intended for
+ ZynqMP-class platforms where a vendor CSR block mediates SMMU
+ interrupts.
+
endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ab33cccd8471..16a5fd126c9a 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_IRQCHIP_XILINX_SMMU_CSR) += irq-xilinx-smmu-csr.o
diff --git a/drivers/irqchip/irq-xilinx-smmu-csr.c b/drivers/irqchip/irq-xilinx-smmu-csr.c
new file mode 100644
index 000000000000..189185d89746
--- /dev/null
+++ b/drivers/irqchip/irq-xilinx-smmu-csr.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Versal Net SMMU CSR interrupt controller driver
+ *
+ * Copyright (C) 2026 Advanced Micro Devices, Inc.
+ *
+ * Vendor CSR block that gates the ARM SMMUv3 wired interrupts. Chains
+ * off the single GIC parent line and demultiplexes the CSR status into
+ * three children (eventq, gerror, priq), each enabled/acked
+ * via SMMU_CSR_IER/IDR/ISR that the ARM SMMUv3 driver requests individually.
+ */
+
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+/* Interrupt types */
+#define SMMU_INTR_EVENT BIT(0)
+#define SMMU_INTR_GLOBAL BIT(2)
+#define SMMU_INTR_PRI BIT(3)
+
+/* Mask for all SMMU interrupts */
+#define SMMU_INTR_ALL (SMMU_INTR_EVENT | \
+ SMMU_INTR_GLOBAL | SMMU_INTR_PRI)
+
+#define SMMU_CSR_ISR 0x24 /* Interrupt Status */
+#define SMMU_CSR_IER 0x2c /* Interrupt Enable */
+#define SMMU_CSR_IDR 0x30 /* Interrupt Disable */
+
+/**
+ * struct xilinx_smmu_csr - SMMU CSR interrupt controller context
+ * @base: MMIO base address of the CSR registers
+ * @domain: IRQ domain for the child interrupts
+ * @parent_irq: parent (GIC) IRQ this block is chained to
+ * @lock: protects the SMMU_CSR_IER/IDR/ISR read and writes
+ */
+struct xilinx_smmu_csr {
+ void __iomem *base;
+ struct irq_domain *domain;
+ int parent_irq;
+ raw_spinlock_t lock;
+};
+
+enum xilinx_smmu_csr_irq {
+ SMMU_CSR_IRQ_EVENTQ = 0,
+ SMMU_CSR_IRQ_GERROR = 2,
+ SMMU_CSR_IRQ_PRIQ = 3,
+ SMMU_CSR_IRQ_NR,
+};
+
+static u32 xilinx_smmu_csr_hwirq_mask(irq_hw_number_t hwirq)
+{
+ if (hwirq >= SMMU_CSR_IRQ_NR)
+ return 0;
+
+ return BIT(hwirq) & SMMU_INTR_ALL;
+}
+
+static void xilinx_smmu_csr_irq_mask(struct irq_data *d)
+{
+ struct xilinx_smmu_csr *csr = irq_data_get_irq_chip_data(d);
+ u32 mask = xilinx_smmu_csr_hwirq_mask(d->hwirq);
+
+ if (!mask)
+ return;
+
+ raw_spin_lock(&csr->lock);
+ writel(mask, csr->base + SMMU_CSR_IDR);
+ raw_spin_unlock(&csr->lock);
+}
+
+static void xilinx_smmu_csr_irq_unmask(struct irq_data *d)
+{
+ struct xilinx_smmu_csr *csr = irq_data_get_irq_chip_data(d);
+ u32 mask = xilinx_smmu_csr_hwirq_mask(d->hwirq);
+
+ if (!mask)
+ return;
+
+ raw_spin_lock(&csr->lock);
+ writel(mask, csr->base + SMMU_CSR_IER);
+ raw_spin_unlock(&csr->lock);
+}
+
+static void xilinx_smmu_csr_irq_ack(struct irq_data *d)
+{
+ struct xilinx_smmu_csr *csr = irq_data_get_irq_chip_data(d);
+ u32 mask = xilinx_smmu_csr_hwirq_mask(d->hwirq);
+
+ if (!mask)
+ return;
+
+ raw_spin_lock(&csr->lock);
+ writel(mask, csr->base + SMMU_CSR_ISR);
+ raw_spin_unlock(&csr->lock);
+}
+
+static struct irq_chip xilinx_smmu_csr_chip = {
+ .name = "xlnx-smmu-csr",
+ .irq_mask = xilinx_smmu_csr_irq_mask,
+ .irq_unmask = xilinx_smmu_csr_irq_unmask,
+ .irq_ack = xilinx_smmu_csr_irq_ack,
+};
+
+static void xilinx_smmu_csr_irq_handler(struct irq_desc *desc)
+{
+ struct xilinx_smmu_csr *csr = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ u32 status, pending;
+
+ chained_irq_enter(chip, desc);
+ raw_spin_lock(&csr->lock);
+ status = readl(csr->base + SMMU_CSR_ISR);
+ raw_spin_unlock(&csr->lock);
+
+ /* Only service sources we support; SMMU_CSR_ISR latches raw status */
+ pending = status & SMMU_INTR_ALL;
+
+ while (pending) {
+ irq_hw_number_t hwirq = __ffs(pending);
+ int ret;
+
+ ret = generic_handle_domain_irq(csr->domain, hwirq);
+ if (ret) {
+ raw_spin_lock(&csr->lock);
+ writel(BIT(hwirq), csr->base + SMMU_CSR_ISR);
+ raw_spin_unlock(&csr->lock);
+ pr_err_ratelimited("xilinx-smmu-csr: Failed to handle domain IRQ %lu: %d\n",
+ hwirq, ret);
+ }
+
+ pending &= ~BIT(hwirq);
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static int xilinx_smmu_csr_domain_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hwirq)
+{
+ struct xilinx_smmu_csr *csr = d->host_data;
+
+ if (!xilinx_smmu_csr_hwirq_mask(hwirq))
+ return -EINVAL;
+
+ irq_set_chip_and_handler(virq, &xilinx_smmu_csr_chip, handle_level_irq);
+ irq_set_chip_data(virq, csr);
+ irq_set_status_flags(virq, IRQ_LEVEL);
+
+ return 0;
+}
+
+static const struct irq_domain_ops xilinx_smmu_csr_domain_ops = {
+ .map = xilinx_smmu_csr_domain_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static int __init xilinx_smmu_csr_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct xilinx_smmu_csr *csr;
+ int ret;
+
+ if (WARN_ON_ONCE(!parent))
+ return -EINVAL;
+
+ if (irq_find_matching_fwnode(of_fwnode_handle(node),
+ DOMAIN_BUS_ANY))
+ return -ENODEV;
+
+ csr = kzalloc(sizeof(*csr), GFP_KERNEL);
+ if (!csr)
+ return -ENOMEM;
+
+ raw_spin_lock_init(&csr->lock);
+
+ csr->base = of_iomap(node, 0);
+ if (!csr->base) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ /* Start from a known state: all sources disabled, latches cleared. */
+ writel(SMMU_INTR_ALL, csr->base + SMMU_CSR_IDR);
+ writel(SMMU_INTR_ALL, csr->base + SMMU_CSR_ISR);
+
+ csr->domain = irq_domain_create_linear(of_fwnode_handle(node), SMMU_CSR_IRQ_NR,
+ &xilinx_smmu_csr_domain_ops,
+ csr);
+ if (!csr->domain) {
+ pr_err("%pOF: failed to create irq domain\n", node);
+ ret = -ENOMEM;
+ goto unmap;
+ }
+
+ csr->parent_irq = irq_of_parse_and_map(node, 0);
+ if (!csr->parent_irq) {
+ pr_err("%pOF: failed to map parent irq\n", node);
+ ret = -EINVAL;
+ goto remove_domain;
+ }
+
+ irq_set_chained_handler_and_data(csr->parent_irq,
+ xilinx_smmu_csr_irq_handler, csr);
+
+ pr_debug("%pOF: Xilinx SMMU CSR interrupt controller registered\n", node);
+
+ return 0;
+
+remove_domain:
+ irq_domain_remove(csr->domain);
+unmap:
+ iounmap(csr->base);
+free:
+ kfree(csr);
+ return ret;
+}
+
+IRQCHIP_DECLARE(xilinx_smmu_csr, "xlnx,versal-net-smmu-csr",
+ xilinx_smmu_csr_init);
--
2.34.1
next reply other threads:[~2026-08-17 10:53 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-17 10:52 Tushar Nimkar [this message]
2026-08-19 19:33 ` [PATCH 2/2] irqchip: Add Xilinx Versal NET SMMU CSR interrupt controller driver Thomas Gleixner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260817105251.1557770-1-tushar.nimkar@amd.com \
--to=tushar.nimkar@amd.com \
--cc=anirudha.sarangi@amd.com \
--cc=git-dev@amd.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=michal.simek@amd.com \
--cc=tglx@linutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox