From nobody Mon Feb 9 18:45:13 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 2050B33120D for ; Mon, 9 Feb 2026 13:06:30 +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=1770642391; cv=none; b=dVmNT0GinlmtHow4dGf8DRlzTz/6JhKfBBkR9egUQ7zE04dklPT5yaESNPoBqAk4v0evSm1pSAG6GRGLxFnqfKSGMN5nIrfT+4JY1VDLZ9C0HbZiBlGT1mS+/bXovSFMzPo5L7LWTwmKHXLHMcQAyXryskkncr0sxtmw235+Mds= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770642391; c=relaxed/simple; bh=R4xZsnod6DVXpbVMowZj57D8q44lRErBpni+s0G76DM=; h=Date:From:To:Cc:Subject:References:Message-ID:Content-Type: MIME-Version; b=t5nX5I8CPS+1Pv++sZrRLorQOvfoB7mYm3upNCktv8g/y7ffGav0/CnIFVBGEDjOQHmtddeWoYfkDYMmP2/+AEby5ajns532m+ngFC95mgIUVcYcio+C4pf6tulc3rP0lqwj2hUrb70sQ4FNSQwWrTgaKZ5CrOgGf/TjYdCQjEI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=KoKf70vQ; 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="KoKf70vQ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D4626C16AAE; Mon, 9 Feb 2026 13:06:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1770642390; bh=R4xZsnod6DVXpbVMowZj57D8q44lRErBpni+s0G76DM=; h=Date:From:To:Cc:Subject:References:From; b=KoKf70vQ4JML2cZxd5urVUkSI0bU8n1LYaUPVSUC56uENpjqM7t9u3e5eca+AsQRb 3ZKsgiNAoZuOMueWyJESZcZxuDQlJFBKQnkyEr3K++hMIU1h6cYH//ko8UeSwp5yr1 ZNu2C3LLK//JuYoEXiAseKv/b2jV82RaufBmqnUcUMAWdLUtUs0mRR8QawlKPCz16s OLatKQqJQn56jlyygxFUYH54LVmbSObcx2flzd35vQ1HqYkDD0IGYJ+Oq2K1to5HnP sXJC46KihzlyYdFZ1vdw/jafZM9+c3L2UCEy6E49QtiC3naD1KCgAk2QVYq/A42+UE Z2BJj4n3p7mdA== Date: Mon, 09 Feb 2026 14:06:27 +0100 From: Thomas Gleixner To: Linus Torvalds Cc: linux-kernel@vger.kernel.org, x86@kernel.org Subject: [GIT pull] irq/drivers for v7.0-rc1 References: <177064216682.108186.17056273147252170162.tglx@xen13> Message-ID: <177064216995.108186.16651394489153474790.tglx@xen13> Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Linus, please pull the latest irq/drivers branch from: git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq-drivers-20= 26-02-09 up to: 6054b10c3288: irqchip/gic-v5: Fix spelling mistake "ouside" -> "out= side" Updates for interrupt chip drivers: - Add support for the Renesas RZ/V2N SoC - Add a new driver for the Renesas RZ/[TN]2H SoCs - Preserve the register state of the RISCV APLIC interrupt controller acc= ross suspend/resume - Reinitialize the RISCV IMSIC registers after suspend/resume - Make the various Loongson interrupt chip drivers 32/64-bit aware - Handle the number of hardware interrupts in the SIFIVE PLIC driver correctly. The hardware interrupt 0 is reserved which resulted in inconsistent accounting. That went unnoticed as the off by one is only noticable when the number of device interrupts is a multiple of 32. - The usual device tree updates, cleanups and improvements all over the p= lace. Thanks, tglx ------------------> Aniket Limaye (2): dt-bindings: interrupt-controller: ti,sci-intr: Per-line interrupt-ty= pes irqchip/ti-sci-intr: Allow parsing interrupt-types per-line Biju Das (1): irqchip/renesas-rzv2h: Add suspend/resume support Colin Ian King (1): irqchip/gic-v5: Fix spelling mistake "ouside" -> "outside" Cosmin Tanislav (4): dt-bindings: interrupt-controller: Document RZ/{T2H,N2H} ICU irqchip: Add RZ/{T2H,N2H} Interrupt Controller (ICU) driver arm64: dts: renesas: r9a09g077: Add ICU support arm64: dts: renesas: r9a09g087: Add ICU support Huacai Chen (7): irqchip/loongarch-avec: Adjust irqchip driver for 32BIT/64BIT irqchip/loongson-liointc: Adjust irqchip driver for 32BIT/64BIT irqchip/loongson-eiointc: Adjust irqchip driver for 32BIT/64BIT irqchip/loongson-htvec: Adjust irqchip driver for 32BIT/64BIT irqchip/loongson-pch-msi: Adjust irqchip driver for 32BIT/64BIT irqchip/loongson-pch-pic: Adjust irqchip driver for 32BIT/64BIT irqchip: Allow LoongArch irqchip drivers on both 32BIT/64BIT Lad Prabhakar (2): dt-bindings: interrupt-controller: renesas,rzv2h-icu: Document RZ/V2N= SoC irqchip/renesas-rzv2h: Add support for RZ/V2N SoC Nick Hu (2): irqchip/riscv-imsic: Add a CPU pm notifier to restore the IMSIC on ex= it irqchip/riscv-aplic: Preserve APLIC states across suspend/resume Thomas Gleixner (2): irqchip/aspeed-scu-ic: Remove unused variable mask irqchip/sifive-plic: Handle number of hardware interrupts correctly Vladimir Kondratiev (2): irqchip/aslint-sswi: Request IO memory resource irqchip/aslint-sswi: Fix error check of of_io_request_and_map() result Yangyu Chen (1): dt-bindings: interrupt-controller: sifive,plic: Clarify the riscv,nde= v meaning in PLIC .../renesas,r9a09g077-icu.yaml | 236 +++++++++++++++++ .../interrupt-controller/renesas,rzv2h-icu.yaml | 1 + .../interrupt-controller/sifive,plic-1.0.0.yaml | 4 +- .../bindings/interrupt-controller/ti,sci-intr.yaml | 38 ++- arch/arm64/boot/dts/renesas/r9a09g077.dtsi | 73 ++++++ arch/arm64/boot/dts/renesas/r9a09g087.dtsi | 73 ++++++ drivers/irqchip/Kconfig | 19 +- drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-aclint-sswi.c | 8 +- drivers/irqchip/irq-aspeed-scu-ic.c | 3 +- drivers/irqchip/irq-gic-v5-its.c | 2 +- drivers/irqchip/irq-loongarch-avec.c | 14 +- drivers/irqchip/irq-loongson-eiointc.c | 36 ++- drivers/irqchip/irq-loongson-htvec.c | 14 +- drivers/irqchip/irq-loongson-liointc.c | 8 +- drivers/irqchip/irq-loongson-pch-msi.c | 9 +- drivers/irqchip/irq-loongson-pch-pic.c | 14 +- drivers/irqchip/irq-renesas-rzt2h.c | 280 +++++++++++++++++= ++++ drivers/irqchip/irq-renesas-rzv2h.c | 61 ++++- drivers/irqchip/irq-riscv-aplic-direct.c | 10 + drivers/irqchip/irq-riscv-aplic-main.c | 170 ++++++++++++- drivers/irqchip/irq-riscv-aplic-main.h | 19 ++ drivers/irqchip/irq-riscv-imsic-early.c | 39 ++- drivers/irqchip/irq-sifive-plic.c | 82 +++--- drivers/irqchip/irq-ti-sci-intr.c | 54 ++-- drivers/soc/renesas/Kconfig | 1 + include/linux/irqchip/irq-renesas-rzt2h.h | 23 ++ 27 files changed, 1172 insertions(+), 120 deletions(-) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/= renesas,r9a09g077-icu.yaml create mode 100644 drivers/irqchip/irq-renesas-rzt2h.c create mode 100644 include/linux/irqchip/irq-renesas-rzt2h.h diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas= ,r9a09g077-icu.yaml b/Documentation/devicetree/bindings/interrupt-controlle= r/renesas,r9a09g077-icu.yaml new file mode 100644 index 000000000000..78c01d14e765 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,r9a09g= 077-icu.yaml @@ -0,0 +1,236 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/renesas,r9a09g077-= icu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas RZ/{T2H,N2H} Interrupt Controller + +maintainers: + - Cosmin Tanislav + +allOf: + - $ref: /schemas/interrupt-controller.yaml# + +description: + The Interrupt Controller (ICU) handles software-triggered interrupts + (INTCPU), external interrupts (IRQ and SEI), error interrupts and DMAC + requests. + +properties: + compatible: + oneOf: + - const: renesas,r9a09g077-icu # RZ/T2H + + - items: + - enum: + - renesas,r9a09g087-icu # RZ/N2H + - const: renesas,r9a09g077-icu + + reg: + items: + - description: Non-safety registers (INTCPU0-13, IRQ0-13) + - description: Safety registers (INTCPU14-15, IRQ14-15, SEI) + + '#interrupt-cells': + description: The first cell is the SPI number of the interrupt, as per= user + manual. The second cell is used to specify the flag. + const: 2 + + '#address-cells': + const: 0 + + interrupt-controller: true + + interrupts: + items: + - description: Software interrupt 0 + - description: Software interrupt 1 + - description: Software interrupt 2 + - description: Software interrupt 3 + - description: Software interrupt 4 + - description: Software interrupt 5 + - description: Software interrupt 6 + - description: Software interrupt 7 + - description: Software interrupt 8 + - description: Software interrupt 9 + - description: Software interrupt 10 + - description: Software interrupt 11 + - description: Software interrupt 12 + - description: Software interrupt 13 + - description: Software interrupt 14 + - description: Software interrupt 15 + - description: External pin interrupt 0 + - description: External pin interrupt 1 + - description: External pin interrupt 2 + - description: External pin interrupt 3 + - description: External pin interrupt 4 + - description: External pin interrupt 5 + - description: External pin interrupt 6 + - description: External pin interrupt 7 + - description: External pin interrupt 8 + - description: External pin interrupt 9 + - description: External pin interrupt 10 + - description: External pin interrupt 11 + - description: External pin interrupt 12 + - description: External pin interrupt 13 + - description: External pin interrupt 14 + - description: External pin interrupt 15 + - description: System error interrupt + - description: Cortex-A55 error event 0 + - description: Cortex-A55 error event 1 + - description: Cortex-R52 CPU 0 error event 0 + - description: Cortex-R52 CPU 0 error event 1 + - description: Cortex-R52 CPU 1 error event 0 + - description: Cortex-R52 CPU 1 error event 1 + - description: Peripherals error event 0 + - description: Peripherals error event 1 + - description: DSMIF error event 0 + - description: DSMIF error event 1 + - description: ENCIF error event 0 + - description: ENCIF error event 1 + + interrupt-names: + items: + - const: intcpu0 + - const: intcpu1 + - const: intcpu2 + - const: intcpu3 + - const: intcpu4 + - const: intcpu5 + - const: intcpu6 + - const: intcpu7 + - const: intcpu8 + - const: intcpu9 + - const: intcpu10 + - const: intcpu11 + - const: intcpu12 + - const: intcpu13 + - const: intcpu14 + - const: intcpu15 + - const: irq0 + - const: irq1 + - const: irq2 + - const: irq3 + - const: irq4 + - const: irq5 + - const: irq6 + - const: irq7 + - const: irq8 + - const: irq9 + - const: irq10 + - const: irq11 + - const: irq12 + - const: irq13 + - const: irq14 + - const: irq15 + - const: sei + - const: ca55-err0 + - const: ca55-err1 + - const: cr520-err0 + - const: cr520-err1 + - const: cr521-err0 + - const: cr521-err1 + - const: peri-err0 + - const: peri-err1 + - const: dsmif-err0 + - const: dsmif-err1 + - const: encif-err0 + - const: encif-err1 + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - '#interrupt-cells' + - '#address-cells' + - interrupt-controller + - interrupts + - interrupt-names + - clocks + - power-domains + +unevaluatedProperties: false + +examples: + - | + #include + #include + + icu: interrupt-controller@802a0000 { + compatible =3D "renesas,r9a09g077-icu"; + reg =3D <0x802a0000 0x10000>, + <0x812a0000 0x50>; + #interrupt-cells =3D <2>; + #address-cells =3D <0>; + interrupt-controller; + interrupts =3D , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + interrupt-names =3D "intcpu0", "intcpu1", "intcpu2", + "intcpu3", "intcpu4", "intcpu5", + "intcpu6", "intcpu7", "intcpu8", + "intcpu9", "intcpu10", "intcpu11", + "intcpu12", "intcpu13", "intcpu14", + "intcpu15", + "irq0", "irq1", "irq2", "irq3", + "irq4", "irq5", "irq6", "irq7", + "irq8", "irq9", "irq10", "irq11", + "irq12", "irq13", "irq14", "irq15", + "sei", + "ca55-err0", "ca55-err1", + "cr520-err0", "cr520-err1", + "cr521-err0", "cr521-err1", + "peri-err0", "peri-err1", + "dsmif-err0", "dsmif-err1", + "encif-err0", "encif-err1"; + clocks =3D <&cpg CPG_CORE R9A09G077_CLK_PCLKM>; + power-domains =3D <&cpg>; + }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas= ,rzv2h-icu.yaml b/Documentation/devicetree/bindings/interrupt-controller/re= nesas,rzv2h-icu.yaml index 3f99c8645767..cb244b8f5e1c 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/renesas,rzv2h-= icu.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,rzv2h-= icu.yaml @@ -22,6 +22,7 @@ properties: compatible: enum: - renesas,r9a09g047-icu # RZ/G3E + - renesas,r9a09g056-icu # RZ/V2N - renesas,r9a09g057-icu # RZ/V2H(P) =20 '#interrupt-cells': diff --git a/Documentation/devicetree/bindings/interrupt-controller/sifive,= plic-1.0.0.yaml b/Documentation/devicetree/bindings/interrupt-controller/si= five,plic-1.0.0.yaml index 388fc2c620c0..e0267223887e 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.= 0.0.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.= 0.0.yaml @@ -108,7 +108,9 @@ properties: riscv,ndev: $ref: /schemas/types.yaml#/definitions/uint32 description: - Specifies how many external interrupts are supported by this control= ler. + Specifies how many external (device) interrupts are supported by this + controller. Note that source 0 is reserved in PLIC, so the valid + interrupt sources are 1 to riscv,ndev inclusive. =20 clocks: true =20 diff --git a/Documentation/devicetree/bindings/interrupt-controller/ti,sci-= intr.yaml b/Documentation/devicetree/bindings/interrupt-controller/ti,sci-i= ntr.yaml index c99cc7323c71..de45f0c4b1d1 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.ya= ml +++ b/Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.ya= ml @@ -15,8 +15,7 @@ allOf: description: | The Interrupt Router (INTR) module provides a mechanism to mux M interrupt inputs to N interrupt outputs, where all M inputs are selectab= le - to be driven per N output. An Interrupt Router can either handle edge - triggered or level triggered interrupts and that is fixed in hardware. + to be driven per N output. =20 Interrupt Router +----------------------+ @@ -64,9 +63,14 @@ properties: interrupt-controller: true =20 '#interrupt-cells': - const: 1 + enum: [1, 2] description: | - The 1st cell should contain interrupt router input hw number. + Number of cells in interrupt specifier. Depends on ti,intr-trigger-t= ype: + - If ti,intr-trigger-type is present: must be 1 + The 1st cell should contain interrupt router input hw number. + - If ti,intr-trigger-type is absent: must be 2 + The 1st cell should contain interrupt router input hw number. + The 2nd cell should contain interrupt trigger type (preserved by r= outer). =20 ti,interrupt-ranges: $ref: /schemas/types.yaml#/definitions/uint32-matrix @@ -82,9 +86,22 @@ properties: - description: | "limit" specifies the limit for translation =20 +if: + required: + - ti,intr-trigger-type +then: + properties: + '#interrupt-cells': + const: 1 + description: Interrupt ID only. Interrupt type is specified globally +else: + properties: + '#interrupt-cells': + const: 2 + description: Interrupt ID and corresponding interrupt type + required: - compatible - - ti,intr-trigger-type - interrupt-controller - '#interrupt-cells' - ti,sci @@ -105,3 +122,14 @@ examples: ti,sci-dev-id =3D <131>; ti,interrupt-ranges =3D <0 360 32>; }; + + - | + interrupt-controller { + compatible =3D "ti,sci-intr"; + interrupt-controller; + interrupt-parent =3D <&gic500>; + #interrupt-cells =3D <2>; + ti,sci =3D <&dmsc>; + ti,sci-dev-id =3D <131>; + ti,interrupt-ranges =3D <0 360 32>; + }; diff --git a/arch/arm64/boot/dts/renesas/r9a09g077.dtsi b/arch/arm64/boot/d= ts/renesas/r9a09g077.dtsi index f5fa6ca06409..e5c98388023f 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g077.dtsi +++ b/arch/arm64/boot/dts/renesas/r9a09g077.dtsi @@ -756,6 +756,79 @@ cpg: clock-controller@80280000 { #power-domain-cells =3D <0>; }; =20 + icu: interrupt-controller@802a0000 { + compatible =3D "renesas,r9a09g077-icu"; + reg =3D <0 0x802a0000 0 0x10000>, + <0 0x812a0000 0 0x50>; + #interrupt-cells =3D <2>; + #address-cells =3D <0>; + interrupt-controller; + interrupts =3D , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + interrupt-names =3D "intcpu0", "intcpu1", "intcpu2", + "intcpu3", "intcpu4", "intcpu5", + "intcpu6", "intcpu7", "intcpu8", + "intcpu9", "intcpu10", "intcpu11", + "intcpu12", "intcpu13", "intcpu14", + "intcpu15", + "irq0", "irq1", "irq2", "irq3", + "irq4", "irq5", "irq6", "irq7", + "irq8", "irq9", "irq10", "irq11", + "irq12", "irq13", "irq14", "irq15", + "sei", + "ca55-err0", "ca55-err1", + "cr520-err0", "cr520-err1", + "cr521-err0", "cr521-err1", + "peri-err0", "peri-err1", + "dsmif-err0", "dsmif-err1", + "encif-err0", "encif-err1"; + clocks =3D <&cpg CPG_CORE R9A09G077_CLK_PCLKM>; + power-domains =3D <&cpg>; + }; + pinctrl: pinctrl@802c0000 { compatible =3D "renesas,r9a09g077-pinctrl"; reg =3D <0 0x802c0000 0 0x10000>, diff --git a/arch/arm64/boot/dts/renesas/r9a09g087.dtsi b/arch/arm64/boot/d= ts/renesas/r9a09g087.dtsi index 361a9235f00d..0e0a9c1bc885 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g087.dtsi +++ b/arch/arm64/boot/dts/renesas/r9a09g087.dtsi @@ -759,6 +759,79 @@ cpg: clock-controller@80280000 { #power-domain-cells =3D <0>; }; =20 + icu: interrupt-controller@802a0000 { + compatible =3D "renesas,r9a09g087-icu", "renesas,r9a09g077-icu"; + reg =3D <0 0x802a0000 0 0x10000>, + <0 0x812a0000 0 0x50>; + #interrupt-cells =3D <2>; + #address-cells =3D <0>; + interrupt-controller; + interrupts =3D , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + interrupt-names =3D "intcpu0", "intcpu1", "intcpu2", + "intcpu3", "intcpu4", "intcpu5", + "intcpu6", "intcpu7", "intcpu8", + "intcpu9", "intcpu10", "intcpu11", + "intcpu12", "intcpu13", "intcpu14", + "intcpu15", + "irq0", "irq1", "irq2", "irq3", + "irq4", "irq5", "irq6", "irq7", + "irq8", "irq9", "irq10", "irq11", + "irq12", "irq13", "irq14", "irq15", + "sei", + "ca55-err0", "ca55-err1", + "cr520-err0", "cr520-err1", + "cr521-err0", "cr521-err1", + "peri-err0", "peri-err1", + "dsmif-err0", "dsmif-err1", + "encif-err0", "encif-err1"; + clocks =3D <&cpg CPG_CORE R9A09G087_CLK_PCLKM>; + power-domains =3D <&cpg>; + }; + pinctrl: pinctrl@802c0000 { compatible =3D "renesas,r9a09g087-pinctrl"; reg =3D <0 0x802c0000 0 0x10000>, diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index f334f49c9791..f07b00d7fef9 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -297,6 +297,14 @@ config RENESAS_RZG2L_IRQC Enable support for the Renesas RZ/G2L (and alike SoC) Interrupt Control= ler for external devices. =20 +config RENESAS_RZT2H_ICU + bool "Renesas RZ/{T2H,N2H} ICU support" if COMPILE_TEST + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN_HIERARCHY + help + Enable support for the Renesas RZ/{T2H,N2H} Interrupt Controller + (ICU). + config RENESAS_RZV2H_ICU bool "Renesas RZ/V2H(P) ICU support" if COMPILE_TEST select GENERIC_IRQ_CHIP @@ -698,7 +706,7 @@ config IRQ_LOONGARCH_CPU =20 config LOONGSON_LIOINTC bool "Loongson Local I/O Interrupt Controller" - depends on MACH_LOONGSON64 + depends on MACH_LOONGSON64 || LOONGARCH default y select IRQ_DOMAIN select GENERIC_IRQ_CHIP @@ -708,7 +716,6 @@ config LOONGSON_LIOINTC config LOONGSON_EIOINTC bool "Loongson Extend I/O Interrupt Controller" depends on LOONGARCH - depends on MACH_LOONGSON64 default MACH_LOONGSON64 select IRQ_DOMAIN_HIERARCHY select GENERIC_IRQ_CHIP @@ -726,7 +733,7 @@ config LOONGSON_HTPIC =20 config LOONGSON_HTVEC bool "Loongson HyperTransport Interrupt Vector Controller" - depends on MACH_LOONGSON64 + depends on MACH_LOONGSON64 || LOONGARCH default MACH_LOONGSON64 select IRQ_DOMAIN_HIERARCHY help @@ -734,7 +741,7 @@ config LOONGSON_HTVEC =20 config LOONGSON_PCH_PIC bool "Loongson PCH PIC Controller" - depends on MACH_LOONGSON64 + depends on MACH_LOONGSON64 || LOONGARCH default MACH_LOONGSON64 select IRQ_DOMAIN_HIERARCHY select IRQ_FASTEOI_HIERARCHY_HANDLERS @@ -743,7 +750,7 @@ config LOONGSON_PCH_PIC =20 config LOONGSON_PCH_MSI bool "Loongson PCH MSI Controller" - depends on MACH_LOONGSON64 + depends on MACH_LOONGSON64 || LOONGARCH depends on PCI default MACH_LOONGSON64 select IRQ_DOMAIN_HIERARCHY @@ -755,7 +762,7 @@ config LOONGSON_PCH_MSI config LOONGSON_PCH_LPC bool "Loongson PCH LPC Controller" depends on LOONGARCH - depends on MACH_LOONGSON64 + depends on MACH_LOONGSON64 || LOONGARCH default MACH_LOONGSON64 select IRQ_DOMAIN_HIERARCHY help diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 6a229443efe0..26aa3b6ec99f 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_RENESAS_INTC_IRQPIN) +=3D irq-renesas-intc-i= rqpin.o obj-$(CONFIG_RENESAS_IRQC) +=3D irq-renesas-irqc.o obj-$(CONFIG_RENESAS_RZA1_IRQC) +=3D irq-renesas-rza1.o obj-$(CONFIG_RENESAS_RZG2L_IRQC) +=3D irq-renesas-rzg2l.o +obj-$(CONFIG_RENESAS_RZT2H_ICU) +=3D irq-renesas-rzt2h.o obj-$(CONFIG_RENESAS_RZV2H_ICU) +=3D irq-renesas-rzv2h.o obj-$(CONFIG_VERSATILE_FPGA_IRQ) +=3D irq-versatile-fpga.o obj-$(CONFIG_ARCH_NSPIRE) +=3D irq-zevio.o diff --git a/drivers/irqchip/irq-aclint-sswi.c b/drivers/irqchip/irq-aclint= -sswi.c index fee30f3bc5ac..ca06efd86fa1 100644 --- a/drivers/irqchip/irq-aclint-sswi.c +++ b/drivers/irqchip/irq-aclint-sswi.c @@ -109,9 +109,11 @@ static int __init aclint_sswi_probe(struct fwnode_hand= le *fwnode) if (!is_of_node(fwnode)) return -EINVAL; =20 - reg =3D of_iomap(to_of_node(fwnode), 0); - if (!reg) - return -ENOMEM; + reg =3D of_io_request_and_map(to_of_node(fwnode), 0, NULL); + if (IS_ERR(reg)) { + pr_err("%pfwP: Failed to map MMIO region\n", fwnode); + return PTR_ERR(reg); + } =20 /* Parse SSWI setting */ rc =3D aclint_sswi_parse_irq(fwnode, reg); diff --git a/drivers/irqchip/irq-aspeed-scu-ic.c b/drivers/irqchip/irq-aspe= ed-scu-ic.c index bee59c8c4c93..7398c3b9eace 100644 --- a/drivers/irqchip/irq-aspeed-scu-ic.c +++ b/drivers/irqchip/irq-aspeed-scu-ic.c @@ -104,11 +104,10 @@ static void aspeed_scu_ic_irq_handler_split(struct ir= q_desc *desc) struct aspeed_scu_ic *scu_ic =3D irq_desc_get_handler_data(desc); struct irq_chip *chip =3D irq_desc_get_chip(desc); unsigned long bit, enabled, max, status; - unsigned int sts, mask; + unsigned int sts; =20 chained_irq_enter(chip, desc); =20 - mask =3D scu_ic->irq_enable; sts =3D readl(scu_ic->base + scu_ic->isr); enabled =3D sts & scu_ic->irq_enable; sts =3D readl(scu_ic->base + scu_ic->isr); diff --git a/drivers/irqchip/irq-gic-v5-its.c b/drivers/irqchip/irq-gic-v5-= its.c index 554485f0be1f..f410e178c841 100644 --- a/drivers/irqchip/irq-gic-v5-its.c +++ b/drivers/irqchip/irq-gic-v5-its.c @@ -900,7 +900,7 @@ static int gicv5_its_alloc_eventid(struct gicv5_its_dev= *its_dev, msi_alloc_info event_id_base =3D info->hwirq; =20 if (event_id_base >=3D its_dev->num_events) { - pr_err("EventID ouside of ITT range; cannot allocate an ITT entry!\n"); + pr_err("EventID outside of ITT range; cannot allocate an ITT entry!\n"); =20 return -EINVAL; } diff --git a/drivers/irqchip/irq-loongarch-avec.c b/drivers/irqchip/irq-loo= ngarch-avec.c index ba556c008cf3..fb8efde95393 100644 --- a/drivers/irqchip/irq-loongarch-avec.c +++ b/drivers/irqchip/irq-loongarch-avec.c @@ -58,11 +58,13 @@ struct avecintc_data { =20 static inline void avecintc_enable(void) { +#ifdef CONFIG_MACH_LOONGSON64 u64 value; =20 value =3D iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC); value |=3D IOCSR_MISC_FUNC_AVEC_EN; iocsr_write64(value, LOONGARCH_IOCSR_MISC_FUNC); +#endif } =20 static inline void avecintc_ack_irq(struct irq_data *d) @@ -167,7 +169,7 @@ void complete_irq_moving(void) struct pending_list *plist =3D this_cpu_ptr(&pending_list); struct avecintc_data *adata, *tdata; int cpu, vector, bias; - uint64_t isr; + unsigned long isr; =20 guard(raw_spinlock)(&loongarch_avec.lock); =20 @@ -177,16 +179,16 @@ void complete_irq_moving(void) bias =3D vector / VECTORS_PER_REG; switch (bias) { case 0: - isr =3D csr_read64(LOONGARCH_CSR_ISR0); + isr =3D csr_read(LOONGARCH_CSR_ISR0); break; case 1: - isr =3D csr_read64(LOONGARCH_CSR_ISR1); + isr =3D csr_read(LOONGARCH_CSR_ISR1); break; case 2: - isr =3D csr_read64(LOONGARCH_CSR_ISR2); + isr =3D csr_read(LOONGARCH_CSR_ISR2); break; case 3: - isr =3D csr_read64(LOONGARCH_CSR_ISR3); + isr =3D csr_read(LOONGARCH_CSR_ISR3); break; } =20 @@ -234,7 +236,7 @@ static void avecintc_irq_dispatch(struct irq_desc *desc) chained_irq_enter(chip, desc); =20 while (true) { - unsigned long vector =3D csr_read64(LOONGARCH_CSR_IRR); + unsigned long vector =3D csr_read(LOONGARCH_CSR_IRR); if (vector & IRR_INVALID_MASK) break; =20 diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-l= oongson-eiointc.c index ad2105685b48..37e7e1f9bbe3 100644 --- a/drivers/irqchip/irq-loongson-eiointc.c +++ b/drivers/irqchip/irq-loongson-eiointc.c @@ -37,9 +37,9 @@ #define EXTIOI_ENABLE_INT_ENCODE BIT(2) #define EXTIOI_ENABLE_CPU_ENCODE BIT(3) =20 -#define VEC_REG_COUNT 4 -#define VEC_COUNT_PER_REG 64 -#define VEC_COUNT (VEC_REG_COUNT * VEC_COUNT_PER_REG) +#define VEC_COUNT 256 +#define VEC_COUNT_PER_REG BITS_PER_LONG +#define VEC_REG_COUNT (VEC_COUNT / BITS_PER_LONG) #define VEC_REG_IDX(irq_id) ((irq_id) / VEC_COUNT_PER_REG) #define VEC_REG_BIT(irq_id) ((irq_id) % VEC_COUNT_PER_REG) #define EIOINTC_ALL_ENABLE 0xffffffff @@ -85,11 +85,13 @@ static struct eiointc_priv *eiointc_priv[MAX_IO_PICS]; =20 static void eiointc_enable(void) { +#ifdef CONFIG_MACH_LOONGSON64 uint64_t misc; =20 misc =3D iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC); misc |=3D IOCSR_MISC_FUNC_EXT_IOI_EN; iocsr_write64(misc, LOONGARCH_IOCSR_MISC_FUNC); +#endif } =20 static int cpu_to_eio_node(int cpu) @@ -281,12 +283,34 @@ static int eiointc_router_init(unsigned int cpu) return 0; } =20 +#if VEC_COUNT_PER_REG =3D=3D 32 +static inline unsigned long read_isr(int i) +{ + return iocsr_read32(EIOINTC_REG_ISR + (i << 2)); +} + +static inline void write_isr(int i, unsigned long val) +{ + iocsr_write32(val, EIOINTC_REG_ISR + (i << 2)); +} +#else +static inline unsigned long read_isr(int i) +{ + return iocsr_read64(EIOINTC_REG_ISR + (i << 3)); +} + +static inline void write_isr(int i, unsigned long val) +{ + iocsr_write64(val, EIOINTC_REG_ISR + (i << 3)); +} +#endif + static void eiointc_irq_dispatch(struct irq_desc *desc) { struct eiointc_ip_route *info =3D irq_desc_get_handler_data(desc); struct irq_chip *chip =3D irq_desc_get_chip(desc); + unsigned long pending; bool handled =3D false; - u64 pending; int i; =20 chained_irq_enter(chip, desc); @@ -299,14 +323,14 @@ static void eiointc_irq_dispatch(struct irq_desc *des= c) * read ISR for these 64 interrupt vectors rather than all vectors */ for (i =3D info->start; i < info->end; i++) { - pending =3D iocsr_read64(EIOINTC_REG_ISR + (i << 3)); + pending =3D read_isr(i); =20 /* Skip handling if pending bitmap is zero */ if (!pending) continue; =20 /* Clear the IRQs */ - iocsr_write64(pending, EIOINTC_REG_ISR + (i << 3)); + write_isr(i, pending); while (pending) { int bit =3D __ffs(pending); int irq =3D bit + VEC_COUNT_PER_REG * i; diff --git a/drivers/irqchip/irq-loongson-htvec.c b/drivers/irqchip/irq-loo= ngson-htvec.c index d2be8e954e92..5a3339da97ad 100644 --- a/drivers/irqchip/irq-loongson-htvec.c +++ b/drivers/irqchip/irq-loongson-htvec.c @@ -295,19 +295,19 @@ static int __init acpi_cascade_irqdomain_init(void) return 0; } =20 -int __init htvec_acpi_init(struct irq_domain *parent, - struct acpi_madt_ht_pic *acpi_htvec) +int __init htvec_acpi_init(struct irq_domain *parent, struct acpi_madt_ht_= pic *acpi_htvec) { - int i, ret; - int num_parents, parent_irq[8]; + int i, ret, num_parents, parent_irq[8]; struct fwnode_handle *domain_handle; + phys_addr_t addr; =20 if (!acpi_htvec) return -EINVAL; =20 num_parents =3D HTVEC_MAX_PARENT_IRQ; + addr =3D (phys_addr_t)acpi_htvec->address; =20 - domain_handle =3D irq_domain_alloc_fwnode(&acpi_htvec->address); + domain_handle =3D irq_domain_alloc_fwnode(&addr); if (!domain_handle) { pr_err("Unable to allocate domain handle\n"); return -ENOMEM; @@ -317,9 +317,7 @@ int __init htvec_acpi_init(struct irq_domain *parent, for (i =3D 0; i < HTVEC_MAX_PARENT_IRQ; i++) parent_irq[i] =3D irq_create_mapping(parent, acpi_htvec->cascade[i]); =20 - ret =3D htvec_init(acpi_htvec->address, acpi_htvec->size, - num_parents, parent_irq, domain_handle); - + ret =3D htvec_init(addr, acpi_htvec->size, num_parents, parent_irq, domai= n_handle); if (ret =3D=3D 0) ret =3D acpi_cascade_irqdomain_init(); else diff --git a/drivers/irqchip/irq-loongson-liointc.c b/drivers/irqchip/irq-l= oongson-liointc.c index 0033c2188abc..551597e2c428 100644 --- a/drivers/irqchip/irq-loongson-liointc.c +++ b/drivers/irqchip/irq-loongson-liointc.c @@ -394,8 +394,9 @@ static int __init acpi_cascade_irqdomain_init(void) =20 int __init liointc_acpi_init(struct irq_domain *parent, struct acpi_madt_l= io_pic *acpi_liointc) { - int ret; + phys_addr_t addr =3D (phys_addr_t)acpi_liointc->address; struct fwnode_handle *domain_handle; + int ret; =20 parent_int_map[0] =3D acpi_liointc->cascade_map[0]; parent_int_map[1] =3D acpi_liointc->cascade_map[1]; @@ -403,14 +404,13 @@ int __init liointc_acpi_init(struct irq_domain *paren= t, struct acpi_madt_lio_pic parent_irq[0] =3D irq_create_mapping(parent, acpi_liointc->cascade[0]); parent_irq[1] =3D irq_create_mapping(parent, acpi_liointc->cascade[1]); =20 - domain_handle =3D irq_domain_alloc_fwnode(&acpi_liointc->address); + domain_handle =3D irq_domain_alloc_fwnode(&addr); if (!domain_handle) { pr_err("Unable to allocate domain handle\n"); return -ENOMEM; } =20 - ret =3D liointc_init(acpi_liointc->address, acpi_liointc->size, - 1, domain_handle, NULL); + ret =3D liointc_init(addr, acpi_liointc->size, 1, domain_handle, NULL); if (ret =3D=3D 0) ret =3D acpi_cascade_irqdomain_init(); else diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-l= oongson-pch-msi.c index 4aedc9b90ff7..91c856c65d9d 100644 --- a/drivers/irqchip/irq-loongson-pch-msi.c +++ b/drivers/irqchip/irq-loongson-pch-msi.c @@ -263,12 +263,13 @@ struct fwnode_handle *get_pch_msi_handle(int pci_segm= ent) =20 int __init pch_msi_acpi_init(struct irq_domain *parent, struct acpi_madt_m= si_pic *acpi_pchmsi) { - int ret; + phys_addr_t msg_address =3D (phys_addr_t)acpi_pchmsi->msg_address; struct fwnode_handle *domain_handle; + int ret; =20 - domain_handle =3D irq_domain_alloc_fwnode(&acpi_pchmsi->msg_address); - ret =3D pch_msi_init(acpi_pchmsi->msg_address, acpi_pchmsi->start, - acpi_pchmsi->count, parent, domain_handle); + domain_handle =3D irq_domain_alloc_fwnode(&msg_address); + ret =3D pch_msi_init(msg_address, acpi_pchmsi->start, acpi_pchmsi->count, + parent, domain_handle); if (ret < 0) irq_domain_free_fwnode(domain_handle); =20 diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-l= oongson-pch-pic.c index c6b369a974a7..f2acaf93f552 100644 --- a/drivers/irqchip/irq-loongson-pch-pic.c +++ b/drivers/irqchip/irq-loongson-pch-pic.c @@ -343,7 +343,7 @@ static int pch_pic_init(phys_addr_t addr, unsigned long= size, int vec_base, priv->table[i] =3D PIC_UNDEF_VECTOR; =20 priv->ht_vec_base =3D vec_base; - priv->vec_count =3D ((readq(priv->base) >> 48) & 0xff) + 1; + priv->vec_count =3D ((readl(priv->base + 4) >> 16) & 0xff) + 1; priv->gsi_base =3D gsi_base; =20 priv->pic_domain =3D irq_domain_create_hierarchy(parent_domain, 0, @@ -446,23 +446,23 @@ static int __init acpi_cascade_irqdomain_init(void) return 0; } =20 -int __init pch_pic_acpi_init(struct irq_domain *parent, - struct acpi_madt_bio_pic *acpi_pchpic) +int __init pch_pic_acpi_init(struct irq_domain *parent, struct acpi_madt_b= io_pic *acpi_pchpic) { - int ret; + phys_addr_t addr =3D (phys_addr_t)acpi_pchpic->address; struct fwnode_handle *domain_handle; + int ret; =20 if (find_pch_pic(acpi_pchpic->gsi_base) >=3D 0) return 0; =20 - domain_handle =3D irq_domain_alloc_fwnode(&acpi_pchpic->address); + domain_handle =3D irq_domain_alloc_fwnode(&addr); if (!domain_handle) { pr_err("Unable to allocate domain handle\n"); return -ENOMEM; } =20 - ret =3D pch_pic_init(acpi_pchpic->address, acpi_pchpic->size, - 0, parent, domain_handle, acpi_pchpic->gsi_base); + ret =3D pch_pic_init(addr, acpi_pchpic->size, 0, parent, + domain_handle, acpi_pchpic->gsi_base); =20 if (ret < 0) { irq_domain_free_fwnode(domain_handle); diff --git a/drivers/irqchip/irq-renesas-rzt2h.c b/drivers/irqchip/irq-rene= sas-rzt2h.c new file mode 100644 index 000000000000..53cf80e1155a --- /dev/null +++ b/drivers/irqchip/irq-renesas-rzt2h.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RZT2H_ICU_INTCPU_NS_START 0 +#define RZT2H_ICU_INTCPU_NS_COUNT 14 + +#define RZT2H_ICU_INTCPU_S_START (RZT2H_ICU_INTCPU_NS_START + \ + RZT2H_ICU_INTCPU_NS_COUNT) +#define RZT2H_ICU_INTCPU_S_COUNT 2 + +#define RZT2H_ICU_IRQ_NS_START (RZT2H_ICU_INTCPU_S_START + \ + RZT2H_ICU_INTCPU_S_COUNT) +#define RZT2H_ICU_IRQ_NS_COUNT 14 + +#define RZT2H_ICU_IRQ_S_START (RZT2H_ICU_IRQ_NS_START + \ + RZT2H_ICU_IRQ_NS_COUNT) +#define RZT2H_ICU_IRQ_S_COUNT 2 + +#define RZT2H_ICU_SEI_START (RZT2H_ICU_IRQ_S_START + \ + RZT2H_ICU_IRQ_S_COUNT) +#define RZT2H_ICU_SEI_COUNT 1 + +#define RZT2H_ICU_NUM_IRQ (RZT2H_ICU_INTCPU_NS_COUNT + \ + RZT2H_ICU_INTCPU_S_COUNT + \ + RZT2H_ICU_IRQ_NS_COUNT + \ + RZT2H_ICU_IRQ_S_COUNT + \ + RZT2H_ICU_SEI_COUNT) + +#define RZT2H_ICU_IRQ_IN_RANGE(n, type) \ + ((n) >=3D RZT2H_ICU_##type##_START && \ + (n) < RZT2H_ICU_##type##_START + RZT2H_ICU_##type##_COUNT) + +#define RZT2H_ICU_PORTNF_MD 0xc +#define RZT2H_ICU_PORTNF_MDi_MASK(i) (GENMASK(1, 0) << ((i) * 2)) +#define RZT2H_ICU_PORTNF_MDi_PREP(i, val) (FIELD_PREP(GENMASK(1, 0), val) = << ((i) * 2)) + +#define RZT2H_ICU_MD_LOW_LEVEL 0b00 +#define RZT2H_ICU_MD_FALLING_EDGE 0b01 +#define RZT2H_ICU_MD_RISING_EDGE 0b10 +#define RZT2H_ICU_MD_BOTH_EDGES 0b11 + +#define RZT2H_ICU_DMACn_RSSELi(n, i) (0x7d0 + 0x18 * (n) + 0x4 * (i)) +#define RZT2H_ICU_DMAC_REQ_SELx_MASK(x) (GENMASK(9, 0) << ((x) * 10)) +#define RZT2H_ICU_DMAC_REQ_SELx_PREP(x, val) (FIELD_PREP(GENMASK(9, 0), va= l) << ((x) * 10)) + +struct rzt2h_icu_priv { + void __iomem *base_ns; + void __iomem *base_s; + struct irq_fwspec fwspec[RZT2H_ICU_NUM_IRQ]; + raw_spinlock_t lock; +}; + +void rzt2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_i= ndex, u8 dmac_channel, + u16 req_no) +{ + struct rzt2h_icu_priv *priv =3D platform_get_drvdata(icu_dev); + u8 y, upper; + u32 val; + + y =3D dmac_channel / 3; + upper =3D dmac_channel % 3; + + guard(raw_spinlock_irqsave)(&priv->lock); + val =3D readl(priv->base_ns + RZT2H_ICU_DMACn_RSSELi(dmac_index, y)); + val &=3D ~RZT2H_ICU_DMAC_REQ_SELx_MASK(upper); + val |=3D RZT2H_ICU_DMAC_REQ_SELx_PREP(upper, req_no); + writel(val, priv->base_ns + RZT2H_ICU_DMACn_RSSELi(dmac_index, y)); +} +EXPORT_SYMBOL_GPL(rzt2h_icu_register_dma_req); + +static inline struct rzt2h_icu_priv *irq_data_to_priv(struct irq_data *dat= a) +{ + return data->domain->host_data; +} + +static inline int rzt2h_icu_irq_to_offset(struct irq_data *d, void __iomem= **base, + unsigned int *offset) +{ + struct rzt2h_icu_priv *priv =3D irq_data_to_priv(d); + unsigned int hwirq =3D irqd_to_hwirq(d); + + /* + * Safety IRQs and SEI use a separate register space from the non-safety = IRQs. + * SEI interrupt number follows immediately after the safety IRQs. + */ + if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, IRQ_NS)) { + *offset =3D hwirq - RZT2H_ICU_IRQ_NS_START; + *base =3D priv->base_ns; + } else if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, IRQ_S) || RZT2H_ICU_IRQ_IN_RANGE= (hwirq, SEI)) { + *offset =3D hwirq - RZT2H_ICU_IRQ_S_START; + *base =3D priv->base_s; + } else { + return -EINVAL; + } + return 0; +} + +static int rzt2h_icu_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct rzt2h_icu_priv *priv =3D irq_data_to_priv(d); + unsigned int offset, parent_type; + void __iomem *base; + u32 val, md; + int ret; + + ret =3D rzt2h_icu_irq_to_offset(d, &base, &offset); + if (ret) + return ret; + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_LEVEL_LOW: + md =3D RZT2H_ICU_MD_LOW_LEVEL; + parent_type =3D IRQ_TYPE_LEVEL_HIGH; + break; + case IRQ_TYPE_EDGE_FALLING: + md =3D RZT2H_ICU_MD_FALLING_EDGE; + parent_type =3D IRQ_TYPE_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_RISING: + md =3D RZT2H_ICU_MD_RISING_EDGE; + parent_type =3D IRQ_TYPE_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_BOTH: + md =3D RZT2H_ICU_MD_BOTH_EDGES; + parent_type =3D IRQ_TYPE_EDGE_RISING; + break; + default: + return -EINVAL; + } + + scoped_guard(raw_spinlock, &priv->lock) { + val =3D readl_relaxed(base + RZT2H_ICU_PORTNF_MD); + val &=3D ~RZT2H_ICU_PORTNF_MDi_MASK(offset); + val |=3D RZT2H_ICU_PORTNF_MDi_PREP(offset, md); + writel_relaxed(val, base + RZT2H_ICU_PORTNF_MD); + } + + return irq_chip_set_type_parent(d, parent_type); +} + +static int rzt2h_icu_set_type(struct irq_data *d, unsigned int type) +{ + unsigned int hw_irq =3D irqd_to_hwirq(d); + + /* IRQn and SEI are selectable, others are edge-only. */ + if (RZT2H_ICU_IRQ_IN_RANGE(hw_irq, IRQ_NS) || + RZT2H_ICU_IRQ_IN_RANGE(hw_irq, IRQ_S) || + RZT2H_ICU_IRQ_IN_RANGE(hw_irq, SEI)) + return rzt2h_icu_irq_set_type(d, type); + + if ((type & IRQ_TYPE_SENSE_MASK) !=3D IRQ_TYPE_EDGE_RISING) + return -EINVAL; + + return irq_chip_set_type_parent(d, IRQ_TYPE_EDGE_RISING); +} + +static const struct irq_chip rzt2h_icu_chip =3D { + .name =3D "rzt2h-icu", + .irq_mask =3D irq_chip_mask_parent, + .irq_unmask =3D irq_chip_unmask_parent, + .irq_eoi =3D irq_chip_eoi_parent, + .irq_set_type =3D rzt2h_icu_set_type, + .irq_set_wake =3D irq_chip_set_wake_parent, + .irq_set_affinity =3D irq_chip_set_affinity_parent, + .irq_retrigger =3D irq_chip_retrigger_hierarchy, + .irq_get_irqchip_state =3D irq_chip_get_parent_state, + .irq_set_irqchip_state =3D irq_chip_set_parent_state, + .flags =3D IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE, +}; + +static int rzt2h_icu_alloc(struct irq_domain *domain, unsigned int virq, u= nsigned int nr_irqs, + void *arg) +{ + struct rzt2h_icu_priv *priv =3D domain->host_data; + irq_hw_number_t hwirq; + unsigned int type; + int ret; + + ret =3D irq_domain_translate_twocell(domain, arg, &hwirq, &type); + if (ret) + return ret; + + ret =3D irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &rzt2h_icu_chi= p, NULL); + if (ret) + return ret; + + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &priv->fwspec[= hwirq]); +} + +static const struct irq_domain_ops rzt2h_icu_domain_ops =3D { + .alloc =3D rzt2h_icu_alloc, + .free =3D irq_domain_free_irqs_common, + .translate =3D irq_domain_translate_twocell, +}; + +static int rzt2h_icu_parse_interrupts(struct rzt2h_icu_priv *priv, struct = device_node *np) +{ + struct of_phandle_args map; + unsigned int i; + int ret; + + for (i =3D 0; i < RZT2H_ICU_NUM_IRQ; i++) { + ret =3D of_irq_parse_one(np, i, &map); + if (ret) + return ret; + + of_phandle_args_to_fwspec(np, map.args, map.args_count, &priv->fwspec[i]= ); + } + + return 0; +} + +static int rzt2h_icu_init(struct platform_device *pdev, struct device_node= *parent) +{ + struct irq_domain *irq_domain, *parent_domain; + struct device_node *node =3D pdev->dev.of_node; + struct device *dev =3D &pdev->dev; + struct rzt2h_icu_priv *priv; + int ret; + + parent_domain =3D irq_find_host(parent); + if (!parent_domain) + return dev_err_probe(dev, -ENODEV, "cannot find parent domain\n"); + + priv =3D devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + raw_spin_lock_init(&priv->lock); + + platform_set_drvdata(pdev, priv); + + priv->base_ns =3D devm_of_iomap(dev, dev->of_node, 0, NULL); + if (IS_ERR(priv->base_ns)) + return PTR_ERR(priv->base_ns); + + priv->base_s =3D devm_of_iomap(dev, dev->of_node, 1, NULL); + if (IS_ERR(priv->base_s)) + return PTR_ERR(priv->base_s); + + ret =3D rzt2h_icu_parse_interrupts(priv, node); + if (ret) + return dev_err_probe(dev, ret, "cannot parse interrupts: %d\n", ret); + + ret =3D devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "devm_pm_runtime_enable failed: %d\n", re= t); + + ret =3D pm_runtime_resume_and_get(dev); + if (ret) + return dev_err_probe(dev, ret, "pm_runtime_resume_and_get failed: %d\n",= ret); + + irq_domain =3D irq_domain_create_hierarchy(parent_domain, 0, RZT2H_ICU_NU= M_IRQ, + dev_fwnode(dev), &rzt2h_icu_domain_ops, priv); + if (!irq_domain) { + pm_runtime_put(dev); + return -ENOMEM; + } + + return 0; +} + +IRQCHIP_PLATFORM_DRIVER_BEGIN(rzt2h_icu) +IRQCHIP_MATCH("renesas,r9a09g077-icu", rzt2h_icu_init) +IRQCHIP_PLATFORM_DRIVER_END(rzt2h_icu) +MODULE_AUTHOR("Cosmin Tanislav "); +MODULE_DESCRIPTION("Renesas RZ/T2H ICU Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-rene= sas-rzv2h.c index 899a423b5da8..69980a8ecccc 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -20,6 +20,7 @@ #include #include #include +#include =20 /* DT "interrupts" indexes */ #define ICU_IRQ_START 1 @@ -89,6 +90,18 @@ #define ICU_RZG3E_TSSEL_MAX_VAL 0x8c #define ICU_RZV2H_TSSEL_MAX_VAL 0x55 =20 +/** + * struct rzv2h_irqc_reg_cache - registers cache (necessary for suspend/re= sume) + * @nitsr: ICU_NITSR register + * @iitsr: ICU_IITSR register + * @titsr: ICU_TITSR registers + */ +struct rzv2h_irqc_reg_cache { + u32 nitsr; + u32 iitsr; + u32 titsr[2]; +}; + /** * struct rzv2h_hw_info - Interrupt Control Unit controller hardware info = structure. * @tssel_lut: TINT lookup table @@ -118,13 +131,15 @@ struct rzv2h_hw_info { * @fwspec: IRQ firmware specific data * @lock: Lock to serialize access to hardware registers * @info: Pointer to struct rzv2h_hw_info + * @cache: Registers cache for suspend/resume */ -struct rzv2h_icu_priv { +static struct rzv2h_icu_priv { void __iomem *base; struct irq_fwspec fwspec[ICU_NUM_IRQ]; raw_spinlock_t lock; const struct rzv2h_hw_info *info; -}; + struct rzv2h_irqc_reg_cache cache; +} *rzv2h_icu_data; =20 void rzv2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_i= ndex, u8 dmac_channel, u16 req_no) @@ -412,6 +427,44 @@ static int rzv2h_icu_set_type(struct irq_data *d, unsi= gned int type) return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); } =20 +static int rzv2h_irqc_irq_suspend(void *data) +{ + struct rzv2h_irqc_reg_cache *cache =3D &rzv2h_icu_data->cache; + void __iomem *base =3D rzv2h_icu_data->base; + + cache->nitsr =3D readl_relaxed(base + ICU_NITSR); + cache->iitsr =3D readl_relaxed(base + ICU_IITSR); + for (unsigned int i =3D 0; i < 2; i++) + cache->titsr[i] =3D readl_relaxed(base + rzv2h_icu_data->info->t_offs + = ICU_TITSR(i)); + + return 0; +} + +static void rzv2h_irqc_irq_resume(void *data) +{ + struct rzv2h_irqc_reg_cache *cache =3D &rzv2h_icu_data->cache; + void __iomem *base =3D rzv2h_icu_data->base; + + /* + * Restore only interrupt type. TSSRx will be restored at the + * request of pin controller to avoid spurious interrupts due + * to invalid PIN states. + */ + for (unsigned int i =3D 0; i < 2; i++) + writel_relaxed(cache->titsr[i], base + rzv2h_icu_data->info->t_offs + IC= U_TITSR(i)); + writel_relaxed(cache->iitsr, base + ICU_IITSR); + writel_relaxed(cache->nitsr, base + ICU_NITSR); +} + +static const struct syscore_ops rzv2h_irqc_syscore_ops =3D { + .suspend =3D rzv2h_irqc_irq_suspend, + .resume =3D rzv2h_irqc_irq_resume, +}; + +static struct syscore rzv2h_irqc_syscore =3D { + .ops =3D &rzv2h_irqc_syscore_ops, +}; + static const struct irq_chip rzv2h_icu_chip =3D { .name =3D "rzv2h-icu", .irq_eoi =3D rzv2h_icu_eoi, @@ -495,7 +548,6 @@ static int rzv2h_icu_probe_common(struct platform_devic= e *pdev, struct device_no { struct irq_domain *irq_domain, *parent_domain; struct device_node *node =3D pdev->dev.of_node; - struct rzv2h_icu_priv *rzv2h_icu_data; struct reset_control *resetn; int ret; =20 @@ -553,6 +605,8 @@ static int rzv2h_icu_probe_common(struct platform_devic= e *pdev, struct device_no =20 rzv2h_icu_data->info =3D hw_info; =20 + register_syscore(&rzv2h_irqc_syscore); + /* * coccicheck complains about a missing put_device call before returning,= but it's a false * positive. We still need &pdev->dev after successfully returning from t= his function. @@ -616,6 +670,7 @@ static int rzv2h_icu_probe(struct platform_device *pdev= , struct device_node *par =20 IRQCHIP_PLATFORM_DRIVER_BEGIN(rzv2h_icu) IRQCHIP_MATCH("renesas,r9a09g047-icu", rzg3e_icu_probe) +IRQCHIP_MATCH("renesas,r9a09g056-icu", rzv2h_icu_probe) IRQCHIP_MATCH("renesas,r9a09g057-icu", rzv2h_icu_probe) IRQCHIP_PLATFORM_DRIVER_END(rzv2h_icu) MODULE_AUTHOR("Fabrizio Castro "); diff --git a/drivers/irqchip/irq-riscv-aplic-direct.c b/drivers/irqchip/irq= -riscv-aplic-direct.c index c2a75bf3d20c..5a9650225dd8 100644 --- a/drivers/irqchip/irq-riscv-aplic-direct.c +++ b/drivers/irqchip/irq-riscv-aplic-direct.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -171,6 +172,15 @@ static void aplic_idc_set_delivery(struct aplic_idc *i= dc, bool en) writel(de, idc->regs + APLIC_IDC_IDELIVERY); } =20 +void aplic_direct_restore_states(struct aplic_priv *priv) +{ + struct aplic_direct *direct =3D container_of(priv, struct aplic_direct, p= riv); + int cpu; + + for_each_cpu(cpu, &direct->lmask) + aplic_idc_set_delivery(per_cpu_ptr(&aplic_idcs, cpu), true); +} + static int aplic_direct_dying_cpu(unsigned int cpu) { if (aplic_direct_parent_irq) diff --git a/drivers/irqchip/irq-riscv-aplic-main.c b/drivers/irqchip/irq-r= iscv-aplic-main.c index 93e7c51f944a..4495ca26abf5 100644 --- a/drivers/irqchip/irq-riscv-aplic-main.c +++ b/drivers/irqchip/irq-riscv-aplic-main.c @@ -12,10 +12,169 @@ #include #include #include +#include +#include #include +#include =20 #include "irq-riscv-aplic-main.h" =20 +static LIST_HEAD(aplics); + +static void aplic_restore_states(struct aplic_priv *priv) +{ + struct aplic_saved_regs *saved_regs =3D &priv->saved_hw_regs; + struct aplic_src_ctrl *srcs; + void __iomem *regs; + u32 nr_irqs, i; + + regs =3D priv->regs; + writel(saved_regs->domaincfg, regs + APLIC_DOMAINCFG); +#ifdef CONFIG_RISCV_M_MODE + writel(saved_regs->msiaddr, regs + APLIC_xMSICFGADDR); + writel(saved_regs->msiaddrh, regs + APLIC_xMSICFGADDRH); +#endif + /* + * The sourcecfg[i] has to be restored prior to the target[i], interrupt-= pending and + * interrupt-enable bits. The AIA specification states that "Whenever int= errupt source i is + * inactive in an interrupt domain, the corresponding interrupt-pending a= nd interrupt-enable + * bits within the domain are read-only zeros, and register target[i] is = also read-only + * zero." + */ + nr_irqs =3D priv->nr_irqs; + for (i =3D 0; i < nr_irqs; i++) { + srcs =3D &priv->saved_hw_regs.srcs[i]; + writel(srcs->sourcecfg, regs + APLIC_SOURCECFG_BASE + i * sizeof(u32)); + writel(srcs->target, regs + APLIC_TARGET_BASE + i * sizeof(u32)); + } + + for (i =3D 0; i <=3D nr_irqs; i +=3D 32) { + srcs =3D &priv->saved_hw_regs.srcs[i]; + writel(-1U, regs + APLIC_CLRIE_BASE + (i / 32) * sizeof(u32)); + writel(srcs->ie, regs + APLIC_SETIE_BASE + (i / 32) * sizeof(u32)); + + /* Re-trigger the interrupts if it forwards interrupts to target harts b= y MSIs */ + if (!priv->nr_idcs) + writel(readl(regs + APLIC_CLRIP_BASE + (i / 32) * sizeof(u32)), + regs + APLIC_SETIP_BASE + (i / 32) * sizeof(u32)); + } + + if (priv->nr_idcs) + aplic_direct_restore_states(priv); +} + +static void aplic_save_states(struct aplic_priv *priv) +{ + struct aplic_src_ctrl *srcs; + void __iomem *regs; + u32 i, nr_irqs; + + regs =3D priv->regs; + nr_irqs =3D priv->nr_irqs; + /* The valid interrupt source IDs range from 1 to N, where N is priv->nr_= irqs */ + for (i =3D 0; i < nr_irqs; i++) { + srcs =3D &priv->saved_hw_regs.srcs[i]; + srcs->target =3D readl(regs + APLIC_TARGET_BASE + i * sizeof(u32)); + + if (i % 32) + continue; + + srcs->ie =3D readl(regs + APLIC_SETIE_BASE + (i / 32) * sizeof(u32)); + } + + /* Save the nr_irqs bit if needed */ + if (!(nr_irqs % 32)) { + srcs =3D &priv->saved_hw_regs.srcs[nr_irqs]; + srcs->ie =3D readl(regs + APLIC_SETIE_BASE + (nr_irqs / 32) * sizeof(u32= )); + } +} + +static int aplic_syscore_suspend(void *data) +{ + struct aplic_priv *priv; + + list_for_each_entry(priv, &aplics, head) + aplic_save_states(priv); + + return 0; +} + +static void aplic_syscore_resume(void *data) +{ + struct aplic_priv *priv; + + list_for_each_entry(priv, &aplics, head) + aplic_restore_states(priv); +} + +static struct syscore_ops aplic_syscore_ops =3D { + .suspend =3D aplic_syscore_suspend, + .resume =3D aplic_syscore_resume, +}; + +static struct syscore aplic_syscore =3D { + .ops =3D &aplic_syscore_ops, +}; + +static int aplic_pm_notifier(struct notifier_block *nb, unsigned long acti= on, void *data) +{ + struct aplic_priv *priv =3D container_of(nb, struct aplic_priv, genpd_nb); + + switch (action) { + case GENPD_NOTIFY_PRE_OFF: + aplic_save_states(priv); + break; + case GENPD_NOTIFY_ON: + aplic_restore_states(priv); + break; + default: + break; + } + + return 0; +} + +static void aplic_pm_remove(void *data) +{ + struct aplic_priv *priv =3D data; + struct device *dev =3D priv->dev; + + list_del(&priv->head); + if (dev->pm_domain) + dev_pm_genpd_remove_notifier(dev); +} + +static int aplic_pm_add(struct device *dev, struct aplic_priv *priv) +{ + struct aplic_src_ctrl *srcs; + int ret; + + srcs =3D devm_kzalloc(dev, (priv->nr_irqs + 1) * sizeof(*srcs), GFP_KERNE= L); + if (!srcs) + return -ENOMEM; + + priv->saved_hw_regs.srcs =3D srcs; + list_add(&priv->head, &aplics); + if (dev->pm_domain) { + priv->genpd_nb.notifier_call =3D aplic_pm_notifier; + ret =3D dev_pm_genpd_add_notifier(dev, &priv->genpd_nb); + if (ret) + goto remove_head; + + ret =3D devm_pm_runtime_enable(dev); + if (ret) + goto remove_notifier; + } + + return devm_add_action_or_reset(dev, aplic_pm_remove, priv); + +remove_notifier: + dev_pm_genpd_remove_notifier(dev); +remove_head: + list_del(&priv->head); + return ret; +} + void aplic_irq_unmask(struct irq_data *d) { struct aplic_priv *priv =3D irq_data_get_irq_chip_data(d); @@ -60,6 +219,8 @@ int aplic_irq_set_type(struct irq_data *d, unsigned int = type) sourcecfg +=3D (d->hwirq - 1) * sizeof(u32); writel(val, sourcecfg); =20 + priv->saved_hw_regs.srcs[d->hwirq - 1].sourcecfg =3D val; + return 0; } =20 @@ -82,6 +243,7 @@ int aplic_irqdomain_translate(struct irq_fwspec *fwspec,= u32 gsi_base, =20 void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode) { + struct aplic_saved_regs *saved_regs =3D &priv->saved_hw_regs; u32 val; #ifdef CONFIG_RISCV_M_MODE u32 valh; @@ -95,6 +257,8 @@ void aplic_init_hw_global(struct aplic_priv *priv, bool = msi_mode) valh |=3D FIELD_PREP(APLIC_xMSICFGADDRH_HHXS, priv->msicfg.hhxs); writel(val, priv->regs + APLIC_xMSICFGADDR); writel(valh, priv->regs + APLIC_xMSICFGADDRH); + saved_regs->msiaddr =3D val; + saved_regs->msiaddrh =3D valh; } #endif =20 @@ -106,6 +270,8 @@ void aplic_init_hw_global(struct aplic_priv *priv, bool= msi_mode) writel(val, priv->regs + APLIC_DOMAINCFG); if (readl(priv->regs + APLIC_DOMAINCFG) !=3D val) dev_warn(priv->dev, "unable to write 0x%x in domaincfg\n", val); + + saved_regs->domaincfg =3D val; } =20 static void aplic_init_hw_irqs(struct aplic_priv *priv) @@ -176,7 +342,7 @@ int aplic_setup_priv(struct aplic_priv *priv, struct de= vice *dev, void __iomem * /* Setup initial state APLIC interrupts */ aplic_init_hw_irqs(priv); =20 - return 0; + return aplic_pm_add(dev, priv); } =20 static int aplic_probe(struct platform_device *pdev) @@ -209,6 +375,8 @@ static int aplic_probe(struct platform_device *pdev) if (rc) dev_err_probe(dev, rc, "failed to setup APLIC in %s mode\n", msi_mode ? "MSI" : "direct"); + else + register_syscore(&aplic_syscore); =20 #ifdef CONFIG_ACPI if (!acpi_disabled) diff --git a/drivers/irqchip/irq-riscv-aplic-main.h b/drivers/irqchip/irq-r= iscv-aplic-main.h index b0ad8cde69b1..2d8ad7138541 100644 --- a/drivers/irqchip/irq-riscv-aplic-main.h +++ b/drivers/irqchip/irq-riscv-aplic-main.h @@ -23,7 +23,25 @@ struct aplic_msicfg { u32 lhxw; }; =20 +struct aplic_src_ctrl { + u32 sourcecfg; + u32 target; + u32 ie; +}; + +struct aplic_saved_regs { + u32 domaincfg; +#ifdef CONFIG_RISCV_M_MODE + u32 msiaddr; + u32 msiaddrh; +#endif + struct aplic_src_ctrl *srcs; +}; + struct aplic_priv { + struct list_head head; + struct notifier_block genpd_nb; + struct aplic_saved_regs saved_hw_regs; struct device *dev; u32 gsi_base; u32 nr_irqs; @@ -40,6 +58,7 @@ int aplic_irqdomain_translate(struct irq_fwspec *fwspec, = u32 gsi_base, unsigned long *hwirq, unsigned int *type); void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode); int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __i= omem *regs); +void aplic_direct_restore_states(struct aplic_priv *priv); int aplic_direct_setup(struct device *dev, void __iomem *regs); #ifdef CONFIG_RISCV_APLIC_MSI int aplic_msi_setup(struct device *dev, void __iomem *regs); diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-= riscv-imsic-early.c index 6bac67cc0b6d..ba903fa689bd 100644 --- a/drivers/irqchip/irq-riscv-imsic-early.c +++ b/drivers/irqchip/irq-riscv-imsic-early.c @@ -7,6 +7,7 @@ #define pr_fmt(fmt) "riscv-imsic: " fmt #include #include +#include #include #include #include @@ -123,14 +124,8 @@ static void imsic_handle_irq(struct irq_desc *desc) chained_irq_exit(chip, desc); } =20 -static int imsic_starting_cpu(unsigned int cpu) +static void imsic_hw_states_init(void) { - /* Mark per-CPU IMSIC state as online */ - imsic_state_online(); - - /* Enable per-CPU parent interrupt */ - enable_percpu_irq(imsic_parent_irq, irq_get_trigger_type(imsic_parent_irq= )); - /* Setup IPIs */ imsic_ipi_starting_cpu(); =20 @@ -142,6 +137,18 @@ static int imsic_starting_cpu(unsigned int cpu) =20 /* Enable local interrupt delivery */ imsic_local_delivery(true); +} + +static int imsic_starting_cpu(unsigned int cpu) +{ + /* Mark per-CPU IMSIC state as online */ + imsic_state_online(); + + /* Enable per-CPU parent interrupt */ + enable_percpu_irq(imsic_parent_irq, irq_get_trigger_type(imsic_parent_irq= )); + + /* Initialize the IMSIC registers to enable the interrupt delivery */ + imsic_hw_states_init(); =20 return 0; } @@ -157,6 +164,22 @@ static int imsic_dying_cpu(unsigned int cpu) return 0; } =20 +static int imsic_pm_notifier(struct notifier_block *self, unsigned long cm= d, void *v) +{ + switch (cmd) { + case CPU_PM_EXIT: + /* Initialize the IMSIC registers to enable the interrupt delivery */ + imsic_hw_states_init(); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block imsic_pm_notifier_block =3D { + .notifier_call =3D imsic_pm_notifier, +}; + static int __init imsic_early_probe(struct fwnode_handle *fwnode) { struct irq_domain *domain; @@ -194,7 +217,7 @@ static int __init imsic_early_probe(struct fwnode_handl= e *fwnode) cpuhp_setup_state(CPUHP_AP_IRQ_RISCV_IMSIC_STARTING, "irqchip/riscv/imsic= :starting", imsic_starting_cpu, imsic_dying_cpu); =20 - return 0; + return cpu_pm_register_notifier(&imsic_pm_notifier_block); } =20 static int __init imsic_early_dt_init(struct device_node *node, struct dev= ice_node *parent) diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive= -plic.c index 210a57959637..60fd8f91762b 100644 --- a/drivers/irqchip/irq-sifive-plic.c +++ b/drivers/irqchip/irq-sifive-plic.c @@ -68,15 +68,17 @@ #define PLIC_QUIRK_CP100_CLAIM_REGISTER_ERRATUM 1 =20 struct plic_priv { - struct fwnode_handle *fwnode; - struct cpumask lmask; - struct irq_domain *irqdomain; - void __iomem *regs; - unsigned long plic_quirks; - unsigned int nr_irqs; - unsigned long *prio_save; - u32 gsi_base; - int acpi_plic_id; + struct fwnode_handle *fwnode; + struct cpumask lmask; + struct irq_domain *irqdomain; + void __iomem *regs; + unsigned long plic_quirks; + /* device interrupts + 1 to compensate for the reserved hwirq 0 */ + unsigned int __private total_irqs; + unsigned int irq_groups; + unsigned long *prio_save; + u32 gsi_base; + int acpi_plic_id; }; =20 struct plic_handler { @@ -91,6 +93,12 @@ struct plic_handler { u32 *enable_save; struct plic_priv *priv; }; + +/* + * Macro to deal with the insanity of hardware interrupt 0 being reserved = */ +#define for_each_device_irq(iter, priv) \ + for (unsigned int iter =3D 1; iter < ACCESS_PRIVATE(priv, total_irqs); it= er++) + static int plic_parent_irq __ro_after_init; static bool plic_global_setup_done __ro_after_init; static DEFINE_PER_CPU(struct plic_handler, plic_handlers); @@ -257,14 +265,11 @@ static int plic_irq_set_type(struct irq_data *d, unsi= gned int type) =20 static int plic_irq_suspend(void *data) { - struct plic_priv *priv; - - priv =3D per_cpu_ptr(&plic_handlers, smp_processor_id())->priv; + struct plic_priv *priv =3D this_cpu_ptr(&plic_handlers)->priv; =20 - /* irq ID 0 is reserved */ - for (unsigned int i =3D 1; i < priv->nr_irqs; i++) { - __assign_bit(i, priv->prio_save, - readl(priv->regs + PRIORITY_BASE + i * PRIORITY_PER_ID)); + for_each_device_irq(irq, priv) { + __assign_bit(irq, priv->prio_save, + readl(priv->regs + PRIORITY_BASE + irq * PRIORITY_PER_ID)); } =20 return 0; @@ -272,18 +277,15 @@ static int plic_irq_suspend(void *data) =20 static void plic_irq_resume(void *data) { - unsigned int i, index, cpu; + struct plic_priv *priv =3D this_cpu_ptr(&plic_handlers)->priv; + unsigned int index, cpu; unsigned long flags; u32 __iomem *reg; - struct plic_priv *priv; - - priv =3D per_cpu_ptr(&plic_handlers, smp_processor_id())->priv; =20 - /* irq ID 0 is reserved */ - for (i =3D 1; i < priv->nr_irqs; i++) { - index =3D BIT_WORD(i); - writel((priv->prio_save[index] & BIT_MASK(i)) ? 1 : 0, - priv->regs + PRIORITY_BASE + i * PRIORITY_PER_ID); + for_each_device_irq(irq, priv) { + index =3D BIT_WORD(irq); + writel((priv->prio_save[index] & BIT_MASK(irq)) ? 1 : 0, + priv->regs + PRIORITY_BASE + irq * PRIORITY_PER_ID); } =20 for_each_present_cpu(cpu) { @@ -293,7 +295,7 @@ static void plic_irq_resume(void *data) continue; =20 raw_spin_lock_irqsave(&handler->enable_lock, flags); - for (i =3D 0; i < DIV_ROUND_UP(priv->nr_irqs, 32); i++) { + for (unsigned int i =3D 0; i < priv->irq_groups; i++) { reg =3D handler->enable_base + i * sizeof(u32); writel(handler->enable_save[i], reg); } @@ -431,7 +433,7 @@ static u32 cp100_isolate_pending_irq(int nr_irq_groups,= struct plic_handler *han =20 static irq_hw_number_t cp100_get_hwirq(struct plic_handler *handler, void = __iomem *claim) { - int nr_irq_groups =3D DIV_ROUND_UP(handler->priv->nr_irqs, 32); + int nr_irq_groups =3D handler->priv->irq_groups; u32 __iomem *enable =3D handler->enable_base; irq_hw_number_t hwirq =3D 0; u32 iso_mask; @@ -614,7 +616,6 @@ static int plic_probe(struct fwnode_handle *fwnode) struct plic_handler *handler; u32 nr_irqs, parent_hwirq; struct plic_priv *priv; - irq_hw_number_t hwirq; void __iomem *regs; int id, context_id; u32 gsi_base; @@ -647,7 +648,16 @@ static int plic_probe(struct fwnode_handle *fwnode) =20 priv->fwnode =3D fwnode; priv->plic_quirks =3D plic_quirks; - priv->nr_irqs =3D nr_irqs; + /* + * The firmware provides the number of device interrupts. As + * hardware interrupt 0 is reserved, the number of total interrupts + * is nr_irqs + 1. + */ + nr_irqs++; + ACCESS_PRIVATE(priv, total_irqs) =3D nr_irqs; + /* Precalculate the number of register groups */ + priv->irq_groups =3D DIV_ROUND_UP(nr_irqs, 32); + priv->regs =3D regs; priv->gsi_base =3D gsi_base; priv->acpi_plic_id =3D id; @@ -686,7 +696,7 @@ static int plic_probe(struct fwnode_handle *fwnode) u32 __iomem *enable_base =3D priv->regs + CONTEXT_ENABLE_BASE + i * CONTEXT_ENABLE_SIZE; =20 - for (int j =3D 0; j <=3D nr_irqs / 32; j++) + for (int j =3D 0; j < priv->irq_groups; j++) writel(0, enable_base + j); } continue; @@ -718,23 +728,21 @@ static int plic_probe(struct fwnode_handle *fwnode) context_id * CONTEXT_ENABLE_SIZE; handler->priv =3D priv; =20 - handler->enable_save =3D kcalloc(DIV_ROUND_UP(nr_irqs, 32), - sizeof(*handler->enable_save), GFP_KERNEL); + handler->enable_save =3D kcalloc(priv->irq_groups, sizeof(*handler->enab= le_save), + GFP_KERNEL); if (!handler->enable_save) { error =3D -ENOMEM; goto fail_cleanup_contexts; } done: - for (hwirq =3D 1; hwirq <=3D nr_irqs; hwirq++) { + for_each_device_irq(hwirq, priv) { plic_toggle(handler, hwirq, 0); - writel(1, priv->regs + PRIORITY_BASE + - hwirq * PRIORITY_PER_ID); + writel(1, priv->regs + PRIORITY_BASE + hwirq * PRIORITY_PER_ID); } nr_handlers++; } =20 - priv->irqdomain =3D irq_domain_create_linear(fwnode, nr_irqs + 1, - &plic_irqdomain_ops, priv); + priv->irqdomain =3D irq_domain_create_linear(fwnode, nr_irqs, &plic_irqdo= main_ops, priv); if (WARN_ON(!priv->irqdomain)) { error =3D -ENOMEM; goto fail_cleanup_contexts; diff --git a/drivers/irqchip/irq-ti-sci-intr.c b/drivers/irqchip/irq-ti-sci= -intr.c index 354613e74ad0..0ea17040e934 100644 --- a/drivers/irqchip/irq-ti-sci-intr.c +++ b/drivers/irqchip/irq-ti-sci-intr.c @@ -61,12 +61,21 @@ static int ti_sci_intr_irq_domain_translate(struct irq_= domain *domain, { struct ti_sci_intr_irq_domain *intr =3D domain->host_data; =20 - if (fwspec->param_count !=3D 1) - return -EINVAL; + if (intr->type) { + /* Global interrupt-type */ + if (fwspec->param_count !=3D 1) + return -EINVAL; =20 - *hwirq =3D fwspec->param[0]; - *type =3D intr->type; + *hwirq =3D fwspec->param[0]; + *type =3D intr->type; + } else { + /* Per-Line interrupt-type */ + if (fwspec->param_count !=3D 2) + return -EINVAL; =20 + *hwirq =3D fwspec->param[0]; + *type =3D fwspec->param[1]; + } return 0; } =20 @@ -128,11 +137,12 @@ static void ti_sci_intr_irq_domain_free(struct irq_do= main *domain, * @domain: Pointer to the interrupt router IRQ domain * @virq: Corresponding Linux virtual IRQ number * @hwirq: Corresponding hwirq for the IRQ within this IRQ domain + * @hwirq_type: Corresponding hwirq trigger type for the IRQ within this I= RQ domain * * Returns intr output irq if all went well else appropriate error pointer. */ -static int ti_sci_intr_alloc_parent_irq(struct irq_domain *domain, - unsigned int virq, u32 hwirq) +static int ti_sci_intr_alloc_parent_irq(struct irq_domain *domain, unsigne= d int virq, + u32 hwirq, u32 hwirq_type) { struct ti_sci_intr_irq_domain *intr =3D domain->host_data; struct device_node *parent_node; @@ -156,11 +166,22 @@ static int ti_sci_intr_alloc_parent_irq(struct irq_do= main *domain, fwspec.param_count =3D 3; fwspec.param[0] =3D 0; /* SPI */ fwspec.param[1] =3D p_hwirq - 32; /* SPI offset */ - fwspec.param[2] =3D intr->type; + fwspec.param[2] =3D hwirq_type; } else { /* Parent is Interrupt Router */ - fwspec.param_count =3D 1; - fwspec.param[0] =3D p_hwirq; + u32 parent_trigger_type; + + if (!of_property_read_u32(parent_node, "ti,intr-trigger-type", + &parent_trigger_type)) { + /* Parent has global trigger type */ + fwspec.param_count =3D 1; + fwspec.param[0] =3D p_hwirq; + } else { + /* Parent supports per-line trigger types */ + fwspec.param_count =3D 2; + fwspec.param[0] =3D p_hwirq; + fwspec.param[1] =3D hwirq_type; + } } =20 err =3D irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); @@ -196,15 +217,15 @@ static int ti_sci_intr_irq_domain_alloc(struct irq_do= main *domain, void *data) { struct irq_fwspec *fwspec =3D data; + unsigned int hwirq_type; unsigned long hwirq; - unsigned int flags; int err, out_irq; =20 - err =3D ti_sci_intr_irq_domain_translate(domain, fwspec, &hwirq, &flags); + err =3D ti_sci_intr_irq_domain_translate(domain, fwspec, &hwirq, &hwirq_t= ype); if (err) return err; =20 - out_irq =3D ti_sci_intr_alloc_parent_irq(domain, virq, hwirq); + out_irq =3D ti_sci_intr_alloc_parent_irq(domain, virq, hwirq, hwirq_type); if (out_irq < 0) return out_irq; =20 @@ -247,12 +268,9 @@ static int ti_sci_intr_irq_domain_probe(struct platfor= m_device *pdev) return -ENOMEM; =20 intr->dev =3D dev; - ret =3D of_property_read_u32(dev_of_node(dev), "ti,intr-trigger-type", - &intr->type); - if (ret) { - dev_err(dev, "missing ti,intr-trigger-type property\n"); - return -EINVAL; - } + + if (of_property_read_u32(dev_of_node(dev), "ti,intr-trigger-type", &intr-= >type)) + intr->type =3D IRQ_TYPE_NONE; =20 intr->sci =3D devm_ti_sci_get_by_phandle(dev, "ti,sci"); if (IS_ERR(intr->sci)) diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 340a1ff7e92b..198baf890b14 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -423,6 +423,7 @@ config ARCH_R9A09G057 config ARCH_R9A09G077 bool "ARM64 Platform support for R9A09G077 (RZ/T2H)" default y if ARCH_RENESAS + select RENESAS_RZT2H_ICU help This enables support for the Renesas RZ/T2H SoC variants. =20 diff --git a/include/linux/irqchip/irq-renesas-rzt2h.h b/include/linux/irqc= hip/irq-renesas-rzt2h.h new file mode 100644 index 000000000000..853fd5ee0b22 --- /dev/null +++ b/include/linux/irqchip/irq-renesas-rzt2h.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Renesas RZ/T2H Interrupt Control Unit (ICU) + * + * Copyright (C) 2025 Renesas Electronics Corporation. + */ + +#ifndef __LINUX_IRQ_RENESAS_RZT2H +#define __LINUX_IRQ_RENESAS_RZT2H + +#include + +#define RZT2H_ICU_DMAC_REQ_NO_DEFAULT 0x3ff + +#ifdef CONFIG_RENESAS_RZT2H_ICU +void rzt2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_i= ndex, u8 dmac_channel, + u16 req_no); +#else +static inline void rzt2h_icu_register_dma_req(struct platform_device *icu_= dev, u8 dmac_index, + u8 dmac_channel, u16 req_no) { } +#endif + +#endif /* __LINUX_IRQ_RENESAS_RZT2H */