From nobody Sun Feb 8 15:25:10 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1914435F8B6; Thu, 15 Jan 2026 09:51:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768470673; cv=none; b=YNYLMhb80VNaV8f73YhREdFGqUVCREyM81/juR350txAa0h6JdGOg14vL26OvwuHhfYfL43GCJ/qjDgOKMyyrwL92IjTszgAtQwfJTlmb4jLqiizCyGkaR0qhTY35rc4GEDBLaAOrnI6jAAJnN7v2/sHPLfN8bvgLY9flilBg5A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768470673; c=relaxed/simple; bh=YBaXKAlozW+FYMpEzXzll2PzBijH4+E23WmpoK+kcUE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NN6vLG/fEQsx+3R/6e6WUe3Dr24WOwZiOcbMzPbhyiM59NYRm4J0n5A2eRojPH1UwHCGg7a167GtgyZFrKgaOrnKG25C/DJzDyOBv0i1tYPgI0RnCnyat1bHSjIdlHg6bCc3lTyL2fGI1Jt4bZHJOG+r9Km1YUgqN8YLS31NnjA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=HnSOVkL4; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="HnSOVkL4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9B831C116D0; Thu, 15 Jan 2026 09:51:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768470672; bh=YBaXKAlozW+FYMpEzXzll2PzBijH4+E23WmpoK+kcUE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=HnSOVkL4OrQiSP2yCQeizqnkagsNgPPwPBF66l8foQfSAlklF+e5AaEjavQA4KYyT jGi7AbKnc87EgONLRTOV0dmACVXNCvrExT3uS8eae74cUZQAHcrrV3lCwBPJe/yLJY 6AXMry/xt41t+868fgUxf2gXhuqvi/0ds8GY3cj4gnFdO0gQX6HSBfoQ+W7F6I+Bh1 OO+ysDCSfnFXhILpK+OUG3oBro9Wh54u2Cvy0dzkjF6/3+X8Xwx5ODeEDErrHGJdJs /31yCPOI12Jum05n0jwN6q4NACHNC4c3RGIRC/6aW7Rc+kn9zbxrDbMAfoulDWCrWc BNUjVdINHiPCQ== From: Lorenzo Pieralisi Date: Thu, 15 Jan 2026 10:50:47 +0100 Subject: [PATCH v3 1/6] irqdomain: Add parent field to struct irqchip_fwid Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260115-gicv5-host-acpi-v3-1-c13a9a150388@kernel.org> References: <20260115-gicv5-host-acpi-v3-0-c13a9a150388@kernel.org> In-Reply-To: <20260115-gicv5-host-acpi-v3-0-c13a9a150388@kernel.org> To: "Rafael J. Wysocki" , Len Brown , Robert Moore , Hanjun Guo , Sudeep Holla , Marc Zyngier , Bjorn Helgaas , Thomas Gleixner , Jonathan Cameron Cc: linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org, Lorenzo Pieralisi X-Mailer: b4 0.14.3 The GICv5 driver IRQ domain hierarchy requires adding a parent field to struct irqchip_fwid so that core code can reference a fwnode_handle parent for a given fwnode. Add a parent field to struct irqchip_fwid and update the related kernel API functions to initialize and handle it. Signed-off-by: Lorenzo Pieralisi Cc: Thomas Gleixner Cc: Marc Zyngier Acked-by: Thomas Gleixner Reviewed-by: Jonathan Cameron --- include/linux/irqdomain.h | 30 ++++++++++++++++++++++++++---- kernel/irq/irqdomain.c | 14 +++++++++++++- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 62f81bbeb490..73c25d40846c 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -257,7 +257,8 @@ static inline void irq_domain_set_pm_device(struct irq_= domain *d, struct device =20 #ifdef CONFIG_IRQ_DOMAIN struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id, - const char *name, phys_addr_t *pa); + const char *name, phys_addr_t *pa, + struct fwnode_handle *parent); =20 enum { IRQCHIP_FWNODE_REAL, @@ -267,18 +268,39 @@ enum { =20 static inline struct fwnode_handle *irq_domain_alloc_named_fwnode(const ch= ar *name) { - return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL); + return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, NUL= L); +} + +static inline +struct fwnode_handle *irq_domain_alloc_named_parented_fwnode(const char *n= ame, + struct fwnode_handle *parent) +{ + return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, par= ent); } =20 static inline struct fwnode_handle *irq_domain_alloc_named_id_fwnode(const= char *name, int id) { return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED_ID, id, name, - NULL); + NULL, NULL); +} + +static inline +struct fwnode_handle *irq_domain_alloc_named_id_parented_fwnode(const char= *name, int id, + struct fwnode_handle *parent) +{ + return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED_ID, id, name, + NULL, parent); } =20 static inline struct fwnode_handle *irq_domain_alloc_fwnode(phys_addr_t *p= a) { - return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_REAL, 0, NULL, pa); + return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_REAL, 0, NULL, pa, NULL); +} + +static inline struct fwnode_handle *irq_domain_alloc_parented_fwnode(phys_= addr_t *pa, + struct fwnode_handle *parent) +{ + return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_REAL, 0, NULL, pa, parent= ); } =20 void irq_domain_free_fwnode(struct fwnode_handle *fwnode); diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 2652c4cfd877..baf77cd167c4 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -33,6 +33,7 @@ static void irq_domain_free_one_irq(struct irq_domain *do= main, unsigned int virq =20 struct irqchip_fwid { struct fwnode_handle fwnode; + struct fwnode_handle *parent; unsigned int type; char *name; phys_addr_t *pa; @@ -53,8 +54,16 @@ static const char *irqchip_fwnode_get_name(const struct = fwnode_handle *fwnode) return fwid->name; } =20 +static struct fwnode_handle *irqchip_fwnode_get_parent(const struct fwnode= _handle *fwnode) +{ + struct irqchip_fwid *fwid =3D container_of(fwnode, struct irqchip_fwid, f= wnode); + + return fwid->parent; +} + const struct fwnode_operations irqchip_fwnode_ops =3D { .get_name =3D irqchip_fwnode_get_name, + .get_parent =3D irqchip_fwnode_get_parent, }; EXPORT_SYMBOL_GPL(irqchip_fwnode_ops); =20 @@ -65,6 +74,7 @@ EXPORT_SYMBOL_GPL(irqchip_fwnode_ops); * @id: Optional user provided id if name !=3D NULL * @name: Optional user provided domain name * @pa: Optional user-provided physical address + * @parent: Optional parent fwnode_handle * * Allocate a struct irqchip_fwid, and return a pointer to the embedded * fwnode_handle (or NULL on failure). @@ -76,7 +86,8 @@ EXPORT_SYMBOL_GPL(irqchip_fwnode_ops); */ struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id, const char *name, - phys_addr_t *pa) + phys_addr_t *pa, + struct fwnode_handle *parent) { struct irqchip_fwid *fwid; char *n; @@ -104,6 +115,7 @@ struct fwnode_handle *__irq_domain_alloc_fwnode(unsigne= d int type, int id, fwid->type =3D type; fwid->name =3D n; fwid->pa =3D pa; + fwid->parent =3D parent; fwnode_init(&fwid->fwnode, &irqchip_fwnode_ops); return &fwid->fwnode; } --=20 2.50.1 From nobody Sun Feb 8 15:25:10 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0AADF334695; Thu, 15 Jan 2026 09:51:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768470677; cv=none; b=iouuvCvi5bSBe8S4WmfXiB5SDT7EmT5nXebBPghliH79ZJHsY1nypWj9OD8NEU4BNrhSCQWfXUK8tJiKDj6B3Qh39CSHkvjoOXxX4sZaZ+qmnvxi9/UWumGD42SCe84nK/ZYNCwyADfd76KUZ1383V7M9g+maDrhL2qEvihTH4w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768470677; c=relaxed/simple; bh=aTM+15MuMBWcKYpMkd2t27DnfiaoGuCnRx72X7ugcxU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=X7izuDHBMNrGCgGDWPnxUxFOxqe6fgl+sT8450czmsySnBE2RmDKS0j5O1esdoAK5AKX3iWtPEFPUNMQJp8HLc91XS6O/OViujC8coYZUWS/WisyZz+rQrSK1NFSmKRSCAv/kgfH/5Bk0Xs8a1OuJf3Ac0sYRQqpmPZ7u5LZT2s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=EuVCJ74v; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="EuVCJ74v" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 51B51C116D0; Thu, 15 Jan 2026 09:51:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768470676; bh=aTM+15MuMBWcKYpMkd2t27DnfiaoGuCnRx72X7ugcxU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=EuVCJ74vrP6Po2rDh46/ZydRo60pFY8Gpd13cGcWTBD2XMDvrqIkdbZzt1V7L26LN BNC2WOLZxdEBp1kaG7vDtS9+ha+T4iaL4SwN+jPWz9bFP7n5WEuhLoqdykUfQiilxG bDf8ihLDNuJ5vDrKEiLbQE+KIe4s8dwDNZ4lK2wrKbBkdwhsP0JZ8Y2Wo5bjoQFr0D yRULNNzWN55GiW214LnBVTcTqmO51xV1YHeD9KftUqy0B6aUZ3JZ4bsj8q8ZGDbgRM /F2xH7sIGV1JPK5BxiFPnOpNunl+Lot7IM+H863EC5Ea2FYx9qrhIgxrRJQPzMj9xN Eb5GnJdvPWUrg== From: Lorenzo Pieralisi Date: Thu, 15 Jan 2026 10:50:48 +0100 Subject: [PATCH v3 2/6] PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260115-gicv5-host-acpi-v3-2-c13a9a150388@kernel.org> References: <20260115-gicv5-host-acpi-v3-0-c13a9a150388@kernel.org> In-Reply-To: <20260115-gicv5-host-acpi-v3-0-c13a9a150388@kernel.org> To: "Rafael J. Wysocki" , Len Brown , Robert Moore , Hanjun Guo , Sudeep Holla , Marc Zyngier , Bjorn Helgaas , Thomas Gleixner , Jonathan Cameron Cc: linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org, Lorenzo Pieralisi X-Mailer: b4 0.14.3 To support booting with OF and ACPI seamlessly, GIC ITS parent code requires the PCI/MSI irqdomain layer to implement a function to retrieve an MSI controller fwnode and map an RID in a firmware agnostic way (ie pci_msi_map_rid_ctlr_node()). Convert pci_msi_map_rid_ctlr_node() to an OF agnostic interface (fwnode_handle based) and update the GIC ITS MSI parent code to reflect the pci_msi_map_rid_ctlr_node() change. Signed-off-by: Lorenzo Pieralisi Reviewed-by: Jonathan Cameron Acked-by: Bjorn Helgaas Cc: Thomas Gleixner Cc: Bjorn Helgaas Cc: Marc Zyngier Acked-by: Thomas Gleixner --- drivers/irqchip/irq-gic-its-msi-parent.c | 8 ++++---- drivers/pci/msi/irqdomain.c | 21 ++++++++++++++++----- include/linux/msi.h | 3 ++- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq= -gic-its-msi-parent.c index 12f45228c867..4d1ad1ee005d 100644 --- a/drivers/irqchip/irq-gic-its-msi-parent.c +++ b/drivers/irqchip/irq-gic-its-msi-parent.c @@ -104,7 +104,7 @@ static int its_pci_msi_prepare(struct irq_domain *domai= n, struct device *dev, static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device= *dev, int nvec, msi_alloc_info_t *info) { - struct device_node *msi_node =3D NULL; + struct fwnode_handle *msi_node =3D NULL; struct msi_domain_info *msi_info; struct pci_dev *pdev; phys_addr_t pa; @@ -116,15 +116,15 @@ static int its_v5_pci_msi_prepare(struct irq_domain *= domain, struct device *dev, =20 pdev =3D to_pci_dev(dev); =20 - rid =3D pci_msi_map_rid_ctlr_node(pdev, &msi_node); + rid =3D pci_msi_map_rid_ctlr_node(domain->parent, pdev, &msi_node); if (!msi_node) return -ENODEV; =20 - ret =3D its_translate_frame_address(msi_node, &pa); + ret =3D its_translate_frame_address(to_of_node(msi_node), &pa); if (ret) return -ENODEV; =20 - of_node_put(msi_node); + fwnode_handle_put(msi_node); =20 /* ITS specific DeviceID */ info->scratchpad[0].ul =3D rid; diff --git a/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c index a329060287b5..3a4038640fda 100644 --- a/drivers/pci/msi/irqdomain.c +++ b/drivers/pci/msi/irqdomain.c @@ -376,23 +376,34 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *dom= ain, struct pci_dev *pdev) } =20 /** - * pci_msi_map_rid_ctlr_node - Get the MSI controller node and MSI request= er id (RID) + * pci_msi_map_rid_ctlr_node - Get the MSI controller fwnode_handle and MS= I requester id (RID) + * @domain: The interrupt domain * @pdev: The PCI device - * @node: Pointer to store the MSI controller device node + * @node: Pointer to store the MSI controller fwnode_handle * - * Use the firmware data to find the MSI controller node for @pdev. + * Use the firmware data to find the MSI controller fwnode_handle for @pde= v. * If found map the RID and initialize @node with it. @node value must * be set to NULL on entry. * * Returns: The RID. */ -u32 pci_msi_map_rid_ctlr_node(struct pci_dev *pdev, struct device_node **n= ode) +u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *p= dev, + struct fwnode_handle **node) { u32 rid =3D pci_dev_id(pdev); =20 pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid); =20 - return of_msi_xlate(&pdev->dev, node, rid); + /* Check whether the domain fwnode is an OF node */ + if (irq_domain_get_of_node(domain)) { + struct device_node *msi_ctlr_node =3D NULL; + + rid =3D of_msi_xlate(&pdev->dev, &msi_ctlr_node, rid); + if (msi_ctlr_node) + *node =3D of_fwnode_handle(msi_ctlr_node); + } + + return rid; } =20 /** diff --git a/include/linux/msi.h b/include/linux/msi.h index 8003e3218c46..8ddb05d5c96a 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -702,7 +702,8 @@ void __pci_write_msi_msg(struct msi_desc *entry, struct= msi_msg *msg); void pci_msi_mask_irq(struct irq_data *data); void pci_msi_unmask_irq(struct irq_data *data); u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *= pdev); -u32 pci_msi_map_rid_ctlr_node(struct pci_dev *pdev, struct device_node **n= ode); +u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *p= dev, + struct fwnode_handle **node); struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev); void pci_msix_prepare_desc(struct irq_domain *domain, msi_alloc_info_t *ar= g, struct msi_desc *desc); --=20 2.50.1 From nobody Sun Feb 8 15:25:10 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BF72D33B94A; Thu, 15 Jan 2026 09:51:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768470680; cv=none; b=hnHMDA0B2PyhUH+hkJKpH32fX/qdL7V6X0G8FvneU22r1Ho+O/0nQFanlZocCRVxwnME10/ZXIWua3po0qPlPVJJmPaeIUrJwxi2/UreLWj/HNjg+LkQbhvpSPZFgPlyaQiurkjjltqVrEFXRVJqiPy+tA+y6XK8rEpGDdlWNIU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768470680; c=relaxed/simple; bh=HpqC5xx2JciOccRQs/mA4K61HFjvK3cJ4krLdRlhU9Q=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ASsvRT0INfxX3u2L6fusKv9iEhHmbaDJY5AKVh/rsVCbIy1iZjUJC26ewPedtEOwn+GljVXL2Fe149c0xP+SYDpGAm8yaFrDitciBfrFIR90LJimiiw0oOZJd1wa254IP/ddlZV411r9jERqUc4777qUerg+3kIPiSIv5hja/B8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QAm8REZz; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="QAm8REZz" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3470FC16AAE; Thu, 15 Jan 2026 09:51:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768470680; bh=HpqC5xx2JciOccRQs/mA4K61HFjvK3cJ4krLdRlhU9Q=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=QAm8REZzpgJ6ozjkDjZvG74LFL6foGHUYhUPiID8BHk89ghMnX1FHRs/dptQuDsMR ubn9o0aUETiUpCFZOqE8repG7S45r8PTGGNrgCYfkm/aiC4GnMZtbgvcipQd65Zefa uHhlTOMBuQuvYyMVoqv7VXHhFvQu9+KbISTX89OizFUuofTWZf2b89pQHao0d0tgnl sP1eTxrhMT5jxULYR+kMUoZ/I6CaVPjUORR/R4HhKheAaWteZhJAYhWj6YfG8xyCVc jxmkcvGaJyM2Jyi+KMOiZarzNgz9HUFanWNiEg/xGEGrcfeaoa0yfUlPF+hkhow4Au Rm5g+JcB2lbuQ== From: Lorenzo Pieralisi Date: Thu, 15 Jan 2026 10:50:49 +0100 Subject: [PATCH v3 3/6] irqchip/gic-v5: Split IRS probing into OF and generic portions Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260115-gicv5-host-acpi-v3-3-c13a9a150388@kernel.org> References: <20260115-gicv5-host-acpi-v3-0-c13a9a150388@kernel.org> In-Reply-To: <20260115-gicv5-host-acpi-v3-0-c13a9a150388@kernel.org> To: "Rafael J. Wysocki" , Len Brown , Robert Moore , Hanjun Guo , Sudeep Holla , Marc Zyngier , Bjorn Helgaas , Thomas Gleixner , Jonathan Cameron Cc: linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org, Lorenzo Pieralisi X-Mailer: b4 0.14.3 Split the IRS driver code into OF specific and generic portions in order to pave the way for adding ACPI firmware bindings support. Signed-off-by: Lorenzo Pieralisi Cc: Thomas Gleixner Cc: Marc Zyngier Acked-by: Thomas Gleixner Reviewed-by: Jonathan Cameron --- drivers/irqchip/irq-gic-v5-irs.c | 112 +++++++++++++++++++++--------------= ---- 1 file changed, 60 insertions(+), 52 deletions(-) diff --git a/drivers/irqchip/irq-gic-v5-irs.c b/drivers/irqchip/irq-gic-v5-= irs.c index ce2732d649a3..7db44a91f043 100644 --- a/drivers/irqchip/irq-gic-v5-irs.c +++ b/drivers/irqchip/irq-gic-v5-irs.c @@ -545,15 +545,13 @@ int gicv5_irs_register_cpu(int cpuid) =20 static void __init gicv5_irs_init_bases(struct gicv5_irs_chip_data *irs_da= ta, void __iomem *irs_base, - struct fwnode_handle *handle) + bool noncoherent) { - struct device_node *np =3D to_of_node(handle); u32 cr0, cr1; =20 - irs_data->fwnode =3D handle; irs_data->irs_base =3D irs_base; =20 - if (of_property_read_bool(np, "dma-noncoherent")) { + if (noncoherent) { /* * A non-coherent IRS implies that some cache levels cannot be * used coherently by the cores and GIC. Our only option is to mark @@ -678,49 +676,13 @@ static void irs_setup_pri_bits(u32 idr1) } } =20 -static int __init gicv5_irs_init(struct device_node *node) +static int __init gicv5_irs_init(struct gicv5_irs_chip_data *irs_data) { - struct gicv5_irs_chip_data *irs_data; - void __iomem *irs_base; - u32 idr, spi_count; - u8 iaffid_bits; - int ret; + u32 spi_count, idr =3D irs_readl_relaxed(irs_data, GICV5_IRS_IDR2); =20 - irs_data =3D kzalloc(sizeof(*irs_data), GFP_KERNEL); - if (!irs_data) - return -ENOMEM; - - raw_spin_lock_init(&irs_data->spi_config_lock); - - ret =3D of_property_match_string(node, "reg-names", "ns-config"); - if (ret < 0) { - pr_err("%pOF: ns-config reg-name not present\n", node); - goto out_err; - } - - irs_base =3D of_io_request_and_map(node, ret, of_node_full_name(node)); - if (IS_ERR(irs_base)) { - pr_err("%pOF: unable to map GICv5 IRS registers\n", node); - ret =3D PTR_ERR(irs_base); - goto out_err; - } - - gicv5_irs_init_bases(irs_data, irs_base, &node->fwnode); - - idr =3D irs_readl_relaxed(irs_data, GICV5_IRS_IDR1); - iaffid_bits =3D FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1; - - ret =3D gicv5_irs_of_init_affinity(node, irs_data, iaffid_bits); - if (ret) { - pr_err("Failed to parse CPU IAFFIDs from the device tree!\n"); - goto out_iomem; - } - - idr =3D irs_readl_relaxed(irs_data, GICV5_IRS_IDR2); if (WARN(!FIELD_GET(GICV5_IRS_IDR2_LPI, idr), "LPI support not available - no IPIs, can't proceed\n")) { - ret =3D -ENODEV; - goto out_iomem; + return -ENODEV; } =20 idr =3D irs_readl_relaxed(irs_data, GICV5_IRS_IDR7); @@ -729,14 +691,6 @@ static int __init gicv5_irs_init(struct device_node *n= ode) idr =3D irs_readl_relaxed(irs_data, GICV5_IRS_IDR6); irs_data->spi_range =3D FIELD_GET(GICV5_IRS_IDR6_SPI_IRS_RANGE, idr); =20 - if (irs_data->spi_range) { - pr_info("%s detected SPI range [%u-%u]\n", - of_node_full_name(node), - irs_data->spi_min, - irs_data->spi_min + - irs_data->spi_range - 1); - } - /* * Do the global setting only on the first IRS. * Global properties (iaffid_bits, global spi count) are guaranteed to @@ -760,6 +714,60 @@ static int __init gicv5_irs_init(struct device_node *n= ode) list_add_tail(&irs_data->entry, &irs_nodes); =20 return 0; +} + +static int __init gicv5_irs_of_init(struct device_node *node) +{ + struct gicv5_irs_chip_data *irs_data; + void __iomem *irs_base; + u8 iaffid_bits; + u32 idr; + int ret; + + irs_data =3D kzalloc(sizeof(*irs_data), GFP_KERNEL); + if (!irs_data) + return -ENOMEM; + + raw_spin_lock_init(&irs_data->spi_config_lock); + + ret =3D of_property_match_string(node, "reg-names", "ns-config"); + if (ret < 0) { + pr_err("%pOF: ns-config reg-name not present\n", node); + goto out_err; + } + + irs_base =3D of_io_request_and_map(node, ret, of_node_full_name(node)); + if (IS_ERR(irs_base)) { + pr_err("%pOF: unable to map GICv5 IRS registers\n", node); + ret =3D PTR_ERR(irs_base); + goto out_err; + } + + irs_data->fwnode =3D of_fwnode_handle(node); + gicv5_irs_init_bases(irs_data, irs_base, of_property_read_bool(node, "dma= -noncoherent")); + + idr =3D irs_readl_relaxed(irs_data, GICV5_IRS_IDR1); + iaffid_bits =3D FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1; + + ret =3D gicv5_irs_of_init_affinity(node, irs_data, iaffid_bits); + if (ret) { + pr_err("Failed to parse CPU IAFFIDs from the device tree!\n"); + goto out_iomem; + } + + ret =3D gicv5_irs_init(irs_data); + if (ret) + goto out_iomem; + + if (irs_data->spi_range) { + pr_info("%s detected SPI range [%u-%u]\n", + of_node_full_name(node), + irs_data->spi_min, + irs_data->spi_min + + irs_data->spi_range - 1); + } + + return ret; =20 out_iomem: iounmap(irs_base); @@ -818,7 +826,7 @@ int __init gicv5_irs_of_probe(struct device_node *paren= t) if (!of_device_is_compatible(np, "arm,gic-v5-irs")) continue; =20 - ret =3D gicv5_irs_init(np); + ret =3D gicv5_irs_of_init(np); if (ret) pr_err("Failed to init IRS %s\n", np->full_name); } --=20 2.50.1 From nobody Sun Feb 8 15:25:10 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 925BE35EDCA; Thu, 15 Jan 2026 09:51:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768470684; cv=none; b=Y/L1yUwd8OhdLDjYNW3ln0PTULgS0Qm4tbJvrFK1bl1dr4svnUqPQUldRsHIKFQrxl9z1DmrZWA5ti+PscQNB4+/adjI+9zWuEHlHVY+ncvAXlG2ZKn9V6zzQjWfvLJrBufJ1Ou9rIxaVm6XFkrBybeIgaPbyaDJdPCCYejw5vM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768470684; c=relaxed/simple; bh=U5y6W9KU/eUjo3vcYYv+6C+tpDKsFnfaRPLMuIAuK8Y=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=qetOMdROd8fyfCYAmpKd7U8OcdKFkRsYVhispPboYFPpc8+RxV9ymY4Q7bYLmbLJcntc9xGErPZTIVfb5l3IMMrt2696SerououvNuttqBFMkf5HgrytuOSyKYS8grH6RLgE4rPtEf6kaASpMDch6M1BhPWSkPio8FkcAW4+Gkg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=p4jG/BSr; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="p4jG/BSr" Received: by smtp.kernel.org (Postfix) with ESMTPSA id EFA48C116D0; Thu, 15 Jan 2026 09:51:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768470684; bh=U5y6W9KU/eUjo3vcYYv+6C+tpDKsFnfaRPLMuIAuK8Y=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=p4jG/BSrnmHlYv4XoUqAikb5/11tHXgkVBryUAPkiRezvsd3jgSDCr269ul/FHDoL yt9x7y3/zeBH3PQtMaPjl63vQgSD4TuxkiimGTozVvQPU9J+4Ch5tXFsw0qGDj4Inl 8m8N4QhNnLmW1K91/IO/DN5lBJf7/3R0b1fe4sYtV7G5R1HOV3IsHQOdSRoSdYTNKh wu8re6vdPwVII5EggULKKANY17jDJDK1mejp0SkJvBdOtpHfyl8zzpuZ1jvDBBTE55 bgay7KzEPJW+Phzn8EbN5uCux7qeINMFT7weYN5AoNy2XmU/4b/CaX1TJKBpCoX/fQ p6Pwh6soPjIdw== From: Lorenzo Pieralisi Date: Thu, 15 Jan 2026 10:50:50 +0100 Subject: [PATCH v3 4/6] irqchip/gic-v5: Add ACPI IRS probing Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260115-gicv5-host-acpi-v3-4-c13a9a150388@kernel.org> References: <20260115-gicv5-host-acpi-v3-0-c13a9a150388@kernel.org> In-Reply-To: <20260115-gicv5-host-acpi-v3-0-c13a9a150388@kernel.org> To: "Rafael J. Wysocki" , Len Brown , Robert Moore , Hanjun Guo , Sudeep Holla , Marc Zyngier , Bjorn Helgaas , Thomas Gleixner , Jonathan Cameron Cc: linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org, Lorenzo Pieralisi X-Mailer: b4 0.14.3 On ARM64 ACPI systems GICv5 IRSes are described in MADT sub-entries. Add the required plumbing to parse MADT IRS firmware table entries and probe the IRS components in ACPI. Augment the irqdomain_ops.translate() for PPI and SPI IRQs in order to provide support for their ACPI based firmware translation. Implement an irqchip ACPI based callback to initialize the global GSI domain upon an MADT IRS detection. The IRQCHIP_ACPI_DECLARE() entry in the top level GICv5 driver is only used to trigger the IRS probing (ie the global GSI domain is initialized once on the first call on multi-IRS systems); IRS probing takes place by calling acpi_table_parse_madt() in the IRS sub-driver, that probes all IRSes in sequence. Add a new ACPI interrupt model so that it can be detected at runtime and distinguished from previous GIC architecture models. Signed-off-by: Lorenzo Pieralisi Cc: Thomas Gleixner Cc: "Rafael J. Wysocki" Cc: Marc Zyngier Acked-by: Thomas Gleixner Reviewed-by: Jonathan Cameron --- drivers/acpi/bus.c | 3 + drivers/irqchip/irq-gic-v5-irs.c | 128 +++++++++++++++++++++++++++++++++= ++ drivers/irqchip/irq-gic-v5.c | 134 +++++++++++++++++++++++++++++++--= ---- include/linux/acpi.h | 1 + include/linux/irqchip/arm-gic-v5.h | 1 + 5 files changed, 248 insertions(+), 19 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index a984ccd4a2a0..e4f4059c4f1d 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -1197,6 +1197,9 @@ static int __init acpi_bus_init_irq(void) case ACPI_IRQ_MODEL_GIC: message =3D "GIC"; break; + case ACPI_IRQ_MODEL_GIC_V5: + message =3D "GICv5"; + break; case ACPI_IRQ_MODEL_PLATFORM: message =3D "platform specific model"; break; diff --git a/drivers/irqchip/irq-gic-v5-irs.c b/drivers/irqchip/irq-gic-v5-= irs.c index 7db44a91f043..a27a01f9e7a2 100644 --- a/drivers/irqchip/irq-gic-v5-irs.c +++ b/drivers/irqchip/irq-gic-v5-irs.c @@ -5,6 +5,7 @@ =20 #define pr_fmt(fmt) "GICv5 IRS: " fmt =20 +#include #include #include #include @@ -833,3 +834,130 @@ int __init gicv5_irs_of_probe(struct device_node *par= ent) =20 return list_empty(&irs_nodes) ? -ENODEV : 0; } + +#ifdef CONFIG_ACPI + +#define ACPI_GICV5_IRS_MEM_SIZE (SZ_64K) +static struct gicv5_irs_chip_data *current_irs_data __initdata; +static int current_irsid __initdata =3D -1; +static u8 current_iaffid_bits __initdata; + +static int __init gic_acpi_parse_iaffid(union acpi_subtable_headers *heade= r, + const unsigned long end) +{ + struct acpi_madt_generic_interrupt *gicc =3D (struct acpi_madt_generic_in= terrupt *)header; + int cpu; + + if (!(gicc->flags & (ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE))) + return 0; + + if (gicc->irs_id !=3D current_irsid) + return 0; + + cpu =3D get_logical_index(gicc->arm_mpidr); + + if (gicc->iaffid & ~GENMASK(current_iaffid_bits - 1, 0)) { + pr_warn("CPU %d iaffid 0x%x exceeds IRS iaffid bits\n", cpu, gicc->iaffi= d); + return 0; + } + + /* Bind the IAFFID and the CPU */ + per_cpu(cpu_iaffid, cpu).iaffid =3D gicc->iaffid; + per_cpu(cpu_iaffid, cpu).valid =3D true; + pr_debug("Processed IAFFID %u for CPU%d", per_cpu(cpu_iaffid, cpu).iaffid= , cpu); + + /* We also know that the CPU is connected to this IRS */ + per_cpu(per_cpu_irs_data, cpu) =3D current_irs_data; + + return 0; +} + +static int __init gicv5_irs_acpi_init_affinity(u32 irsid, struct gicv5_irs= _chip_data *irs_data) +{ + u32 idr; + + current_irsid =3D irsid; + current_irs_data =3D irs_data; + + idr =3D irs_readl_relaxed(irs_data, GICV5_IRS_IDR1); + current_iaffid_bits =3D FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1; + + acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, gic_acpi_parse_ia= ffid, 0); + + return 0; +} + +static struct resource * __init gic_request_region(resource_size_t base, r= esource_size_t size, + const char *name) +{ + struct resource *r =3D request_mem_region(base, size, name); + + if (!r) + pr_warn_once(FW_BUG "%s region %pa has overlapping address\n", name, &ba= se); + + return r; +} + +static int __init gic_acpi_parse_madt_irs(union acpi_subtable_headers *hea= der, + const unsigned long end) +{ + struct acpi_madt_gicv5_irs *irs =3D (struct acpi_madt_gicv5_irs *)header; + struct gicv5_irs_chip_data *irs_data; + void __iomem *irs_base; + struct resource *r; + int ret; + + /* Per-IRS data structure */ + irs_data =3D kzalloc(sizeof(*irs_data), GFP_KERNEL); + if (!irs_data) + return -ENOMEM; + + /* This spinlock is used for SPI config changes */ + raw_spin_lock_init(&irs_data->spi_config_lock); + + r =3D gic_request_region(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZ= E, "GICv5 IRS"); + if (!r) { + ret =3D -EBUSY; + goto out_free; + } + + irs_base =3D ioremap(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE); + if (!irs_base) { + pr_err("Unable to map GIC IRS registers\n"); + ret =3D -ENOMEM; + goto out_release; + } + + gicv5_irs_init_bases(irs_data, irs_base, irs->flags & ACPI_MADT_IRS_NON_C= OHERENT); + + gicv5_irs_acpi_init_affinity(irs->irs_id, irs_data); + + ret =3D gicv5_irs_init(irs_data); + if (ret) + goto out_map; + + if (irs_data->spi_range) { + pr_info("%s @%llx detected SPI range [%u-%u]\n", "IRS", irs->config_base= _address, + irs_data->spi_min, + irs_data->spi_min + + irs_data->spi_range - 1); + } + + return 0; + +out_map: + iounmap(irs_base); +out_release: + release_mem_region(r->start, resource_size(r)); +out_free: + kfree(irs_data); + return ret; +} + +int __init gicv5_irs_acpi_probe(void) +{ + acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_IRS, gic_acpi_parse_madt_irs, = 0); + + return list_empty(&irs_nodes) ? -ENODEV : 0; +} +#endif diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c index 41ef286c4d78..23fd551c4347 100644 --- a/drivers/irqchip/irq-gic-v5.c +++ b/drivers/irqchip/irq-gic-v5.c @@ -579,16 +579,36 @@ static __always_inline int gicv5_irq_domain_translate= (struct irq_domain *d, unsigned int *type, const u8 hwirq_type) { - if (!is_of_node(fwspec->fwnode)) - return -EINVAL; + unsigned int hwirq_trigger; + u8 fwspec_irq_type; =20 - if (fwspec->param_count < 3) - return -EINVAL; + if (is_of_node(fwspec->fwnode)) { =20 - if (fwspec->param[0] !=3D hwirq_type) - return -EINVAL; + if (fwspec->param_count < 3) + return -EINVAL; =20 - *hwirq =3D fwspec->param[1]; + fwspec_irq_type =3D fwspec->param[0]; + + if (fwspec->param[0] !=3D hwirq_type) + return -EINVAL; + + *hwirq =3D fwspec->param[1]; + hwirq_trigger =3D fwspec->param[2]; + } + + if (is_fwnode_irqchip(fwspec->fwnode)) { + + if (fwspec->param_count !=3D 2) + return -EINVAL; + + fwspec_irq_type =3D FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]); + + if (fwspec_irq_type !=3D hwirq_type) + return -EINVAL; + + *hwirq =3D FIELD_GET(GICV5_HWIRQ_ID, fwspec->param[0]); + hwirq_trigger =3D fwspec->param[1]; + } =20 switch (hwirq_type) { case GICV5_HWIRQ_TYPE_PPI: @@ -600,7 +620,7 @@ static __always_inline int gicv5_irq_domain_translate(s= truct irq_domain *d, IRQ_TYPE_EDGE_RISING; break; case GICV5_HWIRQ_TYPE_SPI: - *type =3D fwspec->param[2] & IRQ_TYPE_SENSE_MASK; + *type =3D hwirq_trigger & IRQ_TYPE_SENSE_MASK; break; default: BUILD_BUG_ON(1); @@ -660,10 +680,18 @@ static void gicv5_irq_domain_free(struct irq_domain *= domain, unsigned int virq, static int gicv5_irq_ppi_domain_select(struct irq_domain *d, struct irq_fw= spec *fwspec, enum irq_domain_bus_token bus_token) { + u32 hwirq_type; + if (fwspec->fwnode !=3D d->fwnode) return 0; =20 - if (fwspec->param[0] !=3D GICV5_HWIRQ_TYPE_PPI) + if (is_of_node(fwspec->fwnode)) + hwirq_type =3D fwspec->param[0]; + + if (is_fwnode_irqchip(fwspec->fwnode)) + hwirq_type =3D FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]); + + if (hwirq_type !=3D GICV5_HWIRQ_TYPE_PPI) return 0; =20 return (d =3D=3D gicv5_global_data.ppi_domain); @@ -718,10 +746,18 @@ static int gicv5_irq_spi_domain_alloc(struct irq_doma= in *domain, unsigned int vi static int gicv5_irq_spi_domain_select(struct irq_domain *d, struct irq_fw= spec *fwspec, enum irq_domain_bus_token bus_token) { + u32 hwirq_type; + if (fwspec->fwnode !=3D d->fwnode) return 0; =20 - if (fwspec->param[0] !=3D GICV5_HWIRQ_TYPE_SPI) + if (is_of_node(fwspec->fwnode)) + hwirq_type =3D fwspec->param[0]; + + if (is_fwnode_irqchip(fwspec->fwnode)) + hwirq_type =3D FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]); + + if (hwirq_type !=3D GICV5_HWIRQ_TYPE_SPI) return 0; =20 return (d =3D=3D gicv5_global_data.spi_domain); @@ -1082,16 +1118,12 @@ static inline void __init gic_of_setup_kvm_info(str= uct device_node *node) } #endif // CONFIG_KVM =20 -static int __init gicv5_of_init(struct device_node *node, struct device_no= de *parent) +static int __init gicv5_init_common(struct fwnode_handle *parent_domain) { - int ret =3D gicv5_irs_of_probe(node); + int ret =3D gicv5_init_domains(parent_domain); if (ret) return ret; =20 - ret =3D gicv5_init_domains(of_fwnode_handle(node)); - if (ret) - goto out_irs; - gicv5_set_cpuif_pribits(); gicv5_set_cpuif_idbits(); =20 @@ -1113,18 +1145,82 @@ static int __init gicv5_of_init(struct device_node = *node, struct device_node *pa gicv5_smp_init(); =20 gicv5_irs_its_probe(); - - gic_of_setup_kvm_info(node); - return 0; =20 out_int: gicv5_cpu_disable_interrupts(); out_dom: gicv5_free_domains(); + return ret; +} + +static int __init gicv5_of_init(struct device_node *node, struct device_no= de *parent) +{ + int ret =3D gicv5_irs_of_probe(node); + if (ret) + return ret; + + ret =3D gicv5_init_common(of_fwnode_handle(node)); + if (ret) + goto out_irs; + + gic_of_setup_kvm_info(node); + + return 0; out_irs: gicv5_irs_remove(); =20 return ret; } IRQCHIP_DECLARE(gic_v5, "arm,gic-v5", gicv5_of_init); + +#ifdef CONFIG_ACPI +static bool __init acpi_validate_gic_table(struct acpi_subtable_header *he= ader, + struct acpi_probe_entry *ape) +{ + struct acpi_madt_gicv5_irs *irs =3D (struct acpi_madt_gicv5_irs *)header; + + return (irs->version =3D=3D ape->driver_data); +} + +static struct fwnode_handle *gsi_domain_handle; + +static struct fwnode_handle *gic_v5_get_gsi_domain_id(u32 gsi) +{ + return gsi_domain_handle; +} + +static int __init gic_acpi_init(union acpi_subtable_headers *header, const= unsigned long end) +{ + struct acpi_madt_gicv5_irs *irs =3D (struct acpi_madt_gicv5_irs *)header; + int ret; + + if (gsi_domain_handle) + return 0; + + gsi_domain_handle =3D irq_domain_alloc_fwnode(&irs->config_base_address); + if (!gsi_domain_handle) + return -ENOMEM; + + ret =3D gicv5_irs_acpi_probe(); + if (ret) + goto out_fwnode; + + ret =3D gicv5_init_common(gsi_domain_handle); + if (ret) + goto out_irs; + + acpi_set_irq_model(ACPI_IRQ_MODEL_GIC_V5, gic_v5_get_gsi_domain_id); + + return 0; + +out_irs: + gicv5_irs_remove(); +out_fwnode: + irq_domain_free_fwnode(gsi_domain_handle); + return ret; +} +IRQCHIP_ACPI_DECLARE(gic_v5, ACPI_MADT_TYPE_GICV5_IRS, + acpi_validate_gic_table, ACPI_MADT_GIC_VERSION_V5, + gic_acpi_init); +#endif diff --git a/include/linux/acpi.h b/include/linux/acpi.h index fbf0c3a65f59..3a412dcebc29 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -107,6 +107,7 @@ enum acpi_irq_model_id { ACPI_IRQ_MODEL_IOSAPIC, ACPI_IRQ_MODEL_PLATFORM, ACPI_IRQ_MODEL_GIC, + ACPI_IRQ_MODEL_GIC_V5, ACPI_IRQ_MODEL_LPIC, ACPI_IRQ_MODEL_RINTC, ACPI_IRQ_MODEL_COUNT diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm= -gic-v5.h index 68ddcdb1cec5..ff5b1a4931d7 100644 --- a/include/linux/irqchip/arm-gic-v5.h +++ b/include/linux/irqchip/arm-gic-v5.h @@ -344,6 +344,7 @@ void __init gicv5_init_lpi_domain(void); void __init gicv5_free_lpi_domain(void); =20 int gicv5_irs_of_probe(struct device_node *parent); +int gicv5_irs_acpi_probe(void); void gicv5_irs_remove(void); int gicv5_irs_enable(void); void gicv5_irs_its_probe(void); --=20 2.50.1 From nobody Sun Feb 8 15:25:10 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 19D8735F8DA; Thu, 15 Jan 2026 09:51:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768470692; cv=none; b=WPdD8whkYBtDOUBAMyChyhGf+pLSTc6otwWPpp8Ix++UDh9YzsyUF6mZSk3GHuhv1S1rSSQ5L6UJKDun/MLZ04eUyAMVzjVtr+LwNzAr8Pc/1o0SeSiR94gHEygnR63UEXpzaKQiE/h9EpzCmBj/CzKna6cwhbKmDX5A6tYlyJw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768470692; c=relaxed/simple; bh=SivqKgXw7pxzLdkxoHg1alNOYwKdrnDTSYrk0OF0ZaE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ch+R5FgBKqEiHq3L4SVYVHSL6Xc2DKBU1HvbrGYGP6Xq97T61q0PtNRdEMlsyGRXT5tiHaVjqxuHPkxx4n21zhYvrUjTnOU4nOlUTG6zRZYd8B/jNfPNpaUCs3Z8SldHgZ1ZDBpela1k+X7JUlA7mt9/yCJSzfS3X1yLYiMyP1g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=imhjIHig; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="imhjIHig" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C167BC116D0; Thu, 15 Jan 2026 09:51:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768470688; bh=SivqKgXw7pxzLdkxoHg1alNOYwKdrnDTSYrk0OF0ZaE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=imhjIHig+KihtVH76ExVagwxmSs1aA5Z6eunysMTGy66Ew/SrCJ8cs+P/PdnusvZn KEGq2GLR3MpDrCzoVOi7jBVkhWhh/3dcB40kf4JTlmFPtzUzyqOduTaNcZKqcuXGrb LDVEaCQ6wKsz8uIKEg078WfZTtYeUvTAv5HOKzuP4ssF8MIJKZHB+dk8h40UpCKPTa 7XkKK5zANQMIkVtg+JYkYpQ/x8PCB2bx8t/GwdH2p0S4uZhov9co1vr6VGOphTq6xH Q+xX00iBAM4kocFQWeYZNW8R7kggEnn+mIwdM7r3KNFBffWeaY9MJV1rkas1HGve7B Y0z4/84irD2HQ== From: Lorenzo Pieralisi Date: Thu, 15 Jan 2026 10:50:51 +0100 Subject: [PATCH v3 5/6] irqchip/gic-v5: Add ACPI ITS probing Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260115-gicv5-host-acpi-v3-5-c13a9a150388@kernel.org> References: <20260115-gicv5-host-acpi-v3-0-c13a9a150388@kernel.org> In-Reply-To: <20260115-gicv5-host-acpi-v3-0-c13a9a150388@kernel.org> To: "Rafael J. Wysocki" , Len Brown , Robert Moore , Hanjun Guo , Sudeep Holla , Marc Zyngier , Bjorn Helgaas , Thomas Gleixner , Jonathan Cameron Cc: linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org, Lorenzo Pieralisi X-Mailer: b4 0.14.3 On ACPI ARM64 systems the GICv5 ITS configuration and translate frames are described in the MADT table. Refactor the current GICv5 ITS driver code to share common functions between ACPI and OF and implement ACPI probing in the GICv5 ITS driver. Add iort_msi_xlate() to map a device ID and retrieve an MSI controller fwnode node for ACPI systems and update pci_msi_map_rid_ctlr_node() to use it in its ACPI code path. Add the required functions to IORT code for deviceID retrieval and IRQ domain registration and look-up so that the GICv5 ITS driver in an ACPI based system can be successfully probed. Signed-off-by: Lorenzo Pieralisi Reviewed-by: Jonathan Cameron Cc: Thomas Gleixner Cc: Hanjun Guo Cc: Sudeep Holla Cc: Marc Zyngier Acked-by: Thomas Gleixner --- drivers/acpi/arm64/iort.c | 98 ++++++++++++++++++----- drivers/irqchip/irq-gic-its-msi-parent.c | 39 ++++----- drivers/irqchip/irq-gic-v5-irs.c | 7 +- drivers/irqchip/irq-gic-v5-its.c | 132 +++++++++++++++++++++++++++= +++- drivers/pci/msi/irqdomain.c | 2 + include/linux/acpi_iort.h | 10 ++- include/linux/irqchip/arm-gic-v5.h | 1 + 7 files changed, 244 insertions(+), 45 deletions(-) diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c index 65f0f56ad753..ddd857f05f46 100644 --- a/drivers/acpi/arm64/iort.c +++ b/drivers/acpi/arm64/iort.c @@ -595,45 +595,45 @@ u32 iort_msi_map_id(struct device *dev, u32 input_id) } =20 /** - * iort_pmsi_get_dev_id() - Get the device id for a device + * iort_msi_xlate() - Map a MSI input ID for a device * @dev: The device for which the mapping is to be done. - * @dev_id: The device ID found. + * @input_id: The device input ID. + * @fwnode: Pointer to store the fwnode. * - * Returns: 0 for successful find a dev id, -ENODEV on error + * Returns: mapped MSI ID on success, input ID otherwise + * On success, the fwnode pointer is initialized to the MSI + * controller fwnode handle. */ -int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) +u32 iort_msi_xlate(struct device *dev, u32 input_id, struct fwnode_handle = **fwnode) { - int i, index; + struct acpi_iort_its_group *its; struct acpi_iort_node *node; + u32 dev_id; =20 node =3D iort_find_dev_node(dev); if (!node) - return -ENODEV; + return input_id; =20 - index =3D iort_get_id_mapping_index(node); - /* if there is a valid index, go get the dev_id directly */ - if (index >=3D 0) { - if (iort_node_get_id(node, dev_id, index)) - return 0; - } else { - for (i =3D 0; i < node->mapping_count; i++) { - if (iort_node_map_platform_id(node, dev_id, - IORT_MSI_TYPE, i)) - return 0; - } - } + node =3D iort_node_map_id(node, input_id, &dev_id, IORT_MSI_TYPE); + if (!node) + return input_id; =20 - return -ENODEV; + /* Move to ITS specific data */ + its =3D (struct acpi_iort_its_group *)node->node_data; + + *fwnode =3D iort_find_domain_token(its->identifiers[0]); + + return dev_id; } =20 -static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base) +int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base) { struct iort_its_msi_chip *its_msi_chip; int ret =3D -ENODEV; =20 spin_lock(&iort_msi_chip_lock); list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { - if (its_msi_chip->translation_id =3D=3D its_id) { + if (its_msi_chip->fw_node =3D=3D node) { *base =3D its_msi_chip->base_addr; ret =3D 0; break; @@ -644,6 +644,62 @@ static int __maybe_unused iort_find_its_base(u32 its_i= d, phys_addr_t *base) return ret; } =20 +static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base) +{ + struct fwnode_handle *fwnode =3D iort_find_domain_token(its_id); + + if (!fwnode) + return -ENODEV; + + return iort_its_translate_pa(fwnode, base); +} + +/** + * iort_pmsi_get_msi_info() - Get the device id and translate frame PA for= a device + * @dev: The device for which the mapping is to be done. + * @dev_id: The device ID found. + * @pa: optional pointer to store translate frame address. + * + * Returns: 0 for successful devid and pa retrieval, -ENODEV on error + */ +int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *p= a) +{ + struct acpi_iort_node *node, *parent =3D NULL; + struct acpi_iort_its_group *its; + int i, index; + + node =3D iort_find_dev_node(dev); + if (!node) + return -ENODEV; + + index =3D iort_get_id_mapping_index(node); + /* if there is a valid index, go get the dev_id directly */ + if (index >=3D 0) { + parent =3D iort_node_get_id(node, dev_id, index); + } else { + for (i =3D 0; i < node->mapping_count; i++) { + parent =3D iort_node_map_platform_id(node, dev_id, + IORT_MSI_TYPE, i); + if (parent) + break; + } + } + + if (!parent) + return -ENODEV; + + if (pa) { + int ret; + + its =3D (struct acpi_iort_its_group *)node->node_data; + ret =3D iort_find_its_base(its->identifiers[0], pa); + if (ret) + return ret; + } + + return 0; +} + /** * iort_dev_find_its_id() - Find the ITS identifier for a device * @dev: The device. diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq= -gic-its-msi-parent.c index 4d1ad1ee005d..a832cdb2e697 100644 --- a/drivers/irqchip/irq-gic-its-msi-parent.c +++ b/drivers/irqchip/irq-gic-its-msi-parent.c @@ -19,18 +19,24 @@ MSI_FLAG_PCI_MSIX | \ MSI_FLAG_MULTI_PCI_MSI) =20 -static int its_translate_frame_address(struct device_node *msi_node, phys_= addr_t *pa) +static int its_translate_frame_address(struct fwnode_handle *msi_node, phy= s_addr_t *pa) { struct resource res; int ret; =20 - ret =3D of_property_match_string(msi_node, "reg-names", "ns-translate"); - if (ret < 0) - return ret; + if (is_of_node(msi_node)) { + struct device_node *msi_np =3D to_of_node(msi_node); =20 - ret =3D of_address_to_resource(msi_node, ret, &res); - if (ret) - return ret; + ret =3D of_property_match_string(msi_np, "reg-names", "ns-translate"); + if (ret < 0) + return ret; + + ret =3D of_address_to_resource(msi_np, ret, &res); + if (ret) + return ret; + } else { + ret =3D iort_its_translate_pa(msi_node, &res.start); + } =20 *pa =3D res.start; return 0; @@ -120,7 +126,7 @@ static int its_v5_pci_msi_prepare(struct irq_domain *do= main, struct device *dev, if (!msi_node) return -ENODEV; =20 - ret =3D its_translate_frame_address(to_of_node(msi_node), &pa); + ret =3D its_translate_frame_address(msi_node, &pa); if (ret) return -ENODEV; =20 @@ -161,7 +167,7 @@ static int of_pmsi_get_msi_info(struct irq_domain *doma= in, struct device *dev, u ret =3D -EINVAL; =20 if (!ret && pa) - ret =3D its_translate_frame_address(it.node, pa); + ret =3D its_translate_frame_address(of_fwnode_handle(it.node), pa); =20 if (!ret) *dev_id =3D args; @@ -176,11 +182,6 @@ static int of_pmsi_get_msi_info(struct irq_domain *dom= ain, struct device *dev, u return of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &msi_c= trl, dev_id); } =20 -int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) -{ - return -1; -} - static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, int nvec, msi_alloc_info_t *info) { @@ -191,7 +192,7 @@ static int its_pmsi_prepare(struct irq_domain *domain, = struct device *dev, if (dev->of_node) ret =3D of_pmsi_get_msi_info(domain->parent, dev, &dev_id, NULL); else - ret =3D iort_pmsi_get_dev_id(dev, &dev_id); + ret =3D iort_pmsi_get_msi_info(dev, &dev_id, NULL); if (ret) return ret; =20 @@ -214,10 +215,10 @@ static int its_v5_pmsi_prepare(struct irq_domain *dom= ain, struct device *dev, u32 dev_id; int ret; =20 - if (!dev->of_node) - return -ENODEV; - - ret =3D of_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa); + if (dev->of_node) + ret =3D of_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa); + else + ret =3D iort_pmsi_get_msi_info(dev, &dev_id, &pa); if (ret) return ret; =20 diff --git a/drivers/irqchip/irq-gic-v5-irs.c b/drivers/irqchip/irq-gic-v5-= irs.c index a27a01f9e7a2..fcd032b5e2fa 100644 --- a/drivers/irqchip/irq-gic-v5-irs.c +++ b/drivers/irqchip/irq-gic-v5-irs.c @@ -814,8 +814,11 @@ void __init gicv5_irs_its_probe(void) { struct gicv5_irs_chip_data *irs_data; =20 - list_for_each_entry(irs_data, &irs_nodes, entry) - gicv5_its_of_probe(to_of_node(irs_data->fwnode)); + if (acpi_disabled) + list_for_each_entry(irs_data, &irs_nodes, entry) + gicv5_its_of_probe(to_of_node(irs_data->fwnode)); + else + gicv5_its_acpi_probe(); } =20 int __init gicv5_irs_of_probe(struct device_node *parent) diff --git a/drivers/irqchip/irq-gic-v5-its.c b/drivers/irqchip/irq-gic-v5-= its.c index 8e22134b9f48..e24ce3d9fb62 100644 --- a/drivers/irqchip/irq-gic-v5-its.c +++ b/drivers/irqchip/irq-gic-v5-its.c @@ -5,6 +5,8 @@ =20 #define pr_fmt(fmt) "GICv5 ITS: " fmt =20 +#include +#include #include #include #include @@ -1115,7 +1117,7 @@ static int gicv5_its_init_domain(struct gicv5_its_chi= p_data *its, struct irq_dom } =20 static int __init gicv5_its_init_bases(void __iomem *its_base, struct fwno= de_handle *handle, - struct irq_domain *parent_domain) + struct irq_domain *parent_domain, bool noncoherent) { struct device_node *np =3D to_of_node(handle); struct gicv5_its_chip_data *its_node; @@ -1208,7 +1210,8 @@ static int __init gicv5_its_init(struct device_node *= node) } =20 ret =3D gicv5_its_init_bases(its_base, of_fwnode_handle(node), - gicv5_global_data.lpi_domain); + gicv5_global_data.lpi_domain, + of_property_read_bool(node, "dma-noncoherent")); if (ret) goto out_unmap; =20 @@ -1231,3 +1234,128 @@ void __init gicv5_its_of_probe(struct device_node *= parent) pr_err("Failed to init ITS %s\n", np->full_name); } } + +#ifdef CONFIG_ACPI + +#define ACPI_GICV5_ITS_MEM_SIZE (SZ_64K) + +static struct acpi_madt_gicv5_translator *current_its_entry __initdata; +static struct fwnode_handle *current_its_fwnode __initdata; + +static int __init gic_acpi_parse_madt_its_translate(union acpi_subtable_he= aders *header, + const unsigned long end) +{ + struct acpi_madt_gicv5_translate_frame *its_frame; + struct fwnode_handle *msi_dom_handle; + struct resource res =3D {}; + int err; + + its_frame =3D (struct acpi_madt_gicv5_translate_frame *)header; + if (its_frame->linked_translator_id !=3D current_its_entry->translator_id) + return 0; + + res.start =3D its_frame->base_address; + res.end =3D its_frame->base_address + ACPI_GICV5_ITS_MEM_SIZE - 1; + res.flags =3D IORESOURCE_MEM; + + msi_dom_handle =3D irq_domain_alloc_parented_fwnode(&res.start, current_i= ts_fwnode); + if (!msi_dom_handle) { + pr_err("ITS@%pa: Unable to allocate GICv5 ITS translate domain token\n", + &res.start); + return -ENOMEM; + } + + err =3D iort_register_domain_token(its_frame->translate_frame_id, res.sta= rt, + msi_dom_handle); + if (err) { + pr_err("ITS@%pa: Unable to register GICv5 ITS domain token (ITS TRANSLAT= E FRAME ID %d) to IORT\n", + &res.start, its_frame->translate_frame_id); + irq_domain_free_fwnode(msi_dom_handle); + return err; + } + + return 0; +} + +static int __init gic_acpi_free_madt_its_translate(union acpi_subtable_hea= ders *header, + const unsigned long end) +{ + struct acpi_madt_gicv5_translate_frame *its_frame; + struct fwnode_handle *msi_dom_handle; + + its_frame =3D (struct acpi_madt_gicv5_translate_frame *)header; + if (its_frame->linked_translator_id !=3D current_its_entry->translator_id) + return 0; + + msi_dom_handle =3D iort_find_domain_token(its_frame->translate_frame_id); + if (!msi_dom_handle) + return 0; + + iort_deregister_domain_token(its_frame->translate_frame_id); + irq_domain_free_fwnode(msi_dom_handle); + + return 0; +} + +static int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *hea= der, + const unsigned long end) +{ + struct acpi_madt_gicv5_translator *its_entry; + struct fwnode_handle *dom_handle; + struct resource res =3D {}; + void __iomem *its_base; + int err; + + its_entry =3D (struct acpi_madt_gicv5_translator *)header; + res.start =3D its_entry->base_address; + res.end =3D its_entry->base_address + ACPI_GICV5_ITS_MEM_SIZE - 1; + res.flags =3D IORESOURCE_MEM; + + if (!request_mem_region(res.start, resource_size(&res), "GICv5 ITS")) + return -EBUSY; + + dom_handle =3D irq_domain_alloc_fwnode(&res.start); + if (!dom_handle) { + pr_err("ITS@%pa: Unable to allocate GICv5 ITS domain token\n", + &res.start); + err =3D -ENOMEM; + goto out_rel_res; + } + + current_its_entry =3D its_entry; + current_its_fwnode =3D dom_handle; + + acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE, + gic_acpi_parse_madt_its_translate, 0); + + its_base =3D ioremap(res.start, ACPI_GICV5_ITS_MEM_SIZE); + if (!its_base) { + err =3D -ENOMEM; + goto out_unregister; + } + + err =3D gicv5_its_init_bases(its_base, dom_handle, gicv5_global_data.lpi_= domain, + its_entry->flags & ACPI_MADT_GICV5_ITS_NON_COHERENT); + if (err) + goto out_unmap; + + return 0; + +out_unmap: + iounmap(its_base); +out_unregister: + acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE, + gic_acpi_free_madt_its_translate, 0); + irq_domain_free_fwnode(dom_handle); +out_rel_res: + release_mem_region(res.start, resource_size(&res)); + return err; +} + +void __init gicv5_its_acpi_probe(void) +{ + acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS, gic_acpi_parse_madt_its, = 0); +} +#else +void __init gicv5_its_acpi_probe(void) { } +#endif diff --git a/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c index 3a4038640fda..6e65f0f44112 100644 --- a/drivers/pci/msi/irqdomain.c +++ b/drivers/pci/msi/irqdomain.c @@ -401,6 +401,8 @@ u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain= , struct pci_dev *pdev, rid =3D of_msi_xlate(&pdev->dev, &msi_ctlr_node, rid); if (msi_ctlr_node) *node =3D of_fwnode_handle(msi_ctlr_node); + } else { + rid =3D iort_msi_xlate(&pdev->dev, rid, node); } =20 return rid; diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h index d4ed5622cf2b..2d22268677a9 100644 --- a/include/linux/acpi_iort.h +++ b/include/linux/acpi_iort.h @@ -27,12 +27,14 @@ int iort_register_domain_token(int trans_id, phys_addr_= t base, struct fwnode_handle *fw_node); void iort_deregister_domain_token(int trans_id); struct fwnode_handle *iort_find_domain_token(int trans_id); -int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id); =20 #ifdef CONFIG_ACPI_IORT u32 iort_msi_map_id(struct device *dev, u32 id); +u32 iort_msi_xlate(struct device *dev, u32 id, struct fwnode_handle **node= ); +int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base); struct irq_domain *iort_get_device_domain(struct device *dev, u32 id, enum irq_domain_bus_token bus_token); +int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *p= a); void acpi_configure_pmsi_domain(struct device *dev); void iort_get_rmr_sids(struct fwnode_handle *iommu_fwnode, struct list_head *head); @@ -46,9 +48,15 @@ phys_addr_t acpi_iort_dma_get_max_cpu_address(void); #else static inline u32 iort_msi_map_id(struct device *dev, u32 id) { return id; } +static inline u32 iort_msi_xlate(struct device *dev, u32 id, struct fwnode= _handle **node) +{ return id; } +static inline int iort_its_translate_pa(struct fwnode_handle *node, phys_a= ddr_t *base) +{ return -ENODEV; } static inline struct irq_domain *iort_get_device_domain( struct device *dev, u32 id, enum irq_domain_bus_token bus_token) { return NULL; } +static inline int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, = phys_addr_t *pa) +{ return -ENODEV; } static inline void acpi_configure_pmsi_domain(struct device *dev) { } static inline void iort_get_rmr_sids(struct fwnode_handle *iommu_fwnode, struct list_hea= d *head) { } diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm= -gic-v5.h index ff5b1a4931d7..334b6986435c 100644 --- a/include/linux/irqchip/arm-gic-v5.h +++ b/include/linux/irqchip/arm-gic-v5.h @@ -392,4 +392,5 @@ int gicv5_alloc_lpi(void); void gicv5_free_lpi(u32 lpi); =20 void __init gicv5_its_of_probe(struct device_node *parent); +void __init gicv5_its_acpi_probe(void); #endif --=20 2.50.1 From nobody Sun Feb 8 15:25:10 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 19CF735F8D6; Thu, 15 Jan 2026 09:51:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768470692; cv=none; b=WD6qaRYSO72QtKPwImaXh40gyKUFmxa2IMx6Oh+088slKbfZfn6ubk7oFp+ST9XaScSlTuLVBWsB7GFqIiPC2nVq71NyweHC4xp9JwqAow3Lp+DujbumgF85Qw5f/QJUkiszMJKJ+DX0ZMUjg+vwJnuHPSb0QjFjZLjILAn2bow= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768470692; c=relaxed/simple; bh=w/EtsqaZJ4DVWRdqOwQiLr5EDjpnoPNOQ5mW3z9Rnpk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=JqtxrHhRLva7ACPbiJcvAirWD3hS/aMvZd+29BudPgCOTt9KyL7oE7jazlZkCQBMqu5B9zxVyBQ6xfaW6C6Ups6ARc1Z4l5ro/8DVelUczFChnxLmCo3qqnBltH7/u9N8g6Ej6G+VTa289rZQqrbdyRuDRtcDCLTCS70+aJW5G8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=H8XBnMfv; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="H8XBnMfv" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 75D0BC19423; Thu, 15 Jan 2026 09:51:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768470691; bh=w/EtsqaZJ4DVWRdqOwQiLr5EDjpnoPNOQ5mW3z9Rnpk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=H8XBnMfv4iG1vmKk7gVrjnSpgDiIwzX80k+Yrn+I7G6Hgc0ZgrMwtrP3p2cdANRZ6 z9xVn/u1Z+LgZbgS+zUW3dkLcGnuFsogdttZmrYkdwEKCYF8XRAS4vXutYbYZJvG3H BDGVwkrP6GU5TZirixjWRgU8nKVVT6NZZhJ0R5iMXOwhMO23TAIBfiw6CxmW+JWRux RRb+PI5HFwEB679/MaNLeQx/7w0wfCSPF+WTt8IcWdagTcs5ckbW0rwMttINdIICLq oqX9qgIQgxyaYUkMI0b3CfuLFpo3hoKmg5Uqn4FhEwuHH2L/fpPB0euJg4xMZbA2Hj aCj2Jca4FIl9Q== From: Lorenzo Pieralisi Date: Thu, 15 Jan 2026 10:50:52 +0100 Subject: [PATCH v3 6/6] irqchip/gic-v5: Add ACPI IWB probing Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260115-gicv5-host-acpi-v3-6-c13a9a150388@kernel.org> References: <20260115-gicv5-host-acpi-v3-0-c13a9a150388@kernel.org> In-Reply-To: <20260115-gicv5-host-acpi-v3-0-c13a9a150388@kernel.org> To: "Rafael J. Wysocki" , Len Brown , Robert Moore , Hanjun Guo , Sudeep Holla , Marc Zyngier , Bjorn Helgaas , Thomas Gleixner , Jonathan Cameron Cc: linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org, Lorenzo Pieralisi X-Mailer: b4 0.14.3 To probe an IWB in an ACPI based system it is required: - to implement the IORT functions handling the IWB IORT node and create functions to retrieve IWB firmware information - to augment the driver to match the DSDT ACPI "ARMH0003" device and retrieve the IWB wire and trigger mask from the GSI interrupt descriptor in the IWB msi_domain_ops.msi_translate() function Make the required driver changes to enable IWB probing in ACPI systems. The GICv5 GSI format requires special handling for IWB routed IRQs. Add IWB GSI detection to the top level driver gic_v5_get_gsi_domain_id() function so that the correct IRQ domain for a GSI can be detected by parsing the GSI and check whether it is an IWB-backed IRQ or not. Signed-off-by: Lorenzo Pieralisi Reviewed-by: Jonathan Cameron Cc: Thomas Gleixner Cc: Hanjun Guo Cc: Sudeep Holla Cc: Marc Zyngier Acked-by: Thomas Gleixner --- drivers/acpi/arm64/iort.c | 95 ++++++++++++++++++++++++++++++++--= ---- drivers/irqchip/irq-gic-v5-iwb.c | 42 +++++++++++++---- drivers/irqchip/irq-gic-v5.c | 4 ++ include/linux/acpi_iort.h | 1 + include/linux/irqchip/arm-gic-v5.h | 6 +++ 5 files changed, 123 insertions(+), 25 deletions(-) diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c index ddd857f05f46..ed827b2fc437 100644 --- a/drivers/acpi/arm64/iort.c +++ b/drivers/acpi/arm64/iort.c @@ -264,39 +264,47 @@ static acpi_status iort_match_node_callback(struct ac= pi_iort_node *node, struct device *dev =3D context; acpi_status status =3D AE_NOT_FOUND; =20 - if (node->type =3D=3D ACPI_IORT_NODE_NAMED_COMPONENT) { + if (node->type =3D=3D ACPI_IORT_NODE_NAMED_COMPONENT || + node->type =3D=3D ACPI_IORT_NODE_IWB) { struct acpi_buffer buf =3D { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_device *adev; struct acpi_iort_named_component *ncomp; - struct device *nc_dev =3D dev; + struct acpi_iort_iwb *iwb; + struct device *cdev =3D dev; + struct acpi_device *adev; + const char *device_name; =20 /* * Walk the device tree to find a device with an * ACPI companion; there is no point in scanning - * IORT for a device matching a named component if + * IORT for a device matching a named component or IWB if * the device does not have an ACPI companion to * start with. */ do { - adev =3D ACPI_COMPANION(nc_dev); + adev =3D ACPI_COMPANION(cdev); if (adev) break; =20 - nc_dev =3D nc_dev->parent; - } while (nc_dev); + cdev =3D cdev->parent; + } while (cdev); =20 if (!adev) goto out; =20 status =3D acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf); if (ACPI_FAILURE(status)) { - dev_warn(nc_dev, "Can't get device full path name\n"); + dev_warn(cdev, "Can't get device full path name\n"); goto out; } =20 - ncomp =3D (struct acpi_iort_named_component *)node->node_data; - status =3D !strcmp(ncomp->device_name, buf.pointer) ? - AE_OK : AE_NOT_FOUND; + if (node->type =3D=3D ACPI_IORT_NODE_NAMED_COMPONENT) { + ncomp =3D (struct acpi_iort_named_component *)node->node_data; + device_name =3D ncomp->device_name; + } else { + iwb =3D (struct acpi_iort_iwb *)node->node_data; + device_name =3D iwb->device_name; + } + status =3D !strcmp(device_name, buf.pointer) ? AE_OK : AE_NOT_FOUND; acpi_os_free(buf.pointer); } else if (node->type =3D=3D ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { struct acpi_iort_root_complex *pci_rc; @@ -317,12 +325,28 @@ static acpi_status iort_match_node_callback(struct ac= pi_iort_node *node, return status; } =20 +static acpi_status iort_match_iwb_callback(struct acpi_iort_node *node, vo= id *context) +{ + struct acpi_iort_iwb *iwb; + u32 *id =3D context; + + if (node->type !=3D ACPI_IORT_NODE_IWB) + return AE_NOT_FOUND; + + iwb =3D (struct acpi_iort_iwb *)node->node_data; + if (iwb->iwb_index !=3D *id) + return AE_NOT_FOUND; + + return AE_OK; +} + static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_= in, u32 *rid_out, bool check_overlap) { /* Single mapping does not care for input id */ if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { if (type =3D=3D ACPI_IORT_NODE_NAMED_COMPONENT || + type =3D=3D ACPI_IORT_NODE_IWB || type =3D=3D ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { *rid_out =3D map->output_base; return 0; @@ -392,6 +416,7 @@ static struct acpi_iort_node *iort_node_get_id(struct a= cpi_iort_node *node, =20 if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { if (node->type =3D=3D ACPI_IORT_NODE_NAMED_COMPONENT || + node->type =3D=3D ACPI_IORT_NODE_IWB || node->type =3D=3D ACPI_IORT_NODE_PCI_ROOT_COMPLEX || node->type =3D=3D ACPI_IORT_NODE_SMMU_V3 || node->type =3D=3D ACPI_IORT_NODE_PMCG) { @@ -562,9 +587,14 @@ static struct acpi_iort_node *iort_find_dev_node(struc= t device *dev) return node; /* * if not, then it should be a platform device defined in - * DSDT/SSDT (with Named Component node in IORT) + * DSDT/SSDT (with Named Component node in IORT) or an + * IWB device in the DSDT/SSDT. */ - return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, + node =3D iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, + iort_match_node_callback, dev); + if (node) + return node; + return iort_scan_node(ACPI_IORT_NODE_IWB, iort_match_node_callback, dev); } =20 @@ -759,6 +789,35 @@ struct irq_domain *iort_get_device_domain(struct devic= e *dev, u32 id, return irq_find_matching_fwnode(handle, bus_token); } =20 +struct fwnode_handle *iort_iwb_handle(u32 iwb_id) +{ + struct fwnode_handle *fwnode; + struct acpi_iort_node *node; + struct acpi_device *device; + struct acpi_iort_iwb *iwb; + acpi_status status; + acpi_handle handle; + + /* find its associated IWB node */ + node =3D iort_scan_node(ACPI_IORT_NODE_IWB, iort_match_iwb_callback, &iwb= _id); + if (!node) + return NULL; + + iwb =3D (struct acpi_iort_iwb *)node->node_data; + status =3D acpi_get_handle(NULL, iwb->device_name, &handle); + if (ACPI_FAILURE(status)) + return NULL; + + device =3D acpi_get_acpi_dev(handle); + if (!device) + return NULL; + + fwnode =3D acpi_fwnode_handle(device); + acpi_put_acpi_dev(device); + + return fwnode; +} + static void iort_set_device_domain(struct device *dev, struct acpi_iort_node *node) { @@ -819,8 +878,14 @@ static struct irq_domain *iort_get_platform_device_dom= ain(struct device *dev) /* find its associated iort node */ node =3D iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, iort_match_node_callback, dev); - if (!node) - return NULL; + if (!node) { + /* find its associated iort node */ + node =3D iort_scan_node(ACPI_IORT_NODE_IWB, + iort_match_node_callback, dev); + + if (!node) + return NULL; + } =20 /* then find its msi parent node */ for (i =3D 0; i < node->mapping_count; i++) { diff --git a/drivers/irqchip/irq-gic-v5-iwb.c b/drivers/irqchip/irq-gic-v5-= iwb.c index ad9fdc14d1c6..c7d5fd34d053 100644 --- a/drivers/irqchip/irq-gic-v5-iwb.c +++ b/drivers/irqchip/irq-gic-v5-iwb.c @@ -4,6 +4,7 @@ */ #define pr_fmt(fmt) "GICv5 IWB: " fmt =20 +#include #include #include #include @@ -136,18 +137,31 @@ static int gicv5_iwb_irq_domain_translate(struct irq_= domain *d, struct irq_fwspe irq_hw_number_t *hwirq, unsigned int *type) { - if (!is_of_node(fwspec->fwnode)) - return -EINVAL; + if (is_of_node(fwspec->fwnode)) { =20 - if (fwspec->param_count < 2) - return -EINVAL; + if (fwspec->param_count < 2) + return -EINVAL; =20 - /* - * param[0] is be the wire - * param[1] is the interrupt type - */ - *hwirq =3D fwspec->param[0]; - *type =3D fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + /* + * param[0] is be the wire + * param[1] is the interrupt type + */ + *hwirq =3D fwspec->param[0]; + *type =3D fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + } + + if (is_acpi_device_node(fwspec->fwnode)) { + + if (fwspec->param_count < 2) + return -EINVAL; + + /* + * Extract the wire from param[0] + * param[1] is the interrupt type + */ + *hwirq =3D FIELD_GET(GICV5_GSI_IWB_WIRE, fwspec->param[0]); + *type =3D fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + } =20 return 0; } @@ -265,10 +279,18 @@ static const struct of_device_id gicv5_iwb_of_match[]= =3D { }; MODULE_DEVICE_TABLE(of, gicv5_iwb_of_match); =20 +#ifdef CONFIG_ACPI +static const struct acpi_device_id iwb_acpi_match[] =3D { + { "ARMH0003", 0 }, + {} +}; +#endif + static struct platform_driver gicv5_iwb_platform_driver =3D { .driver =3D { .name =3D "GICv5 IWB", .of_match_table =3D gicv5_iwb_of_match, + .acpi_match_table =3D ACPI_PTR(iwb_acpi_match), .suppress_bind_attrs =3D true, }, .probe =3D gicv5_iwb_device_probe, diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c index 23fd551c4347..da867dd2e77d 100644 --- a/drivers/irqchip/irq-gic-v5.c +++ b/drivers/irqchip/irq-gic-v5.c @@ -5,6 +5,7 @@ =20 #define pr_fmt(fmt) "GICv5: " fmt =20 +#include #include #include #include @@ -1187,6 +1188,9 @@ static struct fwnode_handle *gsi_domain_handle; =20 static struct fwnode_handle *gic_v5_get_gsi_domain_id(u32 gsi) { + if (FIELD_GET(GICV5_GSI_IC_TYPE, gsi) =3D=3D GICV5_GSI_IWB_TYPE) + return iort_iwb_handle(FIELD_GET(GICV5_GSI_IWB_FRAME_ID, gsi)); + return gsi_domain_handle; } =20 diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h index 2d22268677a9..17bb3374f4ca 100644 --- a/include/linux/acpi_iort.h +++ b/include/linux/acpi_iort.h @@ -27,6 +27,7 @@ int iort_register_domain_token(int trans_id, phys_addr_t = base, struct fwnode_handle *fw_node); void iort_deregister_domain_token(int trans_id); struct fwnode_handle *iort_find_domain_token(int trans_id); +struct fwnode_handle *iort_iwb_handle(u32 iwb_id); =20 #ifdef CONFIG_ACPI_IORT u32 iort_msi_map_id(struct device *dev, u32 id); diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm= -gic-v5.h index 334b6986435c..3da1ad80fc9d 100644 --- a/include/linux/irqchip/arm-gic-v5.h +++ b/include/linux/irqchip/arm-gic-v5.h @@ -265,6 +265,12 @@ =20 #define GICV5_IWB_WENABLE_STATUSR_IDLE BIT(0) =20 +#define GICV5_GSI_IC_TYPE GENMASK(31, 29) +#define GICV5_GSI_IWB_TYPE 0x7 + +#define GICV5_GSI_IWB_FRAME_ID GENMASK(28, 16) +#define GICV5_GSI_IWB_WIRE GENMASK(15, 0) + /* * Global Data structures and functions */ --=20 2.50.1