Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2217545/?format=api
{ "id": 2217545, "url": "http://patchwork.ozlabs.org/api/patches/2217545/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-aspeed/patch/20260330-irqchip-v4-2-3c0f1620cc06@aspeedtech.com/", "project": { "id": 57, "url": "http://patchwork.ozlabs.org/api/projects/57/?format=api", "name": "Linux ASPEED SoC development", "link_name": "linux-aspeed", "list_id": "linux-aspeed.lists.ozlabs.org", "list_email": "linux-aspeed@lists.ozlabs.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260330-irqchip-v4-2-3c0f1620cc06@aspeedtech.com>", "list_archive_url": null, "date": "2026-03-30T06:32:11", "name": "[v4,2/4] irqchip/ast2700-intc: Add AST2700-A2 support", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "6021cbf7cc001eda847a501c320558a48aa84fff", "submitter": { "id": 71489, "url": "http://patchwork.ozlabs.org/api/people/71489/?format=api", "name": "Ryan Chen", "email": "ryan_chen@aspeedtech.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-aspeed/patch/20260330-irqchip-v4-2-3c0f1620cc06@aspeedtech.com/mbox/", "series": [ { "id": 497963, "url": "http://patchwork.ozlabs.org/api/series/497963/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-aspeed/list/?series=497963", "date": "2026-03-30T06:32:10", "name": "AST2700-A2 interrupt controller hierarchy and route support", "version": 4, "mbox": "http://patchwork.ozlabs.org/series/497963/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2217545/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2217545/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "\n <linux-aspeed+bounces-3799-incoming=patchwork.ozlabs.org@lists.ozlabs.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-aspeed@lists.ozlabs.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=lists.ozlabs.org\n (client-ip=2404:9400:21b9:f100::1; helo=lists.ozlabs.org;\n envelope-from=linux-aspeed+bounces-3799-incoming=patchwork.ozlabs.org@lists.ozlabs.org;\n receiver=patchwork.ozlabs.org)", "lists.ozlabs.org;\n arc=none smtp.remote-ip=211.20.114.72", "lists.ozlabs.org;\n dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com", "lists.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=aspeedtech.com\n (client-ip=211.20.114.72; helo=twmbx01.aspeed.com;\n envelope-from=ryan_chen@aspeedtech.com; receiver=lists.ozlabs.org)" ], "Received": [ "from lists.ozlabs.org (lists.ozlabs.org\n [IPv6:2404:9400:21b9:f100::1])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1 raw public key)\n server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fkhJ12L7qz1yGH\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 30 Mar 2026 17:32:33 +1100 (AEDT)", "from boromir.ozlabs.org (localhost [127.0.0.1])\n\tby lists.ozlabs.org (Postfix) with ESMTP id 4fkhJ10BXmz2yfl;\n\tMon, 30 Mar 2026 17:32:33 +1100 (AEDT)", "from TWMBX01.aspeed.com (mail.aspeedtech.com [211.20.114.72])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby lists.ozlabs.org (Postfix) with ESMTPS id 4fkhHz0ln7z2xT6\n\tfor <linux-aspeed@lists.ozlabs.org>; Mon, 30 Mar 2026 17:32:31 +1100 (AEDT)", "from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com\n (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Mon, 30 Mar\n 2026 14:32:11 +0800", "from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com\n (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend\n Transport; Mon, 30 Mar 2026 14:32:11 +0800" ], "ARC-Seal": "i=1; a=rsa-sha256; d=lists.ozlabs.org; s=201707; t=1774852352;\n\tcv=none;\n b=gF13+PTwRL92bIkq/edRdW0GJakYg3f3Br4B2X2ZbhaKlDf54+YkCmOUFSd6XRUrVjUP2AYIPbgIGXM3gvysFzFTUrZObRcY4bxG1sBc43i0b/+HbVhIN1wRSfn3EmpkSr+9EGZQ3CewQpYvwj1S2Tey4qHa5nwV4wQ8T8o1/ePsS+emUyRwEmo+O0rKyO6NtxFVI14Flu0MV7S0icdU+EFjde7zsCTTEEcY/4R7WJrqTFB3Z4QTv+YboTdUl3FakYkqa1npUd1miNAqgK+uJJ2kIrCUz58Y8qFs7t+5bfsNZeOAjyxDWoTeUjPqZrTvtblPs1I+CUnLlq27RWt3XQ==", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=lists.ozlabs.org; s=201707;\n\tt=1774852352; c=relaxed/relaxed;\n\tbh=Zam0YgTGG5oL17OH82tXcc4i3mucDsyz06TDj1JGpfU=;\n\th=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References:\n\t In-Reply-To:To:CC;\n b=e+2HUXywliYxk9vf5qiUgfUILxDcESq3cPHukKQoqMBCuB/46ZlLhAaVgdsJ6Vu59LYEtAD/ot+GeqAX02I0yUTULIivO3FyiLin8PGwWQsNaHVpzRc4thR4J0Opy0S/BNQ18iHDii8SGLs1zzT1oouEr5tD0foTJhLE2sKEV1r3Xj8smhzzYa98HFxEFW4qxzbgI+pET3ntNX3crV1uNlT7rOEELMVw9k4tdUh6P1oFo+7AilxaBszRC1JRxmuPlfHhB1apaILfN9oCO68Iegw8uQAamOJwHGl1aNLUAvYbbx81ayp4E//b8dbsbtq85i0A5aQwi/VR52afKjtTYw==", "ARC-Authentication-Results": "i=1; lists.ozlabs.org;\n dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com;\n spf=pass (client-ip=211.20.114.72; helo=twmbx01.aspeed.com;\n envelope-from=ryan_chen@aspeedtech.com;\n receiver=lists.ozlabs.org) smtp.mailfrom=aspeedtech.com", "From": "Ryan Chen <ryan_chen@aspeedtech.com>", "Date": "Mon, 30 Mar 2026 14:32:11 +0800", "Subject": "[PATCH v4 2/4] irqchip/ast2700-intc: Add AST2700-A2 support", "X-Mailing-List": "linux-aspeed@lists.ozlabs.org", "List-Id": "<linux-aspeed.lists.ozlabs.org>", "List-Help": "<mailto:linux-aspeed+help@lists.ozlabs.org>", "List-Owner": "<mailto:linux-aspeed+owner@lists.ozlabs.org>", "List-Post": "<mailto:linux-aspeed@lists.ozlabs.org>", "List-Archive": "<https://lore.kernel.org/linux-aspeed/>,\n <https://lists.ozlabs.org/pipermail/linux-aspeed/>", "List-Subscribe": "<mailto:linux-aspeed+subscribe@lists.ozlabs.org>,\n <mailto:linux-aspeed+subscribe-digest@lists.ozlabs.org>,\n <mailto:linux-aspeed+subscribe-nomail@lists.ozlabs.org>", "List-Unsubscribe": "<mailto:linux-aspeed+unsubscribe@lists.ozlabs.org>", "Precedence": "list", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"utf-8\"", "Content-Transfer-Encoding": "7bit", "Message-ID": "<20260330-irqchip-v4-2-3c0f1620cc06@aspeedtech.com>", "References": "<20260330-irqchip-v4-0-3c0f1620cc06@aspeedtech.com>", "In-Reply-To": "<20260330-irqchip-v4-0-3c0f1620cc06@aspeedtech.com>", "To": "Rob Herring <robh@kernel.org>, Krzysztof Kozlowski <krzk+dt@kernel.org>,\n\tConor Dooley <conor+dt@kernel.org>, Joel Stanley <joel@jms.id.au>, \"Andrew\n Jeffery\" <andrew@codeconstruct.com.au>, Paul Walmsley <pjw@kernel.org>,\n\t\"Palmer Dabbelt\" <palmer@dabbelt.com>, Albert Ou <aou@eecs.berkeley.edu>,\n\t\"Alexandre Ghiti\" <alex@ghiti.fr>, Thomas Gleixner <tglx@kernel.org>, Thomas\n Gleixner <tglx@kernel.org>", "CC": "<linux-kernel@vger.kernel.org>, <devicetree@vger.kernel.org>,\n\t<linux-arm-kernel@lists.infradead.org>, <linux-aspeed@lists.ozlabs.org>,\n\t<linux-riscv@lists.infradead.org>, Ryan Chen <ryan_chen@aspeedtech.com>", "X-Mailer": "b4 0.14.3", "X-Developer-Signature": "v=1; a=ed25519-sha256; t=1774852330; l=34895;\n i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id;\n bh=i6Tc0HPlSQ37VLfqIrBKNmzPPeiMl9bG5qF5c4NSnmA=;\n b=D/K3MwJiDxmKpkBuXuudbApu7akT8dOj8zPrPxnC+C+kPIznqcwjhKZnvXScvRut7JIh187Qw\n lR+dyeySJgkAQzVHGMyY2/BHTvcYbrl7zeiaIEQ8JN/fGjj6HPDUdHR", "X-Developer-Key": "i=ryan_chen@aspeedtech.com; a=ed25519;\n pk=Xe73xY6tcnkuRjjbVAB/oU30KdB3FvG4nuJuILj7ZVc=", "X-Spam-Status": "No, score=0.0 required=5.0 tests=SPF_HELO_FAIL,SPF_PASS\n\tautolearn=disabled version=4.0.1", "X-Spam-Checker-Version": "SpamAssassin 4.0.1 (2024-03-25) on lists.ozlabs.org" }, "content": "The AST2700 interrupt fabric is shared by multiple integrated processors\n(PSP/SSP/TSP/BootMCU), each with its own interrupt controller and its own\ndevicetree view of the system. As a result, interrupt routing cannot be\ntreated as fixed: the valid route for a peripheral interrupt depends on\nwhich processor is consuming it.\n\nThe INTC0 driver models this by creating a hierarchical irqdomain under\nthe upstream interrupt controller selected by the interrupt-parent\nproperty in the devicetree. Information derived from this relationship\nis incorporated into the route resolution logic for the controller.\n\nThe INTC1 driver implements the banked INTM-fed controller and forwards\ninterrupts toward INTC0, without embedding assumptions about the final\ndestination processor.\n\nSigned-off-by: Ryan Chen <ryan_chen@aspeedtech.com>\n---\nChanges in v2:\n- remove typedef u32 aspeed_intc_output_t\n- modify #include <asm-generic/errno.h> to <linux/err.h>\n- add newline after include \"irq-ast2700.h\"\n- make defines tabular\n- Struct declarations should align the struct member names in a table\n- modify raw_spinlock_irqsave() to raw_spin_lock()\n- use u32 ier replace mask/unmask\n- remove pointless line break\n- refine aspeed_intc0_routes, aspeed_intc1_routes array\n- remove range_contains_element(), use in_range32()\n- remove dev_dbg()\n- remove EXPORT_SYMBOL_GPL(aspeed_intc0_resolve_route);\n- make irq_set_chip_and_handler() with one line\n- replace magic constants to macro define\n- move struct aspeed_intc0 to irq-ast2700.h\n- add mcro define for upstream param\n---\n drivers/irqchip/Kconfig | 12 +\n drivers/irqchip/Makefile | 1 +\n drivers/irqchip/irq-ast2700-intc0.c | 584 ++++++++++++++++++++++++++++++++++++\n drivers/irqchip/irq-ast2700-intc1.c | 282 +++++++++++++++++\n drivers/irqchip/irq-ast2700.c | 106 +++++++\n drivers/irqchip/irq-ast2700.h | 47 +++\n 6 files changed, 1032 insertions(+)", "diff": "diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig\nindex f07b00d7fef9..0156fee89b2c 100644\n--- a/drivers/irqchip/Kconfig\n+++ b/drivers/irqchip/Kconfig\n@@ -110,6 +110,18 @@ config AL_FIC\n \thelp\n \t Support Amazon's Annapurna Labs Fabric Interrupt Controller.\n \n+config ASPEED_AST2700_INTC\n+\tbool \"ASPEED AST2700 Interrupt Controller support\"\n+\tdepends on OF\n+\tdepends on ARCH_ASPEED || COMPILE_TEST\n+\tselect IRQ_DOMAIN_HIERARCHY\n+\thelp\n+\t Enable support for the ASPEED AST2700 interrupt controller.\n+\t This driver handles interrupt, routing and merged interrupt\n+\t sources to upstream parent interrupt controllers.\n+\n+\t If unsure, say N.\n+\n config ATMEL_AIC_IRQ\n \tbool\n \tselect GENERIC_IRQ_CHIP\ndiff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile\nindex 26aa3b6ec99f..62790663f982 100644\n--- a/drivers/irqchip/Makefile\n+++ b/drivers/irqchip/Makefile\n@@ -89,6 +89,7 @@ obj-$(CONFIG_MVEBU_PIC)\t\t\t+= irq-mvebu-pic.o\n obj-$(CONFIG_MVEBU_SEI)\t\t\t+= irq-mvebu-sei.o\n obj-$(CONFIG_LS_EXTIRQ)\t\t\t+= irq-ls-extirq.o\n obj-$(CONFIG_LS_SCFG_MSI)\t\t+= irq-ls-scfg-msi.o\n+obj-$(CONFIG_ASPEED_AST2700_INTC)\t+= irq-ast2700.o irq-ast2700-intc0.o irq-ast2700-intc1.o\n obj-$(CONFIG_ARCH_ASPEED)\t\t+= irq-aspeed-vic.o irq-aspeed-i2c-ic.o irq-aspeed-scu-ic.o\n obj-$(CONFIG_ARCH_ASPEED)\t\t+= irq-aspeed-intc.o\n obj-$(CONFIG_STM32MP_EXTI)\t\t+= irq-stm32mp-exti.o\ndiff --git a/drivers/irqchip/irq-ast2700-intc0.c b/drivers/irqchip/irq-ast2700-intc0.c\nnew file mode 100644\nindex 000000000000..66e2fb108281\n--- /dev/null\n+++ b/drivers/irqchip/irq-ast2700-intc0.c\n@@ -0,0 +1,584 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/*\n+ * Aspeed AST2700 Interrupt Controller.\n+ *\n+ * Copyright (C) 2026 ASPEED Technology Inc.\n+ */\n+\n+#include <linux/bitops.h>\n+#include <linux/device.h>\n+#include <linux/err.h>\n+#include <linux/fwnode.h>\n+#include <linux/io.h>\n+#include <linux/irq.h>\n+#include <linux/irqchip.h>\n+#include <linux/irqchip/chained_irq.h>\n+#include <linux/irqdomain.h>\n+#include <linux/kconfig.h>\n+#include <linux/of.h>\n+#include <linux/of_irq.h>\n+#include <linux/overflow.h>\n+#include <linux/property.h>\n+#include <linux/spinlock.h>\n+\n+#include \"irq-ast2700.h\"\n+\n+#define INT_NUM\t\t480\n+#define INTM_NUM\t50\n+#define SWINT_NUM\t16\n+\n+#define INTM_BASE\t(INT_NUM)\n+#define SWINT_BASE\t(INT_NUM + INTM_NUM)\n+#define INT0_NUM\t(INT_NUM + INTM_NUM + SWINT_NUM)\n+\n+#define INTC0_IN_NUM\t\t480\n+#define INTC0_ROUTE_NUM\t\t5\n+#define INTC0_INTM_NUM\t\t50\n+#define INTC0_ROUTE_BITS\t3\n+\n+#define GIC_P2P_SPI_END\t\t128\n+#define INTC0_SWINT_OUT_BASE\t144\n+\n+#define INTC0_SWINT_IER\t\t0x10\n+#define INTC0_SWINT_ISR\t\t0x14\n+#define INTC0_INTBANKX_IER\t0x1000\n+#define INTC0_INTBANK_SIZE\t0x100\n+#define INTC0_INTBANK_GROUPS\t11\n+#define INTC0_INTBANKS_PER_GRP\t3\n+#define INTC0_INTMX_IER\t\t0x1b00\n+#define INTC0_INTMX_ISR\t\t0x1b04\n+#define INTC0_INTMX_BANK_SIZE\t0x10\n+#define INTC0_INTM_BANK_NUM\t3\n+#define INTC0_IRQS_PER_BANK\t32\n+#define INTM_IRQS_PER_BANK\t10\n+#define INTC0_SEL_BASE\t\t\t0x200\n+#define INTC0_SEL_BANK_SIZE\t\t0x4\n+#define INTC0_SEL_ROUTE_SIZE\t0x100\n+\n+static void aspeed_swint_irq_mask(struct irq_data *data)\n+{\n+\tstruct aspeed_intc0 *intc0 = irq_data_get_irq_chip_data(data);\n+\tint bit = data->hwirq - SWINT_BASE;\n+\tu32 ier;\n+\n+\tguard(raw_spinlock)(&intc0->intc_lock);\n+\tier = readl(intc0->base + INTC0_SWINT_IER) & ~BIT(bit);\n+\twritel(ier, intc0->base + INTC0_SWINT_IER);\n+\tirq_chip_mask_parent(data);\n+}\n+\n+static void aspeed_swint_irq_unmask(struct irq_data *data)\n+{\n+\tstruct aspeed_intc0 *intc0 = irq_data_get_irq_chip_data(data);\n+\tint bit = data->hwirq - SWINT_BASE;\n+\tu32 ier;\n+\n+\tguard(raw_spinlock)(&intc0->intc_lock);\n+\tier = readl(intc0->base + INTC0_SWINT_IER) | BIT(bit);\n+\twritel(ier, intc0->base + INTC0_SWINT_IER);\n+\tirq_chip_unmask_parent(data);\n+}\n+\n+static void aspeed_swint_irq_eoi(struct irq_data *data)\n+{\n+\tstruct aspeed_intc0 *intc0 = irq_data_get_irq_chip_data(data);\n+\tint bit = data->hwirq - SWINT_BASE;\n+\n+\twritel(BIT(bit), intc0->base + INTC0_SWINT_ISR);\n+\tirq_chip_eoi_parent(data);\n+}\n+\n+static struct irq_chip aspeed_swint_chip = {\n+\t.name\t\t\t= \"ast2700-swint\",\n+\t.irq_eoi\t\t= aspeed_swint_irq_eoi,\n+\t.irq_mask\t\t= aspeed_swint_irq_mask,\n+\t.irq_unmask\t\t= aspeed_swint_irq_unmask,\n+\t.irq_set_affinity\t= irq_chip_set_affinity_parent,\n+\t.flags\t\t\t= IRQCHIP_SET_TYPE_MASKED,\n+};\n+\n+static void aspeed_intc0_irq_mask(struct irq_data *data)\n+{\n+\tstruct aspeed_intc0 *intc0 = irq_data_get_irq_chip_data(data);\n+\tint bank = (data->hwirq - INTM_BASE) / INTM_IRQS_PER_BANK;\n+\tint bit = (data->hwirq - INTM_BASE) % INTM_IRQS_PER_BANK;\n+\tu32 ier;\n+\n+\tguard(raw_spinlock)(&intc0->intc_lock);\n+\tier = readl(intc0->base + INTC0_INTMX_IER + bank * INTC0_INTMX_BANK_SIZE) & ~BIT(bit);\n+\twritel(ier, intc0->base + INTC0_INTMX_IER + bank * INTC0_INTMX_BANK_SIZE);\n+\tirq_chip_mask_parent(data);\n+}\n+\n+static void aspeed_intc0_irq_unmask(struct irq_data *data)\n+{\n+\tstruct aspeed_intc0 *intc0 = irq_data_get_irq_chip_data(data);\n+\tint bank = (data->hwirq - INTM_BASE) / INTM_IRQS_PER_BANK;\n+\tint bit = (data->hwirq - INTM_BASE) % INTM_IRQS_PER_BANK;\n+\tu32 ier;\n+\n+\tguard(raw_spinlock)(&intc0->intc_lock);\n+\tier = readl(intc0->base + INTC0_INTMX_IER + bank * INTC0_INTMX_BANK_SIZE) | BIT(bit);\n+\twritel(ier, intc0->base + INTC0_INTMX_IER + bank * INTC0_INTMX_BANK_SIZE);\n+\tirq_chip_unmask_parent(data);\n+}\n+\n+static void aspeed_intc0_irq_eoi(struct irq_data *data)\n+{\n+\tstruct aspeed_intc0 *intc0 = irq_data_get_irq_chip_data(data);\n+\tint bank = (data->hwirq - INTM_BASE) / INTM_IRQS_PER_BANK;\n+\tint bit = (data->hwirq - INTM_BASE) % INTM_IRQS_PER_BANK;\n+\n+\twritel(BIT(bit), intc0->base + INTC0_INTMX_ISR + bank * INTC0_INTMX_BANK_SIZE);\n+\tirq_chip_eoi_parent(data);\n+}\n+\n+static struct irq_chip aspeed_intm_chip = {\n+\t.name\t\t\t= \"ast2700-intmerge\",\n+\t.irq_eoi\t\t= aspeed_intc0_irq_eoi,\n+\t.irq_mask\t\t= aspeed_intc0_irq_mask,\n+\t.irq_unmask\t\t= aspeed_intc0_irq_unmask,\n+\t.irq_set_affinity\t= irq_chip_set_affinity_parent,\n+\t.flags\t\t\t= IRQCHIP_SET_TYPE_MASKED,\n+};\n+\n+static struct irq_chip linear_intr_irq_chip = {\n+\t.name\t\t\t= \"ast2700-int\",\n+\t.irq_eoi\t\t= irq_chip_eoi_parent,\n+\t.irq_mask\t\t= irq_chip_mask_parent,\n+\t.irq_unmask\t\t= irq_chip_unmask_parent,\n+\t.irq_set_affinity\t= irq_chip_set_affinity_parent,\n+\t.flags\t\t\t= IRQCHIP_SET_TYPE_MASKED,\n+};\n+\n+static const u32 aspeed_intc0_routes[INTC0_IN_NUM / INTC0_IRQS_PER_BANK][INTC0_ROUTE_NUM] = {\n+\t{ 0, 256, 426, AST2700_INTC_INVALID_ROUTE, AST2700_INTC_INVALID_ROUTE },\n+\t{ 32, 288, 458, AST2700_INTC_INVALID_ROUTE, AST2700_INTC_INVALID_ROUTE },\n+\t{ 64, 320, 490, AST2700_INTC_INVALID_ROUTE, AST2700_INTC_INVALID_ROUTE },\n+\t{ 96, 352, 522, AST2700_INTC_INVALID_ROUTE, AST2700_INTC_INVALID_ROUTE },\n+\t{ 128, 384, 554, 160, 176 },\n+\t{ 129, 385, 555, 161, 177 },\n+\t{ 130, 386, 556, 162, 178 },\n+\t{ 131, 387, 557, 163, 179 },\n+\t{ 132, 388, 558, 164, 180 },\n+\t{ 133, 544, 714, 165, 181 },\n+\t{ 134, 545, 715, 166, 182 },\n+\t{ 135, 546, 706, 167, 183 },\n+\t{ 136, 547, 707, 168, 184 },\n+\t{ 137, 548, 708, 169, 185 },\n+\t{ 138, 549, 709, 170, 186 },\n+};\n+\n+static const u32 aspeed_intc0_intm_routes[INTC0_INTM_NUM / INTM_IRQS_PER_BANK] = {\n+\t192, 416, 586, 208, 224\n+};\n+\n+static int resolve_input_from_child_ranges(const struct aspeed_intc0 *intc0,\n+\t\t\t\t\t const struct aspeed_intc_interrupt_range *range,\n+\t\t\t\t\t u32 outpin, u32 *input)\n+{\n+\tu32 offset, base;\n+\n+\tif (!in_range32(outpin, range->start, range->count))\n+\t\treturn -ENOENT;\n+\n+\tif (range->upstream.param_count == 0)\n+\t\treturn -EINVAL;\n+\n+\tbase = range->upstream.param[ASPEED_INTC_RANGES_BASE];\n+\toffset = outpin - range->start;\n+\tif (check_add_overflow(base, offset, input)) {\n+\t\tdev_warn(intc0->dev, \"%s: Arithmetic overflow for input derivation: %u + %u\\n\",\n+\t\t\t __func__, base, offset);\n+\t\treturn -EINVAL;\n+\t}\n+\treturn 0;\n+}\n+\n+static int resolve_parent_range_for_output(const struct aspeed_intc0 *intc0,\n+\t\t\t\t\t const struct fwnode_handle *parent,\n+\t\t\t\t\t u32 output,\n+\t\t\t\t\t struct aspeed_intc_interrupt_range *resolved)\n+{\n+\tfor (size_t i = 0; i < intc0->ranges.nranges; i++) {\n+\t\tstruct aspeed_intc_interrupt_range range =\n+\t\t\tintc0->ranges.ranges[i];\n+\n+\t\tif (!in_range32(output, range.start, range.count))\n+\t\t\tcontinue;\n+\n+\t\tif (range.upstream.fwnode != parent)\n+\t\t\tcontinue;\n+\n+\t\tif (resolved) {\n+\t\t\tresolved->start = output;\n+\t\t\tresolved->count = 1;\n+\t\t\tresolved->upstream = range.upstream;\n+\t\t\tresolved->upstream.param[ASPEED_INTC_RANGES_COUNT] +=\n+\t\t\t\toutput - range.start;\n+\t\t}\n+\n+\t\treturn 0;\n+\t}\n+\n+\treturn -ENOENT;\n+}\n+\n+static int resolve_parent_route_for_input(const struct aspeed_intc0 *intc0,\n+\t\t\t\t\t const struct fwnode_handle *parent, u32 input,\n+\t\t\t\t\t struct aspeed_intc_interrupt_range *resolved)\n+{\n+\tint rc = -ENOENT;\n+\tu32 c0o;\n+\n+\tif (input < INT_NUM) {\n+\t\tstatic_assert(INTC0_ROUTE_NUM < INT_MAX, \"Broken cast\");\n+\t\tfor (size_t i = 0; rc == -ENOENT && i < INTC0_ROUTE_NUM; i++) {\n+\t\t\tc0o = aspeed_intc0_routes[input / INTC0_IRQS_PER_BANK][i];\n+\t\t\tif (c0o == AST2700_INTC_INVALID_ROUTE)\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (input < GIC_P2P_SPI_END)\n+\t\t\t\tc0o += input % INTC0_IRQS_PER_BANK;\n+\n+\t\t\trc = resolve_parent_range_for_output(intc0, parent, c0o, resolved);\n+\t\t\tif (!rc)\n+\t\t\t\treturn (int)i;\n+\t\t}\n+\t} else if (input < (INT_NUM + INTM_NUM)) {\n+\t\tc0o = aspeed_intc0_intm_routes[(input - INT_NUM) / INTM_IRQS_PER_BANK];\n+\t\tc0o += ((input - INT_NUM) % INTM_IRQS_PER_BANK);\n+\t\treturn resolve_parent_range_for_output(intc0, parent, c0o, resolved);\n+\t} else if (input < (INT_NUM + INTM_NUM + SWINT_NUM)) {\n+\t\tc0o = input - SWINT_BASE + INTC0_SWINT_OUT_BASE;\n+\t\treturn resolve_parent_range_for_output(intc0, parent, c0o, resolved);\n+\t} else {\n+\t\treturn -ENOENT;\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/**\n+ * aspeed_intc0_resolve_route - Determine the necessary interrupt output at intc1\n+ * @c0domain: The pointer to intc0's irq_domain\n+ * @nc1outs: The number of valid intc1 outputs available for the input\n+ * @c1outs: The array of available intc1 output indices for the input\n+ * @nc1ranges: The number of interrupt range entries for intc1\n+ * @c1ranges: The array of configured intc1 interrupt ranges\n+ * @resolved: The fully resolved range entry after applying the resolution\n+ * algorithm\n+ *\n+ * Returns: The intc1 route index associated with the intc1 output identified in\n+ * @resolved on success. Otherwise, a negative errno value.\n+ *\n+ * The AST2700 interrupt architecture allows any peripheral interrupt source\n+ * to be routed to one of up to four processors running in the SoC. A processor\n+ * binding a driver for a peripheral that requests an interrupt is (without\n+ * further design and effort) the destination for the requested interrupt.\n+ *\n+ * Routing a peripheral interrupt to its destination processor requires\n+ * coordination between INTC0 on the CPU die and one or more INTC1 instances.\n+ * At least one INTC1 instance exists in the SoC on the IO-die, however up\n+ * to two more instances may be integrated via LTPI (LVDS Tunneling Protocol\n+ * & Interface).\n+ *\n+ * Between the multiple destinations, various route constraints, and the\n+ * devicetree binding design, some information that's needed at INTC1 instances\n+ * to route inbound interrupts correctly to the destination processor is only\n+ * available at INTC0.\n+ *\n+ * aspeed_intc0_resolve_route() is to be invoked by INTC1 driver instances to\n+ * perform the route resolution. The implementation in INTC0 allows INTC0 to\n+ * encapsulate the information used to perform route selection, and provides it\n+ * with an opportunity to apply policy as part of the selection process. Such\n+ * policy may, for instance, choose to de-prioritise some interrupts destined\n+ * for the PSP (Primary Service Processor) GIC.\n+ */\n+int aspeed_intc0_resolve_route(const struct irq_domain *c0domain, size_t nc1outs,\n+\t\t\t const u32 *c1outs, size_t nc1ranges,\n+\t\t\t const struct aspeed_intc_interrupt_range *c1ranges,\n+\t\t\t struct aspeed_intc_interrupt_range *resolved)\n+{\n+\tstruct fwnode_handle *parent_fwnode;\n+\tstruct aspeed_intc0 *intc0;\n+\tint ret;\n+\n+\tif (!c0domain || !resolved)\n+\t\treturn -EINVAL;\n+\n+\tif (nc1outs > INT_MAX)\n+\t\treturn -EINVAL;\n+\n+\tif (nc1outs == 0 || nc1ranges == 0)\n+\t\treturn -ENOENT;\n+\n+\tif (!fwnode_device_is_compatible(c0domain->fwnode, \"aspeed,ast2700-intc0\"))\n+\t\treturn -ENODEV;\n+\n+\tintc0 = c0domain->host_data;\n+\tif (!intc0)\n+\t\treturn -EINVAL;\n+\n+\tparent_fwnode = of_fwnode_handle(intc0->parent);\n+\n+\tfor (size_t i = 0; i < nc1outs; i++) {\n+\t\tu32 c1o = c1outs[i];\n+\n+\t\tif (c1o == AST2700_INTC_INVALID_ROUTE)\n+\t\t\tcontinue;\n+\n+\t\tfor (size_t j = 0; j < nc1ranges; j++) {\n+\t\t\tstruct aspeed_intc_interrupt_range c1r = c1ranges[j];\n+\t\t\tu32 input;\n+\n+\t\t\t/*\n+\t\t\t * Range match for intc1 output pin\n+\t\t\t *\n+\t\t\t * Assume a failed match is still a match for the purpose of testing,\n+\t\t\t * saves a bunch of mess in the test fixtures\n+\t\t\t */\n+\t\t\tif (!(c0domain == irq_find_matching_fwspec(&c1r.upstream,\n+\t\t\t\t\t\t\t\t c0domain->bus_token) ||\n+\t\t\t IS_ENABLED(CONFIG_ASPEED_AST2700_INTC_TEST)))\n+\t\t\t\tcontinue;\n+\n+\t\t\tret = resolve_input_from_child_ranges(intc0, &c1r, c1o, &input);\n+\t\t\tif (ret)\n+\t\t\t\tcontinue;\n+\n+\t\t\t/*\n+\t\t\t * INTC1 should never request routes for peripheral interrupt sources\n+\t\t\t * directly attached to INTC0.\n+\t\t\t */\n+\t\t\tif (input < GIC_P2P_SPI_END)\n+\t\t\t\tcontinue;\n+\n+\t\t\tret = resolve_parent_route_for_input(intc0, parent_fwnode, input, NULL);\n+\t\t\tif (ret < 0)\n+\t\t\t\tcontinue;\n+\n+\t\t\t/* Route resolution succeeded */\n+\t\t\tresolved->start = c1o;\n+\t\t\tresolved->count = 1;\n+\t\t\tresolved->upstream = c1r.upstream;\n+\t\t\tresolved->upstream.param[ASPEED_INTC_RANGES_BASE] = input;\n+\t\t\t/* Cast protected by prior test against nc1outs */\n+\t\t\treturn (int)i;\n+\t\t}\n+\t}\n+\n+\treturn -ENOENT;\n+}\n+\n+static int aspeed_intc0_irq_domain_map(struct irq_domain *domain,\n+\t\t\t\t unsigned int irq, irq_hw_number_t hwirq)\n+{\n+\tif (hwirq < GIC_P2P_SPI_END)\n+\t\tirq_set_chip_and_handler(irq, &linear_intr_irq_chip, handle_level_irq);\n+\telse if (hwirq < INTM_BASE)\n+\t\treturn -EINVAL;\n+\telse if (hwirq < SWINT_BASE)\n+\t\tirq_set_chip_and_handler(irq, &aspeed_intm_chip, handle_level_irq);\n+\telse if (hwirq < INT0_NUM)\n+\t\tirq_set_chip_and_handler(irq, &aspeed_swint_chip, handle_level_irq);\n+\telse\n+\t\treturn -EINVAL;\n+\n+\tirq_set_chip_data(irq, domain->host_data);\n+\treturn 0;\n+}\n+\n+static int aspeed_intc0_irq_domain_translate(struct irq_domain *domain,\n+\t\t\t\t\t struct irq_fwspec *fwspec,\n+\t\t\t\t\t unsigned long *hwirq,\n+\t\t\t\t\t unsigned int *type)\n+{\n+\tif (fwspec->param_count != 1)\n+\t\treturn -EINVAL;\n+\n+\t*hwirq = fwspec->param[0];\n+\t*type = IRQ_TYPE_NONE;\n+\treturn 0;\n+}\n+\n+static int aspeed_intc0_irq_domain_alloc(struct irq_domain *domain,\n+\t\t\t\t\t unsigned int virq,\n+\t\t\t\t\t unsigned int nr_irqs, void *data)\n+{\n+\tstruct aspeed_intc0 *intc0 = domain->host_data;\n+\tstruct aspeed_intc_interrupt_range resolved;\n+\tstruct irq_fwspec *fwspec = data;\n+\tstruct irq_fwspec parent_fwspec;\n+\tstruct irq_chip *chip;\n+\tunsigned long hwirq;\n+\tunsigned int type;\n+\tint ret;\n+\n+\tret = aspeed_intc0_irq_domain_translate(domain, fwspec, &hwirq, &type);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (hwirq >= GIC_P2P_SPI_END && hwirq < INT_NUM)\n+\t\treturn -EINVAL;\n+\n+\tif (hwirq < INTM_BASE)\n+\t\tchip = &linear_intr_irq_chip;\n+\telse if (hwirq < SWINT_BASE)\n+\t\tchip = &aspeed_intm_chip;\n+\telse\n+\t\tchip = &aspeed_swint_chip;\n+\n+\tret = resolve_parent_route_for_input(intc0, domain->parent->fwnode,\n+\t\t\t\t\t (u32)hwirq, &resolved);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tparent_fwspec = resolved.upstream;\n+\tret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,\n+\t\t\t\t\t &parent_fwspec);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor (int i = 0; i < nr_irqs; ++i, ++hwirq, ++virq) {\n+\t\tret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip,\n+\t\t\t\t\t\t domain->host_data);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int aspeed_intc0_irq_domain_activate(struct irq_domain *domain,\n+\t\t\t\t\t struct irq_data *data, bool reserve)\n+{\n+\tstruct aspeed_intc0 *intc0 = irq_data_get_irq_chip_data(data);\n+\tunsigned long hwirq = data->hwirq;\n+\tint route, bank, bit;\n+\tu32 mask;\n+\n+\tif (hwirq >= INT0_NUM)\n+\t\treturn -EINVAL;\n+\n+\tif (in_range32(hwirq, INTM_BASE, INTM_NUM + SWINT_NUM))\n+\t\treturn 0;\n+\n+\tbank = hwirq / INTC0_IRQS_PER_BANK;\n+\tbit = hwirq % INTC0_IRQS_PER_BANK;\n+\tmask = BIT(bit);\n+\n+\troute = resolve_parent_route_for_input(intc0, intc0->local->parent->fwnode,\n+\t\t\t\t\t hwirq, NULL);\n+\tif (route < 0)\n+\t\treturn route;\n+\n+\tguard(raw_spinlock)(&intc0->intc_lock);\n+\tfor (int i = 0; i < INTC0_ROUTE_BITS; i++) {\n+\t\tvoid __iomem *sel = intc0->base + INTC0_SEL_BASE +\n+\t\t\t\t (bank * INTC0_SEL_BANK_SIZE) +\n+\t\t\t\t (INTC0_SEL_ROUTE_SIZE * i);\n+\t\tu32 reg = readl(sel);\n+\n+\t\tif (route & BIT(i))\n+\t\t\treg |= mask;\n+\t\telse\n+\t\t\treg &= ~mask;\n+\n+\t\twritel(reg, sel);\n+\t\tif (readl(sel) != reg)\n+\t\t\treturn -EACCES;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static const struct irq_domain_ops aspeed_intc0_irq_domain_ops = {\n+\t.translate\t= aspeed_intc0_irq_domain_translate,\n+\t.activate\t= aspeed_intc0_irq_domain_activate,\n+\t.alloc\t\t= aspeed_intc0_irq_domain_alloc,\n+\t.free\t\t= irq_domain_free_irqs_common,\n+\t.map\t\t= aspeed_intc0_irq_domain_map,\n+};\n+\n+static void aspeed_intc0_disable_swint(struct aspeed_intc0 *intc0)\n+{\n+\twritel(0, intc0->base + INTC0_SWINT_IER);\n+}\n+\n+static void aspeed_intc0_disable_intbank(struct aspeed_intc0 *intc0)\n+{\n+\tfor (int i = 0; i < INTC0_INTBANK_GROUPS; i++) {\n+\t\tfor (int j = 0; j < INTC0_INTBANKS_PER_GRP; j++) {\n+\t\t\tu32 base = INTC0_INTBANKX_IER +\n+\t\t\t\t (INTC0_INTBANK_SIZE * i) +\n+\t\t\t\t (INTC0_INTMX_BANK_SIZE * j);\n+\n+\t\t\twritel(0, intc0->base + base);\n+\t\t}\n+\t}\n+}\n+\n+static void aspeed_intc0_disable_intm(struct aspeed_intc0 *intc0)\n+{\n+\tfor (int i = 0; i < INTC0_INTM_BANK_NUM; i++)\n+\t\twritel(0, intc0->base + INTC0_INTMX_IER + (INTC0_INTMX_BANK_SIZE * i));\n+}\n+\n+static int aspeed_intc0_probe(struct platform_device *pdev,\n+\t\t\t struct device_node *parent)\n+{\n+\tstruct device_node *node = pdev->dev.of_node;\n+\tstruct irq_domain *parent_domain;\n+\tstruct aspeed_intc0 *intc0;\n+\tint ret;\n+\n+\tif (!parent) {\n+\t\tpr_err(\"missing parent interrupt node\\n\");\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tintc0 = devm_kzalloc(&pdev->dev, sizeof(*intc0), GFP_KERNEL);\n+\tif (!intc0)\n+\t\treturn -ENOMEM;\n+\n+\tintc0->dev = &pdev->dev;\n+\tintc0->parent = parent;\n+\tintc0->base = devm_platform_ioremap_resource(pdev, 0);\n+\tif (IS_ERR(intc0->base))\n+\t\treturn PTR_ERR(intc0->base);\n+\n+\taspeed_intc0_disable_swint(intc0);\n+\taspeed_intc0_disable_intbank(intc0);\n+\taspeed_intc0_disable_intm(intc0);\n+\n+\traw_spin_lock_init(&intc0->intc_lock);\n+\n+\tparent_domain = irq_find_host(parent);\n+\tif (!parent_domain) {\n+\t\tpr_err(\"unable to obtain parent domain\\n\");\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tif (!of_device_is_compatible(parent, \"arm,gic-v3\"))\n+\t\treturn -ENODEV;\n+\n+\tintc0->local = irq_domain_create_hierarchy(parent_domain, 0, INT0_NUM,\n+\t\t\t\t\t\t of_fwnode_handle(node),\n+\t\t\t\t\t\t &aspeed_intc0_irq_domain_ops,\n+\t\t\t\t\t\t intc0);\n+\tif (!intc0->local)\n+\t\treturn -ENOMEM;\n+\n+\tret = aspeed_intc_populate_ranges(&pdev->dev, &intc0->ranges);\n+\tif (ret < 0) {\n+\t\tirq_domain_remove(intc0->local);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+IRQCHIP_PLATFORM_DRIVER_BEGIN(ast2700_intc0)\n+IRQCHIP_MATCH(\"aspeed,ast2700-intc0\", aspeed_intc0_probe)\n+IRQCHIP_PLATFORM_DRIVER_END(ast2700_intc0)\ndiff --git a/drivers/irqchip/irq-ast2700-intc1.c b/drivers/irqchip/irq-ast2700-intc1.c\nnew file mode 100644\nindex 000000000000..09b9d5d743e6\n--- /dev/null\n+++ b/drivers/irqchip/irq-ast2700-intc1.c\n@@ -0,0 +1,282 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/*\n+ * Aspeed AST2700 Interrupt Controller.\n+ *\n+ * Copyright (C) 2026 ASPEED Technology Inc.\n+ */\n+\n+#include <linux/bitops.h>\n+#include <linux/device.h>\n+#include <linux/io.h>\n+#include <linux/irq.h>\n+#include <linux/irqchip.h>\n+#include <linux/irqchip/chained_irq.h>\n+#include <linux/irqdomain.h>\n+#include <linux/of.h>\n+#include <linux/of_irq.h>\n+#include <linux/spinlock.h>\n+\n+#include \"irq-ast2700.h\"\n+\n+#define INTC1_IER\t\t\t0x100\n+#define INTC1_ISR\t\t\t0x104\n+#define INTC1_BANK_SIZE\t\t0x10\n+#define INTC1_SEL_BASE\t\t\t0x80\n+#define INTC1_SEL_BANK_SIZE\t\t0x4\n+#define INTC1_SEL_ROUTE_SIZE\t0x20\n+#define INTC1_IRQS_PER_BANK\t\t32\n+#define INTC1_BANK_NUM\t\t\t6\n+#define INTC1_ROUTE_NUM\t\t\t7\n+#define INTC1_IN_NUM\t\t\t192\n+#define INTC1_BOOTMCU_ROUTE\t\t6\n+#define INTC1_ROUTE_SELECTOR_BITS\t3\n+#define INTC1_ROUTE_IRQS_PER_GROUP\t32\n+#define INTC1_ROUTE_SHIFT\t\t5\n+\n+struct aspeed_intc1 {\n+\tstruct device\t\t\t\t*dev;\n+\tvoid __iomem\t\t\t\t*base;\n+\traw_spinlock_t\t\t\t\tintc_lock;\n+\tstruct irq_domain\t\t\t*local;\n+\tstruct irq_domain\t\t\t*upstream;\n+\tstruct aspeed_intc_interrupt_ranges\tranges;\n+};\n+\n+static void aspeed_intc1_disable_int(struct aspeed_intc1 *intc1)\n+{\n+\tfor (int i = 0; i < INTC1_BANK_NUM; i++)\n+\t\twritel(0, intc1->base + INTC1_IER + (INTC1_BANK_SIZE * i));\n+}\n+\n+static void aspeed_intc1_irq_handler(struct irq_desc *desc)\n+{\n+\tstruct aspeed_intc1 *intc1 = irq_desc_get_handler_data(desc);\n+\tstruct irq_chip *chip = irq_desc_get_chip(desc);\n+\tunsigned long bit, status;\n+\n+\tchained_irq_enter(chip, desc);\n+\n+\tfor (int bank = 0; bank < INTC1_BANK_NUM; bank++) {\n+\t\tstatus = readl(intc1->base + INTC1_ISR + (INTC1_BANK_SIZE * bank));\n+\t\tif (!status)\n+\t\t\tcontinue;\n+\n+\t\tfor_each_set_bit(bit, &status, INTC1_IRQS_PER_BANK) {\n+\t\t\tgeneric_handle_domain_irq(intc1->local, (bank * INTC1_IRQS_PER_BANK) + bit);\n+\t\t\twritel(BIT(bit), intc1->base + INTC1_ISR + (INTC1_BANK_SIZE * bank));\n+\t\t}\n+\t}\n+\n+\tchained_irq_exit(chip, desc);\n+}\n+\n+static void aspeed_intc1_irq_mask(struct irq_data *data)\n+{\n+\tstruct aspeed_intc1 *intc1 = irq_data_get_irq_chip_data(data);\n+\tint bank = data->hwirq / INTC1_IRQS_PER_BANK;\n+\tint bit = data->hwirq % INTC1_IRQS_PER_BANK;\n+\tu32 ier;\n+\n+\tguard(raw_spinlock)(&intc1->intc_lock);\n+\tier = readl(intc1->base + INTC1_IER + (INTC1_BANK_SIZE * bank)) & ~BIT(bit);\n+\twritel(ier, intc1->base + INTC1_IER + (INTC1_BANK_SIZE * bank));\n+}\n+\n+static void aspeed_intc1_irq_unmask(struct irq_data *data)\n+{\n+\tstruct aspeed_intc1 *intc1 = irq_data_get_irq_chip_data(data);\n+\tint bank = data->hwirq / INTC1_IRQS_PER_BANK;\n+\tint bit = data->hwirq % INTC1_IRQS_PER_BANK;\n+\tu32 ier;\n+\n+\tguard(raw_spinlock)(&intc1->intc_lock);\n+\tier = readl(intc1->base + INTC1_IER + (INTC1_BANK_SIZE * bank)) | BIT(bit);\n+\twritel(ier, intc1->base + INTC1_IER + (INTC1_BANK_SIZE * bank));\n+}\n+\n+static struct irq_chip aspeed_intc_chip = {\n+\t.name\t\t= \"ASPEED INTC1\",\n+\t.irq_mask\t= aspeed_intc1_irq_mask,\n+\t.irq_unmask\t= aspeed_intc1_irq_unmask,\n+};\n+\n+static int aspeed_intc1_irq_domain_translate(struct irq_domain *domain,\n+\t\t\t\t\t struct irq_fwspec *fwspec,\n+\t\t\t\t\t unsigned long *hwirq,\n+\t\t\t\t\t unsigned int *type)\n+{\n+\tif (fwspec->param_count != 1)\n+\t\treturn -EINVAL;\n+\n+\t*hwirq = fwspec->param[0];\n+\t*type = IRQ_TYPE_LEVEL_HIGH;\n+\treturn 0;\n+}\n+\n+static int aspeed_intc1_map_irq_domain(struct irq_domain *domain,\n+\t\t\t\t unsigned int irq,\n+\t\t\t\t irq_hw_number_t hwirq)\n+{\n+\tirq_domain_set_info(domain, irq, hwirq, &aspeed_intc_chip,\n+\t\t\t domain->host_data, handle_level_irq, NULL, NULL);\n+\treturn 0;\n+}\n+\n+/*\n+ * In-bound interrupts are progressively merged into one out-bound interrupt in\n+ * groups of 32. Apply this fact to compress the route table in corresponding\n+ * groups of 32.\n+ */\n+static const u32\n+aspeed_intc1_routes[INTC1_IN_NUM / INTC1_ROUTE_IRQS_PER_GROUP][INTC1_ROUTE_NUM] = {\n+\t{ 0, AST2700_INTC_INVALID_ROUTE, 10, 20, 30, 40, 50 },\n+\t{ 1, AST2700_INTC_INVALID_ROUTE, 11, 21, 31, 41, 50 },\n+\t{ 2, AST2700_INTC_INVALID_ROUTE, 12, 22, 32, 42, 50 },\n+\t{ 3, AST2700_INTC_INVALID_ROUTE, 13, 23, 33, 43, 50 },\n+\t{ 4, AST2700_INTC_INVALID_ROUTE, 14, 24, 34, 44, 50 },\n+\t{ 5, AST2700_INTC_INVALID_ROUTE, 15, 25, 35, 45, 50 },\n+};\n+\n+static int aspeed_intc1_irq_domain_activate(struct irq_domain *domain,\n+\t\t\t\t\t struct irq_data *data, bool reserve)\n+{\n+\tstruct aspeed_intc1 *intc1 = irq_data_get_irq_chip_data(data);\n+\tstruct aspeed_intc_interrupt_range resolved;\n+\tint rc, bank, bit;\n+\tu32 mask;\n+\n+\tif (WARN_ON_ONCE((data->hwirq >> INTC1_ROUTE_SHIFT) >= ARRAY_SIZE(aspeed_intc1_routes)))\n+\t\treturn -EINVAL;\n+\n+\t/*\n+\t * outpin may be an error if the upstream is the BootMCU APLIC node, or\n+\t * anything except a valid intc0 driver instance\n+\t */\n+\trc = aspeed_intc0_resolve_route(intc1->upstream, INTC1_ROUTE_NUM,\n+\t\t\t\t\taspeed_intc1_routes[data->hwirq >> INTC1_ROUTE_SHIFT],\n+\t\t\t\t\tintc1->ranges.nranges,\n+\t\t\t\t\tintc1->ranges.ranges, &resolved);\n+\tif (rc < 0) {\n+\t\tif (!fwnode_device_is_compatible(intc1->upstream->fwnode, \"riscv,aplic\")) {\n+\t\t\tdev_warn(intc1->dev,\n+\t\t\t\t \"Failed to resolve interrupt route for hwirq %lu in domain %s\\n\",\n+\t\t\t\t data->hwirq, domain->name);\n+\t\t\treturn rc;\n+\t\t}\n+\t\trc = INTC1_BOOTMCU_ROUTE;\n+\t}\n+\n+\tbank = data->hwirq / INTC1_IRQS_PER_BANK;\n+\tbit = data->hwirq % INTC1_IRQS_PER_BANK;\n+\tmask = BIT(bit);\n+\n+\tguard(raw_spinlock)(&intc1->intc_lock);\n+\tfor (int i = 0; i < INTC1_ROUTE_SELECTOR_BITS; i++) {\n+\t\tvoid __iomem *sel = intc1->base + INTC1_SEL_BASE +\n+\t\t\t\t (bank * INTC1_SEL_BANK_SIZE) +\n+\t\t\t\t (INTC1_SEL_ROUTE_SIZE * i);\n+\t\tu32 reg = readl(sel);\n+\n+\t\tif (rc & BIT(i))\n+\t\t\treg |= mask;\n+\t\telse\n+\t\t\treg &= ~mask;\n+\n+\t\twritel(reg, sel);\n+\t\tif (readl(sel) != reg)\n+\t\t\treturn -EACCES;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static const struct irq_domain_ops aspeed_intc1_irq_domain_ops = {\n+\t.map\t\t= aspeed_intc1_map_irq_domain,\n+\t.translate\t= aspeed_intc1_irq_domain_translate,\n+\t.activate\t= aspeed_intc1_irq_domain_activate,\n+};\n+\n+static void aspeed_intc1_request_interrupts(struct aspeed_intc1 *intc1)\n+{\n+\tfor (unsigned int i = 0; i < intc1->ranges.nranges; i++) {\n+\t\tstruct aspeed_intc_interrupt_range *r =\n+\t\t\t&intc1->ranges.ranges[i];\n+\n+\t\tif (intc1->upstream !=\n+\t\t irq_find_matching_fwspec(&r->upstream,\n+\t\t\t\t\t intc1->upstream->bus_token))\n+\t\t\tcontinue;\n+\n+\t\tfor (u32 k = 0; k < r->count; k++) {\n+\t\t\tstruct of_phandle_args parent_irq;\n+\t\t\tint irq;\n+\n+\t\t\tparent_irq.np = to_of_node(r->upstream.fwnode);\n+\t\t\tparent_irq.args_count = 1;\n+\t\t\tparent_irq.args[0] =\n+\t\t\t\tintc1->ranges.ranges[i].upstream.param[ASPEED_INTC_RANGES_BASE] + k;\n+\n+\t\t\tirq = irq_create_of_mapping(&parent_irq);\n+\t\t\tif (!irq)\n+\t\t\t\tcontinue;\n+\n+\t\t\tirq_set_chained_handler_and_data(irq,\n+\t\t\t\t\t\t\t aspeed_intc1_irq_handler, intc1);\n+\t\t}\n+\t}\n+}\n+\n+static int aspeed_intc1_probe(struct platform_device *pdev,\n+\t\t\t struct device_node *parent)\n+{\n+\tstruct device_node *node = pdev->dev.of_node;\n+\tstruct aspeed_intc1 *intc1;\n+\tstruct irq_domain *host;\n+\tint ret;\n+\n+\tif (!parent) {\n+\t\tdev_err(&pdev->dev, \"missing parent interrupt node\\n\");\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tif (!of_device_is_compatible(parent, \"aspeed,ast2700-intc0\"))\n+\t\treturn -ENODEV;\n+\n+\thost = irq_find_host(parent);\n+\tif (!host)\n+\t\treturn -ENODEV;\n+\n+\tintc1 = devm_kzalloc(&pdev->dev, sizeof(*intc1), GFP_KERNEL);\n+\tif (!intc1)\n+\t\treturn -ENOMEM;\n+\n+\tintc1->dev = &pdev->dev;\n+\tintc1->upstream = host;\n+\tintc1->base = devm_platform_ioremap_resource(pdev, 0);\n+\tif (IS_ERR(intc1->base))\n+\t\treturn PTR_ERR(intc1->base);\n+\n+\taspeed_intc1_disable_int(intc1);\n+\n+\traw_spin_lock_init(&intc1->intc_lock);\n+\n+\tintc1->local = irq_domain_create_linear(of_fwnode_handle(node),\n+\t\t\t\t\t\tINTC1_BANK_NUM * INTC1_IRQS_PER_BANK,\n+\t\t\t\t\t\t&aspeed_intc1_irq_domain_ops, intc1);\n+\tif (!intc1->local)\n+\t\treturn -ENOMEM;\n+\n+\tret = aspeed_intc_populate_ranges(&pdev->dev, &intc1->ranges);\n+\tif (ret < 0) {\n+\t\tirq_domain_remove(intc1->local);\n+\t\treturn ret;\n+\t}\n+\n+\taspeed_intc1_request_interrupts(intc1);\n+\n+\treturn 0;\n+}\n+\n+IRQCHIP_PLATFORM_DRIVER_BEGIN(ast2700_intc1)\n+IRQCHIP_MATCH(\"aspeed,ast2700-intc1\", aspeed_intc1_probe)\n+IRQCHIP_PLATFORM_DRIVER_END(ast2700_intc1)\ndiff --git a/drivers/irqchip/irq-ast2700.c b/drivers/irqchip/irq-ast2700.c\nnew file mode 100644\nindex 000000000000..280657480d5f\n--- /dev/null\n+++ b/drivers/irqchip/irq-ast2700.c\n@@ -0,0 +1,106 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/*\n+ * Aspeed AST2700 Interrupt Controller.\n+ *\n+ * Copyright (C) 2026 ASPEED Technology Inc.\n+ */\n+#include \"irq-ast2700.h\"\n+\n+#define ASPEED_INTC_RANGE_FIXED_CELLS\t3U\n+#define ASPEED_INTC_RANGE_OFF_START\t0U\n+#define ASPEED_INTC_RANGE_OFF_COUNT\t1U\n+#define ASPEED_INTC_RANGE_OFF_PHANDLE\t2U\n+\n+/**\n+ * aspeed_intc_populate_ranges\n+ * @dev: Device owning the interrupt controller node.\n+ * @ranges: Destination for parsed range descriptors.\n+ *\n+ * Return: 0 on success, negative errno on error.\n+ */\n+int aspeed_intc_populate_ranges(struct device *dev,\n+\t\t\t\tstruct aspeed_intc_interrupt_ranges *ranges)\n+{\n+\tstruct aspeed_intc_interrupt_range *arr;\n+\tconst __be32 *pvs, *pve;\n+\tstruct device_node *dn;\n+\tint len;\n+\n+\tif (!dev || !ranges)\n+\t\treturn -EINVAL;\n+\n+\tdn = dev->of_node;\n+\n+\tpvs = of_get_property(dn, \"aspeed,interrupt-ranges\", &len);\n+\tif (!pvs)\n+\t\treturn -EINVAL;\n+\n+\tif (len % sizeof(__be32))\n+\t\treturn -EINVAL;\n+\n+\t/* Over-estimate the range entry count for now */\n+\tranges->ranges = devm_kmalloc_array(dev,\n+\t\t\t\t\t len / (ASPEED_INTC_RANGE_FIXED_CELLS * sizeof(__be32)),\n+\t\t\t\t\t sizeof(*ranges->ranges),\n+\t\t\t\t\t GFP_KERNEL);\n+\tif (!ranges->ranges)\n+\t\treturn -ENOMEM;\n+\n+\tpve = pvs + (len / sizeof(__be32));\n+\tfor (unsigned int i = 0; pve - pvs >= ASPEED_INTC_RANGE_FIXED_CELLS; i++) {\n+\t\tstruct aspeed_intc_interrupt_range *r;\n+\t\tstruct device_node *target;\n+\t\tu32 target_cells;\n+\n+\t\ttarget = of_find_node_by_phandle(be32_to_cpu(pvs[ASPEED_INTC_RANGE_OFF_PHANDLE]));\n+\t\tif (!target)\n+\t\t\treturn -EINVAL;\n+\n+\t\tif (of_property_read_u32(target, \"#interrupt-cells\",\n+\t\t\t\t\t &target_cells)) {\n+\t\t\tof_node_put(target);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\tif (!target_cells || target_cells > IRQ_DOMAIN_IRQ_SPEC_PARAMS) {\n+\t\t\tof_node_put(target);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\tif (pve - pvs < ASPEED_INTC_RANGE_FIXED_CELLS + target_cells) {\n+\t\t\tof_node_put(target);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\tr = &ranges->ranges[i];\n+\t\tr->start = be32_to_cpu(pvs[ASPEED_INTC_RANGE_OFF_START]);\n+\t\tr->count = be32_to_cpu(pvs[ASPEED_INTC_RANGE_OFF_COUNT]);\n+\n+\t\t{\n+\t\t\tstruct of_phandle_args args = {\n+\t\t\t\t.np = target,\n+\t\t\t\t.args_count = target_cells,\n+\t\t\t};\n+\n+\t\t\tfor (u32 j = 0; j < target_cells; j++)\n+\t\t\t\targs.args[j] = be32_to_cpu(pvs[ASPEED_INTC_RANGE_FIXED_CELLS + j]);\n+\n+\t\t\tof_phandle_args_to_fwspec(target, args.args,\n+\t\t\t\t\t\t args.args_count,\n+\t\t\t\t\t\t &r->upstream);\n+\t\t}\n+\n+\t\tof_node_put(target);\n+\t\tpvs += ASPEED_INTC_RANGE_FIXED_CELLS + target_cells;\n+\t\tranges->nranges++;\n+\t}\n+\n+\t/* Re-fit the range array now we know the entry count */\n+\tarr = devm_krealloc_array(dev, ranges->ranges, ranges->nranges,\n+\t\t\t\t sizeof(*ranges->ranges), GFP_KERNEL);\n+\tif (!arr)\n+\t\treturn -ENOMEM;\n+\tranges->ranges = arr;\n+\n+\treturn 0;\n+}\ndiff --git a/drivers/irqchip/irq-ast2700.h b/drivers/irqchip/irq-ast2700.h\nnew file mode 100644\nindex 000000000000..544f1af4c8ab\n--- /dev/null\n+++ b/drivers/irqchip/irq-ast2700.h\n@@ -0,0 +1,47 @@\n+/* SPDX-License-Identifier: GPL-2.0-only */\n+/*\n+ * Aspeed AST2700 Interrupt Controller.\n+ *\n+ * Copyright (C) 2026 ASPEED Technology Inc.\n+ */\n+#ifndef DRIVERS_IRQCHIP_AST2700\n+#define DRIVERS_IRQCHIP_AST2700\n+\n+#include <linux/device.h>\n+#include <linux/irqdomain.h>\n+\n+#define AST2700_INTC_INVALID_ROUTE (~0U)\n+#define ASPEED_INTC_RANGES_BASE\t\t0U\n+#define ASPEED_INTC_RANGES_COUNT\t1U\n+\n+struct aspeed_intc_interrupt_range {\n+\tu32 start;\n+\tu32 count;\n+\tstruct irq_fwspec upstream;\n+};\n+\n+struct aspeed_intc_interrupt_ranges {\n+\tstruct aspeed_intc_interrupt_range *ranges;\n+\tunsigned int nranges;\n+};\n+\n+struct aspeed_intc0 {\n+\tstruct device\t\t\t\t*dev;\n+\tvoid __iomem\t\t\t\t*base;\n+\traw_spinlock_t\t\t\t\tintc_lock;\n+\tstruct irq_domain\t\t\t*local;\n+\tstruct device_node\t\t\t*parent;\n+\tstruct aspeed_intc_interrupt_ranges\tranges;\n+};\n+\n+int aspeed_intc_populate_ranges(struct device *dev,\n+\t\t\t\tstruct aspeed_intc_interrupt_ranges *ranges);\n+\n+int aspeed_intc0_resolve_route(const struct irq_domain *c0domain,\n+\t\t\t size_t nc1outs,\n+\t\t\t const u32 *c1outs,\n+\t\t\t size_t nc1ranges,\n+\t\t\t const struct aspeed_intc_interrupt_range *c1ranges,\n+\t\t\t struct aspeed_intc_interrupt_range *resolved);\n+\n+#endif\n", "prefixes": [ "v4", "2/4" ] }