diff --git a/patches-sonic/aspeed-ast2700-support.patch b/patches-sonic/aspeed-ast2700-support.patch index cc9a6c492..edc5a1c55 100644 --- a/patches-sonic/aspeed-ast2700-support.patch +++ b/patches-sonic/aspeed-ast2700-support.patch @@ -1,5 +1,5 @@ From: Chander -Date: Tue, 23 Dec 2025 10:16:25 +0000 +Date: Wed, 08 Apr 2026 18:03:52 +0000 Subject: [PATCH] Add Aspeed AST2700 support This patch adds support for Aspeed AST2700 ARM64 SoC including: @@ -12,13 +12,14 @@ Signed-off-by: Chander --- diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms --- a/arch/arm64/Kconfig.platforms 2025-08-01 08:48:47.000000000 +0000 -+++ b/arch/arm64/Kconfig.platforms 2025-12-23 10:16:21.331029200 +0000 -@@ -40,6 +40,12 @@ ++++ b/arch/arm64/Kconfig.platforms 2026-04-08 18:03:48.534701227 +0000 +@@ -40,6 +40,13 @@ This enables support for Apple's in-house ARM SoC family, starting with the Apple M1. +config ARCH_ASPEED + bool "Aspeed SoC family" ++ select AST2700_IRQ + help + Say yes if you intend to run on an Aspeed ast2700 or similar + seventh generation Aspeed BMCs. @@ -27,8 +28,8 @@ diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms bool "Broadcom SoC Support" diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile ---- a/arch/arm64/boot/dts/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/arch/arm64/boot/dts/Makefile 2025-12-23 10:16:21.337029100 +0000 +--- a/arch/arm64/boot/dts/Makefile 2026-04-08 18:03:23.250162666 +0000 ++++ b/arch/arm64/boot/dts/Makefile 2026-04-08 18:03:48.565700661 +0000 @@ -9,6 +9,7 @@ subdir-y += apm subdir-y += apple @@ -39,8 +40,8 @@ diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile subdir-y += cavium diff --git a/arch/arm64/boot/dts/aspeed/Makefile b/arch/arm64/boot/dts/aspeed/Makefile --- a/arch/arm64/boot/dts/aspeed/Makefile 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/Makefile 2025-12-23 10:16:06.861271783 +0000 -@@ -0,0 +1,16 @@ ++++ b/arch/arm64/boot/dts/aspeed/Makefile 2026-04-08 18:03:31.587010606 +0000 +@@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0 + +dtb-$(CONFIG_ARCH_ASPEED) += \ @@ -56,10 +57,21 @@ diff --git a/arch/arm64/boot/dts/aspeed/Makefile b/arch/arm64/boot/dts/aspeed/Ma + ast2700-evb-256-abr.dtb \ + ast2700-slt.dtb \ + ast2700-fpga.dtb \ -+ ast2700-ci-host.dtb ++ ast2700-ci-host.dtb \ ++ ast2700a1-evb.dtb \ ++ ast2700a1-raw.dtb \ ++ ast2700a1-ncsi.dtb \ ++ ast2700a1-dcscm.dtb \ ++ ast2700a1-dcscm_ast1700a1-evb.dtb \ ++ ast2700a1-dcscm_ast1700a1-evb-dual.dtb \ ++ ast2700a1-dcscm_ast1800-evb.dtb \ ++ ast2700a1-evb-256-abr.dtb \ ++ ast2700a1-evb-s0.dtb \ ++ ast2700a1-evb-s1.dtb \ ++ ast2700a1-ci-host.dtb diff --git a/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-128.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-128.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-128.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-128.dtsi 2025-12-23 10:16:06.861271783 +0000 ++++ b/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-128.dtsi 2026-04-08 18:03:31.587010606 +0000 @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ + @@ -95,7 +107,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-128.dtsi b/arch/ +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-256-abr.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-256-abr.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-256-abr.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-256-abr.dtsi 2025-12-23 10:16:06.861271783 +0000 ++++ b/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-256-abr.dtsi 2026-04-08 18:03:31.587010606 +0000 @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0+ + @@ -156,7 +168,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-256-abr.dtsi b/a +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7-pinctrl.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-g7-pinctrl.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-g7-pinctrl.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-g7-pinctrl.dtsi 2025-12-23 10:16:06.861271783 +0000 ++++ b/arch/arm64/boot/dts/aspeed/aspeed-g7-pinctrl.dtsi 2026-04-08 18:03:31.587010606 +0000 @@ -0,0 +1,1425 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright 2025 ASPEED Corp. @@ -1585,8 +1597,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7-pinctrl.dtsi b/arch/arm64/boot +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi 2025-12-23 10:16:06.862271766 +0000 -@@ -0,0 +1,2870 @@ ++++ b/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi 2026-04-08 18:03:31.587010606 +0000 +@@ -0,0 +1,2923 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include @@ -1838,6 +1850,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + pinctrl-0 = <&pinctrl_usb2axhpd1_default>; + aspeed,device = <&pcie_cfg0>; + aspeed,scu = <&syscon0>; ++ aspeed,enlarge-fifo; + status = "disabled"; + }; + @@ -1871,6 +1884,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + pinctrl-0 = <&pinctrl_usb2bxhpd1_default>; + aspeed,device = <&pcie_cfg1>; + aspeed,scu = <&syscon0>; ++ aspeed,enlarge-fifo; + status = "disabled"; + }; + @@ -2129,8 +2143,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x00 0xff>; -+ ranges = <0x01000000 0x0 0x00000000 0x0 0x00000000 0x0 0x00008000>, /* I/O */ -+ <0x02000000 0x0 0x60000000 0x0 0x60000000 0x0 0x20000000>; /* memory */ ++ ranges = <0x02000000 0x0 0x60000000 0x0 0x60000000 0x0 0x20000000>; + interrupts = ; + resets = <&syscon0 SCU0_RESET_H2X0>, + <&syscon0 SCU0_RESET_PCIE0RST>; @@ -2167,8 +2180,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x00 0xff>; -+ ranges = <0x01000000 0 0x00000000 0x0 0x00000000 0x0 0x00008000>, -+ <0x02000000 0 0x80000000 0x0 0x80000000 0x0 0x20000000>; /* memory */ ++ ranges = <0x02000000 0 0x80000000 0x0 0x80000000 0x0 0x20000000>; + interrupts = ; + resets = <&syscon0 SCU0_RESET_H2X1>, + <&syscon0 SCU0_RESET_PCIE1RST>; @@ -2285,18 +2297,18 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + }; + + scu_ic0: interrupt-controller@1D0 { -+ #interrupt-cells = <1>; + compatible = "aspeed,ast2700-scu-ic0"; + reg = <0x1d0 0xc>; + interrupts = ; ++ #interrupt-cells = <1>; + interrupt-controller; + }; + + scu_ic1: interrupt-controller@1E0 { -+ #interrupt-cells = <1>; + compatible = "aspeed,ast2700-scu-ic1"; + reg = <0x1e0 0xc>; + interrupts = ; ++ #interrupt-cells = <1>; + interrupt-controller; + }; + @@ -2378,6 +2390,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + clocks = <&syscon0 SCU0_CLK_GATE_CRT0CLK>; + resets = <&syscon0 SCU0_RESET_CRT0>; + syscon = <&syscon0>; ++ syscon_io = <&syscon1>; + status = "disabled"; + interrupts-extended = <&gic GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>, + <&scu_ic0 ASPEED_AST2700_SCU_IC0_PCIE_PERST_LO_TO_HI>, @@ -2424,6 +2437,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + pcie_vuart0: serial@12c18000 { + compatible = "aspeed,ast2700-uart"; + reg = <0x0 0x12c18000 0x0 0x40>; ++ reg-shift = <2>; ++ reg-io-width = <4>; + interrupts = ; + clocks = <&syscon0 SCU0_CLK_APB>; + virtual; @@ -2433,6 +2448,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + pcie_vuart1: serial@12c18100 { + compatible = "aspeed,ast2700-uart"; + reg = <0x0 0x12c18100 0x0 0x40>; ++ reg-shift = <2>; ++ reg-io-width = <4>; + interrupts = ; + clocks = <&syscon0 SCU0_CLK_APB>; + virtual; @@ -2442,6 +2459,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + pcie_vuart2: serial@12c18200 { + compatible = "aspeed,ast2700-uart"; + reg = <0x0 0x12c18200 0x0 0x40>; ++ reg-shift = <2>; ++ reg-io-width = <4>; + interrupts = ; + clocks = <&syscon0 SCU0_CLK_APB>; + virtual; @@ -2451,6 +2470,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + pcie_vuart3: serial@12c18300 { + compatible = "aspeed,ast2700-uart"; + reg = <0x0 0x12c18300 0x0 0x40>; ++ reg-shift = <2>; ++ reg-io-width = <4>; + interrupts = ; + clocks = <&syscon0 SCU0_CLK_APB>; + virtual; @@ -2884,18 +2905,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + status = "disabled"; + }; + -+ sgmii: phy@14C01000 { -+ compatible = "aspeed,ast2700-sgmii"; -+ reg = <0x0 0x14c01000 0x0 0x40>; -+ -+ aspeed,plda = <&pcie_phy2>; -+ -+ pinctrl-names = "default"; -+ pinctrl-0 = <&pinctrl_sgmii_default>; -+ #phy-cells = <0>; -+ status = "disabled"; -+ }; -+ + mac0: ethernet@14050000 { + compatible = "aspeed,ast2700-mac", "faraday,ftgmac100"; + reg = <0x0 0x14050000 0x0 0x200>; @@ -2904,6 +2913,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + clocks = <&syscon1 SCU1_CLK_GATE_MAC0CLK>; + resets = <&syscon1 SCU1_RESET_MAC0>; + status = "disabled"; ++ ++ aspeed,scu = <&syscon1>; + }; + + mac1: ethernet@14060000 { @@ -2914,6 +2925,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + clocks = <&syscon1 SCU1_CLK_GATE_MAC1CLK>; + resets = <&syscon1 SCU1_RESET_MAC1>; + status = "disabled"; ++ ++ aspeed,scu = <&syscon1>; + }; + + mac2: ethernet@14070000 { @@ -2927,6 +2940,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + clocks = <&syscon1 SCU1_CLK_GATE_MAC2CLK>; + resets = <&syscon1 SCU1_RESET_MAC2>; + status = "disabled"; ++ ++ aspeed,scu = <&syscon1>; + }; + + sdio_controller: sdc@14080000 { @@ -2968,8 +2983,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x00 0xff>; -+ ranges = <0x01000000 0 0x00000000 0x0 0x00000000 0x0 0x00008000>, -+ <0x02000000 0 0xa0000000 0x0 0xa0000000 0x0 0x20000000>; /* memory */ ++ ranges = <0x02000000 0 0xa0000000 0x0 0xa0000000 0x0 0x20000000>; + interrupts-extended = <&intc1_4 31>; + resets = <&syscon1 SCU1_RESET_H2X>, + <&syscon1 SCU1_RESET_PCIE2RST>; @@ -3092,6 +3106,19 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + status = "disabled"; + }; + ++ sgmii: phy@14c01000 { ++ compatible = "aspeed,ast2700-sgmii"; ++ reg = <0x0 0x14c01000 0x0 0x40>; ++ ++ phys = <&pcie_phy2>; ++ aspeed,scu = <&syscon1>; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_sgmii_default>; ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ + syscon1: syscon@14c02000 { + compatible = "aspeed,ast2700-scu1", "syscon", "simple-mfd"; + reg = <0x0 0x14c02000 0x0 0x1000>; @@ -3101,19 +3128,24 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + #clock-cells = <1>; + #reset-cells = <1>; + ++ assigned-clocks = <&syscon1 SCU1_CLK_MACHCLK>, ++ <&syscon1 SCU1_CLK_RGMII>, ++ <&syscon1 SCU1_CLK_RMII>; ++ assigned-clock-rates = <200000000>, <125000000>, <50000000>; ++ + scu_ic2: interrupt-controller@100 { -+ #interrupt-cells = <1>; + compatible = "aspeed,ast2700-scu-ic2"; + reg = <0x100 0x8>; + interrupts-extended = <&intc1_5 0>; ++ #interrupt-cells = <1>; + interrupt-controller; + }; + + scu_ic3: interrupt-controller@108 { -+ #interrupt-cells = <1>; + compatible = "aspeed,ast2700-scu-ic3"; + reg = <0x108 0x8>; + interrupts-extended = <&intc1_5 26>; ++ #interrupt-cells = <1>; + interrupt-controller; + }; + @@ -3367,6 +3399,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + pcie_phy2: phy@14c1c000 { + compatible = "aspeed,ast2700-pcie-phy", "syscon"; + reg = <0x0 0x14c1c000 0x0 0x800>; ++ #phy-cells = <0>; + }; + + e2m_config2: e2m-config@14c1d000 { @@ -3671,36 +3704,48 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + vuart0: serial@14c30000 { + compatible = "aspeed,ast2700-uart"; + reg = <0x0 0x14c30000 0x0 0x40>; ++ reg-shift = <2>; ++ reg-io-width = <4>; + interrupts-extended = <&intc1_0 17>; + clocks = <&syscon0 SCU0_CLK_APB>; + virtual; ++ dma-channel = <12>; + status = "disabled"; + }; + + vuart1: serial@14c30100 { + compatible = "aspeed,ast2700-uart"; + reg = <0x0 0x14c30100 0x0 0x40>; ++ reg-shift = <2>; ++ reg-io-width = <4>; + interrupts-extended = <&intc1_0 18>; + clocks = <&syscon0 SCU0_CLK_APB>; + virtual; ++ dma-channel = <13>; + status = "disabled"; + }; + + vuart2: serial@14c30200 { + compatible = "aspeed,ast2700-uart"; + reg = <0x0 0x14c30200 0x0 0x40>; ++ reg-shift = <2>; ++ reg-io-width = <4>; + interrupts-extended = <&intc1_1 17>; + clocks = <&syscon0 SCU0_CLK_APB>; + virtual; ++ dma-channel = <14>; + status = "disabled"; + }; + + vuart3: serial@14c30300 { + compatible = "aspeed,ast2700-uart"; + reg = <0x0 0x14c30300 0x0 0x40>; ++ reg-shift = <2>; ++ reg-io-width = <4>; + interrupts-extended = <&intc1_1 18>; + clocks = <&syscon0 SCU0_CLK_APB>; + virtual; ++ dma-channel = <15>; + status = "disabled"; + }; + @@ -3871,6 +3916,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + no-loopback-test; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_txd0_default &pinctrl_rxd0_default>; ++ dma-channel = <0>; + status = "disabled"; + }; + @@ -3885,6 +3931,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + no-loopback-test; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_txd1_default &pinctrl_rxd1_default>; ++ dma-channel = <1>; + status = "disabled"; + }; + @@ -3899,6 +3946,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + no-loopback-test; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_txd2_default &pinctrl_rxd2_default>; ++ dma-channel = <2>; + status = "disabled"; + }; + @@ -3913,6 +3961,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + no-loopback-test; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_txd3_default &pinctrl_rxd3_default>; ++ dma-channel = <3>; + status = "disabled"; + }; + @@ -3927,6 +3976,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + no-loopback-test; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_txd5_default &pinctrl_rxd5_default>; ++ dma-channel = <4>; + status = "disabled"; + }; + @@ -3941,6 +3991,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + no-loopback-test; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_txd6_default &pinctrl_rxd6_default>; ++ dma-channel = <5>; + status = "disabled"; + }; + @@ -3955,6 +4006,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + no-loopback-test; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_txd7_default &pinctrl_rxd7_default>; ++ dma-channel = <6>; + status = "disabled"; + }; + @@ -3969,6 +4021,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + no-loopback-test; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_txd8_default &pinctrl_rxd8_default>; ++ dma-channel = <7>; + status = "disabled"; + }; + @@ -3982,6 +4035,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + no-loopback-test; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_txd9_default &pinctrl_rxd9_default>; ++ dma-channel = <8>; + status = "disabled"; + }; + @@ -3995,6 +4049,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + no-loopback-test; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_txd10_default &pinctrl_rxd10_default>; ++ dma-channel = <9>; + status = "disabled"; + }; + @@ -4008,6 +4063,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + no-loopback-test; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_txd11_default &pinctrl_rxd11_default>; ++ dma-channel = <10>; + status = "disabled"; + }; + @@ -4020,6 +4076,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + interrupts-extended = <&intc1_4 18>; + no-loopback-test; + pinctrl-names = "default"; ++ dma-channel = <11>; + status = "disabled"; + }; + @@ -4049,10 +4106,14 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + + ltpi0: ltpi@14c34000 { + compatible = "aspeed-ltpi"; -+ reg = <0x0 0x14c34000 0x0 0x100>; ++ reg = <0x0 0x14c34000 0x0 0x100>, ++ <0x0 0x14c34200 0x0 0x100>, ++ <0x0 0x14c34800 0x0 0x100>; ++ reg-names = "base", "phy", "top"; + clocks = <&syscon1 SCU1_CLK_GATE_LTPICLK>, + <&syscon1 SCU1_CLK_GATE_LTPIPHYCLK>; + clock-names = "ahb", "phy"; ++ aspeed,scu = <&syscon1>; + resets = <&syscon1 SCU1_RESET_LTPI0>; + interrupts-extended = <&intc1_5 12>; + status = "disabled"; @@ -4072,10 +4133,14 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + + ltpi1: ltpi@14c35000 { + compatible = "aspeed-ltpi"; -+ reg = <0x0 0x14c35000 0x0 0x100>; ++ reg = <0x0 0x14c35000 0x0 0x100>, ++ <0x0 0x14c35200 0x0 0x100>, ++ <0x0 0x14c35800 0x0 0x100>; ++ reg-names = "base", "phy", "top"; + clocks = <&syscon1 SCU1_CLK_GATE_LTPICLK>, + <&syscon1 SCU1_CLK_GATE_LTPI1TXCLK>; + clock-names = "ahb", "phy"; ++ aspeed,scu = <&syscon1>; + resets = <&syscon1 SCU1_RESET_LTPI1>; + interrupts-extended = <&intc1_5 13>; + status = "disabled"; @@ -4155,7 +4220,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0x0 0x23800000 0x0 0x94>; + interrupts-extended = <&intc1_5 7>; + pinctrl-names = "default"; -+ pinctrl-0 = <&pinctrl_fsi1_default>; ++ pinctrl-0 = <&pinctrl_fsi2_default>; + clocks = <&syscon1 SCU1_CLK_GATE_FSICLK>; + resets = <&syscon1 SCU1_RESET_FSI>; + status = "disabled"; @@ -4189,7 +4254,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c0: i2c-bus@100 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x100 0x80>, <0x1A0 0x20>; ++ reg = <0x100 0xA0>, <0x1C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + aspeed,enable-dma; @@ -4206,7 +4271,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c1: i2c-bus@200 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x200 0x80>, <0x2A0 0x20>; ++ reg = <0x200 0xA0>, <0x2C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + aspeed,enable-dma; @@ -4223,7 +4288,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c2: i2c-bus@300 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x300 0x80>, <0x3A0 0x20>; ++ reg = <0x300 0xA0>, <0x3C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + aspeed,enable-dma; @@ -4240,7 +4305,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c3: i2c-bus@400 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x400 0x80>, <0x4A0 0x20>; ++ reg = <0x400 0xA0>, <0x4C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + clocks = <&syscon1 SCU1_CLK_APB>; @@ -4256,7 +4321,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c4: i2c-bus@500 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x500 0x80>, <0x5A0 0x20>; ++ reg = <0x500 0xA0>, <0x5C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + aspeed,enable-dma; @@ -4273,7 +4338,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c5: i2c-bus@600 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x600 0x80>, <0x6A0 0x20>; ++ reg = <0x600 0xA0>, <0x6C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + aspeed,enable-dma; @@ -4290,7 +4355,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c6: i2c-bus@700 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x700 0x80>, <0x7A0 0x20>; ++ reg = <0x700 0xA0>, <0x7C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + aspeed,enable-dma; @@ -4307,7 +4372,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c7: i2c-bus@800 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x800 0x80>, <0x8A0 0x20>; ++ reg = <0x800 0xA0>, <0x8C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + aspeed,enable-dma; @@ -4324,7 +4389,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c8: i2c-bus@900 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x900 0x80>, <0x9A0 0x20>; ++ reg = <0x900 0xA0>, <0x9C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + aspeed,enable-dma; @@ -4341,7 +4406,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c9: i2c-bus@a00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xA00 0x80>, <0xAA0 0x20>; ++ reg = <0xA00 0xA0>, <0xAC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + aspeed,enable-dma; @@ -4358,7 +4423,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c10: i2c-bus@b00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xB00 0x80>, <0xBA0 0x20>; ++ reg = <0xB00 0xA0>, <0xBC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + aspeed,enable-dma; @@ -4375,7 +4440,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c11: i2c-bus@c00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xC00 0x80>, <0xCA0 0x20>; ++ reg = <0xC00 0xA0>, <0xCC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + aspeed,enable-dma; @@ -4392,7 +4457,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c12: i2c-bus@d00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xD00 0x80>, <0xDA0 0x20>; ++ reg = <0xD00 0xA0>, <0xDC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + aspeed,enable-dma; @@ -4409,7 +4474,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c13: i2c-bus@e00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xE00 0x80>, <0xEA0 0x20>; ++ reg = <0xE00 0xA0>, <0xEC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + aspeed,enable-dma; @@ -4426,7 +4491,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c14: i2c-bus@f00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xF00 0x80>, <0xFA0 0x20>; ++ reg = <0xF00 0xA0>, <0xFC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + aspeed,enable-dma; @@ -4443,7 +4508,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + i2c15: i2c-bus@1000 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x1000 0x80>, <0x10A0 0x20>; ++ reg = <0x1000 0xA0>, <0x10C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; + aspeed,enable-dma; @@ -4459,7 +4524,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0-pinctrl.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-ltpi0-pinctrl.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0-pinctrl.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi0-pinctrl.dtsi 2025-12-23 10:16:06.862271766 +0000 ++++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi0-pinctrl.dtsi 2026-04-08 18:03:31.587010606 +0000 @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -4872,7 +4937,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0-pinctrl.dtsi b/arch/arm64/b +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi 2025-12-23 10:16:06.862271766 +0000 ++++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi 2026-04-08 18:03:31.587010606 +0000 @@ -0,0 +1,659 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include @@ -5264,7 +5329,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c0: i2c-bus@100 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x100 0x80>, <0x1a0 0x20>; ++ reg = <0x100 0xA0>, <0x1C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5281,7 +5346,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c1: i2c-bus@200 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x200 0x80>, <0x2a0 0x20>; ++ reg = <0x200 0xA0>, <0x2C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5298,7 +5363,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c2: i2c-bus@300 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x300 0x80>, <0x3a0 0x20>; ++ reg = <0x300 0xA0>, <0x3C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5315,7 +5380,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c3: i2c-bus@400 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x400 0x80>, <0x4a0 0x20>; ++ reg = <0x400 0xA0>, <0x4C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5332,7 +5397,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c4: i2c-bus@500 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x500 0x80>, <0x5a0 0x20>; ++ reg = <0x500 0xA0>, <0x5C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5349,7 +5414,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c5: i2c-bus@600 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x600 0x80>, <0x6a0 0x20>; ++ reg = <0x600 0xA0>, <0x6C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5366,7 +5431,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c6: i2c-bus@700 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x700 0x80>, <0x7a0 0x20>; ++ reg = <0x700 0xA0>, <0x7C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5383,7 +5448,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c7: i2c-bus@800 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x800 0x80>, <0x8a0 0x20>; ++ reg = <0x800 0xA0>, <0x8C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5400,7 +5465,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c8: i2c-bus@900 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x900 0x80>, <0x9a0 0x20>; ++ reg = <0x900 0xA0>, <0x9C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5417,7 +5482,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c9: i2c-bus@a00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xa00 0x80>, <0xaa0 0x20>; ++ reg = <0xA00 0xA0>, <0xAC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5434,7 +5499,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c10: i2c-bus@b00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xb00 0x80>, <0xba0 0x20>; ++ reg = <0xB00 0xA0>, <0xBC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5451,7 +5516,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c11: i2c-bus@c00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xc00 0x80>, <0xca0 0x20>; ++ reg = <0xC00 0xA0>, <0xCC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5468,7 +5533,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c12: i2c-bus@d00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xd00 0x80>, <0xda0 0x20>; ++ reg = <0xD00 0xA0>, <0xDC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5485,7 +5550,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c13: i2c-bus@e00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xe00 0x80>, <0xea0 0x20>; ++ reg = <0xE00 0xA0>, <0xEC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5502,7 +5567,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c14: i2c-bus@f00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xf00 0x80>, <0xfa0 0x20>; ++ reg = <0xF00 0xA0>, <0xFC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5519,7 +5584,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + ltpi0_i2c15: i2c-bus@1000 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x1000 0x80>, <0x10a0 0x20>; ++ reg = <0x1000 0xA0>, <0x10C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -5535,7 +5600,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1-pinctrl.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1-pinctrl.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1-pinctrl.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1-pinctrl.dtsi 2025-12-23 10:16:06.862271766 +0000 ++++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1-pinctrl.dtsi 2026-04-08 18:03:31.587010606 +0000 @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -5948,7 +6013,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1-pinctrl.dtsi b/arch/arm64/b +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi 2025-12-23 10:16:06.862271766 +0000 ++++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi 2026-04-08 18:03:31.587010606 +0000 @@ -0,0 +1,659 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include @@ -6340,7 +6405,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c0: i2c-bus@100 { + #address-cells = <1>; + #size-cells = <1>; -+ reg = <0x100 0x80>, <0x1a0 0x20>; ++ reg = <0x100 0xA0>, <0x1C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6357,7 +6422,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c1: i2c-bus@200 { + #address-cells = <1>; + #size-cells = <1>; -+ reg = <0x200 0x80>, <0x2a0 0x20>; ++ reg = <0x200 0xA0>, <0x2C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6374,7 +6439,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c2: i2c-bus@300 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x300 0x80>, <0x3a0 0x20>; ++ reg = <0x300 0xA0>, <0x3C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6391,7 +6456,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c3: i2c-bus@400 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x400 0x80>, <0x4a0 0x20>; ++ reg = <0x400 0xA0>, <0x4C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6408,7 +6473,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c4: i2c-bus@500 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x500 0x80>, <0x5a0 0x20>; ++ reg = <0x500 0xA0>, <0x5C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6425,7 +6490,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c5: i2c-bus@600 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x600 0x80>, <0x6a0 0x20>; ++ reg = <0x600 0xA0>, <0x6C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6442,7 +6507,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c6: i2c-bus@700 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x700 0x80>, <0x7a0 0x20>; ++ reg = <0x700 0xA0>, <0x7C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6459,7 +6524,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c7: i2c-bus@800 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x800 0x80>, <0x8a0 0x20>; ++ reg = <0x800 0xA0>, <0x8C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6476,7 +6541,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c8: i2c-bus@900 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x900 0x80>, <0x9a0 0x20>; ++ reg = <0x900 0xA0>, <0x9C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6493,7 +6558,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c9: i2c-bus@a00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xa00 0x80>, <0xaa0 0x20>; ++ reg = <0xA00 0xA0>, <0xAC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6510,7 +6575,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c10: i2c-bus@b00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xb00 0x80>, <0xba0 0x20>; ++ reg = <0xB00 0xA0>, <0xBC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6527,7 +6592,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c11: i2c-bus@c00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xc00 0x80>, <0xca0 0x20>; ++ reg = <0xC00 0xA0>, <0xCC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6544,7 +6609,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c12: i2c-bus@d00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xd00 0x80>, <0xda0 0x20>; ++ reg = <0xD00 0xA0>, <0xDC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6561,7 +6626,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c13: i2c-bus@e00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xe00 0x80>, <0xea0 0x20>; ++ reg = <0xE00 0xA0>, <0xEC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6578,7 +6643,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c14: i2c-bus@f00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xf00 0x80>, <0xfa0 0x20>; ++ reg = <0xF00 0xA0>, <0xFC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6595,7 +6660,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + ltpi1_i2c15: i2c-bus@1000 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x1000 0x80>, <0x10a0 0x20>; ++ reg = <0x1000 0xA0>, <0x10C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi1_i2c_global>; + aspeed,enable-dma; @@ -6611,7 +6676,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi 2025-12-23 10:16:06.862271766 +0000 ++++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi 2026-04-08 18:03:31.587010606 +0000 @@ -0,0 +1,647 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include @@ -6991,7 +7056,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c0: i2c-bus@100 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x100 0x80>, <0x1a0 0x20>; ++ reg = <0x100 0xA0>, <0x1C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7008,7 +7073,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c1: i2c-bus@200 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x200 0x80>, <0x2a0 0x20>; ++ reg = <0x200 0xA0>, <0x2C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7025,7 +7090,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c2: i2c-bus@300 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x300 0x80>, <0x3a0 0x20>; ++ reg = <0x300 0xA0>, <0x3C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7042,7 +7107,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c3: i2c-bus@400 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x400 0x80>, <0x4a0 0x20>; ++ reg = <0x400 0xA0>, <0x4C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7059,7 +7124,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c4: i2c-bus@500 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x500 0x80>, <0x5a0 0x20>; ++ reg = <0x500 0xA0>, <0x5C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7076,7 +7141,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c5: i2c-bus@600 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x600 0x80>, <0x6a0 0x20>; ++ reg = <0x600 0xA0>, <0x6C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7093,7 +7158,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c6: i2c-bus@700 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x700 0x80>, <0x7a0 0x20>; ++ reg = <0x700 0xA0>, <0x7C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7110,7 +7175,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c7: i2c-bus@800 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x800 0x80>, <0x8a0 0x20>; ++ reg = <0x800 0xA0>, <0x8C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7127,7 +7192,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c8: i2c-bus@900 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x900 0x80>, <0x9a0 0x20>; ++ reg = <0x900 0xA0>, <0x9C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7144,7 +7209,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c9: i2c-bus@a00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xa00 0x80>, <0xaa0 0x20>; ++ reg = <0xA00 0xA0>, <0xAC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7161,7 +7226,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c10: i2c-bus@b00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xb00 0x80>, <0xba0 0x20>; ++ reg = <0xB00 0xA0>, <0xBC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7178,7 +7243,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c11: i2c-bus@c00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xc00 0x80>, <0xca0 0x20>; ++ reg = <0xC00 0xA0>, <0xCC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7195,7 +7260,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c12: i2c-bus@d00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xd00 0x80>, <0xda0 0x20>; ++ reg = <0xD00 0xA0>, <0xDC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7212,7 +7277,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c13: i2c-bus@e00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xe00 0x80>, <0xea0 0x20>; ++ reg = <0xE00 0xA0>, <0xEC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7229,7 +7294,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c14: i2c-bus@f00 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0xf00 0x80>, <0xfa0 0x20>; ++ reg = <0xF00 0xA0>, <0xFC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7246,7 +7311,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d + ltpi0_i2c15: i2c-bus@1000 { + #address-cells = <1>; + #size-cells = <0>; -+ reg = <0x1000 0x80>, <0x10a0 0x20>; ++ reg = <0x1000 0xA0>, <0x10C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <<pi0_i2c_global>; + aspeed,enable-dma; @@ -7262,8 +7327,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-ci-host.dts b/arch/arm64/boot/dts/aspeed/ast2700-ci-host.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-ci-host.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-ci-host.dts 2025-12-23 10:16:06.862271766 +0000 -@@ -0,0 +1,97 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-ci-host.dts 2026-04-08 18:03:31.587010606 +0000 +@@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; @@ -7361,10 +7426,40 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-ci-host.dts b/arch/arm64/boot/dt +&pcie1 { + status = "okay"; +}; ++ ++&mdio0 { ++ status = "disabled"; ++}; ++ ++&mac0 { ++ status = "disabled"; ++}; ++ ++&mdio2 { ++ status = "okay"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ ethphy2: ethernet-phy@0 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <0>; ++ }; ++}; ++ ++&sgmii { ++ status = "okay"; ++}; ++ ++&mac2 { ++ status = "okay"; ++ ++ phy-mode = "sgmii"; ++ phy-handle = <ðphy2>; ++}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts b/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts 2025-12-23 10:16:06.862271766 +0000 -@@ -0,0 +1,978 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts 2026-04-08 18:03:31.587010606 +0000 +@@ -0,0 +1,973 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; @@ -7860,13 +7955,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts b/arch/arm64/boot/dts/ + pinctrl-0 = <&pinctrl_rgmii0_default &pinctrl_rgmii0_driving>; +}; + -+&syscon1 { -+ assigned-clocks = <&syscon1 SCU1_CLK_MACHCLK>, -+ <&syscon1 SCU1_CLK_RGMII>, -+ <&syscon1 SCU1_CLK_RMII>; -+ assigned-clock-rates = <200000000>, <125000000>, <50000000>; -+}; -+ +&jtag1 { + status = "okay"; +}; @@ -7956,6 +8044,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts b/arch/arm64/boot/dts/ + perif-mcyc-enable; + perif-mcyc-src-addr = <0x0 0x98000000>; + perif-mcyc-size = <0x0 0x10000>; ++ vw-pltrst-monitor; + oob-dma-mode; + flash-dma-mode; +}; @@ -7971,6 +8060,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts b/arch/arm64/boot/dts/ + perif-mcyc-enable; + perif-mcyc-src-addr = <0x0 0x98000000>; + perif-mcyc-size = <0x0 0x10000>; ++ vw-pltrst-monitor; + oob-dma-mode; + flash-dma-mode; +}; @@ -8345,7 +8435,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts b/arch/arm64/boot/dts/ +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb-dual.dts b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb-dual.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb-dual.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb-dual.dts 2025-12-23 10:16:06.862271766 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb-dual.dts 2026-04-08 18:03:31.587010606 +0000 @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -8567,7 +8657,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb-dual.dts b/arc +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb.dts b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb.dts 2025-12-23 10:16:06.862271766 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb.dts 2026-04-08 18:03:31.587010606 +0000 @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -9027,7 +9117,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb.dts b/arch/arm +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1800-evb.dts b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1800-evb.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1800-evb.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1800-evb.dts 2025-12-23 10:16:06.862271766 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1800-evb.dts 2026-04-08 18:03:31.587010606 +0000 @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -9467,7 +9557,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1800-evb.dts b/arch/arm +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-256-abr.dts b/arch/arm64/boot/dts/aspeed/ast2700-evb-256-abr.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-evb-256-abr.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-evb-256-abr.dts 2025-12-23 10:16:06.862271766 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-evb-256-abr.dts 2026-04-08 18:03:31.587010606 +0000 @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -9511,7 +9601,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-256-abr.dts b/arch/arm64/boo + diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts 2025-12-23 10:16:06.862271766 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts 2026-04-08 18:03:31.587010606 +0000 @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -9882,8 +9972,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts 2025-12-23 10:16:06.862271766 +0000 -@@ -0,0 +1,465 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ast2700-evb.dts" @@ -10319,10 +10409,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + mac1-clk-delay = <0 0 + 0 0 + 0 0>; -+ assigned-clocks = <&syscon1 SCU1_CLK_MACHCLK>, -+ <&syscon1 SCU1_CLK_RGMII>, -+ <&syscon1 SCU1_CLK_RMII>; -+ assigned-clock-rates = <200000000>, <125000000>, <50000000>; +}; + +&vhuba0 { @@ -10349,10 +10435,30 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts +&vhubb1 { + status = "disabled"; +}; ++ ++&vhubc { ++ status = "disabled"; ++}; ++ ++&vhubd { ++ status = "okay"; ++}; ++ ++&ehci2 { ++ status = "okay"; ++}; ++ ++&ehci3 { ++ status = "disabled"; ++}; ++ ++&pcie2 { ++ status = "okay"; ++}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/aspeed/ast2700-evb.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-evb.dts 2025-12-23 10:16:06.862271766 +0000 -@@ -0,0 +1,1270 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-evb.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,1271 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; @@ -10364,7 +10470,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as +#define DUAL_NODE 0 // 1: DUAL_NODE, 0: SINGLE_NODE +#define PCIE0_EP 1 // 1: EP, 0: RC +#define PCIE1_EP 1 // 1: EP, 0: RC -+#define PCIE2_RC 1 // 1: RC, 0: SGMII + +/ { + model = "AST2700 EVB"; @@ -10624,7 +10729,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + pinctrl-0 = <&pinctrl_adc8_default &pinctrl_adc9_default + &pinctrl_adc10_default &pinctrl_adc11_default + &pinctrl_adc12_default &pinctrl_adc13_default -+ &pinctrl_adc14_default &pinctrl_adc15_default>; ++ &pinctrl_adc14_default &pinctrl_adc15_default ++ &pinctrl_adc13_bias>; +}; + +&pinctrl0 { @@ -10710,6 +10816,10 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + "C18", "C6", "C7", "D7", "N13", "C8"; + drive-strength = <1>; + }; ++ pinctrl_adc13_bias: adc13-bias { ++ pins = "W14"; ++ bias-disable; ++ }; +}; + +&gpio1 { @@ -11037,6 +11147,9 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rgmii0_default &pinctrl_rgmii0_driving>; ++ ++ rx-internal-delay-ps = <0>; ++ tx-internal-delay-ps = <0>; +}; + +&mac1 { @@ -11047,6 +11160,9 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rgmii1_default &pinctrl_rgmii1_driving>; ++ ++ rx-internal-delay-ps = <0>; ++ tx-internal-delay-ps = <0>; +}; + +#if 0 // Default to disable RC & SGMII @@ -11080,13 +11196,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as +#endif +#endif + -+&syscon1 { -+ assigned-clocks = <&syscon1 SCU1_CLK_MACHCLK>, -+ <&syscon1 SCU1_CLK_RGMII>, -+ <&syscon1 SCU1_CLK_RMII>; -+ assigned-clock-rates = <200000000>, <125000000>, <50000000>; -+}; -+ +&espi0 { + status = "okay"; + perif-dma-mode; @@ -11099,6 +11208,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + perif-mcyc-size = <0x0 0x10000>; + memory-region = <&espi0_mcyc_memory>; + perif-rtc-enable; ++ vw-pltrst-monitor; + oob-dma-mode; + flash-dma-mode; +#if 0 // eDAF mode: Change 1 to enable MIX mode in Linux, but HW mode in SPL would be overwritten @@ -11136,6 +11246,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + perif-mcyc-src-addr = <0x0 0x98000000>; + perif-mcyc-size = <0x0 0x10000>; + perif-rtc-enable; ++ vw-pltrst-monitor; + oob-dma-mode; + flash-dma-mode; +#if 0 // eDAF mode: Change 1 to enable MIX mode in Linux, but HW mode in SPL would be overwritten @@ -11526,7 +11637,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + +&uhci0 { + status = "okay"; -+ memory-region = <&uhci0_reserved>; +}; + +#endif @@ -11561,16 +11671,14 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + pinctrl-0 = <&pinctrl_usb3axhp_default &pinctrl_usb2axhp_default>; +}; + -+&usb3bhp { -+ status = "okay"; -+}; -+ -+&uphy2b { ++&vhubb0 { + status = "okay"; ++ pinctrl-0 = <&pinctrl_usb2bhpd0_default>; +}; + -+&vhubb1 { ++&usb3bhp { + status = "okay"; ++ pinctrl-0 = <&pinctrl_usb3bxhp_default &pinctrl_usb2bxhp_default>; +}; + +&vhubc { @@ -11587,7 +11695,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + +&uhci1 { + status = "okay"; -+ memory-region = <&uhci1_reserved>; +}; + +&wdt0 { @@ -11625,8 +11732,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as +#endif diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evbeeprom.dts b/arch/arm64/boot/dts/aspeed/ast2700-evbeeprom.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-evbeeprom.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-evbeeprom.dts 2025-12-23 10:16:06.862271766 +0000 -@@ -0,0 +1,1179 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-evbeeprom.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,1175 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; @@ -12310,10 +12417,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evbeeprom.dts b/arch/arm64/boot/ + mac1-clk-delay = <0x31 0x31 + 0x10 0x10 + 0x10 0x10>; -+ assigned-clocks = <&syscon1 SCU1_CLK_MACHCLK>, -+ <&syscon1 SCU1_CLK_RGMII>, -+ <&syscon1 SCU1_CLK_RMII>; -+ assigned-clock-rates = <200000000>, <125000000>, <50000000>; +}; + +&espi0 { @@ -12808,8 +12911,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evbeeprom.dts b/arch/arm64/boot/ +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts b/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts 2025-12-23 10:16:06.862271766 +0000 -@@ -0,0 +1,1179 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,1175 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; @@ -13493,10 +13596,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts b/arch/arm64/boot/dts + mac1-clk-delay = <0x31 0x31 + 0x10 0x10 + 0x10 0x10>; -+ assigned-clocks = <&syscon1 SCU1_CLK_MACHCLK>, -+ <&syscon1 SCU1_CLK_RGMII>, -+ <&syscon1 SCU1_CLK_RMII>; -+ assigned-clock-rates = <200000000>, <125000000>, <50000000>; +}; + +&espi0 { @@ -13991,8 +14090,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts b/arch/arm64/boot/dts +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts b/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts 2025-12-23 10:16:06.862271766 +0000 -@@ -0,0 +1,1132 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,1128 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; @@ -14671,10 +14770,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts b/arch/arm64/boot/dts/a + mac1-clk-delay = <0x18 0x17 + 0x10 0x10 + 0x10 0x10>; -+ assigned-clocks = <&syscon1 SCU1_CLK_MACHCLK>, -+ <&syscon1 SCU1_CLK_RGMII>, -+ <&syscon1 SCU1_CLK_RMII>; -+ assigned-clock-rates = <200000000>, <125000000>, <50000000>; +}; + +&espi0 { @@ -15127,8 +15222,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts b/arch/arm64/boot/dts/a +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-ncsi.dts b/arch/arm64/boot/dts/aspeed/ast2700-ncsi.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-ncsi.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-ncsi.dts 2025-12-23 10:16:06.862271766 +0000 -@@ -0,0 +1,52 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-ncsi.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; @@ -15176,14 +15271,10 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-ncsi.dts b/arch/arm64/boot/dts/a + mac1-clk-delay = <0 0 + 0 0 + 0 0>; -+ assigned-clocks = <&syscon1 SCU1_CLK_MACHCLK>, -+ <&syscon1 SCU1_CLK_RGMII>, -+ <&syscon1 SCU1_CLK_RMII>; -+ assigned-clock-rates = <200000000>, <125000000>, <50000000>; +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-raw.dts b/arch/arm64/boot/dts/aspeed/ast2700-raw.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-raw.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-raw.dts 2025-12-23 10:16:06.862271766 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-raw.dts 2026-04-08 18:03:31.588010588 +0000 @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -15407,8 +15498,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-raw.dts b/arch/arm64/boot/dts/as + diff --git a/arch/arm64/boot/dts/aspeed/ast2700-reserved-mem.dtsi b/arch/arm64/boot/dts/aspeed/ast2700-reserved-mem.dtsi --- a/arch/arm64/boot/dts/aspeed/ast2700-reserved-mem.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-reserved-mem.dtsi 2025-12-23 10:16:06.862271766 +0000 -@@ -0,0 +1,126 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-reserved-mem.dtsi 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* AST2700 reserved memories with no-map property */ @@ -15437,6 +15528,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-reserved-mem.dtsi b/arch/arm64/b + +#if PCIE0_EP +bmc_dev0_memory: bmc-dev0-memory@423800000 { ++ compatible = "shared-dma-pool"; + reg = <0x4 0x23800000 0x0 0x100000>; + no-map; +}; @@ -15455,6 +15547,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-reserved-mem.dtsi b/arch/arm64/b + +#if PCIE1_EP +bmc_dev1_memory: bmc-dev1-memory@423900000 { ++ compatible = "shared-dma-pool"; + reg = <0x4 0x23900000 0x0 0x100000>; + no-map; +}; @@ -15537,8 +15630,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-reserved-mem.dtsi b/arch/arm64/b +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-slt.dts b/arch/arm64/boot/dts/aspeed/ast2700-slt.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-slt.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-slt.dts 2025-12-23 10:16:06.862271766 +0000 -@@ -0,0 +1,857 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-slt.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,844 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; @@ -16106,19 +16199,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-slt.dts b/arch/arm64/boot/dts/as + phy-handle = <ðphy2>; +}; + -+&syscon1 { -+ mac0-clk-delay = <0x1a 0x15 -+ 0x10 0x10 -+ 0x10 0x10>; -+ mac1-clk-delay = <0x19 0x17 -+ 0x10 0x10 -+ 0x10 0x10>; -+ assigned-clocks = <&syscon1 SCU1_CLK_MACHCLK>, -+ <&syscon1 SCU1_CLK_RGMII>, -+ <&syscon1 SCU1_CLK_RMII>; -+ assigned-clock-rates = <200000000>, <125000000>, <50000000>; -+}; -+ +&lpc0_kcs0 { + status = "okay"; + kcs-io-addr = <0xca0>; @@ -16396,6329 +16476,12457 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-slt.dts b/arch/arm64/boot/dts/as +&otp { + status = "okay"; +}; -diff --git a/drivers/Makefile b/drivers/Makefile ---- a/drivers/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/Makefile 2025-12-23 10:16:13.352162932 +0000 -@@ -195,3 +195,4 @@ - obj-$(CONFIG_DPLL) += dpll/ - - obj-$(CONFIG_S390) += s390/ -+obj-$(CONFIG_JTAG_ASPEED) += jtag/ -diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig ---- a/drivers/bus/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/bus/Kconfig 2025-12-23 10:16:08.870238093 +0000 -@@ -261,6 +261,12 @@ - configuration. Allows to adjust the priorities of all master - peripherals. - -+config ASPEED_LTPI -+ bool "Aspeed LTPI bus controller driver" -+ depends on ARCH_ASPEED -+ help -+ LVDS Tunneling Protocol and Interface (LTPI) bus controller -+ - source "drivers/bus/fsl-mc/Kconfig" - source "drivers/bus/mhi/Kconfig" - -diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile ---- a/drivers/bus/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/bus/Makefile 2025-12-23 10:16:12.865171099 +0000 -@@ -39,6 +39,7 @@ - obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o - - obj-$(CONFIG_DA8XX_MSTPRI) += da8xx-mstpri.o -+obj-$(CONFIG_ASPEED_LTPI) += aspeed-ltpi.o - - # MHI - obj-y += mhi/ -diff --git a/drivers/bus/aspeed-ltpi.c b/drivers/bus/aspeed-ltpi.c ---- a/drivers/bus/aspeed-ltpi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/bus/aspeed-ltpi.c 2025-12-23 10:16:20.967035301 +0000 -@@ -0,0 +1,272 @@ +diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-ci-host.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-ci-host.dts +--- a/arch/arm64/boot/dts/aspeed/ast2700a1-ci-host.dts 1970-01-01 00:00:00.000000000 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-ci-host.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later -+// Copyright ASPEED Technology + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++/dts-v1/; + -+#define LTPI_AUTO_CAP_LOW 0x24 -+#define LTPI_I2C_IO_FRAME_EN GENMASK(29, 24) -+#define LTPI_AUTO_CAP_HIGH 0x28 -+#define LTPI_UART_IO_FRAME_EN GENMASK(14, 13) ++#include "ast2700a1-evb.dts" + -+#define LTPI_LINK_CONTROLL 0x80 -+#define LTPI_AUTO_CONFIG BIT(10) ++/ { ++ model = "AST2700A1-CI-HOST"; ++}; + -+#define LTPI_INTR_STATUS 0x100 -+#define LTPI_INTR_EN 0x104 -+#define LTPI_INTR_EN_OP_LINK_LOST BIT(4) -+#define LTPI_LINK_MANAGE_ST 0x108 -+#define LTPI_LINK_PARTNER_FLAG BIT(24) ++&bmc_dev0 { ++ status = "disabled"; ++}; + -+#define LTPI_MANUAL_CAP_LOW 0x118 -+#define LTPI_MANUAL_CAP_HIGH 0x11c ++&xdma0 { ++ status = "disabled"; ++}; + -+#define LTPI_I2C_TIMING_0 0x134 -+#define LTPI_I2C_TIMING_1 0x138 ++&pcie_vuart0 { ++ status = "disabled"; ++}; + -+#define LTPI_I2C_100K_0 0x3535352f -+#define LTPI_I2C_100K_1 0x09353535 ++&pcie_vuart1 { ++ status = "disabled"; ++}; + -+#define LTPI_I2C_400K_0 0x06060d06 -+#define LTPI_I2C_400K_1 0x090d0a06 ++&pcie_lpc0_kcs0 { ++ status = "disabled"; ++}; + -+#define SCU_IO_PINS_TRAP1 0x10 -+#define SCU_IO_PINS_TRAP1_CLEAR 0x14 -+#define SCU_IO_PINS_TRAP_LTPI GENMASK(2, 0) -+#define SCU_IO_OTP_TRAP1 0xa00 -+#define SCU_IO_OTP_TRAP1_CLEAR 0xa04 -+#define SCU_IO_OTP_TRAP2 0xa20 -+#define SCU_IO_OTP_TRAP2_CLEAR 0xa24 ++&pcie_lpc0_kcs1 { ++ status = "disabled"; ++}; + -+#define MAX_I2C_IN_LTPI 6 -+#define MAX_UART_IN_LTPI 2 ++&pcie_lpc0_kcs2 { ++ status = "disabled"; ++}; + -+enum chip_version { -+ AST2700, -+ AST1700, ++&pcie_lpc0_kcs3 { ++ status = "disabled"; +}; + -+struct aspeed_ltpi_priv { -+ struct device *dev; -+ void __iomem *regs; -+ struct clk *ltpi_clk; -+ struct clk *ltpi_phyclk; -+ struct reset_control *ltpi_rst; -+ struct regmap *scu; -+ u32 version; -+ u32 i2c_tunneling; -+ u32 i2c_timing_0; -+ u32 i2c_timing_1; -+ u32 uart_tunneling; ++&pcie_lpc0_ibt { ++ status = "disabled"; +}; + -+static irqreturn_t aspeed_ltpi_irq_handler(int irq, void *dev_id) -+{ -+ struct aspeed_ltpi_priv *priv = dev_id; -+ u32 status = readl(priv->regs + LTPI_INTR_STATUS); ++&pcie0_mmbi0 { ++ status = "disabled"; ++}; + -+ if (status & LTPI_INTR_EN_OP_LINK_LOST) { -+ writel(0, priv->regs + LTPI_INTR_EN); -+ writel(status, priv->regs + LTPI_INTR_STATUS); -+ panic("LTPI link lost!\n"); -+ /* Will not return */ -+ } ++&pcie0 { ++ status = "okay"; ++}; + -+ writel(status, priv->regs + LTPI_INTR_STATUS); ++&bmc_dev1 { ++ status = "disabled"; ++}; + -+ return IRQ_HANDLED; -+} ++&xdma1 { ++ status = "disabled"; ++}; + -+static int aspeed_ltpi_init_mux(struct aspeed_ltpi_priv *priv) -+{ -+ u32 reg, i2c_en, uart_en, i; ++&pcie_vuart2 { ++ status = "disabled"; ++}; + -+ reg = readl(priv->regs + LTPI_AUTO_CAP_LOW); ++&pcie_vuart3 { ++ status = "disabled"; ++}; + -+ i2c_en = FIELD_GET(LTPI_I2C_IO_FRAME_EN, reg); -+ i2c_en &= priv->i2c_tunneling; ++&pcie_lpc1_kcs0 { ++ status = "disabled"; ++}; + -+ reg &= ~LTPI_I2C_IO_FRAME_EN; -+ reg |= FIELD_PREP(LTPI_I2C_IO_FRAME_EN, i2c_en); -+ writel(reg, priv->regs + LTPI_MANUAL_CAP_LOW); ++&pcie_lpc1_kcs1 { ++ status = "disabled"; ++}; + -+ reg = readl(priv->regs + LTPI_AUTO_CAP_HIGH); ++&pcie_lpc1_kcs2 { ++ status = "disabled"; ++}; + -+ uart_en = FIELD_GET(LTPI_UART_IO_FRAME_EN, reg); -+ uart_en &= priv->uart_tunneling; ++&pcie_lpc1_kcs3 { ++ status = "disabled"; ++}; + -+ reg &= ~LTPI_UART_IO_FRAME_EN; -+ reg |= FIELD_PREP(LTPI_UART_IO_FRAME_EN, uart_en); ++&pcie_lpc1_ibt { ++ status = "disabled"; ++}; + -+ writel(reg, priv->regs + LTPI_MANUAL_CAP_HIGH); ++&pcie1_mmbi4 { ++ status = "disabled"; ++}; + -+ /* Apply LTPI manual configuration */ -+ reg = readl(priv->regs + LTPI_LINK_CONTROLL); -+ reg &= ~LTPI_AUTO_CONFIG; -+ writel(reg, priv->regs + LTPI_LINK_CONTROLL); ++&pcie1 { ++ status = "okay"; ++}; + -+ /* Set the AST1700 i2c ac-timing */ -+ if (priv->version == AST1700) { -+ /* Apply i2c timing with i2c tunneling setting */ -+ for (i = 0; i < MAX_I2C_IN_LTPI; i++) { -+ if ((priv->i2c_tunneling >> i) & 0x1) { -+ writel(priv->i2c_timing_0, -+ priv->regs + LTPI_I2C_TIMING_0 + (0x8 * i)); -+ writel(priv->i2c_timing_1, -+ priv->regs + LTPI_I2C_TIMING_1 + (0x8 * i)); -+ } -+ } -+ } ++&mdio0 { ++ status = "disabled"; ++}; + -+ return 0; -+} ++&mac0 { ++ status = "disabled"; ++}; + -+static int aspeed_ltpi_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ const struct of_dev_auxdata *lookup = dev_get_platdata(dev); -+ struct device_node *np = dev->of_node; -+ const struct of_device_id *match; -+ struct aspeed_ltpi_priv *priv; -+ int irq, ret; ++&mdio2 { ++ status = "okay"; ++ #address-cells = <1>; ++ #size-cells = <0>; + -+ match = of_match_device(dev->driver->of_match_table, dev); ++ ethphy2: ethernet-phy@0 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <0>; ++ }; ++}; + -+ if (match) { -+ if (of_property_match_string(np, "compatible", match->compatible) < 0) -+ return -ENODEV; -+ } else { -+ return -ENODEV; -+ } ++&sgmii { ++ status = "okay"; ++}; + -+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; ++&mac2 { ++ status = "okay"; + -+ priv->dev = dev; -+ priv->regs = devm_platform_ioremap_resource(pdev, 0); -+ if (IS_ERR(priv->regs)) -+ return PTR_ERR(priv->regs); ++ phy-mode = "sgmii"; ++ phy-handle = <ðphy2>; ++}; +diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm.dts +--- a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm.dts 1970-01-01 00:00:00.000000000 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,974 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later + -+ priv->version = (enum chip_version)device_get_match_data(dev); ++/dts-v1/; + -+ priv->ltpi_clk = devm_clk_get(&pdev->dev, "ltpi"); -+ if (IS_ERR(priv->ltpi_clk)) { -+ priv->ltpi_clk = devm_clk_get(&pdev->dev, "ahb"); -+ if (IS_ERR(priv->ltpi_clk)) -+ return PTR_ERR(priv->ltpi_clk); ++#include "aspeed-g7.dtsi" ++#include ++#include + -+ clk_prepare_enable(priv->ltpi_clk); ++#define PCIE0_EP 1 // 1: EP, 0: RC ++#define PCIE1_EP 1 // 1: EP, 0: RC + -+ priv->ltpi_phyclk = devm_clk_get(&pdev->dev, "phy"); -+ if (IS_ERR(priv->ltpi_phyclk)) -+ return PTR_ERR(priv->ltpi_phyclk); ++/ { ++ model = "AST2700A1-DCSCM"; ++ compatible = "aspeed,ast2700"; + -+ clk_prepare_enable(priv->ltpi_phyclk); -+ } else { -+ priv->ltpi_phyclk = NULL; -+ } ++ chosen { ++ stdout-path = "serial12:115200n8"; ++ }; + -+ priv->ltpi_rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); -+ if (IS_ERR(priv->ltpi_rst)) -+ return PTR_ERR(priv->ltpi_rst); ++ memory@400000000 { ++ device_type = "memory"; ++ reg = <0x4 0x00000000 0x0 0x40000000>; ++ }; + -+ reset_control_deassert(priv->ltpi_rst); ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; + -+ priv->i2c_tunneling = GENMASK(MAX_I2C_IN_LTPI - 1, 0); -+ if (!of_property_read_u32(np, "i2c-tunneling", &ret)) -+ priv->i2c_tunneling = ret; ++ #include "ast2700-reserved-mem.dtsi" + -+ priv->i2c_timing_0 = LTPI_I2C_100K_0; -+ priv->i2c_timing_1 = LTPI_I2C_100K_1; -+ if (!of_property_read_u32(np, "i2c-tunneling-timing", &ret)) { -+ if (ret == 400) { -+ priv->i2c_timing_0 = LTPI_I2C_400K_0; -+ priv->i2c_timing_1 = LTPI_I2C_400K_1; -+ } -+ } -+ priv->uart_tunneling = GENMASK(MAX_UART_IN_LTPI - 1, 0); -+ if (!of_property_read_u32(np, "uart-tunneling", &ret)) -+ priv->uart_tunneling = ret; ++ video_engine_memory0: video0 { ++ size = <0x0 0x02000000>; ++ alignment = <0x0 0x00010000>; ++ compatible = "shared-dma-pool"; ++ reusable; ++ }; + -+ priv->scu = syscon_regmap_lookup_by_phandle(np, "aspeed,scu"); -+ if (of_get_property(np, "remote-controller", NULL)) { -+ u32 reg; ++ video_engine_memory1: video1{ ++ size = <0x0 0x02000000>; ++ alignment = <0x0 0x00010000>; ++ compatible = "shared-dma-pool"; ++ reusable; ++ }; + -+ /* Clear all the pins/otp strap but LTPI related settings for AST1700 */ -+ regmap_read(priv->scu, SCU_IO_PINS_TRAP1, ®); -+ reg &= ~SCU_IO_PINS_TRAP_LTPI; -+ regmap_write(priv->scu, SCU_IO_PINS_TRAP1_CLEAR, reg); ++#if 0 ++ gfx_memory: framebuffer { ++ size = <0x0 0x01000000>; ++ alignment = <0x0 0x01000000>; ++ compatible = "shared-dma-pool"; ++ reusable; ++ }; ++#endif ++ }; + -+ regmap_read(priv->scu, SCU_IO_OTP_TRAP1, ®); -+ regmap_write(priv->scu, SCU_IO_OTP_TRAP1_CLEAR, reg); ++ iio-hwmon { ++ compatible = "iio-hwmon"; ++ io-channels = <&adc0 7>, <&adc1 7>; ++ }; ++}; + -+ regmap_read(priv->scu, SCU_IO_OTP_TRAP2, ®); -+ regmap_write(priv->scu, SCU_IO_OTP_TRAP2_CLEAR, reg); -+ } else { -+ irq = platform_get_irq(pdev, 0); -+ ret = devm_request_irq(priv->dev, irq, aspeed_ltpi_irq_handler, -+ 0, dev_name(priv->dev), priv); -+ if (ret) { -+ dev_err(priv->dev, "failed to request irq\n"); -+ reset_control_assert(priv->ltpi_rst); -+ clk_disable_unprepare(priv->ltpi_phyclk); -+ clk_disable_unprepare(priv->ltpi_clk); -+ return ret; -+ } ++&pwm_tach { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm9_default>; ++}; + -+ writel(LTPI_INTR_EN_OP_LINK_LOST, priv->regs + LTPI_INTR_STATUS); -+ writel(LTPI_INTR_EN_OP_LINK_LOST, priv->regs + LTPI_INTR_EN); -+ } ++&adc0 { ++ aspeed,int-vref-microvolt = <2500000>; ++ status = "okay"; ++ aspeed,battery-sensing; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_adc7_default>; ++}; + -+ aspeed_ltpi_init_mux(priv); ++&adc1 { ++ aspeed,int-vref-microvolt = <2500000>; ++ status = "okay"; ++ aspeed,battery-sensing; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_adc15_default>; ++}; + -+ platform_set_drvdata(pdev, priv); -+ if (np) -+ of_platform_populate(np, NULL, lookup, priv->dev); ++&pinctrl1 { ++ pinctrl_i3c0_3_hv_voltage: i3chv-voltage { ++ pins = "U25"; ++ power-source = <1800>; ++ }; + -+ return 0; -+} ++ pinctrl_i3c0_driving: i3c0-driving { ++ pins = "U25", "U26"; ++ drive-strength = <2>; ++ }; + -+static void aspeed_ltpi_remove(struct platform_device *pdev) -+{ -+ struct aspeed_ltpi_priv *priv; ++ pinctrl_i3c1_driving: i3c1-driving { ++ pins = "Y26", "AA24"; ++ drive-strength = <2>; ++ }; + -+ priv = platform_get_drvdata(pdev); -+ reset_control_assert(priv->ltpi_rst); -+ clk_disable_unprepare(priv->ltpi_phyclk); -+ clk_disable_unprepare(priv->ltpi_clk); -+} ++ pinctrl_i3c2_driving: i3c2-driving { ++ pins = "R25", "AA26"; ++ drive-strength = <2>; ++ }; + -+static const struct of_device_id aspeed_ltpi_of_match[] = { -+ { .compatible = "aspeed-ltpi", .data = (const void *)AST2700,}, -+ { .compatible = "aspeed-ast1700-ltpi", .data = (const void *)AST1700,}, -+ { /* sentinel */ } ++ pinctrl_i3c3_driving: i3c3-driving { ++ pins = "R26", "Y25"; ++ drive-strength = <2>; ++ }; ++ ++ pinctrl_rgmii0_driving: rgmii0-driving { ++ pins = "C20", "C19", "A8", "R14", "A7", "P14", ++ "D20", "A6", "B6", "N14", "B7", "B8"; ++ drive-strength = <1>; ++ }; +}; -+MODULE_DEVICE_TABLE(of, aspeed_ltpi_of_match); + -+static struct platform_driver aspeed_ltpi_driver = { -+ .probe = aspeed_ltpi_probe, -+ .remove = aspeed_ltpi_remove, -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .of_match_table = aspeed_ltpi_of_match, -+ }, ++&i3c0 { ++ /* BMC_HPM_I3C_I2C_14, If AST1060 I3C_BMC_PFR_SCM_SEL(GPION4)=0 and I3C_SCM_EN(GPION5)=0 */ ++ initial-role = "primary"; ++ status = "okay"; +}; + -+module_platform_driver(aspeed_ltpi_driver); ++&i3c1 { ++ /* BMC_I2C_I3C1_SCL1 */ ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+MODULE_DESCRIPTION("LVDS Tunneling Protocol and Interface Bus Driver"); -+MODULE_AUTHOR("Dylan Hung "); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig ---- a/drivers/char/hw_random/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/char/hw_random/Kconfig 2025-12-23 10:16:09.603225801 +0000 -@@ -286,6 +286,7 @@ - config HW_RANDOM_NOMADIK - tristate "ST-Ericsson Nomadik Random Number Generator support" - depends on ARCH_NOMADIK || COMPILE_TEST -+ depends on ARM_AMBA - default HW_RANDOM - help - This driver provides kernel-side support for the Random Number -@@ -587,6 +588,21 @@ - - If unsure, say Y. - -+config HW_RANDOM_ASPEED -+ tristate "Aspeed Random Number Generator support" -+ depends on ARCH_ASPEED -+ default HW_RANDOM -+ help -+ This driver provides kernel-side support for the Random Number -+ Generator hardware found on Aspeed ast2600/ast2700 devices. ++&i3c2 { ++ /* BMC_I2C_I3C2_SCL2 */ ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ To compile this driver as a module, choose M here: the -+ module will be called aspeed-rng. ++&i3c3 { ++ /* I3C_DBG_SCM */ ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ If unsure, say Y. ++/* AST2700 i3c4 -> AST1060 i3c2 for MCTP over I3C. */ ++&i3c4 { ++ /* I3C_PFR_BMC */ ++ initial-role = "target"; ++ pid = <0x000007ec 0x06000000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+source "drivers/char/hw_random/dwc/Kconfig" ++&i3c5 { ++ /* I3C_MNG_BMC_SCM */ ++ initial-role = "primary"; ++ status = "okay"; ++}; + - endif # HW_RANDOM - - config UML_RANDOM -diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile ---- a/drivers/char/hw_random/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/char/hw_random/Makefile 2025-12-23 10:16:13.771155906 +0000 -@@ -50,3 +50,5 @@ - obj-$(CONFIG_HW_RANDOM_POLARFIRE_SOC) += mpfs-rng.o - obj-$(CONFIG_HW_RANDOM_ROCKCHIP) += rockchip-rng.o - obj-$(CONFIG_HW_RANDOM_JH7110) += jh7110-trng.o -+obj-$(CONFIG_HW_RANDOM_ASPEED) += aspeed-rng.o -+obj-$(CONFIG_HW_RANDOM_DWC) += dwc/ -diff --git a/drivers/char/hw_random/aspeed-rng.c b/drivers/char/hw_random/aspeed-rng.c ---- a/drivers/char/hw_random/aspeed-rng.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/aspeed-rng.c 2025-12-23 10:16:21.099033088 +0000 -@@ -0,0 +1,134 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (C) ASPEED Technology Inc. -+ */ ++&i3c6 { ++ /* I3C_SPD_SCM */ ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++/* Enable UART2 and UART9 for obmc-console. */ ++&uart2 { ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; + -+#define TRNG_CTL 0x00 -+#define TRNG_EN 0x0 -+#define TRNG_MODE 0x04 -+#define TRNG_RDY 0x1f -+#define TRNG_ODATA 0x04 ++&uart9 { ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; + -+struct aspeed_trng { -+ u32 ver; -+ void __iomem *base; -+ struct hwrng rng; -+ unsigned int present: 1; -+ ktime_t period; -+ struct hrtimer timer; -+ struct completion completion; ++/* Enable UART7 and UART10 for obmc-console. */ ++&uart7 { ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; +}; + -+static int aspeed_trng_read(struct hwrng *rng, void *buf, size_t max, -+ bool wait) -+{ -+ struct aspeed_trng *priv = container_of(rng, struct aspeed_trng, rng); -+ u32 *data = buf; -+ size_t read = 0; -+ int timeout = max / 4 + 1; ++&uart10 { ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; + -+ while (read < max) { -+ if (!(readl(priv->base + TRNG_CTL) & (1 << TRNG_RDY))) { -+ if (wait) { -+ if (timeout-- == 0) -+ return read; -+ } else { -+ return 0; -+ } -+ } else { -+ *data = readl(priv->base + TRNG_ODATA); -+ data++; -+ read += 4; -+ } -+ } ++/* UART3, UART13 and UART14 will be tunnelded to LTPI UART channels. */ ++&uart3 { ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; + -+ return read; -+} ++&uart13 { ++ status = "okay"; ++}; + -+static void aspeed_trng_enable(struct aspeed_trng *priv) -+{ -+ u32 ctl; ++&uart14 { ++ status = "okay"; ++}; + -+ ctl = readl(priv->base + TRNG_CTL); -+ ctl = ctl & ~(1 << TRNG_EN); /* enable rng */ -+ ctl = ctl | (3 << TRNG_MODE); /* select mode */ ++&uart12 { ++ status = "okay"; ++}; + -+ writel(ctl, priv->base + TRNG_CTL); -+} ++&fmc { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_fwspi_quad_default>; ++ pinctrl-names = "default"; + -+static void aspeed_trng_disable(struct aspeed_trng *priv) -+{ -+ writel(1, priv->base + TRNG_CTL); -+} ++ flash@0 { ++ status = "okay"; ++ m25p,fast-read; ++ label = "bmc"; ++ spi-max-frequency = <12500000>; ++ spi-tx-bus-width = <4>; ++ spi-rx-bus-width = <4>; ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ u-boot@0 { ++ reg = <0x0 0x400000>; // 4MB ++ label = "u-boot"; ++ }; ++ u-boot-env@400000 { ++ reg = <0x400000 0x20000>; // 128KB ++ label = "u-boot-env"; ++ }; ++ kernel@420000 { ++ reg = <0x420000 0x900000>; // 9MB ++ label = "kernel"; ++ }; ++ rofs@d20000 { ++ reg = <0xd20000 0x24a0000>; // 36.625MB ++ label = "rofs"; ++ }; ++ rwfs@31c0000 { ++ reg = <0x31c0000 0xE40000>; // 14.25MB ++ label = "rwfs"; ++ }; ++ pfm@4000000 { ++ reg = <0x4000000 0x20000>; // 128KB ++ label = "pfm"; ++ }; ++ reserved-1@4020000 { ++ reg = <0x4020000 0x200000>; // 128KB ++ label = "reserved-1"; ++ }; ++ rc-image@4220000 { ++ reg = <0x4220000 0x3de0000>; // 63360KB ++ label = "rc-image"; ++ }; ++ image-stg@8000000 { ++ reg = <0x8000000 0x3de0000>; // 63360KB ++ label = "img-stg"; ++ }; ++ pfr-stg@bde0000 { ++ reg = <0xbde0000 0x100000>; // 1024KB ++ label = "pfr-stg"; ++ }; ++ cpld-stg@bee0000 { ++ reg = <0xbee0000 0x400000>; // 4096KB ++ label = "cpld-stg"; ++ }; ++ afm-stg@c2e0000 { ++ reg = <0xc2e0000 0x20000>; // 128KB ++ label = "afm-stg"; ++ }; ++ afm-rc@c300000 { ++ reg = <0xc300000 0x20000>; // 128KB ++ label = "afm-rc"; ++ }; ++ reserved-2@c320000 { ++ reg = <0xc320000 0x3ce0000>; // 62336KB ++ label = "reserved-2"; ++ }; ++ }; ++ }; ++}; + -+static int aspeed_trng_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct aspeed_trng *priv; -+ struct resource *res; -+ int ret; ++&spi0 { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_spi0_default &pinctrl_spi0_cs1_default>; ++ pinctrl-names = "default"; + -+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; ++ flash@0 { ++ status = "okay"; ++ m25p,fast-read; ++ label = "spi0:0"; ++ spi-max-frequency = <12500000>; ++ spi-tx-bus-width = <1>; ++ spi-rx-bus-width = <1>; ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ spi0_pch_bios@0 { ++ reg = <0x0 0x3fe0000>; ++ label = "spi0_pch_reserved"; ++ }; ++ spi0_pch_pfm@3fe0000 { ++ reg = <0x3fe0000 0x20000>; ++ label = "spi0_pch_pfm"; ++ }; ++ spi0_pch_stg@4000000 { ++ reg = <0x4000000 0x2000000>; ++ label = "spi0_pch_stg"; ++ }; ++ spi0_pch_rc@6000000 { ++ reg = <0x6000000 0x2000000>; ++ label = "spi0_pch_rc"; ++ }; ++ }; ++ }; + -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ priv->base = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(priv->base)) -+ return PTR_ERR(priv->base); ++ flash@1 { ++ status = "okay"; ++ m25p,fast-read; ++ label = "spi0:1"; ++ spi-max-frequency = <12500000>; ++ spi-tx-bus-width = <1>; ++ spi-rx-bus-width = <1>; ++ }; ++}; + -+ priv->rng.name = pdev->name; -+ priv->rng.quality = 900; -+ priv->rng.read = aspeed_trng_read; ++&spi1 { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_spi1_default &pinctrl_spi1_cs1_default>; ++ pinctrl-names = "default"; + -+ aspeed_trng_enable(priv); ++ flash@0 { ++ status = "okay"; ++ m25p,fast-read; ++ label = "spi1:0"; ++ spi-max-frequency = <12500000>; ++ spi-tx-bus-width = <1>; ++ spi-rx-bus-width = <1>; ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; + -+ ret = devm_hwrng_register(&pdev->dev, &priv->rng); -+ if (ret) -+ return ret; ++ spi1_pch_reserved@0 { ++ reg = <0x0 0x7f0000>; ++ label = "spi1_pch_reserved"; ++ }; + -+ platform_set_drvdata(pdev, priv); ++ spi1_pch_stg@7f0000 { ++ reg = <0x7f0000 0x1400000>; ++ label = "spi1_pch_stg"; ++ }; + -+ dev_info(dev, "Aspeed Hardware RNG successfully registered\n"); ++ spi1_pch_rc@1bf0000 { ++ reg = <0x1bf0000 0x1400000>; ++ label = "spi1_pch_rc"; ++ }; + -+ return 0; -+} ++ spi1_pch_pfm@2ff0000 { ++ reg = <0x2ff0000 0x10000>; ++ label = "spi1_pch_pfm"; ++ }; + -+static void aspeed_trng_remove(struct platform_device *pdev) -+{ -+ struct aspeed_trng *priv = platform_get_drvdata(pdev); ++ spi1_pch_bios@3000000 { ++ reg = <0x3000000 0x1000000>; ++ label = "spi1_pch_bios"; ++ }; ++ }; ++ }; + -+ aspeed_trng_disable(priv); -+} ++ flash@1 { ++ status = "okay"; ++ m25p,fast-read; ++ label = "spi1:1"; ++ spi-max-frequency = <12500000>; ++ spi-tx-bus-width = <1>; ++ spi-rx-bus-width = <1>; ++ }; ++}; + -+static const struct of_device_id aspeed_trng_dt_ids[] = { -+ { .compatible = "aspeed,ast2600-trng" }, -+ { .compatible = "aspeed,ast2700-trng" }, -+ {} ++<pi0 { ++ status = "okay"; +}; -+MODULE_DEVICE_TABLE(of, aspeed_trng_dt_ids); + -+static struct platform_driver aspeed_trng_driver = { -+ .probe = aspeed_trng_probe, -+ .remove = aspeed_trng_remove, -+ .driver = { -+ .name = "aspeed-trng", -+ .of_match_table = aspeed_trng_dt_ids, -+ }, ++<pi1 { ++ /* Do not tunnel I2C10 to the HPM */ ++ i2c-tunneling = <0x2f>; ++ status = "okay"; +}; + -+module_platform_driver(aspeed_trng_driver); ++/* The LTPI GPIO table is defined in Chapter 6 of the AST2700 LTPI Design Guide v10.pdf. */ ++/* line 0:BMC_GPI0(LTPI0_INL16), 1:BMC_GPO0(LTPI0_ONL16), 2:BMC_GPI1(LTPI0_INL17), 3:BMC_GPO1(LTPI0_ONL17), etc... */ ++/* BMC_GPI[63:0] = LTPI0_INL[79:16], BMC_GPO[63:0] = LTPI0_ONL[79:16] */ ++/* BMC_GPI[71:64] = LTPI0_ILL[11:4], BMC_GPO[71:64] = LTPI0_OLL[11:4] */ ++/* BMC_GPI[111:72] = LTPI0_INL[127:88],BMC_GPO[111:72] = LTPI0_ONL[127:88] */ ++<pi0_gpio { ++ status = "okay"; ++ gpio-line-names = ++ /*00-07*/ "","","","","","","","", ++ /*08-15*/ "","","","","","","","", ++ /*16-23*/ "","FM_CPU_FBRK_DEBUG_N","","FM_BMC_TRUST_N","","FM_RST_BTN_OUT_CPU0_PLD_N_OE","","FM_PWR_BTN_OUT_CPU0_N", ++ /*24-31*/ "","FM_BMC_ONCTL_N","","","","","","", ++ /*32-39*/ "","BIOS_POST_CODE_LED_0","","BIOS_POST_CODE_LED_1","","BIOS_POST_CODE_LED_2","","BIOS_POST_CODE_LED_3", ++ /*40-47*/ "FP_ID_BTN_N","BIOS_POST_CODE_LED_4","FP_RST_BTN_N","BIOS_POST_CODE_LED_5","","BIOS_POST_CODE_LED_6","","BIOS_POST_CODE_LED_7", ++ /*48-55*/ "","A_P3V_BAT_SCALED_EN","","FM_TPM_EN_PULSE","","FM_SKT0_FAULT_LED","","FM_SKT1_FAULT_LED", ++ /*56-63*/ "","","","","","RST_BMC_SMB_PCIE_MUX_N","","SURPRISE_RESET", ++ /*64-71*/ "PWRGD_S0_PWROK_CPU0","","","","","","","", ++ /*72-79*/ "","","","","","","","", ++ /*80-87*/ "","","","","","","","", ++ /*88-95*/ "","","","","","","","", ++ /*96-103*/ "","","","","","","","", ++ /*104-111*/ "","","","","","","","", ++ /*112-119*/ "","","","","","","","", ++ /*120-127*/ "","","","","","","","", ++ /*128-135*/ "","","","","","","","", ++ /*136-143*/ "","","","","","","","", ++ /*144-151*/ "","","","","","","","", ++ /*152-159*/ "","","","","","","","", ++ /*160-167*/ "","","","","","","","", ++ /*168-175*/ "","","","","","","","", ++ /*176-183*/ "","","","","","","","", ++ /*184-191*/ "","","","","","","","", ++ /*192-199*/ "","","","","","","","", ++ /*200-207*/ "","","","","","","","", ++ /*208-215*/ "","","","","","","","", ++ /*216-223*/ "","","","","","","",""; + -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Neal Liu "); -+MODULE_DESCRIPTION("Aspeed true random number generator driver"); -diff --git a/drivers/char/hw_random/dwc/Kconfig b/drivers/char/hw_random/dwc/Kconfig ---- a/drivers/char/hw_random/dwc/Kconfig 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/Kconfig 2025-12-23 10:16:19.523059502 +0000 -@@ -0,0 +1,18 @@ -+# SPDX-License-Identifier: GPL-2.0-only -+# -+# DWC Hardware Random Number Generator (RNG) configuration -+# ++ gpio_17 { ++ gpio-hog; ++ gpios = <17 GPIO_ACTIVE_HIGH>; ++ output-high; ++ line-name = "FM_CPU_FBRK_DEBUG_N"; ++ }; ++ gpio_19 { ++ gpio-hog; ++ gpios = <19 GPIO_ACTIVE_HIGH>; ++ output-high; ++ line-name = "FM_BMC_TRUST_N"; ++ }; ++ gpio_25 { ++ gpio-hog; ++ gpios = <25 GPIO_ACTIVE_HIGH>; ++ output-low; ++ line-name = "FM_BMC_ONCTL_N"; ++ }; ++ gpio_53 { ++ gpio-hog; ++ gpios = <53 GPIO_ACTIVE_HIGH>; ++ output-high; ++ line-name = "FM_SKT0_FAULT_LED"; ++ }; ++ gpio_61 { ++ gpio-hog; ++ gpios = <61 GPIO_ACTIVE_HIGH>; ++ output-high; ++ line-name = "RST_BMC_SMB_PCIE_MUX_N"; ++ }; ++ gpio_63 { ++ gpio-hog; ++ gpios = <63 GPIO_ACTIVE_HIGH>; ++ output-low; ++ line-name = "SURPRISE_RESET"; ++ }; ++}; + -+config HW_RANDOM_DWC -+ tristate "DesignWare Cores HW Random Number Generator support" -+ depends on HW_RANDOM -+ depends on ARCH_ASPEED || COMPILE_TEST -+ help -+ This driver provides kernel-side support for the DWC -+ Random Number Generator hardware found on Aspeed SoCs. ++&peci0 { ++ status = "okay"; ++}; + -+ To compile this driver as a module, choose M here. the -+ module will be called dwc-rng. ++&chassis { ++ status = "okay"; ++}; + -+ If unsure, say Y. ++&mdio0 { ++ status = "okay"; ++ #address-cells = <1>; ++ #size-cells = <0>; + -diff --git a/drivers/char/hw_random/dwc/Makefile b/drivers/char/hw_random/dwc/Makefile ---- a/drivers/char/hw_random/dwc/Makefile 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/Makefile 2025-12-23 10:16:19.523059502 +0000 -@@ -0,0 +1,22 @@ -+# SPDX-License-Identifier: GPL-2.0 ++ ethphy0: ethernet-phy@0 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <0>; ++ }; ++}; + -+ccflags-y := -I $(srctree)/drivers/char/hw_random/dwc/src/trng/include \ -+ -I $(srctree)/drivers/char/hw_random/dwc/src/pdu/linux/include ++&mac0 { ++ status = "okay"; + -+obj-$(CONFIG_HW_RANDOM_DWC) += elppdu.o -+elppdu-objs := src/pdu/linux/kernel/pdu.o \ -+ src/pdu/common/pdu/pdu_dev32.o \ ++ phy-mode = "rgmii-id"; ++ phy-handle = <ðphy0>; + -+obj-$(CONFIG_HW_RANDOM_DWC) += elpmem.o -+elpmem-objs := src/pdu/linux/kernel/spacc_mem.o \ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_rgmii0_default &pinctrl_rgmii0_driving>; ++}; + -+obj-$(CONFIG_HW_RANDOM_DWC) += nisttrng.o -+nisttrng-objs := src/trng/kernel/nist_trng.o \ -+ src/trng/trng/nist_trng.o \ -+ src/trng/trng/nist_trng_private.o \ ++&jtag1 { ++ status = "okay"; ++}; + -+clean: -+ @find \( -name '*.o' \ -+ -o -name '*.a' \ -+ -o -name '*.order' \ -+ \) -type f -print | xargs rm -rvf -diff --git a/drivers/char/hw_random/dwc/src/pdu/common/include/elppdu_error.h b/drivers/char/hw_random/dwc/src/pdu/common/include/elppdu_error.h ---- a/drivers/char/hw_random/dwc/src/pdu/common/include/elppdu_error.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/pdu/common/include/elppdu_error.h 2025-12-23 10:16:19.524059486 +0000 -@@ -0,0 +1,96 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * This Synopsys software and associated documentation (hereinafter the -+ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless -+ * otherwise expressly agreed to in writing between Synopsys and you. The -+ * Software IS NOT an item of Licensed Software or a Licensed Product under -+ * any End User Software License Agreement or Agreement for Licensed Products -+ * with Synopsys or any supplement thereto. Synopsys is a registered trademark -+ * of Synopsys, Inc. Other names included in the SOFTWARE may be the -+ * trademarks of their respective owners. -+ * -+ * The contents of this file are dual-licensed; you may select either version -+ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license -+ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the -+ * SOFTWARE. The BSD License is copied below. -+ * -+ * BSD-3-Clause License: -+ * Copyright (c) 2011-2017 Synopsys, Inc. and/or its affiliates. -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright notice, -+ * this list of conditions, and the following disclaimer, without -+ * modification. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * 3. The names of the above-listed copyright holders may not be used to -+ * endorse or promote products derived from this software without specific -+ * prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -+ * POSSIBILITY OF SUCH DAMAGE. -+ */ ++&gpio1 { ++ pinctrl-0 = <&pinctrl_i3c0_3_hv_voltage ++ &pinctrl_i3c0_driving &pinctrl_i3c1_driving ++ &pinctrl_i3c2_driving &pinctrl_i3c3_driving>; ++ pinctrl-names = "default"; + -+#ifndef SYNPDU_ERROR_H_ -+#define SYNPDU_ERROR_H_ ++ gpio-line-names = ++ /*A0-A7*/ "","","","","","","","", ++ /*B0-B7*/ "","","","","","","","", ++ /*C0-C7*/ "","","","","","","","", ++ /*D0-D7*/ "","","","","","","","", ++ /*E0-E7*/ "","","","","FP_LED_STATUS_GREEN_CPLD_N","FP_LED_STATUS_AMBER_CPLD_N","","", ++ /*F0-F7*/ "","","","","","","","", ++ /*G0-G7*/ "","","","","","","","", ++ /*H0-H7*/ "","","","","","","","", ++ /*I0-I7*/ "","","","","","","","", ++ /*J0-J7*/ "","","","","","","","", ++ /*K0-K7*/ "","","","","","","","", ++ /*L0-L7*/ "","","","","","","","", ++ /*M0-M7*/ "","","","","","","","", ++ /*N0-N7*/ "","","","","","","","", ++ /*O0-O7*/ "","","","","","","","", ++ /*P0-P7*/ "","","","","","","","", ++ /*Q0-Q7*/ "","","","","","","","", ++ /*R0-R7*/ "","","","","","","","", ++ /*S0-S7*/ "","","","","","","","", ++ /*T0-T7*/ "","","","","","","","", ++ /*U0-U7*/ "","","","","","SCM_PHY_RST","","", ++ /*V0-V7*/ "","","","","","","","", ++ /*W0-W7*/ "","","","","","","","", ++ /*X0-X7*/ "","","","","","","","", ++ /*Y0-Y7*/ "","","","","IRQ_PMBUS1_ALERT_LVC3_N","FM_NVME_LVC3_ALERT_N","","", ++ /*Z0-Z7*/ "","","FM_NODE_ID0_N","FM_NODE_ID1_N","","PWRGD_AUX_PWRGD_PFR_CPU0","PWRGD_AUX_PWRGD_PFR_CPU1","", ++ /*AA0-AA7*/ "BMC_BOOT_DONE","","","","","","","", ++ /*AB0-AB7*/ "","","","","","","","", ++ /*AC0-AC7*/ "","","","","","","","", ++ /*AD0-AD7*/ "","","","","","","","", ++ /*AE0-AE7*/ "","","","","","","",""; ++}; + -+/* -+ * Common error definitions. Be sure to update pdu_error_code when changing -+ * anything in this list. -+ */ ++/* AST2700 A1 support SGPIO slave to 72*2 pins. */ ++/* The SGPIO slave table is defined in Chapter 7 of the AST2700 LTPI Design Guide v10.pdf. */ ++/* line 0:BMC_SGPI0(SCM_GPI0), 1:BMC_SGPO0(SCM_GPO0), 2:BMC_SGPI1(SCM_GPI1), 3:BMC_SGPO1(SCM_GPO1), etc... */ ++/* line 32:BMC_SGPI16(LTPI0_INL80), 33:BMC_SGPO16(LTPI0_ONL80), 34:LTPI0_INL81, 35:LTPI0_ONL81, etc... */ ++/* BMC_SGPI[15:0] = SCM_GPI0[15:0], BMC_SGPO[15:0] = BMC_SGPO0[15:0] */ ++/* BMC_SGPI[23:16] = LTPI0_INL[87:80], BMC_SGPO[23:16] = LTPI0_ONL[87:80] */ ++/* BMC_SGPI[31:24] = LTPI1_INL[87:80], BMC_SGPO[31:24] = LTPI1_ONL[87:80] */ ++/* BMC_SGPI[47:32] = LTPI0_INL[15:0], BMC_SGPO[47:32] = LTPI0_ONL[15:0] */ ++/* BMC_SGPI[63:48] = LTPI1_INL[15:0], BMC_SGPO[63:48] = LTPI1_ONL[15:0] */ ++/* BMC_SGPI[71:64] = SREG_GPO[7:0], BMC_SGPO[71:64] = SREG_GPI[7:0] */ ++/* The designe guide only define SCM_GPO9(FP_PWR_BTN_PFR_N) for PFR output pin. */ ++/* We use AST2700 SGPIOS line 18 (FP_PWR_BTN_PFR_N_BMC_IN) for x86-power-control power button input. */ ++&sgpios { ++ status = "okay"; ++ gpio-line-names = ++ /*00-07*/ "","","","","","","","", ++ /*08-15*/ "","","","","","","","", ++ /*16-23*/ "","","FP_PWR_BTN_PFR_N_BMC_IN","","","","","", ++ /*24-31*/ "","","","","","","","", ++ /*32-39*/ "","","","","","","","", ++ /*40-47*/ "","","","","","","","", ++ /*48-55*/ "","","","","","","","", ++ /*56-63*/ "","","","","","","","", ++ /*64-71*/ "","","","","","","","", ++ /*72-79*/ "","","","","","","","", ++ /*80-87*/ "","","","","","","","", ++ /*88-95*/ "","","","","","","","", ++ /*96-103*/ "","","","","","","","", ++ /*104-111*/ "","","","","","","","", ++ /*112-119*/ "","","","","","","","", ++ /*120-127*/ "","","","","","","","", ++ /*128-135*/ "","","","","","","","", ++ /*136-143*/ "","","","","","","",""; ++}; + -+#define CRYPTO_OK (0) -+#define CRYPTO_FAILED (-1) -+#define CRYPTO_INPROGRESS (-2) -+#define CRYPTO_INVALID_HANDLE (-3) -+#define CRYPTO_INVALID_CONTEXT (-4) -+#define CRYPTO_INVALID_SIZE (-5) -+#define CRYPTO_NOT_INITIALIZED (-6) -+#define CRYPTO_NO_MEM (-7) -+#define CRYPTO_INVALID_ALG (-8) -+#define CRYPTO_INVALID_KEY_SIZE (-9) -+#define CRYPTO_INVALID_ARGUMENT (-10) -+#define CRYPTO_MODULE_DISABLED (-11) -+#define CRYPTO_NOT_IMPLEMENTED (-12) -+#define CRYPTO_INVALID_BLOCK_ALIGNMENT (-13) -+#define CRYPTO_INVALID_MODE (-14) -+#define CRYPTO_INVALID_KEY (-15) -+#define CRYPTO_AUTHENTICATION_FAILED (-16) -+#define CRYPTO_INVALID_IV_SIZE (-17) -+#define CRYPTO_MEMORY_ERROR (-18) -+#define CRYPTO_LAST_ERROR (-19) -+#define CRYPTO_HALTED (-20) -+#define CRYPTO_TIMEOUT (-21) -+#define CRYPTO_SRM_FAILED (-22) -+#define CRYPTO_COMMON_ERROR_MAX (-100) -+#define CRYPTO_INVALID_ICV_KEY_SIZE (-100) -+#define CRYPTO_INVALID_PARAMETER_SIZE (-101) -+#define CRYPTO_SEQUENCE_OVERFLOW (-102) -+#define CRYPTO_DISABLED (-103) -+#define CRYPTO_INVALID_VERSION (-104) -+#define CRYPTO_FATAL (-105) -+#define CRYPTO_INVALID_PAD (-106) -+#define CRYPTO_FIFO_FULL (-107) -+#define CRYPTO_INVALID_SEQUENCE (-108) -+#define CRYPTO_INVALID_FIRMWARE (-109) -+#define CRYPTO_NOT_FOUND (-110) -+#define CRYPTO_CMD_FIFO_INACTIVE (-111) -+#define CRYPTO_INVALID_PROTOCOL (-112) -+#define CRYPTO_REPLAY (-113) -+#define CRYPTO_NOT_INSTANTIATED (-114) -+#define CRYPTO_RESEED_REQUIRED (-115) ++&espi0 { ++ status = "okay"; ++ perif-dma-mode; ++ perif-mmbi-enable; ++ perif-mmbi-src-addr = <0x0 0xa8000000>; ++ perif-mmbi-tgt-memory = <&espi0_mmbi_memory>; ++ perif-mmbi-instance-num = <0x1>; ++ perif-mcyc-enable; ++ perif-mcyc-src-addr = <0x0 0x98000000>; ++ perif-mcyc-size = <0x0 0x10000>; ++ vw-pltrst-monitor; ++ oob-dma-mode; ++ flash-dma-mode; ++}; + ++#if 0 //eSPI1 and SD are multi-functional pin, SD default on ++&espi1 { ++ status = "okay"; ++ perif-dma-mode; ++ perif-mmbi-enable; ++ perif-mmbi-src-addr = <0x0 0xa8000000>; ++ perif-mmbi-tgt-memory = <&espi1_mmbi_memory>; ++ perif-mmbi-instance-num = <0x1>; ++ perif-mcyc-enable; ++ perif-mcyc-src-addr = <0x0 0x98000000>; ++ perif-mcyc-size = <0x0 0x10000>; ++ vw-pltrst-monitor; ++ oob-dma-mode; ++ flash-dma-mode; ++}; +#endif -diff --git a/drivers/char/hw_random/dwc/src/pdu/common/pdu/pdu_dev32.c b/drivers/char/hw_random/dwc/src/pdu/common/pdu/pdu_dev32.c ---- a/drivers/char/hw_random/dwc/src/pdu/common/pdu/pdu_dev32.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/pdu/common/pdu/pdu_dev32.c 2025-12-23 10:16:19.524059486 +0000 -@@ -0,0 +1,165 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * This Synopsys software and associated documentation (hereinafter the -+ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless -+ * otherwise expressly agreed to in writing between Synopsys and you. The -+ * Software IS NOT an item of Licensed Software or a Licensed Product under -+ * any End User Software License Agreement or Agreement for Licensed Products -+ * with Synopsys or any supplement thereto. Synopsys is a registered trademark -+ * of Synopsys, Inc. Other names included in the SOFTWARE may be the -+ * trademarks of their respective owners. -+ * -+ * The contents of this file are dual-licensed; you may select either version -+ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license -+ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the -+ * SOFTWARE. The BSD License is copied below. -+ * -+ * BSD-3-Clause License: -+ * Copyright (c) 2011-2017 Synopsys, Inc. and/or its affiliates. -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright notice, -+ * this list of conditions, and the following disclaimer, without -+ * modification. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * 3. The names of the above-listed copyright holders may not be used to -+ * endorse or promote products derived from this software without specific -+ * prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -+ * POSSIBILITY OF SUCH DAMAGE. -+ */ + -+#include "elppdu.h" ++&lpc0_kcs0 { ++ status = "okay"; ++ kcs-io-addr = <0xca0>; ++ kcs-channel = <0>; ++}; + -+void pdu_to_dev32(void *addr_, u32 *src, unsigned long nword) -+{ -+ unsigned char *addr = addr_; ++&lpc0_kcs1 { ++ status = "okay"; ++ kcs-io-addr = <0xca8>; ++ kcs-channel = <1>; ++}; + -+ while (nword--) { -+ pdu_io_write32(addr, *src++); -+ addr += 4; -+ } -+} -+EXPORT_SYMBOL(pdu_to_dev32); ++&lpc0_kcs2 { ++ status = "okay"; ++ kcs-io-addr = <0xca2>; ++ kcs-channel = <2>; ++}; + -+void pdu_from_dev32(u32 *dst, void *addr_, unsigned long nword) -+{ -+ unsigned char *addr = addr_; ++&lpc0_kcs3 { ++ status = "okay"; ++ kcs-io-addr = <0xca4>; ++ kcs-channel = <3>; ++}; + -+ while (nword--) { -+ *dst++ = pdu_io_read32(addr); -+ addr += 4; -+ } -+} -+EXPORT_SYMBOL(pdu_from_dev32); ++&lpc0_ibt { ++ status = "okay"; ++}; + -+void pdu_to_dev32_big(void *addr_, const unsigned char *src, -+ unsigned long nword) -+{ -+ unsigned char *addr = addr_; -+ unsigned long v; ++&lpc0_mbox { ++ status = "okay"; ++}; + -+ while (nword--) { -+ v = 0; -+ v = (v << 8) | ((unsigned long)*src++); -+ v = (v << 8) | ((unsigned long)*src++); -+ v = (v << 8) | ((unsigned long)*src++); -+ v = (v << 8) | ((unsigned long)*src++); -+ pdu_io_write32(addr, v); -+ addr += 4; -+ } -+} -+EXPORT_SYMBOL(pdu_to_dev32_big); ++&lpc0_snoop { ++ status = "okay"; ++ snoop-ports = <0x80>, <0x81>; ++}; + -+void pdu_from_dev32_big(unsigned char *dst, void *addr_, unsigned long nword) -+{ -+ unsigned char *addr = addr_; -+ unsigned long v; ++&lpc0_uart_routing { ++ status = "okay"; ++}; + -+ while (nword--) { -+ v = pdu_io_read32(addr); -+ addr += 4; -+ *dst++ = (v >> 24) & 0xFF; -+ v <<= 8; -+ *dst++ = (v >> 24) & 0xFF; -+ v <<= 8; -+ *dst++ = (v >> 24) & 0xFF; -+ v <<= 8; -+ *dst++ = (v >> 24) & 0xFF; -+ v <<= 8; -+ } -+} -+EXPORT_SYMBOL(pdu_from_dev32_big); ++&lpc1_kcs0 { ++ status = "okay"; ++ kcs-io-addr = <0xca0>; ++ kcs-channel = <4>; ++}; + -+void pdu_to_dev32_little(void *addr_, const unsigned char *src, -+ unsigned long nword) -+{ -+ unsigned char *addr = addr_; -+ unsigned long v; ++&lpc1_kcs1 { ++ status = "okay"; ++ kcs-io-addr = <0xca8>; ++ kcs-channel = <5>; ++}; + -+ while (nword--) { -+ v = 0; -+ v = (v >> 8) | ((unsigned long)*src++ << 24UL); -+ v = (v >> 8) | ((unsigned long)*src++ << 24UL); -+ v = (v >> 8) | ((unsigned long)*src++ << 24UL); -+ v = (v >> 8) | ((unsigned long)*src++ << 24UL); -+ pdu_io_write32(addr, v); -+ addr += 4; -+ } -+} -+EXPORT_SYMBOL(pdu_to_dev32_little); ++&lpc1_kcs2 { ++ status = "okay"; ++ kcs-io-addr = <0xca2>; ++ kcs-channel = <6>; ++}; + -+void pdu_from_dev32_little(unsigned char *dst, void *addr_, unsigned long nword) -+{ -+ unsigned char *addr = addr_; -+ unsigned long v; ++&lpc1_kcs3 { ++ status = "okay"; ++ kcs-io-addr = <0xca4>; ++ kcs-channel = <7>; ++}; + -+ while (nword--) { -+ v = pdu_io_read32(addr); -+ addr += 4; -+ *dst++ = v & 0xFF; -+ v >>= 8; -+ *dst++ = v & 0xFF; -+ v >>= 8; -+ *dst++ = v & 0xFF; -+ v >>= 8; -+ *dst++ = v & 0xFF; -+ v >>= 8; -+ } -+} -+EXPORT_SYMBOL(pdu_from_dev32_little); ++&lpc1_ibt { ++ status = "okay"; ++}; + -+void pdu_to_dev32_s(void *addr, const unsigned char *src, unsigned long nword, -+ int endian) -+{ -+ if (endian) -+ pdu_to_dev32_big(addr, src, nword); -+ else -+ pdu_to_dev32_little(addr, src, nword); -+} -+EXPORT_SYMBOL(pdu_to_dev32_s); ++&lpc1_mbox { ++ status = "okay"; ++}; + -+void pdu_from_dev32_s(unsigned char *dst, void *addr, unsigned long nword, -+ int endian) -+{ -+ if (endian) -+ pdu_from_dev32_big(dst, addr, nword); -+ else -+ pdu_from_dev32_little(dst, addr, nword); -+} -+EXPORT_SYMBOL(pdu_from_dev32_s); -diff --git a/drivers/char/hw_random/dwc/src/pdu/linux/include/elppdu.h b/drivers/char/hw_random/dwc/src/pdu/linux/include/elppdu.h ---- a/drivers/char/hw_random/dwc/src/pdu/linux/include/elppdu.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/pdu/linux/include/elppdu.h 2025-12-23 10:16:19.524059486 +0000 -@@ -0,0 +1,125 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * This Synopsys software and associated documentation (hereinafter the -+ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless -+ * otherwise expressly agreed to in writing between Synopsys and you. The -+ * Software IS NOT an item of Licensed Software or a Licensed Product under -+ * any End User Software License Agreement or Agreement for Licensed Products -+ * with Synopsys or any supplement thereto. Synopsys is a registered trademark -+ * of Synopsys, Inc. Other names included in the SOFTWARE may be the -+ * trademarks of their respective owners. -+ * -+ * The contents of this file are dual-licensed; you may select either version -+ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license -+ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the -+ * SOFTWARE. The BSD License is copied below. -+ * -+ * BSD-3-Clause License: -+ * Copyright (c) 2011-2017 Synopsys, Inc. and/or its affiliates. -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright notice, -+ * this list of conditions, and the following disclaimer, without -+ * modification. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * 3. The names of the above-listed copyright holders may not be used to -+ * endorse or promote products derived from this software without specific -+ * prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -+ * POSSIBILITY OF SUCH DAMAGE. -+ */ ++&lpc1_snoop { ++ status = "okay"; ++ snoop-ports = <0x80>, <0x81>; ++}; + -+#ifndef SYNPDU_H_ -+#define SYNPDU_H_ ++&lpc1_uart_routing { ++ status = "okay"; ++}; + -+/* Platform Specific */ -+#include /* printk() */ -+#include /* size_t */ -+#include /* memcpy()/etc */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#ifndef PDU_BASE_ADDR -+#define PDU_BASE_ADDR 0x14c3b000 -+#endif -+ -+#ifndef PDU_BASE_IRQ -+#define PDU_BASE_IRQ 91 -+#endif -+ -+#define PDU_SINGLE_CORE 1 -+#define PDU_SINGLE_NIST_TRNG 1 -+ -+#if 1 -+#define SYNHW_PRINT printk -+#else -+#define SYNHW_PRINT(...) -+#endif ++&video0 { ++ status = "okay"; ++ memory-region = <&video_engine_memory0>; ++}; + -+#define CPU_YIELD -+#define SYNHW_MEMCPY memcpy ++&video1 { ++ status = "okay"; ++ memory-region = <&video_engine_memory1>; ++}; + -+// Debug modifier for printing, in linux adding KERN_DEBUG makes the output only show up in debug logs (avoids /var/log/messages) -+#define SYNHW_PRINT_DEBUG KERN_DEBUG ++&disp_intf { ++ status = "okay"; ++}; + -+// Locking -+#define PDU_LOCK_TYPE spinlock_t -+#define PDU_INIT_LOCK(lock) spin_lock_init(lock) ++&rtc { ++ status = "okay"; ++}; + -+// these are for IRQ contexts -+#define PDU_LOCK(lock, flags) spin_lock_irqsave(lock, flags) -+#define PDU_UNLOCK(lock, flags) spin_unlock_irqrestore(lock, flags) ++&rsss { ++ status = "okay"; ++}; + -+// these are for bottom half BH contexts -+#define PDU_LOCK_TYPE_BH struct mutex -+#define PDU_INIT_LOCK_BH(lock) mutex_init(lock) -+#define PDU_LOCK_BH(lock) mutex_lock(lock) -+#define PDU_UNLOCK_BH(lock) mutex_unlock(lock) ++&ecdsa { ++ status = "okay"; ++}; + -+#include "../../common/include/elppdu_error.h" ++&hace { ++ status = "okay"; ++}; + -+void *pdu_linux_map_regs(struct device *dev, struct resource *regs); ++&bmc_dev0 { ++ status = "okay"; ++ memory-region = <&bmc_dev0_memory>; ++}; + -+void pdu_io_write32(void *addr, unsigned long val); -+void pdu_io_cached_write32(void *addr, unsigned long val, u32 *cache); -+unsigned long pdu_io_read32(void *addr); ++&xdma0 { ++ status = "okay"; ++ memory-region = <&xdma_memory0>; ++}; + -+void pdu_to_dev32(void *addr, u32 *src, unsigned long nword); -+void pdu_from_dev32(u32 *dst, void *addr, unsigned long nword); -+void pdu_to_dev32_big(void *addr, const unsigned char *src, unsigned long nword); -+void pdu_from_dev32_big(unsigned char *dst, void *addr, unsigned long nword); -+void pdu_to_dev32_little(void *addr, const unsigned char *src, unsigned long nword); -+void pdu_from_dev32_little(unsigned char *dst, void *addr, unsigned long nword); -+void pdu_from_dev32_s(unsigned char *dst, void *addr, unsigned long nword, int endian); -+void pdu_to_dev32_s(void *addr, const unsigned char *src, unsigned long nword, int endian); ++&pcie_vuart0 { ++ port = <0x3f8>; ++ sirq = <4>; ++ sirq-polarity = <0>; + -+void *pdu_malloc(unsigned long n); -+void pdu_free(void *p); ++ status = "okay"; ++}; + -+int pdu_error_code(int code); ++&pcie_vuart1 { ++ port = <0x2f8>; ++ sirq = <3>; ++ sirq-polarity = <0>; + -+#endif ++ status = "okay"; ++}; + -diff --git a/drivers/char/hw_random/dwc/src/pdu/linux/kernel/pdu.c b/drivers/char/hw_random/dwc/src/pdu/linux/kernel/pdu.c ---- a/drivers/char/hw_random/dwc/src/pdu/linux/kernel/pdu.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/pdu/linux/kernel/pdu.c 2025-12-23 10:16:19.524059486 +0000 -@@ -0,0 +1,188 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * This Synopsys software and associated documentation (hereinafter the -+ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless -+ * otherwise expressly agreed to in writing between Synopsys and you. The -+ * Software IS NOT an item of Licensed Software or a Licensed Product under -+ * any End User Software License Agreement or Agreement for Licensed Products -+ * with Synopsys or any supplement thereto. Synopsys is a registered trademark -+ * of Synopsys, Inc. Other names included in the SOFTWARE may be the -+ * trademarks of their respective owners. -+ * -+ * The contents of this file are dual-licensed; you may select either version -+ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license -+ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the -+ * SOFTWARE. The BSD License is copied below. -+ * -+ * BSD-3-Clause License: -+ * Copyright (c) 2011-2017 Synopsys, Inc. and/or its affiliates. -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright notice, -+ * this list of conditions, and the following disclaimer, without -+ * modification. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * 3. The names of the above-listed copyright holders may not be used to -+ * endorse or promote products derived from this software without specific -+ * prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -+ * POSSIBILITY OF SUCH DAMAGE. -+ */ ++&pcie_lpc0_kcs0 { ++ status = "okay"; ++ kcs-io-addr = <0x3a0>; ++ kcs-channel = <8>; ++}; + -+#include ++&pcie_lpc0_kcs1 { ++ status = "okay"; ++ kcs-io-addr = <0x3a8>; ++ kcs-channel = <9>; ++}; + -+#include "elppdu.h" ++&pcie_lpc0_kcs2 { ++ status = "okay"; ++ kcs-io-addr = <0x3a2>; ++ kcs-channel = <10>; ++}; + -+static bool trace_io; -+module_param(trace_io, bool, 0600); -+MODULE_PARM_DESC(trace_io, "Trace MMIO reads/writes"); ++&pcie_lpc0_kcs3 { ++ status = "okay"; ++ kcs-io-addr = <0x3a4>; ++ kcs-channel = <11>; ++}; + -+void *pdu_linux_map_regs(struct device *dev, struct resource *regs) -+{ -+ return devm_ioremap_resource(dev, regs); -+} -+EXPORT_SYMBOL(pdu_linux_map_regs); ++&pcie_lpc0_ibt { ++ status = "okay"; ++ bt-channel = <2>; ++}; + -+void pdu_io_write32(void *addr, unsigned long val) -+{ -+ if (trace_io) -+ SYNHW_PRINT("PDU: write %.8lx -> %p\n", val, addr); ++&bmc_dev1 { ++ status = "okay"; ++ memory-region = <&bmc_dev1_memory>; ++}; + -+ writel(val, addr); -+} -+EXPORT_SYMBOL(pdu_io_write32); ++&xdma1 { ++ status = "okay"; ++ memory-region = <&xdma_memory1>; ++}; + -+void pdu_io_cached_write32(void *addr, unsigned long val, uint32_t *cache) -+{ -+ if (*cache == val) { -+ if (trace_io) { -+ SYNHW_PRINT("PDU: write %.8lx -> %p (cached)\n", val, -+ addr); -+ } -+ return; -+ } ++&pcie_vuart2 { ++ port = <0x3f8>; ++ sirq = <4>; ++ sirq-polarity = <0>; + -+ *cache = val; -+ pdu_io_write32(addr, val); -+} -+EXPORT_SYMBOL(pdu_io_cached_write32); ++ status = "okay"; ++}; + -+unsigned long pdu_io_read32(void *addr) -+{ -+ unsigned long val; ++&pcie_vuart3 { ++ port = <0x2f8>; ++ sirq = <3>; ++ sirq-polarity = <0>; + -+ val = readl(addr); ++ status = "okay"; ++}; + -+ if (trace_io) -+ SYNHW_PRINT("PDU: read %.8lx <- %p\n", val, addr); ++&pcie_lpc1_kcs0 { ++ status = "okay"; ++ kcs-io-addr = <0x3a0>; ++ kcs-channel = <12>; ++}; + -+ return val; -+} -+EXPORT_SYMBOL(pdu_io_read32); ++&pcie_lpc1_kcs1 { ++ status = "okay"; ++ kcs-io-addr = <0x3a8>; ++ kcs-channel = <13>; ++}; + -+/* Platform specific memory allocation */ -+void *pdu_malloc(unsigned long n) -+{ -+ return vmalloc(n); -+} ++&pcie_lpc1_kcs2 { ++ status = "okay"; ++ kcs-io-addr = <0x3a2>; ++ kcs-channel = <14>; ++}; + -+void pdu_free(void *p) -+{ -+ vfree(p); -+} ++&pcie_lpc1_kcs3 { ++ status = "okay"; ++ kcs-io-addr = <0x3a4>; ++ kcs-channel = <15>; ++}; + -+/* Convert SDK error codes to corresponding kernel error codes. */ -+int pdu_error_code(int code) -+{ -+ switch (code) { -+ case CRYPTO_INPROGRESS: -+ return -EINPROGRESS; -+ case CRYPTO_INVALID_HANDLE: -+ case CRYPTO_INVALID_CONTEXT: -+ return -ENXIO; -+ case CRYPTO_NOT_INITIALIZED: -+ return -ENODATA; -+ case CRYPTO_INVALID_SIZE: -+ case CRYPTO_INVALID_ALG: -+ case CRYPTO_INVALID_KEY_SIZE: -+ case CRYPTO_INVALID_ARGUMENT: -+ case CRYPTO_INVALID_BLOCK_ALIGNMENT: -+ case CRYPTO_INVALID_MODE: -+ case CRYPTO_INVALID_KEY: -+ case CRYPTO_INVALID_IV_SIZE: -+ case CRYPTO_INVALID_ICV_KEY_SIZE: -+ case CRYPTO_INVALID_PARAMETER_SIZE: -+ case CRYPTO_REPLAY: -+ case CRYPTO_INVALID_PROTOCOL: -+ case CRYPTO_RESEED_REQUIRED: -+ return -EINVAL; -+ case CRYPTO_NOT_IMPLEMENTED: -+ case CRYPTO_MODULE_DISABLED: -+ return -ENOTSUPP; -+ case CRYPTO_NO_MEM: -+ return -ENOMEM; -+ case CRYPTO_INVALID_PAD: -+ case CRYPTO_INVALID_SEQUENCE: -+ return -EILSEQ; -+ case CRYPTO_MEMORY_ERROR: -+ return -EIO; -+ case CRYPTO_TIMEOUT: -+ return -ETIMEDOUT; -+ case CRYPTO_HALTED: -+ return -ECANCELED; -+ case CRYPTO_AUTHENTICATION_FAILED: -+ case CRYPTO_SEQUENCE_OVERFLOW: -+ case CRYPTO_INVALID_VERSION: -+ return -EPROTO; -+ case CRYPTO_FIFO_FULL: -+ return -EBUSY; -+ case CRYPTO_SRM_FAILED: -+ case CRYPTO_DISABLED: -+ case CRYPTO_LAST_ERROR: -+ return -EAGAIN; -+ case CRYPTO_FAILED: -+ case CRYPTO_FATAL: -+ return -EIO; -+ case CRYPTO_INVALID_FIRMWARE: -+ return -ENOEXEC; -+ case CRYPTO_NOT_FOUND: -+ return -ENOENT; -+ } ++&pcie_lpc1_ibt { ++ status = "okay"; ++ bt-channel = <3>; ++}; + -+ /* -+ * Any unrecognized code is either success (i.e., zero) or a negative -+ * error code, which may be meaningless but at least will still be -+ * recognized as an error. -+ */ -+ return code; -+} -+EXPORT_SYMBOL(pdu_error_code); ++&mctp0 { ++ status = "okay"; ++ memory-region = <&mctp0_reserved>; ++}; + -+static int __init pdu_mod_init(void) -+{ -+ return 0; -+} ++&mctp1 { ++ status = "okay"; ++ memory-region = <&mctp1_reserved>; ++}; + -+static void __exit pdu_mod_exit(void) -+{ -+} ++/* Enable i2c0~i2c4 for LTPI testing. */ ++&i2c0 { ++ /* SMB_PMBUS1_SCM */ ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; + -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Synopsys, Inc."); -+module_init(pdu_mod_init); -+module_exit(pdu_mod_exit); -diff --git a/drivers/char/hw_random/dwc/src/pdu/linux/kernel/spacc_mem.c b/drivers/char/hw_random/dwc/src/pdu/linux/kernel/spacc_mem.c ---- a/drivers/char/hw_random/dwc/src/pdu/linux/kernel/spacc_mem.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/pdu/linux/kernel/spacc_mem.c 2025-12-23 10:16:19.524059486 +0000 -@@ -0,0 +1,191 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * This Synopsys software and associated documentation (hereinafter the -+ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless -+ * otherwise expressly agreed to in writing between Synopsys and you. The -+ * Software IS NOT an item of Licensed Software or a Licensed Product under -+ * any End User Software License Agreement or Agreement for Licensed Products -+ * with Synopsys or any supplement thereto. Synopsys is a registered trademark -+ * of Synopsys, Inc. Other names included in the SOFTWARE may be the -+ * trademarks of their respective owners. -+ * -+ * The contents of this file are dual-licensed; you may select either version -+ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license -+ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the -+ * SOFTWARE. The BSD License is copied below. -+ * -+ * BSD-3-Clause License: -+ * Copyright (c) 2011-2016 Synopsys, Inc. and/or its affiliates. -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright notice, -+ * this list of conditions, and the following disclaimer, without -+ * modification. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * 3. The names of the above-listed copyright holders may not be used to -+ * endorse or promote products derived from this software without specific -+ * prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -+ * POSSIBILITY OF SUCH DAMAGE. -+ */ ++&i2c1 { ++ /* SMB_IPMB_SCL1 */ ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; + -+#include -+#include -+#include -+#include -+#include -+#include ++&i2c2 { ++ /* SMB_CPLD_SCL2 */ ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; + -+#include "elppdu.h" ++&i2c3 { ++ /* SMB_PMBUS2_SCM */ ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; + -+static unsigned long vex_baseaddr = PDU_BASE_ADDR; -+module_param_named(baseaddr, vex_baseaddr, ulong, 0); -+MODULE_PARM_DESC(baseaddr, "Hardware base address (default " __stringify(PDU_BASE_ADDR) ")"); ++&i2c4 { ++ /* SMB_PMBUS1_SCM */ ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; + -+// max of 16 devices -+#define MAX_DEV 16 ++/* Default i2c5 is tunnelded to LTPI. */ ++/* Enable i2c5 pinctrl for testing on board LM75. */ ++&i2c5 { ++ /* SMB_TMP_BMC */ ++ status = "okay"; ++}; + -+static struct platform_device *devices[MAX_DEV]; -+static int dev_id; ++/* AST2700 i2c8 -> AST1060 i2c5 for PCH mailbox emulation test. */ ++&i2c8 { ++ /* SMB_PCIE_SCM */ ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+static void register_device(const char *name, int id, -+ const struct resource *res, unsigned int num) -+{ -+ char suffix[16] = ""; -+ struct platform_device_info pdevinfo = { -+ .name = name, -+ .id = id, -+ .res = res, -+ .num_res = num, -+ .dma_mask = 0xffffffff, ++&i2c9 { ++ /* SMB_HOST_BMC */ ++ status = "okay"; ++ eeprom@50 { ++ compatible = "atmel,24c04"; ++ reg = <0x50>; ++ pagesize = <16>; + }; ++}; + -+ if (dev_id >= MAX_DEV) { -+ pr_err("Too many devices; increase MAX_DEV.\n"); -+ return; -+ } ++/* AST2700 A1 i2c10 -> AST1060 i2c0 for PFR mailbox. */ ++&i2c10 { ++ /* SMB_HSBP_BMC */ ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ devices[dev_id] = platform_device_register_full(&pdevinfo); -+ if (IS_ERR(devices[dev_id])) { -+ if (id >= 0) -+ snprintf(suffix, sizeof(suffix), ".%d", id); -+ pr_err("Failed to register %s%s\n", name, suffix); ++&i2c11 { ++ /* BMC_HPM_I3C_I2C_13 */ ++ status = "okay"; ++}; + -+ devices[dev_id] = NULL; -+ return; -+ } ++&uphy3a { ++ status = "okay"; ++}; + -+ dev_id++; -+} ++&uphy3b { ++ status = "okay"; ++}; + -+static int __init get_irq_num(unsigned int irq_num) -+{ -+ if (IS_ENABLED(CONFIG_ARCH_ZYNQ)) { -+ struct of_phandle_args args = { 0 }; ++&vhuba0 { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_usb2ahpd0_default>; ++}; + -+ /* -+ * Since this driver is for non-DT use but Zynq uses DT to setup IRQs, -+ * find the GIC by searching for its DT node then manually create the -+ * IRQ mappings. -+ */ ++&usb3ahp { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_usb3axhp_default &pinctrl_usb2axhp_default>; ++}; + -+ do { -+ args.np = of_find_node_with_property(args.np, -+ "interrupt-controller"); -+ if (!args.np) { -+ pr_err("cannot find IRQ controller"); -+ return -ENODEV; -+ } -+ } while (!of_device_is_compatible(args.np, "arm,cortex-a9-gic")); ++&usb3bhp { ++ status = "okay"; ++}; + -+ if (irq_num < 32 || irq_num >= 96) { -+ pr_err("SPI interrupts must be in the range [32,96) on Zynq\n"); -+ return -EINVAL; -+ } ++&uphy2b { ++ status = "okay"; ++}; + -+ args.args_count = 3; -+ args.args[0] = 0; /* SPI */ -+ args.args[1] = irq_num - 32; -+ args.args[2] = 4; /* Active high, level-sensitive */ ++&vhubb1 { ++ status = "okay"; ++}; + -+ irq_num = irq_create_of_mapping(&args); -+ of_node_put(args.np); -+ if (irq_num == 0) -+ return -EINVAL; -+ } ++&vhubc { ++ status = "okay"; ++}; + -+ if (irq_num > INT_MAX) -+ return -EINVAL; ++&ehci3 { ++ status = "okay"; ++}; + -+ return irq_num; -+} ++&uhci1 { ++ status = "okay"; ++ memory-region = <&uhci1_reserved>; ++}; + -+static int __init pdu_vex_mod_init(void) -+{ -+ int irq_num = get_irq_num(PDU_BASE_IRQ); -+ struct resource res[2]; -+#ifndef PDU_SINGLE_CORE -+ void *pdu_mem; -+ int i, rc; -+#endif ++&wdt0 { ++ status = "okay"; ++}; + -+ if (irq_num >= 0) { -+ res[1] = (struct resource){ -+ .start = irq_num, -+ .end = irq_num, -+ .flags = IORESOURCE_IRQ, -+ }; -+ } else { -+ res[1] = (struct resource){ 0 }; -+ pr_err("IRQ setup failed (error %d), not using IRQs\n", -+ irq_num); -+ } ++&wdt1 { ++ status = "okay"; ++}; + -+#ifdef PDU_SINGLE_BASIC_TRNG -+ res[0] = (struct resource){ -+ .start = vex_baseaddr, -+ .end = vex_baseaddr + 0x80 - 1, -+ .flags = IORESOURCE_MEM, -+ }; -+ register_device("basic_trng", -1, res, 2); -+#endif ++&otp { ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb-dual.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb-dual.dts +--- a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb-dual.dts 1970-01-01 00:00:00.000000000 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb-dual.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,218 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later + -+#ifdef PDU_SINGLE_NIST_TRNG -+ res[0] = (struct resource){ -+ .start = vex_baseaddr, -+ .end = vex_baseaddr + 0x800 - 1, -+ .flags = IORESOURCE_MEM, ++/dts-v1/; ++ ++#include "ast2700a1-dcscm_ast1700a1-evb.dts" ++#include "aspeed-ltpi1.dtsi" ++ ++/ { ++ model = "AST2700A1-DCSCM_AST1700A1-EVB-DUAL"; ++ ++ ltpi1-iio-hwmon { ++ compatible = "iio-hwmon"; ++ io-channels = <<pi1_adc0 0>, <<pi1_adc0 1>, <<pi1_adc0 2>, <<pi1_adc0 3>, ++ <<pi1_adc0 4>, <<pi1_adc0 5>, <<pi1_adc0 6>, <<pi1_adc0 7>, ++ <<pi1_adc1 0>, <<pi1_adc1 1>, <<pi1_adc1 2>, <<pi1_adc1 3>, ++ <<pi1_adc1 4>, <<pi1_adc1 5>, <<pi1_adc1 6>, <<pi1_adc1 7>; + }; -+ register_device("nist_trng", -1, res, 2); -+#endif ++}; + -+ return 0; -+} -+module_init(pdu_vex_mod_init); ++<pi1_adc0 { ++ aspeed,int-vref-microvolt = <2500000>; ++ status = "okay"; ++}; + -+static void __exit pdu_vex_mod_exit(void) -+{ -+ int i; ++<pi1_adc1 { ++ aspeed,int-vref-microvolt = <2500000>; ++ status = "okay"; ++}; + -+ for (i = 0; i < MAX_DEV; i++) -+ platform_device_unregister(devices[i]); -+} -+module_exit(pdu_vex_mod_exit); ++<pi1 { ++ i2c-tunneling = <0x0>; ++ status = "okay"; ++}; + -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Synopsys, Inc."); -diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng.h b/drivers/char/hw_random/dwc/src/trng/include/nisttrng.h ---- a/drivers/char/hw_random/dwc/src/trng/include/nisttrng.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng.h 2025-12-23 10:16:19.524059486 +0000 -@@ -0,0 +1,63 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * This Synopsys software and associated documentation (hereinafter the -+ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless -+ * otherwise expressly agreed to in writing between Synopsys and you. The -+ * Software IS NOT an item of Licensed Software or a Licensed Product under -+ * any End User Software License Agreement or Agreement for Licensed Products -+ * with Synopsys or any supplement thereto. Synopsys is a registered trademark -+ * of Synopsys, Inc. Other names included in the SOFTWARE may be the -+ * trademarks of their respective owners. -+ * -+ * The contents of this file are dual-licensed; you may select either version -+ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license -+ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the -+ * SOFTWARE. The BSD License is copied below. -+ * -+ * BSD-3-Clause License: -+ * Copyright (c) 2012-2016 Synopsys, Inc. and/or its affiliates. -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright notice, -+ * this list of conditions, and the following disclaimer, without -+ * modification. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * 3. The names of the above-listed copyright holders may not be used to -+ * endorse or promote products derived from this software without specific -+ * prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -+ * POSSIBILITY OF SUCH DAMAGE. -+ */ ++<pi1_gpio { ++ status = "okay"; ++}; + -+#ifndef NISTTRNG_H -+#define NISTTRNG_H ++<pi1_i3c0 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06010000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+#include "synversion.h" -+#include "elppdu.h" -+#include "nisttrng_hw.h" -+#include "nisttrng_common.h" -+#include "nisttrng_private.h" ++<pi1_i3c1 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+int nisttrng_init(struct nist_trng_state *state, u32 *base); -+int nisttrng_instantiate(struct nist_trng_state *state, int req_sec_strength, int pred_resist, void *personal_str); -+int nisttrng_uninstantiate(struct nist_trng_state *state); -+int nisttrng_reseed(struct nist_trng_state *state, int pred_resist, void *addin_str); -+int nisttrng_generate(struct nist_trng_state *state, void *random_bits, unsigned long req_num_bytes, int req_sec_strength, int pred_resist, void *addin_str); -+int nisttrng_rbc(struct nist_trng_state *state, int enable, int rbc_num, int rate, int urun_blnk); -+int nisttrng_generate_public_vtrng(struct nist_trng_state *state, void *random_bits, unsigned long req_num_bytes, int vtrng); -+#endif -diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_common.h b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_common.h ---- a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_common.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_common.h 2025-12-23 10:16:19.524059486 +0000 -@@ -0,0 +1,144 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+// ------------------------------------------------------------------------ -+// -+// (C) COPYRIGHT 2012 - 2016 SYNOPSYS, INC. -+// ALL RIGHTS RESERVED -+// -+// (C) COPYRIGHT 2012-2016 Synopsys, Inc. -+// This Synopsys software and all associated documentation are -+// proprietary to Synopsys, Inc. and may only be used pursuant -+// to the terms and conditions of a written license agreement -+// with Synopsys, Inc. All other use, reproduction, modification, -+// or distribution of the Synopsys software or the associated -+// documentation is strictly prohibited. -+// -+// ------------------------------------------------------------------------ ++<pi1_i3c2 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06012000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+#ifndef NISTTRNG_COMMON_H -+#define NISTTRNG_COMMON_H ++<pi1_i3c3 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+#define NIST_TRNG_RETRY_MAX 5000000UL ++<pi1_i3c4 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06014000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+#define NIST_DFLT_MAX_BITS_PER_REQ BIT(19) -+#define NIST_DFLT_MAX_REQ_PER_SEED BIT(48) ++<pi1_i3c5 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+/* Do not change the following parameters */ -+#define NIST_TRNG_DFLT_MAX_REJECTS 10 ++<pi1_i3c6 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06016000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+#define DEBUG(...) -+//#define DEBUG(...) printk(__VA_ARGS__) ++<pi1_i3c7 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+enum nisttrng_sec_strength { -+ SEC_STRNT_AES128 = 0, -+ SEC_STRNT_AES256 = 1 ++<pi1_i3c8 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06018000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; +}; + -+enum nisttrng_drbg_arch { -+ AES128 = 0, -+ AES256 = 1 ++<pi1_i3c9 { ++ initial-role = "primary"; ++ status = "okay"; +}; + -+enum nisttrng_current_state { -+ NIST_TRNG_STATE_INITIALIZE = 0, -+ NIST_TRNG_STATE_UNINSTANTIATE, -+ NIST_TRNG_STATE_INSTANTIATE, -+ NIST_TRNG_STATE_RESEED, -+ NIST_TRNG_STATE_GENERATE ++<pi1_i3c10 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601A000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; +}; + -+struct nist_trng_state { -+ u32 *base; ++<pi1_i3c11 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ /* Hardware features and build ID */ -+ struct { -+ struct { -+ enum nisttrng_drbg_arch drbg_arch; -+ unsigned int extra_ps_present, -+ secure_rst_state, -+ diag_level_basic_trng, -+ diag_level_stat_hlt, -+ diag_level_ns; -+ } features; ++<pi1_i3c12 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601C000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ struct { -+ unsigned int ext_enum, -+ ext_ver, -+ rel_num; -+ } corekit_rel; ++<pi1_i3c13 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ struct { -+ unsigned int core_type, -+ bg8, -+ cdc_synch_depth, -+ background_noise, -+ edu_present, -+ aes_datapath, -+ aes_max_key_size, -+ personilzation_str; -+ } build_cfg0; ++<pi1_i3c14 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601E000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ struct { -+ unsigned int num_raw_noise_blks, -+ sticky_startup, -+ auto_correlation_test, -+ mono_bit_test, -+ run_test, -+ poker_test, -+ raw_ht_adap_test, -+ raw_ht_rep_test, -+ ent_src_rep_smpl_size, -+ ent_src_rep_test, -+ ent_src_rep_min_entropy; -+ } build_cfg1; ++<pi1_i3c15 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ struct { -+ unsigned int rbc2_rate_width, -+ rbc1_rate_width, -+ rbc0_rate_width, -+ public_vtrng_channels, -+ esm_channel, -+ rbc_channels, -+ fifo_depth; -+ } edu_build_cfg0; -+ } config; ++&i2c6 { ++ status = "disabled"; ++}; + -+ /* status */ -+ struct { -+ //nist_trng_current_state current_state; -+ enum nisttrng_current_state current_state; // old for now -+ unsigned int nonce_mode, -+ secure_mode, -+ pred_resist; -+ //nist_trng_sec_strength sec_strength; -+ enum nisttrng_sec_strength sec_strength; -+ unsigned int pad_ps_addin; -+ unsigned int alarm_code; -+ // Private VTRNG STAT, all the public trng will have the same STAT as public TRNG in terms of -+ // rnc_enabled and seed_enum -+ struct { -+ unsigned int seed_enum, -+ rnc_enabled; -+ } edu_vstat; -+ } status; ++&i2c7 { ++ status = "disabled"; ++}; + -+ /* reminders and alarms */ -+ struct { -+ unsigned long max_bits_per_req; -+ unsigned long long max_req_per_seed; -+ unsigned long bits_per_req_left; -+ unsigned long long req_per_seed_left; -+ } counters; ++// The following I2C buses require enabling according to the ast2700-dcscm.dts. ++// i2c8 / i2c9 / i2c10 ++ ++&i2c11 { ++ status = "disabled"; +}; + -+#define nist_trng_zero_status(x) \ -+ memset(&((x)->status), 0, sizeof((x)->status)) ++<pi1_i2c0 { ++ status = "okay"; ++}; + -+#define DRBG_INSTANTIATED(cs) \ -+ ((((cs) == NIST_TRNG_STATE_INSTANTIATE) || \ -+ ((cs) == NIST_TRNG_STATE_RESEED) || \ -+ ((cs) == NIST_TRNG_STATE_GENERATE)) ? 1 : 0) ++<pi1_i2c1 { ++ status = "okay"; ++}; + -+#define REQ_SEC_STRENGTH_IS_VALID(sec_st) \ -+ ((((sec_st) > 0) && ((sec_st) <= 256)) ? 1 : 0) ++<pi1_i2c2 { ++ status = "okay"; ++}; + -+#endif -diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_hw.h b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_hw.h ---- a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_hw.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_hw.h 2025-12-23 10:16:19.524059486 +0000 -@@ -0,0 +1,457 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * This Synopsys software and associated documentation (hereinafter the -+ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless -+ * otherwise expressly agreed to in writing between Synopsys and you. The -+ * Software IS NOT an item of Licensed Software or a Licensed Product under -+ * any End User Software License Agreement or Agreement for Licensed Products -+ * with Synopsys or any supplement thereto. Synopsys is a registered trademark -+ * of Synopsys, Inc. Other names included in the SOFTWARE may be the -+ * trademarks of their respective owners. -+ * -+ * The contents of this file are dual-licensed; you may select either version -+ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license -+ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the -+ * SOFTWARE. The BSD License is copied below. -+ * -+ * BSD-3-Clause License: -+ * Copyright (c) 2012-2016 Synopsys, Inc. and/or its affiliates. -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright notice, -+ * this list of conditions, and the following disclaimer, without -+ * modification. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * 3. The names of the above-listed copyright holders may not be used to -+ * endorse or promote products derived from this software without specific -+ * prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -+ * POSSIBILITY OF SUCH DAMAGE. -+ */ ++<pi1_i2c3 { ++ status = "okay"; ++}; + -+#ifndef NISTTRNG_HW_H -+#define NISTTRNG_HW_H ++<pi1_i2c4 { ++ status = "okay"; ++}; + -+/* HW related Parameters */ -+#define NIST_TRNG_RAND_BLK_SIZE_BITS 128 -+#define CHX_URUN_BLANK_AFTER_RESET 0x3 ++<pi1_i2c5 { ++ status = "okay"; ++}; + -+/* registers */ -+#define NIST_TRNG_REG_CTRL 0x00 -+#define NIST_TRNG_REG_MODE 0x01 -+#define NIST_TRNG_REG_SMODE 0x02 -+#define NIST_TRNG_REG_STAT 0x03 -+#define NIST_TRNG_REG_IE 0x04 -+#define NIST_TRNG_REG_ISTAT 0x05 -+#define NIST_TRNG_REG_ALARM 0x06 -+#define NIST_TRNG_REG_COREKIT_REL 0x07 -+#define NIST_TRNG_REG_FEATURES 0x08 -+#define NIST_TRNG_REG_RAND0 0x09 -+#define NIST_TRNG_REG_RAND1 0x0A -+#define NIST_TRNG_REG_RAND2 0x0B -+#define NIST_TRNG_REG_RAND3 0x0C -+#define NIST_TRNG_REG_NPA_DATA0 0x0D -+#define NIST_TRNG_REG_NPA_DATA1 0x0E -+#define NIST_TRNG_REG_NPA_DATA2 0x0F -+#define NIST_TRNG_REG_NPA_DATA3 0x10 -+#define NIST_TRNG_REG_NPA_DATA4 0x11 -+#define NIST_TRNG_REG_NPA_DATA5 0x12 -+#define NIST_TRNG_REG_NPA_DATA6 0x13 -+#define NIST_TRNG_REG_NPA_DATA7 0x14 -+#define NIST_TRNG_REG_NPA_DATA8 0x15 -+#define NIST_TRNG_REG_NPA_DATA9 0x16 -+#define NIST_TRNG_REG_NPA_DATA10 0x17 -+#define NIST_TRNG_REG_NPA_DATA11 0x18 -+#define NIST_TRNG_REG_NPA_DATA12 0x19 -+#define NIST_TRNG_REG_NPA_DATA13 0x1A -+#define NIST_TRNG_REG_NPA_DATA14 0x1B -+#define NIST_TRNG_REG_NPA_DATA15 0x1C -+#define NIST_TRNG_REG_SEED0 0x1D -+#define NIST_TRNG_REG_SEED1 0x1E -+#define NIST_TRNG_REG_SEED2 0x1F -+#define NIST_TRNG_REG_SEED3 0x20 -+#define NIST_TRNG_REG_SEED4 0x21 -+#define NIST_TRNG_REG_SEED5 0x22 -+#define NIST_TRNG_REG_SEED6 0x23 -+#define NIST_TRNG_REG_SEED7 0x24 -+#define NIST_TRNG_REG_SEED8 0x25 -+#define NIST_TRNG_REG_SEED9 0x26 -+#define NIST_TRNG_REG_SEED10 0x27 -+#define NIST_TRNG_REG_SEED11 0x28 -+#define NIST_TRNG_REG_TIME_TO_SEED 0x34 -+#define NIST_TRNG_REG_IA_RDATA 0x38 -+#define NIST_TRNG_REG_IA_WDATA 0x39 -+#define NIST_TRNG_REG_IA_ADDR 0x3A -+#define NIST_TRNG_REG_IA_CMD 0x3B -+#define NIST_TRNG_REG_BUILD_CFG0 0x3C -+#define NIST_TRNG_REG_BUILD_CFG1 0x3D ++<pi1_i2c6 { ++ status = "okay"; ++}; + -+/* nist edu registers */ -+#define NIST_TRNG_EDU_RNC_CTRL 0x100 -+#define NIST_TRNG_EDU_FLUSH_CTRL 0x101 -+#define NIST_TRNG_EDU_RESEED_CNTR 0x102 -+#define NIST_TRNG_EDU_RBC_CTRL 0x104 -+#define NIST_TRNG_EDU_STAT 0x106 -+#define NIST_TRNG_EDU_IE 0x108 -+#define NIST_TRNG_EDU_ISTAT 0x109 -+#define NIST_TRNG_EDU_BUILD_CFG0 0x12C -+#define NIST_TRNG_EDU_VCTRL 0x138 -+#define NIST_TRNG_EDU_VSTAT 0x139 -+#define NIST_TRNG_EDU_VIE 0x13A -+#define NIST_TRNG_EDU_VISTAT 0x13B -+#define NIST_TRNG_EDU_VRAND_0 0x13C -+#define NIST_TRNG_EDU_VRAND_1 0x13D -+#define NIST_TRNG_EDU_VRAND_2 0x13E -+#define NIST_TRNG_EDU_VRAND_3 0x13F ++<pi1_i2c7 { ++ status = "okay"; ++}; + -+/* edu vtrng registers */ -+#define NIST_TRNG_EDU_VTRNG_VCTRL0 0x180 -+#define NIST_TRNG_EDU_VTRNG_VSTAT0 0x181 -+#define NIST_TRNG_EDU_VTRNG_VIE0 0x182 -+#define NIST_TRNG_EDU_VTRNG_VISTAT0 0x183 -+#define NIST_TRNG_EDU_VTRNG_VRAND0_0 0x184 -+#define NIST_TRNG_EDU_VTRNG_VRAND0_1 0x185 -+#define NIST_TRNG_EDU_VTRNG_VRAND0_2 0x186 -+#define NIST_TRNG_EDU_VTRNG_VRAND0_3 0x187 -+#define NIST_TRNG_EDU_VTRNG_VCTRL1 0x188 -+#define NIST_TRNG_EDU_VTRNG_VSTAT1 0x189 -+#define NIST_TRNG_EDU_VTRNG_VIE1 0x18A -+#define NIST_TRNG_EDU_VTRNG_VISTAT1 0x18B -+#define NIST_TRNG_EDU_VTRNG_VRAND1_0 0x18C -+#define NIST_TRNG_EDU_VTRNG_VRAND1_1 0x18D -+#define NIST_TRNG_EDU_VTRNG_VRAND1_2 0x18E -+#define NIST_TRNG_EDU_VTRNG_VRAND1_3 0x18F -+#define NIST_TRNG_EDU_VTRNG_VCTRL2 0x190 -+#define NIST_TRNG_EDU_VTRNG_VSTAT2 0x191 -+#define NIST_TRNG_EDU_VTRNG_VIE2 0x192 -+#define NIST_TRNG_EDU_VTRNG_VISTAT2 0x193 -+#define NIST_TRNG_EDU_VTRNG_VRAND2_0 0x194 -+#define NIST_TRNG_EDU_VTRNG_VRAND2_1 0x195 -+#define NIST_TRNG_EDU_VTRNG_VRAND2_2 0x196 -+#define NIST_TRNG_EDU_VTRNG_VRAND2_3 0x197 -+#define NIST_TRNG_EDU_VTRNG_VCTRL3 0x198 -+#define NIST_TRNG_EDU_VTRNG_VSTAT3 0x199 -+#define NIST_TRNG_EDU_VTRNG_VIE3 0x19A -+#define NIST_TRNG_EDU_VTRNG_VISTAT3 0x19B -+#define NIST_TRNG_EDU_VTRNG_VRAND3_0 0x19C -+#define NIST_TRNG_EDU_VTRNG_VRAND3_1 0x19D -+#define NIST_TRNG_EDU_VTRNG_VRAND3_2 0x19E -+#define NIST_TRNG_EDU_VTRNG_VRAND3_3 0x19F -+#define NIST_TRNG_EDU_VTRNG_VCTRL4 0x1A0 -+#define NIST_TRNG_EDU_VTRNG_VSTAT4 0x1A1 -+#define NIST_TRNG_EDU_VTRNG_VIE4 0x1A2 -+#define NIST_TRNG_EDU_VTRNG_VISTAT4 0x1A3 -+#define NIST_TRNG_EDU_VTRNG_VRAND4_0 0x1A4 -+#define NIST_TRNG_EDU_VTRNG_VRAND4_1 0x1A5 -+#define NIST_TRNG_EDU_VTRNG_VRAND4_2 0x1A6 -+#define NIST_TRNG_EDU_VTRNG_VRAND4_3 0x1A7 -+#define NIST_TRNG_EDU_VTRNG_VCTRL5 0x1A8 -+#define NIST_TRNG_EDU_VTRNG_VSTAT5 0x1A9 -+#define NIST_TRNG_EDU_VTRNG_VIE5 0x1AA -+#define NIST_TRNG_EDU_VTRNG_VISTAT5 0x1AB -+#define NIST_TRNG_EDU_VTRNG_VRAND5_0 0x1AC -+#define NIST_TRNG_EDU_VTRNG_VRAND5_1 0x1AD -+#define NIST_TRNG_EDU_VTRNG_VRAND5_2 0x1AE -+#define NIST_TRNG_EDU_VTRNG_VRAND5_3 0x1AF -+#define NIST_TRNG_EDU_VTRNG_VCTRL6 0x1B0 -+#define NIST_TRNG_EDU_VTRNG_VSTAT6 0x1B1 -+#define NIST_TRNG_EDU_VTRNG_VIE6 0x1B2 -+#define NIST_TRNG_EDU_VTRNG_VISTAT6 0x1B3 -+#define NIST_TRNG_EDU_VTRNG_VRAND6_0 0x1B4 -+#define NIST_TRNG_EDU_VTRNG_VRAND6_1 0x1B5 -+#define NIST_TRNG_EDU_VTRNG_VRAND6_2 0x1B6 -+#define NIST_TRNG_EDU_VTRNG_VRAND6_3 0x1B7 -+#define NIST_TRNG_EDU_VTRNG_VCTRL7 0x1B8 -+#define NIST_TRNG_EDU_VTRNG_VSTAT7 0x1B9 -+#define NIST_TRNG_EDU_VTRNG_VIE7 0x1BA -+#define NIST_TRNG_EDU_VTRNG_VISTAT7 0x1BB -+#define NIST_TRNG_EDU_VTRNG_VRAND7_0 0x1BC -+#define NIST_TRNG_EDU_VTRNG_VRAND7_1 0x1BD -+#define NIST_TRNG_EDU_VTRNG_VRAND7_2 0x1BE -+#define NIST_TRNG_EDU_VTRNG_VRAND7_3 0x1BF ++<pi1_i2c8 { ++ status = "okay"; ++}; + -+#define NIST_TRNG_EDU_VTRNG_VCTRL_CMD_NOP 0x0 -+#define NIST_TRNG_EDU_VTRNG_VCTRL_CMD_GET_RANDOM 0x1 -+#define NIST_TRNG_EDU_VTRNG_VCTRL_CMD_INIT 0x2 ++<pi1_i2c9 { ++ status = "okay"; ++}; + -+#define NIST_TRNG_EDU_VTRNG_VCTRL_CMD_MASK 0x3Ul -+#define NIST_TRNG_EDU_VTRNG_VCTRL_CMD_SET(y, x) (((y) & ~(NIST_TRNG_EDU_VTRNG_VCTRL_CMD_MASK)) | ((x))) ++<pi1_i2c10 { ++ status = "okay"; ++}; + -+/* CTRL */ -+#define NIST_TRNG_REG_CTRL_CMD_NOP 0 -+#define NIST_TRNG_REG_CTRL_CMD_GEN_NOISE 1 -+#define NIST_TRNG_REG_CTRL_CMD_GEN_NONCE 2 -+#define NIST_TRNG_REG_CTRL_CMD_CREATE_STATE 3 -+#define NIST_TRNG_REG_CTRL_CMD_RENEW_STATE 4 -+#define NIST_TRNG_REG_CTRL_CMD_REFRESH_ADDIN 5 -+#define NIST_TRNG_REG_CTRL_CMD_GEN_RANDOM 6 -+#define NIST_TRNG_REG_CTRL_CMD_ADVANCE_STATE 7 -+#define NIST_TRNG_REG_CTRL_CMD_KAT 8 -+#define NIST_TRNG_REG_CTRL_CMD_ZEROIZE 15 ++<pi1_i2c11 { ++ status = "okay"; ++}; + -+/* EDU CTRL */ -+#define NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_DISABLE_TO_HOLD 0 -+#define NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_ENABLE 1 -+#define NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_DISABLE_TO_IDLE 2 -+#define NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_FINISH_TO_IDLE 3 ++<pi1_i2c12 { ++ status = "okay"; ++}; + -+#define NIST_TRNG_EDU_RNC_CTRL_CMD_MASK 0x3Ul -+#define NIST_TRNG_EDU_RNC_CTRL_CMD_SET(y, x) (((y) & ~(NIST_TRNG_EDU_RNC_CTRL_CMD_MASK)) | ((x))) ++<pi1_i2c13 { ++ status = "okay"; ++}; + -+/* EDU_FLUSH_CTRL */ -+#define _NIST_TRNG_EDU_FLUSH_CTRL_CH2_RBC 3 -+#define _NIST_TRNG_EDU_FLUSH_CTRL_CH1_RBC 2 -+#define _NIST_TRNG_EDU_FLUSH_CTRL_CH0_RBC 1 -+#define _NIST_TRNG_EDU_FLUSH_CTRL_FIFO 0 ++<pi1_i2c14 { ++ status = "okay"; ++}; + -+#define NIST_TRNG_EDU_FLUSH_CTRL_CH2_RBC BIT(_NIST_TRNG_EDU_FLUSH_CTRL_CH2_RBC) -+#define NIST_TRNG_EDU_FLUSH_CTRL_CH1_RBC BIT(_NIST_TRNG_EDU_FLUSH_CTRL_CH1_RBC) -+#define NIST_TRNG_EDU_FLUSH_CTRL_CH0_RBC BIT(_NIST_TRNG_EDU_FLUSH_CTRL_CH0_RBC) -+#define NIST_TRNG_EDU_FLUSH_CTRL_FIFO BIT(_NIST_TRNG_EDU_FLUSH_CTRL_FIFO) ++<pi1_i2c15 { ++ status = "okay"; ++}; + -+/*EDU_RBC_CTRL*/ -+#define _NIST_TRNG_EDU_RBC_CTRL_CH2_URUN_BLANK 28 -+#define _NIST_TRNG_EDU_RBC_CTRL_CH1_URUN_BLANK 26 -+#define _NIST_TRNG_EDU_RBC_CTRL_CH0_URUN_BLANK 24 -+#define _NIST_TRNG_EDU_RBC_CTRL_CH2_RATE 16 -+#define _NIST_TRNG_EDU_RBC_CTRL_CH1_RATE 8 -+#define _NIST_TRNG_EDU_RBC_CTRL_CH0_RATE 0 ++&uart8 { ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb.dts +--- a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb.dts 1970-01-01 00:00:00.000000000 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,456 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later + -+#define _NIST_TRNG_EDU_RBC_CTRL_CH_RATE_MASK 0xFUL -+#define _NIST_TRNG_EDU_RBC_CTRL_CH_URUN_BLANK_MASK 0x3UL ++/dts-v1/; + -+#define NISTTRNG_EDU_RBC_CTRL_SET_CH_RATE(z, y, x) (((y) & ~(_NIST_TRNG_EDU_RBC_CTRL_CH_RATE_MASK << (x))) | ((z) << (x))) -+#define NISTTRNG_EDU_RBC_CTRL_SET_CH_URUN_BLANK(z, y, x) (((y) & ~(_NIST_TRNG_EDU_RBC_CTRL_CH_URUN_BLANK_MASK << (x))) | ((z) << (x))) ++#include "ast2700a1-dcscm.dts" ++#include "aspeed-ltpi0.dtsi" + -+#define NISTTRNG_EDU_RBC_CTRL_GET_CH_RATE(y, x) ((_NIST_TRNG_EDU_RBC_CTRL_CH_RATE_MASK) & ((y) >> (x))) -+#define NISTTRNG_EDU_RBC_CTRL_GET_CH_URUN_BLANK(y, x) ((_NIST_TRNG_EDU_RBC_CTRL_CH_URUN_BLANK_MASK) & ((y) >> (x))) ++/ { ++ model = "AST2700A1-DCSCM_AST1700A1-EVB"; + -+#define NISTTRNG_EDU_RBC_CTRL_GET_CH_RATE_AFTER_RESET 0x0 -+#define NISTTRNG_EDU_RBC_CTRL_SET_CH_URUN_BLANK_AFTER_RESET 0x3 ++ ltpi_fan0: ltpi-pwm-fan0 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 0 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+/* MODE */ -+#define _NIST_TRNG_REG_MODE_KAT_SEL 7 -+#define _NIST_TRNG_REG_MODE_KAT_VEC 5 -+#define _NIST_TRNG_REG_MODE_ADDIN_PRESENT 4 -+#define _NIST_TRNG_REG_MODE_PRED_RESIST 3 -+#define _NIST_TRNG_REG_MODE_SEC_ALG 0 ++ ltpi_fan1: ltpi-pwm-fan1 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 1 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+#define NIST_TRNG_REG_MODE_ADDIN_PRESENT BIT(_NIST_TRNG_REG_MODE_ADDIN_PRESENT) -+#define NIST_TRNG_REG_MODE_PRED_RESIST BIT(_NIST_TRNG_REG_MODE_PRED_RESIST) -+#define NIST_TRNG_REG_MODE_SEC_ALG BIT(_NIST_TRNG_REG_MODE_SEC_ALG) ++ ltpi_fan2: ltpi-pwm-fan2 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 2 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+/* SMODE */ -+#define _NIST_TRNG_REG_SMODE_NOISE_COLLECT 31 -+#define _NIST_TRNG_REG_SMODE_INDIV_HT_DISABLE 16 -+#define _NIST_TRNG_REG_SMODE_MAX_REJECTS 2 -+#define _NIST_TRNG_REG_SMODE_MISSION_MODE 1 -+#define _NIST_TRNG_REG_SMODE_SECURE_EN _NIST_TRNG_REG_SMODE_MISSION_MODE -+#define _NIST_TRNG_REG_SMODE_NONCE 0 ++ ltpi_fan3: ltpi-pwm-fan3 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 3 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+#define NIST_TRNG_REG_SMODE_MAX_REJECTS(x) ((x) << _NIST_TRNG_REG_SMODE_MAX_REJECTS) -+#define NIST_TRNG_REG_SMODE_SECURE_EN(x) ((x) << _NIST_TRNG_REG_SMODE_SECURE_EN) -+#define NIST_TRNG_REG_SMODE_NONCE BIT(_NIST_TRNG_REG_SMODE_NONCE) ++ ltpi_fan4: ltpi-pwm-fan4 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 4 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+/* STAT */ -+#define _NIST_TRNG_REG_STAT_BUSY 31 -+#define _NIST_TRNG_REG_STAT_STARTUP_TEST_IN_PROG 10 -+#define _NIST_TRNG_REG_STAT_STARTUP_TEST_STUCK 9 -+#define _NIST_TRNG_REG_STAT_DRBG_STATE 7 -+#define _NIST_TRNG_REG_STAT_SECURE 6 -+#define _NIST_TRNG_REG_STAT_NONCE_MODE 5 -+#define _NIST_TRNG_REG_STAT_SEC_ALG 4 -+#define _NIST_TRNG_REG_STAT_LAST_CMD 0 ++ ltpi_fan5: ltpi-pwm-fan5 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 5 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+#define NIST_TRNG_REG_STAT_BUSY BIT(_NIST_TRNG_REG_STAT_BUSY) -+//#define NIST_TRNG_REG_STAT_DRBG_STATE (1UL<<_NIST_TRNG_REG_STAT_DRBG_STATE) -+//#define NIST_TRNG_REG_STAT_SECURE (1UL << _NIST_TRNG_REG_STAT_SECURE) -+//#define NIST_TRNG_REG_STAT_NONCE_MODE (1UL << _NIST_TRNG_REG_STAT_NONCE_MODE) -+//#define NIST_TRNG_REG_STAT_SEC_ALG (1UL << _NIST_TRNG_REG_STAT_SEC_ALG) -+//#define NIST_TRNG_REG_STAT_LAST_CMD(x) (((x) >> _NIST_TRNG_REG_STAT_LAST_CMD)&0xF) ++ ltpi_fan6: ltpi-pwm-fan6 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 6 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+/*EDU_STAT*/ ++ ltpi_fan7: ltpi-pwm-fan7 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 7 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+#define NIST_TRNG_EDU_STAT_FIFO_LEVEL(x) (((x) >> 24) & 255) -+#define NIST_TRNG_EDU_STAT_TTT_INDEX(x) (((x) >> 16) & 255) -+#define NIST_TRNG_EDU_STAT_RNC_BUSY(x) (((x) >> 3) & 7) -+#define NIST_TRNG_EDU_STAT_RNC_ENABLED(x) (((x) >> 2) & 1) -+#define NIST_TRNG_EDU_STAT_FIFO_EMPTY(x) (((x) >> 1) & 1) -+#define NIST_TRNG_EDU_STAT_FIFO_FULL(x) ((x) & 1) ++ ltpi_fan8: ltpi-pwm-fan8 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 8 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+/* IE */ -+#define _NIST_TRNG_REG_IE_GLBL 31 -+#define _NIST_TRNG_REG_IE_DONE 4 -+#define _NIST_TRNG_REG_IE_ALARMS 3 -+#define _NIST_TRNG_REG_IE_NOISE_RDY 2 -+#define _NIST_TRNG_REG_IE_KAT_COMPLETE 1 -+#define _NIST_TRNG_REG_IE_ZEROIZE 0 ++ ltpi_fan9: ltpi-pwm-fan9 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 9 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+#define NIST_TRNG_REG_IE_GLBL BIT(_NIST_TRNG_REG_IE_GLBL) -+#define NIST_TRNG_REG_IE_DONE BIT(_NIST_TRNG_REG_IE_DONE) -+#define NIST_TRNG_REG_IE_ALARMS BIT(_NIST_TRNG_REG_IE_ALARMS) -+#define NIST_TRNG_REG_IE_NOISE_RDY BIT(_NIST_TRNG_REG_IE_NOISE_RDY) -+#define NIST_TRNG_REG_IE_KAT_COMPLETE BIT(_NIST_TRNG_REG_IE_KAT_COMPLETE) -+#define NIST_TRNG_REG_IE_ZEROIZE BIT(_NIST_TRNG_REG_IE_ZEROIZE) ++ ltpi_fan10: ltpi-pwm-fan10 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 10 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+/* ISTAT */ -+#define _NIST_TRNG_REG_ISTAT_DONE 4 -+#define _NIST_TRNG_REG_ISTAT_ALARMS 3 -+#define _NIST_TRNG_REG_ISTAT_NOISE_RDY 2 -+#define _NIST_TRNG_REG_ISTAT_KAT_COMPLETE 1 -+#define _NIST_TRNG_REG_ISTAT_ZEROIZE 0 ++ ltpi_fan11: ltpi-pwm-fan11 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 11 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+#define NIST_TRNG_REG_ISTAT_DONE BIT(_NIST_TRNG_REG_ISTAT_DONE) -+#define NIST_TRNG_REG_ISTAT_ALARMS BIT(_NIST_TRNG_REG_ISTAT_ALARMS) -+#define NIST_TRNG_REG_ISTAT_NOISE_RDY BIT(_NIST_TRNG_REG_ISTAT_NOISE_RDY) -+#define NIST_TRNG_REG_ISTAT_KAT_COMPLETE BIT(_NIST_TRNG_REG_ISTAT_KAT_COMPLETE) -+#define NIST_TRNG_REG_ISTAT_ZEROIZE BIT(_NIST_TRNG_REG_ISTAT_ZEROIZE) ++ ltpi_fan12: ltpi-pwm-fan12 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 12 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+/*EDU_ISTAT*/ ++ ltpi_fan13: ltpi-pwm-fan13 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 13 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+#define _NIST_TRNG_EDU_ISTAT_CH2_RBC_URUN 8 -+#define _NIST_TRNG_EDU_ISTAT_CH1_RBC_URUN 7 -+#define _NIST_TRNG_EDU_ISTAT_CH0_RBC_URUN 6 -+#define _NIST_TRNG_EDU_ISTAT_PRIVATE_VTRNG 5 -+#define _NIST_TRNG_EDU_ISTAT_WAIT_EXP_TIMEOUT 4 -+#define _NIST_TRNG_EDU_ISTAT_RNC_DRVN_OFFLINE 3 -+#define _NIST_TRNG_EDU_ISTAT_FIFO_URUN 2 -+#define _NIST_TRNG_EDU_ISTAT_ACCESS_VIOL 1 -+#define _NIST_TRNG_EDU_ISTAT_RESEED_REMINDER 0 ++ ltpi_fan14: ltpi-pwm-fan14 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 14 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+#define NIST_TRNG_EDU_ISTAT_CH2_RBC_URUN BIT(_NIST_TRNG_EDU_ISTAT_CH2_RBC_URUN) -+#define NIST_TRNG_EDU_ISTAT_CH1_RBC_URUN BIT(_NIST_TRNG_EDU_ISTAT_CH1_RBC_URUN) -+#define NIST_TRNG_EDU_ISTAT_CH0_RBC_URUN BIT(_NIST_TRNG_EDU_ISTAT_CH0_RBC_URUN) -+#define NIST_TRNG_EDU_ISTAT_PRIVATE_VTRNG BIT(_NIST_TRNG_EDU_ISTAT_PRIVATE_VTRNG) -+#define NIST_TRNG_EDU_ISTAT_WAIT_EXP_TIMEOUT BIT(_NIST_TRNG_EDU_ISTAT_WAIT_EXP_TIMEOUT) -+#define NIST_TRNG_EDU_ISTAT_RNC_DRVN_OFFLINE BIT(_NIST_TRNG_EDU_ISTAT_RNC_DRVN_OFFLINE) -+#define NIST_TRNG_EDU_ISTAT_FIFO_URUN BIT(_NIST_TRNG_EDU_ISTAT_FIFO_URUN) -+#define NIST_TRNG_EDU_ISTAT_ACCESS_VIOL BIT(_NIST_TRNG_EDU_ISTAT_ACCESS_VIOL) -+#define NIST_TRNG_EDU_ISTAT_RESEED_REMINDER BIT(_NIST_TRNG_EDU_ISTAT_RESEED_REMINDER) ++ ltpi_fan15: ltpi-pwm-fan15 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 15 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+/* ALARMS */ -+#define NIST_TRNG_REG_ALARM_ILLEGAL_CMD_SEQ BIT(4) -+#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_OK 0 -+#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_KAT_STAT 1 -+#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_KAT 2 -+#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_MONOBIT 3 -+#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_RUN 4 -+#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_LONGRUN 5 -+#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_AUTOCORRELATION 6 -+#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_POKER 7 -+#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_REPETITION_COUNT 8 -+#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_ADAPATIVE_PROPORTION 9 ++ ltpi0-iio-hwmon { ++ compatible = "iio-hwmon"; ++ io-channels = <<pi0_adc0 0>, <<pi0_adc0 1>, <<pi0_adc0 2>, <<pi0_adc0 3>, ++ <<pi0_adc0 4>, <<pi0_adc0 5>, <<pi0_adc0 6>, <<pi0_adc0 7>, ++ <<pi0_adc1 0>, <<pi0_adc1 1>, <<pi0_adc1 2>, <<pi0_adc1 3>, ++ <<pi0_adc1 4>, <<pi0_adc1 5>, <<pi0_adc1 6>, <<pi0_adc1 7>; ++ }; ++}; + -+/* COREKIT_REL */ -+#define NIST_TRNG_REG_EXT_ENUM(x) (((x) >> 28) & 0xF) -+#define NIST_TRNG_REG_EXT_VER(x) (((x) >> 23) & 0xFF) -+#define NIST_TRNG_REG_REL_NUM(x) ((x) & 0xFFFF) ++<pi0_pwm_tach { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_ltpi0_pwm0_default &pinctrl_ltpi0_pwm1_default ++ &pinctrl_ltpi0_pwm2_default &pinctrl_ltpi0_pwm3_default ++ &pinctrl_ltpi0_pwm4_default &pinctrl_ltpi0_pwm5_default ++ &pinctrl_ltpi0_pwm6_default &pinctrl_ltpi0_pwm7_default ++ &pinctrl_ltpi0_pwm8_default ++ &pinctrl_ltpi0_tach0_default &pinctrl_ltpi0_tach1_default ++ &pinctrl_ltpi0_tach2_default &pinctrl_ltpi0_tach3_default ++ &pinctrl_ltpi0_tach4_default &pinctrl_ltpi0_tach5_default ++ &pinctrl_ltpi0_tach6_default &pinctrl_ltpi0_tach7_default ++ &pinctrl_ltpi0_tach8_default &pinctrl_ltpi0_tach9_default ++ &pinctrl_ltpi0_tach10_default &pinctrl_ltpi0_tach11_default ++ &pinctrl_ltpi0_tach12_default &pinctrl_ltpi0_tach13_default ++ &pinctrl_ltpi0_tach14_default &pinctrl_ltpi0_tach15_default>; ++ ltpi_fan0 { ++ tach-ch = /bits/ 8 <0x0>; ++ }; ++ ltpi_fan1 { ++ tach-ch = /bits/ 8 <0x1>; ++ }; ++ ltpi_fan2 { ++ tach-ch = /bits/ 8 <0x2>; ++ }; ++ ltpi_fan3 { ++ tach-ch = /bits/ 8 <0x3>; ++ }; ++ ltpi_fan4 { ++ tach-ch = /bits/ 8 <0x4>; ++ }; ++ ltpi_fan5 { ++ tach-ch = /bits/ 8 <0x5>; ++ }; ++ ltpi_fan6 { ++ tach-ch = /bits/ 8 <0x6>; ++ }; ++ ltpi_fan7 { ++ tach-ch = /bits/ 8 <0x7>; ++ }; ++ ltpi_fan8 { ++ tach-ch = /bits/ 8 <0x8>; ++ }; ++ ltpi_fan9 { ++ tach-ch = /bits/ 8 <0x9>; ++ }; ++ ltpi_fan10 { ++ tach-ch = /bits/ 8 <0xA>; ++ }; ++ ltpi_fan11 { ++ tach-ch = /bits/ 8 <0xB>; ++ }; ++ ltpi_fan12 { ++ tach-ch = /bits/ 8 <0xC>; ++ }; ++ ltpi_fan13 { ++ tach-ch = /bits/ 8 <0xD>; ++ }; ++ ltpi_fan14 { ++ tach-ch = /bits/ 8 <0xE>; ++ }; ++ ltpi_fan15 { ++ tach-ch = /bits/ 8 <0xF>; ++ }; ++}; + -+// This will be deleted ?? per comments in hw details. ie use CFG -+/* FEATURES */ -+#define NIST_TRNG_REG_FEATURES_AES_256(x) (((x) >> 9) & 1) -+#define NIST_TRNG_REG_FEATURES_EXTRA_PS_PRESENT(x) (((x) >> 8) & 1) -+#define NIST_TRNG_REG_FEATURES_DIAG_LEVEL_NS(x) (((x) >> 7) & 1) -+#define NIST_TRNG_REG_FEATURES_DIAG_LEVEL_BASIC_TRNG(x) (((x) >> 4) & 7) -+#define NIST_TRNG_REG_FEATURES_DIAG_LEVEL_ST_HLT(x) (((x) >> 1) & 7) -+#define NIST_TRNG_REG_FEATURES_SECURE_RST_STATE(x) ((x) & 1) ++&sgpios { ++ /delete-property/ gpio-line-names; ++}; + -+/* build_CFG0 */ -+#define NIST_TRNG_REG_CFG0_PERSONILIZATION_STR(x) (((x) >> 14) & 1) -+#define NIST_TRNG_REG_CFG0_AES_MAX_KEY_SIZE(x) (((x) >> 13) & 1) -+#define NIST_TRNG_REG_CFG0_AES_DATAPATH(x) (((x) >> 12) & 1) -+#define NIST_TRNG_REG_CFG0_EDU_PRESENT(x) (((x) >> 11) & 1) -+#define NIST_TRNG_REG_CFG0_BACGROUND_NOISE(x) (((x) >> 10) & 1) -+#define NIST_TRNG_REG_CFG0_CDC_SYNCH_DEPTH(x) (((x) >> 8) & 3) -+#define NIST_TRNG_REG_CFG0_BG8(x) (((x) >> 7) & 1) -+#define NIST_TRNG_REG_CFG0_CORE_TYPE(x) ((x) & 3) ++<pi0_gpio { ++ /delete-property/ gpio-line-names; ++ /delete-node/ gpio_17; ++ /delete-node/ gpio_19; ++ /delete-node/ gpio_25; ++ /delete-node/ gpio_53; ++ /delete-node/ gpio_61; ++ /delete-node/ gpio_63; ++}; + -+/* build_CFG1 */ -+#define NIST_TRNG_REG_CFG1_ENT_SRC_REP_MIN_ENTROPY(x) (((x) >> 24) & 255) -+#define NIST_TRNG_REG_CFG1_ENT_SRC_REP_TEST(x) (((x) >> 23) & 1) -+#define NIST_TRNG_REG_CFG1_ENT_SRC_REP_SMPL_SIZE(x) (((x) >> 20) & 7) -+#define NIST_TRNG_REG_CFG1_RAW_HT_REP_TEST(x) (((x) >> 19) & 1) -+#define NIST_TRNG_REG_CFG1_RAW_HT_ADAP_TEST(x) (((x) >> 16) & 7) -+#define NIST_TRNG_REG_CFG1_POKER_TEST(x) (((x) >> 15) & 1) -+#define NIST_TRNG_REG_CFG1_RUN_TEST(x) (((x) >> 14) & 1) -+#define NIST_TRNG_REG_CFG1_MONO_BIT_TEST(x) (((x) >> 13) & 1) -+#define NIST_TRNG_REG_CFG1_AUTO_CORRELATION_TEST(x) (((x) >> 12) & 1) -+#define NIST_TRNG_REG_CFG1_STICKY_STARTUP(x) (((x) >> 8) & 1) -+#define NIST_TRNG_REG_CFG1_NUM_RAW_NOISE_BLKS(x) ((x) & 255) ++<pi0_adc0 { ++ aspeed,int-vref-microvolt = <2500000>; ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_ltpi0_adc0_default &pinctrl_ltpi0_adc1_default ++ &pinctrl_ltpi0_adc2_default &pinctrl_ltpi0_adc3_default ++ &pinctrl_ltpi0_adc4_default &pinctrl_ltpi0_adc5_default ++ &pinctrl_ltpi0_adc6_default &pinctrl_ltpi0_adc7_default>; ++}; + -+/* EDU_BUILD_CFG0 */ -+#define NIST_TRNG_REG_EDU_CFG0_RBC2_RATE_WIDTH(x) (((x) >> 20) & 7) -+#define NIST_TRNG_REG_EDU_CFG0_RBC1_RATE_WIDTH(x) (((x) >> 16) & 7) -+#define NIST_TRNG_REG_EDU_CFG0_RBC0_RATE_WIDTH(x) (((x) >> 12) & 7) -+#define NIST_TRNG_REG_EDU_CFG0_PUBLIC_VTRNG_CHANNELS(x) (((x) >> 8) & 15) -+#define NIST_TRNG_REG_EDU_CFG0_ESM_CHANNEL(x) (((x) >> 6) & 1) -+#define NIST_TRNG_REG_EDU_CFG0_RBC_CHANNELS(x) (((x) >> 4) & 3) -+#define NIST_TRNG_REG_EDU_CFG0_FIFO_DEPTH(x) (((x) >> 2) & 7) ++<pi0_adc1 { ++ aspeed,int-vref-microvolt = <2500000>; ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_ltpi0_adc8_default &pinctrl_ltpi0_adc9_default ++ &pinctrl_ltpi0_adc10_default &pinctrl_ltpi0_adc11_default ++ &pinctrl_ltpi0_adc12_default &pinctrl_ltpi0_adc13_default ++ &pinctrl_ltpi0_adc14_default &pinctrl_ltpi0_adc15_default>; ++}; + -+/* EDU_VSTAT */ -+#define NIST_TRNG_REG_EDU_VSTAT_BUSY(x) (((x) >> 31) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_RNC_ENABLED(x) (((x) >> 30) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_SEED_ENUM(x) (((x) >> 28) & 3) -+#define NIST_TRNG_REG_EDU_VSTAT_RWUE(x) (((x) >> 27) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_RWNE(x) (((x) >> 26) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_SRWE(x) (((x) >> 25) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_ANY_RW1(x) (((x) >> 24) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_BCKGRND_NOISE(x) (((x) >> 23) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_RNC_FIFO_EMPTY(x) (((x) >> 22) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_SLICE_RWI3(x) (((x) >> 15) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_SLICE_RWI2(x) (((x) >> 14) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_SLICE_RWI1(x) (((x) >> 13) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_SLICE_RWI0(x) (((x) >> 12) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD3(x) (((x) >> 11) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD2(x) (((x) >> 10) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD1(x) (((x) >> 9) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD0(x) (((x) >> 8) & 1) -+#define NIST_TRNG_REG_EDU_VSTAT_CURRENT_CMD(x) (((x) >> 4) & 15) -+#define NIST_TRNG_REG_EDU_VSTAT_LAST_CMD(x) ((x) & 15) ++<pi0 { ++ i2c-tunneling = <0x0>; ++ status = "okay"; ++}; + -+#define _NIST_TRNG_REG_SMODE_MAX_REJECTS_MASK 255UL -+#define _NIST_TRNG_REG_SMODE_SECURE_EN_MASK 1UL -+#define _NIST_TRNG_REG_SMODE_NONCE_MASK 1UL -+#define _NIST_TRNG_REG_MODE_SEC_ALG_MASK 1UL -+#define _NIST_TRNG_REG_MODE_ADDIN_PRESENT_MASK 1UL -+#define _NIST_TRNG_REG_MODE_PRED_RESIST_MASK 1UL -+#define _NIST_TRNG_REG_MODE_KAT_SEL_MASK 3UL -+#define _NIST_TRNG_REG_MODE_KAT_VEC_MASK 3UL -+#define _NIST_TRNG_REG_STAT_DRBG_STATE_MASK 3UL -+#define _NIST_TRNG_REG_STAT_SECURE_MASK 1UL -+#define _NIST_TRNG_REG_STAT_NONCE_MASK 1UL ++<pi0_i3c0 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06010000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+#define NIST_TRNG_REG_SMODE_SET_MAX_REJECTS(y, x) (((y) & ~(_NIST_TRNG_REG_SMODE_MAX_REJECTS_MASK << _NIST_TRNG_REG_SMODE_MAX_REJECTS)) | ((x) << _NIST_TRNG_REG_SMODE_MAX_REJECTS)) -+#define NIST_TRNG_REG_SMODE_SET_SECURE_EN(y, x) (((y) & ~(_NIST_TRNG_REG_SMODE_SECURE_EN_MASK << _NIST_TRNG_REG_SMODE_SECURE_EN)) | ((x) << _NIST_TRNG_REG_SMODE_SECURE_EN)) -+#define NIST_TRNG_REG_SMODE_SET_NONCE(y, x) (((y) & ~(_NIST_TRNG_REG_SMODE_NONCE_MASK << _NIST_TRNG_REG_SMODE_NONCE)) | ((x) << _NIST_TRNG_REG_SMODE_NONCE)) -+#define NIST_TRNG_REG_SMODE_GET_MAX_REJECTS(x) (((x) >> _NIST_TRNG_REG_SMODE_MAX_REJECTS) & _NIST_TRNG_REG_SMODE_MAX_REJECTS_MASK) -+#define NIST_TRNG_REG_SMODE_GET_SECURE_EN(x) (((x) >> _NIST_TRNG_REG_SMODE_SECURE_EN) & _NIST_TRNG_REG_SMODE_SECURE_EN_MASK) -+#define NIST_TRNG_REG_SMODE_GET_NONCE(x) (((x) >> _NIST_TRNG_REG_SMODE_NONCE) & _NIST_TRNG_REG_SMODE_NONCE_MASK) ++<pi0_i3c1 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+#define NIST_TRNG_REG_MODE_SET_SEC_ALG(y, x) (((y) & ~(_NIST_TRNG_REG_MODE_SEC_ALG_MASK << _NIST_TRNG_REG_MODE_SEC_ALG)) | ((x) << _NIST_TRNG_REG_MODE_SEC_ALG)) -+#define NIST_TRNG_REG_MODE_SET_PRED_RESIST(y, x) (((y) & ~(_NIST_TRNG_REG_MODE_PRED_RESIST_MASK << _NIST_TRNG_REG_MODE_PRED_RESIST)) | ((x) << _NIST_TRNG_REG_MODE_PRED_RESIST)) -+#define NIST_TRNG_REG_MODE_SET_ADDIN_PRESENT(y, x) (((y) & ~(_NIST_TRNG_REG_MODE_ADDIN_PRESENT_MASK << _NIST_TRNG_REG_MODE_ADDIN_PRESENT)) | ((x) << _NIST_TRNG_REG_MODE_ADDIN_PRESENT)) -+#define NIST_TRNG_REG_MODE_SET_KAT_SEL(y, x) (((y) & ~(_NIST_TRNG_REG_MODE_KAT_SEL_MASK << _NIST_TRNG_REG_MODE_KAT_SEL)) | ((x) << _NIST_TRNG_REG_MODE_KAT_SEL)) -+#define NIST_TRNG_REG_MODE_SET_KAT_VEC(y, x) (((y) & ~(_NIST_TRNG_REG_MODE_KAT_VEC_MASK << _NIST_TRNG_REG_MODE_KAT_VEC)) | ((x) << _NIST_TRNG_REG_MODE_KAT_VEC)) -+#define NIST_TRNG_REG_MODE_GET_SEC_ALG(x) (((x) >> _NIST_TRNG_REG_MODE_SEC_ALG) & _NIST_TRNG_REG_MODE_SEC_ALG_MASK) -+#define NIST_TRNG_REG_MODE_GET_PRED_RESIST(x) (((x) >> _NIST_TRNG_REG_MODE_PRED_RESIST) & _NIST_TRNG_REG_MODE_PRED_RESIST_MASK) -+#define NIST_TRNG_REG_MODE_GET_ADDIN_PRESENT(x) (((x) >> _NIST_TRNG_REG_MODE_ADDIN_PRESENT) & _NIST_TRNG_REG_MODE_ADDIN_PRESENT_MASK) -+#define NIST_TRNG_REG_STAT_GET_DRBG_STATE(x) (((x) >> _NIST_TRNG_REG_STAT_DRBG_STATE) & _NIST_TRNG_REG_STAT_DRBG_STATE_MASK) -+#define NIST_TRNG_REG_STAT_GET_SECURE(x) (((x) >> _NIST_TRNG_REG_STAT_SECURE) & _NIST_TRNG_REG_STAT_SECURE_MASK) -+#define NIST_TRNG_REG_STAT_GET_NONCE(x) (((x) >> _NIST_TRNG_REG_STAT_NONCE_MODE) & _NIST_TRNG_REG_STAT_NONCE_MASK) ++<pi0_i3c2 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06012000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+#endif -diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_private.h b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_private.h ---- a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_private.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_private.h 2025-12-23 10:16:19.524059486 +0000 -@@ -0,0 +1,89 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * This Synopsys software and associated documentation (hereinafter the -+ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless -+ * otherwise expressly agreed to in writing between Synopsys and you. The -+ * Software IS NOT an item of Licensed Software or a Licensed Product under -+ * any End User Software License Agreement or Agreement for Licensed Products -+ * with Synopsys or any supplement thereto. Synopsys is a registered trademark -+ * of Synopsys, Inc. Other names included in the SOFTWARE may be the -+ * trademarks of their respective owners. -+ * -+ * The contents of this file are dual-licensed; you may select either version -+ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license -+ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the -+ * SOFTWARE. The BSD License is copied below. -+ * -+ * BSD-3-Clause License: -+ * Copyright (c) 2012-2016 Synopsys, Inc. and/or its affiliates. -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright notice, -+ * this list of conditions, and the following disclaimer, without -+ * modification. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * 3. The names of the above-listed copyright holders may not be used to -+ * endorse or promote products derived from this software without specific -+ * prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -+ * POSSIBILITY OF SUCH DAMAGE. -+ */ ++<pi0_i3c3 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+#ifndef NISTTRNG_PRIVATE_H -+#define NISTTRNG_PRIVATE_H ++<pi0_i3c4 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06014000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+#include "elppdu.h" -+#include "nisttrng_hw.h" -+#include "nisttrng_common.h" ++<pi0_i3c5 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+int nisttrng_wait_on_busy(struct nist_trng_state *state); -+int nisttrng_wait_on_done(struct nist_trng_state *state); -+int nisttrng_wait_on_noise_rdy(struct nist_trng_state *state); -+int nisttrng_get_alarms(struct nist_trng_state *state); -+int nisttrng_reset_state(struct nist_trng_state *state); ++<pi0_i3c6 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06016000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+/* ---------- Reminders ---------- */ -+int nisttrng_reset_counters(struct nist_trng_state *state); -+int nisttrng_set_reminder_max_bits_per_req(struct nist_trng_state *state, unsigned long max_bits_per_req); -+int nisttrng_set_reminder_max_req_per_seed(struct nist_trng_state *state, unsigned long long max_req_per_seed); -+int nisttrng_check_seed_lifetime(struct nist_trng_state *state); ++<pi0_i3c7 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+/* ---------- Set field APIs ---------- */ -+int nisttrng_set_sec_strength(struct nist_trng_state *state, int req_sec_strength); -+int nisttrng_set_addin_present(struct nist_trng_state *state, int addin_present); -+int nisttrng_set_pred_resist(struct nist_trng_state *state, int pred_resist); -+int nisttrng_set_secure_mode(struct nist_trng_state *state, int secure_mode); -+int nisttrng_set_nonce_mode(struct nist_trng_state *state, int nonce_mode); ++<pi0_i3c8 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06018000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+/* ---------- Load data APIs ---------- */ -+int nisttrng_load_ps_addin(struct nist_trng_state *state, void *input_str); ++<pi0_i3c9 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+/* ---------- Command APIs ---------- */ -+int nisttrng_get_entropy_input(struct nist_trng_state *state, void *input_nonce, int nonce_operation); -+int nisttrng_refresh_addin(struct nist_trng_state *state, void *addin_str); -+int nisttrng_gen_random(struct nist_trng_state *state, void *random_bits, unsigned long req_num_bytes); -+int nisttrng_advance_state(struct nist_trng_state *state); -+int nisttrng_kat(struct nist_trng_state *state, int kat_sel, int kat_vec); -+int nisttrng_full_kat(struct nist_trng_state *state); -+int nisttrng_zeroize(struct nist_trng_state *state); ++<pi0_i3c10 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601A000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+/* ---------- edu related ---------- */ ++<pi0_i3c11 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+int nisttrng_rnc(struct nist_trng_state *state, int rnc_ctrl_cmd); -+int nisttrng_wait_fifo_full(struct nist_trng_state *state); -+#endif -diff --git a/drivers/char/hw_random/dwc/src/trng/include/synversion.h b/drivers/char/hw_random/dwc/src/trng/include/synversion.h ---- a/drivers/char/hw_random/dwc/src/trng/include/synversion.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/include/synversion.h 2025-12-23 10:16:19.524059486 +0000 -@@ -0,0 +1,52 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * This Synopsys software and associated documentation (hereinafter the -+ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless -+ * otherwise expressly agreed to in writing between Synopsys and you. The -+ * Software IS NOT an item of Licensed Software or a Licensed Product under -+ * any End User Software License Agreement or Agreement for Licensed Products -+ * with Synopsys or any supplement thereto. Synopsys is a registered trademark -+ * of Synopsys, Inc. Other names included in the SOFTWARE may be the -+ * trademarks of their respective owners. -+ * -+ * The contents of this file are dual-licensed; you may select either version -+ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license -+ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the -+ * SOFTWARE. The BSD License is copied below. -+ * -+ * BSD-3-Clause License: -+ * Copyright (c) 2012-2016 Synopsys, Inc. and/or its affiliates. -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright notice, -+ * this list of conditions, and the following disclaimer, without -+ * modification. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * 3. The names of the above-listed copyright holders may not be used to -+ * endorse or promote products derived from this software without specific -+ * prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -+ * POSSIBILITY OF SUCH DAMAGE. -+ */ ++<pi0_i3c12 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601C000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+#ifndef VERSION_H -+#define VERSION_H ++<pi0_i3c13 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+#define TRNG_VERSION "1.00a" ++<pi0_i3c14 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601E000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+#endif -diff --git a/drivers/char/hw_random/dwc/src/trng/kernel/nist_trng.c b/drivers/char/hw_random/dwc/src/trng/kernel/nist_trng.c ---- a/drivers/char/hw_random/dwc/src/trng/kernel/nist_trng.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/kernel/nist_trng.c 2025-12-23 10:16:19.524059486 +0000 -@@ -0,0 +1,2170 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * This Synopsys software and associated documentation (hereinafter the -+ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless -+ * otherwise expressly agreed to in writing between Synopsys and you. The -+ * Software IS NOT an item of Licensed Software or a Licensed Product under -+ * any End User Software License Agreement or Agreement for Licensed Products -+ * with Synopsys or any supplement thereto. Synopsys is a registered trademark -+ * of Synopsys, Inc. Other names included in the SOFTWARE may be the -+ * trademarks of their respective owners. -+ * -+ * The contents of this file are dual-licensed; you may select either version -+ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license -+ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the -+ * SOFTWARE. The BSD License is copied below. -+ * -+ * BSD-3-Clause License: -+ * Copyright (c) 2012-2017 Synopsys, Inc. and/or its affiliates. -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright notice, -+ * this list of conditions, and the following disclaimer, without -+ * modification. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * 3. The names of the above-listed copyright holders may not be used to -+ * endorse or promote products derived from this software without specific -+ * prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -+ * POSSIBILITY OF SUCH DAMAGE. -+ */ ++<pi0_i3c15 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++&i2c0 { ++ status = "disabled"; ++}; + -+#include -+#include ++&i2c1 { ++ status = "disabled"; ++}; + -+#include "nisttrng.h" ++&i2c2 { ++ status = "disabled"; ++}; + -+#define SYNOPSYS_HWRNG_DRIVER_NAME "hwrng-nist_trng" ++&i2c3 { ++ status = "disabled"; ++}; + -+#define num_gen_bytes 64 -+static unsigned long max_reads = 128; ++&i2c4 { ++ status = "disabled"; ++}; + -+struct synopsys_nisttrng_driver { -+ struct nist_trng_state nisttrng; -+ void *hwrng_drv; -+ void *crypto_drv; -+ unsigned char rand_out[num_gen_bytes]; ++&i2c5 { ++ status = "disabled"; +}; + -+static unsigned int xxd_vtrng; ++<pi0_i2c0 { ++ status = "okay"; ++}; + -+static void nisttrng_reinit(struct nist_trng_state *nist_trng) -+{ -+ int err; ++<pi0_i2c1 { ++ status = "okay"; ++}; + -+ err = nisttrng_uninstantiate(nist_trng); -+ if (err && err != CRYPTO_NOT_INSTANTIATED) -+ goto ERR; ++<pi0_i2c2 { ++ status = "okay"; ++}; + -+ err = nisttrng_instantiate(nist_trng, 128, 1, NULL); -+ if (err) -+ goto ERR; ++<pi0_i2c3 { ++ status = "okay"; ++}; + -+ERR: -+ DEBUG("NIST_TRNG: Trying to reinitialize after a fatal alarm: %d\n", -+ err); -+} ++<pi0_i2c4 { ++ status = "okay"; ++}; + -+static int nisttrng_platform_driver_read(struct platform_device *pdev, -+ void *buf, size_t max, bool wait) -+{ -+ struct synopsys_nisttrng_driver *data = 0; -+ int nisttrng_error = -1; -+ u32 *out = kmalloc(max, GFP_KERNEL); -+ unsigned int vtrng; ++<pi0_i2c5 { ++ status = "okay"; ++}; + -+ if (!out) { -+ SYNHW_PRINT("memory not allocated\n"); -+ return -1; -+ } ++<pi0_i2c6 { ++ status = "okay"; ++}; + -+ if (!pdev || !buf || !max) -+ return nisttrng_error; ++<pi0_i2c7 { ++ status = "okay"; ++}; + -+ data = platform_get_drvdata(pdev); -+ if (data == 0) -+ return nisttrng_error; ++<pi0_i2c8 { ++ status = "okay"; ++}; + -+ if (data->nisttrng.config.build_cfg0.edu_present) { -+ vtrng = xxd_vtrng % ((data->nisttrng.config.edu_build_cfg0 -+ .public_vtrng_channels) + -+ 1); -+ if (vtrng == 0) { -+ /* private vtrng */ -+ nisttrng_error = nisttrng_generate(&data->nisttrng, out, max, -+ data->nisttrng.status.sec_strength ? 256 : 128, -+ data->nisttrng.status.pred_resist, NULL); -+ } else { -+ /* public vtrng */ -+ nisttrng_error = nisttrng_generate_public_vtrng(&data->nisttrng, out, max, vtrng - 1); -+ } -+ xxd_vtrng++; -+ } else { -+ /* nist core vtrng */ -+ nisttrng_error = nisttrng_generate(&data->nisttrng, out, max, -+ data->nisttrng.status.sec_strength ? 256 : 128, -+ data->nisttrng.status.pred_resist, NULL); -+ } -+ if (nisttrng_error < 0) { -+ if (data->nisttrng.status.alarm_code) -+ nisttrng_reinit(&data->nisttrng); ++<pi0_i2c9 { ++ status = "okay"; ++}; + -+ return nisttrng_error; -+ } ++<pi0_i2c10 { ++ status = "okay"; ++}; + -+ memcpy(buf, out, max); -+ kfree(out); ++<pi0_i2c11 { ++ status = "okay"; ++}; + -+ return max; -+} ++<pi0_i2c12 { ++ status = "okay"; ++}; + -+static int nisttrng_hwrng_driver_read(struct hwrng *rng, void *buf, size_t max, -+ bool wait) -+{ -+ struct platform_device *pdev = 0; ++<pi0_i2c13 { ++ status = "okay"; ++}; + -+ if (rng == 0) -+ return -1; ++<pi0_i2c14 { ++ status = "okay"; ++}; + -+ pdev = (struct platform_device *)rng->priv; -+ return nisttrng_platform_driver_read(pdev, buf, max, wait); -+} ++<pi0_i2c15 { ++ status = "okay"; ++}; + -+static ssize_t ckr_show(struct device *dev, struct device_attribute *devattr, -+ char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++&uart3 { ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1800-evb.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1800-evb.dts +--- a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1800-evb.dts 1970-01-01 00:00:00.000000000 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1800-evb.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,436 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later + -+ return sprintf(buf, "rel_num=%u, ext_ver=%u, ext_enum=%u\n", -+ priv->nisttrng.config.corekit_rel.rel_num, -+ priv->nisttrng.config.corekit_rel.ext_ver, -+ priv->nisttrng.config.corekit_rel.ext_enum); -+} ++/dts-v1/; + -+static ssize_t features_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++#include "ast2700a1-dcscm.dts" ++#include "aspeed-ltpi1800.dtsi" + -+ return sprintf(buf, -+ "drbg_arch = %u, diag_basic_trng=%u, diag_st_hlt=%u, diag_ns=%u, secure_rst_state=%u, extra_ps_present=%u\n", -+ priv->nisttrng.config.features.drbg_arch, -+ priv->nisttrng.config.features.diag_level_basic_trng, -+ priv->nisttrng.config.features.diag_level_stat_hlt, -+ priv->nisttrng.config.features.diag_level_ns, -+ priv->nisttrng.config.features.secure_rst_state, -+ priv->nisttrng.config.features.extra_ps_present); -+} ++/ { ++ model = "AST2700A1-DCSCM_AST1800-EVB"; + -+static ssize_t secure_show(struct device *dev, struct device_attribute *devattr, -+ char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ltpi_fan0: ltpi-pwm-fan0 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 0 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ return sprintf(buf, "%s\n", NIST_TRNG_REG_SMODE_GET_SECURE_EN(pdu_io_read32(priv->nisttrng.base + -+ NIST_TRNG_REG_SMODE)) ? "on" : "off"); -+} ++ ltpi_fan1: ltpi-pwm-fan1 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 1 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+static ssize_t secure_store(struct device *dev, -+ struct device_attribute *devattr, const char *buf, -+ size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ int ret; ++ ltpi_fan2: ltpi-pwm-fan2 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 2 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ ret = nisttrng_set_secure_mode(&priv->nisttrng, -+ sysfs_streq(buf, "on") ? 1 : 0); -+ if (ret) -+ return -1; ++ ltpi_fan3: ltpi-pwm-fan3 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 3 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ return count; -+} ++ ltpi_fan4: ltpi-pwm-fan4 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 4 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+static ssize_t nonce_show(struct device *dev, struct device_attribute *devattr, -+ char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ltpi_fan5: ltpi-pwm-fan5 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 5 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ return sprintf(buf, "%s\n", NIST_TRNG_REG_SMODE_GET_NONCE(pdu_io_read32(priv->nisttrng.base + -+ NIST_TRNG_REG_SMODE)) ? "on" : "off"); -+} ++ ltpi_fan6: ltpi-pwm-fan6 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 6 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+static ssize_t nonce_store(struct device *dev, struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ int ret; ++ ltpi_fan7: ltpi-pwm-fan7 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 7 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ ret = nisttrng_set_nonce_mode(&priv->nisttrng, -+ sysfs_streq(buf, "on") ? 1 : 0); -+ if (ret) -+ return -1; ++ ltpi_fan8: ltpi-pwm-fan8 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 8 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ return count; -+} ++ ltpi_fan9: ltpi-pwm-fan9 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 9 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+static ssize_t sec_strength_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ltpi_fan10: ltpi-pwm-fan10 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 10 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ return sprintf(buf, "%s\n", -+ priv->nisttrng.status.sec_strength ? "256" : "128"); -+} ++ ltpi_fan11: ltpi-pwm-fan11 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 11 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+static ssize_t sec_strength_store(struct device *dev, -+ struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[9]; -+ int tmp; -+ int ret; ++ ltpi_fan12: ltpi-pwm-fan12 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 12 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ if (count > 8) -+ return -1; ++ ltpi_fan13: ltpi-pwm-fan13 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 13 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ foo[8] = 0; -+ memcpy(foo, buf, 8); -+ ret = kstrtoint(foo, 10, &tmp); -+ if (ret) -+ return ret; ++ ltpi_fan14: ltpi-pwm-fan14 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 14 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ ret = nisttrng_set_sec_strength(&priv->nisttrng, tmp); -+ if (ret) -+ return -1; ++ ltpi_fan15: ltpi-pwm-fan15 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 15 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ return count; -+} ++ ltpi0-iio-hwmon { ++ compatible = "iio-hwmon"; ++ io-channels = <<pi0_adc0 0>, <<pi0_adc0 1>, <<pi0_adc0 2>, <<pi0_adc0 3>, ++ <<pi0_adc0 4>, <<pi0_adc0 5>, <<pi0_adc0 6>, <<pi0_adc0 7>, ++ <<pi0_adc1 0>, <<pi0_adc1 1>, <<pi0_adc1 2>, <<pi0_adc1 3>, ++ <<pi0_adc1 4>, <<pi0_adc1 5>, <<pi0_adc1 6>, <<pi0_adc1 7>; ++ }; ++}; + -+static ssize_t rand_reg_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ unsigned int x; -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++<pi0_jtag { ++ status = "okay"; ++}; + -+ for (x = 0; x < 4; x++) { -+ sprintf(buf + 8 * x, "%08lx", -+ pdu_io_read32(priv->nisttrng.base + -+ NIST_TRNG_REG_RAND0 + 3 - x)); -+ } ++<pi0_pwm_tach { ++ status = "okay"; ++ ltpi_fan0 { ++ tach-ch = /bits/ 8 <0x0>; ++ }; ++ ltpi_fan1 { ++ tach-ch = /bits/ 8 <0x1>; ++ }; ++ ltpi_fan2 { ++ tach-ch = /bits/ 8 <0x2>; ++ }; ++ ltpi_fan3 { ++ tach-ch = /bits/ 8 <0x3>; ++ }; ++ ltpi_fan4 { ++ tach-ch = /bits/ 8 <0x4>; ++ }; ++ ltpi_fan5 { ++ tach-ch = /bits/ 8 <0x5>; ++ }; ++ ltpi_fan6 { ++ tach-ch = /bits/ 8 <0x6>; ++ }; ++ ltpi_fan7 { ++ tach-ch = /bits/ 8 <0x7>; ++ }; ++ ltpi_fan8 { ++ tach-ch = /bits/ 8 <0x8>; ++ }; ++ ltpi_fan9 { ++ tach-ch = /bits/ 8 <0x9>; ++ }; ++ ltpi_fan10 { ++ tach-ch = /bits/ 8 <0xA>; ++ }; ++ ltpi_fan11 { ++ tach-ch = /bits/ 8 <0xB>; ++ }; ++ ltpi_fan12 { ++ tach-ch = /bits/ 8 <0xC>; ++ }; ++ ltpi_fan13 { ++ tach-ch = /bits/ 8 <0xD>; ++ }; ++ ltpi_fan14 { ++ tach-ch = /bits/ 8 <0xE>; ++ }; ++ ltpi_fan15 { ++ tach-ch = /bits/ 8 <0xF>; ++ }; ++}; + -+ strcat(buf, "\n"); -+ return strlen(buf); -+} ++&sgpios { ++ /delete-property/ gpio-line-names; ++}; + -+static ssize_t seed_reg_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ unsigned int x; -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++<pi0_gpio { ++ /delete-property/ gpio-line-names; ++ /delete-node/ gpio_17; ++ /delete-node/ gpio_19; ++ /delete-node/ gpio_25; ++ /delete-node/ gpio_53; ++ /delete-node/ gpio_61; ++ /delete-node/ gpio_63; ++}; + -+ for (x = 0; x < 12; x++) { -+ sprintf(buf + 8 * x, "%08lx", -+ pdu_io_read32(priv->nisttrng.base + -+ NIST_TRNG_REG_SEED0 + 11 - x)); -+ } -+ strcat(buf, "\n"); -+ return strlen(buf); -+} ++<pi0_adc0 { ++ aspeed,int-vref-microvolt = <2500000>; ++ status = "okay"; ++}; + -+static ssize_t seed_reg_store(struct device *dev, -+ struct device_attribute *devattr, const char *buf, -+ size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[9]; -+ unsigned int x, tmp; -+ int ret; ++<pi0_adc1 { ++ aspeed,int-vref-microvolt = <2500000>; ++ status = "okay"; ++}; + -+ // string must be at least 12 32-bit words long in 0 padded hex -+ if (count < (2 * 12 * 4)) -+ return -1; ++<pi0 { ++ i2c-tunneling = <0x0>; ++}; + -+ foo[8] = 0; -+ for (x = 0; x < 12; x++) { -+ memcpy(foo, buf + x * 8, 8); -+ ret = kstrtouint(foo, 16, &tmp); -+ if (ret) -+ return ret; ++<pi0_i3c0 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06010000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_SEED0 + x, -+ tmp); -+ } ++<pi0_i3c1 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ return count; -+} ++<pi0_i3c2 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06012000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+static ssize_t npa_data_reg_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ unsigned int x; -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++<pi0_i3c3 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ for (x = 0; x < 16; x++) { -+ sprintf(buf + 8 * x, "%08lx", -+ pdu_io_read32(priv->nisttrng.base + -+ NIST_TRNG_REG_NPA_DATA0 + 15 - x)); -+ } ++<pi0_i3c4 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06014000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ strcat(buf, "\n"); -+ return strlen(buf); -+} ++<pi0_i3c5 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+static ssize_t npa_data_reg_store(struct device *dev, -+ struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[9]; -+ unsigned int x, tmp; -+ int ret; ++<pi0_i3c6 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06016000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ // string must be at least 16 32-bit words long in 0 padded hex -+ if (count < (2 * 16 * 4)) -+ return -1; ++<pi0_i3c7 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ foo[8] = 0; -+ for (x = 0; x < 16; x++) { -+ memcpy(foo, buf + x * 8, 8); -+ ret = kstrtouint(foo, 16, &tmp); -+ if (ret) -+ return ret; ++<pi0_i3c8 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06018000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_NPA_DATA0 + x, tmp); -+ } ++<pi0_i3c9 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ return count; -+} ++<pi0_i3c10 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601A000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+static ssize_t ctrl_reg_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++<pi0_i3c11 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ return sprintf(buf, "%08lx\n", -+ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_CTRL)); -+} ++<pi0_i3c12 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601C000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+static ssize_t ctrl_reg_store(struct device *dev, -+ struct device_attribute *devattr, const char *buf, -+ size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[9]; -+ unsigned int tmp; -+ int ret; ++<pi0_i3c13 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ // string must be at least a 32-bit word in 0 padded hex -+ if (count < 8) -+ return -1; ++<pi0_i3c14 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601E000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ foo[8] = 0; -+ memcpy(foo, buf, 8); -+ ret = kstrtouint(foo, 16, &tmp); -+ if (ret) -+ return ret; ++<pi0_i3c15 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_CTRL, tmp); -+ return count; -+} ++&i2c0 { ++ status = "disabled"; ++}; + -+static ssize_t istat_reg_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%08lx\n", -+ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_ISTAT)); -+} -+ -+static ssize_t istat_reg_store(struct device *dev, -+ struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[9]; -+ unsigned int tmp; -+ int ret; ++&i2c1 { ++ status = "disabled"; ++}; + -+ // string must be at least a 32-bit word in 0 padded hex -+ if (count < 8) -+ return -1; ++&i2c2 { ++ status = "disabled"; ++}; + -+ foo[8] = 0; -+ memcpy(foo, buf, 8); -+ ret = kstrtouint(foo, 16, &tmp); -+ if (ret) -+ return ret; ++&i2c3 { ++ status = "disabled"; ++}; + -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_ISTAT, tmp); -+ return count; -+} ++&i2c4 { ++ status = "disabled"; ++}; + -+static ssize_t mode_reg_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++&i2c5 { ++ status = "disabled"; ++}; + -+ return sprintf(buf, "%08lx\n", -+ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_MODE)); -+} ++<pi0_i2c0 { ++ status = "okay"; ++}; + -+static ssize_t mode_reg_store(struct device *dev, -+ struct device_attribute *devattr, const char *buf, -+ size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[9]; -+ unsigned int tmp; -+ int ret; ++<pi0_i2c1 { ++ status = "okay"; ++}; + -+ // string must be at least a 32-bit word in 0 padded hex -+ if (count < 8) -+ return -1; ++<pi0_i2c2 { ++ status = "okay"; ++}; + -+ foo[8] = 0; -+ memcpy(foo, buf, 8); -+ ret = kstrtouint(foo, 16, &tmp); -+ if (ret) -+ return ret; ++<pi0_i2c3 { ++ status = "okay"; ++}; + -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_MODE, tmp); ++<pi0_i2c4 { ++ status = "okay"; ++}; + -+ return count; -+} ++<pi0_i2c5 { ++ status = "okay"; ++}; + -+static ssize_t smode_reg_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++<pi0_i2c6 { ++ status = "okay"; ++}; + -+ return sprintf(buf, "%08lx\n", -+ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_SMODE)); -+} ++<pi0_i2c7 { ++ status = "okay"; ++}; + -+static ssize_t smode_reg_store(struct device *dev, -+ struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[9]; -+ unsigned int tmp; -+ int ret; ++<pi0_i2c8 { ++ status = "okay"; ++}; + -+ // string must be at least a 32-bit word in 0 padded hex -+ if (count < 8) -+ return -1; ++<pi0_i2c9 { ++ status = "okay"; ++}; + -+ foo[8] = 0; -+ memcpy(foo, buf, 8); -+ ret = kstrtouint(foo, 16, &tmp); -+ if (ret) -+ return ret; ++<pi0_i2c10 { ++ status = "okay"; ++}; + -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_SMODE, tmp); -+ return count; -+} ++<pi0_i2c11 { ++ status = "okay"; ++}; + -+static ssize_t alarm_reg_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++<pi0_i2c12 { ++ status = "okay"; ++}; + -+ return sprintf(buf, "%08lx\n", -+ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_ALARM)); -+} ++<pi0_i2c13 { ++ status = "okay"; ++}; + -+static ssize_t alarm_reg_store(struct device *dev, -+ struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[9]; -+ unsigned int tmp; -+ int ret; ++<pi0_i2c14 { ++ status = "okay"; ++}; + -+ // string must be at least a 32-bit word in 0 padded hex -+ if (count < 8) -+ return -1; ++<pi0_i2c15 { ++ status = "okay"; ++}; + -+ foo[8] = 0; -+ memcpy(foo, buf, 8); -+ ret = kstrtouint(foo, 16, &tmp); -+ if (ret) -+ return ret; ++&uart3 { ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-256-abr.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-evb-256-abr.dts +--- a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-256-abr.dts 1970-01-01 00:00:00.000000000 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-evb-256-abr.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,40 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later + -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_ALARM, tmp); -+ return count; -+} ++/dts-v1/; + -+static ssize_t stat_reg_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++#include "ast2700a1-evb.dts" + -+ return sprintf(buf, "%08lx\n", -+ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_STAT)); -+} ++&fmc { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_fwspi_quad_default>; ++ pinctrl-names = "default"; + -+static ssize_t ia_wdata_reg_store(struct device *dev, -+ struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[9]; -+ unsigned int tmp; -+ int ret; ++ flash@0 { ++ status = "okay"; ++ m25p,fast-read; ++ label = "bmc"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <4>; ++ spi-rx-bus-width = <4>; ++#include "aspeed-evb-flash-layout-256-abr.dtsi" ++ }; + -+ // string must be at least a 32-bit word in 0 padded hex -+ if (count < 8) -+ return -1; ++ flash@1 { ++ status = "okay"; ++ m25p,fast-read; ++ label = "fmc0:1"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <4>; ++ spi-rx-bus-width = <4>; ++ }; + -+ foo[8] = 0; -+ memcpy(foo, buf, 8); -+ ret = kstrtouint(foo, 16, &tmp); -+ if (ret) -+ return ret; ++ flash@2 { ++ status = "disabled"; ++ m25p,fast-read; ++ label = "fmc0:2"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <4>; ++ spi-rx-bus-width = <4>; ++ }; ++}; + -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_WDATA, tmp); -+ return count; -+} +diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts +--- a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts 1970-01-01 00:00:00.000000000 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,367 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later + -+static ssize_t ia_wdata_reg_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++#include "ast2700a1-evb.dts" ++#include + -+ return sprintf(buf, "%08lx\n", -+ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_IA_WDATA)); -+} ++/ { ++ model = "AST2700A1 EVB Test S0 Test"; ++}; + -+static ssize_t ia_rdata_reg_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++&pcie2 { ++ status = "disabled"; ++}; + -+ return sprintf(buf, "%08lx\n", -+ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_IA_RDATA)); -+} ++&mdio2 { ++ status = "okay"; ++ #address-cells = <1>; ++ #size-cells = <0>; + -+static ssize_t ia_addr_reg_store(struct device *dev, -+ struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[9]; -+ unsigned int tmp; -+ int ret; ++ ethphy2: ethernet-phy@0 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <0>; ++ }; ++}; + -+ // string must be at least a 32-bit word in 0 padded hex -+ if (count < 8) -+ return -1; ++&sgmii { ++ status = "okay"; ++}; + -+ foo[8] = 0; -+ memcpy(foo, buf, 8); -+ ret = kstrtouint(foo, 16, &tmp); -+ if (ret) -+ return ret; ++&mac2 { ++ status = "okay"; + -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_ADDR, tmp); -+ return count; -+} ++ phy-mode = "sgmii"; ++ phy-handle = <ðphy2>; ++}; + -+static ssize_t ia_addr_reg_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++&sdio_controller { ++ status = "disabled"; ++}; + -+ return sprintf(buf, "%08lx\n", -+ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_IA_ADDR)); -+} ++&sdhci { ++ status = "disabled"; ++}; + -+static ssize_t ia_cmd_reg_store(struct device *dev, -+ struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[9]; -+ unsigned int tmp; -+ int ret; ++&espi1 { ++ status = "okay"; ++ perif-dma-mode; ++#if 0 //TODO: No enough memory for espi1 MMBI ++ perif-mmbi-enable; ++ perif-mmbi-src-addr = <0x0 0xa8000000>; ++ perif-mmbi-tgt-memory = <&espi1_mmbi_memory>; ++ perif-mmbi-instance-num = <0x1>; ++#endif ++ perif-mcyc-enable; ++ perif-mcyc-src-addr = <0x0 0x98000000>; ++ perif-mcyc-size = <0x0 0x10000>; ++ oob-dma-mode; ++ flash-dma-mode; ++}; + -+ // string must be at least a 32-bit word in 0 padded hex -+ if (count < 8) -+ return -1; ++&can0 { ++ status = "disabled"; ++}; + -+ foo[8] = 0; -+ memcpy(foo, buf, 8); -+ ret = kstrtouint(foo, 16, &tmp); -+ if (ret) -+ return ret; ++// S0 use 0x10 as its slave address ++&i2c0 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_CMD, tmp); -+ return count; -+} ++&i2c1 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+static ssize_t ia_cmd_reg_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++&i2c2 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ return sprintf(buf, "%08lx\n", -+ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_IA_CMD)); -+} ++&i2c3 { ++ clock-frequency = <1000000>; ++ debounce-level = <6>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+static ssize_t rnc_reg_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++&i2c4 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ return sprintf(buf, "%08lx\n", -+ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_EDU_RNC_CTRL)); -+} ++&i2c5 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+static ssize_t rnc_reg_store(struct device *dev, -+ struct device_attribute *devattr, const char *buf, -+ size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[9]; -+ unsigned int tmp; -+ int ret; ++&i2c6 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ // string must be at least a 32-bit word in 0 padded hex -+ if (count < 8) -+ return -1; ++&i2c7 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ foo[8] = 0; -+ memcpy(foo, buf, 8); -+ ret = kstrtouint(foo, 16, &tmp); -+ if (ret) -+ return ret; ++&i2c8 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_EDU_RNC_CTRL, tmp); ++&i2c9 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ return count; -+} ++&i2c10 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+static ssize_t rbc_reg_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++&i2c11 { ++ clock-frequency = <1000000>; ++ debounce-level = <6>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ return sprintf(buf, "%08lx\n", -+ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_EDU_RBC_CTRL)); -+} -+ -+static ssize_t rbc_reg_store(struct device *dev, -+ struct device_attribute *devattr, const char *buf, -+ size_t count) -+{ -+ char opts_str[5]; -+ unsigned int opts_int; -+ int enable, rbc_num, rate, urun_blnk, ret; -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++&i2c12 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ opts_str[4] = 0; -+ memcpy(opts_str, buf, 4); -+ ret = kstrtouint(opts_str, 16, &opts_int); -+ if (ret) -+ return ret; ++&i2c13 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ SYNHW_PRINT("%s %x\n", __func__, opts_int); ++&i2c14 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ enable = (opts_int >> 12 & 0xf); -+ if (enable > 1) { -+ SYNHW_PRINT("incorrect enable %x\n", enable); -+ return -1; -+ } ++&i2c15 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@10 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ rbc_num = (opts_int >> 8 & 0xf); -+ if (rbc_num > priv->nisttrng.config.edu_build_cfg0.rbc_channels - 1) { -+ SYNHW_PRINT("incorrect rbc_num %x\n", rbc_num); -+ return -1; -+ } ++&i3c0 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06010000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ rate = (opts_int >> 4 & 0xf); -+ if (rate > 8) { -+ SYNHW_PRINT("incorrect rate %x\n", rate); -+ return -1; -+ } ++&i3c1 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06011000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ urun_blnk = (opts_int & 0xf); -+ if (urun_blnk > 3) { -+ SYNHW_PRINT("incorrect urun_blnk %x\n", urun_blnk); -+ return -1; -+ } ++&i3c2 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06012000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ SYNHW_PRINT("enable %x rbc_num %x rate %x urun_blnk %x\n", enable, -+ rbc_num, rate, urun_blnk); ++&i3c3 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06013000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ ret = nisttrng_rbc(&priv->nisttrng, enable, rbc_num, rate, -+ urun_blnk); -+ if (ret) -+ return -1; ++&i3c4 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06014000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ return count; -+} ++&i3c5 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06015000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+static ssize_t hw_state_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ u32 addr; -+ int i; -+ int tot_char; ++&i3c6 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06016000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ addr = 0x20; -+ tot_char = sprintf(buf, "Key = "); -+ for (i = 0; i < 8; i++) { -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_ADDR, -+ addr + 7 - i); -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_CMD, -+ 0x80000000); -+ tot_char += sprintf(buf + tot_char, "%08lx", -+ pdu_io_read32(priv->nisttrng.base + -+ NIST_TRNG_REG_IA_RDATA)); -+ } -+ tot_char += sprintf(buf + tot_char, "\n"); ++&i3c7 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06017000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ addr = 0x28; -+ tot_char += sprintf(buf + tot_char, "V = "); -+ for (i = 0; i < 4; i++) { -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_ADDR, -+ addr + 3 - i); -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_CMD, -+ 0x80000000); -+ tot_char += sprintf(buf + tot_char, "%08lx", -+ pdu_io_read32(priv->nisttrng.base + -+ NIST_TRNG_REG_IA_RDATA)); -+ } ++&i3c8 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06018000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ tot_char += sprintf(buf + tot_char, "\n"); ++&i3c9 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06019000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ return tot_char; -+} ++&i3c10 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601A000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+static ssize_t max_bits_per_req_store(struct device *dev, -+ struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[9]; -+ unsigned long tmp; -+ int ret; ++&i3c11 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601B000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ // string must be at least a 32-bit word in 0 padded hex -+ if (count < 8) -+ return -1; ++&i3c12 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601C000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ foo[8] = 0; -+ memcpy(foo, buf, 8); -+ ret = kstrtoul(foo, 16, &tmp); -+ if (ret) -+ return ret; ++&i3c13 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601D000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ ret = nisttrng_set_reminder_max_bits_per_req(&priv->nisttrng, -+ tmp); -+ if (ret) -+ return -1; ++&i3c14 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601E000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ return count; -+} ++&i3c15 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601F000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts +--- a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts 1970-01-01 00:00:00.000000000 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,481 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later + -+static ssize_t max_bits_per_req_show(struct device *dev, -+ struct device_attribute *devattr, -+ char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++#include "ast2700a1-evb.dts" ++#include + -+ return sprintf(buf, "%08lx\n", -+ priv->nisttrng.counters.max_bits_per_req); -+} ++/ { ++ model = "AST2700A1 EVB Test S1 Test"; ++}; + -+static ssize_t max_req_per_seed_store(struct device *dev, -+ struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[17]; -+ unsigned long long tmp; -+ int ret; ++&bmc_dev0 { ++ status = "disabled"; ++}; + -+ // string must be at least a 64-bit word in 0 padded hex -+ if (count < 16) -+ return -1; ++&xdma0 { ++ status = "disabled"; ++}; + -+ foo[16] = 0; -+ memcpy(foo, buf, 16); -+ ret = kstrtoull(foo, 16, &tmp); -+ if (ret) -+ return ret; ++&pcie_vuart0 { ++ status = "disabled"; ++}; + -+ ret = nisttrng_set_reminder_max_req_per_seed(&priv->nisttrng, -+ tmp); -+ if (ret) -+ return -1; ++&pcie_vuart1 { ++ status = "disabled"; ++}; + -+ return count; -+} ++&pcie_lpc0_kcs0 { ++ status = "disabled"; ++}; + -+static ssize_t max_req_per_seed_show(struct device *dev, -+ struct device_attribute *devattr, -+ char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++&pcie_lpc0_kcs1 { ++ status = "disabled"; ++}; + -+ return sprintf(buf, "%08llx\n", -+ priv->nisttrng.counters.max_req_per_seed); -+} ++&pcie_lpc0_kcs2 { ++ status = "disabled"; ++}; + -+static ssize_t collect_ent_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ int rep; -+ int i, j; -+ int ret; -+ u32 tmp; -+ int t; ++&pcie_lpc0_kcs3 { ++ status = "disabled"; ++}; + -+ t = NIST_TRNG_RETRY_MAX; ++&pcie_lpc0_ibt { ++ status = "disabled"; ++}; + -+ // Change to TEST mode -+ DEBUG("Change to TEST mode\n"); -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_SMODE, 0x00000028); -+ // Turn on the noise collect mode -+ DEBUG("Turn on the noise collect mode\n"); -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_SMODE, 0x80000028); ++&pcie0_mmbi0 { ++ status = "disabled"; ++}; + -+ // issue generate entropy command -+ DEBUG("Issue a GEN_NOISE command\n"); -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_CTRL, -+ NIST_TRNG_REG_CTRL_CMD_GEN_NOISE); ++&pcie0 { ++ status = "okay"; ++}; + -+ // read raw noise -+ // 2 reads if sec_strength is 128 and 3 reads if it is 256 -+ if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES128) -+ rep = 2; -+ else if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES256) -+ rep = 3; ++&bmc_dev1 { ++ status = "disabled"; ++}; + -+ for (i = 0; i < rep; i++) { -+ t = NIST_TRNG_RETRY_MAX; -+ tmp = 0; -+ DEBUG("Wait for NOISE_RDY interrupt.\n"); -+ do { -+ tmp = pdu_io_read32(priv->nisttrng.base + -+ NIST_TRNG_REG_ISTAT); -+ } while (!(tmp & (NIST_TRNG_REG_ISTAT_NOISE_RDY | -+ NIST_TRNG_REG_ISTAT_ALARMS)) && -+ --t); ++&xdma1 { ++ status = "disabled"; ++}; + -+ DEBUG("Read NPA_DATAx\n"); -+ for (j = 0; j < 16; j++) { -+ sprintf(buf + 128 * i + 8 * j, "%08lx", -+ pdu_io_read32(priv->nisttrng.base + -+ NIST_TRNG_REG_NPA_DATA0 + j)); -+ } ++&pcie_vuart2 { ++ status = "disabled"; ++}; + -+ // clear NOISE_RDY IRQ -+ DEBUG("Clear NOISE_RDY interrupt.\n"); -+ ret = nisttrng_wait_on_noise_rdy(&priv->nisttrng); -+ if (ret) -+ return -1; -+ } ++&pcie_vuart3 { ++ status = "disabled"; ++}; + -+ DEBUG("Wait for DONE\n"); -+ ret = nisttrng_wait_on_done(&priv->nisttrng); -+ if (ret) -+ return -1; ++&pcie_lpc1_kcs0 { ++ status = "disabled"; ++}; + -+ strcat(buf, "\n"); -+ return strlen(buf); -+} ++&pcie_lpc1_kcs1 { ++ status = "disabled"; ++}; + -+static ssize_t collect_ent_nsout_show(struct device *dev, -+ struct device_attribute *devattr, -+ char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ int rep; -+ int i; -+ int ret; ++&pcie_lpc1_kcs2 { ++ status = "disabled"; ++}; + -+ // generate entropy -+ ret = nisttrng_get_entropy_input(&priv->nisttrng, NULL, 0); -+ if (ret) -+ return -1; ++&pcie_lpc1_kcs3 { ++ status = "disabled"; ++}; + -+ // read NS_OUTPUTx -+ // 32 reads if sec_strength is 128 and 48 reads if it is 256 -+ if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES128) -+ rep = 32; -+ else if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES256) -+ rep = 48; ++&pcie_lpc1_ibt { ++ status = "disabled"; ++}; + -+ for (i = 0; i < rep; i++) { -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_ADDR, -+ 0x70 + rep - 1 - i); -+ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_CMD, -+ 0x80000000); -+ sprintf(buf + 8 * i, "%08lx", -+ pdu_io_read32(priv->nisttrng.base + -+ NIST_TRNG_REG_IA_RDATA)); -+ } ++&pcie1_mmbi4 { ++ status = "disabled"; ++}; + -+ strcat(buf, "\n"); -+ return strlen(buf); -+} ++&pcie1 { ++ status = "okay"; ++}; + -+static ssize_t nonce_seed_with_df_store(struct device *dev, -+ struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[9]; -+ u32 seed[48] = { 0 }; -+ int rep; -+ int i; -+ int ret; ++&sdio_controller { ++ status = "disabled"; ++}; + -+ if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES128) -+ rep = 2; -+ else if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES256) -+ rep = 3; ++&sdhci { ++ status = "disabled"; ++}; + -+ DEBUG("Number of char in input = %zu\n", count); -+ if (count != (rep * 128)) -+ return -1; ++&espi1 { ++ status = "okay"; ++ perif-dma-mode; ++#if 0 // TODO: No enough memory for espi1 MMBI ++ perif-mmbi-enable; ++ perif-mmbi-src-addr = <0x0 0xa8000000>; ++ perif-mmbi-tgt-memory = <&espi1_mmbi_memory>; ++ perif-mmbi-instance-num = <0x1>; ++#endif ++ perif-mcyc-enable; ++ perif-mcyc-src-addr = <0x0 0x98000000>; ++ perif-mcyc-size = <0x0 0x10000>; ++ oob-dma-mode; ++ flash-dma-mode; ++}; + -+ foo[8] = 0; -+ for (i = 0; i < (rep * 16); i++) { -+ memcpy(foo, buf + i * 8, 8); -+ ret = kstrtouint(foo, 16, (seed + (rep * 16 - 1) - i)); -+ if (ret) -+ return ret; -+ } ++&can0 { ++ status = "disabled"; ++}; + -+ ret = nisttrng_get_entropy_input(&priv->nisttrng, seed, 1); -+ if (ret) -+ return -1; ++// S1 use 0x12 as its slave address ++&i2c0 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ return count; -+} ++&i2c1 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+static ssize_t nonce_seed_direct_store(struct device *dev, -+ struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char foo[9]; -+ u32 seed[12] = { 0 }; -+ int rep; -+ int i; -+ int ret; -+ -+ if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES128) -+ rep = 2; -+ else if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES256) -+ rep = 3; ++&i2c2 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ DEBUG("Number of char in input = %zu\n", count); -+ if (count != (rep * 32)) -+ return -1; ++&i2c3 { ++ clock-frequency = <1000000>; ++ debounce-level = <6>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ foo[8] = 0; -+ for (i = 0; i < (rep * 4); i++) { -+ memcpy(foo, buf + i * 8, 8); -+ ret = kstrtouint(foo, 16, (seed + (rep * 4 - 1) - i)); -+ if (ret) -+ return ret; -+ } ++&i2c4 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ ret = nisttrng_get_entropy_input(&priv->nisttrng, seed, 0); -+ if (ret) -+ return -1; ++&i2c5 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ return count; -+} ++&i2c6 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+static ssize_t instantiate_store(struct device *dev, -+ struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char opts_str[101]; -+ unsigned int opts_int; -+ int req_sec_strength = 256; -+ int pred_resist = 1; -+ bool ps_exists = 0; -+ u32 ps[12]; -+ unsigned int ps_length; -+ int i; -+ int ret; ++&i2c7 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ /* First 3 digits: -+ * they have to be 0 or 1 -+ * 2-1-0 --> 2: predictoin resistance, 1: security strength, 0: personilizatoin string existence -+ */ -+ opts_str[3] = 0; -+ memcpy(opts_str, buf, 3); -+ ret = kstrtouint(opts_str, 2, &opts_int); -+ if (ret) -+ return ret; ++&i2c8 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ if (((opts_str[0] != '0') && (opts_str[0] != '1')) || -+ ((opts_str[1] != '0') && (opts_str[1] != '1')) || -+ ((opts_str[2] != '0') && (opts_str[2] != '1'))) { -+ SYNHW_PRINT("Invalid input options: First 3 digits can only be 1 or 0\n"); -+ return -1; -+ } ++&i2c9 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ if (opts_int & 1) -+ ps_exists = 1; -+ else -+ ps_exists = 0; ++&i2c10 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ if (opts_int & 2) -+ req_sec_strength = 256; -+ else -+ req_sec_strength = 128; ++&i2c11 { ++ clock-frequency = <1000000>; ++ debounce-level = <6>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ if (opts_int & 4) -+ pred_resist = 1; -+ else -+ pred_resist = 0; ++&i2c12 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ /* check input option length */ -+ if (!ps_exists) { -+ if (count != 3) { -+ SYNHW_PRINT("Invalid input options: If personilization string does not exist, options has to be 3 char.\n"); -+ return -1; -+ } -+ } else { -+ if (req_sec_strength == 128) { -+ if (count != 64 + 4) { // +4 for options and "-" -+ SYNHW_PRINT("Invalid input options: If personilization string exists and security strength is 128-bit, options has to be 68 char (not %zu char).\n", -+ count); -+ return -1; -+ } -+ } else if (req_sec_strength == 256) { -+ if (count != -+ 96 + 4) { // +4 for options and "-", +1 because of the termination char that count includes -+ SYNHW_PRINT("Invalid input options: If personilization string exists and security strength is 256-bit, options has to be 100 char (not %zu char).\n", -+ count); -+ return -1; -+ } -+ } else { -+ SYNHW_PRINT("Invalid input options\n"); -+ return -1; -+ } -+ } ++&i2c13 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ /* Personilization string */ -+ for (i = 0; i < 12; i++) -+ ps[i] = 0; ++&i2c14 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ if (req_sec_strength == 128) -+ ps_length = 64; -+ else if (req_sec_strength == 256) -+ ps_length = 96; -+ else -+ SYNHW_PRINT("Invalid security strength\n"); ++&i2c15 { ++ clock-frequency = <1000000>; ++ debounce-level = <4>; ++ status = "okay"; ++ multi-master; ++ mctp-controller; ++ mctp@12 { ++ compatible = "mctp-i2c-controller"; ++ reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; ++ }; ++}; + -+ if (ps_exists) { -+ opts_str[1] = 0; -+ memcpy(opts_str, buf + 3, 1); ++&i3c0 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ if (opts_str[0] == '-') { -+ opts_str[8] = 0; -+ for (i = 0; i < ps_length / 8; i++) { -+ memcpy(opts_str, buf + 4 + i * 8, 8); -+ ret = kstrtouint(opts_str, 16, -+ ps + (ps_length / 8 - 1) - i); -+ if (ret) -+ return ret; -+ } -+ } else { -+ SYNHW_PRINT("4th character of input has to be \"-\" when personilization string exists\n"); -+ } ++&i3c1 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ ret = nisttrng_instantiate(&priv->nisttrng, -+ req_sec_strength, pred_resist, -+ ps); -+ if (ret) -+ return -1; ++&i3c2 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ } else { -+ ret = nisttrng_instantiate(&priv->nisttrng, -+ req_sec_strength, pred_resist, -+ NULL); -+ if (ret) -+ return -1; -+ } ++&i3c3 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ return count; -+} ++&i3c4 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+static ssize_t uninstantiate_store(struct device *dev, -+ struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++&i3c5 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ nisttrng_uninstantiate(&priv->nisttrng); ++&i3c6 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ return count; -+} ++&i3c7 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+static ssize_t reseed_store(struct device *dev, struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char opts_str[100]; -+ unsigned int opts_int; -+ int pred_resist = 1; -+ bool addin_exists = 0; -+ u32 addin[12]; -+ unsigned int addin_length; -+ int i; -+ int ret; ++&i3c8 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ /* First 2 digits: -+ * they have to be 0 or 1 -+ * 1-0 --> 1: predictoin resistance, 0: additional input string existence -+ */ -+ opts_str[2] = 0; -+ memcpy(opts_str, buf, 2); -+ ret = kstrtouint(opts_str, 2, &opts_int); -+ if (ret) -+ return ret; ++&i3c9 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ if (((opts_str[0] != '0') && (opts_str[0] != '1')) || -+ ((opts_str[1] != '0') && (opts_str[1] != '1'))) { -+ SYNHW_PRINT("Invalid input options: First 2 digits can only be 1 or 0\n"); -+ return -1; -+ } ++&i3c10 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ if (opts_int & 1) -+ addin_exists = 1; -+ else -+ addin_exists = 0; ++&i3c11 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ if (opts_int & 2) -+ pred_resist = 1; -+ else -+ pred_resist = 0; ++&i3c12 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ /* check input option length */ -+ if (!addin_exists) { -+ if (count != 2) { -+ SYNHW_PRINT("Invalid input options: If additional input does not exist, options has to be 2 char.\n"); -+ return -1; -+ } -+ } else { -+ if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES128) { -+ if (count != 64 + 3) { // +3 for options and "-" -+ SYNHW_PRINT("Invalid input options: If additional input exists and security strength is 128-bit, options has to be 67 char.\n"); -+ return -1; -+ } -+ } else if (priv->nisttrng.status.sec_strength == -+ SEC_STRNT_AES256) { -+ if (count != 96 + 3) { // +3 for options and "-" -+ SYNHW_PRINT("Invalid input options: If additional input exists and security strength is 256-bit, options has to be 99 char.\n"); -+ return -1; -+ } -+ } else { -+ SYNHW_PRINT("Invalid input options\n"); -+ return -1; -+ } -+ } ++&i3c13 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ /* Additional input */ -+ for (i = 0; i < 12; i++) -+ addin[i] = 0; ++&i3c14 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES128) -+ addin_length = 64; -+ else if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES256) -+ addin_length = 96; -+ else -+ SYNHW_PRINT("Invalid security strength\n"); ++&i3c15 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ if (addin_exists) { -+ opts_str[1] = 0; -+ memcpy(opts_str, buf + 2, 1); ++&sgpios { ++ status = "disabled"; ++}; + -+ if (opts_str[0] == '-') { -+ opts_str[8] = 0; -+ for (i = 0; i < addin_length / 8; i++) { -+ memcpy(opts_str, buf + 3 + i * 8, 8); -+ ret = kstrtouint(opts_str, 16, addin + (addin_length / 8 - 1) - i); -+ if (ret) -+ return ret; -+ } -+ } else { -+ SYNHW_PRINT("3rd character of input has to be \"-\" when additional input exists\n"); -+ } ++&mac0 { ++ status = "okay"; + -+ ret = nisttrng_reseed(&priv->nisttrng, pred_resist, -+ addin); -+ if (ret) -+ return -1; ++ phy-mode = "rmii"; ++ use-ncsi; + -+ } else { -+ ret = nisttrng_reseed(&priv->nisttrng, pred_resist, -+ NULL); -+ if (ret) -+ return -1; -+ } ++ pinctrl-names = "default"; ++ /* If you want to use RMII0 RCLKO as internal clock for RMII, ++ * add &pinctrl_rmii0_rclko_default in pinctrl-0. ++ */ ++ pinctrl-0 = <&pinctrl_rmii0_default>; ++}; + -+ return count; -+} ++&mac1 { ++ status = "okay"; + -+static ssize_t generate_store(struct device *dev, -+ struct device_attribute *devattr, const char *buf, -+ size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char opts_str[101]; -+ unsigned int opts_int; -+ int req_sec_strength = 128; -+ int pred_resist = 1; -+ bool addin_exists = 0; -+ unsigned char out[num_gen_bytes]; -+ u32 addin[12]; -+ unsigned int addin_length; -+ int i; -+ int ret; ++ phy-mode = "rmii"; ++ use-ncsi; + -+ /* First 3 digits: -+ * they have to be 0 or 1 -+ * 2-1-0 --> 2: predictoin resistance, 1: security strength, 0: additional input string existence ++ pinctrl-names = "default"; ++ /* If you want to use RMII1 RCLKO as internal clock for RMII, ++ * add &pinctrl_rmii1_rclko_default in pinctrl-0. + */ -+ opts_str[3] = 0; -+ memcpy(opts_str, buf, 3); -+ ret = kstrtouint(opts_str, 2, &opts_int); -+ if (ret) -+ return ret; -+ -+ if (((opts_str[0] != '0') && (opts_str[0] != '1')) || -+ ((opts_str[1] != '0') && (opts_str[1] != '1')) || -+ ((opts_str[2] != '0') && (opts_str[2] != '1'))) { -+ SYNHW_PRINT("Invalid input options: First 3 digits can only be 1 or 0\n"); -+ return -1; -+ } ++ pinctrl-0 = <&pinctrl_rmii1_default>; ++}; + -+ if (opts_int & 1) -+ addin_exists = 1; -+ else -+ addin_exists = 0; ++&syscon1 { ++ mac0-clk-delay = <0 0 ++ 0 0 ++ 0 0>; ++ mac1-clk-delay = <0 0 ++ 0 0 ++ 0 0>; ++}; + -+ if (opts_int & 2) -+ req_sec_strength = 256; -+ else -+ req_sec_strength = 128; -+ -+ if (opts_int & 4) -+ pred_resist = 1; -+ else -+ pred_resist = 0; ++&vhuba0 { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_usb2ad0_default>; ++}; + -+ /* check input option length */ -+ if (!addin_exists) { -+ if (count != 3) { -+ SYNHW_PRINT("Invalid input options: If additional input does not exist, options has to be 3 char.\n"); -+ return -1; -+ } -+ } else { -+ if (req_sec_strength == 128) { -+ if (count != 64 + 4) { // +4 for options and "-" -+ SYNHW_PRINT("Invalid input options: If additional input exists and security strength is 128-bit, options has to be 68 char.\n"); -+ return -1; -+ } -+ } else if (req_sec_strength == 256) { -+ if (count != 96 + 4) { // +4 for options and "-" -+ SYNHW_PRINT("Invalid input options: If additional input exists and security strength is 256-bit, options has to be 100 char.\n"); -+ return -1; -+ } -+ } else { -+ SYNHW_PRINT("Invalid input options\n"); -+ return -1; -+ } -+ } ++&uphy2a { ++ status = "okay"; ++}; + -+ /* Additional input */ -+ for (i = 0; i < 12; i++) -+ addin[i] = 0; ++&usb3ahp { ++ status = "disabled"; ++}; + -+ if (req_sec_strength == 128) -+ addin_length = 64; -+ else if (req_sec_strength == 256) -+ addin_length = 96; -+ else -+ SYNHW_PRINT("Invalid security strength\n"); ++&usb3bhp { ++ status = "disabled"; ++}; + -+ if (addin_exists) { -+ opts_str[1] = 0; -+ memcpy(opts_str, buf + 3, 1); ++&vhubb0 { ++ status = "okay"; ++}; + -+ if (opts_str[0] == '-') { -+ opts_str[8] = 0; -+ for (i = 0; i < addin_length / 8; i++) { -+ memcpy(opts_str, buf + 4 + i * 8, 8); -+ ret = kstrtouint(opts_str, 16, addin + (addin_length / 8 - 1) - i); -+ if (ret) -+ return ret; -+ } -+ } else { -+ SYNHW_PRINT("4th character of input has to be \"-\" when additional input exists\n"); -+ } ++&vhubb1 { ++ status = "disabled"; ++}; + -+ ret = nisttrng_generate(&priv->nisttrng, (u32 *)out, -+ num_gen_bytes, req_sec_strength, -+ pred_resist, addin); -+ if (ret) -+ return -1; ++&vhubc { ++ status = "disabled"; ++}; + -+ } else { -+ ret = nisttrng_generate(&priv->nisttrng, (u32 *)out, -+ num_gen_bytes, req_sec_strength, -+ pred_resist, NULL); -+ if (ret) -+ return -1; -+ } ++&vhubd { ++ status = "okay"; ++}; + -+ /* store the result */ -+ memcpy(priv->rand_out, out, sizeof(out)); ++&ehci2 { ++ status = "okay"; ++}; + -+ return count; -+} ++&ehci3 { ++ status = "disabled"; ++}; + -+static ssize_t generate_pub_vtrng_store(struct device *dev, -+ struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ char opts_str[2]; -+ unsigned int opts_int; -+ unsigned char out[num_gen_bytes]; -+ int ret; ++&pcie2 { ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts +--- a/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts 1970-01-01 00:00:00.000000000 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,1265 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later + -+ opts_str[1] = 0; -+ memcpy(opts_str, buf, 1); -+ ret = kstrtouint(opts_str, 16, &opts_int); -+ if (ret) -+ return ret; ++/dts-v1/; ++#include "aspeed-g7.dtsi" ++#include ++#include ++#include + -+ SYNHW_PRINT("%s %d %d %d %d\n", __func__, opts_str[0], -+ priv->nisttrng.config.edu_build_cfg0.public_vtrng_channels, -+ opts_str[1], opts_int); ++#define DUAL_NODE 0 // 1: DUAL_NODE, 0: SINGLE_NODE ++#define PCIE0_EP 1 // 1: EP, 0: RC ++#define PCIE1_EP 1 // 1: EP, 0: RC ++#define PCIE2_RC 1 // 1: RC, 0: SGMII + -+ ret = nisttrng_generate_public_vtrng(&priv->nisttrng, -+ (u32 *)out, -+ num_gen_bytes, opts_int); -+ if (ret) -+ return -1; ++/ { ++ model = "AST2700A1 EVB"; ++ compatible = "aspeed,ast2700-evb", "aspeed,ast2700"; + -+ memcpy(priv->rand_out, out, sizeof(out)); ++ chosen { ++ stdout-path = "serial12:115200n8"; ++ }; + -+ return count; -+} ++ memory@400000000 { ++ device_type = "memory"; ++ reg = <0x4 0x00000000 0x0 0x40000000>; ++ }; + -+/* rand_out_show displays last generated random number (num_gen_bytes number of bytes), not just the last block. */ -+static ssize_t rand_out_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ unsigned int i, j; -+ unsigned long rand; -+ bool all_zero = true; ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; + -+ /* If all bits of the rand_reg register are 0, display 0 */ -+ for (i = 0; i < 4; i++) { -+ rand = pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_RAND0 + -+ (3 - i)); -+ if (rand != 0) { -+ all_zero = false; -+ break; -+ } -+ } ++ #include "ast2700-reserved-mem.dtsi" + -+ if (all_zero) { -+ sprintf(buf + 2 * i, "%02x", 0); -+ } else { -+ for (i = 0; i < (num_gen_bytes / 16); i++) { -+ for (j = 0; j < 16; j++) { -+ sprintf(buf + 2 * (i * 16 + j), "%02x", -+ priv->rand_out[(i + 1) * 16 - 1 - j]); -+ } -+ } -+ j = 0; -+ while (i * 16 + j < num_gen_bytes) { -+ sprintf(buf + 2 * (i * 16 + j), "%02x", -+ priv->rand_out[num_gen_bytes - 1 - j]); -+ j++; -+ } -+ } ++ video_engine_memory0: video0 { ++ size = <0x0 0x02000000>; ++ alignment = <0x0 0x00010000>; ++ compatible = "shared-dma-pool"; ++ reusable; ++ }; + -+ strcat(buf, "\n"); -+ return strlen(buf); -+} ++ video_engine_memory1: video1 { ++ size = <0x0 0x02000000>; ++ alignment = <0x0 0x00010000>; ++ compatible = "shared-dma-pool"; ++ reusable; ++ }; + -+/* rand_out_vtrng_show displays last generated random number (num_gen_bytes number of bytes), not just the last block. */ -+static ssize_t rand_out_vtrng_show(struct device *dev, -+ struct device_attribute *devattr, char *buf) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ unsigned int i, j; ++#if 0 ++ gfx_memory: framebuffer { ++ size = <0x0 0x01000000>; ++ alignment = <0x0 0x01000000>; ++ compatible = "shared-dma-pool"; ++ reusable; ++ }; ++#endif + -+ /* If all bits of the rand_reg register are 0, display 0 */ ++ espi0_mcyc_memory: mcyc0 { ++ size = <0x0 0x01000000>; ++ alignment = <0x0 0x00010000>; ++ compatible = "shared-dma-pool"; ++ reusable; ++ }; ++ }; + -+ for (i = 0; i < (num_gen_bytes / 16); i++) { -+ for (j = 0; j < 16; j++) { -+ sprintf(buf + 2 * (i * 16 + j), "%02x", -+ priv->rand_out[(i + 1) * 16 - 1 - j]); -+ } -+ } ++ fan0: pwm-fan0 { ++ compatible = "pwm-fan"; ++ pwms = <&pwm_tach 0 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ j = 0; -+ while (i * 16 + j < num_gen_bytes) { -+ sprintf(buf + 2 * (i * 16 + j), "%02x", -+ priv->rand_out[num_gen_bytes - 1 - j]); -+ j++; -+ } ++ fan1: pwm-fan1 { ++ compatible = "pwm-fan"; ++ pwms = <&pwm_tach 1 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ strcat(buf, "\n"); -+ return strlen(buf); -+} ++ fan2: pwm-fan2 { ++ compatible = "pwm-fan"; ++ pwms = <&pwm_tach 2 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+static ssize_t kat_store(struct device *dev, struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ int ret; ++ fan3: pwm-fan3 { ++ compatible = "pwm-fan"; ++ pwms = <&pwm_tach 3 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ if (sysfs_streq(buf, "full")) { -+ ret = nisttrng_full_kat(&priv->nisttrng); -+ if (ret) -+ return -1; ++ fan4: pwm-fan4 { ++ compatible = "pwm-fan"; ++ pwms = <&pwm_tach 4 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ } else if (sysfs_streq(buf, "00")) { -+ ret = nisttrng_kat(&priv->nisttrng, 0, 0); -+ if (ret) -+ return -1; ++ fan5: pwm-fan5 { ++ compatible = "pwm-fan"; ++ pwms = <&pwm_tach 5 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ } else if (sysfs_streq(buf, "01")) { -+ ret = nisttrng_kat(&priv->nisttrng, 0, 1); -+ if (ret) -+ return -1; ++ fan6: pwm-fan6 { ++ compatible = "pwm-fan"; ++ pwms = <&pwm_tach 6 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ } else if (sysfs_streq(buf, "10")) { -+ ret = nisttrng_kat(&priv->nisttrng, 1, 0); -+ if (ret) -+ return -1; ++ fan7: pwm-fan7 { ++ compatible = "pwm-fan"; ++ pwms = <&pwm_tach 7 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ } else if (sysfs_streq(buf, "11")) { -+ ret = nisttrng_kat(&priv->nisttrng, 1, 1); -+ if (ret) -+ return -1; ++ fan8: pwm-fan8 { ++ compatible = "pwm-fan"; ++ pwms = <&pwm_tach 8 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; + -+ } else { -+ ret = nisttrng_full_kat(&priv->nisttrng); -+ if (ret) -+ return -1; -+ } ++ iio-hwmon { ++ compatible = "iio-hwmon"; ++ status = "okay"; ++ io-channels = <&adc0 0>, <&adc0 1>, <&adc0 2>, <&adc0 3>, ++ <&adc0 4>, <&adc0 5>, <&adc0 6>, <&adc0 7>, ++ <&adc1 0>, <&adc1 1>, <&adc1 2>, <&adc1 3>, ++ <&adc1 4>, <&adc1 5>, <&adc1 6>, <&adc1 7>; ++ }; ++}; + -+ return count; -+} ++&pwm_tach { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm0_default &pinctrl_pwm1_default ++ &pinctrl_pwm2_default &pinctrl_pwm3_default ++ &pinctrl_pwm4_default &pinctrl_pwm5_default ++ &pinctrl_pwm6_default &pinctrl_pwm7_default ++ &pinctrl_pwm8_default ++ &pinctrl_tach0_default &pinctrl_tach1_default ++ &pinctrl_tach2_default &pinctrl_tach3_default ++ &pinctrl_tach4_default &pinctrl_tach5_default ++ &pinctrl_tach6_default &pinctrl_tach7_default ++ &pinctrl_tach8_default &pinctrl_tach9_default ++ &pinctrl_tach10_default &pinctrl_tach11_default ++ &pinctrl_tach12_default &pinctrl_tach13_default ++ &pinctrl_tach14_default &pinctrl_tach15_default>; ++ fan-0 { ++ tach-ch = /bits/ 8 <0x0>; ++ }; ++ fan-1 { ++ tach-ch = /bits/ 8 <0x1>; ++ }; ++ fan-2 { ++ tach-ch = /bits/ 8 <0x2>; ++ }; ++ fan-3 { ++ tach-ch = /bits/ 8 <0x3>; ++ }; ++ fan-4 { ++ tach-ch = /bits/ 8 <0x4>; ++ }; ++ fan-5 { ++ tach-ch = /bits/ 8 <0x5>; ++ }; ++ fan-6 { ++ tach-ch = /bits/ 8 <0x6>; ++ }; ++ fan-7 { ++ tach-ch = /bits/ 8 <0x7>; ++ }; ++ fan-8 { ++ tach-ch = /bits/ 8 <0x8>; ++ }; ++ fan-9 { ++ tach-ch = /bits/ 8 <0x9>; ++ }; ++ fan-10 { ++ tach-ch = /bits/ 8 <0xA>; ++ }; ++ fan-11 { ++ tach-ch = /bits/ 8 <0xB>; ++ }; ++ fan-12 { ++ tach-ch = /bits/ 8 <0xC>; ++ }; ++ fan-13 { ++ tach-ch = /bits/ 8 <0xD>; ++ }; ++ fan-14 { ++ tach-ch = /bits/ 8 <0xE>; ++ }; ++ fan-15 { ++ tach-ch = /bits/ 8 <0xF>; ++ }; ++}; + -+static void str_to_384_bit(char *buf, u32 *out) -+{ -+ char foo[9]; -+ int i; -+ int ret; ++&edac { ++ status = "okay"; ++}; + -+ foo[8] = 0; -+ for (i = 0; i < 12; i++) { -+ memcpy(foo, buf + i * 8, 8); -+ ret = kstrtouint(foo, 16, out + 11 - i); -+ } -+} ++&mctp0 { ++ status = "okay"; ++ memory-region = <&mctp0_reserved>; ++}; + -+/* This attribute is only for test purpuses */ -+static ssize_t test_attr_store(struct device *dev, -+ struct device_attribute *devattr, const char *buf, -+ size_t count) -+{ -+ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); -+ int i; -+ int err; -+ u32 addin[12]; -+ u32 ps[12]; -+ char *out; ++&mctp1 { ++ status = "okay"; ++ memory-region = <&mctp1_reserved>; ++}; + -+ char buf_seed1[96] = -+ "c54805274bde00aa5289e0513579019707666d2fa7a1c8908865891c87c0c652335a4d3cc415bc30742b164647f8820f"; -+ char buf_ps1[96] = -+ "d63fb5afa2101fa4b8a6c3b89d9c250ac728fc1ddad0e7585b5d54728ed20c2f940e89155596e3b963635b6d6088164b"; -+ char buf_addin1[96] = -+ "744bfae3c23a5cc9a3b373b6c50795068d35eb8a339746ac810d16f864e880061082edf9d2687c211960aa83400f85f9"; -+ char buf_seed2[96] = -+ "b2ad31d1f20dcf30dd526ec9156c07f270216bdb59197325bab180675929888ab699c54fb21819b7d921d6346bff2f7f"; -+ char buf_addin2[96] = -+ "ad55c682962aa4fe9ebc227c9402e79b0aa7874844d33eaee7e2d15baf81d9d33936e4d93f28ad109657b512aee115a5"; -+ char buf_seed3[96] = -+ "eca449048d26fd38f8ca435237dce66eadec7069ee5dd0b70084b819a711c0820a7556bbd0ae20f06e5169278b593b71"; -+ u32 tmp[12]; ++&mctp2 { ++ status = "okay"; ++ memory-region = <&mctp2_reserved>; ++}; + -+ for (i = 0; i < 12; i++) -+ addin[i] = i; ++&sgpiom0 { ++ status = "okay"; ++}; + -+ for (i = 0; i < 12; i++) -+ ps[i] = i + 100; ++&sgpiom1 { ++ status = "okay"; ++}; + -+ /* SDK doc example - Prediction Resistance not available, no Reseed */ -+ err = nisttrng_uninstantiate(&priv->nisttrng); -+ if (err && err != CRYPTO_NOT_INSTANTIATED) -+ return -1; ++&jtag1 { ++ status = "okay"; ++}; + -+ if (nisttrng_instantiate(&priv->nisttrng, 128, 0, ps) < 0) -+ return -1; ++&adc0 { ++ aspeed,int-vref-microvolt = <2500000>; ++ status = "okay"; + -+ out = kmalloc(10, GFP_KERNEL); -+ if (nisttrng_generate(&priv->nisttrng, out, 10, 128, 0, addin) < 0) -+ return -1; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_adc0_default &pinctrl_adc1_default ++ &pinctrl_adc2_default &pinctrl_adc3_default ++ &pinctrl_adc4_default &pinctrl_adc5_default ++ &pinctrl_adc6_default &pinctrl_adc7_default>; ++}; + -+ DEBUG("----- Generate 10 bytes\n"); -+ for (i = 0; i < 10; i++) -+ DEBUG("%02x", out[i]); ++&adc1 { ++ aspeed,int-vref-microvolt = <2500000>; ++ status = "okay"; + -+ DEBUG("\n"); -+ kfree(out); ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_adc8_default &pinctrl_adc9_default ++ &pinctrl_adc10_default &pinctrl_adc11_default ++ &pinctrl_adc12_default &pinctrl_adc13_default ++ &pinctrl_adc14_default &pinctrl_adc15_default>; ++}; + -+ out = kmalloc(512, GFP_KERNEL); -+ if (nisttrng_generate(&priv->nisttrng, out, 512, 128, 0, addin) < 0) -+ return -1; ++&pinctrl0 { ++ pinctrl_emmcclk_driving: emmcclk-driving { ++ pins = "AC14"; ++ drive-strength = <2>; ++ }; + -+ DEBUG("----- Generate 512 bytes\n"); -+ for (i = 0; i < 512; i++) -+ DEBUG("%02x", out[i]); ++ pinctrl_emmccmd_driving: emmccmd-driving { ++ pins = "AE15"; ++ drive-strength = <1>; ++ }; ++ pinctrl_emmc4bit_driving: emmcdat-driving { ++ pins = "AD14", "AE14", "AF14", "AB13"; ++ drive-strength = <1>; ++ }; + -+ DEBUG("\n"); -+ kfree(out); ++ pinctrl_emmc8bit_driving: emmcdat-driving { ++ pins = "AD14", "AE14", "AF14", "AB13", "AF13", "AC13", "AD13", "AE13"; ++ drive-strength = <1>; ++ }; ++}; + -+ out = kmalloc(41, GFP_KERNEL); -+ if (nisttrng_generate(&priv->nisttrng, out, 41, 128, 0, addin) < 0) -+ return -1; ++&pinctrl1 { ++ pinctrl_i3c0_3_hv_voltage: i3chv-voltage { ++ pins = "U25"; ++ power-source = <1800>; ++ }; + -+ DEBUG("----- Generate 41 bytes\n"); -+ for (i = 0; i < 41; i++) -+ DEBUG("%02x", out[i]); ++ pinctrl_i3c0_driving: i3c0-driving { ++ pins = "U25", "U26"; ++ drive-strength = <2>; ++ }; + -+ DEBUG("\n"); -+ kfree(out); ++ pinctrl_i3c1_driving: i3c1-driving { ++ pins = "Y26", "AA24"; ++ drive-strength = <2>; ++ }; + -+ err = nisttrng_uninstantiate(&priv->nisttrng); -+ if (err < 0 && err != CRYPTO_NOT_INSTANTIATED) -+ return -1; ++ pinctrl_i3c2_driving: i3c2-driving { ++ pins = "R25", "AA26"; ++ drive-strength = <2>; ++ }; + -+ /* SDK doc example - DRBG Validation */ -+ err = nisttrng_uninstantiate(&priv->nisttrng); -+ if (err && err != CRYPTO_NOT_INSTANTIATED) -+ return -1; ++ pinctrl_i3c3_driving: i3c3-driving { ++ pins = "R26", "Y25"; ++ drive-strength = <2>; ++ }; + -+ if (nisttrng_set_nonce_mode(&priv->nisttrng, 1) < 0) -+ return -1; ++ pinctrl_i3c12_15_hv_voltage: i3chv-voltage { ++ pins = "W25"; ++ power-source = <1800>; ++ }; + -+ out = kmalloc(64, GFP_KERNEL); -+ str_to_384_bit(buf_seed1, tmp); -+ if (nisttrng_get_entropy_input(&priv->nisttrng, tmp, 0) < 0) -+ return -1; ++ pinctrl_i3c12_driving: i3c12-driving { ++ pins = "W25", "Y23"; ++ drive-strength = <2>; ++ }; + -+ str_to_384_bit(buf_ps1, tmp); -+ if (nisttrng_instantiate(&priv->nisttrng, 256, 1, tmp) < 0) -+ return -1; ++ pinctrl_i3c13_driving: i3c13-driving { ++ pins = "Y24", "W21"; ++ drive-strength = <2>; ++ }; + -+ str_to_384_bit(buf_seed2, tmp); -+ if (nisttrng_get_entropy_input(&priv->nisttrng, tmp, 0) < 0) -+ return -1; ++ pinctrl_i3c14_driving: i3c14-driving { ++ pins = "AA23", "AC22"; ++ drive-strength = <2>; ++ }; + -+ str_to_384_bit(buf_addin1, tmp); -+ if (nisttrng_generate(&priv->nisttrng, out, 64, 256, 1, tmp) < 0) -+ return -1; ++ pinctrl_i3c15_driving: i3c15-driving { ++ pins = "AB22", "Y21"; ++ drive-strength = <2>; ++ }; + -+ str_to_384_bit(buf_seed3, tmp); -+ if (nisttrng_get_entropy_input(&priv->nisttrng, tmp, 0) < 0) -+ return -1; ++ pinctrl_rgmii0_driving: rgmii0-driving { ++ pins = "C20", "C19", "A8", "R14", "A7", "P14", ++ "D20", "A6", "B6", "N14", "B7", "B8"; ++ drive-strength = <1>; ++ }; + -+ str_to_384_bit(buf_addin2, tmp); -+ if (nisttrng_generate(&priv->nisttrng, out, 64, 256, 1, tmp) < 0) -+ return -1; ++ pinctrl_rgmii1_driving: rgmii1-driving { ++ pins = "D19", "C19", "D15", "B12", "B10", "P13", ++ "C18", "C6", "C7", "D7", "N13", "C8"; ++ drive-strength = <1>; ++ }; ++}; + -+ memcpy(priv->rand_out, out, 64); ++&gpio1 { ++ pinctrl-0 = <&pinctrl_i3c0_3_hv_voltage &pinctrl_i3c12_15_hv_voltage ++ &pinctrl_i3c0_driving &pinctrl_i3c1_driving ++ &pinctrl_i3c2_driving &pinctrl_i3c3_driving ++ &pinctrl_i3c12_driving &pinctrl_i3c13_driving ++ &pinctrl_i3c14_driving &pinctrl_i3c15_driving>; ++ pinctrl-names = "default"; ++}; + -+ return count; -+} ++&i3c0 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06010000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+static DEVICE_ATTR_RO(ckr); -+static DEVICE_ATTR_RO(features); -+static DEVICE_ATTR_RW(secure); -+static DEVICE_ATTR_RW(nonce); -+static DEVICE_ATTR_RW(sec_strength); ++&i3c1 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+static DEVICE_ATTR_RW(mode_reg); -+static DEVICE_ATTR_RW(smode_reg); -+static DEVICE_ATTR_RW(alarm_reg); -+static DEVICE_ATTR_RO(rand_reg); -+static DEVICE_ATTR_RO(rand_out); -+static DEVICE_ATTR_RO(rand_out_vtrng); -+static DEVICE_ATTR_RW(seed_reg); -+static DEVICE_ATTR_RW(npa_data_reg); -+static DEVICE_ATTR_RW(ctrl_reg); -+static DEVICE_ATTR_RW(istat_reg); -+static DEVICE_ATTR_RO(stat_reg); -+static DEVICE_ATTR_RW(rnc_reg); -+static DEVICE_ATTR_RW(rbc_reg); ++&i3c2 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06012000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+static DEVICE_ATTR_RW(ia_wdata_reg); -+static DEVICE_ATTR_RO(ia_rdata_reg); -+static DEVICE_ATTR_RW(ia_addr_reg); -+static DEVICE_ATTR_RW(ia_cmd_reg); -+static DEVICE_ATTR_RO(hw_state); ++&i3c3 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+static DEVICE_ATTR_RO(collect_ent); -+static DEVICE_ATTR_RO(collect_ent_nsout); -+static DEVICE_ATTR_WO(nonce_seed_with_df); -+static DEVICE_ATTR_WO(nonce_seed_direct); -+static DEVICE_ATTR_WO(instantiate); -+static DEVICE_ATTR_WO(uninstantiate); -+static DEVICE_ATTR_WO(reseed); -+static DEVICE_ATTR_WO(generate); -+static DEVICE_ATTR_WO(generate_pub_vtrng); -+static DEVICE_ATTR_WO(kat); ++&i3c4 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06014000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+static DEVICE_ATTR_RW(max_bits_per_req); -+static DEVICE_ATTR_RW(max_req_per_seed); ++&i3c5 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+static DEVICE_ATTR_WO(test_attr); ++&i3c6 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06016000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+static const struct attribute_group nisttrng_attr_group = { -+ .attrs = -+ (struct attribute *[]){ -+ &dev_attr_ckr.attr, -+ //&dev_attr_stepping.attr, -+ &dev_attr_features.attr, &dev_attr_secure.attr, -+ &dev_attr_nonce.attr, &dev_attr_sec_strength.attr, ++&i3c7 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ &dev_attr_mode_reg.attr, &dev_attr_smode_reg.attr, -+ &dev_attr_alarm_reg.attr, &dev_attr_rand_reg.attr, -+ &dev_attr_rand_out.attr, &dev_attr_rand_out_vtrng.attr, -+ &dev_attr_seed_reg.attr, &dev_attr_npa_data_reg.attr, -+ &dev_attr_ctrl_reg.attr, &dev_attr_istat_reg.attr, -+ &dev_attr_stat_reg.attr, &dev_attr_rnc_reg.attr, -+ &dev_attr_rbc_reg.attr, ++&i3c8 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06018000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ &dev_attr_ia_wdata_reg.attr, -+ &dev_attr_ia_rdata_reg.attr, &dev_attr_ia_addr_reg.attr, -+ &dev_attr_ia_cmd_reg.attr, &dev_attr_hw_state.attr, ++&i3c9 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ &dev_attr_collect_ent.attr, -+ &dev_attr_collect_ent_nsout.attr, -+ &dev_attr_nonce_seed_with_df.attr, -+ &dev_attr_nonce_seed_direct.attr, -+ &dev_attr_instantiate.attr, -+ &dev_attr_uninstantiate.attr, &dev_attr_reseed.attr, -+ &dev_attr_generate.attr, -+ &dev_attr_generate_pub_vtrng.attr, &dev_attr_kat.attr, ++&i3c10 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601A000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ &dev_attr_max_bits_per_req.attr, -+ &dev_attr_max_req_per_seed.attr, ++&i3c11 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ &dev_attr_test_attr.attr, NULL }, ++&i3c12 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601C000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; +}; + -+static int nisttrng_self_test(struct nist_trng_state *nist_trng) -+{ -+ u32 seed[16], out[4], x, y; ++&i3c13 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ static const u32 exp128[10][4] = { -+ { 0x5db79bb2, 0xc3a0df1e, 0x099482b6, -+ 0xc319981e }, // The 1st generated output -+ { 0xb344d301, 0xdbd97ca0, 0x6e66e668, -+ 0x0bcd4625 }, // The 2nd generate output -+ { 0xec553f18, 0xa0e5c3cb, 0x752c03c2, -+ 0x5e7b04f7 }, // The 3rd generate output -+ { 0xcfe23e6e, 0x5302edc2, 0xdbf7b05b, -+ 0x2c817c0f }, // The 4th generate output -+ { 0xbd5a8726, 0x028c43d0, 0xb77ac4e3, -+ 0x0844ba2c }, // The 5th generate output -+ { 0xa63b4c0e, 0x8d11d0ba, 0x08b5a10f, -+ 0xab731aff }, // The 6th generate output -+ { 0xb7b56a2f, 0x1d84d1f0, 0xe48d1a0a, -+ 0x43a010a6 }, // The 7th generate output -+ { 0xcf66439d, 0xc937451d, 0x75c34d20, -+ 0x21a21398 }, // The 8th generate output -+ { 0xcb6f0a57, 0x5ff34705, 0x08838e49, -+ 0x21137614 }, // The 9th generate output -+ { 0x61c48b24, 0x25c18d29, 0xc6005e4e, -+ 0xae3b0389 }, // The 10th generate output ++&i3c14 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601E000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; ++ ++&i3c15 { ++ initial-role = "primary"; ++ status = "okay"; ++}; ++ ++&uart12 { ++ status = "okay"; ++}; ++ ++#if 0 ++&vuart0 { ++ virtual; ++ port = <0x3f8>; ++ sirq = <4>; ++ sirq-polarity = <0>; ++ status = "okay"; ++}; ++#endif ++ ++&fmc { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_fwspi_quad_default>; ++ pinctrl-names = "default"; ++ ++ flash@0 { ++ status = "okay"; ++ m25p,fast-read; ++ label = "bmc"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <4>; ++ spi-rx-bus-width = <4>; ++#include "aspeed-evb-flash-layout-128.dtsi" + }; + -+ static const u32 exp256[10][4] = { -+ { 0x1f1a1441, 0xa0865ece, 0x9ff8d5b9, -+ 0x3f78ace6 }, // The 1st generated output -+ { 0xf8190a86, 0x6d6ded2a, 0xc4d0e9bf, -+ 0x24dab55c }, // The 2nd generate output -+ { 0xd3948b74, 0x3dfea516, 0x9c3b86a2, -+ 0xeb184b41 }, // The 3rd generate output -+ { 0x2eb82ab6, 0x2aceefda, 0xc0cf6a5f, -+ 0xa45cb333 }, // The 4th generate output -+ { 0xa49b1c7b, 0x5b51bac7, 0x7586770b, -+ 0x8cb2c392 }, // The 5th generate output -+ { 0x3f3ba09d, 0xa2c9ad29, 0x9687fb8f, -+ 0xa5ae3fd5 }, // The 6th generate output -+ { 0x11dd1076, 0xe37e86cb, 0xced0220a, -+ 0x00448c4f }, // The 7th generate output -+ { 0x955a5e52, 0x84ee38b1, 0xb3271e5f, -+ 0x097751e3 }, // The 8th generate output -+ { 0x5cd73ba8, 0xd8a36a1e, 0xa8a2d7c3, -+ 0xa96de048 }, // The 9th generate output -+ { 0xfb374c63, 0x827b85fa, 0x244e0c7a, -+ 0xa09afd39 }, // The 10th generate output ++ flash@1 { ++ status = "okay"; ++ m25p,fast-read; ++ label = "fmc0:1"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <4>; ++ spi-rx-bus-width = <4>; + }; + -+ int ret, enable, rate, urun; -+ u32 tmp; ++ flash@2 { ++ status = "disabled"; ++ m25p,fast-read; ++ label = "fmc0:2"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <4>; ++ spi-rx-bus-width = <4>; ++ }; ++}; + -+ for (x = 0; x < 16; x++) -+ seed[x] = 0x12345679 * (x + 1); ++&spi0 { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_spi0_default &pinctrl_spi0_cs1_default>; ++ pinctrl-names = "default"; + -+ DEBUG("Doing a self-test with security strength of 128\n"); -+ ret = nisttrng_uninstantiate(nist_trng); -+ if (ret && ret != CRYPTO_NOT_INSTANTIATED) -+ goto ERR; ++ flash@0 { ++ status = "okay"; ++ m25p,fast-read; ++ label = "spi0:0"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <2>; ++ spi-rx-bus-width = <2>; ++ }; + -+ //if ((ret = nisttrng_set_secure_mode(nist_trng, 0))) { goto ERR; } -+ ret = nisttrng_set_nonce_mode(nist_trng, 1); -+ if (ret) -+ goto ERR; ++ flash@1 { ++ status = "disabled"; ++ m25p,fast-read; ++ label = "spi0:1"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <2>; ++ spi-rx-bus-width = <2>; ++ }; ++}; + -+ ret = nisttrng_set_sec_strength(nist_trng, 128); -+ if (ret) -+ goto ERR; ++&spi1 { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_spi1_default &pinctrl_spi1_cs1_default>; ++ pinctrl-names = "default"; + -+ ret = nisttrng_get_entropy_input(nist_trng, seed, 0); -+ if (ret) -+ goto ERR; ++ flash@0 { ++ status = "okay"; ++ m25p,fast-read; ++ label = "spi1:0"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <2>; ++ spi-rx-bus-width = <2>; ++ }; + -+ ret = nisttrng_instantiate(nist_trng, 128, 0, NULL); -+ if (ret) -+ goto ERR; ++ flash@1 { ++ status = "disabled"; ++ m25p,fast-read; ++ label = "spi1:1"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <2>; ++ spi-rx-bus-width = <2>; ++ }; ++}; + -+ if (nist_trng->config.build_cfg0.edu_present) { -+ ret = nisttrng_wait_fifo_full(nist_trng); -+ if (ret) -+ goto ERR; -+ } ++#if 1 ++&spi2 { ++ compatible = "aspeed,ast2700-spi-txrx"; ++ pinctrl-0 = <&pinctrl_spi2_default>; ++ pinctrl-names = "default"; ++ status = "okay"; + -+ ret = nisttrng_generate(nist_trng, out, 16, 128, 0, NULL); -+ if (ret) -+ goto ERR; ++ tpm0: tpmdev@0 { ++ compatible = "tcg,tpm_tis-spi"; ++ spi-max-frequency = <25000000>; ++ reg = <0>; ++ status = "okay"; ++ }; ++}; ++#else ++&spi2 { ++ compatible = "aspeed,ast2700-spi"; ++ pinctrl-0 = <&pinctrl_spi2_default &pinctrl_spi2_cs1_default>; ++ pinctrl-names = "default"; ++ status = "okay"; + -+ if (nist_trng->config.features.extra_ps_present) { -+ DEBUG("skip KAT with extra_ps_present\n"); -+ } else { -+ DEBUG("nist_trng: AES-128 Self-test output: "); -+ for (x = 0; x < 4; x++) -+ DEBUG("0x%08lx ", (unsigned long)out[x]); ++ flash@0 { ++ status = "okay"; ++ reg = < 0 >; ++ compatible = "jedec,spi-nor"; ++ m25p,fast-read; ++ label = "spi2:0"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <2>; ++ spi-rx-bus-width = <2>; ++ }; + -+ if (nist_trng->config.build_cfg0.edu_present) { -+ if (nist_trng->config.edu_build_cfg0 -+ .esm_channel) { //if esm_channel is available the first random number goes to esm -+ for (x = 0; x < 4; x++) { -+ if (out[x] != exp128[1][x]) -+ ret = 1; -+ } -+ } -+ } else { -+ for (x = 0; x < 4; x++) { -+ if (out[x] != exp128[0][x]) -+ ret = 1; -+ } -+ } ++ flash@1 { ++ status = "okay"; ++ reg = < 1 >; ++ compatible = "jedec,spi-nor"; ++ m25p,fast-read; ++ label = "spi2:1"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <2>; ++ spi-rx-bus-width = <2>; ++ }; ++}; ++#endif + -+ if (ret) { -+ SYNHW_PRINT("... FAILED comparison\n"); -+ ret = -1; -+ goto ERR; -+ } else { -+ DEBUG("... PASSED\n"); -+ } -+ } ++&can0 { ++ status = "okay"; ++}; + -+ // if edu is available check all the pvtrng's -+ if (nist_trng->config.build_cfg0.edu_present) { -+ for (x = 0; -+ x < nist_trng->config.edu_build_cfg0.public_vtrng_channels; -+ x++) { -+ DEBUG("vtrng %d\n", x); -+ ret = nisttrng_generate_public_vtrng(nist_trng, out, 16, x); -+ if (ret) -+ goto ERR; ++&emmc_controller { ++ status = "okay"; ++ mmc-hs200-1_8v; ++}; + -+ for (y = 0; y < 4; y++) { -+ DEBUG("0x%08lx ", (unsigned long)out[y]); -+ if (out[y] != exp128[x + 2][y]) -+ ret = 1; -+ } -+ if (ret) { -+ SYNHW_PRINT("... FAILED comparison\n"); -+ ret = -1; -+ goto ERR; -+ } else { -+ DEBUG("... PASSED\n"); -+ } -+ } -+ } -+ // if edu is available empty the fifo before creating the new instance with strength of 256 -+ if (nist_trng->config.build_cfg0.edu_present) { -+ nisttrng_rnc(nist_trng, -+ NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_FINISH_TO_IDLE); -+ tmp = NIST_TRNG_REG_ISTAT_DONE; -+ //always clear the busy bit after disabling RNC -+ pdu_io_write32(nist_trng->base + NIST_TRNG_REG_ISTAT, tmp); -+ tmp = pdu_io_read32(nist_trng->base + NIST_TRNG_REG_ISTAT); -+ do { -+ ret = nisttrng_generate_public_vtrng(nist_trng, out, 16, 0); -+ if (ret) -+ goto ERR; -+ -+ tmp = pdu_io_read32(nist_trng->base + -+ NIST_TRNG_EDU_STAT); -+ -+ } while (!NIST_TRNG_EDU_STAT_FIFO_EMPTY(tmp)); -+ } ++&emmc { ++ status = "okay"; ++#if 1 ++ bus-width = <4>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_emmc_default ++ &pinctrl_emmcclk_driving ++ &pinctrl_emmccmd_driving ++ &pinctrl_emmc4bit_driving>; ++#else ++ bus-width = <8>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_emmc_default ++ &pinctrl_emmcg8_default ++ &pinctrl_emmcclk_driving ++ &pinctrl_emmccmd_driving ++ &pinctrl_emmc8bit_driving>; ++#endif + -+ if (nist_trng->config.features.drbg_arch == AES256) { -+ // test AES-256 mode -+ DEBUG("Doing a self-test with security strength of 256\n"); -+ ret = nisttrng_uninstantiate(nist_trng); -+ if (ret && ret != CRYPTO_NOT_INSTANTIATED) -+ goto ERR; ++ non-removable; ++ max-frequency = <200000000>; ++}; + -+ ret = nisttrng_set_nonce_mode(nist_trng, 1); -+ if (ret) -+ goto ERR; ++&ufs_controller { ++ status = "okay"; ++}; + -+ ret = nisttrng_set_sec_strength(nist_trng, 256); -+ if (ret) -+ goto ERR; ++&ufs { ++ status = "okay"; ++ lanes-per-direction = <2>; ++ ref-clk-freq = <26000000>; ++}; + -+ ret = nisttrng_get_entropy_input(nist_trng, seed, 0); -+ if (ret) -+ goto ERR; ++&chassis { ++ status = "okay"; ++}; + -+ ret = nisttrng_instantiate(nist_trng, 256, 0, NULL); -+ if (ret) -+ goto ERR; ++&mdio0 { ++ status = "okay"; ++ #address-cells = <1>; ++ #size-cells = <0>; + -+ ret = nisttrng_generate(nist_trng, out, 16, 256, 0, NULL); -+ if (ret) -+ goto ERR; ++ ethphy0: ethernet-phy@0 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <0>; ++ }; ++}; + -+ if (nist_trng->config.features.extra_ps_present) { -+ DEBUG("skip KAT with extra_ps_present\n"); -+ } else { -+ DEBUG("nist_trng: AES-256 Self-test output: "); -+ for (x = 0; x < 4; x++) -+ DEBUG("0x%08lx ", (unsigned long)out[x]); ++&mdio1 { ++ status = "okay"; ++ #address-cells = <1>; ++ #size-cells = <0>; + -+ for (x = 0; x < 4; x++) { -+ if (out[x] != exp256[0][x]) -+ ret = 1; -+ } -+ if (ret) { -+ SYNHW_PRINT("... FAILED comparison\n"); -+ ret = -1; -+ goto ERR; -+ } else { -+ DEBUG("... PASSED\n"); -+ } -+ } -+ } ++ ethphy1: ethernet-phy@0 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <0>; ++ /* For DDR5 board */ ++ ti,rx-internal-delay = ; ++ ti,tx-internal-delay = ; ++ }; ++}; + -+ // if edu is available check all the pvtrng's -+ if (nist_trng->config.build_cfg0.edu_present) { -+ for (x = 0; -+ x < nist_trng->config.edu_build_cfg0.public_vtrng_channels; -+ x++) { -+ DEBUG("vtrng 256 %d\n", x); -+ ret = nisttrng_generate_public_vtrng(nist_trng, out, 16, x); -+ if (ret) -+ goto ERR; ++&mac0 { ++ status = "okay"; + -+ for (y = 0; y < 4; y++) { -+ DEBUG("0x%08lx ", (unsigned long)out[y]); -+ if (out[y] != exp256[x + 1][y]) -+ ret = 1; -+ } -+ if (ret) { -+ SYNHW_PRINT("... FAILED comparison\n"); -+ ret = -1; -+ goto ERR; -+ } else { -+ DEBUG("... PASSED\n"); -+ } -+ } ++ phy-mode = "rgmii-id"; ++ phy-handle = <ðphy0>; + -+ //Test RBC channels -+ // enable RBC channels with rate of 2 and urun 1 -+ enable = 1; -+ rate = 2; -+ urun = 1; -+ for (x = 0; x < nist_trng->config.edu_build_cfg0.rbc_channels; -+ x++) { -+ ret = nisttrng_rbc(nist_trng, enable, x, rate, urun); -+ if (ret) -+ goto ERR; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_rgmii0_default &pinctrl_rgmii0_driving>; ++}; + -+ tmp = pdu_io_read32(nist_trng->base + -+ NIST_TRNG_EDU_RBC_CTRL); ++&mac1 { ++ status = "okay"; + -+ switch (x) { -+ case 0: -+ if (rate != NISTTRNG_EDU_RBC_CTRL_GET_CH_RATE(tmp, _NIST_TRNG_EDU_RBC_CTRL_CH0_RATE) || -+ urun != NISTTRNG_EDU_RBC_CTRL_GET_CH_URUN_BLANK(tmp, _NIST_TRNG_EDU_RBC_CTRL_CH0_URUN_BLANK)) { -+ goto ERR; -+ } -+ break; -+ case 1: -+ if (rate != NISTTRNG_EDU_RBC_CTRL_GET_CH_RATE(tmp, _NIST_TRNG_EDU_RBC_CTRL_CH1_RATE) || -+ urun != NISTTRNG_EDU_RBC_CTRL_GET_CH_URUN_BLANK(tmp, _NIST_TRNG_EDU_RBC_CTRL_CH1_URUN_BLANK)) { -+ goto ERR; -+ } -+ break; -+ case 2: -+ if (rate != NISTTRNG_EDU_RBC_CTRL_GET_CH_RATE(tmp, _NIST_TRNG_EDU_RBC_CTRL_CH2_RATE) || -+ urun != NISTTRNG_EDU_RBC_CTRL_GET_CH_URUN_BLANK(tmp, _NIST_TRNG_EDU_RBC_CTRL_CH2_URUN_BLANK)) { -+ goto ERR; -+ } -+ break; -+ default: -+ DEBUG("Incorrect rbc_num = %d\n", x); -+ goto ERR; -+ } -+ } -+ DEBUG("RBC test passed\n"); -+ } ++ phy-mode = "rgmii-id"; ++ phy-handle = <ðphy1>; + -+ //IF RNCis not disable, disable it -+ if (pdu_io_read32(nist_trng->base + NIST_TRNG_EDU_RNC_CTRL) != -+ NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_FINISH_TO_IDLE) { -+ nisttrng_rnc(nist_trng, -+ NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_FINISH_TO_IDLE); -+ tmp = NIST_TRNG_REG_ISTAT_DONE; -+ //always clear the busy bit after disabling RNC -+ pdu_io_write32(nist_trng->base + NIST_TRNG_REG_ISTAT, tmp); -+ } ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_rgmii1_default &pinctrl_rgmii1_driving>; ++}; + -+ /* back to the noise mode */ -+ ret = nisttrng_set_nonce_mode(nist_trng, 0); -+ if (ret) -+ goto ERR; ++#if 0 // Default to disable RC & SGMII ++#define PCIE2_RC 1 // 1: RC, 0: SGMII ++#if PCIE2_RC ++&pcie2 { ++ status = "okay"; ++}; ++#else ++&mdio2 { ++ status = "okay"; ++ #address-cells = <1>; ++ #size-cells = <0>; + -+ ret = nisttrng_zeroize(nist_trng); -+ if (ret) -+ goto ERR; -+ERR: -+ return ret; -+} ++ ethphy2: ethernet-phy@0 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <0>; ++ }; ++}; + -+static int nisttrng_driver_probe(struct platform_device *pdev) -+{ -+ struct synopsys_nisttrng_driver *data; -+ struct hwrng *hwrng_driver_info = 0; -+ struct resource *cfg, *irq; -+ u32 *base_addr; -+ int ret; ++&sgmii { ++ status = "okay"; ++}; + -+ // version -+ SYNHW_PRINT("DWC_TRNG_DriverSDK_%s\n", TRNG_VERSION); ++&mac2 { ++ status = "okay"; + -+ cfg = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ phy-mode = "sgmii"; ++ phy-handle = <ðphy2>; ++}; ++#endif ++#endif + -+ if (!cfg || !irq) { -+ SYNHW_PRINT("no memory or IRQ resource\n"); -+ return -ENOMEM; -+ } ++&espi0 { ++ status = "okay"; ++ perif-dma-mode; ++ perif-mmbi-enable; ++ perif-mmbi-src-addr = <0x0 0xa8000000>; ++ perif-mmbi-tgt-memory = <&espi0_mmbi_memory>; ++ perif-mmbi-instance-num = <0x1>; ++ perif-mcyc-enable; ++ perif-mcyc-src-addr = <0x0 0x98000000>; ++ perif-mcyc-size = <0x0 0x10000>; ++ memory-region = <&espi0_mcyc_memory>; ++ perif-rtc-enable; ++ vw-pltrst-monitor; ++ oob-dma-mode; ++ flash-dma-mode; ++#if 0 // eDAF mode: Change 1 to enable MIX mode in Linux, but HW mode in SPL would be overwritten ++ flash-edaf-mode = <0x0>; ++ flash-edaf-tgt-addr = <&edaf0>; ++#endif ++}; + -+ DEBUG("=================================================================\n"); -+ DEBUG("nisttrng_probe: Device at %08lx(%08lx) of size %lu bytes\n", -+ (unsigned long)cfg->start, (unsigned long)cfg->end, -+ (unsigned long)resource_size(cfg)); ++&rtc_over_espi0 { ++ status = "okay"; ++}; + -+ data = devm_kzalloc(&pdev->dev, sizeof(struct synopsys_nisttrng_driver), -+ GFP_KERNEL); -+ if (!data) -+ return -ENOMEM; ++/* Enable UART2 and UART9 for obmc-console. */ ++&uart2 { ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; + -+ platform_set_drvdata(pdev, data); ++&uart9 { ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; + -+ base_addr = pdu_linux_map_regs(&pdev->dev, cfg); -+ if (IS_ERR(base_addr)) { -+ dev_err(&pdev->dev, "unable to remap io mem\n"); -+ return PTR_ERR(base_addr); -+ } ++#if DUAL_NODE ++&espi1 { ++ status = "okay"; ++ perif-dma-mode; ++ perif-mmbi-enable; ++ perif-mmbi-src-addr = <0x0 0xa8000000>; ++ perif-mmbi-tgt-memory = <&espi1_mmbi_memory>; ++ perif-mmbi-instance-num = <0x1>; ++ perif-mcyc-enable; ++ perif-mcyc-src-addr = <0x0 0x98000000>; ++ perif-mcyc-size = <0x0 0x10000>; ++ perif-rtc-enable; ++ vw-pltrst-monitor; ++ oob-dma-mode; ++ flash-dma-mode; ++#if 0 // eDAF mode: Change 1 to enable MIX mode in Linux, but HW mode in SPL would be overwritten ++ flash-edaf-mode = <0x0>; ++ flash-edaf-tgt-addr = <&edaf1>; ++#endif ++}; + -+ ret = nisttrng_init(&data->nisttrng, (u32 *)base_addr); -+ if (ret) { -+ SYNHW_PRINT("NIST_TRNG init failed (%d)\n", ret); -+ devm_kfree(&pdev->dev, data); -+ return ret; -+ } ++&rtc_over_espi1 { ++ status = "okay"; ++}; + -+ /* if max_reads is not 0, change the max_req_per_seed according to max_reads */ -+ if (max_reads) { -+ ret = nisttrng_set_reminder_max_req_per_seed(&data->nisttrng, max_reads); -+ if (ret) { -+ SYNHW_PRINT("NIST_TRNG maximum request-per-seed setup failed (%d)\n", -+ ret); -+ devm_kfree(&pdev->dev, data); -+ return ret; -+ } -+ } ++/* Enable UART7 and UART10 for obmc-console. */ ++&uart7 { ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; + -+ // issue quick self test -+ ret = nisttrng_self_test(&data->nisttrng); -+ if (ret) { -+ devm_kfree(&pdev->dev, data); -+ return -ENOMEM; -+ } ++&uart10 { ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; ++#endif /* DUAL_NODE */ + -+ // ready the device for use -+ ret = nisttrng_instantiate(&data->nisttrng, -+ data->nisttrng.config.features.drbg_arch ? 256 : 128, 1, NULL); -+ if (ret) { -+ SYNHW_PRINT("NIST_TRNG instantiate failed (%d)\n", ret); -+ devm_kfree(&pdev->dev, data); -+ return -ENOMEM; -+ } ++&lpc0_kcs0 { ++ status = "okay"; ++ kcs-io-addr = <0xca0>; ++ kcs-channel = <0>; ++}; + -+ // at this point the device should be ready for a call to gen_random -+ hwrng_driver_info = -+ devm_kzalloc(&pdev->dev, sizeof(struct hwrng), GFP_KERNEL); -+ if (!hwrng_driver_info) { -+ devm_kfree(&pdev->dev, data); -+ return -ENOMEM; -+ } ++&lpc0_kcs1 { ++ status = "okay"; ++ kcs-io-addr = <0xca8>; ++ kcs-channel = <1>; ++}; + -+ hwrng_driver_info->name = devm_kzalloc(&pdev->dev, -+ sizeof(SYNOPSYS_HWRNG_DRIVER_NAME) + 1, GFP_KERNEL); -+ if (!hwrng_driver_info->name) { -+ devm_kfree(&pdev->dev, data); -+ devm_kfree(&pdev->dev, hwrng_driver_info); -+ return -ENOMEM; -+ } ++&lpc0_kcs2 { ++ status = "okay"; ++ kcs-io-addr = <0xca2>; ++ kcs-channel = <2>; ++}; + -+ memset((void *)hwrng_driver_info->name, 0, -+ sizeof(SYNOPSYS_HWRNG_DRIVER_NAME) + 1); -+ strscpy((char *)hwrng_driver_info->name, SYNOPSYS_HWRNG_DRIVER_NAME, -+ sizeof(SYNOPSYS_HWRNG_DRIVER_NAME)); ++&lpc0_kcs3 { ++ status = "okay"; ++ kcs-io-addr = <0xca4>; ++ kcs-channel = <3>; ++}; + -+ hwrng_driver_info->read = &nisttrng_hwrng_driver_read; -+ hwrng_driver_info->data_present = 0; -+ hwrng_driver_info->priv = (unsigned long)pdev; -+ hwrng_driver_info->quality = 1024; ++&lpc0_ibt { ++ status = "okay"; ++}; + -+ data->hwrng_drv = hwrng_driver_info; -+ ret = hwrng_register(hwrng_driver_info); ++&lpc0_mbox { ++ status = "okay"; ++}; + -+ if (ret) { -+ SYNHW_PRINT("unable to load HWRNG driver (error %d)\n", ret); -+ devm_kfree(&pdev->dev, (void *)hwrng_driver_info->name); -+ devm_kfree(&pdev->dev, hwrng_driver_info); -+ devm_kfree(&pdev->dev, data); -+ return ret; -+ } ++#if 1 ++&lpc0_snoop { ++ status = "okay"; ++ snoop-ports = <0x80>, <0x81>; ++}; ++#else ++&lpc0_pcc { ++ status = "okay"; ++ pcc-ports = <0x80>; ++}; ++#endif + -+ ret = sysfs_create_group(&pdev->dev.kobj, &nisttrng_attr_group); -+ if (ret < 0) { -+ SYNHW_PRINT("unable to initialize sysfs group (error %d)\n", -+ ret); -+ hwrng_unregister(hwrng_driver_info); -+ devm_kfree(&pdev->dev, (void *)hwrng_driver_info->name); -+ devm_kfree(&pdev->dev, hwrng_driver_info); -+ devm_kfree(&pdev->dev, data); -+ return ret; -+ } -+ SYNHW_PRINT("SYN NIST_TRNG registering HW_RANDOM\n"); -+ return 0; -+} ++&lpc0_uart_routing { ++ status = "okay"; ++}; + -+static void nisttrng_driver_remove(struct platform_device *pdev) -+{ -+ struct synopsys_nisttrng_driver *data = platform_get_drvdata(pdev); -+ struct hwrng *hwrng_driver_info = (struct hwrng *)data->hwrng_drv; ++&lpc1_kcs0 { ++ status = "okay"; ++ kcs-io-addr = <0xca0>; ++ kcs-channel = <4>; ++}; + -+ SYNHW_PRINT("SYN NIST_TRNG unregistering from HW_RANDOM\n"); -+ hwrng_unregister(hwrng_driver_info); -+ sysfs_remove_group(&pdev->dev.kobj, &nisttrng_attr_group); -+ devm_kfree(&pdev->dev, (void *)hwrng_driver_info->name); -+ devm_kfree(&pdev->dev, hwrng_driver_info); -+ devm_kfree(&pdev->dev, data); -+} ++&lpc1_kcs1 { ++ status = "okay"; ++ kcs-io-addr = <0xca8>; ++ kcs-channel = <5>; ++}; + -+static struct platform_driver s_nisttrng_platform_driver_info = { -+ .probe = nisttrng_driver_probe, -+ .remove = nisttrng_driver_remove, -+ .driver = { -+ .name = "nist_trng", -+ .owner = THIS_MODULE, -+ }, ++&lpc1_kcs2 { ++ status = "okay"; ++ kcs-io-addr = <0xca2>; ++ kcs-channel = <6>; +}; + -+static int __init nisttrng_platform_driver_start(void) -+{ -+ return platform_driver_register(&s_nisttrng_platform_driver_info); -+} ++&lpc1_kcs3 { ++ status = "okay"; ++ kcs-io-addr = <0xca4>; ++ kcs-channel = <7>; ++}; + -+static void __exit nisttrng_platform_driver_end(void) -+{ -+ platform_driver_unregister(&s_nisttrng_platform_driver_info); -+} ++&lpc1_ibt { ++ status = "okay"; ++}; + -+module_init(nisttrng_platform_driver_start); -+module_exit(nisttrng_platform_driver_end); ++&lpc1_mbox { ++ status = "okay"; ++}; + -+module_param(max_reads, ulong, 0); -+MODULE_PARM_DESC(max_reads, "Max # of reads between reseeds (default is 128)"); -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Synopsys, Inc."); -diff --git a/drivers/char/hw_random/dwc/src/trng/trng/nist_trng.c b/drivers/char/hw_random/dwc/src/trng/trng/nist_trng.c ---- a/drivers/char/hw_random/dwc/src/trng/trng/nist_trng.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/trng/nist_trng.c 2025-12-23 10:16:19.525059469 +0000 -@@ -0,0 +1,956 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * This Synopsys software and associated documentation (hereinafter the -+ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless -+ * otherwise expressly agreed to in writing between Synopsys and you. The -+ * Software IS NOT an item of Licensed Software or a Licensed Product under -+ * any End User Software License Agreement or Agreement for Licensed Products -+ * with Synopsys or any supplement thereto. Synopsys is a registered trademark -+ * of Synopsys, Inc. Other names included in the SOFTWARE may be the -+ * trademarks of their respective owners. -+ * -+ * The contents of this file are dual-licensed; you may select either version -+ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license -+ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the -+ * SOFTWARE. The BSD License is copied below. -+ * -+ * BSD-3-Clause License: -+ * Copyright (c) 2012-2016 Synopsys, Inc. and/or its affiliates. -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright notice, -+ * this list of conditions, and the following disclaimer, without -+ * modification. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * 3. The names of the above-listed copyright holders may not be used to -+ * endorse or promote products derived from this software without specific -+ * prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -+ * POSSIBILITY OF SUCH DAMAGE. -+ */ ++#if 1 ++&lpc1_snoop { ++ status = "okay"; ++ snoop-ports = <0x80>, <0x81>; ++}; ++#else ++&lpc1_pcc { ++ status = "okay"; ++ pcc-ports = <0x80>; ++}; ++#endif + -+#include "nisttrng_hw.h" -+#include "nisttrng.h" ++&lpc1_uart_routing { ++ status = "okay"; ++}; + -+/* Initialize the NIST_TRNG state structure */ -+int nisttrng_init(struct nist_trng_state *state, u32 *base) -+{ -+ int err; -+ u32 tmp; ++&video0 { ++ status = "okay"; ++ memory-region = <&video_engine_memory0>; ++}; + -+ DEBUG(">> %s: initialize the NIST_TRNG\n", __func__); ++&video1 { ++ status = "okay"; ++ memory-region = <&video_engine_memory1>; ++}; + -+ memset(state, 0, sizeof(*state)); ++&disp_intf { ++ status = "okay"; ++}; + -+ state->base = base; ++&rtc { ++ status = "okay"; ++}; + -+ /* make sure there is no alarm and the core is not busy */ -+ err = nisttrng_get_alarms(state); -+ if (err) -+ goto ERR; ++&rsss { ++ status = "okay"; ++}; + -+ err = nisttrng_wait_on_busy(state); -+ if (err) -+ goto ERR; ++&ecdsa { ++ status = "okay"; ++}; + -+ /* hardware features*/ -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_FEATURES); ++&hace { ++ status = "okay"; ++}; + -+ state->config.features.drbg_arch = NIST_TRNG_REG_FEATURES_AES_256(tmp); -+ state->config.features.extra_ps_present = -+ NIST_TRNG_REG_FEATURES_EXTRA_PS_PRESENT(tmp); -+ state->config.features.secure_rst_state = -+ NIST_TRNG_REG_FEATURES_SECURE_RST_STATE(tmp); -+ state->config.features.diag_level_basic_trng = -+ NIST_TRNG_REG_FEATURES_DIAG_LEVEL_BASIC_TRNG(tmp); -+ state->config.features.diag_level_stat_hlt = -+ NIST_TRNG_REG_FEATURES_DIAG_LEVEL_ST_HLT(tmp); -+ state->config.features.diag_level_ns = -+ NIST_TRNG_REG_FEATURES_DIAG_LEVEL_NS(tmp); ++#if PCIE0_EP ++&bmc_dev0 { ++ status = "okay"; ++ memory-region = <&bmc_dev0_memory>; ++}; + -+ /* corekit */ -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_COREKIT_REL); -+ state->config.corekit_rel.ext_enum = NIST_TRNG_REG_EXT_ENUM(tmp); -+ state->config.corekit_rel.ext_ver = NIST_TRNG_REG_EXT_VER(tmp); -+ state->config.corekit_rel.rel_num = NIST_TRNG_REG_REL_NUM(tmp); ++&xdma0 { ++ status = "okay"; ++ memory-region = <&xdma_memory0>; ++}; + -+ /* clear registers */ -+ pdu_io_write32(state->base + NIST_TRNG_REG_ALARM, 0xFFFFFFFF); -+ pdu_io_write32(state->base + NIST_TRNG_REG_ISTAT, 0xFFFFFFFF); ++&pcie_vuart0 { ++ port = <0x3f8>; ++ sirq = <4>; ++ sirq-polarity = <0>; + -+ /* setup the NIST_TRNG in secure mode, self seeding mode, with prediction resistance, maximum possible security strength */ -+ /* SMODE */ -+ tmp = 0; -+ tmp = NIST_TRNG_REG_SMODE_SET_SECURE_EN(tmp, 1); -+ tmp = NIST_TRNG_REG_SMODE_SET_NONCE(tmp, 0); -+ tmp = NIST_TRNG_REG_SMODE_SET_MAX_REJECTS(tmp, -+ NIST_TRNG_DFLT_MAX_REJECTS); -+ pdu_io_write32(state->base + NIST_TRNG_REG_SMODE, tmp); -+ state->status.secure_mode = 1; -+ state->status.nonce_mode = 0; -+ /* MODE */ -+ tmp = 0; -+ if (state->config.features.drbg_arch == AES256) { -+ tmp = NIST_TRNG_REG_MODE_SET_SEC_ALG(tmp, 1); -+ state->status.sec_strength = SEC_STRNT_AES256; ++ status = "okay"; ++}; + -+ } else if (state->config.features.drbg_arch == AES128) { -+ tmp = NIST_TRNG_REG_MODE_SET_SEC_ALG(tmp, 0); -+ state->status.sec_strength = SEC_STRNT_AES128; ++&pcie_vuart1 { ++ port = <0x2f8>; ++ sirq = <3>; ++ sirq-polarity = <0>; + -+ } else { -+ SYNHW_PRINT("Invalid DRBG architecture"); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++ status = "okay"; ++}; + -+ tmp = NIST_TRNG_REG_MODE_SET_PRED_RESIST(tmp, 1); -+ pdu_io_write32(state->base + NIST_TRNG_REG_MODE, 0); -+ state->status.pred_resist = 1; -+ /* rest of the status */ -+ state->status.alarm_code = 0; -+ state->status.pad_ps_addin = 0; ++&pcie_lpc0_kcs0 { ++ status = "okay"; ++ kcs-io-addr = <0x3a0>; ++ kcs-channel = <8>; ++}; + -+ /* reminders - set the counters to the standard's maximum values. An API is be provided to change those on demand.*/ -+ nisttrng_set_reminder_max_bits_per_req(state, -+ NIST_DFLT_MAX_BITS_PER_REQ); -+ nisttrng_set_reminder_max_req_per_seed(state, -+ NIST_DFLT_MAX_REQ_PER_SEED); ++&pcie_lpc0_kcs1 { ++ status = "okay"; ++ kcs-io-addr = <0x3a8>; ++ kcs-channel = <9>; ++}; + -+ /* display features */ -+ SYNHW_PRINT("NIST_TRNG: Hardware rel_num=0x%x, ext_ver=0x%x, ext_enum=0x%x\n", -+ state->config.corekit_rel.rel_num, -+ state->config.corekit_rel.ext_ver, -+ state->config.corekit_rel.ext_enum); -+ switch (state->config.features.drbg_arch) { -+ case AES128: -+ DEBUG("NIST_TRNG: DRBG Architecture=128-bit AES, Extra Personalization Existence=%u\n", -+ state->config.features.extra_ps_present); -+ break; -+ case AES256: -+ DEBUG("NIST_TRNG: DRBG Architecture=256-bit AES, Extra Personalization Existence=%u\n", -+ state->config.features.extra_ps_present); -+ break; -+ default: -+ SYNHW_PRINT("Invalid DRBG architecture"); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++&pcie_lpc0_kcs2 { ++ status = "okay"; ++ kcs-io-addr = <0x3a2>; ++ kcs-channel = <10>; ++}; + -+ DEBUG("initialization is done, going for a zeroize\n"); ++&pcie_lpc0_kcs3 { ++ status = "okay"; ++ kcs-io-addr = <0x3a4>; ++ kcs-channel = <11>; ++}; + -+ // BUILD_CFG0 -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_BUILD_CFG0); -+ state->config.build_cfg0.core_type = NIST_TRNG_REG_CFG0_CORE_TYPE(tmp); -+ state->config.build_cfg0.bg8 = NIST_TRNG_REG_CFG0_BG8(tmp); -+ state->config.build_cfg0.cdc_synch_depth = -+ NIST_TRNG_REG_CFG0_CDC_SYNCH_DEPTH(tmp); -+ state->config.build_cfg0.background_noise = -+ NIST_TRNG_REG_CFG0_BACGROUND_NOISE(tmp); -+ state->config.build_cfg0.edu_present = -+ NIST_TRNG_REG_CFG0_EDU_PRESENT(tmp); -+ state->config.build_cfg0.aes_datapath = -+ NIST_TRNG_REG_CFG0_AES_DATAPATH(tmp); -+ state->config.build_cfg0.aes_max_key_size = -+ NIST_TRNG_REG_CFG0_AES_MAX_KEY_SIZE(tmp); -+ state->config.build_cfg0.personilzation_str = -+ NIST_TRNG_REG_CFG0_PERSONILIZATION_STR(tmp); -+ DEBUG("NIST_TRNG: BUILD_CFG0 core_type=%u, bg8=%u, cdc_synch_depth=%u, background_noise=%u\n", -+ state->config.build_cfg0.core_type, state->config.build_cfg0.bg8, -+ state->config.build_cfg0.cdc_synch_depth, -+ state->config.build_cfg0.background_noise); -+ DEBUG("edu_present=%u, aes_datapath=%u, aes_max_key_size=%u, personilzation_str=%u\n", -+ state->config.build_cfg0.edu_present, -+ state->config.build_cfg0.aes_datapath, -+ state->config.build_cfg0.aes_max_key_size, -+ state->config.build_cfg0.personilzation_str); ++&pcie_lpc0_ibt { ++ status = "okay"; ++ bt-channel = <2>; ++}; + -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_BUILD_CFG1); -+ DEBUG("NIST_TRNG: NIST_TRNG_REG_BUILD_CFG1=0x%x\n", tmp); -+ state->config.build_cfg1.num_raw_noise_blks = -+ NIST_TRNG_REG_CFG1_NUM_RAW_NOISE_BLKS(tmp); -+ state->config.build_cfg1.sticky_startup = -+ NIST_TRNG_REG_CFG1_STICKY_STARTUP(tmp); -+ state->config.build_cfg1.auto_correlation_test = -+ NIST_TRNG_REG_CFG1_AUTO_CORRELATION_TEST(tmp); -+ state->config.build_cfg1.mono_bit_test = -+ NIST_TRNG_REG_CFG1_MONO_BIT_TEST(tmp); -+ state->config.build_cfg1.run_test = NIST_TRNG_REG_CFG1_RUN_TEST(tmp); -+ state->config.build_cfg1.poker_test = -+ NIST_TRNG_REG_CFG1_POKER_TEST(tmp); -+ state->config.build_cfg1.raw_ht_adap_test = -+ NIST_TRNG_REG_CFG1_RAW_HT_ADAP_TEST(tmp); -+ state->config.build_cfg1.raw_ht_rep_test = -+ NIST_TRNG_REG_CFG1_RAW_HT_REP_TEST(tmp); -+ state->config.build_cfg1.ent_src_rep_smpl_size = -+ NIST_TRNG_REG_CFG1_ENT_SRC_REP_SMPL_SIZE(tmp); -+ state->config.build_cfg1.ent_src_rep_test = -+ NIST_TRNG_REG_CFG1_ENT_SRC_REP_TEST(tmp); -+ state->config.build_cfg1.ent_src_rep_min_entropy = -+ NIST_TRNG_REG_CFG1_ENT_SRC_REP_MIN_ENTROPY(tmp); -+ DEBUG("NIST_TRNG: BUILD_CFG1 num_raw_noise_blks=%u, sticky_startup=%u, auto_correlation_test=%u\n", -+ state->config.build_cfg1.num_raw_noise_blks, -+ state->config.build_cfg1.sticky_startup, -+ state->config.build_cfg1.auto_correlation_test); -+ DEBUG("mono_bit_test=%u, run_test=%u, poker_test=%u, raw_ht_adap_test=%u\n", -+ state->config.build_cfg1.mono_bit_test, -+ state->config.build_cfg1.run_test, -+ state->config.build_cfg1.poker_test, -+ state->config.build_cfg1.raw_ht_adap_test); -+ DEBUG("raw_ht_rep_test=%u, ent_src_rep_smpl_size=%u, ent_src_rep_test=%u, ent_src_rep_min_entropy=%u\n", -+ state->config.build_cfg1.raw_ht_rep_test, -+ state->config.build_cfg1.ent_src_rep_smpl_size, -+ state->config.build_cfg1.ent_src_rep_test, -+ state->config.build_cfg1.ent_src_rep_min_entropy); ++&pcie0_mmbi0 { ++ status = "okay"; ++ memory-region = <&pcie0_mmbi0_memory>; + -+ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_BUILD_CFG0); -+ state->config.edu_build_cfg0.rbc2_rate_width = -+ NIST_TRNG_REG_EDU_CFG0_RBC2_RATE_WIDTH(tmp); -+ state->config.edu_build_cfg0.rbc1_rate_width = -+ NIST_TRNG_REG_EDU_CFG0_RBC1_RATE_WIDTH(tmp); -+ state->config.edu_build_cfg0.rbc0_rate_width = -+ NIST_TRNG_REG_EDU_CFG0_RBC0_RATE_WIDTH(tmp); -+ state->config.edu_build_cfg0.public_vtrng_channels = -+ NIST_TRNG_REG_EDU_CFG0_PUBLIC_VTRNG_CHANNELS(tmp); -+ state->config.edu_build_cfg0.esm_channel = -+ NIST_TRNG_REG_EDU_CFG0_ESM_CHANNEL(tmp); -+ state->config.edu_build_cfg0.rbc_channels = -+ NIST_TRNG_REG_EDU_CFG0_RBC_CHANNELS(tmp); -+ state->config.edu_build_cfg0.fifo_depth = -+ NIST_TRNG_REG_EDU_CFG0_FIFO_DEPTH(tmp); -+ DEBUG("NIST_TRNG: EDU_BUILD_CFG0 rbc2_rate_width=%u, rbc1_rate_width=%u, rbc0_rate_width=%u\n", -+ state->config.edu_build_cfg0.rbc2_rate_width, -+ state->config.edu_build_cfg0.rbc1_rate_width, -+ state->config.edu_build_cfg0.rbc0_rate_width); -+ DEBUG("public_vtrng_channels=%u, esm_channel=%u, rbc_channels=%u, fifo_depth=%u\n", -+ state->config.edu_build_cfg0.public_vtrng_channels, -+ state->config.edu_build_cfg0.esm_channel, -+ state->config.edu_build_cfg0.rbc_channels, -+ state->config.edu_build_cfg0.fifo_depth); ++ bmc-int-value = /bits/ 8 <0x00>; ++ bmc-int-location = <0>; ++}; ++#else /* !PCIE0_EP */ ++&pcie0 { ++ status = "okay"; ++}; ++#endif /* PCIE0_EP */ + -+ state->status.edu_vstat.seed_enum = -+ NIST_TRNG_REG_EDU_VSTAT_SEED_ENUM(tmp); -+ state->status.edu_vstat.rnc_enabled = -+ NIST_TRNG_REG_EDU_VSTAT_RNC_ENABLED(tmp); ++#if PCIE1_EP ++&bmc_dev1 { ++ status = "okay"; ++ memory-region = <&bmc_dev1_memory>; ++}; + -+ err = nisttrng_zeroize(state); -+ if (err) -+ goto ERR; ++&xdma1 { ++ status = "okay"; ++ memory-region = <&xdma_memory1>; ++}; + -+ err = CRYPTO_OK; -+ state->status.current_state = NIST_TRNG_STATE_INITIALIZE; -+ERR: -+ DEBUG("--- %s Return, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_init */ -+EXPORT_SYMBOL(nisttrng_init); ++&pcie_vuart2 { ++ port = <0x3f8>; ++ sirq = <4>; ++ sirq-polarity = <0>; + -+/* Instantiate the DRBG state */ -+int nisttrng_instantiate(struct nist_trng_state *state, int req_sec_strength, -+ int pred_resist, void *personal_str) -+{ -+ int err; -+ u32 tmp; -+ u32 zero_ps[12] = { 0 }; -+ int i = 0; ++ status = "okay"; ++}; + -+ DEBUG(">> %s: security strength = %u, pred_resist = %u, personilization string existence = %u\n", -+ __func__, req_sec_strength, pred_resist, (personal_str) ? 1 : 0); ++&pcie_vuart3 { ++ port = <0x2f8>; ++ sirq = <3>; ++ sirq-polarity = <0>; + -+ /* make sure there is no alarm and the core is not busy */ -+ err = nisttrng_get_alarms(state); -+ if (err) -+ goto ERR; ++ status = "okay"; ++}; + -+ err = nisttrng_wait_on_busy(state); -+ if (err) -+ goto ERR; ++&pcie_lpc1_kcs0 { ++ status = "okay"; ++ kcs-io-addr = <0x3a0>; ++ kcs-channel = <12>; ++}; + -+ /* If DRBG is already instantiated or if current state does not allow an instantiate, return error */ -+ if (DRBG_INSTANTIATED(state->status.current_state)) { -+ DEBUG("Initial check: DRBG state is already instantiated\n"); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } -+ if (state->status.current_state != NIST_TRNG_STATE_INITIALIZE && -+ state->status.current_state != NIST_TRNG_STATE_UNINSTANTIATE) { -+ DEBUG("Cannot instantiate in the current state (%u)\n", -+ state->status.current_state); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++&pcie_lpc1_kcs1 { ++ status = "okay"; ++ kcs-io-addr = <0x3a8>; ++ kcs-channel = <13>; ++}; + -+ /* if hardware is not configured to accept extra personalization string, but personal_str is not NULL, return error */ -+ if (!state->config.features.extra_ps_present && personal_str) { -+ DEBUG("HW config does not allow extra PS\n"); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++&pcie_lpc1_kcs2 { ++ status = "okay"; ++ kcs-io-addr = <0x3a2>; ++ kcs-channel = <14>; ++}; + -+ /* Validate and set the security strength */ -+ err = nisttrng_set_sec_strength(state, req_sec_strength); -+ if (err) -+ goto ERR; ++&pcie_lpc1_kcs3 { ++ status = "okay"; ++ kcs-io-addr = <0x3a4>; ++ kcs-channel = <15>; ++}; + -+ /* get entropy - noise seeding. If the mode is nonce, get_entropy must be called by the user prior to the instantiate function */ -+ DEBUG("Seeding mode is: %s\n", -+ state->status.nonce_mode ? "Nonce" : "Noise"); -+ if (!state->status.nonce_mode) { /* noise seeding */ -+ err = nisttrng_get_entropy_input(state, NULL, 0); -+ if (err) -+ goto ERR; -+ } ++&pcie_lpc1_ibt { ++ status = "okay"; ++ bt-channel = <3>; ++}; + -+ /* load the personilization string if hardware is configured to accept it */ -+ if (state->config.features.extra_ps_present) { -+ /* if HW is configured to accept personilizatoin string, it will use whatever is in the NPA_DATAx. So, if the string is NULL, just load 0. */ -+ if (!personal_str) -+ personal_str = &zero_ps[0]; ++&pcie1_mmbi4 { ++ status = "okay"; ++ memory-region = <&pcie1_mmbi4_memory>; + -+ err = nisttrng_load_ps_addin(state, personal_str); -+ if (err) -+ goto ERR; -+ } ++ bmc-int-value = /bits/ 8 <0x00>; ++ bmc-int-location = <0>; ++}; ++#else /* !PCIE1_EP */ ++&pcie1 { ++ status = "okay"; ++}; ++#endif /* PCIE1_EP */ + -+ /* initiate the Create_State command and wait on done */ -+ DEBUG("Create the DRBG state\n"); ++&sdio_controller { ++ status = "okay"; ++ mmc-hs200-1_8v; + -+ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, -+ NIST_TRNG_REG_CTRL_CMD_CREATE_STATE); -+ err = nisttrng_wait_on_done(state); -+ if (err) -+ goto ERR; ++ vcc_sdhci0: regulator-vcc-sdhci0 { ++ compatible = "regulator-fixed"; ++ regulator-name = "SDHCI0 Vcc"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ gpios = <&gpio1 ASPEED_GPIO(G, 6) GPIO_ACTIVE_HIGH>; ++ enable-active-high; ++ }; + -+ /* check STAT register to make sure DRBG is instantiated */ -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_STAT); -+ if (!NIST_TRNG_REG_STAT_GET_DRBG_STATE(tmp)) { -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++ vccq_sdhci0: regulator-vccq-sdhci0 { ++ compatible = "regulator-gpio"; ++ regulator-name = "SDHCI0 VccQ"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ gpios = <&gpio1 ASPEED_GPIO(G, 7) GPIO_ACTIVE_HIGH>; ++ gpios-states = <1>; ++ states = <3300000 1>, ++ <1800000 0>; ++ }; ++}; + -+ /* reset reminder and alarms counters */ -+ nisttrng_reset_counters(state); ++&sdhci { ++ status = "okay"; ++ bus-width = <4>; ++ max-frequency = <125000000>; ++ /* DDR50 bits in CAPA2 are not supported */ ++ sdhci-caps-mask = <0x6 0x0>; ++ sdhci-drive-type = /bits/ 8 <3>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_sd_default>; ++ vmmc-supply = <&vcc_sdhci0>; ++ vqmmc-supply = <&vccq_sdhci0>; ++ sd-uhs-sdr104; /* enable sdr104 to execute tuning */ ++}; + -+ //if EDU is available enable RNC and disable prediction resistance , disable all RBC,s -+ //state->config.build_cfg0.edu_present = 0; -+ if (state->config.build_cfg0.edu_present) { -+ //disable prediction resistance -+ err = nisttrng_set_pred_resist(state, 0); -+ if (err) -+ goto ERR; ++#if 1 ++&i2c0 { ++ status = "okay"; ++}; + -+ //enable RNC -+ nisttrng_rnc(state, NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_ENABLE); -+ // disable all RBC,s ++&i2c1 { ++ status = "okay"; ++}; + -+ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_RBC_CTRL); -+ for (i = 0; i < state->config.edu_build_cfg0.rbc_channels; -+ i++) { -+ err = nisttrng_rbc(state, 0, i, 0, -+ CHX_URUN_BLANK_AFTER_RESET); -+ if (err) -+ goto ERR; -+ } -+ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_RBC_CTRL); ++&i2c2 { ++ status = "okay"; ++}; + -+ } else { -+ /* set the prediction resistance */ -+ err = nisttrng_set_pred_resist(state, pred_resist); -+ if (err) -+ goto ERR; -+ } ++&i2c3 { ++ status = "okay"; ++}; + -+ err = CRYPTO_OK; -+ state->status.current_state = NIST_TRNG_STATE_INSTANTIATE; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_instantiate */ -+EXPORT_SYMBOL(nisttrng_instantiate); ++&i2c4 { ++ status = "okay"; ++}; + -+/* Uninstantiate the DRBG state and zeroize */ -+int nisttrng_uninstantiate(struct nist_trng_state *state) -+{ -+ int err; -+ int err_tmp; -+ u32 tmp; ++&i2c5 { ++ status = "okay"; ++}; + -+ DEBUG(">> %s: uninstantiate the DRBG and zeroize\n", __func__); -+ //printf(" nisttrng_uninstantiate: uninstantiate the DRBG and zeroize\n"); -+ err = CRYPTO_OK; -+ err_tmp = CRYPTO_OK; ++&i2c6 { ++ status = "okay"; ++}; + -+ //disable RNC -+ if (state->config.build_cfg0.edu_present) { -+ if (state->status.edu_vstat.rnc_enabled) { -+ DEBUG("%s: disable RNC\n", __func__); -+ nisttrng_rnc(state, NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_FINISH_TO_IDLE); -+ //always clear the busy bit after disabling RNC -+ pdu_io_write32(state->base + NIST_TRNG_REG_ISTAT, tmp); -+ } -+ } ++&i2c7 { ++ status = "okay"; ++}; + -+ /* if DRBG is instantiated, return CRYPTO_NOT_INSTANTIATED, but still do the zeroize */ -+ if (!DRBG_INSTANTIATED(state->status.current_state)) -+ err_tmp = CRYPTO_NOT_INSTANTIATED; ++&i2c8 { ++ status = "okay"; ++}; + -+ /* zeroize */ -+ err = nisttrng_zeroize(state); -+ if (err) -+ goto ERR; ++&i2c11 { ++ status = "okay"; ++}; + -+ if (err == CRYPTO_OK && err_tmp == CRYPTO_NOT_INSTANTIATED) -+ err = CRYPTO_NOT_INSTANTIATED; ++&i2c12 { ++ status = "okay"; ++}; + -+ state->status.current_state = NIST_TRNG_STATE_UNINSTANTIATE; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_uninstantiate */ -+EXPORT_SYMBOL(nisttrng_uninstantiate); ++&i2c13 { ++ status = "okay"; ++}; ++#endif + -+/* enable/disable specific rbc -+ * rbc_num = rbc channel num -+ * urun_blnk = underrun blanking duration for rbc channel -+ * rate = sets rate of serial entropy output for rbc channel -+ */ -+int nisttrng_rbc(struct nist_trng_state *state, int enable, int rbc_num, int rate, -+ int urun_blnk) -+{ -+ int err = 0; -+ u32 tmp_rbc = 0; ++#if 0 ++&ehci0 { ++ status = "okay"; ++}; + -+ tmp_rbc = pdu_io_read32(state->base + NIST_TRNG_EDU_RBC_CTRL); ++&ehci1 { ++ status = "okay"; ++}; + -+ if (enable) { -+ if (rate > 15) { -+ DEBUG("Incorrect rate = %d\n", rate); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } -+ if (urun_blnk > 3) { -+ DEBUG("Incorrect urun_blnk = %d\n", urun_blnk); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } -+ } else { //disable -+ rate = NISTTRNG_EDU_RBC_CTRL_GET_CH_RATE_AFTER_RESET; -+ urun_blnk = NISTTRNG_EDU_RBC_CTRL_SET_CH_URUN_BLANK_AFTER_RESET; -+ } ++&uhci0 { ++ status = "okay"; ++ memory-region = <&uhci0_reserved>; ++}; + -+ switch (rbc_num) { -+ case 0: -+ tmp_rbc = NISTTRNG_EDU_RBC_CTRL_SET_CH_RATE(rate, tmp_rbc, _NIST_TRNG_EDU_RBC_CTRL_CH0_RATE); -+ tmp_rbc = NISTTRNG_EDU_RBC_CTRL_SET_CH_URUN_BLANK(urun_blnk, tmp_rbc, -+ _NIST_TRNG_EDU_RBC_CTRL_CH0_URUN_BLANK); ++#endif + -+ break; -+ case 1: -+ tmp_rbc = NISTTRNG_EDU_RBC_CTRL_SET_CH_RATE(rate, tmp_rbc, _NIST_TRNG_EDU_RBC_CTRL_CH1_RATE); -+ tmp_rbc = NISTTRNG_EDU_RBC_CTRL_SET_CH_URUN_BLANK(urun_blnk, tmp_rbc, -+ _NIST_TRNG_EDU_RBC_CTRL_CH1_URUN_BLANK); ++#if 1 ++&uphy3a { ++ status = "okay"; ++}; + -+ break; -+ case 2: -+ tmp_rbc = NISTTRNG_EDU_RBC_CTRL_SET_CH_RATE(rate, tmp_rbc, _NIST_TRNG_EDU_RBC_CTRL_CH2_RATE); -+ tmp_rbc = NISTTRNG_EDU_RBC_CTRL_SET_CH_URUN_BLANK(urun_blnk, tmp_rbc, -+ _NIST_TRNG_EDU_RBC_CTRL_CH2_URUN_BLANK); -+ break; -+ default: -+ DEBUG("Incorrect rbc_num = %d\n", rbc_num); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++&uphy3b { ++ status = "okay"; ++}; ++#endif + -+ pdu_io_write32(state->base + NIST_TRNG_EDU_RBC_CTRL, tmp_rbc); ++#if 0 ++&xhci0 { ++ status = "okay"; ++}; + -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} ++&xhci1 { ++ status = "okay"; ++}; ++#endif + -+/* Reseed */ -+int nisttrng_reseed(struct nist_trng_state *state, int pred_resist, void *addin_str) -+{ -+ int rnc_flag = 0; -+ int err; ++&vhuba0 { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_usb2ahpd0_default>; ++}; + -+ DEBUG(">> %s: pred_resist = %u, additional strign existence = %u\n", -+ __func__, pred_resist, (addin_str) ? 1 : 0); ++&usb3ahp { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_usb3axhp_default &pinctrl_usb2axhp_default>; ++}; + -+ if (state->config.build_cfg0.edu_present) { -+ if (state->status.edu_vstat.rnc_enabled) { -+ // disable_rnc -+ err = nisttrng_rnc(state, NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_DISABLE_TO_HOLD); -+ if (err) -+ goto ERR; ++&usb3bhp { ++ status = "okay"; ++}; + -+ rnc_flag = 1; -+ } -+ } ++&uphy2b { ++ status = "okay"; ++}; + -+ /* make sure there is no alarm and the core is not busy */ -+ err = nisttrng_get_alarms(state); -+ if (err) -+ goto ERR; ++&vhubb1 { ++ status = "okay"; ++}; + -+ err = nisttrng_wait_on_busy(state); -+ if (err) -+ goto ERR; ++&vhubc { ++ status = "okay"; ++#if 0 ++ pinctrl-0 = <&pinctrl_usb2cud_default>; ++ aspeed,uart-ports = <12>; ++#endif ++}; + -+ /* if the DRBG is not instantiated return error */ -+ if (!DRBG_INSTANTIATED(state->status.current_state)) { -+ DEBUG("DRBG is not instantiated\n"); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++&ehci3 { ++ status = "okay"; ++}; + -+ /* if pred_resist is set but, pred_resist that the DRBG is instantiated with is not 1, return error */ -+ err = nisttrng_set_pred_resist(state, pred_resist); -+ if (err) -+ goto ERR; ++&uhci1 { ++ status = "okay"; ++ memory-region = <&uhci1_reserved>; ++}; + -+ /* get entropy - noise seeding. If the mode is nonce, get_entropy must be called by the user prior to the instantiate function */ -+ if (!state->status.nonce_mode) { /* noise seeding */ -+ err = nisttrng_get_entropy_input(state, NULL, 0); -+ if (err) -+ goto ERR; -+ } ++&wdt0 { ++ status = "okay"; ++}; + -+ /* if addin_str is not NULL, it means that the additionl input is available and has to be loaded */ -+ if (addin_str) { -+ /* set the ADDIN_PRESENT field of the MODE register to 1 */ -+ err = nisttrng_set_addin_present(state, 1); -+ if (err) -+ goto ERR; ++&wdt1 { ++ status = "okay"; ++}; + -+ /* load the additional input */ -+ err = nisttrng_load_ps_addin(state, addin_str); -+ if (err) -+ goto ERR; ++&otp { ++ status = "okay"; ++}; + -+ } else { -+ /* set the ADDIN_PRESENT field of the MODE register to 0 */ -+ err = nisttrng_set_addin_present(state, 0); -+ if (err) -+ goto ERR; -+ } ++#if 0 ++&soc0 { ++ mbox-ssp-0 { ++ compatible = "aspeed,aspeed-mbox"; ++ reg = <0x4 0x31480000 0x0 0x200000>, <0x0 0x10001000 0x0 0x1000>; ++ mboxes = <&mbox0 0>; ++ aspeed,tx-timeout = <100>; ++ }; ++}; ++#endif + -+ /* initiate the reseed and wait on done */ -+ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, -+ NIST_TRNG_REG_CTRL_CMD_RENEW_STATE); -+ err = nisttrng_wait_on_done(state); -+ if (err) -+ goto ERR; ++#if 1 ++&soc0 { ++ mbox-bootmcu-1 { ++ compatible = "aspeed,aspeed-mbox"; ++ reg = <0x4 0x31880000 0x0 0x100000>, <0x4 0x31980000 0x0 0x100000>; ++ mboxes = <&mbox2 1>; ++ aspeed,tx-timeout = <10000>; ++ }; ++}; ++#endif +diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-ncsi.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-ncsi.dts +--- a/arch/arm64/boot/dts/aspeed/ast2700a1-ncsi.dts 1970-01-01 00:00:00.000000000 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-ncsi.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,48 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later + -+ /* reset reminder and alarms counters */ -+ nisttrng_reset_counters(state); ++/dts-v1/; + -+ if (rnc_flag) { -+ // rnc_enable -+ err = nisttrng_rnc(state, NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_ENABLE); -+ if (err) -+ goto ERR; -+ } ++#include "ast2700a1-evb.dts" + -+ err = CRYPTO_OK; -+ state->status.current_state = NIST_TRNG_STATE_RESEED; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_reseed */ -+EXPORT_SYMBOL(nisttrng_reseed); ++/ { ++ model = "AST2700A1-NCSI"; ++}; + -+static int nisttrng_vtrng_wait_on_busy(struct nist_trng_state *state, int priv, int vtrng) -+{ -+ u32 tmp, t; ++&sgpios { ++ status = "disabled"; ++}; + -+ t = NIST_TRNG_RETRY_MAX; ++&mac0 { ++ status = "okay"; + -+ if (priv) { //private vtrng -+ do { -+ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_VSTAT); -+ } while (NIST_TRNG_REG_EDU_VSTAT_BUSY(tmp) && --t); ++ phy-mode = "rmii"; ++ use-ncsi; + -+ } else { //public vtrng -+ do { -+ tmp = pdu_io_read32(state->base + -+ NIST_TRNG_EDU_VTRNG_VSTAT0 + -+ 8 * vtrng); -+ } while (NIST_TRNG_REG_EDU_VSTAT_BUSY(tmp) && --t); -+ } ++ pinctrl-names = "default"; ++ /* If you want to use RMII0 RCLKO as internal clock for RMII, ++ * add &pinctrl_rmii0_rclko_default in pinctrl-0. ++ */ ++ pinctrl-0 = <&pinctrl_rmii0_default>; ++}; + -+ if (t) -+ return CRYPTO_OK; ++&mac1 { ++ status = "okay"; + -+ SYNHW_PRINT("wait_on_: failed timeout: %08lx\n", -+ (unsigned long)tmp); ++ phy-mode = "rmii"; ++ use-ncsi; + -+ return CRYPTO_TIMEOUT; -+} /* nisttrng_vtrng_wait_on_busy */ ++ pinctrl-names = "default"; ++ /* If you want to use RMII1 RCLKO as internal clock for RMII, ++ * add &pinctrl_rmii1_rclko_default in pinctrl-0. ++ */ ++ pinctrl-0 = <&pinctrl_rmii1_default>; ++}; + -+int nisttrng_generate_public_vtrng(struct nist_trng_state *state, void *random_bits, -+ unsigned long req_num_bytes, int vtrng) -+{ -+ int err = 0; -+ u32 tmp; -+ unsigned int remained_bytes; -+ unsigned long req_num_blks; -+ int i, j; ++&syscon1 { ++ mac0-clk-delay = <0 0 ++ 0 0 ++ 0 0>; ++ mac1-clk-delay = <0 0 ++ 0 0 ++ 0 0>; ++}; +diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-raw.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-raw.dts +--- a/arch/arm64/boot/dts/aspeed/ast2700a1-raw.dts 1970-01-01 00:00:00.000000000 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-raw.dts 2026-04-08 18:03:31.588010588 +0000 +@@ -0,0 +1,220 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later + -+ DEBUG(">> %s : requested number of bytes = %lu, vtrng num = %u\n", -+ __func__, req_num_bytes, vtrng); ++/dts-v1/; + -+ /* make sure random_bits is not NULL */ -+ if (!random_bits) { -+ DEBUG("random_bits pointer cannot be NULL\n"); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++#include "ast2700a1-evb.dts" + -+ if (vtrng > state->config.edu_build_cfg0.public_vtrng_channels) { -+ DEBUG("vtrng channel invalid (%u)\n", vtrng); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++&fmc { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_fwspi_quad_default>; ++ pinctrl-names = "default"; + -+ if (state->status.edu_vstat.rnc_enabled == 0) { -+ DEBUG("rnc_disabled\n"); -+ } ++ flash@0 { ++ status = "okay"; ++ m25p,fast-read; ++ label = "bmc"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <2>; ++ spi-rx-bus-width = <2>; ++#include "aspeed-evb-flash-layout-128.dtsi" ++ }; + -+ if (state->status.edu_vstat.seed_enum == 0) { -+ DEBUG("not seed_enum\n"); -+ } ++ flash@1 { ++ status = "okay"; ++ m25p,fast-read; ++ label = "fmc0:1"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <2>; ++ spi-rx-bus-width = <2>; ++ }; + -+ /* loop on generate to get the requested number of bits. Each generate gives NIST_TRNG_RAND_BLK_SIZE_BITS bits. */ -+ req_num_blks = ((req_num_bytes * 8) % NIST_TRNG_RAND_BLK_SIZE_BITS) ? -+ (((req_num_bytes * 8) / NIST_TRNG_RAND_BLK_SIZE_BITS) + 1) : -+ ((req_num_bytes * 8) / NIST_TRNG_RAND_BLK_SIZE_BITS); ++ flash@2 { ++ status = "disabled"; ++ m25p,fast-read; ++ label = "fmc0:2"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <2>; ++ spi-rx-bus-width = <2>; ++ }; ++}; + -+ for (i = 0; i < req_num_blks; i++) { -+ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_VTRNG_VCTRL0 + -+ (vtrng * 8)); -+ tmp = NIST_TRNG_EDU_VTRNG_VCTRL_CMD_SET(tmp, NIST_TRNG_EDU_VTRNG_VCTRL_CMD_GET_RANDOM); -+ pdu_io_write32(state->base + NIST_TRNG_EDU_VTRNG_VCTRL0 + (vtrng * 8), -+ tmp); ++&spi0 { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_spi0_default &pinctrl_spi0_cs1_default>; ++ pinctrl-names = "default"; + -+ // check busy -+ err = nisttrng_vtrng_wait_on_busy(state, 0, vtrng); -+ if (err) -+ goto ERR; ++ flash@0 { ++ status = "okay"; ++ m25p,fast-read; ++ label = "spi0:0"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <2>; ++ spi-rx-bus-width = <2>; ++ }; + -+ // check for error -+ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_VTRNG_VISTAT0 + -+ (vtrng * 8)); -+ if (NIST_TRNG_REG_EDU_VSTAT_ANY_RW1(tmp)) { -+ DEBUG("EDU_VSTAT_ANY_RW1 set 0x%x\n", tmp); -+ } ++ flash@1 { ++ status = "disabled"; ++ m25p,fast-read; ++ label = "spi0:1"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <2>; ++ spi-rx-bus-width = <2>; ++ }; ++}; + -+ // check that all valid -+ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_VTRNG_VSTAT0 + -+ 8 * vtrng); -+ if ((NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD0(tmp) == 0) || -+ (NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD1(tmp) == 0) || -+ (NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD2(tmp) == 0) || -+ (NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD3(tmp) == 0)) { -+ DEBUG("EDU_VSTAT_SLICE_VLD fail 0x%x\n", tmp); -+ } ++&spi1 { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_spi1_default &pinctrl_spi1_cs1_default>; ++ pinctrl-names = "default"; + -+ /* read the generated random number block and store */ -+ for (j = 0; j < (NIST_TRNG_RAND_BLK_SIZE_BITS / 32); j++) { -+ tmp = pdu_io_read32(state->base + -+ NIST_TRNG_EDU_VTRNG_VRAND0_0 + -+ (vtrng * 8) + j); -+ /* copy to random_bits byte-by-byte, until req_num_bytes are copied */ -+ remained_bytes = req_num_bytes - -+ (i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + -+ j * 4); -+ if (remained_bytes > 4) { -+ memcpy(random_bits + i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + -+ j * 4, &tmp, 4); ++ flash@0 { ++ status = "okay"; ++ m25p,fast-read; ++ label = "spi1:0"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <2>; ++ spi-rx-bus-width = <2>; ++ }; + -+ /* decrement the bits counter and return error if generated more than the maximum*/ -+ state->counters.bits_per_req_left = -+ state->counters.bits_per_req_left - -+ 4 * 8; -+ if (state->counters.bits_per_req_left < 0) { -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } -+ } else { -+ memcpy(random_bits + i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + -+ j * 4, &tmp, remained_bytes); ++ flash@1 { ++ status = "disabled"; ++ m25p,fast-read; ++ label = "spi1:1"; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <2>; ++ spi-rx-bus-width = <2>; ++ }; ++}; + -+ /* decrement the bits counter and return error if generated more than the maximum*/ -+ state->counters.bits_per_req_left = -+ state->counters.bits_per_req_left - -+ remained_bytes * 8; -+ if (state->counters.bits_per_req_left < 0) { -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } -+ break; -+ } -+ } -+ } ++&spi2 { ++ status = "disabled"; ++}; + -+ err = CRYPTO_OK; -+ state->status.current_state = NIST_TRNG_STATE_GENERATE; -+ERR: -+ if (err) -+ random_bits = NULL; ++&emmc_controller { ++ status = "disabled"; ++}; + -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} ++&emmc { ++ status = "disabled"; ++}; + -+static int nisttrng_generate_private_vtrng(struct nist_trng_state *state, void *random_bits, -+ unsigned long req_num_bytes) -+{ -+ int err; -+ u32 tmp; -+ unsigned int remained_bytes; -+ unsigned long req_num_blks; -+ int i, j; ++&ufs_controller { ++ status = "disabled"; ++}; + -+ DEBUG(">> %s : requested number of bytes = %lu ", -+ __func__, req_num_bytes); ++&ufs { ++ status = "disabled"; ++}; + -+ /* requested number of bits has to be less that the programmed maximum */ -+ if ((req_num_bytes * 8) > state->counters.max_bits_per_req) { -+ SYNHW_PRINT("requested number of bits (%lu) is larger than the set maximum (%lu)\n", -+ (req_num_bytes * 8), state->counters.max_bits_per_req); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++&video0 { ++ status = "disabled"; ++}; + -+ /* make sure random_bits is not NULL */ -+ if (!random_bits) { -+ SYNHW_PRINT("random_bits pointer cannot be NULL\n"); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++&video1 { ++ status = "disabled"; ++}; + -+ if (state->status.edu_vstat.rnc_enabled == 0) { -+ DEBUG("rnc_disabled\n"); -+ } ++&disp_intf { ++ status = "disabled"; ++}; + -+ if (state->status.edu_vstat.seed_enum == 0) { -+ DEBUG("not seed_enum\n"); -+ } ++#if PCIE0_EP ++&bmc_dev0 { ++ status = "disabled"; ++}; + -+ /* loop on generate to get the requested number of bits. Each generate gives NIST_TRNG_RAND_BLK_SIZE_BITS bits. */ -+ req_num_blks = ((req_num_bytes * 8) % NIST_TRNG_RAND_BLK_SIZE_BITS) ? -+ (((req_num_bytes * 8) / NIST_TRNG_RAND_BLK_SIZE_BITS) + 1) : -+ ((req_num_bytes * 8) / NIST_TRNG_RAND_BLK_SIZE_BITS); ++&xdma0 { ++ status = "disabled"; ++}; + -+ for (i = 0; i < req_num_blks; i++) { -+ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_VCTRL); -+ tmp = NIST_TRNG_EDU_VTRNG_VCTRL_CMD_SET(tmp, NIST_TRNG_EDU_VTRNG_VCTRL_CMD_GET_RANDOM); -+ pdu_io_write32(state->base + NIST_TRNG_EDU_VCTRL, tmp); ++&pcie_vuart0 { ++ status = "disabled"; ++}; + -+ // check busy -+ err = nisttrng_vtrng_wait_on_busy(state, 1, 0); -+ if (err) -+ goto ERR; ++&pcie_vuart1 { ++ status = "disabled"; ++}; + -+ // check for error -+ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_VISTAT); -+ if (NIST_TRNG_REG_EDU_VSTAT_ANY_RW1(tmp)) { -+ DEBUG("EDU_VSTAT_ANY_RW1 set 0x%x\n", tmp); -+ } ++&pcie_lpc0_kcs0 { ++ status = "disabled"; ++}; + -+ //check that all valid -+ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_VSTAT); -+ if ((NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD0(tmp) == 0) || -+ (NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD1(tmp) == 0) || -+ (NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD2(tmp) == 0) || -+ (NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD3(tmp) == 0)) { -+ DEBUG("EDU_VSTAT_SLICE_VLD fail 0x%x\n", tmp); -+ } ++&pcie_lpc0_kcs1 { ++ status = "disabled"; ++}; + -+ /* read the generated random number block and store */ -+ for (j = 0; j < (NIST_TRNG_RAND_BLK_SIZE_BITS / 32); j++) { -+ tmp = pdu_io_read32(state->base + -+ NIST_TRNG_EDU_VRAND_0 + j); -+ /* copy to random_bits byte-by-byte, until req_num_bytes are copied */ -+ remained_bytes = req_num_bytes - -+ (i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + -+ j * 4); -+ if (remained_bytes > 4) { -+ memcpy(random_bits + i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + -+ j * 4, &tmp, 4); ++&pcie_lpc0_kcs2 { ++ status = "disabled"; ++}; + -+ /* decrement the bits counter and return error if generated more than the maximum*/ -+ state->counters.bits_per_req_left = -+ state->counters.bits_per_req_left - -+ 4 * 8; -+ if (state->counters.bits_per_req_left < 0) { -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } -+ } else { -+ memcpy(random_bits + i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + -+ j * 4, &tmp, remained_bytes); ++&pcie_lpc0_kcs3 { ++ status = "disabled"; ++}; + -+ /* decrement the bits counter and return error if generated more than the maximum*/ -+ state->counters.bits_per_req_left = -+ state->counters.bits_per_req_left - -+ remained_bytes * 8; -+ if (state->counters.bits_per_req_left < 0) { -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } -+ break; -+ } -+ } -+ } -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} ++&pcie_lpc0_ibt { ++ status = "disabled"; ++}; + -+/* Generate */ -+int nisttrng_generate(struct nist_trng_state *state, void *random_bits, -+ unsigned long req_num_bytes, int req_sec_strength, -+ int pred_resist, void *addin_str) -+{ -+ int err; -+ int reseed_required; ++&pcie0_mmbi0 { ++ status = "disabled"; ++}; ++#else ++&pcie0 { ++ status = "disabled"; ++}; ++#endif + -+ DEBUG(">> %s: requested number of bytes = %lu, security strength = %u, pred_resist = %u, additional string existence = %u\n", -+ __func__, req_num_bytes, req_sec_strength, pred_resist, -+ (addin_str) ? 1 : 0); ++#if PCIE1_EP ++&bmc_dev1 { ++ status = "disabled"; ++}; + -+ /* make sure there is no alarm and the core is not busy */ -+ err = nisttrng_get_alarms(state); -+ if (err) -+ goto ERR; ++&xdma1 { ++ status = "disabled"; ++}; + -+ err = nisttrng_wait_on_busy(state); -+ if (err) -+ goto ERR; ++&pcie_vuart2 { ++ status = "disabled"; ++}; + -+ /* if the DRBG is not instantiated return error */ -+ if (!DRBG_INSTANTIATED(state->status.current_state)) { -+ SYNHW_PRINT("DRBG is not instantiated\n"); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++&pcie_vuart3 { ++ status = "disabled"; ++}; + -+ /* requested number of bits has to be less that the programmed maximum */ -+ if ((req_num_bytes * 8) > state->counters.max_bits_per_req) { -+ SYNHW_PRINT("requested number of bits (%lu) is larger than the set maximum (%lu)\n", -+ (req_num_bytes * 8), state->counters.max_bits_per_req); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++&pcie_lpc1_kcs0 { ++ status = "disabled"; ++}; + -+ /* security strength has to be lower than what the DRBG is instantiated with. set_sec_strength function checks this. */ -+ err = nisttrng_set_sec_strength(state, req_sec_strength); -+ if (err) -+ goto ERR; ++&pcie_lpc1_kcs1 { ++ status = "disabled"; ++}; + -+ /* set the prediction resistance - if pred_resist is set but, pred_resist that the DRBG is instantiated with is not 1, return error */ -+ err = nisttrng_set_pred_resist(state, pred_resist); -+ if (err) -+ goto ERR; ++&pcie_lpc1_kcs2 { ++ status = "disabled"; ++}; + -+ /* make sure random_bits is not NULL */ -+ if (!random_bits) { -+ DEBUG("random_bits pointer cannot be NULL\n"); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++&pcie_lpc1_kcs3 { ++ status = "disabled"; ++}; + -+ /* set the reseed required flag to 0. The loop is to check at the end whether a reseed is required at the end and jump back to reseed and generate if needed. This is the NIST mandated procedure */ -+ reseed_required = 0; ++&pcie_lpc1_ibt { ++ status = "disabled"; ++}; + -+ if (!addin_str) { -+ /* set the ADDIN_PRESENT field of the MODE register to 1 */ -+ err = nisttrng_set_addin_present(state, 0); -+ if (err) -+ goto ERR; -+ } ++&pcie1_mmbi4 { ++ status = "disabled"; ++}; ++#else ++&pcie1 { ++ status = "disabled"; ++}; ++#endif + -+ do { -+ void *generate_addin_str = addin_str; ++&sdio_controller { ++ status = "disabled"; ++}; + -+ if (pred_resist | reseed_required) { -+ err = nisttrng_reseed(state, pred_resist, addin_str); -+ if (err) -+ goto ERR; ++&sdhci { ++ status = "disabled"; ++}; + -+ /* SP800-90a says that if reseed is executed, any additional input string is only used in the reseed phase and replaced by NULL in the generate phase */ -+ generate_addin_str = NULL; -+ err = nisttrng_set_addin_present(state, 0); -+ if (err) -+ goto ERR; +diff --git a/crypto/Makefile b/crypto/Makefile +--- a/crypto/Makefile 2025-08-01 08:48:47.000000000 +0000 ++++ b/crypto/Makefile 2026-04-08 18:03:36.190926635 +0000 +@@ -181,6 +181,7 @@ + obj-$(CONFIG_CRYPTO_ECC) += ecc.o + obj-$(CONFIG_CRYPTO_ESSIV) += essiv.o + obj-$(CONFIG_CRYPTO_CURVE25519) += curve25519-generic.o ++obj-$(CONFIG_CRYPTO_DEV_ASPEED) += aspeed_crypt.o + + ecdh_generic-y += ecdh.o + ecdh_generic-y += ecdh_helper.o +diff --git a/crypto/aspeed_crypt.c b/crypto/aspeed_crypt.c +--- a/crypto/aspeed_crypt.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/crypto/aspeed_crypt.c 2026-04-08 18:03:48.306705393 +0000 +@@ -0,0 +1,264 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2025 Aspeed Technology Inc. ++ */ + -+ /* ADDIN_PRESENT field in MODE has to be set back to 0 to avoid illegal cmd sequence */ -+ reseed_required = 0; -+ } ++#define ASPEED_CRYPT_NAME "aspeed-crypt" ++#define pr_fmt(fmt) ASPEED_CRYPT_NAME ": " fmt + -+ /* generate process */ -+ if (nisttrng_check_seed_lifetime(state) == CRYPTO_RESEED_REQUIRED) { -+ reseed_required = 1; ++#include ++#include ++#include ++#include ++#include + -+ } else { -+ reseed_required = 0; ++#include "internal.h" + -+ /* Refresh_Addin command if additional input is not NULL*/ -+ if (generate_addin_str) { -+ err = nisttrng_refresh_addin(state, generate_addin_str); -+ if (err) -+ goto ERR; -+ } ++enum aspeed_crypt_type { ++ ASPEED_SKCIPHER_TYPE, ++ ASPEED_AHASH_TYPE, ++ ASPEED_AKCIPHER_TYPE, ++ ASPEED_SIG_TYPE, ++}; + -+ /* Generate all random bits */ -+ /* if EDU present then get random number from private vtrng */ ++struct aspeed_crypt_drv { ++ char *algo; ++ char *drv_name; ++}; + -+ //state->config.build_cfg0.edu_present = 0; -+ if (state->config.build_cfg0.edu_present) { -+ err = nisttrng_generate_private_vtrng(state, random_bits, -+ req_num_bytes); -+ if (err) -+ goto ERR; ++struct aspeed_crypt_test { ++ char *test_item; ++ enum aspeed_crypt_type type; ++ int num; ++ struct aspeed_crypt_drv *algo; ++}; + -+ } else { -+ err = nisttrng_gen_random(state, random_bits, -+ req_num_bytes); -+ if (err) -+ goto ERR; ++struct aspeed_crypt_drv aspeed_des_binding[] = { ++ { "ecb(des)", "aspeed-ecb-des" }, ++ { "cbc(des)", "aspeed-cbc-des" }, ++ { "ctr(des)", "aspeed-ctr-des" }, ++}; + -+ /* Advance the state - if it returns CRYPTO_RESEED_REQUIRED, have to jump back and do a reseed and generate */ -+ err = nisttrng_advance_state(state); -+ if (err) -+ goto ERR; -+ } -+ } -+ } while (reseed_required); ++struct aspeed_crypt_drv aspeed_tdes_binding[] = { ++ { "ecb(des3_ede)", "aspeed-ecb-tdes" }, ++ { "cbc(des3_ede)", "aspeed-cbc-tdes" }, ++ { "ctr(des3_ede)", "aspeed-ctr-tdes" }, ++}; + -+ err = CRYPTO_OK; -+ state->status.current_state = NIST_TRNG_STATE_GENERATE; -+ERR: -+ if (err) -+ random_bits = NULL; ++struct aspeed_crypt_drv aspeed_sha3_binding[] = { ++ { "sha3-224", "aspeed-sha3-224" }, ++ { "sha3-256", "aspeed-sha3-256" }, ++ { "sha3-384", "aspeed-sha3-384" }, ++ { "sha3-512", "aspeed-sha3-512" }, ++}; + -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_generate */ -+EXPORT_SYMBOL(nisttrng_generate); -diff --git a/drivers/char/hw_random/dwc/src/trng/trng/nist_trng_private.c b/drivers/char/hw_random/dwc/src/trng/trng/nist_trng_private.c ---- a/drivers/char/hw_random/dwc/src/trng/trng/nist_trng_private.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/trng/nist_trng_private.c 2025-12-23 10:16:19.525059469 +0000 -@@ -0,0 +1,1022 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * This Synopsys software and associated documentation (hereinafter the -+ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless -+ * otherwise expressly agreed to in writing between Synopsys and you. The -+ * Software IS NOT an item of Licensed Software or a Licensed Product under -+ * any End User Software License Agreement or Agreement for Licensed Products -+ * with Synopsys or any supplement thereto. Synopsys is a registered trademark -+ * of Synopsys, Inc. Other names included in the SOFTWARE may be the -+ * trademarks of their respective owners. -+ * -+ * The contents of this file are dual-licensed; you may select either version -+ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license -+ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the -+ * SOFTWARE. The BSD License is copied below. -+ * -+ * BSD-3-Clause License: -+ * Copyright (c) 2012-2016 Synopsys, Inc. and/or its affiliates. -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright notice, -+ * this list of conditions, and the following disclaimer, without -+ * modification. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * 3. The names of the above-listed copyright holders may not be used to -+ * endorse or promote products derived from this software without specific -+ * prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -+ * POSSIBILITY OF SUCH DAMAGE. -+ */ ++struct aspeed_crypt_drv aspeed_rsa_binding[] = { ++ { "rsa", "aspeed-rsa" }, ++}; + -+#include "nisttrng_hw.h" -+#include "nisttrng.h" ++struct aspeed_crypt_drv aspeed_ecdsa_binding[] = { ++ { "ecdsa-nist-p256", "aspeed-ecdsa-nist-p256" }, ++ { "ecdsa-nist-p384", "aspeed-ecdsa-nist-p384" }, ++}; + -+/* Wait functions */ -+static int nisttrng_wait_on_(struct nist_trng_state *state, u32 mask) ++static bool aspeed_detect_skcipher_drv(char *drv_name) +{ -+ u32 tmp; -+ int t; -+ -+ t = NIST_TRNG_RETRY_MAX; ++ struct crypto_skcipher *tfm = NULL; ++ bool found = false; + -+ do { -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_ISTAT); -+ } while (!(tmp & (mask | NIST_TRNG_REG_ISTAT_ALARMS)) && --t); ++ tfm = crypto_alloc_skcipher(drv_name, 0, 0); ++ if (!IS_ERR(tfm)) { ++ found = true; ++ crypto_free_skcipher(tfm); ++ } + -+ if (tmp & NIST_TRNG_REG_ISTAT_ALARMS) -+ return nisttrng_get_alarms(state); ++ return found; ++} + -+ if (t) { -+ pdu_io_write32(state->base + NIST_TRNG_REG_ISTAT, mask); -+ return CRYPTO_OK; ++static bool aspeed_detect_ahash_drv(char *drv_name) ++{ ++ struct crypto_ahash *tfm = NULL; ++ bool found = false; + -+ } else { -+ SYNHW_PRINT("wait_on_: failed timeout: %08lx\n", -+ (unsigned long)tmp); -+ return CRYPTO_TIMEOUT; ++ tfm = crypto_alloc_ahash(drv_name, 0, 0); ++ if (!IS_ERR(tfm)) { ++ found = true; ++ crypto_free_ahash(tfm); + } -+} /* nisttrng_wait_on_ */ + -+int nisttrng_wait_on_done(struct nist_trng_state *state) ++ return found; ++} ++ ++static bool aspeed_detect_akcipher_drv(char *drv_name) +{ -+ return nisttrng_wait_on_(state, NIST_TRNG_REG_ISTAT_DONE); -+} /* nisttrng_wait_on_done */ -+EXPORT_SYMBOL(nisttrng_wait_on_done); ++ struct crypto_akcipher *tfm = NULL; ++ bool found = false; + -+int nisttrng_wait_on_noise_rdy(struct nist_trng_state *state) ++ tfm = crypto_alloc_akcipher(drv_name, 0, 0); ++ if (!IS_ERR(tfm)) { ++ found = true; ++ crypto_free_akcipher(tfm); ++ } ++ ++ return found; ++} ++ ++static bool aspeed_detect_sig_drv(char *drv_name) +{ -+ return nisttrng_wait_on_(state, NIST_TRNG_REG_ISTAT_NOISE_RDY); -+} /* nisttrng_wait_on_noise_rdy */ ++ struct crypto_sig *tfm = NULL; ++ bool found = false; + -+static int nisttrng_wait_on_zeroize(struct nist_trng_state *state) ++ tfm = crypto_alloc_sig(drv_name, 0, 0); ++ if (!IS_ERR(tfm)) { ++ found = true; ++ crypto_free_sig(tfm); ++ } ++ ++ return found; ++} ++ ++static bool aspeed_detect_drv(enum aspeed_crypt_type type, char *drv_name) +{ -+ return nisttrng_wait_on_(state, NIST_TRNG_REG_ISTAT_ZEROIZE); -+} /* nisttrng_wait_on_zeroize */ ++ switch (type) { ++ case ASPEED_SKCIPHER_TYPE: ++ return aspeed_detect_skcipher_drv(drv_name); ++ case ASPEED_AHASH_TYPE: ++ return aspeed_detect_ahash_drv(drv_name); ++ case ASPEED_AKCIPHER_TYPE: ++ return aspeed_detect_akcipher_drv(drv_name); ++ case ASPEED_SIG_TYPE: ++ return aspeed_detect_sig_drv(drv_name); ++ default: ++ return false; ++ } ++} + -+static int nisttrng_wait_on_kat_completed(struct nist_trng_state *state) ++static const struct aspeed_crypt_test * ++aspeed_crypt_get_alg(const char *algo_name) +{ -+ return nisttrng_wait_on_(state, NIST_TRNG_REG_ISTAT_KAT_COMPLETE); -+} /* nisttrng_wait_on_kat_completed */ ++ int i = 0; ++ static const struct aspeed_crypt_test algo_ts[] = { ++ { "des", ASPEED_SKCIPHER_TYPE, ARRAY_SIZE(aspeed_des_binding), ++ aspeed_des_binding }, ++ { "des3", ASPEED_SKCIPHER_TYPE, ARRAY_SIZE(aspeed_tdes_binding), ++ aspeed_tdes_binding }, ++ { "sha-3", ASPEED_AHASH_TYPE, ARRAY_SIZE(aspeed_sha3_binding), ++ aspeed_sha3_binding }, ++ { "rsa", ASPEED_AKCIPHER_TYPE, ARRAY_SIZE(aspeed_rsa_binding), ++ aspeed_rsa_binding }, ++ { "ecdsa", ASPEED_SIG_TYPE, ARRAY_SIZE(aspeed_ecdsa_binding), ++ aspeed_ecdsa_binding }, ++ }; ++ ++ for (i = 0; i < ARRAY_SIZE(algo_ts); i++) { ++ if (strlen(algo_ts[i].test_item) == strlen(algo_name) && ++ strncmp(algo_ts[i].test_item, algo_name, ++ strlen(algo_name)) == 0) ++ return &algo_ts[i]; ++ } + -+int nisttrng_wait_on_busy(struct nist_trng_state *state) ++ return NULL; ++} ++ ++static __maybe_unused int aspeed_crypt_run_tests(char *alg_name) +{ -+ u32 tmp, t; ++ const struct aspeed_crypt_test *ts; ++ int ret = 0; ++ int i = 0; + -+ t = NIST_TRNG_RETRY_MAX; ++ ts = aspeed_crypt_get_alg(alg_name); ++ if (!ts) { ++ pr_err("No test suite found for algorithm: %s", alg_name); ++ return -EINVAL; ++ } + -+ do { -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_STAT); -+ } while ((tmp & NIST_TRNG_REG_STAT_BUSY) && --t); ++ for (i = 0; i < ts->num && ret == 0; i++) { ++ if (aspeed_detect_drv(ts->type, ts->algo[i].drv_name)) ++ ret = alg_test(ts->algo[i].drv_name, ts->algo[i].algo, ++ 0, 0); ++ else ++ ret = -ENOENT; + -+ if (t) -+ return CRYPTO_OK; ++ pr_info("alg: %s tests %s\n", ts->algo[i].algo, ++ !ret ? "passed" : "failed"); ++ } + -+ SYNHW_PRINT("wait_on_busy: failed timeout: %08lx\n", -+ (unsigned long)tmp); -+ return CRYPTO_TIMEOUT; -+} /* nisttrng_wait_on_busy */ -+EXPORT_SYMBOL(nisttrng_wait_on_busy); ++ return ret; ++} + -+/* Read and return alarm. Zeroize if there is an alarm*/ -+int nisttrng_get_alarms(struct nist_trng_state *state) ++#if defined(CONFIG_CRYPTO_MANAGER_DISABLE_TESTS) && \ ++ !defined(CONFIG_CRYPTO_DEV_ASPEED_ACRY) ++static ssize_t aspeed_crypt_write(struct file *file, const char __user *ubuf, ++ size_t count, loff_t *ppos) +{ -+ u32 tmp; ++ pr_err("Crypto tests are disabled\n"); ++ return -EOPNOTSUPP; ++} ++#else ++static ssize_t aspeed_crypt_write(struct file *file, const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ u8 kbuf[16] = { 0 }; + -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_ISTAT); -+ if (tmp & NIST_TRNG_REG_ISTAT_ALARMS) { -+ // alarm happened -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_ALARM); -+ DEBUG("Received alarm: %lx\n", (unsigned long)tmp); -+ // clear istat -+ pdu_io_write32(state->base + NIST_TRNG_REG_ISTAT, -+ NIST_TRNG_REG_ISTAT_ALARMS); -+ pdu_io_write32(state->base + NIST_TRNG_REG_ALARM, 0x1F); -+ state->status.alarm_code = tmp & 0x1F; ++ if (count > sizeof(kbuf) - 1) ++ return -EINVAL; + -+ /* zeroize if there was an alarm */ -+ if (state->status.alarm_code != -+ NIST_TRNG_REG_ALARM_FAILED_TEST_ID_OK) { -+ nisttrng_zeroize(state); -+ } -+ } else { -+ state->status.alarm_code = 0; -+ } ++ if (copy_from_user(kbuf, ubuf, min(count, sizeof(kbuf)))) ++ return -EFAULT; ++ strim(kbuf); + -+ if (state->status.alarm_code) -+ return CRYPTO_FATAL; ++ if (!aspeed_crypt_run_tests(kbuf)) ++ pr_err("alg: %s tests passed\n", kbuf); + else -+ return CRYPTO_OK; -+} /* nisttrng_get_alarms */ -+EXPORT_SYMBOL(nisttrng_get_alarms); ++ pr_err("alg: %s tests failed\n", kbuf); + -+/* Reset reminder and alarm counters */ -+int nisttrng_reset_counters(struct nist_trng_state *state) -+{ -+ state->counters.bits_per_req_left = state->counters.max_bits_per_req; -+ state->counters.req_per_seed_left = state->counters.max_req_per_seed; ++ return min(count, sizeof(kbuf)); ++} ++#endif + -+ return 0; -+} /* nisttrng_reset_counters */ -+EXPORT_SYMBOL(nisttrng_reset_counters); ++static const struct file_operations fops = { ++ .owner = THIS_MODULE, ++ .write = aspeed_crypt_write, ++}; + -+/* When a zeroize happens some of the struct objects should reset */ -+int nisttrng_reset_state(struct nist_trng_state *state) ++static dev_t dev; ++static struct cdev cdev; ++static struct class *class; ++static int __init acrypt_mod_init(void) +{ -+ nisttrng_reset_counters(state); -+ state->status.pad_ps_addin = 0; -+ state->status.current_state = NIST_TRNG_STATE_UNINSTANTIATE; ++ int ret; + -+ return 0; -+} /* nisttrng_reset_state */ ++ ret = alloc_chrdev_region(&dev, 0, 1, ASPEED_CRYPT_NAME); ++ if (ret < 0) ++ return ret; + -+/* ---------- Set field APIs ---------- */ ++ cdev_init(&cdev, &fops); ++ ret = cdev_add(&cdev, dev, 1); ++ if (ret) ++ goto del_region; + -+/* -+ * Sets the security strength of the DRBG instance. -+ * > req_sec_strength has to be an integer. The API chooses one of SEC_STRNT_AES128 or SEC_STRNT_AES256 as follows: -+ * 0 < req_sec_strength <= 128 --> security strength = SEC_STRNT_AES128 -+ * 128 < req_sec_strength <= 256 --> security strength = SEC_STRNT_AES256 -+ * else --> Invalid security strength -+ * > If the DRBG is instantiated, a new security strength change request with greater security strength will return error. -+ */ -+int nisttrng_set_sec_strength(struct nist_trng_state *state, int req_sec_strength) -+{ -+ int err; -+ u32 tmp; -+ enum nisttrng_sec_strength chosen_sec_strength; ++ class = class_create(ASPEED_CRYPT_NAME); ++ if (IS_ERR(class)) { ++ ret = PTR_ERR(class); ++ goto del_cdev; ++ } + -+ DEBUG(">> %s: security strength = %i\n", __func__, -+ req_sec_strength); ++ device_create(class, NULL, dev, NULL, ASPEED_CRYPT_NAME); ++ return 0; ++del_cdev: ++ cdev_del(&cdev); ++del_region: ++ unregister_chrdev_region(dev, 1); ++ return ret; ++} + -+ /* choose the security strength */ -+ /* set the security strength to the lowest security strength greater or equal to the req_sec_strenght from the set {128, 256} */ -+ if (REQ_SEC_STRENGTH_IS_VALID(req_sec_strength)) { -+ if (req_sec_strength > 0 && req_sec_strength <= 128) { -+ chosen_sec_strength = SEC_STRNT_AES128; ++static void __exit acrypt_mod_fini(void) ++{ ++ device_destroy(class, dev); ++ class_destroy(class); ++ cdev_del(&cdev); ++ unregister_chrdev_region(dev, 1); ++} + -+ } else if (((req_sec_strength > 128) && -+ (req_sec_strength <= 256)) && -+ (state->config.features.drbg_arch == AES256)) { -+ chosen_sec_strength = SEC_STRNT_AES256; ++subsys_initcall(acrypt_mod_init); ++module_exit(acrypt_mod_fini); +diff --git a/drivers/Makefile b/drivers/Makefile +--- a/drivers/Makefile 2025-08-01 08:48:47.000000000 +0000 ++++ b/drivers/Makefile 2026-04-08 18:03:39.533865664 +0000 +@@ -195,3 +195,4 @@ + obj-$(CONFIG_DPLL) += dpll/ + + obj-$(CONFIG_S390) += s390/ ++obj-$(CONFIG_JTAG_ASPEED) += jtag/ +diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig +--- a/drivers/bus/Kconfig 2025-08-01 08:48:47.000000000 +0000 ++++ b/drivers/bus/Kconfig 2026-04-08 18:03:34.258961872 +0000 +@@ -261,6 +261,12 @@ + configuration. Allows to adjust the priorities of all master + peripherals. + ++config ASPEED_LTPI ++ bool "Aspeed LTPI bus controller driver" ++ depends on ARCH_ASPEED ++ help ++ LVDS Tunneling Protocol and Interface (LTPI) bus controller + -+ } else { /* should not get here, because we have already checked the validity */ -+ DEBUG("Invalid security strength\n"); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } -+ } else { -+ DEBUG("Invalid security strength\n"); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } -+ DEBUG("chosen security strength = %u\n", chosen_sec_strength); + source "drivers/bus/fsl-mc/Kconfig" + source "drivers/bus/mhi/Kconfig" + +diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile +--- a/drivers/bus/Makefile 2025-08-01 08:48:47.000000000 +0000 ++++ b/drivers/bus/Makefile 2026-04-08 18:03:38.952876260 +0000 +@@ -39,6 +39,7 @@ + obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o + + obj-$(CONFIG_DA8XX_MSTPRI) += da8xx-mstpri.o ++obj-$(CONFIG_ASPEED_LTPI) += aspeed-ltpi.o + + # MHI + obj-y += mhi/ +diff --git a/drivers/bus/aspeed-ltpi.c b/drivers/bus/aspeed-ltpi.c +--- a/drivers/bus/aspeed-ltpi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/bus/aspeed-ltpi.c 2026-04-08 18:03:48.139708444 +0000 +@@ -0,0 +1,1173 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++// Copyright ASPEED Technology + -+ /* set the security strenght - at this point security strength is validated and converted */ -+ if (DRBG_INSTANTIATED(state->status.current_state) && -+ chosen_sec_strength != state->status.sec_strength) { -+ /* security strength can only change when the DRBG is not instantiated. */ -+ /* if the new security strength is less that what the DRBG is instantiated with, accept it, but don't change in HW. If it's more, return error */ -+ if (chosen_sec_strength < state->status.sec_strength) { -+ DEBUG("Lowering the security strength. DRBG is already instantiated.\n"); -+ state->status.pad_ps_addin = 4; -+ state->status.sec_strength = chosen_sec_strength; ++#include "linux/dev_printk.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ } else { -+ state->status.pad_ps_addin = 0; -+ DEBUG("Cannot select a higher security strenght once the DRBG is instantiated\n"); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } -+ } else { -+ DEBUG("Updating the security strength.\n"); -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_MODE); -+ tmp = NIST_TRNG_REG_MODE_SET_SEC_ALG(tmp, chosen_sec_strength); -+ pdu_io_write32(state->base + NIST_TRNG_REG_MODE, tmp); ++#include "aspeed-ltpi.h" + -+ state->status.pad_ps_addin = 0; -+ state->status.sec_strength = chosen_sec_strength; -+ } ++#define LTPI_AUTO_CAP_LOW 0x24 ++#define LTPI_I2C_IO_FRAME_EN GENMASK(29, 24) ++#define LTPI_AUTO_CAP_HIGH 0x28 ++#define LTPI_UART_IO_FRAME_EN GENMASK(14, 13) + -+ err = CRYPTO_OK; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_set_sec_strength */ -+EXPORT_SYMBOL(nisttrng_set_sec_strength); ++#define LTPI_LINK_CONTROLL 0x80 ++#define LTPI_AUTO_CONFIG BIT(10) + -+/* -+ * Sets the ADDIN_PRESENT field of the MODE register according to the addin_present input. -+ */ -+int nisttrng_set_addin_present(struct nist_trng_state *state, int addin_present) -+{ -+ u32 tmp; ++#define LTPI_INTR_STATUS 0x100 ++#define LTPI_INTR_EN 0x104 ++#define LTPI_INTR_EN_OP_LINK_LOST BIT(4) ++#define LTPI_LINK_MANAGE_ST 0x108 ++#define LTPI_LINK_PARTNER_FLAG BIT(24) + -+ DEBUG(">> %s, adding_present = %u\n", __func__, -+ addin_present); ++#define LTPI_MANUAL_CAP_LOW 0x118 ++#define LTPI_MANUAL_CAP_HIGH 0x11c + -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_MODE); -+ tmp = NIST_TRNG_REG_MODE_SET_ADDIN_PRESENT(tmp, addin_present); -+ pdu_io_write32(state->base + NIST_TRNG_REG_MODE, tmp); ++#define LTPI_I2C_TIMING_0 0x134 ++#define LTPI_I2C_TIMING_1 0x138 + -+ DEBUG("--- Return %s, err = %i\n", __func__, 0); -+ return 0; -+} /* nisttrng_set_addin_present */ -+EXPORT_SYMBOL(nisttrng_set_addin_present); ++#define LTPI_I2C_100K_0 0x3535352f ++#define LTPI_I2C_100K_1 0x09353535 + -+/* -+ * Sets the PRED_RESIST field of the MODE register according to the pred_resist input. -+ * > If the DRBG is instantiated with prediction resistance of 0, and a change to the prediction resistance of 1 is requested, -+ * the API will return an error. -+ */ -+int nisttrng_set_pred_resist(struct nist_trng_state *state, int pred_resist) -+{ -+ int err; -+ u32 tmp; ++#define LTPI_I2C_400K_0 0x06060d06 ++#define LTPI_I2C_400K_1 0x090d0a06 + -+ DEBUG(">> %s: pred_resist = %u\n", __func__, pred_resist); ++#define SCU_IO_PINS_TRAP1 0x10 ++#define SCU_IO_PINS_TRAP1_CLEAR 0x14 ++#define SCU_IO_PINS_TRAP_LTPI GENMASK(2, 0) ++#define SCU_IO_OTP_TRAP1 0xa00 ++#define SCU_IO_OTP_TRAP1_CLEAR 0xa04 ++#define SCU_IO_OTP_TRAP2 0xa20 ++#define SCU_IO_OTP_TRAP2_CLEAR 0xa24 + -+ /* if DRBG is instantiated, prediction resistance can only change from 1 to 0 and not vice versa. This is a NIST requirement. */ -+ if (DRBG_INSTANTIATED(state->status.current_state) && pred_resist && -+ !state->status.pred_resist) { -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++/* SCU registers for PLL control */ ++#define SCU1_HPLL_1 0x300 ++#define SCU1_HPLL_2 0x304 ++#define SCU1_APLL_1 0x310 ++#define SCU1_APLL_2 0x314 ++#define SCU1_DPLL_1 0x320 ++#define SCU1_DPLL_2 0x324 ++#define SCU1_L0PLL_1 0x340 ++#define SCU1_L0PLL_2 0x344 ++#define SCU1_L1PLL_1 0x350 ++#define SCU1_L1PLL_2 0x354 ++ ++#define PLL_REG1_RESET BIT(25) ++#define PLL_REG1_BYPASS BIT(24) ++#define PLL_REG1_DIS BIT(23) ++#define PLL_REG1_P GENMASK(22, 19) ++#define PLL_REG1_N GENMASK(18, 13) ++#define PLL_REG1_M GENMASK(12, 0) ++ ++#define PLL_REG2_LOCK BIT(31) ++#define PLL_REG2_BWADJ GENMASK(11, 0) + -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_MODE); -+ tmp = NIST_TRNG_REG_MODE_SET_PRED_RESIST(tmp, pred_resist); -+ pdu_io_write32(state->base + NIST_TRNG_REG_MODE, tmp); ++#define MAX_I2C_IN_LTPI 6 ++#define MAX_UART_IN_LTPI 2 + -+ state->status.pred_resist = pred_resist; ++#define ADVERTISE_TIMEOUT_US 105000 /* 105 ms */ + -+ err = CRYPTO_OK; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_set_pred_resist */ -+EXPORT_SYMBOL(nisttrng_set_pred_resist); ++enum chip_version { ++ AST2700, ++ AST1700, ++}; + -+/* -+ * Puts the NIST_TRNG in either the SECURE or PROMISCUOUS mode. -+ * > A value of 1 for secure_mode puts the core in the SECURE mode and a value of 0 puts it in the PROMISCUOUS mode. -+ * > Any change to the secure mode of the NIST_TRNG will result in a complete zeroize, and will set the seeding mode to self-seeding. -+ * A zeroize will not destroy the programmed mode and ALARM register value. -+ * It keeps the programmed mode to avoid re-programming. -+ * It also, maintains the ALARM register value, so that the user can read the value to understand the reason of the occurred alarm. -+ */ -+int nisttrng_set_secure_mode(struct nist_trng_state *state, int secure_mode) -+{ -+ int err; -+ u32 tmp; -+ int t; ++struct ltpi_clk_info { ++ s16 freq; /* clock frequency in MHz*/ ++ s16 clk_sel; /* clock selection */ ++}; ++ ++static const struct ltpi_clk_info ltpi_clk_lookup_sdr[13] = { ++ { 25, REG_LTPI_PLL_25M }, ++ { 50, REG_LTPI_PLL_LPLL }, ++ { 75, REG_LTPI_PLL_LPLL }, ++ { 100, REG_LTPI_PLL_LPLL }, ++ { 150, REG_LTPI_PLL_LPLL }, ++ { 200, REG_LTPI_PLL_LPLL }, ++ { 250, REG_LTPI_PLL_LPLL }, ++ { 300, REG_LTPI_PLL_LPLL }, ++ { 400, REG_LTPI_PLL_LPLL }, ++ { 600, REG_LTPI_PLL_LPLL }, ++ { -1, -1 }, ++ { -1, -1 }, ++ { 500, REG_LTPI_PLL_LPLL } ++}; ++ ++static const struct ltpi_clk_info ltpi_clk_lookup_ddr[13] = { ++ { 50, REG_LTPI_PLL_LPLL }, ++ { 100, REG_LTPI_PLL_LPLL }, ++ { 150, REG_LTPI_PLL_LPLL }, ++ { 200, REG_LTPI_PLL_LPLL }, ++ { 300, REG_LTPI_PLL_LPLL }, ++ { 400, REG_LTPI_PLL_LPLL }, ++ { 500, REG_LTPI_PLL_LPLL }, ++ { 600, REG_LTPI_PLL_LPLL }, ++ { 800, REG_LTPI_PLL_LPLL }, ++ { 1200, REG_LTPI_PLL_LPLL }, ++ { -1, -1 }, ++ { -1, -1 }, ++ { 1000, REG_LTPI_PLL_LPLL } ++}; ++ ++#define MHZ(x) ((x) * 1000000) ++#define NUM_PLL_PARAM 13 ++#define REG_N_M_P(n, m, p) ((((n) - 1) << 13) | ((m) - 1) | (((p) - 1) << 19)) ++#define REG_BWADJ(bwadj) ((bwadj) - 1) ++ ++struct pll_param { ++ int freq; ++ u32 n_m_p; ++ u16 bwadj; ++}; ++ ++static const struct pll_param pll_param_lookup[NUM_PLL_PARAM] = { ++ { .freq = MHZ(50), .n_m_p = REG_N_M_P(1, 32, 16), .bwadj = REG_BWADJ(16) }, ++ { .freq = MHZ(75), .n_m_p = REG_N_M_P(1, 48, 16), .bwadj = REG_BWADJ(24) }, ++ { .freq = MHZ(100), .n_m_p = REG_N_M_P(1, 56, 14), .bwadj = REG_BWADJ(28) }, ++ { .freq = MHZ(150), .n_m_p = REG_N_M_P(1, 60, 10), .bwadj = REG_BWADJ(30) }, ++ { .freq = MHZ(200), .n_m_p = REG_N_M_P(1, 48, 6), .bwadj = REG_BWADJ(24) }, ++ { .freq = MHZ(250), .n_m_p = REG_N_M_P(1, 60, 6), .bwadj = REG_BWADJ(30) }, ++ { .freq = MHZ(300), .n_m_p = REG_N_M_P(1, 48, 4), .bwadj = REG_BWADJ(24) }, ++ { .freq = MHZ(400), .n_m_p = REG_N_M_P(1, 32, 2), .bwadj = REG_BWADJ(16) }, ++ { .freq = MHZ(500), .n_m_p = REG_N_M_P(1, 40, 2), .bwadj = REG_BWADJ(20) }, ++ { .freq = MHZ(600), .n_m_p = REG_N_M_P(1, 48, 2), .bwadj = REG_BWADJ(24) }, ++ { .freq = MHZ(800), .n_m_p = REG_N_M_P(1, 32, 1), .bwadj = REG_BWADJ(16) }, ++ { .freq = MHZ(1000), .n_m_p = REG_N_M_P(1, 40, 1), .bwadj = REG_BWADJ(20) }, ++ { .freq = MHZ(1200), .n_m_p = REG_N_M_P(1, 48, 1), .bwadj = REG_BWADJ(24) }, ++}; ++ ++struct pll_info { ++ u32 reg_offset0; ++ u32 reg_offset1; ++}; ++ ++static const struct pll_info scu_pll_info[] = { ++ [0] = { .reg_offset0 = SCU1_L0PLL_1, .reg_offset1 = SCU1_L0PLL_2 }, ++ [1] = { .reg_offset0 = SCU1_L1PLL_1, .reg_offset1 = SCU1_L1PLL_2 }, ++}; + -+ DEBUG(">> %s: secure_mode = %u\n", __func__, secure_mode); ++struct aspeed_ltpi_priv { ++ struct device *dev; ++ void __iomem *regs; ++ void __iomem *phy_regs; ++ void __iomem *top_regs; ++ struct clk *ltpi_clk; ++ struct clk *ltpi_phyclk; ++ struct reset_control *ltpi_rst; ++ struct regmap *scu; ++ u32 version; ++ u32 i2c_tunneling; ++ u32 i2c_timing_0; ++ u32 i2c_timing_1; ++ u32 uart_tunneling; ++ int index; + -+ t = NIST_TRNG_RETRY_MAX; ++ /* Training parameters */ ++ u16 phy_speed_cap; ++ bool otp_ddr_dis; ++ int disable_auto_downshift; ++ int crc_format; ++ int io_driving; ++ int clk_inverse; ++ int link_speed_frm_rx_cnt; ++ int ad_timeout; ++ u64 op_timeout; ++ u64 t_link_detect; ++}; + -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_SMODE); -+ tmp = NIST_TRNG_REG_SMODE_SET_SECURE_EN(tmp, secure_mode); -+ pdu_io_write32(state->base + NIST_TRNG_REG_SMODE, tmp); ++static int ltpi_get_link_partner(struct aspeed_ltpi_priv *ltpi) ++{ ++ u32 reg = readl(ltpi->regs + LTPI_LINK_MNG_ST); + -+ /* wait until STAT register indicates that the mode is applied */ -+ do { -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_STAT); -+ } while ((NIST_TRNG_REG_STAT_GET_SECURE(tmp) != secure_mode) && --t); ++ return FIELD_GET(REG_LTPI_LINK_PARTNER_FLAG, reg); ++} + -+ if (!t) { -+ err = CRYPTO_TIMEOUT; -+ goto ERR; -+ } ++static irqreturn_t aspeed_ltpi_irq_handler(int irq, void *dev_id) ++{ ++ struct aspeed_ltpi_priv *priv = dev_id; ++ u32 status = readl(priv->regs + LTPI_INTR_STATUS); + -+ /* if secure mode changes, a zeroize will happen in HW. */ -+ if (state->status.secure_mode != secure_mode) { -+ DEBUG("secure mode changed. zeroize happened. reset sw state\n"); -+ /* nonce mode goes back to default. */ -+ state->status.nonce_mode = 0; -+ /* reset the SW state */ -+ nisttrng_reset_state(state); ++ if (status & LTPI_INTR_EN_OP_LINK_LOST) { ++ writel(0, priv->regs + LTPI_INTR_EN); ++ writel(status, priv->regs + LTPI_INTR_STATUS); ++ if (ltpi_get_link_partner(priv)) ++ panic("LTPI link lost!\n"); ++ /* Will not return */ ++ else ++ dev_err(priv->dev, "LTPI link lost!\n"); + } + -+ state->status.secure_mode = secure_mode; ++ writel(status, priv->regs + LTPI_INTR_STATUS); + -+ err = CRYPTO_OK; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_set_secure_mode */ -+EXPORT_SYMBOL(nisttrng_set_secure_mode); ++ return IRQ_HANDLED; ++} + -+/* -+ * To change the seeding mode of the NIST_TRNG. -+ * > A value of 1 for nonce_mode will put the NIST_TRNG in the nonce seeding mode, which means that the seed will be provided by the user, -+ * unlike the noise or self-seeding mode (normal mode of operation) in which the seed is generated by the internal entropy source. -+ * > Any transition to or from the nonce mode will zeroize the NIST_TRNG. -+ */ -+int nisttrng_set_nonce_mode(struct nist_trng_state *state, int nonce_mode) ++static int aspeed_ltpi_init_mux(struct aspeed_ltpi_priv *priv) +{ -+ int err; -+ u32 tmp; -+ int t; ++ u32 reg, i2c_en, uart_en, i; + -+ DEBUG(">> %s: nonce_mode = %u\n", __func__, nonce_mode); ++ reg = readl(priv->regs + LTPI_AUTO_CAP_LOW); + -+ t = NIST_TRNG_RETRY_MAX; ++ i2c_en = FIELD_GET(LTPI_I2C_IO_FRAME_EN, reg); ++ i2c_en &= priv->i2c_tunneling; + -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_SMODE); -+ tmp = NIST_TRNG_REG_SMODE_SET_NONCE(tmp, nonce_mode); -+ pdu_io_write32(state->base + NIST_TRNG_REG_SMODE, tmp); ++ reg &= ~LTPI_I2C_IO_FRAME_EN; ++ reg |= FIELD_PREP(LTPI_I2C_IO_FRAME_EN, i2c_en); ++ writel(reg, priv->regs + LTPI_MANUAL_CAP_LOW); + -+ /* wait until STAT register indicates that the mode is applied */ -+ do { -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_STAT); -+ } while ((NIST_TRNG_REG_STAT_GET_NONCE(tmp) != nonce_mode) && --t); ++ reg = readl(priv->regs + LTPI_AUTO_CAP_HIGH); + -+ if (!t) { -+ err = CRYPTO_TIMEOUT; -+ goto ERR; -+ } ++ uart_en = FIELD_GET(LTPI_UART_IO_FRAME_EN, reg); ++ uart_en &= priv->uart_tunneling; + -+ /* if nonce mode changes, a zeroize will happen in HW. */ -+ if (state->status.nonce_mode != nonce_mode) { -+ DEBUG("nonce mode changed. zeroize happened. reset sw state\n"); -+ /* reset the SW state */ -+ nisttrng_reset_state(state); -+ } ++ reg &= ~LTPI_UART_IO_FRAME_EN; ++ reg |= FIELD_PREP(LTPI_UART_IO_FRAME_EN, uart_en); + -+ state->status.nonce_mode = nonce_mode; ++ writel(reg, priv->regs + LTPI_MANUAL_CAP_HIGH); + -+ err = CRYPTO_OK; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_set_nonce_mode */ -+EXPORT_SYMBOL(nisttrng_set_nonce_mode); ++ /* Apply LTPI manual configuration */ ++ reg = readl(priv->regs + LTPI_LINK_CONTROLL); ++ reg &= ~LTPI_AUTO_CONFIG; ++ writel(reg, priv->regs + LTPI_LINK_CONTROLL); + -+/* ---------- Load data APIs ---------- */ -+/* -+ * Loads the additional input or personalization string into the NPA_DATAx registers. -+ * > Loads the proper number of bits (256 or 384) according to the security strength stored in the state handle. -+ */ -+int nisttrng_load_ps_addin(struct nist_trng_state *state, void *input_str) ++ /* Set the AST1700 i2c ac-timing */ ++ if (priv->version == AST1700) { ++ /* Apply i2c timing with i2c tunneling setting */ ++ for (i = 0; i < MAX_I2C_IN_LTPI; i++) { ++ if ((priv->i2c_tunneling >> i) & 0x1) { ++ writel(priv->i2c_timing_0, ++ priv->regs + LTPI_I2C_TIMING_0 + ++ (0x8 * i)); ++ writel(priv->i2c_timing_1, ++ priv->regs + LTPI_I2C_TIMING_1 + ++ (0x8 * i)); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int clz16(u16 x) +{ -+ int err; -+ int i, j; -+ int str_size; ++ int n = 0; + -+ DEBUG(">> %s starts...\n", __func__); ++ if (x == 0) ++ return 16; + -+ /* return error if the pointer is NULL */ -+ if (!input_str) { -+ err = CRYPTO_FAILED; -+ goto ERR; ++ if (x <= 0x00ff) { ++ n += 8; ++ x <<= 8; + } ++ if (x <= 0x0fff) { ++ n += 4; ++ x <<= 4; ++ } ++ if (x <= 0x3fff) { ++ n += 2; ++ x <<= 2; ++ } ++ if (x <= 0x7fff) ++ n += 1; + -+ /* calculate the length based on the security strength */ -+ if (state->status.sec_strength == SEC_STRNT_AES128) -+ str_size = 8; /* 256/32 */ -+ else if (state->status.sec_strength == SEC_STRNT_AES256) -+ str_size = 12; /* 384/32 */ ++ return n; ++} + -+ for (i = 0; i < str_size; i++) { -+ pdu_io_write32(state->base + NIST_TRNG_REG_NPA_DATA0 + i, -+ ((u32 *)input_str)[i]); -+ } ++static u16 find_max_speed(u16 cap) ++{ ++ return 15 - clz16(cap & ~LTPI_SP_CAP_DDR); ++} + -+ j = str_size + state->status.pad_ps_addin; -+ /* if security strength is lowered after the DRBG is instantiated, pad PS and ADDIN with 0 at the MSB side */ -+ DEBUG("pad NPA_DATA with %u zeros at the MSB side\n", -+ state->status.pad_ps_addin); -+ for (i = str_size; i < j; i++) -+ pdu_io_write32(state->base + NIST_TRNG_REG_NPA_DATA0 + i, 0); ++static void ltpi_phy_unlock(struct aspeed_ltpi_priv *ltpi) ++{ ++ writel(LTPI_PROT_KEY_UNLOCK, ltpi->phy_regs + LTPI_PROT_KEY); ++} + -+ err = CRYPTO_OK; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_load_ps_addin */ -+EXPORT_SYMBOL(nisttrng_load_ps_addin); ++static void ltpi_enable_rx_bias(struct aspeed_ltpi_priv *ltpi) ++{ ++ u32 val = readl(ltpi->top_regs + LTPI_LVDS_RX_CTRL); + -+/* ---------- Command APIs ---------- */ -+/* -+ * Provides entropy and is used in both nonce and noise (self) seeding modes of operation: -+ * > If the NIST_TRNG is in the nonce mode, entropy must be provided by the user; otherwise (in the self-seeding mode) entropy will be generated by the internal entropy source of the NIST_TRNG. -+ * > In the noise mode, calling the API will initiate a seeding command. Depending on the programmed security strength, a 256 or 384-bit seed will be generated. -+ * > Inputs 2 and 3 are only used when the core is in the nonce mode. -+ * > In the nonce mode, the NIST_TRNG can be seeded either through 2 or 3 blocks of 512-bit nonce values which are passed to the internal derivation function to increase the entropy, -+ * or it can be seeded by a 256 or 384-bit nonce written directly into the SEEDx registers. -+ * Passing a value of 1 to nonce_operation selects the former scenario and a value of 0 selects the latter. -+ * > The input_nonce pointer must point to a memory location with a sufficient number of initialized bits. -+ * > Table below shows the required number of bits depending on the nonce_operation and the security strength values. -+ * nonce_operation | Security Strength | Bit length requirement -+ * ------------------------------------------------------------------------------------------ -+ * 1 (using the Derivation Function) | SEC_STRNT_AES128 | 2x512 = 1024 -+ * 1 (using the Derivation Function) | SEC_STRNT_AES256 | 3x512 = 1536 -+ * 0 (loading the seed into SEEDx) | SEC_STRNT_AES128 | 256 -+ * 0 (loading the seed into SEEDx) | SEC_STRNT_AES256 | 384 -+ * > Generated entropy is secret information held securely within the HW and remains inaccessible to the user, unless the HW core is in the PROMISCUOUS mode. -+ */ -+int nisttrng_get_entropy_input(struct nist_trng_state *state, void *input_nonce, -+ int nonce_operation) ++ val |= (REG_LTPI_LVDS_RX1_BIAS_EN | REG_LTPI_LVDS_RX0_BIAS_EN); ++ writel(val, ltpi->top_regs + LTPI_LVDS_RX_CTRL); ++ udelay(1); ++} ++ ++static int ltpi_phy_get_mode(struct aspeed_ltpi_priv *ltpi) +{ -+ int err; -+ int nonce_ld_cntr = 0; -+ int i, j; ++ u32 reg = readl(ltpi->phy_regs + LTPI_PHY_CTRL); + -+ DEBUG(">> %s: seeding mode = %s, nonce_operation = %u\n", __func__, -+ (state->status.nonce_mode ? "Nonce" : "Noise"), nonce_operation); ++ return FIELD_GET(REG_LTPI_PHY_MODE, reg); ++} + -+ /* make sure there is no alarm and the core is not busy */ -+ err = nisttrng_get_alarms(state); -+ if (err) -+ goto ERR; ++static int ltpi_phy_set_mode(struct aspeed_ltpi_priv *ltpi, int mode) ++{ ++ u32 reg; + -+ err = nisttrng_wait_on_busy(state); -+ if (err) -+ goto ERR; ++ if (mode < 0 || mode > LTPI_PHY_MODE_CDR_HI_SP) { ++ dev_err(ltpi->dev, "%s: invalid mode %d\n", __func__, mode); ++ return -1; ++ } + -+ /* --- Seeding --- */ -+ if (state->status.nonce_mode) { /* --- nonce mode --- */ -+ if (!input_nonce) { -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++ reg = readl(ltpi->phy_regs + LTPI_PHY_CTRL); ++ reg &= ~REG_LTPI_PHY_MODE; ++ reg |= mode; ++ writel(reg, ltpi->phy_regs + LTPI_PHY_CTRL); + -+ nonce_ld_cntr = 0; ++ return 0; ++} + -+ if (state->status.sec_strength == SEC_STRNT_AES128) -+ nonce_ld_cntr = 2; -+ else if (state->status.sec_strength == SEC_STRNT_AES256) -+ nonce_ld_cntr = 3; ++static int ltpi_phy_set_clksel(struct aspeed_ltpi_priv *ltpi, int clksel, ++ bool is_op_clk) ++{ ++ u32 reg; + -+ if (nonce_operation) { /* load the noise inside NPA_DATAx register and issue gen_nonce command */ -+ for (i = 0; i < nonce_ld_cntr; i++) { -+ /* load the nonoce */ -+ for (j = 0; j < 16; j++) { -+ pdu_io_write32(state->base + -+ NIST_TRNG_REG_NPA_DATA0 + j, -+ ((u32 *)input_nonce)[16 * i + j]); -+ } ++#define RX_CLK_INVERSE BIT(1) ++#define TX_CLK_INVERSE BIT(0) + -+ /* issue the command and wait on done */ -+ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, -+ NIST_TRNG_REG_CTRL_CMD_GEN_NONCE); ++ reg = readl(ltpi->phy_regs + LTPI_PLL_CTRL); ++ reg &= ~(REG_LTPI_PLL_SELECT | REG_LTPI_PLL_SET | ++ REG_LTPI_RX_PHY_CLK_INV | REG_LTPI_TX_PHY_CLK_INV); ++ reg |= FIELD_PREP(REG_LTPI_PLL_SELECT, clksel); + -+ if (nisttrng_wait_on_done(state)) { -+ err = CRYPTO_FATAL; -+ goto ERR; -+ }; -+ } ++ if (ltpi->clk_inverse & RX_CLK_INVERSE) ++ reg |= REG_LTPI_RX_PHY_CLK_INV; + -+ } else { -+ /* load the nonoce */ -+ for (i = 0; i < 4 * nonce_ld_cntr; i++) { -+ pdu_io_write32(state->base + NIST_TRNG_REG_SEED0 + i, -+ ((u32 *)input_nonce)[i]); -+ } -+ } -+ } else { /* --- noise mode --- */ -+ /* issue the command and wait on done */ -+ DEBUG("issue the Gen_Noise command\n"); -+ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, -+ NIST_TRNG_REG_CTRL_CMD_GEN_NOISE); ++ if (ltpi->clk_inverse & TX_CLK_INVERSE) ++ reg |= REG_LTPI_TX_PHY_CLK_INV; + -+ if (nisttrng_wait_on_done(state)) { -+ err = CRYPTO_FATAL; -+ goto ERR; -+ }; -+ } ++ if (is_op_clk) ++ reg |= REG_LTPI_PLL_SET; + -+ err = CRYPTO_OK; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_get_entropy_input */ -+EXPORT_SYMBOL(nisttrng_get_entropy_input); ++ writel(reg, ltpi->phy_regs + LTPI_PLL_CTRL); + -+/* -+ * Generate Function: -+ * > The Generate function in NIST_TRNG HW is broken down into 3 steps: Refresh_Addin, Gen_Random and Advance_State. -+ * nisttrng_generate incorporates all these steps and some extra checks into one simple API. -+ * > There is one API for each step, below || -+ * \/ -+ */ -+/* -+ * Generate Part 1 - Refresh_Addin: Additional input string is used to add to the HW state entropy. -+ * > This API calls nisttrng_set_addin_present to set the ADDIN_PRESENT field of the MODE register to 1. -+ * > Then it loads the additional input provided by addin_str pointer into the NPA_DATAx by calling the nisttrng_load_ps_addin. -+ * > Then, it issues a Refresh_Addin command to the HW. -+ * > If the addin_str pointer is NULL, the API will return error. -+ */ -+int nisttrng_refresh_addin(struct nist_trng_state *state, void *addin_str) ++ return 0; ++} ++ ++static void ltpi_set_crc_format(struct aspeed_ltpi_priv *ltpi, int crc_fmt) +{ -+ int err; ++ u32 val = readl(ltpi->regs + LTPI_CRC_OPTION); + -+ DEBUG(">> %s starts...\n", __func__); ++ val &= ~(REG_LTPI_SW_CRC_OUT_ML_FIRST | REG_LTPI_SW_CRC_IN_LSB_FIRST); ++ if (crc_fmt) ++ val |= REG_LTPI_SW_CRC_OUT_ML_FIRST | ++ REG_LTPI_SW_CRC_IN_LSB_FIRST; + -+ /* if the DRBG is not intantiated return error */ -+ if (!DRBG_INSTANTIATED(state->status.current_state)) { -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++ writel(val, ltpi->regs + LTPI_CRC_OPTION); ++} + -+ /* make sure there is no alarm and the core is not busy */ -+ err = nisttrng_get_alarms(state); -+ if (err) -+ goto ERR; ++static int ltpi_reset(struct aspeed_ltpi_priv *ltpi) ++{ ++ /* Using reset controller */ ++ reset_control_assert(ltpi->ltpi_rst); ++ udelay(1); ++ /* Assuming clk gate is handled by clock framework or enabled */ ++ /* U-Boot does ungate here */ ++ if (ltpi->ltpi_clk) ++ clk_prepare_enable(ltpi->ltpi_clk); + -+ err = nisttrng_wait_on_busy(state); -+ if (err) -+ goto ERR; ++ reset_control_deassert(ltpi->ltpi_rst); + -+ /* This API should not be called with a NULL additional input string */ -+ if (!addin_str) { -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++ ltpi_phy_unlock(ltpi); + -+ /* set the ADDIN_PRESENT field of the MODE register to 1 */ -+ err = nisttrng_set_addin_present(state, 1); -+ if (err) -+ goto ERR; ++ return 0; ++} + -+ err = nisttrng_load_ps_addin(state, addin_str); -+ if (err) -+ goto ERR; ++static u32 ltpi_get_link_mng_state(struct aspeed_ltpi_priv *ltpi) ++{ ++ return FIELD_GET(REG_LTPI_LINK_MNG_ST, ++ readl(ltpi->regs + LTPI_LINK_MNG_ST)); ++} + -+ /* execute the command and wait on done*/ -+ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, -+ NIST_TRNG_REG_CTRL_CMD_REFRESH_ADDIN); ++static int ltpi_poll_link_mng_state(struct aspeed_ltpi_priv *ltpi, ++ u32 expected, u32 unexpected, ++ int timeout_us) ++{ ++ u32 state; ++ int ret; + -+ err = nisttrng_wait_on_done(state); -+ if (err) -+ goto ERR; ++ ret = readl_poll_timeout(ltpi->regs + LTPI_LINK_MNG_ST, state, ++ (FIELD_GET(REG_LTPI_LINK_MNG_ST, state) == expected) || ++ (FIELD_GET(REG_LTPI_LINK_MNG_ST, state) == unexpected), ++ 100, timeout_us); + -+ err = 0; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_refresh_addin */ -+EXPORT_SYMBOL(nisttrng_refresh_addin); ++ if (FIELD_GET(REG_LTPI_LINK_MNG_ST, state) == unexpected) ++ return -ENOLINK; + -+/* -+ * Generate Part 2 - Gen_Random: generates the requested number of bits. -+ * > This API issues the Gen_Random command to the HW as many times as indicated by req_num_bytes to generate the requested number of bits. -+ * > If the requested number of bits (i.e. 128×req_num_blks) is more than the maximum value specified by max_bits_per_req, the API will return error. -+ * > Random bits will be returned in random_bits. -+ */ -+int nisttrng_gen_random(struct nist_trng_state *state, void *random_bits, -+ unsigned long req_num_bytes) -+{ -+ int err; -+ int i, j; -+ u32 tmp; -+ unsigned int remained_bytes; -+ unsigned long req_num_blks; ++ if (ret) ++ return -ETIMEDOUT; + -+ DEBUG(">> %s: req_num_bytes = %lu\n", __func__, req_num_bytes); ++ return 0; ++} + -+ /* if the DRBG is not intantiated return error */ -+ if (!DRBG_INSTANTIATED(state->status.current_state)) { -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++static int ltpi_wait_state_pll_set(struct aspeed_ltpi_priv *ltpi, ++ int timeout_us) ++{ ++ return ltpi_poll_link_mng_state(ltpi, LTPI_LINK_MNG_ST_WAIT_PLL_SET, -1, ++ timeout_us); ++} + -+ /* make sure there is no alarm and the core is not busy */ -+ err = nisttrng_get_alarms(state); -+ if (err) -+ goto ERR; ++static int ltpi_wait_state_op(struct aspeed_ltpi_priv *ltpi) ++{ ++ u32 val = readl(ltpi->regs + LTPI_LINK_ST); + -+ err = nisttrng_wait_on_busy(state); -+ if (err) -+ goto ERR; ++ val |= (REG_LTPI_CON_ACC_TO_ERR | REG_LTPI_FRM_CRC_ERR | ++ REG_LTPI_LINK_LOST_ERR); ++ writel(val, ltpi->regs + LTPI_LINK_ST); + -+ /* requested number of bits has to be less that the programmed maximum */ -+ if ((req_num_bytes * 8) > state->counters.max_bits_per_req) { -+ DEBUG("requested number of bits (%lu) is larger than the set maximum (%lu)\n", -+ (req_num_bytes * 8), state->counters.max_bits_per_req); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++ return ltpi_poll_link_mng_state(ltpi, LTPI_LINK_MNG_ST_OP, ++ LTPI_LINK_MNG_ST_DETECT_ALIGN, ++ ltpi->ad_timeout); ++} + -+ /* make sure random_bits is not NULL */ -+ if (!random_bits) { -+ DEBUG("random_bits pointer cannot be NULL\n"); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++static int ltpi_set_lvds_io_driving(struct aspeed_ltpi_priv *ltpi, int driving) ++{ ++ u32 val; + -+ /* loop on generate to get the requested number of bits. Each generate gives NIST_TRNG_RAND_BLK_SIZE_BITS bits. */ -+ req_num_blks = -+ ((req_num_bytes * 8) % NIST_TRNG_RAND_BLK_SIZE_BITS) ? -+ (((req_num_bytes * 8) / NIST_TRNG_RAND_BLK_SIZE_BITS) + -+ 1) : -+ ((req_num_bytes * 8) / NIST_TRNG_RAND_BLK_SIZE_BITS); ++ val = readl(ltpi->top_regs + LTPI_SW_FORCE_EN); ++ val |= REG_LTPI_SW_FORCE_LVDS_TX_DS_EN; ++ writel(val, ltpi->top_regs + LTPI_SW_FORCE_EN); + -+ for (i = 0; i < req_num_blks; i++) { -+ /* issue gen_random and wait on done */ -+ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, -+ NIST_TRNG_REG_CTRL_CMD_GEN_RANDOM); ++ val = readl(ltpi->top_regs + LTPI_LVDS_TX_CTRL); ++ val &= ~(REG_LTPI_LVDS_TX1_DS1 | REG_LTPI_LVDS_TX1_DS0 | ++ REG_LTPI_LVDS_TX0_DS1 | REG_LTPI_LVDS_TX0_DS0); + -+ err = nisttrng_wait_on_done(state); -+ if (err) -+ goto ERR; ++ /* tx1: clk, tx0: data */ ++ if (driving & BIT(1)) ++ val |= (REG_LTPI_LVDS_TX1_DS1 | REG_LTPI_LVDS_TX0_DS1); + -+ /* read the generated random number block and store */ -+ for (j = 0; j < (NIST_TRNG_RAND_BLK_SIZE_BITS / 32); j++) { -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_RAND0 + j); -+ /* copy to random_bits byte-by-byte, until req_num_bytes are copied */ -+ remained_bytes = req_num_bytes - -+ (i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + -+ j * 4); -+ if (remained_bytes > 4) { -+ memcpy(random_bits + -+ i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + -+ j * 4, &tmp, 4); ++ if (driving & BIT(0)) ++ val |= (REG_LTPI_LVDS_TX1_DS0 | REG_LTPI_LVDS_TX0_DS0); + -+ /* decrement the bits counter and return error if generated more than the maximum*/ -+ state->counters.bits_per_req_left = -+ state->counters.bits_per_req_left - -+ 4 * 8; ++ writel(val, ltpi->top_regs + LTPI_LVDS_TX_CTRL); + -+ if (state->counters.bits_per_req_left < 0) { -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++ return 0; ++} + -+ } else { -+ memcpy(random_bits + i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + -+ j * 4, &tmp, remained_bytes); ++static int ltpi_set_local_speed_cap(struct aspeed_ltpi_priv *ltpi, ++ u32 speed_cap) ++{ ++ u32 reg; + -+ /* decrement the bits counter and return error if generated more than the maximum*/ -+ state->counters.bits_per_req_left = -+ state->counters.bits_per_req_left - -+ remained_bytes * 8; ++ /* only set bits that Aspeed SOC supported */ ++ speed_cap &= LTPI_SP_CAP_ASPEED_SUPPORTED; ++ if (ltpi->otp_ddr_dis) ++ speed_cap &= ~LTPI_SP_CAP_DDR; + -+ if (state->counters.bits_per_req_left < 0) { -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } -+ break; -+ } -+ } -+ } ++ reg = readl(ltpi->regs + LTPI_CAP_LOCAL); ++ reg &= ~REG_LTPI_SP_CAP_LOCAL; ++ reg |= FIELD_PREP(REG_LTPI_SP_CAP_LOCAL, speed_cap); ++ writel(reg, ltpi->regs + LTPI_CAP_LOCAL); + -+ err = CRYPTO_OK; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_gen_random */ -+EXPORT_SYMBOL(nisttrng_gen_random); ++ return 0; ++} + -+/* -+ * Generate Part 3 - Advance the state: advances the state of the DRBG. -+ * > This API issues the Advance_State command to the HW. -+ * > Then it updates the counter for the number of generate requests per seed. -+ * > The counter must be checked every time before starting the Generate process and a reseed must be issued if the limit is reached. This check is incorporated inside nisttrng_generate API. -+ * > Note that we don't have to provide additional input again for this API, because if it had been provided in refresh_addin stage, HW will lock the NPA_DATAx, so it will be still available -+ */ -+int nisttrng_advance_state(struct nist_trng_state *state) ++static void ltpi_do_link_training(struct aspeed_ltpi_priv *ltpi) +{ -+ int err; ++ u32 val; + -+ DEBUG(">> %s starts...\n", __func__); ++ /* Reset the PHY to PHY_MODE_OFF */ ++ ltpi_reset(ltpi); + -+ /* if the DRBG is not intantiated return error */ -+ if (!DRBG_INSTANTIATED(state->status.current_state)) { -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++ ltpi_enable_rx_bias(ltpi); ++ ltpi_set_local_speed_cap(ltpi, ltpi->phy_speed_cap); ++ ltpi_set_lvds_io_driving(ltpi, ltpi->io_driving); ++ ltpi_set_crc_format(ltpi, ltpi->crc_format); + -+ /* make sure there is no alarm and the core is not busy */ -+ err = nisttrng_get_alarms(state); -+ if (err) -+ goto ERR; ++ /* ++ * Configure the LINK_SPEED frame count to be received before ++ * entering AD. This configuraiton only effects the SCM LTPI. ++ */ ++ val = readl(ltpi->regs + LTPI_LINK_MANAGE_CTRL0); ++ val &= ~REG_LTPI_RX_LINK_SP_FRM_NUM; ++ val |= FIELD_PREP(REG_LTPI_RX_LINK_SP_FRM_NUM, ++ ltpi->link_speed_frm_rx_cnt); ++ writel(val, ltpi->regs + LTPI_LINK_MANAGE_CTRL0); + -+ err = nisttrng_wait_on_busy(state); -+ if (err) -+ goto ERR; ++ /* ++ * ad_timeout in us = ad_timeout in clock cycles * period of 25MHz ++ * ad_timeout in clock cycles ++ * = ad_timeout in us / period of 25MHz ++ * = (ad_timeout in us * 1000)ns / 40ns = ad_timeout * 25 ++ */ ++ writel(ltpi->ad_timeout * 25, ltpi->regs + LTPI_LINK_MANAGE_CTRL1); + -+ /* issue advance_state and wait on done */ -+ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, -+ NIST_TRNG_REG_CTRL_CMD_ADVANCE_STATE); -+ err = nisttrng_wait_on_done(state); -+ if (err) -+ goto ERR; ++ /* Set the clock source to the base frequency 25MHz */ ++ ltpi_phy_set_clksel(ltpi, REG_LTPI_PLL_25M, false); + -+ /* generate is finished, reset the bits_per_req_left counter */ -+ state->counters.bits_per_req_left = state->counters.max_bits_per_req; ++ /* To make the remote side back to the link lost state */ ++ mdelay(ADVERTISE_TIMEOUT_US / 1000); + -+ --state->counters.req_per_seed_left; -+ if (state->counters.req_per_seed_left < 0) { -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } /* just a check */ ++ ltpi_phy_set_mode(ltpi, LTPI_PHY_MODE_SDR); ++} + -+ err = CRYPTO_OK; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_advance_state */ ++static int scu_get_pll_freq(struct aspeed_ltpi_priv *ltpi, int pll_id) ++{ ++ const struct pll_info *info; ++ u32 reg; ++ int m, n, p; + -+int nisttrng_check_seed_lifetime(struct nist_trng_state *state) ++ if (pll_id > 1) ++ return -1; ++ ++ info = &scu_pll_info[pll_id]; ++ regmap_read(ltpi->scu, info->reg_offset0, ®); ++ m = FIELD_GET(PLL_REG1_M, reg); ++ n = FIELD_GET(PLL_REG1_N, reg); ++ p = FIELD_GET(PLL_REG1_P, reg); ++ ++ return (25000000 * (m + 1) / (n + 1) / (p + 1)); ++} ++ ++static int scu_set_pll_freq(struct aspeed_ltpi_priv *ltpi, int pll_id, int freq) +{ -+ int err; ++ const struct pll_info *info; ++ const struct pll_param *param; ++ int curr_freq, i; ++ bool match = false; + -+ if (state->counters.req_per_seed_left <= 0) { -+ DEBUG("maximum number of requests per seed is reached\n"); -+ err = CRYPTO_RESEED_REQUIRED; -+ goto ERR; ++ if (pll_id > 1) ++ return -EINVAL; ++ ++ curr_freq = scu_get_pll_freq(ltpi, pll_id); ++ if (curr_freq == freq) ++ return 0; ++ ++ for (i = 0; i < NUM_PLL_PARAM; i++) { ++ if (freq == pll_param_lookup[i].freq) { ++ match = true; ++ break; ++ } + } + -+ err = CRYPTO_OK; -+ERR: -+ return err; ++ if (!match) ++ return -EINVAL; ++ ++ param = &pll_param_lookup[i]; ++ info = &scu_pll_info[pll_id]; ++ ++ dev_info(ltpi->dev, "Setting PLL frequency to %d MHz\n", ++ freq / 1000000); ++ regmap_update_bits(ltpi->scu, info->reg_offset0, PLL_REG1_RESET, ++ PLL_REG1_RESET); ++ regmap_update_bits(ltpi->scu, info->reg_offset0, ++ PLL_REG1_P | PLL_REG1_N | PLL_REG1_M, param->n_m_p); ++ regmap_update_bits(ltpi->scu, info->reg_offset1, PLL_REG2_BWADJ, ++ param->bwadj); ++ ++ udelay(5); ++ regmap_update_bits(ltpi->scu, info->reg_offset0, PLL_REG1_RESET, 0); ++ udelay(20); ++ ++ return 0; +} -+EXPORT_SYMBOL(nisttrng_advance_state); + -+/* -+ * Perform Known Answer Test -+ * > The NIST_TRNG can perform a KAT on the DRBG and the derivation function inside the entropy source. There are also two different vectors available to do the KAT. -+ * > The kat_sel input selects whether the KAT should be performed on the DRBG or the derivation function. -+ * > The kat_vec input chooses the KAT vector. -+ * > Selections are done by writing the values to the MODE register. -+ * > If the KAT fails, the API returns error. -+ */ -+int nisttrng_kat(struct nist_trng_state *state, int kat_sel, int kat_vec) ++static int ltpi_set_operational_clk(struct aspeed_ltpi_priv *ltpi, ++ u16 speed_cap) +{ -+ int err; -+ u32 tmp; ++ const struct ltpi_clk_info *info; ++ int target_speed, clksel, phy_mode, pll_id; + -+ DEBUG(">> %s: kat_sel = %u, kat_vec = %u\n", __func__, -+ kat_sel, kat_vec); ++ pll_id = ltpi->index; /* 0 or 1 maps to L0PLL or L1PLL in array */ + -+ /* set KAT_SEL and KAT_VEC */ -+ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_MODE); -+ tmp = NIST_TRNG_REG_MODE_SET_KAT_SEL(tmp, kat_sel); -+ tmp = NIST_TRNG_REG_MODE_SET_KAT_VEC(tmp, kat_vec); -+ pdu_io_write32(state->base + NIST_TRNG_REG_MODE, tmp); ++ /* find max attainable speed */ ++ target_speed = find_max_speed(speed_cap); + -+ /* issue the command and wait on kat_completed */ -+ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, -+ NIST_TRNG_REG_CTRL_CMD_KAT); ++ /* set phy mode "OFF" */ ++ ltpi_phy_set_mode(ltpi, LTPI_PHY_MODE_OFF); + -+ err = nisttrng_wait_on_kat_completed(state); -+ if (err) -+ goto ERR; ++ if (speed_cap & LTPI_SP_CAP_DDR) { ++ phy_mode = LTPI_PHY_MODE_DDR; ++ info = ltpi_clk_lookup_ddr; ++ } else { ++ phy_mode = LTPI_PHY_MODE_SDR; ++ info = ltpi_clk_lookup_sdr; ++ } ++ clksel = info[target_speed].clk_sel; ++ ltpi_phy_set_clksel(ltpi, clksel, true); ++ if (clksel == REG_LTPI_PLL_LPLL) ++ scu_set_pll_freq(ltpi, pll_id, ++ info[target_speed].freq * 1000000); + -+ /* check for alarms */ -+ err = nisttrng_get_alarms(state); -+ if (err) -+ goto ERR; ++ /* Start TX with the operational frequency */ ++ ltpi_phy_set_mode(ltpi, phy_mode); + -+ err = CRYPTO_OK; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_kat */ -+EXPORT_SYMBOL(nisttrng_kat); ++ return target_speed; ++} + -+/* -+ * Performs a full KAT with all four combinations of the kat_sel and kat_vec -+ * > If any of the KAT fails, the API returns error. -+ */ -+int nisttrng_full_kat(struct nist_trng_state *state) ++static int ltpi_scm_init(struct aspeed_ltpi_priv *ltpi) +{ -+ int err; ++ int ret, target_speed; ++ u32 reg, state; + -+ DEBUG(">> %s starts...\n", __func__); ++ dev_info(ltpi->dev, "Starting LTPI initialization\n"); ++ /* Check whether LTPI is initialized */ ++ state = ltpi_get_link_mng_state(ltpi); ++ if (state == LTPI_LINK_MNG_ST_OP) { ++ dev_info(ltpi->dev, "LTPI already operational PHY mode: %d\n", ++ ltpi_phy_get_mode(ltpi)); ++ return 0; ++ } + -+ /* SEL = 0, Vec = 0 */ -+ err = nisttrng_kat(state, 0, 0); -+ if (err) -+ goto ERR; ++ ltpi->t_link_detect = ktime_get_ns(); + -+ /* SEL = 0, Vec = 1 */ -+ err = nisttrng_kat(state, 0, 1); -+ if (err) -+ goto ERR; ++ /* LTPI initialization is required, start link training phase */ ++ do { ++ dev_info(ltpi->dev, "Starting LTPI link training\n"); ++ ltpi_do_link_training(ltpi); ++ do { ++ ret = ltpi_wait_state_pll_set(ltpi, 20000); ++ if (ret == 0) ++ break; + -+ /* SEL = 1, Vec = 0 */ -+ err = nisttrng_kat(state, 1, 0); -+ if (err) -+ goto ERR; ++ if (ltpi->op_timeout && ++ (ktime_get_ns() - ltpi->t_link_detect > ++ ltpi->op_timeout)) { ++ dev_err(ltpi->dev, ++ "Timeout while waiting for PLL set state\n"); ++ ret = -ETIMEDOUT; ++ goto ltpi_scm_exit; ++ } ++ } while (1); ++ ++ /* read intersection of the speed capabilities */ ++ reg = FIELD_GET(REG_LTPI_SP_INTERSETION, ++ readl(ltpi->regs + LTPI_LINK_MNG_ST)); ++ if (reg == 0) { ++ dev_err(ltpi->dev, "No common speed\n"); ++ ret = -ENOLINK; ++ goto ltpi_scm_exit; ++ } ++ ++ target_speed = ltpi_set_operational_clk(ltpi, reg); ++ ++ /* poll link state 0x7 */ ++ ret = ltpi_wait_state_op(ltpi); ++ if (ret == 0) { ++ /* Start OEM TX & RX if the link partner is AST1700 */ ++ if (readl(ltpi->regs + LTPI_LINK_MNG_ST) & ++ REG_LTPI_LINK_PARTNER_FLAG) { ++ u32 val = readl(ltpi->regs + ++ LTPI_OEM_BUS_SETTING); ++ val |= (REG_LTPI_OEM_RX_START_TRIG | ++ REG_LTPI_OEM_TX_START_TRIG); ++ writel(val, ltpi->regs + LTPI_OEM_BUS_SETTING); ++ } ++ break; ++ } + -+ /* SEL = 1, Vec = 1 */ -+ err = nisttrng_kat(state, 1, 1); -+ if (err) -+ goto ERR; ++ if (ltpi->op_timeout && ++ (ktime_get_ns() - ltpi->t_link_detect > ltpi->op_timeout)) { ++ dev_err(ltpi->dev, ++ "Timeout while waiting for operational state\n"); ++ ret = -ETIMEDOUT; ++ goto ltpi_scm_exit; ++ } + -+ err = CRYPTO_OK; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_full_kat */ -+EXPORT_SYMBOL(nisttrng_full_kat); ++ if (!ltpi->disable_auto_downshift) ++ /* clear the bit to specify the current speed doesn't work */ ++ ltpi->phy_speed_cap &= ~BIT(target_speed); + -+/* -+ * max_bits_per_req reminder initialized by nisttrng_init can change using this API. -+ * > If this API is called when the DRBG is instantiated, an error will be returned. -+ * > If the requested maximum is more than the standard's limit (determinded by NIST_TRNG_DFLT_MAX_BITS_PER_REQ), the API will return an error. -+ */ -+int nisttrng_set_reminder_max_bits_per_req(struct nist_trng_state *state, -+ unsigned long max_bits_per_req) ++ /* the lowest speed 25M should always be supported */ ++ if ((ltpi->phy_speed_cap & LTPI_SP_CAP_25M) == 0) ++ ltpi->phy_speed_cap |= LTPI_SP_CAP_25M; ++ ++ dev_warn(ltpi->dev, ++ "Failed to enter operational state, restarting link training\n"); ++ } while (1); ++ ++ dev_info(ltpi->dev, "LTPI Link trained successfully\n"); ++ return 0; ++ ++ltpi_scm_exit: ++ dev_err(ltpi->dev, "Exiting initialization\n"); ++ ltpi_reset(ltpi); ++ return ret; ++} ++ ++/* Sysfs attribute show/store functions */ ++static ssize_t ad_timeout_show(struct device *dev, ++ struct device_attribute *attr, char *buf) +{ -+ int err; ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); + -+ DEBUG(">> %s: %lu\n", __func__, max_bits_per_req); ++ return sprintf(buf, "%d\n", priv->ad_timeout); ++} + -+ /* if the DRBG is instantiated, cannot change the value */ -+ if (DRBG_INSTANTIATED(state->status.current_state)) { -+ DEBUG("cannot change the reminder value when DRBG is already instantiated\n"); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++static ssize_t ad_timeout_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t count) ++{ ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); ++ unsigned int val; + -+ /* requested value cannot be more than NIST's limit */ -+ if (max_bits_per_req > NIST_DFLT_MAX_BITS_PER_REQ) { -+ DEBUG("requested max_bits_per_req is more than standard's limit\n"); -+ err = CRYPTO_INVALID_ARGUMENT; -+ goto ERR; -+ } ++ if (kstrtouint(buf, 0, &val)) ++ return -EINVAL; + -+ state->counters.max_bits_per_req = max_bits_per_req; -+ state->counters.bits_per_req_left = max_bits_per_req; ++ priv->ad_timeout = val; ++ return count; ++} + -+ err = CRYPTO_OK; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; ++static ssize_t io_driving_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "0x%x\n", priv->io_driving); +} -+EXPORT_SYMBOL(nisttrng_set_reminder_max_bits_per_req); + -+/* -+ * max_req_per_seed reminder initialized by nisttrng_init can change using this API. -+ * > If this API is called when the DRBG is instantiated, an error will be returned. -+ * > If the requested maximum is more than the standard's limit (determinded by NIST_TRNG_DFLT_MAX_REQ_PER_SEED), the API will return an error. -+ */ -+int nisttrng_set_reminder_max_req_per_seed(struct nist_trng_state *state, -+ unsigned long long max_req_per_seed) ++static ssize_t io_driving_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t count) +{ -+ int err; ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); ++ unsigned int val; + -+ DEBUG(">> %s: %llu\n", __func__, max_req_per_seed); ++ if (kstrtouint(buf, 0, &val)) ++ return -EINVAL; + -+ /* if the DRBG is instantiated, cannot change the value */ -+ if (DRBG_INSTANTIATED(state->status.current_state)) { -+ DEBUG("cannot change the reminder value when DRBG is already instantiated\n"); -+ err = CRYPTO_FAILED; -+ goto ERR; -+ } ++ priv->io_driving = val; ++ return count; ++} + -+ /* requested value cannot be more than NIST's limit */ -+ if (max_req_per_seed > NIST_DFLT_MAX_REQ_PER_SEED) { -+ DEBUG("requested max_req_per_seed is more than standard's limit\n"); -+ err = CRYPTO_INVALID_ARGUMENT; -+ goto ERR; -+ } ++static ssize_t clk_inverse_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); + -+ state->counters.max_req_per_seed = max_req_per_seed; -+ state->counters.req_per_seed_left = max_req_per_seed; ++ return sprintf(buf, "0x%x\n", priv->clk_inverse); ++} + -+ err = CRYPTO_OK; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; ++static ssize_t clk_inverse_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t count) ++{ ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); ++ unsigned int val; ++ ++ if (kstrtouint(buf, 0, &val)) ++ return -EINVAL; ++ ++ priv->clk_inverse = val; ++ return count; +} -+EXPORT_SYMBOL(nisttrng_set_reminder_max_req_per_seed); + -+/* -+ * Zeroize command -+ * > A zeroize will not destroy the programmed mode and ALARM register value. -+ * It keeps the programmed mode to avoid re-programming. -+ * It also, maintains the ALARM register value, so that the user can read the value to understand the reason of the occurred alarm. -+ */ -+int nisttrng_zeroize(struct nist_trng_state *state) ++static ssize_t link_speed_frm_rx_cnt_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) +{ -+ int err; ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); + -+ DEBUG(">> %s: zeroize the core\n", __func__); ++ return sprintf(buf, "%d\n", priv->link_speed_frm_rx_cnt); ++} + -+ /* issue zeroize command */ -+ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, -+ NIST_TRNG_REG_CTRL_CMD_ZEROIZE); ++static ssize_t link_speed_frm_rx_cnt_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); ++ unsigned int val; + -+ /* wait on zeroize done */ -+ err = nisttrng_wait_on_zeroize(state); -+ if (err) -+ goto ERR; ++ if (kstrtouint(buf, 0, &val)) ++ return -EINVAL; + -+ /* reset the SW state */ -+ nisttrng_reset_state(state); ++ priv->link_speed_frm_rx_cnt = val; ++ return count; ++} + -+ err = CRYPTO_OK; -+ state->status.current_state = NIST_TRNG_STATE_UNINSTANTIATE; -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; -+} /* nisttrng_zeroize */ -+EXPORT_SYMBOL(nisttrng_zeroize); ++static ssize_t disable_auto_downshift_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); + -+int nisttrng_rnc(struct nist_trng_state *state, int rnc_ctrl_cmd) ++ return sprintf(buf, "%d\n", priv->disable_auto_downshift); ++} ++ ++static ssize_t disable_auto_downshift_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) +{ -+ int err = 0; -+ u32 tmp; ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); ++ unsigned int val; + -+ DEBUG(">> %s cmd %d\n", __func__, rnc_ctrl_cmd); ++ if (kstrtouint(buf, 0, &val)) ++ return -EINVAL; + -+ if (rnc_ctrl_cmd > NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_FINISH_TO_IDLE) { -+ DEBUG(">> Invalid cmd %d\n", rnc_ctrl_cmd); -+ err = -1; -+ goto ERR; -+ } ++ priv->disable_auto_downshift = val ? 1 : 0; ++ return count; ++} + -+ if (!state->config.build_cfg0.edu_present) { -+ DEBUG(">> edu not present\n"); -+ err = -1; -+ goto ERR; -+ } ++static ssize_t crc_format_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); + -+ pdu_io_write32(state->base + NIST_TRNG_EDU_RNC_CTRL, rnc_ctrl_cmd); -+ if (rnc_ctrl_cmd == NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_ENABLE) { -+ // wait till rnc is enabled -+ do { -+ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_STAT); -+ } while (!NIST_TRNG_EDU_STAT_RNC_ENABLED(tmp)); ++ return sprintf(buf, "%d\n", priv->crc_format); ++} + -+ state->status.edu_vstat.rnc_enabled = 1; ++static ssize_t crc_format_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t count) ++{ ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); ++ unsigned int val; + -+ } else { -+ // wait till rnc is idle (disabled) -+ do { -+ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_STAT); -+ } while (NIST_TRNG_EDU_STAT_RNC_ENABLED(tmp)); ++ if (kstrtouint(buf, 0, &val)) ++ return -EINVAL; + -+ state->status.edu_vstat.rnc_enabled = 0; -+ } -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; ++ priv->crc_format = val ? 1 : 0; ++ return count; +} -+EXPORT_SYMBOL(nisttrng_rnc); + -+int nisttrng_wait_fifo_full(struct nist_trng_state *state) ++static ssize_t phy_speed_cap_show(struct device *dev, ++ struct device_attribute *attr, char *buf) +{ -+ int err = 0; -+ u32 tmp, t; ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); + -+ t = NIST_TRNG_RETRY_MAX; ++ return sprintf(buf, "0x%x\n", priv->phy_speed_cap); ++} + -+ DEBUG(">> %s starts...\n", __func__); ++static ssize_t phy_speed_cap_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); ++ unsigned int val; + -+ do { -+ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_STAT); -+ } while ((!NIST_TRNG_EDU_STAT_FIFO_FULL(tmp)) && --t); ++ if (kstrtouint(buf, 0, &val)) ++ return -EINVAL; + -+ if (t) { -+ err = CRYPTO_OK; -+ } else { -+ DEBUG("wait_on_fifo_full: failed timeout: %08lx\n", -+ (unsigned long)tmp); -+ err = CRYPTO_TIMEOUT; -+ goto ERR; ++ priv->phy_speed_cap = val; ++ return count; ++} ++ ++static ssize_t op_timeout_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%llu\n", priv->op_timeout); ++} ++ ++static ssize_t op_timeout_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t count) ++{ ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); ++ unsigned long long val; ++ ++ if (kstrtoull(buf, 0, &val)) ++ return -EINVAL; ++ ++ priv->op_timeout = val; ++ return count; ++} ++ ++static ssize_t rescan_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); ++ u32 val, reg; ++ ++ if (kstrtouint(buf, 0, &val)) ++ return -EINVAL; ++ ++ if (val == 1 && priv->version == AST2700) { ++ dev_info(priv->dev, "Triggering LTPI rescan\n"); ++ if (ltpi_scm_init(priv)) { ++ dev_err(priv->dev, "LTPI rescan failed\n"); ++ return -EIO; ++ } ++ writel(LTPI_INTR_EN_OP_LINK_LOST, ++ priv->regs + LTPI_INTR_STATUS); ++ writel(LTPI_INTR_EN_OP_LINK_LOST, priv->regs + LTPI_INTR_EN); ++ aspeed_ltpi_init_mux(priv); ++ if (ltpi_get_link_partner(priv)) { ++ reg = FIELD_PREP(REG_LTPI_AHB_ADDR_MAP0, 0x5) | ++ FIELD_PREP(REG_LTPI_AHB_ADDR_MAP1, 0xa0); ++ } else { ++ reg = 0; ++ writel(0, priv->regs + LTPI_DATA_CH_CFG0); ++ } ++ writel(reg, priv->regs + LTPI_AHB_CTRL0); + } + -+ERR: -+ DEBUG("--- Return %s, err = %i\n", __func__, err); -+ return err; ++ return count; +} -diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c ---- a/drivers/char/ipmi/kcs_bmc_aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c 2025-12-23 10:16:21.105032988 +0000 -@@ -1,13 +1,14 @@ - // SPDX-License-Identifier: GPL-2.0 - /* - * Copyright (c) 2015-2018, Intel Corporation. -+ * Copyright (c) 2023, Aspeed Technology Inc. - */ -- - #define pr_fmt(fmt) "aspeed-kcs-bmc: " fmt - - #include - #include ++ ++static ssize_t link_status_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct aspeed_ltpi_priv *priv = dev_get_drvdata(dev); ++ u32 state = ltpi_get_link_mng_state(priv); ++ char state_str[128]; ++ int speed; ++ u32 phy_mode, clk_select; ++ char modes[9][8] = { "OFF", "SDR", "DDR", "NA", "CDR_LO", ++ "NA", "NA", "NA", "CDR_HI" }; ++ ++ if (state != LTPI_LINK_MNG_ST_OP) { ++ sprintf(state_str, "LTPI: not linked\n"); ++ } else { ++ phy_mode = FIELD_GET(REG_LTPI_PHY_MODE, readl(priv->phy_regs + LTPI_PHY_CTRL)); ++ clk_select = FIELD_GET(REG_LTPI_PLL_SELECT, readl(priv->phy_regs + LTPI_PLL_CTRL)); ++ if (clk_select == REG_LTPI_PLL_LPLL) ++ speed = scu_get_pll_freq(priv, priv->index) / 1000000; ++ else ++ speed = 25; ++ ++ sprintf(state_str, ++ "LTPI%d:\n" ++ " link partner : %s\n" ++ " link mode : %s\n" ++ " link bandwidth : %dMbps\n", ++ priv->index, ++ ltpi_get_link_partner(priv) ? "ast1700" : "fpga", ++ &modes[phy_mode][0], speed); ++ } ++ ++ return sprintf(buf, "%s\n", state_str); ++} ++ ++/* Device attributes */ ++static DEVICE_ATTR_RW(ad_timeout); ++static DEVICE_ATTR_RW(io_driving); ++static DEVICE_ATTR_RW(clk_inverse); ++static DEVICE_ATTR_RW(link_speed_frm_rx_cnt); ++static DEVICE_ATTR_RW(disable_auto_downshift); ++static DEVICE_ATTR_RW(crc_format); ++static DEVICE_ATTR_RW(phy_speed_cap); ++static DEVICE_ATTR_RW(op_timeout); ++static DEVICE_ATTR_WO(rescan); ++static DEVICE_ATTR_RO(link_status); ++ ++static struct attribute *aspeed_ltpi_attrs[] = { ++ &dev_attr_ad_timeout.attr, ++ &dev_attr_io_driving.attr, ++ &dev_attr_clk_inverse.attr, ++ &dev_attr_link_speed_frm_rx_cnt.attr, ++ &dev_attr_disable_auto_downshift.attr, ++ &dev_attr_crc_format.attr, ++ &dev_attr_phy_speed_cap.attr, ++ &dev_attr_op_timeout.attr, ++ &dev_attr_rescan.attr, ++ &dev_attr_link_status.attr, ++ NULL, ++}; ++ ++static const struct attribute_group aspeed_ltpi_attr_group = { ++ .attrs = aspeed_ltpi_attrs, ++}; ++ ++static int aspeed_ltpi_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const struct of_dev_auxdata *lookup = dev_get_platdata(dev); ++ struct device_node *np = dev->of_node; ++ const struct of_device_id *match; ++ struct aspeed_ltpi_priv *priv; ++ int irq, ret; ++ struct resource *res; ++ ++ match = of_match_device(dev->driver->of_match_table, dev); ++ ++ if (match) { ++ if (of_property_match_string(np, "compatible", ++ match->compatible) < 0) ++ return -ENODEV; ++ } else { ++ return -ENODEV; ++ } ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->dev = dev; ++ priv->regs = devm_platform_ioremap_resource_byname(pdev, "base"); ++ if (IS_ERR(priv->regs)) ++ priv->regs = ++ devm_platform_ioremap_resource(pdev, 0); // Fallback ++ if (IS_ERR(priv->regs)) ++ return PTR_ERR(priv->regs); ++ ++ priv->phy_regs = devm_platform_ioremap_resource_byname(pdev, "phy"); ++ if (IS_ERR(priv->phy_regs)) ++ priv->phy_regs = ++ priv->regs + 0x200; // Fallback unsafe if size small ++ ++ priv->top_regs = devm_platform_ioremap_resource_byname(pdev, "top"); ++ if (IS_ERR(priv->top_regs)) ++ priv->top_regs = ++ priv->regs + 0x800; // Fallback unsafe if size small ++ ++ /* Identify index based on physical address for PLL control */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res && (res->start & 0x1000)) ++ priv->index = 1; ++ else ++ priv->index = 0; ++ ++ priv->version = (enum chip_version)device_get_match_data(dev); ++ ++ priv->ltpi_clk = devm_clk_get(&pdev->dev, "ltpi"); ++ if (IS_ERR(priv->ltpi_clk)) { ++ priv->ltpi_clk = devm_clk_get(&pdev->dev, "ahb"); ++ if (IS_ERR(priv->ltpi_clk)) ++ return PTR_ERR(priv->ltpi_clk); ++ ++ clk_prepare_enable(priv->ltpi_clk); ++ ++ priv->ltpi_phyclk = devm_clk_get(&pdev->dev, "phy"); ++ if (IS_ERR(priv->ltpi_phyclk)) ++ return PTR_ERR(priv->ltpi_phyclk); ++ ++ clk_prepare_enable(priv->ltpi_phyclk); ++ } else { ++ priv->ltpi_phyclk = NULL; ++ } ++ ++ priv->ltpi_rst = ++ devm_reset_control_get_optional_shared(&pdev->dev, NULL); ++ if (IS_ERR(priv->ltpi_rst)) ++ return PTR_ERR(priv->ltpi_rst); ++ ++ reset_control_deassert(priv->ltpi_rst); ++ ++ priv->i2c_tunneling = GENMASK(MAX_I2C_IN_LTPI - 1, 0); ++ if (!of_property_read_u32(np, "i2c-tunneling", &ret)) ++ priv->i2c_tunneling = ret; ++ ++ priv->i2c_timing_0 = LTPI_I2C_100K_0; ++ priv->i2c_timing_1 = LTPI_I2C_100K_1; ++ if (!of_property_read_u32(np, "i2c-tunneling-timing", &ret)) { ++ if (ret == 400) { ++ priv->i2c_timing_0 = LTPI_I2C_400K_0; ++ priv->i2c_timing_1 = LTPI_I2C_400K_1; ++ } ++ } ++ priv->uart_tunneling = GENMASK(MAX_UART_IN_LTPI - 1, 0); ++ if (!of_property_read_u32(np, "uart-tunneling", &ret)) ++ priv->uart_tunneling = ret; ++ ++ priv->scu = syscon_regmap_lookup_by_phandle(np, "aspeed,scu"); ++ if (IS_ERR(priv->scu)) { ++ dev_err(&pdev->dev, "failed to get SCU regmap\n"); ++ return PTR_ERR(priv->scu); ++ } ++ if (of_get_property(np, "remote-controller", NULL)) { ++ u32 reg; ++ ++ /* Clear all the pins/otp strap but LTPI related settings for AST1700 */ ++ regmap_read(priv->scu, SCU_IO_PINS_TRAP1, ®); ++ reg &= ~SCU_IO_PINS_TRAP_LTPI; ++ regmap_write(priv->scu, SCU_IO_PINS_TRAP1_CLEAR, reg); ++ ++ regmap_read(priv->scu, SCU_IO_OTP_TRAP1, ®); ++ regmap_write(priv->scu, SCU_IO_OTP_TRAP1_CLEAR, reg); ++ ++ regmap_read(priv->scu, SCU_IO_OTP_TRAP2, ®); ++ regmap_write(priv->scu, SCU_IO_OTP_TRAP2_CLEAR, reg); ++ } else { ++ irq = platform_get_irq(pdev, 0); ++ ret = devm_request_irq(priv->dev, irq, aspeed_ltpi_irq_handler, ++ 0, dev_name(priv->dev), priv); ++ if (ret) { ++ dev_err(priv->dev, "failed to request irq\n"); ++ reset_control_assert(priv->ltpi_rst); ++ clk_disable_unprepare(priv->ltpi_phyclk); ++ clk_disable_unprepare(priv->ltpi_clk); ++ return ret; ++ } ++ ++ writel(LTPI_INTR_EN_OP_LINK_LOST, ++ priv->regs + LTPI_INTR_STATUS); ++ writel(LTPI_INTR_EN_OP_LINK_LOST, priv->regs + LTPI_INTR_EN); ++ } ++ ++ /* Initialize training parameters with defaults */ ++ priv->ad_timeout = ADVERTISE_TIMEOUT_US; ++ priv->io_driving = 0x2; ++ priv->clk_inverse = 0x0; ++ priv->link_speed_frm_rx_cnt = 4; ++ priv->disable_auto_downshift = 0; ++ priv->crc_format = 0; ++ priv->otp_ddr_dis = false; ++ priv->op_timeout = 2000000000; /* 2 seconds timeout by default */ ++ priv->phy_speed_cap = LTPI_SP_CAP_ASPEED_SUPPORTED; ++ ++ aspeed_ltpi_init_mux(priv); ++ ++ platform_set_drvdata(pdev, priv); ++ ++ /* Create sysfs attributes */ ++ ret = sysfs_create_group(&dev->kobj, &aspeed_ltpi_attr_group); ++ if (ret) { ++ dev_err(dev, "Failed to create sysfs group\n"); ++ reset_control_assert(priv->ltpi_rst); ++ clk_disable_unprepare(priv->ltpi_phyclk); ++ clk_disable_unprepare(priv->ltpi_clk); ++ return ret; ++ } ++ ++ if (np) ++ of_platform_populate(np, NULL, lookup, priv->dev); ++ ++ return 0; ++} ++ ++static void aspeed_ltpi_remove(struct platform_device *pdev) ++{ ++ struct aspeed_ltpi_priv *priv; ++ ++ priv = platform_get_drvdata(pdev); ++ ++ /* Remove sysfs attributes */ ++ sysfs_remove_group(&pdev->dev.kobj, &aspeed_ltpi_attr_group); ++ ++ reset_control_assert(priv->ltpi_rst); ++ clk_disable_unprepare(priv->ltpi_phyclk); ++ clk_disable_unprepare(priv->ltpi_clk); ++} ++ ++static const struct of_device_id aspeed_ltpi_of_match[] = { ++ { ++ .compatible = "aspeed-ltpi", ++ .data = (const void *)AST2700, ++ }, ++ { ++ .compatible = "aspeed-ast1700-ltpi", ++ .data = (const void *)AST1700, ++ }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, aspeed_ltpi_of_match); ++ ++static struct platform_driver aspeed_ltpi_driver = { ++ .probe = aspeed_ltpi_probe, ++ .remove = aspeed_ltpi_remove, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = aspeed_ltpi_of_match, ++ }, ++}; ++ ++module_platform_driver(aspeed_ltpi_driver); ++ ++MODULE_DESCRIPTION("LVDS Tunneling Protocol and Interface Bus Driver"); ++MODULE_AUTHOR("Dylan Hung "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/bus/aspeed-ltpi.h b/drivers/bus/aspeed-ltpi.h +--- a/drivers/bus/aspeed-ltpi.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/bus/aspeed-ltpi.h 2026-04-08 18:03:48.134708536 +0000 +@@ -0,0 +1,276 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2024 ASPEED Technology Inc. ++ */ ++#ifndef __ASPEED_LTPI_H__ ++#define __ASPEED_LTPI_H__ ++ ++#include ++ ++/* OCP_DC-SCM_2.0_LTPI_ver_1.0, Table 21 LTPI speed capability encoding */ ++#define LTPI_SP_CAP_25M BIT(0) ++#define LTPI_SP_CAP_50M BIT(1) ++#define LTPI_SP_CAP_75M BIT(2) ++#define LTPI_SP_CAP_100M BIT(3) ++#define LTPI_SP_CAP_150M BIT(4) ++#define LTPI_SP_CAP_200M BIT(5) ++#define LTPI_SP_CAP_250M BIT(6) ++#define LTPI_SP_CAP_300M BIT(7) ++#define LTPI_SP_CAP_400M BIT(8) ++#define LTPI_SP_CAP_600M BIT(9) ++#define LTPI_SP_CAP_800M BIT(10) ++#define LTPI_SP_CAP_1G BIT(11) ++#define LTPI_SP_CAP_500M BIT(12) /* Aspeed only */ ++/* --gap-- */ ++#define LTPI_SP_CAP_DDR BIT(15) ++ ++#define LTPI_SP_CAP_ASPEED_SUPPORTED \ ++ (LTPI_SP_CAP_25M | LTPI_SP_CAP_50M | LTPI_SP_CAP_75M | \ ++ LTPI_SP_CAP_100M | LTPI_SP_CAP_150M | LTPI_SP_CAP_200M | \ ++ LTPI_SP_CAP_250M | LTPI_SP_CAP_300M | LTPI_SP_CAP_400M | \ ++ LTPI_SP_CAP_500M | LTPI_SP_CAP_DDR) ++ ++/* control registers */ ++#define LTPI_LINK_ST 0x000 ++#define REG_LTPI_LINK_ST_LOCAL GENMASK(19, 16) ++#define REG_LTPI_LINK_ST_REMOTE GENMASK(15, 12) ++#define REG_LTPI_LINK_SPEED GENMASK(11, 8) ++#define REG_LTPI_LINK_DDR_MODE BIT(7) ++#define REG_LTPI_LINK_ST_RESERVED BIT(6) ++#define REG_LTPI_CON_ACC_TO_ERR BIT(5) ++#define REG_LTPI_LINK_SP_TO_ERR BIT(4) ++#define REG_LTPI_UNKNOWN_COMMA_ERR BIT(3) ++#define REG_LTPI_FRM_CRC_ERR BIT(2) ++#define REG_LTPI_LINK_LOST_ERR BIT(1) ++#define REG_LTPI_LINK_ALIGN BIT(0) ++ ++#define LTPI_CAP_LOCAL 0x004 ++#define REG_LTPI_SP_CAP_LOCAL GENMASK(23, 8) ++#define REG_LTPI_MAJOR_VER_LOCAL GENMASK(7, 4) ++#define REG_LTPI_MIN_VER_LOCAL GENMASK(3, 0) ++#define LTPI_CAP_REMOTE 0x008 ++#define REG_LTPI_SP_CAP_REMOTE GENMASK(23, 8) ++#define REG_LTPI_MAJOR_VER_REMOTE GENMASK(7, 4) ++#define REG_LTPI_MIN_VER_REMOTE GENMASK(3, 0) ++#define LTPI_PF_ID_LOCAL 0x00C ++#define LTPI_PF_ID_REMOTE 0x010 ++#define LTPI_AD_CAP_LOW_LOCAL 0x014 ++#define LTPI_AD_CAP_HIGH_LOCAL 0x018 ++#define LTPI_AD_CAP_LOW_REMOTE 0x01C ++#define LTPI_AD_CAP_HIGH_REMOTE 0x020 ++#define LTPI_DEFAULT_CON_LOW 0x024 ++#define LTPI_DEFAULT_CON_HIGH 0x028 ++#define LTPI_LINK_ALIGN_ERR_CNT 0x02C ++#define LTPI_LINK_LOST_ERR_CNT 0x030 ++#define LTPI_CRC_ERR_CNT 0x034 ++#define LTPI_UNKNOWN_COMMA_ERR_CNT 0x038 ++#define LTPI_LINK_SP_TO_ERR_CNT 0x03C ++#define LTPI_CON_ACC_TO_ERR_CNT 0x040 ++#define LTPI_LINK_TRAIN_RX_FRM_CNT_LOW 0x044 ++#define REG_LTPI_CON_ACC_FRM_RX_CNT GENMASK(31, 24) ++#define REG_LTPI_LINK_SP_FRM_RX_CNT GENMASK(23, 16) ++#define REG_LTPI_LINK_DET_FRM_RX_CNT GENMASK(15, 0) ++#define LTPI_LINK_TRAIN_RX_FRM_CNT_HIGH 0x048 ++#define LTPI_LINK_TRAIN_TX_FRM_CNT_LOW 0x04C ++#define REG_LTPI_CON_ACC_FRM_TX_CNT GENMASK(31, 24) ++#define REG_LTPI_LINK_SP_FRM_TX_CNT GENMASK(23, 16) ++#define REG_LTPI_LINK_DET_FRM_TX_CNT GENMASK(15, 0) ++#define LTPI_LINK_TRAIN_TX_FRM_CNT_HIGH 0x050 ++#define LTPI_OP_RX_FRM_CNT 0x054 ++#define LTPI_OP_TX_FRM_CNT 0x058 ++#define LTPI_LINK_LOST_CNT 0x05c ++#define REG_LTPI_AD_ALIGN_TO_CNT GENMASK(27, 24) ++#define REG_LTPI_LINK_LOST_OP_CNT GENMASK(23, 16) ++#define REG_LTPI_LINK_LOST_CON_OR_ACC_CNT GENMASK(15, 8) ++#define REG_LTPI_LINK_LOST_AD_CNT GENMASK(7, 0) ++#define LTPI_LINK_CONTROL 0x080 ++#define REG_LTPI_TRIG_CON_ST BIT(11) ++#define REG_LTPI_AUTO_CON_ST BIT(10) ++#define REG_LTPI_DATA_CH_RST BIT(9) ++#define REG_LTPI_I2C_RST GENMASK(8, 2) ++#define REG_LTPI_LINK_RETRAIN_REQ BIT(1) ++#define REG_LTPI_LINK_SW_RST BIT(0) ++ ++#define LTPI_INTR 0x100 ++#define REG_LTPI_LINK_HALT BIT(24) ++#define REG_LTPI_DATA_CH_DROP_FRAME BIT(23) ++#define REG_LTPI_DATA_CH_INVALID_ACCESS BIT(22) ++#define REG_LTPI_I2C5_TO_ERR BIT(21) ++#define REG_LTPI_I2C4_TO_ERR BIT(20) ++#define REG_LTPI_I2C3_TO_ERR BIT(19) ++#define REG_LTPI_I2C2_TO_ERR BIT(18) ++#define REG_LTPI_I2C1_TO_ERR BIT(17) ++#define REG_LTPI_I2C0_TO_ERR BIT(16) ++#define REG_LTPI_AD_ALIGN_TO BIT(12) ++#define REG_LTPI_HW_RETRY_LINK_DET_STUCK_TO_ERR BIT(11) ++#define REG_LTPI_HW_RETRY_TO_ERR BIT(10) ++#define REG_LTPI_OP_LINK_LOST_ERR BIT(4) ++#define REG_LTPI_CON_OR_ACC_LINK_LOST_ERR BIT(3) ++#define REG_LTPI_AD_LINK_LOST_ERR BIT(2) ++#define REG_LTPI_CON_READY BIT(1) ++#define REG_LTPI_AD_READY BIT(0) ++#define LTPI_INTR_EN 0x104 ++#define REG_LTPI_LINK_HALT_INTR_EN BIT(24) ++#define REG_LTPI_DATA_CH_DROP_FRAME_EN BIT(23) ++#define REG_LTPI_DATA_CH_INVALID_ACCESS_EN BIT(22) ++#define REG_LTPI_I2C5_TO_ERR_EN BIT(21) ++#define REG_LTPI_I2C4_TO_ERR_EN BIT(20) ++#define REG_LTPI_I2C3_TO_ERR_EN BIT(19) ++#define REG_LTPI_I2C2_TO_ERR_EN BIT(18) ++#define REG_LTPI_I2C1_TO_ERR_EN BIT(17) ++#define REG_LTPI_I2C0_TO_ERR_EN BIT(16) ++#define REG_LTPI_AD_ALIGN_TO_EN BIT(12) ++#define REG_LTPI_HW_RETRY_LINK_DET_STUCK_TO_ERR_EN BIT(11) ++#define REG_LTPI_HW_RETRY_TO_ERR_EN BIT(10) ++#define REG_LTPI_CON_ACC_TO_ERR_EN BIT(9) ++#define REG_LTPI_LINK_SP_TO_ERR_EN BIT(8) ++#define REG_LTPI_UNKNOWN_COMMA_ERR_EN BIT(7) ++#define REG_LTPI_FRM_CRC_ERR_EN BIT(6) ++#define REG_LTPI_LINK_LOST_ERR_EN BIT(5) ++#define REG_LTPI_OP_LINK_LOST_ERR_EN BIT(4) ++#define REG_LTPI_CON_OR_ACC_LINK_LOST_ERR_EN BIT(3) ++#define REG_LTPI_AD_LINK_LOST_ERR_EN BIT(2) ++#define REG_LTPI_CON_READY_EN BIT(1) ++#define REG_LTPI_AD_READY_EN BIT(0) ++#define LTPI_LINK_MNG_ST 0x108 ++#define REG_LTPI_LINK_PARTNER_FLAG BIT(24) ++#define REG_LTPI_LINK_PARTNER_FPGA 0b0 ++#define REG_LTPI_LINK_PARTNER_1700 0b1 ++#define REG_LTPI_SP_INTERSETION GENMASK(23, 8) ++#define REG_LTPI_LINK_MNG_ST GENMASK(3, 0) ++#define LTPI_LINK_MNG_ST_DETECT_ALIGN 0 ++#define LTPI_LINK_MNG_ST_DETECT 1 ++#define LTPI_LINK_MNG_ST_SPEED 2 ++#define LTPI_LINK_MNG_ST_WAIT_PLL_SET 3 ++#define LTPI_LINK_MNG_ST_ADV_ALIGN 4 ++#define LTPI_LINK_MNG_ST_ADV 5 ++#define LTPI_LINK_MNG_ST_CONFIG_ACC 6 ++#define LTPI_LINK_MNG_ST_OP 7 ++#define LTPI_LINK_MANAGE_CTRL0 0x10C ++#define REG_LTPI_LINK_PARTNER_CHKNUM GENMASK(19, 16) ++#define REG_LTPI_CON_FRM_NOCHK BIT(8) ++#define REG_LTPI_TX_LINK_SP_FRM_NUM GENMASK(7, 4) ++#define REG_LTPI_RX_LINK_SP_FRM_NUM GENMASK(3, 0) ++#define LTPI_LINK_MANAGE_CTRL1 0x110 ++#define LTPI_LINK_MANAGE_CTRL2 0x114 ++#define LTPI_CON_CAP_LOW 0x118 ++#define LTPI_CON_CAP_HIGH 0x11C ++#define LTPI_MAC_CTRL 0x120 ++#define LTPI_AHB_CTRL0 0x124 ++#define REG_LTPI_AHB_ADDR_MAP1 GENMASK(25, 16) ++#define REG_LTPI_AHB_ADDR_MAP0 GENMASK(9, 0) ++#define LTPI_AHB_CTRL1 0x128 ++#define REG_LTPI_AHB_ADDR_MAP3 GENMASK(25, 16) ++#define REG_LTPI_AHB_ADDR_MAP2 GENMASK(9, 0) ++#define LTPI_CRC_OPTION 0x12c ++#define REG_LTPI_SW_CRC_OUT_ML_FIRST BIT(2) ++#define REG_LTPI_SW_CRC_IN_LSB_FIRST BIT(1) ++#define REG_LTPI_CRC_SW_FORCE BIT(0) ++#define LTPI_I2C 0x130 ++#define REG_LTPI_I2C_SLV_MST_SWITCH_SW_EN BIT(8) ++#define REG_LTPI_I2C_SLV_EN GENMASK(5, 0) ++ ++#define LTPI_OEM_BUS_SETTING 0x188 ++#define REG_LTPI_OEM_RX_START_TRIG BIT(1) ++#define REG_LTPI_OEM_TX_START_TRIG BIT(0) ++ ++#define LTPI_OEM_DBG0 0x190 ++#define REG_LTPI_OEM_RX_INIT_DONE BIT(9) ++#define REG_LTPI_OEM_TX_INIT_DONE BIT(8) ++ ++#define LTPI_DATA_CH_CFG0 0x1D8 ++#define REG_LTPI_DATA_CH_TAG_CHK_EN BIT(2) ++#define REG_LTPI_DATA_CH_ADDR_CHK_EN BIT(1) ++#define REG_LTPI_DATA_CH_WAIT_ACK_TO_EN BIT(0) ++ ++/* LTPI PHY control registers */ ++#define LTPI_PHY_CTRL 0x000 ++#define REG_LTPI_PHY_MODE GENMASK(3, 0) ++#define LTPI_PHY_MODE_CDR_HI_SP 0b1000 ++#define LTPI_PHY_MODE_CDR_LO_SP 0b0100 ++#define LTPI_PHY_MODE_DDR 0b0010 ++#define LTPI_PHY_MODE_SDR 0b0001 ++#define LTPI_PHY_MODE_OFF 0b0000 ++#define LTPI_PLL_CTRL 0x004 ++#define REG_LTPI_RX_PHY_CLK_INV BIT(9) ++#define REG_LTPI_TX_PHY_CLK_INV BIT(8) ++#define REG_LTPI_PLL_SET BIT(4) ++#define REG_LTPI_RX_PLL_DIV2 BIT(3) ++#define REG_LTPI_PLL_SELECT GENMASK(2, 0) ++#define REG_LTPI_PLL_25M 0 ++/* AST2700A0 */ ++#define REG_LTPI_PLL_50M 1 ++#define REG_LTPI_PLL_100M 2 ++#define REG_LTPI_PLL_200M 3 ++#define REG_LTPI_PLL_250M 4 ++#define REG_LTPI_PLL_400M 5 ++#define REG_LTPI_PLL_800M 6 ++#define REG_LTPI_PLL_1G 7 ++/* AST2700A1 */ ++#define REG_LTPI_PLL_LPLL 7 ++ ++#define LTPI_PHY_ALIGN_CTRL 0x008 ++#define LTPI_DLL_CTRL 0x00C ++#define REG_LTPI_SW_DLL_RST BIT(26) ++#define REG_LTPI_FORCE_DLL_RST BIT(25) ++#define REG_LTPI_DLL_PD BIT(24) ++#define REG_LTPI_DLL_TSTCTRL GENMASK(23, 16) ++#define REG_LTPI_DLL_RST_TIMEOUT_A0 GENMASK(15, 0) ++#define REG_LTPI_DLL_CLK_2X BIT(8) ++#define REG_LTPI_DLL_RST_TIMEOUT GENMASK(7, 0) ++#define LTPI_PHY_HI_SP_CDR_CTRL 0x010 ++#define REG_LTPI_SW_HI_SP_CDR_EN BIT(10) ++#define REG_LTPI_FORCE_HI_SP_CDR_EN BIT(9) ++#define REG_LTPI_HI_SP_CDR_EN_TIMEOUT_EN BIT(8) ++#define LTPI_HI_SP_CDR_EN_TIMEOUT GENMASK(7, 0) ++ ++#define LTPI_PROT_KEY 0x34 ++#define LTPI_PROT_KEY_UNLOCK 0x1728aacc ++ ++/* LVDS TOP registers */ ++#define LTPI_LVDS_TX_CTRL 0x000 ++#define REG_LTPI_LVDS_TX1_DS1 BIT(22) ++#define REG_LTPI_LVDS_TX1_DS0 BIT(21) ++#define REG_LTPI_LVDS_TX1_IPREE BIT(20) ++#define REG_LTPI_LVDS_TX1_IPREE_EN BIT(19) ++#define REG_LTPI_LVDS_TX1_PD BIT(18) ++#define REG_LTPI_LVDS_TX1_PU BIT(17) ++#define REG_LTPI_LVDS_TX1_OE BIT(16) ++#define REG_LTPI_LVDS_TX0_DS1 BIT(6) ++#define REG_LTPI_LVDS_TX0_DS0 BIT(5) ++#define REG_LTPI_LVDS_TX0_IPREE BIT(4) ++#define REG_LTPI_LVDS_TX0_IPREE_EN BIT(3) ++#define REG_LTPI_LVDS_TX0_PD BIT(2) ++#define REG_LTPI_LVDS_TX0_PU BIT(1) ++#define REG_LTPI_LVDS_TX0_OE BIT(0) ++#define LTPI_LVDS_RX_CTRL 0x004 ++#define REG_LTPI_LVDS_RX1_BIAS_EN BIT(18) ++#define REG_LTPI_LVDS_RX1_ST BIT(17) ++#define REG_LTPI_LVDS_RX1_IE BIT(16) ++#define REG_LTPI_LVDS_RX0_BIAS_EN BIT(2) ++#define REG_LTPI_LVDS_RX0_ST BIT(1) ++#define REG_LTPI_LVDS_RX0_IE BIT(0) ++#define LTPI_SW_RST 0x008 ++#define REG_LTPI_ALL_SW_RST BIT(31) ++#define REG_LTPI1_SW_RST BIT(17) ++#define REG_LTPI0_SW_RST BIT(16) ++#define REG_LTPI_DLL_CTRL_SW_RST BIT(9) ++#define REG_LTPI_REF_SW_RST BIT(8) ++#define REG_LTPI1_RX_PHY_SW_RST BIT(7) ++#define REG_LTPI1_TX_PHY_SW_RST BIT(6) ++#define REG_LTPI1_RX_MAC_SW_RST BIT(5) ++#define REG_LTPI1_TX_MAC_SW_RST BIT(4) ++#define REG_LTPI0_RX_PHY_SW_RST BIT(3) ++#define REG_LTPI0_TX_PHY_SW_RST BIT(2) ++#define REG_LTPI0_RX_MAC_SW_RST BIT(1) ++#define REG_LTPI0_TX_MAC_SW_RST BIT(0) ++#define LTPI_STRAP_VAL 0x00c ++#define REG_LTPI_STRAP_2LTPI_EN BIT(1) ++#define REG_LTPI_STRAP_1700_EN BIT(0) ++#define LTPI_SW_FORCE_EN 0x010 ++#define LTPI_SW_FORCE_VAL 0x014 ++#define REG_LTPI_SW_FORCE_LVDS_TX_DS_EN BIT(2) ++#define REG_LTPI_SW_FORCE_2LTPI_EN BIT(1) ++#define REG_LTPI_SW_FORCE_1700_EN BIT(0) ++ ++#endif /* __ASPEED_LTPI_H__ */ +diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig +--- a/drivers/char/hw_random/Kconfig 2025-08-01 08:48:47.000000000 +0000 ++++ b/drivers/char/hw_random/Kconfig 2026-04-08 18:03:35.280943232 +0000 +@@ -587,6 +587,21 @@ + + If unsure, say Y. + ++config HW_RANDOM_ASPEED ++ tristate "Aspeed Random Number Generator support" ++ depends on ARCH_ASPEED ++ default HW_RANDOM ++ help ++ This driver provides kernel-side support for the Random Number ++ Generator hardware found on Aspeed ast2600/ast2700 devices. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called aspeed-rng. ++ ++ If unsure, say Y. ++ ++source "drivers/char/hw_random/dwc/Kconfig" ++ + endif # HW_RANDOM + + config UML_RANDOM +diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile +--- a/drivers/char/hw_random/Makefile 2025-08-01 08:48:47.000000000 +0000 ++++ b/drivers/char/hw_random/Makefile 2026-04-08 18:03:40.212853274 +0000 +@@ -50,3 +50,5 @@ + obj-$(CONFIG_HW_RANDOM_POLARFIRE_SOC) += mpfs-rng.o + obj-$(CONFIG_HW_RANDOM_ROCKCHIP) += rockchip-rng.o + obj-$(CONFIG_HW_RANDOM_JH7110) += jh7110-trng.o ++obj-$(CONFIG_HW_RANDOM_ASPEED) += aspeed-rng.o ++obj-$(CONFIG_HW_RANDOM_DWC) += dwc/ +diff --git a/drivers/char/hw_random/aspeed-rng.c b/drivers/char/hw_random/aspeed-rng.c +--- a/drivers/char/hw_random/aspeed-rng.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/aspeed-rng.c 2026-04-08 18:03:48.278705905 +0000 +@@ -0,0 +1,134 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) ASPEED Technology Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define TRNG_CTL 0x00 ++#define TRNG_EN 0x0 ++#define TRNG_MODE 0x04 ++#define TRNG_RDY 0x1f ++#define TRNG_ODATA 0x04 ++ ++struct aspeed_trng { ++ u32 ver; ++ void __iomem *base; ++ struct hwrng rng; ++ unsigned int present: 1; ++ ktime_t period; ++ struct hrtimer timer; ++ struct completion completion; ++}; ++ ++static int aspeed_trng_read(struct hwrng *rng, void *buf, size_t max, ++ bool wait) ++{ ++ struct aspeed_trng *priv = container_of(rng, struct aspeed_trng, rng); ++ u32 *data = buf; ++ size_t read = 0; ++ int timeout = max / 4 + 1; ++ ++ while (read < max) { ++ if (!(readl(priv->base + TRNG_CTL) & (1 << TRNG_RDY))) { ++ if (wait) { ++ if (timeout-- == 0) ++ return read; ++ } else { ++ return 0; ++ } ++ } else { ++ *data = readl(priv->base + TRNG_ODATA); ++ data++; ++ read += 4; ++ } ++ } ++ ++ return read; ++} ++ ++static void aspeed_trng_enable(struct aspeed_trng *priv) ++{ ++ u32 ctl; ++ ++ ctl = readl(priv->base + TRNG_CTL); ++ ctl = ctl & ~(1 << TRNG_EN); /* enable rng */ ++ ctl = ctl | (3 << TRNG_MODE); /* select mode */ ++ ++ writel(ctl, priv->base + TRNG_CTL); ++} ++ ++static void aspeed_trng_disable(struct aspeed_trng *priv) ++{ ++ writel(1, priv->base + TRNG_CTL); ++} ++ ++static int aspeed_trng_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct aspeed_trng *priv; ++ struct resource *res; ++ int ret; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->base)) ++ return PTR_ERR(priv->base); ++ ++ priv->rng.name = pdev->name; ++ priv->rng.quality = 900; ++ priv->rng.read = aspeed_trng_read; ++ ++ aspeed_trng_enable(priv); ++ ++ ret = devm_hwrng_register(&pdev->dev, &priv->rng); ++ if (ret) ++ return ret; ++ ++ platform_set_drvdata(pdev, priv); ++ ++ dev_info(dev, "Aspeed Hardware RNG successfully registered\n"); ++ ++ return 0; ++} ++ ++static void aspeed_trng_remove(struct platform_device *pdev) ++{ ++ struct aspeed_trng *priv = platform_get_drvdata(pdev); ++ ++ aspeed_trng_disable(priv); ++} ++ ++static const struct of_device_id aspeed_trng_dt_ids[] = { ++ { .compatible = "aspeed,ast2600-trng" }, ++ { .compatible = "aspeed,ast2700-trng" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, aspeed_trng_dt_ids); ++ ++static struct platform_driver aspeed_trng_driver = { ++ .probe = aspeed_trng_probe, ++ .remove = aspeed_trng_remove, ++ .driver = { ++ .name = "aspeed-trng", ++ .of_match_table = aspeed_trng_dt_ids, ++ }, ++}; ++ ++module_platform_driver(aspeed_trng_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Neal Liu "); ++MODULE_DESCRIPTION("Aspeed true random number generator driver"); +diff --git a/drivers/char/hw_random/dwc/Kconfig b/drivers/char/hw_random/dwc/Kconfig +--- a/drivers/char/hw_random/dwc/Kconfig 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/dwc/Kconfig 2026-04-08 18:03:46.629736033 +0000 +@@ -0,0 +1,18 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++# ++# DWC Hardware Random Number Generator (RNG) configuration ++# ++ ++config HW_RANDOM_DWC ++ tristate "DesignWare Cores HW Random Number Generator support" ++ depends on HW_RANDOM ++ depends on ARCH_ASPEED || COMPILE_TEST ++ help ++ This driver provides kernel-side support for the DWC ++ Random Number Generator hardware found on Aspeed SoCs. ++ ++ To compile this driver as a module, choose M here. the ++ module will be called dwc-rng. ++ ++ If unsure, say Y. ++ +diff --git a/drivers/char/hw_random/dwc/Makefile b/drivers/char/hw_random/dwc/Makefile +--- a/drivers/char/hw_random/dwc/Makefile 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/dwc/Makefile 2026-04-08 18:03:46.629736033 +0000 +@@ -0,0 +1,22 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++ccflags-y := -I $(srctree)/drivers/char/hw_random/dwc/src/trng/include \ ++ -I $(srctree)/drivers/char/hw_random/dwc/src/pdu/linux/include ++ ++obj-$(CONFIG_HW_RANDOM_DWC) += elppdu.o ++elppdu-objs := src/pdu/linux/kernel/pdu.o \ ++ src/pdu/common/pdu/pdu_dev32.o \ ++ ++obj-$(CONFIG_HW_RANDOM_DWC) += elpmem.o ++elpmem-objs := src/pdu/linux/kernel/spacc_mem.o \ ++ ++obj-$(CONFIG_HW_RANDOM_DWC) += nisttrng.o ++nisttrng-objs := src/trng/kernel/nist_trng.o \ ++ src/trng/trng/nist_trng.o \ ++ src/trng/trng/nist_trng_private.o \ ++ ++clean: ++ @find \( -name '*.o' \ ++ -o -name '*.a' \ ++ -o -name '*.order' \ ++ \) -type f -print | xargs rm -rvf +diff --git a/drivers/char/hw_random/dwc/src/pdu/common/include/elppdu_error.h b/drivers/char/hw_random/dwc/src/pdu/common/include/elppdu_error.h +--- a/drivers/char/hw_random/dwc/src/pdu/common/include/elppdu_error.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/dwc/src/pdu/common/include/elppdu_error.h 2026-04-08 18:03:46.629736033 +0000 +@@ -0,0 +1,96 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * This Synopsys software and associated documentation (hereinafter the ++ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. The ++ * Software IS NOT an item of Licensed Software or a Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Products ++ * with Synopsys or any supplement thereto. Synopsys is a registered trademark ++ * of Synopsys, Inc. Other names included in the SOFTWARE may be the ++ * trademarks of their respective owners. ++ * ++ * The contents of this file are dual-licensed; you may select either version ++ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license ++ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the ++ * SOFTWARE. The BSD License is copied below. ++ * ++ * BSD-3-Clause License: ++ * Copyright (c) 2011-2017 Synopsys, Inc. and/or its affiliates. ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions, and the following disclaimer, without ++ * modification. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * 3. The names of the above-listed copyright holders may not be used to ++ * endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef SYNPDU_ERROR_H_ ++#define SYNPDU_ERROR_H_ ++ ++/* ++ * Common error definitions. Be sure to update pdu_error_code when changing ++ * anything in this list. ++ */ ++ ++#define CRYPTO_OK (0) ++#define CRYPTO_FAILED (-1) ++#define CRYPTO_INPROGRESS (-2) ++#define CRYPTO_INVALID_HANDLE (-3) ++#define CRYPTO_INVALID_CONTEXT (-4) ++#define CRYPTO_INVALID_SIZE (-5) ++#define CRYPTO_NOT_INITIALIZED (-6) ++#define CRYPTO_NO_MEM (-7) ++#define CRYPTO_INVALID_ALG (-8) ++#define CRYPTO_INVALID_KEY_SIZE (-9) ++#define CRYPTO_INVALID_ARGUMENT (-10) ++#define CRYPTO_MODULE_DISABLED (-11) ++#define CRYPTO_NOT_IMPLEMENTED (-12) ++#define CRYPTO_INVALID_BLOCK_ALIGNMENT (-13) ++#define CRYPTO_INVALID_MODE (-14) ++#define CRYPTO_INVALID_KEY (-15) ++#define CRYPTO_AUTHENTICATION_FAILED (-16) ++#define CRYPTO_INVALID_IV_SIZE (-17) ++#define CRYPTO_MEMORY_ERROR (-18) ++#define CRYPTO_LAST_ERROR (-19) ++#define CRYPTO_HALTED (-20) ++#define CRYPTO_TIMEOUT (-21) ++#define CRYPTO_SRM_FAILED (-22) ++#define CRYPTO_COMMON_ERROR_MAX (-100) ++#define CRYPTO_INVALID_ICV_KEY_SIZE (-100) ++#define CRYPTO_INVALID_PARAMETER_SIZE (-101) ++#define CRYPTO_SEQUENCE_OVERFLOW (-102) ++#define CRYPTO_DISABLED (-103) ++#define CRYPTO_INVALID_VERSION (-104) ++#define CRYPTO_FATAL (-105) ++#define CRYPTO_INVALID_PAD (-106) ++#define CRYPTO_FIFO_FULL (-107) ++#define CRYPTO_INVALID_SEQUENCE (-108) ++#define CRYPTO_INVALID_FIRMWARE (-109) ++#define CRYPTO_NOT_FOUND (-110) ++#define CRYPTO_CMD_FIFO_INACTIVE (-111) ++#define CRYPTO_INVALID_PROTOCOL (-112) ++#define CRYPTO_REPLAY (-113) ++#define CRYPTO_NOT_INSTANTIATED (-114) ++#define CRYPTO_RESEED_REQUIRED (-115) ++ ++#endif +diff --git a/drivers/char/hw_random/dwc/src/pdu/common/pdu/pdu_dev32.c b/drivers/char/hw_random/dwc/src/pdu/common/pdu/pdu_dev32.c +--- a/drivers/char/hw_random/dwc/src/pdu/common/pdu/pdu_dev32.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/dwc/src/pdu/common/pdu/pdu_dev32.c 2026-04-08 18:03:46.629736033 +0000 +@@ -0,0 +1,165 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * This Synopsys software and associated documentation (hereinafter the ++ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. The ++ * Software IS NOT an item of Licensed Software or a Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Products ++ * with Synopsys or any supplement thereto. Synopsys is a registered trademark ++ * of Synopsys, Inc. Other names included in the SOFTWARE may be the ++ * trademarks of their respective owners. ++ * ++ * The contents of this file are dual-licensed; you may select either version ++ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license ++ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the ++ * SOFTWARE. The BSD License is copied below. ++ * ++ * BSD-3-Clause License: ++ * Copyright (c) 2011-2017 Synopsys, Inc. and/or its affiliates. ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions, and the following disclaimer, without ++ * modification. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * 3. The names of the above-listed copyright holders may not be used to ++ * endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "elppdu.h" ++ ++void pdu_to_dev32(void *addr_, u32 *src, unsigned long nword) ++{ ++ unsigned char *addr = addr_; ++ ++ while (nword--) { ++ pdu_io_write32(addr, *src++); ++ addr += 4; ++ } ++} ++EXPORT_SYMBOL(pdu_to_dev32); ++ ++void pdu_from_dev32(u32 *dst, void *addr_, unsigned long nword) ++{ ++ unsigned char *addr = addr_; ++ ++ while (nword--) { ++ *dst++ = pdu_io_read32(addr); ++ addr += 4; ++ } ++} ++EXPORT_SYMBOL(pdu_from_dev32); ++ ++void pdu_to_dev32_big(void *addr_, const unsigned char *src, ++ unsigned long nword) ++{ ++ unsigned char *addr = addr_; ++ unsigned long v; ++ ++ while (nword--) { ++ v = 0; ++ v = (v << 8) | ((unsigned long)*src++); ++ v = (v << 8) | ((unsigned long)*src++); ++ v = (v << 8) | ((unsigned long)*src++); ++ v = (v << 8) | ((unsigned long)*src++); ++ pdu_io_write32(addr, v); ++ addr += 4; ++ } ++} ++EXPORT_SYMBOL(pdu_to_dev32_big); ++ ++void pdu_from_dev32_big(unsigned char *dst, void *addr_, unsigned long nword) ++{ ++ unsigned char *addr = addr_; ++ unsigned long v; ++ ++ while (nword--) { ++ v = pdu_io_read32(addr); ++ addr += 4; ++ *dst++ = (v >> 24) & 0xFF; ++ v <<= 8; ++ *dst++ = (v >> 24) & 0xFF; ++ v <<= 8; ++ *dst++ = (v >> 24) & 0xFF; ++ v <<= 8; ++ *dst++ = (v >> 24) & 0xFF; ++ v <<= 8; ++ } ++} ++EXPORT_SYMBOL(pdu_from_dev32_big); ++ ++void pdu_to_dev32_little(void *addr_, const unsigned char *src, ++ unsigned long nword) ++{ ++ unsigned char *addr = addr_; ++ unsigned long v; ++ ++ while (nword--) { ++ v = 0; ++ v = (v >> 8) | ((unsigned long)*src++ << 24UL); ++ v = (v >> 8) | ((unsigned long)*src++ << 24UL); ++ v = (v >> 8) | ((unsigned long)*src++ << 24UL); ++ v = (v >> 8) | ((unsigned long)*src++ << 24UL); ++ pdu_io_write32(addr, v); ++ addr += 4; ++ } ++} ++EXPORT_SYMBOL(pdu_to_dev32_little); ++ ++void pdu_from_dev32_little(unsigned char *dst, void *addr_, unsigned long nword) ++{ ++ unsigned char *addr = addr_; ++ unsigned long v; ++ ++ while (nword--) { ++ v = pdu_io_read32(addr); ++ addr += 4; ++ *dst++ = v & 0xFF; ++ v >>= 8; ++ *dst++ = v & 0xFF; ++ v >>= 8; ++ *dst++ = v & 0xFF; ++ v >>= 8; ++ *dst++ = v & 0xFF; ++ v >>= 8; ++ } ++} ++EXPORT_SYMBOL(pdu_from_dev32_little); ++ ++void pdu_to_dev32_s(void *addr, const unsigned char *src, unsigned long nword, ++ int endian) ++{ ++ if (endian) ++ pdu_to_dev32_big(addr, src, nword); ++ else ++ pdu_to_dev32_little(addr, src, nword); ++} ++EXPORT_SYMBOL(pdu_to_dev32_s); ++ ++void pdu_from_dev32_s(unsigned char *dst, void *addr, unsigned long nword, ++ int endian) ++{ ++ if (endian) ++ pdu_from_dev32_big(dst, addr, nword); ++ else ++ pdu_from_dev32_little(dst, addr, nword); ++} ++EXPORT_SYMBOL(pdu_from_dev32_s); +diff --git a/drivers/char/hw_random/dwc/src/pdu/linux/include/elppdu.h b/drivers/char/hw_random/dwc/src/pdu/linux/include/elppdu.h +--- a/drivers/char/hw_random/dwc/src/pdu/linux/include/elppdu.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/dwc/src/pdu/linux/include/elppdu.h 2026-04-08 18:03:46.629736033 +0000 +@@ -0,0 +1,125 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * This Synopsys software and associated documentation (hereinafter the ++ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. The ++ * Software IS NOT an item of Licensed Software or a Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Products ++ * with Synopsys or any supplement thereto. Synopsys is a registered trademark ++ * of Synopsys, Inc. Other names included in the SOFTWARE may be the ++ * trademarks of their respective owners. ++ * ++ * The contents of this file are dual-licensed; you may select either version ++ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license ++ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the ++ * SOFTWARE. The BSD License is copied below. ++ * ++ * BSD-3-Clause License: ++ * Copyright (c) 2011-2017 Synopsys, Inc. and/or its affiliates. ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions, and the following disclaimer, without ++ * modification. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * 3. The names of the above-listed copyright holders may not be used to ++ * endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef SYNPDU_H_ ++#define SYNPDU_H_ ++ ++/* Platform Specific */ ++#include /* printk() */ ++#include /* size_t */ ++#include /* memcpy()/etc */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifndef PDU_BASE_ADDR ++#define PDU_BASE_ADDR 0x14c3b000 ++#endif ++ ++#ifndef PDU_BASE_IRQ ++#define PDU_BASE_IRQ 91 ++#endif ++ ++#define PDU_SINGLE_CORE 1 ++#define PDU_SINGLE_NIST_TRNG 1 ++ ++#if 1 ++#define SYNHW_PRINT printk ++#else ++#define SYNHW_PRINT(...) ++#endif ++ ++#define CPU_YIELD ++#define SYNHW_MEMCPY memcpy ++ ++// Debug modifier for printing, in linux adding KERN_DEBUG makes the output only show up in debug logs (avoids /var/log/messages) ++#define SYNHW_PRINT_DEBUG KERN_DEBUG ++ ++// Locking ++#define PDU_LOCK_TYPE spinlock_t ++#define PDU_INIT_LOCK(lock) spin_lock_init(lock) ++ ++// these are for IRQ contexts ++#define PDU_LOCK(lock, flags) spin_lock_irqsave(lock, flags) ++#define PDU_UNLOCK(lock, flags) spin_unlock_irqrestore(lock, flags) ++ ++// these are for bottom half BH contexts ++#define PDU_LOCK_TYPE_BH struct mutex ++#define PDU_INIT_LOCK_BH(lock) mutex_init(lock) ++#define PDU_LOCK_BH(lock) mutex_lock(lock) ++#define PDU_UNLOCK_BH(lock) mutex_unlock(lock) ++ ++#include "../../common/include/elppdu_error.h" ++ ++void *pdu_linux_map_regs(struct device *dev, struct resource *regs); ++ ++void pdu_io_write32(void *addr, unsigned long val); ++void pdu_io_cached_write32(void *addr, unsigned long val, u32 *cache); ++unsigned long pdu_io_read32(void *addr); ++ ++void pdu_to_dev32(void *addr, u32 *src, unsigned long nword); ++void pdu_from_dev32(u32 *dst, void *addr, unsigned long nword); ++void pdu_to_dev32_big(void *addr, const unsigned char *src, unsigned long nword); ++void pdu_from_dev32_big(unsigned char *dst, void *addr, unsigned long nword); ++void pdu_to_dev32_little(void *addr, const unsigned char *src, unsigned long nword); ++void pdu_from_dev32_little(unsigned char *dst, void *addr, unsigned long nword); ++void pdu_from_dev32_s(unsigned char *dst, void *addr, unsigned long nword, int endian); ++void pdu_to_dev32_s(void *addr, const unsigned char *src, unsigned long nword, int endian); ++ ++void *pdu_malloc(unsigned long n); ++void pdu_free(void *p); ++ ++int pdu_error_code(int code); ++ ++#endif ++ +diff --git a/drivers/char/hw_random/dwc/src/pdu/linux/kernel/pdu.c b/drivers/char/hw_random/dwc/src/pdu/linux/kernel/pdu.c +--- a/drivers/char/hw_random/dwc/src/pdu/linux/kernel/pdu.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/dwc/src/pdu/linux/kernel/pdu.c 2026-04-08 18:03:46.629736033 +0000 +@@ -0,0 +1,188 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * This Synopsys software and associated documentation (hereinafter the ++ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. The ++ * Software IS NOT an item of Licensed Software or a Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Products ++ * with Synopsys or any supplement thereto. Synopsys is a registered trademark ++ * of Synopsys, Inc. Other names included in the SOFTWARE may be the ++ * trademarks of their respective owners. ++ * ++ * The contents of this file are dual-licensed; you may select either version ++ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license ++ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the ++ * SOFTWARE. The BSD License is copied below. ++ * ++ * BSD-3-Clause License: ++ * Copyright (c) 2011-2017 Synopsys, Inc. and/or its affiliates. ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions, and the following disclaimer, without ++ * modification. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * 3. The names of the above-listed copyright holders may not be used to ++ * endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++ ++#include "elppdu.h" ++ ++static bool trace_io; ++module_param(trace_io, bool, 0600); ++MODULE_PARM_DESC(trace_io, "Trace MMIO reads/writes"); ++ ++void *pdu_linux_map_regs(struct device *dev, struct resource *regs) ++{ ++ return devm_ioremap_resource(dev, regs); ++} ++EXPORT_SYMBOL(pdu_linux_map_regs); ++ ++void pdu_io_write32(void *addr, unsigned long val) ++{ ++ if (trace_io) ++ SYNHW_PRINT("PDU: write %.8lx -> %p\n", val, addr); ++ ++ writel(val, addr); ++} ++EXPORT_SYMBOL(pdu_io_write32); ++ ++void pdu_io_cached_write32(void *addr, unsigned long val, uint32_t *cache) ++{ ++ if (*cache == val) { ++ if (trace_io) { ++ SYNHW_PRINT("PDU: write %.8lx -> %p (cached)\n", val, ++ addr); ++ } ++ return; ++ } ++ ++ *cache = val; ++ pdu_io_write32(addr, val); ++} ++EXPORT_SYMBOL(pdu_io_cached_write32); ++ ++unsigned long pdu_io_read32(void *addr) ++{ ++ unsigned long val; ++ ++ val = readl(addr); ++ ++ if (trace_io) ++ SYNHW_PRINT("PDU: read %.8lx <- %p\n", val, addr); ++ ++ return val; ++} ++EXPORT_SYMBOL(pdu_io_read32); ++ ++/* Platform specific memory allocation */ ++void *pdu_malloc(unsigned long n) ++{ ++ return vmalloc(n); ++} ++ ++void pdu_free(void *p) ++{ ++ vfree(p); ++} ++ ++/* Convert SDK error codes to corresponding kernel error codes. */ ++int pdu_error_code(int code) ++{ ++ switch (code) { ++ case CRYPTO_INPROGRESS: ++ return -EINPROGRESS; ++ case CRYPTO_INVALID_HANDLE: ++ case CRYPTO_INVALID_CONTEXT: ++ return -ENXIO; ++ case CRYPTO_NOT_INITIALIZED: ++ return -ENODATA; ++ case CRYPTO_INVALID_SIZE: ++ case CRYPTO_INVALID_ALG: ++ case CRYPTO_INVALID_KEY_SIZE: ++ case CRYPTO_INVALID_ARGUMENT: ++ case CRYPTO_INVALID_BLOCK_ALIGNMENT: ++ case CRYPTO_INVALID_MODE: ++ case CRYPTO_INVALID_KEY: ++ case CRYPTO_INVALID_IV_SIZE: ++ case CRYPTO_INVALID_ICV_KEY_SIZE: ++ case CRYPTO_INVALID_PARAMETER_SIZE: ++ case CRYPTO_REPLAY: ++ case CRYPTO_INVALID_PROTOCOL: ++ case CRYPTO_RESEED_REQUIRED: ++ return -EINVAL; ++ case CRYPTO_NOT_IMPLEMENTED: ++ case CRYPTO_MODULE_DISABLED: ++ return -ENOTSUPP; ++ case CRYPTO_NO_MEM: ++ return -ENOMEM; ++ case CRYPTO_INVALID_PAD: ++ case CRYPTO_INVALID_SEQUENCE: ++ return -EILSEQ; ++ case CRYPTO_MEMORY_ERROR: ++ return -EIO; ++ case CRYPTO_TIMEOUT: ++ return -ETIMEDOUT; ++ case CRYPTO_HALTED: ++ return -ECANCELED; ++ case CRYPTO_AUTHENTICATION_FAILED: ++ case CRYPTO_SEQUENCE_OVERFLOW: ++ case CRYPTO_INVALID_VERSION: ++ return -EPROTO; ++ case CRYPTO_FIFO_FULL: ++ return -EBUSY; ++ case CRYPTO_SRM_FAILED: ++ case CRYPTO_DISABLED: ++ case CRYPTO_LAST_ERROR: ++ return -EAGAIN; ++ case CRYPTO_FAILED: ++ case CRYPTO_FATAL: ++ return -EIO; ++ case CRYPTO_INVALID_FIRMWARE: ++ return -ENOEXEC; ++ case CRYPTO_NOT_FOUND: ++ return -ENOENT; ++ } ++ ++ /* ++ * Any unrecognized code is either success (i.e., zero) or a negative ++ * error code, which may be meaningless but at least will still be ++ * recognized as an error. ++ */ ++ return code; ++} ++EXPORT_SYMBOL(pdu_error_code); ++ ++static int __init pdu_mod_init(void) ++{ ++ return 0; ++} ++ ++static void __exit pdu_mod_exit(void) ++{ ++} ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Synopsys, Inc."); ++module_init(pdu_mod_init); ++module_exit(pdu_mod_exit); +diff --git a/drivers/char/hw_random/dwc/src/pdu/linux/kernel/spacc_mem.c b/drivers/char/hw_random/dwc/src/pdu/linux/kernel/spacc_mem.c +--- a/drivers/char/hw_random/dwc/src/pdu/linux/kernel/spacc_mem.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/dwc/src/pdu/linux/kernel/spacc_mem.c 2026-04-08 18:03:46.629736033 +0000 +@@ -0,0 +1,191 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * This Synopsys software and associated documentation (hereinafter the ++ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. The ++ * Software IS NOT an item of Licensed Software or a Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Products ++ * with Synopsys or any supplement thereto. Synopsys is a registered trademark ++ * of Synopsys, Inc. Other names included in the SOFTWARE may be the ++ * trademarks of their respective owners. ++ * ++ * The contents of this file are dual-licensed; you may select either version ++ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license ++ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the ++ * SOFTWARE. The BSD License is copied below. ++ * ++ * BSD-3-Clause License: ++ * Copyright (c) 2011-2016 Synopsys, Inc. and/or its affiliates. ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions, and the following disclaimer, without ++ * modification. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * 3. The names of the above-listed copyright holders may not be used to ++ * endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "elppdu.h" ++ ++static unsigned long vex_baseaddr = PDU_BASE_ADDR; ++module_param_named(baseaddr, vex_baseaddr, ulong, 0); ++MODULE_PARM_DESC(baseaddr, "Hardware base address (default " __stringify(PDU_BASE_ADDR) ")"); ++ ++// max of 16 devices ++#define MAX_DEV 16 ++ ++static struct platform_device *devices[MAX_DEV]; ++static int dev_id; ++ ++static void register_device(const char *name, int id, ++ const struct resource *res, unsigned int num) ++{ ++ char suffix[16] = ""; ++ struct platform_device_info pdevinfo = { ++ .name = name, ++ .id = id, ++ .res = res, ++ .num_res = num, ++ .dma_mask = 0xffffffff, ++ }; ++ ++ if (dev_id >= MAX_DEV) { ++ pr_err("Too many devices; increase MAX_DEV.\n"); ++ return; ++ } ++ ++ devices[dev_id] = platform_device_register_full(&pdevinfo); ++ if (IS_ERR(devices[dev_id])) { ++ if (id >= 0) ++ snprintf(suffix, sizeof(suffix), ".%d", id); ++ pr_err("Failed to register %s%s\n", name, suffix); ++ ++ devices[dev_id] = NULL; ++ return; ++ } ++ ++ dev_id++; ++} ++ ++static int __init get_irq_num(unsigned int irq_num) ++{ ++ if (IS_ENABLED(CONFIG_ARCH_ZYNQ)) { ++ struct of_phandle_args args = { 0 }; ++ ++ /* ++ * Since this driver is for non-DT use but Zynq uses DT to setup IRQs, ++ * find the GIC by searching for its DT node then manually create the ++ * IRQ mappings. ++ */ ++ ++ do { ++ args.np = of_find_node_with_property(args.np, ++ "interrupt-controller"); ++ if (!args.np) { ++ pr_err("cannot find IRQ controller"); ++ return -ENODEV; ++ } ++ } while (!of_device_is_compatible(args.np, "arm,cortex-a9-gic")); ++ ++ if (irq_num < 32 || irq_num >= 96) { ++ pr_err("SPI interrupts must be in the range [32,96) on Zynq\n"); ++ return -EINVAL; ++ } ++ ++ args.args_count = 3; ++ args.args[0] = 0; /* SPI */ ++ args.args[1] = irq_num - 32; ++ args.args[2] = 4; /* Active high, level-sensitive */ ++ ++ irq_num = irq_create_of_mapping(&args); ++ of_node_put(args.np); ++ if (irq_num == 0) ++ return -EINVAL; ++ } ++ ++ if (irq_num > INT_MAX) ++ return -EINVAL; ++ ++ return irq_num; ++} ++ ++static int __init pdu_vex_mod_init(void) ++{ ++ int irq_num = get_irq_num(PDU_BASE_IRQ); ++ struct resource res[2]; ++#ifndef PDU_SINGLE_CORE ++ void *pdu_mem; ++ int i, rc; ++#endif ++ ++ if (irq_num >= 0) { ++ res[1] = (struct resource){ ++ .start = irq_num, ++ .end = irq_num, ++ .flags = IORESOURCE_IRQ, ++ }; ++ } else { ++ res[1] = (struct resource){ 0 }; ++ pr_err("IRQ setup failed (error %d), not using IRQs\n", ++ irq_num); ++ } ++ ++#ifdef PDU_SINGLE_BASIC_TRNG ++ res[0] = (struct resource){ ++ .start = vex_baseaddr, ++ .end = vex_baseaddr + 0x80 - 1, ++ .flags = IORESOURCE_MEM, ++ }; ++ register_device("basic_trng", -1, res, 2); ++#endif ++ ++#ifdef PDU_SINGLE_NIST_TRNG ++ res[0] = (struct resource){ ++ .start = vex_baseaddr, ++ .end = vex_baseaddr + 0x800 - 1, ++ .flags = IORESOURCE_MEM, ++ }; ++ register_device("nist_trng", -1, res, 2); ++#endif ++ ++ return 0; ++} ++module_init(pdu_vex_mod_init); ++ ++static void __exit pdu_vex_mod_exit(void) ++{ ++ int i; ++ ++ for (i = 0; i < MAX_DEV; i++) ++ platform_device_unregister(devices[i]); ++} ++module_exit(pdu_vex_mod_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Synopsys, Inc."); +diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng.h b/drivers/char/hw_random/dwc/src/trng/include/nisttrng.h +--- a/drivers/char/hw_random/dwc/src/trng/include/nisttrng.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng.h 2026-04-08 18:03:46.629736033 +0000 +@@ -0,0 +1,63 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * This Synopsys software and associated documentation (hereinafter the ++ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. The ++ * Software IS NOT an item of Licensed Software or a Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Products ++ * with Synopsys or any supplement thereto. Synopsys is a registered trademark ++ * of Synopsys, Inc. Other names included in the SOFTWARE may be the ++ * trademarks of their respective owners. ++ * ++ * The contents of this file are dual-licensed; you may select either version ++ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license ++ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the ++ * SOFTWARE. The BSD License is copied below. ++ * ++ * BSD-3-Clause License: ++ * Copyright (c) 2012-2016 Synopsys, Inc. and/or its affiliates. ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions, and the following disclaimer, without ++ * modification. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * 3. The names of the above-listed copyright holders may not be used to ++ * endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef NISTTRNG_H ++#define NISTTRNG_H ++ ++#include "synversion.h" ++#include "elppdu.h" ++#include "nisttrng_hw.h" ++#include "nisttrng_common.h" ++#include "nisttrng_private.h" ++ ++int nisttrng_init(struct nist_trng_state *state, u32 *base); ++int nisttrng_instantiate(struct nist_trng_state *state, int req_sec_strength, int pred_resist, void *personal_str); ++int nisttrng_uninstantiate(struct nist_trng_state *state); ++int nisttrng_reseed(struct nist_trng_state *state, int pred_resist, void *addin_str); ++int nisttrng_generate(struct nist_trng_state *state, void *random_bits, unsigned long req_num_bytes, int req_sec_strength, int pred_resist, void *addin_str); ++int nisttrng_rbc(struct nist_trng_state *state, int enable, int rbc_num, int rate, int urun_blnk); ++int nisttrng_generate_public_vtrng(struct nist_trng_state *state, void *random_bits, unsigned long req_num_bytes, int vtrng); ++#endif +diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_common.h b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_common.h +--- a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_common.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_common.h 2026-04-08 18:03:46.629736033 +0000 +@@ -0,0 +1,144 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++// ------------------------------------------------------------------------ ++// ++// (C) COPYRIGHT 2012 - 2016 SYNOPSYS, INC. ++// ALL RIGHTS RESERVED ++// ++// (C) COPYRIGHT 2012-2016 Synopsys, Inc. ++// This Synopsys software and all associated documentation are ++// proprietary to Synopsys, Inc. and may only be used pursuant ++// to the terms and conditions of a written license agreement ++// with Synopsys, Inc. All other use, reproduction, modification, ++// or distribution of the Synopsys software or the associated ++// documentation is strictly prohibited. ++// ++// ------------------------------------------------------------------------ ++ ++#ifndef NISTTRNG_COMMON_H ++#define NISTTRNG_COMMON_H ++ ++#define NIST_TRNG_RETRY_MAX 5000000UL ++ ++#define NIST_DFLT_MAX_BITS_PER_REQ BIT(19) ++#define NIST_DFLT_MAX_REQ_PER_SEED BIT(48) ++ ++/* Do not change the following parameters */ ++#define NIST_TRNG_DFLT_MAX_REJECTS 10 ++ ++#define DEBUG(...) ++//#define DEBUG(...) printk(__VA_ARGS__) ++ ++enum nisttrng_sec_strength { ++ SEC_STRNT_AES128 = 0, ++ SEC_STRNT_AES256 = 1 ++}; ++ ++enum nisttrng_drbg_arch { ++ AES128 = 0, ++ AES256 = 1 ++}; ++ ++enum nisttrng_current_state { ++ NIST_TRNG_STATE_INITIALIZE = 0, ++ NIST_TRNG_STATE_UNINSTANTIATE, ++ NIST_TRNG_STATE_INSTANTIATE, ++ NIST_TRNG_STATE_RESEED, ++ NIST_TRNG_STATE_GENERATE ++}; ++ ++struct nist_trng_state { ++ u32 *base; ++ ++ /* Hardware features and build ID */ ++ struct { ++ struct { ++ enum nisttrng_drbg_arch drbg_arch; ++ unsigned int extra_ps_present, ++ secure_rst_state, ++ diag_level_basic_trng, ++ diag_level_stat_hlt, ++ diag_level_ns; ++ } features; ++ ++ struct { ++ unsigned int ext_enum, ++ ext_ver, ++ rel_num; ++ } corekit_rel; ++ ++ struct { ++ unsigned int core_type, ++ bg8, ++ cdc_synch_depth, ++ background_noise, ++ edu_present, ++ aes_datapath, ++ aes_max_key_size, ++ personilzation_str; ++ } build_cfg0; ++ ++ struct { ++ unsigned int num_raw_noise_blks, ++ sticky_startup, ++ auto_correlation_test, ++ mono_bit_test, ++ run_test, ++ poker_test, ++ raw_ht_adap_test, ++ raw_ht_rep_test, ++ ent_src_rep_smpl_size, ++ ent_src_rep_test, ++ ent_src_rep_min_entropy; ++ } build_cfg1; ++ ++ struct { ++ unsigned int rbc2_rate_width, ++ rbc1_rate_width, ++ rbc0_rate_width, ++ public_vtrng_channels, ++ esm_channel, ++ rbc_channels, ++ fifo_depth; ++ } edu_build_cfg0; ++ } config; ++ ++ /* status */ ++ struct { ++ //nist_trng_current_state current_state; ++ enum nisttrng_current_state current_state; // old for now ++ unsigned int nonce_mode, ++ secure_mode, ++ pred_resist; ++ //nist_trng_sec_strength sec_strength; ++ enum nisttrng_sec_strength sec_strength; ++ unsigned int pad_ps_addin; ++ unsigned int alarm_code; ++ // Private VTRNG STAT, all the public trng will have the same STAT as public TRNG in terms of ++ // rnc_enabled and seed_enum ++ struct { ++ unsigned int seed_enum, ++ rnc_enabled; ++ } edu_vstat; ++ } status; ++ ++ /* reminders and alarms */ ++ struct { ++ unsigned long max_bits_per_req; ++ unsigned long long max_req_per_seed; ++ unsigned long bits_per_req_left; ++ unsigned long long req_per_seed_left; ++ } counters; ++}; ++ ++#define nist_trng_zero_status(x) \ ++ memset(&((x)->status), 0, sizeof((x)->status)) ++ ++#define DRBG_INSTANTIATED(cs) \ ++ ((((cs) == NIST_TRNG_STATE_INSTANTIATE) || \ ++ ((cs) == NIST_TRNG_STATE_RESEED) || \ ++ ((cs) == NIST_TRNG_STATE_GENERATE)) ? 1 : 0) ++ ++#define REQ_SEC_STRENGTH_IS_VALID(sec_st) \ ++ ((((sec_st) > 0) && ((sec_st) <= 256)) ? 1 : 0) ++ ++#endif +diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_hw.h b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_hw.h +--- a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_hw.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_hw.h 2026-04-08 18:03:46.629736033 +0000 +@@ -0,0 +1,457 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * This Synopsys software and associated documentation (hereinafter the ++ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. The ++ * Software IS NOT an item of Licensed Software or a Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Products ++ * with Synopsys or any supplement thereto. Synopsys is a registered trademark ++ * of Synopsys, Inc. Other names included in the SOFTWARE may be the ++ * trademarks of their respective owners. ++ * ++ * The contents of this file are dual-licensed; you may select either version ++ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license ++ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the ++ * SOFTWARE. The BSD License is copied below. ++ * ++ * BSD-3-Clause License: ++ * Copyright (c) 2012-2016 Synopsys, Inc. and/or its affiliates. ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions, and the following disclaimer, without ++ * modification. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * 3. The names of the above-listed copyright holders may not be used to ++ * endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef NISTTRNG_HW_H ++#define NISTTRNG_HW_H ++ ++/* HW related Parameters */ ++#define NIST_TRNG_RAND_BLK_SIZE_BITS 128 ++#define CHX_URUN_BLANK_AFTER_RESET 0x3 ++ ++/* registers */ ++#define NIST_TRNG_REG_CTRL 0x00 ++#define NIST_TRNG_REG_MODE 0x01 ++#define NIST_TRNG_REG_SMODE 0x02 ++#define NIST_TRNG_REG_STAT 0x03 ++#define NIST_TRNG_REG_IE 0x04 ++#define NIST_TRNG_REG_ISTAT 0x05 ++#define NIST_TRNG_REG_ALARM 0x06 ++#define NIST_TRNG_REG_COREKIT_REL 0x07 ++#define NIST_TRNG_REG_FEATURES 0x08 ++#define NIST_TRNG_REG_RAND0 0x09 ++#define NIST_TRNG_REG_RAND1 0x0A ++#define NIST_TRNG_REG_RAND2 0x0B ++#define NIST_TRNG_REG_RAND3 0x0C ++#define NIST_TRNG_REG_NPA_DATA0 0x0D ++#define NIST_TRNG_REG_NPA_DATA1 0x0E ++#define NIST_TRNG_REG_NPA_DATA2 0x0F ++#define NIST_TRNG_REG_NPA_DATA3 0x10 ++#define NIST_TRNG_REG_NPA_DATA4 0x11 ++#define NIST_TRNG_REG_NPA_DATA5 0x12 ++#define NIST_TRNG_REG_NPA_DATA6 0x13 ++#define NIST_TRNG_REG_NPA_DATA7 0x14 ++#define NIST_TRNG_REG_NPA_DATA8 0x15 ++#define NIST_TRNG_REG_NPA_DATA9 0x16 ++#define NIST_TRNG_REG_NPA_DATA10 0x17 ++#define NIST_TRNG_REG_NPA_DATA11 0x18 ++#define NIST_TRNG_REG_NPA_DATA12 0x19 ++#define NIST_TRNG_REG_NPA_DATA13 0x1A ++#define NIST_TRNG_REG_NPA_DATA14 0x1B ++#define NIST_TRNG_REG_NPA_DATA15 0x1C ++#define NIST_TRNG_REG_SEED0 0x1D ++#define NIST_TRNG_REG_SEED1 0x1E ++#define NIST_TRNG_REG_SEED2 0x1F ++#define NIST_TRNG_REG_SEED3 0x20 ++#define NIST_TRNG_REG_SEED4 0x21 ++#define NIST_TRNG_REG_SEED5 0x22 ++#define NIST_TRNG_REG_SEED6 0x23 ++#define NIST_TRNG_REG_SEED7 0x24 ++#define NIST_TRNG_REG_SEED8 0x25 ++#define NIST_TRNG_REG_SEED9 0x26 ++#define NIST_TRNG_REG_SEED10 0x27 ++#define NIST_TRNG_REG_SEED11 0x28 ++#define NIST_TRNG_REG_TIME_TO_SEED 0x34 ++#define NIST_TRNG_REG_IA_RDATA 0x38 ++#define NIST_TRNG_REG_IA_WDATA 0x39 ++#define NIST_TRNG_REG_IA_ADDR 0x3A ++#define NIST_TRNG_REG_IA_CMD 0x3B ++#define NIST_TRNG_REG_BUILD_CFG0 0x3C ++#define NIST_TRNG_REG_BUILD_CFG1 0x3D ++ ++/* nist edu registers */ ++#define NIST_TRNG_EDU_RNC_CTRL 0x100 ++#define NIST_TRNG_EDU_FLUSH_CTRL 0x101 ++#define NIST_TRNG_EDU_RESEED_CNTR 0x102 ++#define NIST_TRNG_EDU_RBC_CTRL 0x104 ++#define NIST_TRNG_EDU_STAT 0x106 ++#define NIST_TRNG_EDU_IE 0x108 ++#define NIST_TRNG_EDU_ISTAT 0x109 ++#define NIST_TRNG_EDU_BUILD_CFG0 0x12C ++#define NIST_TRNG_EDU_VCTRL 0x138 ++#define NIST_TRNG_EDU_VSTAT 0x139 ++#define NIST_TRNG_EDU_VIE 0x13A ++#define NIST_TRNG_EDU_VISTAT 0x13B ++#define NIST_TRNG_EDU_VRAND_0 0x13C ++#define NIST_TRNG_EDU_VRAND_1 0x13D ++#define NIST_TRNG_EDU_VRAND_2 0x13E ++#define NIST_TRNG_EDU_VRAND_3 0x13F ++ ++/* edu vtrng registers */ ++#define NIST_TRNG_EDU_VTRNG_VCTRL0 0x180 ++#define NIST_TRNG_EDU_VTRNG_VSTAT0 0x181 ++#define NIST_TRNG_EDU_VTRNG_VIE0 0x182 ++#define NIST_TRNG_EDU_VTRNG_VISTAT0 0x183 ++#define NIST_TRNG_EDU_VTRNG_VRAND0_0 0x184 ++#define NIST_TRNG_EDU_VTRNG_VRAND0_1 0x185 ++#define NIST_TRNG_EDU_VTRNG_VRAND0_2 0x186 ++#define NIST_TRNG_EDU_VTRNG_VRAND0_3 0x187 ++#define NIST_TRNG_EDU_VTRNG_VCTRL1 0x188 ++#define NIST_TRNG_EDU_VTRNG_VSTAT1 0x189 ++#define NIST_TRNG_EDU_VTRNG_VIE1 0x18A ++#define NIST_TRNG_EDU_VTRNG_VISTAT1 0x18B ++#define NIST_TRNG_EDU_VTRNG_VRAND1_0 0x18C ++#define NIST_TRNG_EDU_VTRNG_VRAND1_1 0x18D ++#define NIST_TRNG_EDU_VTRNG_VRAND1_2 0x18E ++#define NIST_TRNG_EDU_VTRNG_VRAND1_3 0x18F ++#define NIST_TRNG_EDU_VTRNG_VCTRL2 0x190 ++#define NIST_TRNG_EDU_VTRNG_VSTAT2 0x191 ++#define NIST_TRNG_EDU_VTRNG_VIE2 0x192 ++#define NIST_TRNG_EDU_VTRNG_VISTAT2 0x193 ++#define NIST_TRNG_EDU_VTRNG_VRAND2_0 0x194 ++#define NIST_TRNG_EDU_VTRNG_VRAND2_1 0x195 ++#define NIST_TRNG_EDU_VTRNG_VRAND2_2 0x196 ++#define NIST_TRNG_EDU_VTRNG_VRAND2_3 0x197 ++#define NIST_TRNG_EDU_VTRNG_VCTRL3 0x198 ++#define NIST_TRNG_EDU_VTRNG_VSTAT3 0x199 ++#define NIST_TRNG_EDU_VTRNG_VIE3 0x19A ++#define NIST_TRNG_EDU_VTRNG_VISTAT3 0x19B ++#define NIST_TRNG_EDU_VTRNG_VRAND3_0 0x19C ++#define NIST_TRNG_EDU_VTRNG_VRAND3_1 0x19D ++#define NIST_TRNG_EDU_VTRNG_VRAND3_2 0x19E ++#define NIST_TRNG_EDU_VTRNG_VRAND3_3 0x19F ++#define NIST_TRNG_EDU_VTRNG_VCTRL4 0x1A0 ++#define NIST_TRNG_EDU_VTRNG_VSTAT4 0x1A1 ++#define NIST_TRNG_EDU_VTRNG_VIE4 0x1A2 ++#define NIST_TRNG_EDU_VTRNG_VISTAT4 0x1A3 ++#define NIST_TRNG_EDU_VTRNG_VRAND4_0 0x1A4 ++#define NIST_TRNG_EDU_VTRNG_VRAND4_1 0x1A5 ++#define NIST_TRNG_EDU_VTRNG_VRAND4_2 0x1A6 ++#define NIST_TRNG_EDU_VTRNG_VRAND4_3 0x1A7 ++#define NIST_TRNG_EDU_VTRNG_VCTRL5 0x1A8 ++#define NIST_TRNG_EDU_VTRNG_VSTAT5 0x1A9 ++#define NIST_TRNG_EDU_VTRNG_VIE5 0x1AA ++#define NIST_TRNG_EDU_VTRNG_VISTAT5 0x1AB ++#define NIST_TRNG_EDU_VTRNG_VRAND5_0 0x1AC ++#define NIST_TRNG_EDU_VTRNG_VRAND5_1 0x1AD ++#define NIST_TRNG_EDU_VTRNG_VRAND5_2 0x1AE ++#define NIST_TRNG_EDU_VTRNG_VRAND5_3 0x1AF ++#define NIST_TRNG_EDU_VTRNG_VCTRL6 0x1B0 ++#define NIST_TRNG_EDU_VTRNG_VSTAT6 0x1B1 ++#define NIST_TRNG_EDU_VTRNG_VIE6 0x1B2 ++#define NIST_TRNG_EDU_VTRNG_VISTAT6 0x1B3 ++#define NIST_TRNG_EDU_VTRNG_VRAND6_0 0x1B4 ++#define NIST_TRNG_EDU_VTRNG_VRAND6_1 0x1B5 ++#define NIST_TRNG_EDU_VTRNG_VRAND6_2 0x1B6 ++#define NIST_TRNG_EDU_VTRNG_VRAND6_3 0x1B7 ++#define NIST_TRNG_EDU_VTRNG_VCTRL7 0x1B8 ++#define NIST_TRNG_EDU_VTRNG_VSTAT7 0x1B9 ++#define NIST_TRNG_EDU_VTRNG_VIE7 0x1BA ++#define NIST_TRNG_EDU_VTRNG_VISTAT7 0x1BB ++#define NIST_TRNG_EDU_VTRNG_VRAND7_0 0x1BC ++#define NIST_TRNG_EDU_VTRNG_VRAND7_1 0x1BD ++#define NIST_TRNG_EDU_VTRNG_VRAND7_2 0x1BE ++#define NIST_TRNG_EDU_VTRNG_VRAND7_3 0x1BF ++ ++#define NIST_TRNG_EDU_VTRNG_VCTRL_CMD_NOP 0x0 ++#define NIST_TRNG_EDU_VTRNG_VCTRL_CMD_GET_RANDOM 0x1 ++#define NIST_TRNG_EDU_VTRNG_VCTRL_CMD_INIT 0x2 ++ ++#define NIST_TRNG_EDU_VTRNG_VCTRL_CMD_MASK 0x3Ul ++#define NIST_TRNG_EDU_VTRNG_VCTRL_CMD_SET(y, x) (((y) & ~(NIST_TRNG_EDU_VTRNG_VCTRL_CMD_MASK)) | ((x))) ++ ++/* CTRL */ ++#define NIST_TRNG_REG_CTRL_CMD_NOP 0 ++#define NIST_TRNG_REG_CTRL_CMD_GEN_NOISE 1 ++#define NIST_TRNG_REG_CTRL_CMD_GEN_NONCE 2 ++#define NIST_TRNG_REG_CTRL_CMD_CREATE_STATE 3 ++#define NIST_TRNG_REG_CTRL_CMD_RENEW_STATE 4 ++#define NIST_TRNG_REG_CTRL_CMD_REFRESH_ADDIN 5 ++#define NIST_TRNG_REG_CTRL_CMD_GEN_RANDOM 6 ++#define NIST_TRNG_REG_CTRL_CMD_ADVANCE_STATE 7 ++#define NIST_TRNG_REG_CTRL_CMD_KAT 8 ++#define NIST_TRNG_REG_CTRL_CMD_ZEROIZE 15 ++ ++/* EDU CTRL */ ++#define NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_DISABLE_TO_HOLD 0 ++#define NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_ENABLE 1 ++#define NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_DISABLE_TO_IDLE 2 ++#define NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_FINISH_TO_IDLE 3 ++ ++#define NIST_TRNG_EDU_RNC_CTRL_CMD_MASK 0x3Ul ++#define NIST_TRNG_EDU_RNC_CTRL_CMD_SET(y, x) (((y) & ~(NIST_TRNG_EDU_RNC_CTRL_CMD_MASK)) | ((x))) ++ ++/* EDU_FLUSH_CTRL */ ++#define _NIST_TRNG_EDU_FLUSH_CTRL_CH2_RBC 3 ++#define _NIST_TRNG_EDU_FLUSH_CTRL_CH1_RBC 2 ++#define _NIST_TRNG_EDU_FLUSH_CTRL_CH0_RBC 1 ++#define _NIST_TRNG_EDU_FLUSH_CTRL_FIFO 0 ++ ++#define NIST_TRNG_EDU_FLUSH_CTRL_CH2_RBC BIT(_NIST_TRNG_EDU_FLUSH_CTRL_CH2_RBC) ++#define NIST_TRNG_EDU_FLUSH_CTRL_CH1_RBC BIT(_NIST_TRNG_EDU_FLUSH_CTRL_CH1_RBC) ++#define NIST_TRNG_EDU_FLUSH_CTRL_CH0_RBC BIT(_NIST_TRNG_EDU_FLUSH_CTRL_CH0_RBC) ++#define NIST_TRNG_EDU_FLUSH_CTRL_FIFO BIT(_NIST_TRNG_EDU_FLUSH_CTRL_FIFO) ++ ++/*EDU_RBC_CTRL*/ ++#define _NIST_TRNG_EDU_RBC_CTRL_CH2_URUN_BLANK 28 ++#define _NIST_TRNG_EDU_RBC_CTRL_CH1_URUN_BLANK 26 ++#define _NIST_TRNG_EDU_RBC_CTRL_CH0_URUN_BLANK 24 ++#define _NIST_TRNG_EDU_RBC_CTRL_CH2_RATE 16 ++#define _NIST_TRNG_EDU_RBC_CTRL_CH1_RATE 8 ++#define _NIST_TRNG_EDU_RBC_CTRL_CH0_RATE 0 ++ ++#define _NIST_TRNG_EDU_RBC_CTRL_CH_RATE_MASK 0xFUL ++#define _NIST_TRNG_EDU_RBC_CTRL_CH_URUN_BLANK_MASK 0x3UL ++ ++#define NISTTRNG_EDU_RBC_CTRL_SET_CH_RATE(z, y, x) (((y) & ~(_NIST_TRNG_EDU_RBC_CTRL_CH_RATE_MASK << (x))) | ((z) << (x))) ++#define NISTTRNG_EDU_RBC_CTRL_SET_CH_URUN_BLANK(z, y, x) (((y) & ~(_NIST_TRNG_EDU_RBC_CTRL_CH_URUN_BLANK_MASK << (x))) | ((z) << (x))) ++ ++#define NISTTRNG_EDU_RBC_CTRL_GET_CH_RATE(y, x) ((_NIST_TRNG_EDU_RBC_CTRL_CH_RATE_MASK) & ((y) >> (x))) ++#define NISTTRNG_EDU_RBC_CTRL_GET_CH_URUN_BLANK(y, x) ((_NIST_TRNG_EDU_RBC_CTRL_CH_URUN_BLANK_MASK) & ((y) >> (x))) ++ ++#define NISTTRNG_EDU_RBC_CTRL_GET_CH_RATE_AFTER_RESET 0x0 ++#define NISTTRNG_EDU_RBC_CTRL_SET_CH_URUN_BLANK_AFTER_RESET 0x3 ++ ++/* MODE */ ++#define _NIST_TRNG_REG_MODE_KAT_SEL 7 ++#define _NIST_TRNG_REG_MODE_KAT_VEC 5 ++#define _NIST_TRNG_REG_MODE_ADDIN_PRESENT 4 ++#define _NIST_TRNG_REG_MODE_PRED_RESIST 3 ++#define _NIST_TRNG_REG_MODE_SEC_ALG 0 ++ ++#define NIST_TRNG_REG_MODE_ADDIN_PRESENT BIT(_NIST_TRNG_REG_MODE_ADDIN_PRESENT) ++#define NIST_TRNG_REG_MODE_PRED_RESIST BIT(_NIST_TRNG_REG_MODE_PRED_RESIST) ++#define NIST_TRNG_REG_MODE_SEC_ALG BIT(_NIST_TRNG_REG_MODE_SEC_ALG) ++ ++/* SMODE */ ++#define _NIST_TRNG_REG_SMODE_NOISE_COLLECT 31 ++#define _NIST_TRNG_REG_SMODE_INDIV_HT_DISABLE 16 ++#define _NIST_TRNG_REG_SMODE_MAX_REJECTS 2 ++#define _NIST_TRNG_REG_SMODE_MISSION_MODE 1 ++#define _NIST_TRNG_REG_SMODE_SECURE_EN _NIST_TRNG_REG_SMODE_MISSION_MODE ++#define _NIST_TRNG_REG_SMODE_NONCE 0 ++ ++#define NIST_TRNG_REG_SMODE_MAX_REJECTS(x) ((x) << _NIST_TRNG_REG_SMODE_MAX_REJECTS) ++#define NIST_TRNG_REG_SMODE_SECURE_EN(x) ((x) << _NIST_TRNG_REG_SMODE_SECURE_EN) ++#define NIST_TRNG_REG_SMODE_NONCE BIT(_NIST_TRNG_REG_SMODE_NONCE) ++ ++/* STAT */ ++#define _NIST_TRNG_REG_STAT_BUSY 31 ++#define _NIST_TRNG_REG_STAT_STARTUP_TEST_IN_PROG 10 ++#define _NIST_TRNG_REG_STAT_STARTUP_TEST_STUCK 9 ++#define _NIST_TRNG_REG_STAT_DRBG_STATE 7 ++#define _NIST_TRNG_REG_STAT_SECURE 6 ++#define _NIST_TRNG_REG_STAT_NONCE_MODE 5 ++#define _NIST_TRNG_REG_STAT_SEC_ALG 4 ++#define _NIST_TRNG_REG_STAT_LAST_CMD 0 ++ ++#define NIST_TRNG_REG_STAT_BUSY BIT(_NIST_TRNG_REG_STAT_BUSY) ++//#define NIST_TRNG_REG_STAT_DRBG_STATE (1UL<<_NIST_TRNG_REG_STAT_DRBG_STATE) ++//#define NIST_TRNG_REG_STAT_SECURE (1UL << _NIST_TRNG_REG_STAT_SECURE) ++//#define NIST_TRNG_REG_STAT_NONCE_MODE (1UL << _NIST_TRNG_REG_STAT_NONCE_MODE) ++//#define NIST_TRNG_REG_STAT_SEC_ALG (1UL << _NIST_TRNG_REG_STAT_SEC_ALG) ++//#define NIST_TRNG_REG_STAT_LAST_CMD(x) (((x) >> _NIST_TRNG_REG_STAT_LAST_CMD)&0xF) ++ ++/*EDU_STAT*/ ++ ++#define NIST_TRNG_EDU_STAT_FIFO_LEVEL(x) (((x) >> 24) & 255) ++#define NIST_TRNG_EDU_STAT_TTT_INDEX(x) (((x) >> 16) & 255) ++#define NIST_TRNG_EDU_STAT_RNC_BUSY(x) (((x) >> 3) & 7) ++#define NIST_TRNG_EDU_STAT_RNC_ENABLED(x) (((x) >> 2) & 1) ++#define NIST_TRNG_EDU_STAT_FIFO_EMPTY(x) (((x) >> 1) & 1) ++#define NIST_TRNG_EDU_STAT_FIFO_FULL(x) ((x) & 1) ++ ++/* IE */ ++#define _NIST_TRNG_REG_IE_GLBL 31 ++#define _NIST_TRNG_REG_IE_DONE 4 ++#define _NIST_TRNG_REG_IE_ALARMS 3 ++#define _NIST_TRNG_REG_IE_NOISE_RDY 2 ++#define _NIST_TRNG_REG_IE_KAT_COMPLETE 1 ++#define _NIST_TRNG_REG_IE_ZEROIZE 0 ++ ++#define NIST_TRNG_REG_IE_GLBL BIT(_NIST_TRNG_REG_IE_GLBL) ++#define NIST_TRNG_REG_IE_DONE BIT(_NIST_TRNG_REG_IE_DONE) ++#define NIST_TRNG_REG_IE_ALARMS BIT(_NIST_TRNG_REG_IE_ALARMS) ++#define NIST_TRNG_REG_IE_NOISE_RDY BIT(_NIST_TRNG_REG_IE_NOISE_RDY) ++#define NIST_TRNG_REG_IE_KAT_COMPLETE BIT(_NIST_TRNG_REG_IE_KAT_COMPLETE) ++#define NIST_TRNG_REG_IE_ZEROIZE BIT(_NIST_TRNG_REG_IE_ZEROIZE) ++ ++/* ISTAT */ ++#define _NIST_TRNG_REG_ISTAT_DONE 4 ++#define _NIST_TRNG_REG_ISTAT_ALARMS 3 ++#define _NIST_TRNG_REG_ISTAT_NOISE_RDY 2 ++#define _NIST_TRNG_REG_ISTAT_KAT_COMPLETE 1 ++#define _NIST_TRNG_REG_ISTAT_ZEROIZE 0 ++ ++#define NIST_TRNG_REG_ISTAT_DONE BIT(_NIST_TRNG_REG_ISTAT_DONE) ++#define NIST_TRNG_REG_ISTAT_ALARMS BIT(_NIST_TRNG_REG_ISTAT_ALARMS) ++#define NIST_TRNG_REG_ISTAT_NOISE_RDY BIT(_NIST_TRNG_REG_ISTAT_NOISE_RDY) ++#define NIST_TRNG_REG_ISTAT_KAT_COMPLETE BIT(_NIST_TRNG_REG_ISTAT_KAT_COMPLETE) ++#define NIST_TRNG_REG_ISTAT_ZEROIZE BIT(_NIST_TRNG_REG_ISTAT_ZEROIZE) ++ ++/*EDU_ISTAT*/ ++ ++#define _NIST_TRNG_EDU_ISTAT_CH2_RBC_URUN 8 ++#define _NIST_TRNG_EDU_ISTAT_CH1_RBC_URUN 7 ++#define _NIST_TRNG_EDU_ISTAT_CH0_RBC_URUN 6 ++#define _NIST_TRNG_EDU_ISTAT_PRIVATE_VTRNG 5 ++#define _NIST_TRNG_EDU_ISTAT_WAIT_EXP_TIMEOUT 4 ++#define _NIST_TRNG_EDU_ISTAT_RNC_DRVN_OFFLINE 3 ++#define _NIST_TRNG_EDU_ISTAT_FIFO_URUN 2 ++#define _NIST_TRNG_EDU_ISTAT_ACCESS_VIOL 1 ++#define _NIST_TRNG_EDU_ISTAT_RESEED_REMINDER 0 ++ ++#define NIST_TRNG_EDU_ISTAT_CH2_RBC_URUN BIT(_NIST_TRNG_EDU_ISTAT_CH2_RBC_URUN) ++#define NIST_TRNG_EDU_ISTAT_CH1_RBC_URUN BIT(_NIST_TRNG_EDU_ISTAT_CH1_RBC_URUN) ++#define NIST_TRNG_EDU_ISTAT_CH0_RBC_URUN BIT(_NIST_TRNG_EDU_ISTAT_CH0_RBC_URUN) ++#define NIST_TRNG_EDU_ISTAT_PRIVATE_VTRNG BIT(_NIST_TRNG_EDU_ISTAT_PRIVATE_VTRNG) ++#define NIST_TRNG_EDU_ISTAT_WAIT_EXP_TIMEOUT BIT(_NIST_TRNG_EDU_ISTAT_WAIT_EXP_TIMEOUT) ++#define NIST_TRNG_EDU_ISTAT_RNC_DRVN_OFFLINE BIT(_NIST_TRNG_EDU_ISTAT_RNC_DRVN_OFFLINE) ++#define NIST_TRNG_EDU_ISTAT_FIFO_URUN BIT(_NIST_TRNG_EDU_ISTAT_FIFO_URUN) ++#define NIST_TRNG_EDU_ISTAT_ACCESS_VIOL BIT(_NIST_TRNG_EDU_ISTAT_ACCESS_VIOL) ++#define NIST_TRNG_EDU_ISTAT_RESEED_REMINDER BIT(_NIST_TRNG_EDU_ISTAT_RESEED_REMINDER) ++ ++/* ALARMS */ ++#define NIST_TRNG_REG_ALARM_ILLEGAL_CMD_SEQ BIT(4) ++#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_OK 0 ++#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_KAT_STAT 1 ++#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_KAT 2 ++#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_MONOBIT 3 ++#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_RUN 4 ++#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_LONGRUN 5 ++#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_AUTOCORRELATION 6 ++#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_POKER 7 ++#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_REPETITION_COUNT 8 ++#define NIST_TRNG_REG_ALARM_FAILED_TEST_ID_ADAPATIVE_PROPORTION 9 ++ ++/* COREKIT_REL */ ++#define NIST_TRNG_REG_EXT_ENUM(x) (((x) >> 28) & 0xF) ++#define NIST_TRNG_REG_EXT_VER(x) (((x) >> 23) & 0xFF) ++#define NIST_TRNG_REG_REL_NUM(x) ((x) & 0xFFFF) ++ ++// This will be deleted ?? per comments in hw details. ie use CFG ++/* FEATURES */ ++#define NIST_TRNG_REG_FEATURES_AES_256(x) (((x) >> 9) & 1) ++#define NIST_TRNG_REG_FEATURES_EXTRA_PS_PRESENT(x) (((x) >> 8) & 1) ++#define NIST_TRNG_REG_FEATURES_DIAG_LEVEL_NS(x) (((x) >> 7) & 1) ++#define NIST_TRNG_REG_FEATURES_DIAG_LEVEL_BASIC_TRNG(x) (((x) >> 4) & 7) ++#define NIST_TRNG_REG_FEATURES_DIAG_LEVEL_ST_HLT(x) (((x) >> 1) & 7) ++#define NIST_TRNG_REG_FEATURES_SECURE_RST_STATE(x) ((x) & 1) ++ ++/* build_CFG0 */ ++#define NIST_TRNG_REG_CFG0_PERSONILIZATION_STR(x) (((x) >> 14) & 1) ++#define NIST_TRNG_REG_CFG0_AES_MAX_KEY_SIZE(x) (((x) >> 13) & 1) ++#define NIST_TRNG_REG_CFG0_AES_DATAPATH(x) (((x) >> 12) & 1) ++#define NIST_TRNG_REG_CFG0_EDU_PRESENT(x) (((x) >> 11) & 1) ++#define NIST_TRNG_REG_CFG0_BACGROUND_NOISE(x) (((x) >> 10) & 1) ++#define NIST_TRNG_REG_CFG0_CDC_SYNCH_DEPTH(x) (((x) >> 8) & 3) ++#define NIST_TRNG_REG_CFG0_BG8(x) (((x) >> 7) & 1) ++#define NIST_TRNG_REG_CFG0_CORE_TYPE(x) ((x) & 3) ++ ++/* build_CFG1 */ ++#define NIST_TRNG_REG_CFG1_ENT_SRC_REP_MIN_ENTROPY(x) (((x) >> 24) & 255) ++#define NIST_TRNG_REG_CFG1_ENT_SRC_REP_TEST(x) (((x) >> 23) & 1) ++#define NIST_TRNG_REG_CFG1_ENT_SRC_REP_SMPL_SIZE(x) (((x) >> 20) & 7) ++#define NIST_TRNG_REG_CFG1_RAW_HT_REP_TEST(x) (((x) >> 19) & 1) ++#define NIST_TRNG_REG_CFG1_RAW_HT_ADAP_TEST(x) (((x) >> 16) & 7) ++#define NIST_TRNG_REG_CFG1_POKER_TEST(x) (((x) >> 15) & 1) ++#define NIST_TRNG_REG_CFG1_RUN_TEST(x) (((x) >> 14) & 1) ++#define NIST_TRNG_REG_CFG1_MONO_BIT_TEST(x) (((x) >> 13) & 1) ++#define NIST_TRNG_REG_CFG1_AUTO_CORRELATION_TEST(x) (((x) >> 12) & 1) ++#define NIST_TRNG_REG_CFG1_STICKY_STARTUP(x) (((x) >> 8) & 1) ++#define NIST_TRNG_REG_CFG1_NUM_RAW_NOISE_BLKS(x) ((x) & 255) ++ ++/* EDU_BUILD_CFG0 */ ++#define NIST_TRNG_REG_EDU_CFG0_RBC2_RATE_WIDTH(x) (((x) >> 20) & 7) ++#define NIST_TRNG_REG_EDU_CFG0_RBC1_RATE_WIDTH(x) (((x) >> 16) & 7) ++#define NIST_TRNG_REG_EDU_CFG0_RBC0_RATE_WIDTH(x) (((x) >> 12) & 7) ++#define NIST_TRNG_REG_EDU_CFG0_PUBLIC_VTRNG_CHANNELS(x) (((x) >> 8) & 15) ++#define NIST_TRNG_REG_EDU_CFG0_ESM_CHANNEL(x) (((x) >> 6) & 1) ++#define NIST_TRNG_REG_EDU_CFG0_RBC_CHANNELS(x) (((x) >> 4) & 3) ++#define NIST_TRNG_REG_EDU_CFG0_FIFO_DEPTH(x) (((x) >> 2) & 7) ++ ++/* EDU_VSTAT */ ++#define NIST_TRNG_REG_EDU_VSTAT_BUSY(x) (((x) >> 31) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_RNC_ENABLED(x) (((x) >> 30) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_SEED_ENUM(x) (((x) >> 28) & 3) ++#define NIST_TRNG_REG_EDU_VSTAT_RWUE(x) (((x) >> 27) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_RWNE(x) (((x) >> 26) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_SRWE(x) (((x) >> 25) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_ANY_RW1(x) (((x) >> 24) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_BCKGRND_NOISE(x) (((x) >> 23) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_RNC_FIFO_EMPTY(x) (((x) >> 22) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_SLICE_RWI3(x) (((x) >> 15) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_SLICE_RWI2(x) (((x) >> 14) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_SLICE_RWI1(x) (((x) >> 13) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_SLICE_RWI0(x) (((x) >> 12) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD3(x) (((x) >> 11) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD2(x) (((x) >> 10) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD1(x) (((x) >> 9) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD0(x) (((x) >> 8) & 1) ++#define NIST_TRNG_REG_EDU_VSTAT_CURRENT_CMD(x) (((x) >> 4) & 15) ++#define NIST_TRNG_REG_EDU_VSTAT_LAST_CMD(x) ((x) & 15) ++ ++#define _NIST_TRNG_REG_SMODE_MAX_REJECTS_MASK 255UL ++#define _NIST_TRNG_REG_SMODE_SECURE_EN_MASK 1UL ++#define _NIST_TRNG_REG_SMODE_NONCE_MASK 1UL ++#define _NIST_TRNG_REG_MODE_SEC_ALG_MASK 1UL ++#define _NIST_TRNG_REG_MODE_ADDIN_PRESENT_MASK 1UL ++#define _NIST_TRNG_REG_MODE_PRED_RESIST_MASK 1UL ++#define _NIST_TRNG_REG_MODE_KAT_SEL_MASK 3UL ++#define _NIST_TRNG_REG_MODE_KAT_VEC_MASK 3UL ++#define _NIST_TRNG_REG_STAT_DRBG_STATE_MASK 3UL ++#define _NIST_TRNG_REG_STAT_SECURE_MASK 1UL ++#define _NIST_TRNG_REG_STAT_NONCE_MASK 1UL ++ ++#define NIST_TRNG_REG_SMODE_SET_MAX_REJECTS(y, x) (((y) & ~(_NIST_TRNG_REG_SMODE_MAX_REJECTS_MASK << _NIST_TRNG_REG_SMODE_MAX_REJECTS)) | ((x) << _NIST_TRNG_REG_SMODE_MAX_REJECTS)) ++#define NIST_TRNG_REG_SMODE_SET_SECURE_EN(y, x) (((y) & ~(_NIST_TRNG_REG_SMODE_SECURE_EN_MASK << _NIST_TRNG_REG_SMODE_SECURE_EN)) | ((x) << _NIST_TRNG_REG_SMODE_SECURE_EN)) ++#define NIST_TRNG_REG_SMODE_SET_NONCE(y, x) (((y) & ~(_NIST_TRNG_REG_SMODE_NONCE_MASK << _NIST_TRNG_REG_SMODE_NONCE)) | ((x) << _NIST_TRNG_REG_SMODE_NONCE)) ++#define NIST_TRNG_REG_SMODE_GET_MAX_REJECTS(x) (((x) >> _NIST_TRNG_REG_SMODE_MAX_REJECTS) & _NIST_TRNG_REG_SMODE_MAX_REJECTS_MASK) ++#define NIST_TRNG_REG_SMODE_GET_SECURE_EN(x) (((x) >> _NIST_TRNG_REG_SMODE_SECURE_EN) & _NIST_TRNG_REG_SMODE_SECURE_EN_MASK) ++#define NIST_TRNG_REG_SMODE_GET_NONCE(x) (((x) >> _NIST_TRNG_REG_SMODE_NONCE) & _NIST_TRNG_REG_SMODE_NONCE_MASK) ++ ++#define NIST_TRNG_REG_MODE_SET_SEC_ALG(y, x) (((y) & ~(_NIST_TRNG_REG_MODE_SEC_ALG_MASK << _NIST_TRNG_REG_MODE_SEC_ALG)) | ((x) << _NIST_TRNG_REG_MODE_SEC_ALG)) ++#define NIST_TRNG_REG_MODE_SET_PRED_RESIST(y, x) (((y) & ~(_NIST_TRNG_REG_MODE_PRED_RESIST_MASK << _NIST_TRNG_REG_MODE_PRED_RESIST)) | ((x) << _NIST_TRNG_REG_MODE_PRED_RESIST)) ++#define NIST_TRNG_REG_MODE_SET_ADDIN_PRESENT(y, x) (((y) & ~(_NIST_TRNG_REG_MODE_ADDIN_PRESENT_MASK << _NIST_TRNG_REG_MODE_ADDIN_PRESENT)) | ((x) << _NIST_TRNG_REG_MODE_ADDIN_PRESENT)) ++#define NIST_TRNG_REG_MODE_SET_KAT_SEL(y, x) (((y) & ~(_NIST_TRNG_REG_MODE_KAT_SEL_MASK << _NIST_TRNG_REG_MODE_KAT_SEL)) | ((x) << _NIST_TRNG_REG_MODE_KAT_SEL)) ++#define NIST_TRNG_REG_MODE_SET_KAT_VEC(y, x) (((y) & ~(_NIST_TRNG_REG_MODE_KAT_VEC_MASK << _NIST_TRNG_REG_MODE_KAT_VEC)) | ((x) << _NIST_TRNG_REG_MODE_KAT_VEC)) ++#define NIST_TRNG_REG_MODE_GET_SEC_ALG(x) (((x) >> _NIST_TRNG_REG_MODE_SEC_ALG) & _NIST_TRNG_REG_MODE_SEC_ALG_MASK) ++#define NIST_TRNG_REG_MODE_GET_PRED_RESIST(x) (((x) >> _NIST_TRNG_REG_MODE_PRED_RESIST) & _NIST_TRNG_REG_MODE_PRED_RESIST_MASK) ++#define NIST_TRNG_REG_MODE_GET_ADDIN_PRESENT(x) (((x) >> _NIST_TRNG_REG_MODE_ADDIN_PRESENT) & _NIST_TRNG_REG_MODE_ADDIN_PRESENT_MASK) ++#define NIST_TRNG_REG_STAT_GET_DRBG_STATE(x) (((x) >> _NIST_TRNG_REG_STAT_DRBG_STATE) & _NIST_TRNG_REG_STAT_DRBG_STATE_MASK) ++#define NIST_TRNG_REG_STAT_GET_SECURE(x) (((x) >> _NIST_TRNG_REG_STAT_SECURE) & _NIST_TRNG_REG_STAT_SECURE_MASK) ++#define NIST_TRNG_REG_STAT_GET_NONCE(x) (((x) >> _NIST_TRNG_REG_STAT_NONCE_MODE) & _NIST_TRNG_REG_STAT_NONCE_MASK) ++ ++#endif +diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_private.h b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_private.h +--- a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_private.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_private.h 2026-04-08 18:03:46.629736033 +0000 +@@ -0,0 +1,89 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * This Synopsys software and associated documentation (hereinafter the ++ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. The ++ * Software IS NOT an item of Licensed Software or a Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Products ++ * with Synopsys or any supplement thereto. Synopsys is a registered trademark ++ * of Synopsys, Inc. Other names included in the SOFTWARE may be the ++ * trademarks of their respective owners. ++ * ++ * The contents of this file are dual-licensed; you may select either version ++ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license ++ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the ++ * SOFTWARE. The BSD License is copied below. ++ * ++ * BSD-3-Clause License: ++ * Copyright (c) 2012-2016 Synopsys, Inc. and/or its affiliates. ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions, and the following disclaimer, without ++ * modification. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * 3. The names of the above-listed copyright holders may not be used to ++ * endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef NISTTRNG_PRIVATE_H ++#define NISTTRNG_PRIVATE_H ++ ++#include "elppdu.h" ++#include "nisttrng_hw.h" ++#include "nisttrng_common.h" ++ ++int nisttrng_wait_on_busy(struct nist_trng_state *state); ++int nisttrng_wait_on_done(struct nist_trng_state *state); ++int nisttrng_wait_on_noise_rdy(struct nist_trng_state *state); ++int nisttrng_get_alarms(struct nist_trng_state *state); ++int nisttrng_reset_state(struct nist_trng_state *state); ++ ++/* ---------- Reminders ---------- */ ++int nisttrng_reset_counters(struct nist_trng_state *state); ++int nisttrng_set_reminder_max_bits_per_req(struct nist_trng_state *state, unsigned long max_bits_per_req); ++int nisttrng_set_reminder_max_req_per_seed(struct nist_trng_state *state, unsigned long long max_req_per_seed); ++int nisttrng_check_seed_lifetime(struct nist_trng_state *state); ++ ++/* ---------- Set field APIs ---------- */ ++int nisttrng_set_sec_strength(struct nist_trng_state *state, int req_sec_strength); ++int nisttrng_set_addin_present(struct nist_trng_state *state, int addin_present); ++int nisttrng_set_pred_resist(struct nist_trng_state *state, int pred_resist); ++int nisttrng_set_secure_mode(struct nist_trng_state *state, int secure_mode); ++int nisttrng_set_nonce_mode(struct nist_trng_state *state, int nonce_mode); ++ ++/* ---------- Load data APIs ---------- */ ++int nisttrng_load_ps_addin(struct nist_trng_state *state, void *input_str); ++ ++/* ---------- Command APIs ---------- */ ++int nisttrng_get_entropy_input(struct nist_trng_state *state, void *input_nonce, int nonce_operation); ++int nisttrng_refresh_addin(struct nist_trng_state *state, void *addin_str); ++int nisttrng_gen_random(struct nist_trng_state *state, void *random_bits, unsigned long req_num_bytes); ++int nisttrng_advance_state(struct nist_trng_state *state); ++int nisttrng_kat(struct nist_trng_state *state, int kat_sel, int kat_vec); ++int nisttrng_full_kat(struct nist_trng_state *state); ++int nisttrng_zeroize(struct nist_trng_state *state); ++ ++/* ---------- edu related ---------- */ ++ ++int nisttrng_rnc(struct nist_trng_state *state, int rnc_ctrl_cmd); ++int nisttrng_wait_fifo_full(struct nist_trng_state *state); ++#endif +diff --git a/drivers/char/hw_random/dwc/src/trng/include/synversion.h b/drivers/char/hw_random/dwc/src/trng/include/synversion.h +--- a/drivers/char/hw_random/dwc/src/trng/include/synversion.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/include/synversion.h 2026-04-08 18:03:46.629736033 +0000 +@@ -0,0 +1,52 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * This Synopsys software and associated documentation (hereinafter the ++ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. The ++ * Software IS NOT an item of Licensed Software or a Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Products ++ * with Synopsys or any supplement thereto. Synopsys is a registered trademark ++ * of Synopsys, Inc. Other names included in the SOFTWARE may be the ++ * trademarks of their respective owners. ++ * ++ * The contents of this file are dual-licensed; you may select either version ++ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license ++ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the ++ * SOFTWARE. The BSD License is copied below. ++ * ++ * BSD-3-Clause License: ++ * Copyright (c) 2012-2016 Synopsys, Inc. and/or its affiliates. ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions, and the following disclaimer, without ++ * modification. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * 3. The names of the above-listed copyright holders may not be used to ++ * endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VERSION_H ++#define VERSION_H ++ ++#define TRNG_VERSION "1.00a" ++ ++#endif +diff --git a/drivers/char/hw_random/dwc/src/trng/kernel/nist_trng.c b/drivers/char/hw_random/dwc/src/trng/kernel/nist_trng.c +--- a/drivers/char/hw_random/dwc/src/trng/kernel/nist_trng.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/kernel/nist_trng.c 2026-04-08 18:03:46.630736014 +0000 +@@ -0,0 +1,2170 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * This Synopsys software and associated documentation (hereinafter the ++ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. The ++ * Software IS NOT an item of Licensed Software or a Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Products ++ * with Synopsys or any supplement thereto. Synopsys is a registered trademark ++ * of Synopsys, Inc. Other names included in the SOFTWARE may be the ++ * trademarks of their respective owners. ++ * ++ * The contents of this file are dual-licensed; you may select either version ++ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license ++ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the ++ * SOFTWARE. The BSD License is copied below. ++ * ++ * BSD-3-Clause License: ++ * Copyright (c) 2012-2017 Synopsys, Inc. and/or its affiliates. ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions, and the following disclaimer, without ++ * modification. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * 3. The names of the above-listed copyright holders may not be used to ++ * endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "nisttrng.h" ++ ++#define SYNOPSYS_HWRNG_DRIVER_NAME "hwrng-nist_trng" ++ ++#define num_gen_bytes 64 ++static unsigned long max_reads = 128; ++ ++struct synopsys_nisttrng_driver { ++ struct nist_trng_state nisttrng; ++ void *hwrng_drv; ++ void *crypto_drv; ++ unsigned char rand_out[num_gen_bytes]; ++}; ++ ++static unsigned int xxd_vtrng; ++ ++static void nisttrng_reinit(struct nist_trng_state *nist_trng) ++{ ++ int err; ++ ++ err = nisttrng_uninstantiate(nist_trng); ++ if (err && err != CRYPTO_NOT_INSTANTIATED) ++ goto ERR; ++ ++ err = nisttrng_instantiate(nist_trng, 128, 1, NULL); ++ if (err) ++ goto ERR; ++ ++ERR: ++ DEBUG("NIST_TRNG: Trying to reinitialize after a fatal alarm: %d\n", ++ err); ++} ++ ++static int nisttrng_platform_driver_read(struct platform_device *pdev, ++ void *buf, size_t max, bool wait) ++{ ++ struct synopsys_nisttrng_driver *data = 0; ++ int nisttrng_error = -1; ++ u32 *out = kmalloc(max, GFP_KERNEL); ++ unsigned int vtrng; ++ ++ if (!out) { ++ SYNHW_PRINT("memory not allocated\n"); ++ return -1; ++ } ++ ++ if (!pdev || !buf || !max) ++ return nisttrng_error; ++ ++ data = platform_get_drvdata(pdev); ++ if (data == 0) ++ return nisttrng_error; ++ ++ if (data->nisttrng.config.build_cfg0.edu_present) { ++ vtrng = xxd_vtrng % ((data->nisttrng.config.edu_build_cfg0 ++ .public_vtrng_channels) + ++ 1); ++ if (vtrng == 0) { ++ /* private vtrng */ ++ nisttrng_error = nisttrng_generate(&data->nisttrng, out, max, ++ data->nisttrng.status.sec_strength ? 256 : 128, ++ data->nisttrng.status.pred_resist, NULL); ++ } else { ++ /* public vtrng */ ++ nisttrng_error = nisttrng_generate_public_vtrng(&data->nisttrng, out, max, vtrng - 1); ++ } ++ xxd_vtrng++; ++ } else { ++ /* nist core vtrng */ ++ nisttrng_error = nisttrng_generate(&data->nisttrng, out, max, ++ data->nisttrng.status.sec_strength ? 256 : 128, ++ data->nisttrng.status.pred_resist, NULL); ++ } ++ if (nisttrng_error < 0) { ++ if (data->nisttrng.status.alarm_code) ++ nisttrng_reinit(&data->nisttrng); ++ ++ return nisttrng_error; ++ } ++ ++ memcpy(buf, out, max); ++ kfree(out); ++ ++ return max; ++} ++ ++static int nisttrng_hwrng_driver_read(struct hwrng *rng, void *buf, size_t max, ++ bool wait) ++{ ++ struct platform_device *pdev = 0; ++ ++ if (rng == 0) ++ return -1; ++ ++ pdev = (struct platform_device *)rng->priv; ++ return nisttrng_platform_driver_read(pdev, buf, max, wait); ++} ++ ++static ssize_t ckr_show(struct device *dev, struct device_attribute *devattr, ++ char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "rel_num=%u, ext_ver=%u, ext_enum=%u\n", ++ priv->nisttrng.config.corekit_rel.rel_num, ++ priv->nisttrng.config.corekit_rel.ext_ver, ++ priv->nisttrng.config.corekit_rel.ext_enum); ++} ++ ++static ssize_t features_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, ++ "drbg_arch = %u, diag_basic_trng=%u, diag_st_hlt=%u, diag_ns=%u, secure_rst_state=%u, extra_ps_present=%u\n", ++ priv->nisttrng.config.features.drbg_arch, ++ priv->nisttrng.config.features.diag_level_basic_trng, ++ priv->nisttrng.config.features.diag_level_stat_hlt, ++ priv->nisttrng.config.features.diag_level_ns, ++ priv->nisttrng.config.features.secure_rst_state, ++ priv->nisttrng.config.features.extra_ps_present); ++} ++ ++static ssize_t secure_show(struct device *dev, struct device_attribute *devattr, ++ char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%s\n", NIST_TRNG_REG_SMODE_GET_SECURE_EN(pdu_io_read32(priv->nisttrng.base + ++ NIST_TRNG_REG_SMODE)) ? "on" : "off"); ++} ++ ++static ssize_t secure_store(struct device *dev, ++ struct device_attribute *devattr, const char *buf, ++ size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = nisttrng_set_secure_mode(&priv->nisttrng, ++ sysfs_streq(buf, "on") ? 1 : 0); ++ if (ret) ++ return -1; ++ ++ return count; ++} ++ ++static ssize_t nonce_show(struct device *dev, struct device_attribute *devattr, ++ char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%s\n", NIST_TRNG_REG_SMODE_GET_NONCE(pdu_io_read32(priv->nisttrng.base + ++ NIST_TRNG_REG_SMODE)) ? "on" : "off"); ++} ++ ++static ssize_t nonce_store(struct device *dev, struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = nisttrng_set_nonce_mode(&priv->nisttrng, ++ sysfs_streq(buf, "on") ? 1 : 0); ++ if (ret) ++ return -1; ++ ++ return count; ++} ++ ++static ssize_t sec_strength_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%s\n", ++ priv->nisttrng.status.sec_strength ? "256" : "128"); ++} ++ ++static ssize_t sec_strength_store(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[9]; ++ int tmp; ++ int ret; ++ ++ if (count > 8) ++ return -1; ++ ++ foo[8] = 0; ++ memcpy(foo, buf, 8); ++ ret = kstrtoint(foo, 10, &tmp); ++ if (ret) ++ return ret; ++ ++ ret = nisttrng_set_sec_strength(&priv->nisttrng, tmp); ++ if (ret) ++ return -1; ++ ++ return count; ++} ++ ++static ssize_t rand_reg_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ unsigned int x; ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ for (x = 0; x < 4; x++) { ++ sprintf(buf + 8 * x, "%08lx", ++ pdu_io_read32(priv->nisttrng.base + ++ NIST_TRNG_REG_RAND0 + 3 - x)); ++ } ++ ++ strcat(buf, "\n"); ++ return strlen(buf); ++} ++ ++static ssize_t seed_reg_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ unsigned int x; ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ for (x = 0; x < 12; x++) { ++ sprintf(buf + 8 * x, "%08lx", ++ pdu_io_read32(priv->nisttrng.base + ++ NIST_TRNG_REG_SEED0 + 11 - x)); ++ } ++ strcat(buf, "\n"); ++ return strlen(buf); ++} ++ ++static ssize_t seed_reg_store(struct device *dev, ++ struct device_attribute *devattr, const char *buf, ++ size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[9]; ++ unsigned int x, tmp; ++ int ret; ++ ++ // string must be at least 12 32-bit words long in 0 padded hex ++ if (count < (2 * 12 * 4)) ++ return -1; ++ ++ foo[8] = 0; ++ for (x = 0; x < 12; x++) { ++ memcpy(foo, buf + x * 8, 8); ++ ret = kstrtouint(foo, 16, &tmp); ++ if (ret) ++ return ret; ++ ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_SEED0 + x, ++ tmp); ++ } ++ ++ return count; ++} ++ ++static ssize_t npa_data_reg_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ unsigned int x; ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ for (x = 0; x < 16; x++) { ++ sprintf(buf + 8 * x, "%08lx", ++ pdu_io_read32(priv->nisttrng.base + ++ NIST_TRNG_REG_NPA_DATA0 + 15 - x)); ++ } ++ ++ strcat(buf, "\n"); ++ return strlen(buf); ++} ++ ++static ssize_t npa_data_reg_store(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[9]; ++ unsigned int x, tmp; ++ int ret; ++ ++ // string must be at least 16 32-bit words long in 0 padded hex ++ if (count < (2 * 16 * 4)) ++ return -1; ++ ++ foo[8] = 0; ++ for (x = 0; x < 16; x++) { ++ memcpy(foo, buf + x * 8, 8); ++ ret = kstrtouint(foo, 16, &tmp); ++ if (ret) ++ return ret; ++ ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_NPA_DATA0 + x, tmp); ++ } ++ ++ return count; ++} ++ ++static ssize_t ctrl_reg_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%08lx\n", ++ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_CTRL)); ++} ++ ++static ssize_t ctrl_reg_store(struct device *dev, ++ struct device_attribute *devattr, const char *buf, ++ size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[9]; ++ unsigned int tmp; ++ int ret; ++ ++ // string must be at least a 32-bit word in 0 padded hex ++ if (count < 8) ++ return -1; ++ ++ foo[8] = 0; ++ memcpy(foo, buf, 8); ++ ret = kstrtouint(foo, 16, &tmp); ++ if (ret) ++ return ret; ++ ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_CTRL, tmp); ++ return count; ++} ++ ++static ssize_t istat_reg_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%08lx\n", ++ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_ISTAT)); ++} ++ ++static ssize_t istat_reg_store(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[9]; ++ unsigned int tmp; ++ int ret; ++ ++ // string must be at least a 32-bit word in 0 padded hex ++ if (count < 8) ++ return -1; ++ ++ foo[8] = 0; ++ memcpy(foo, buf, 8); ++ ret = kstrtouint(foo, 16, &tmp); ++ if (ret) ++ return ret; ++ ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_ISTAT, tmp); ++ return count; ++} ++ ++static ssize_t mode_reg_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%08lx\n", ++ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_MODE)); ++} ++ ++static ssize_t mode_reg_store(struct device *dev, ++ struct device_attribute *devattr, const char *buf, ++ size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[9]; ++ unsigned int tmp; ++ int ret; ++ ++ // string must be at least a 32-bit word in 0 padded hex ++ if (count < 8) ++ return -1; ++ ++ foo[8] = 0; ++ memcpy(foo, buf, 8); ++ ret = kstrtouint(foo, 16, &tmp); ++ if (ret) ++ return ret; ++ ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_MODE, tmp); ++ ++ return count; ++} ++ ++static ssize_t smode_reg_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%08lx\n", ++ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_SMODE)); ++} ++ ++static ssize_t smode_reg_store(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[9]; ++ unsigned int tmp; ++ int ret; ++ ++ // string must be at least a 32-bit word in 0 padded hex ++ if (count < 8) ++ return -1; ++ ++ foo[8] = 0; ++ memcpy(foo, buf, 8); ++ ret = kstrtouint(foo, 16, &tmp); ++ if (ret) ++ return ret; ++ ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_SMODE, tmp); ++ return count; ++} ++ ++static ssize_t alarm_reg_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%08lx\n", ++ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_ALARM)); ++} ++ ++static ssize_t alarm_reg_store(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[9]; ++ unsigned int tmp; ++ int ret; ++ ++ // string must be at least a 32-bit word in 0 padded hex ++ if (count < 8) ++ return -1; ++ ++ foo[8] = 0; ++ memcpy(foo, buf, 8); ++ ret = kstrtouint(foo, 16, &tmp); ++ if (ret) ++ return ret; ++ ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_ALARM, tmp); ++ return count; ++} ++ ++static ssize_t stat_reg_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%08lx\n", ++ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_STAT)); ++} ++ ++static ssize_t ia_wdata_reg_store(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[9]; ++ unsigned int tmp; ++ int ret; ++ ++ // string must be at least a 32-bit word in 0 padded hex ++ if (count < 8) ++ return -1; ++ ++ foo[8] = 0; ++ memcpy(foo, buf, 8); ++ ret = kstrtouint(foo, 16, &tmp); ++ if (ret) ++ return ret; ++ ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_WDATA, tmp); ++ return count; ++} ++ ++static ssize_t ia_wdata_reg_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%08lx\n", ++ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_IA_WDATA)); ++} ++ ++static ssize_t ia_rdata_reg_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%08lx\n", ++ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_IA_RDATA)); ++} ++ ++static ssize_t ia_addr_reg_store(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[9]; ++ unsigned int tmp; ++ int ret; ++ ++ // string must be at least a 32-bit word in 0 padded hex ++ if (count < 8) ++ return -1; ++ ++ foo[8] = 0; ++ memcpy(foo, buf, 8); ++ ret = kstrtouint(foo, 16, &tmp); ++ if (ret) ++ return ret; ++ ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_ADDR, tmp); ++ return count; ++} ++ ++static ssize_t ia_addr_reg_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%08lx\n", ++ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_IA_ADDR)); ++} ++ ++static ssize_t ia_cmd_reg_store(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[9]; ++ unsigned int tmp; ++ int ret; ++ ++ // string must be at least a 32-bit word in 0 padded hex ++ if (count < 8) ++ return -1; ++ ++ foo[8] = 0; ++ memcpy(foo, buf, 8); ++ ret = kstrtouint(foo, 16, &tmp); ++ if (ret) ++ return ret; ++ ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_CMD, tmp); ++ return count; ++} ++ ++static ssize_t ia_cmd_reg_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%08lx\n", ++ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_IA_CMD)); ++} ++ ++static ssize_t rnc_reg_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%08lx\n", ++ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_EDU_RNC_CTRL)); ++} ++ ++static ssize_t rnc_reg_store(struct device *dev, ++ struct device_attribute *devattr, const char *buf, ++ size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[9]; ++ unsigned int tmp; ++ int ret; ++ ++ // string must be at least a 32-bit word in 0 padded hex ++ if (count < 8) ++ return -1; ++ ++ foo[8] = 0; ++ memcpy(foo, buf, 8); ++ ret = kstrtouint(foo, 16, &tmp); ++ if (ret) ++ return ret; ++ ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_EDU_RNC_CTRL, tmp); ++ ++ return count; ++} ++ ++static ssize_t rbc_reg_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%08lx\n", ++ pdu_io_read32(priv->nisttrng.base + NIST_TRNG_EDU_RBC_CTRL)); ++} ++ ++static ssize_t rbc_reg_store(struct device *dev, ++ struct device_attribute *devattr, const char *buf, ++ size_t count) ++{ ++ char opts_str[5]; ++ unsigned int opts_int; ++ int enable, rbc_num, rate, urun_blnk, ret; ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ opts_str[4] = 0; ++ memcpy(opts_str, buf, 4); ++ ret = kstrtouint(opts_str, 16, &opts_int); ++ if (ret) ++ return ret; ++ ++ SYNHW_PRINT("%s %x\n", __func__, opts_int); ++ ++ enable = (opts_int >> 12 & 0xf); ++ if (enable > 1) { ++ SYNHW_PRINT("incorrect enable %x\n", enable); ++ return -1; ++ } ++ ++ rbc_num = (opts_int >> 8 & 0xf); ++ if (rbc_num > priv->nisttrng.config.edu_build_cfg0.rbc_channels - 1) { ++ SYNHW_PRINT("incorrect rbc_num %x\n", rbc_num); ++ return -1; ++ } ++ ++ rate = (opts_int >> 4 & 0xf); ++ if (rate > 8) { ++ SYNHW_PRINT("incorrect rate %x\n", rate); ++ return -1; ++ } ++ ++ urun_blnk = (opts_int & 0xf); ++ if (urun_blnk > 3) { ++ SYNHW_PRINT("incorrect urun_blnk %x\n", urun_blnk); ++ return -1; ++ } ++ ++ SYNHW_PRINT("enable %x rbc_num %x rate %x urun_blnk %x\n", enable, ++ rbc_num, rate, urun_blnk); ++ ++ ret = nisttrng_rbc(&priv->nisttrng, enable, rbc_num, rate, ++ urun_blnk); ++ if (ret) ++ return -1; ++ ++ return count; ++} ++ ++static ssize_t hw_state_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ u32 addr; ++ int i; ++ int tot_char; ++ ++ addr = 0x20; ++ tot_char = sprintf(buf, "Key = "); ++ for (i = 0; i < 8; i++) { ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_ADDR, ++ addr + 7 - i); ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_CMD, ++ 0x80000000); ++ tot_char += sprintf(buf + tot_char, "%08lx", ++ pdu_io_read32(priv->nisttrng.base + ++ NIST_TRNG_REG_IA_RDATA)); ++ } ++ tot_char += sprintf(buf + tot_char, "\n"); ++ ++ addr = 0x28; ++ tot_char += sprintf(buf + tot_char, "V = "); ++ for (i = 0; i < 4; i++) { ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_ADDR, ++ addr + 3 - i); ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_CMD, ++ 0x80000000); ++ tot_char += sprintf(buf + tot_char, "%08lx", ++ pdu_io_read32(priv->nisttrng.base + ++ NIST_TRNG_REG_IA_RDATA)); ++ } ++ ++ tot_char += sprintf(buf + tot_char, "\n"); ++ ++ return tot_char; ++} ++ ++static ssize_t max_bits_per_req_store(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[9]; ++ unsigned long tmp; ++ int ret; ++ ++ // string must be at least a 32-bit word in 0 padded hex ++ if (count < 8) ++ return -1; ++ ++ foo[8] = 0; ++ memcpy(foo, buf, 8); ++ ret = kstrtoul(foo, 16, &tmp); ++ if (ret) ++ return ret; ++ ++ ret = nisttrng_set_reminder_max_bits_per_req(&priv->nisttrng, ++ tmp); ++ if (ret) ++ return -1; ++ ++ return count; ++} ++ ++static ssize_t max_bits_per_req_show(struct device *dev, ++ struct device_attribute *devattr, ++ char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%08lx\n", ++ priv->nisttrng.counters.max_bits_per_req); ++} ++ ++static ssize_t max_req_per_seed_store(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[17]; ++ unsigned long long tmp; ++ int ret; ++ ++ // string must be at least a 64-bit word in 0 padded hex ++ if (count < 16) ++ return -1; ++ ++ foo[16] = 0; ++ memcpy(foo, buf, 16); ++ ret = kstrtoull(foo, 16, &tmp); ++ if (ret) ++ return ret; ++ ++ ret = nisttrng_set_reminder_max_req_per_seed(&priv->nisttrng, ++ tmp); ++ if (ret) ++ return -1; ++ ++ return count; ++} ++ ++static ssize_t max_req_per_seed_show(struct device *dev, ++ struct device_attribute *devattr, ++ char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%08llx\n", ++ priv->nisttrng.counters.max_req_per_seed); ++} ++ ++static ssize_t collect_ent_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ int rep; ++ int i, j; ++ int ret; ++ u32 tmp; ++ int t; ++ ++ t = NIST_TRNG_RETRY_MAX; ++ ++ // Change to TEST mode ++ DEBUG("Change to TEST mode\n"); ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_SMODE, 0x00000028); ++ // Turn on the noise collect mode ++ DEBUG("Turn on the noise collect mode\n"); ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_SMODE, 0x80000028); ++ ++ // issue generate entropy command ++ DEBUG("Issue a GEN_NOISE command\n"); ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_CTRL, ++ NIST_TRNG_REG_CTRL_CMD_GEN_NOISE); ++ ++ // read raw noise ++ // 2 reads if sec_strength is 128 and 3 reads if it is 256 ++ if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES128) ++ rep = 2; ++ else if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES256) ++ rep = 3; ++ ++ for (i = 0; i < rep; i++) { ++ t = NIST_TRNG_RETRY_MAX; ++ tmp = 0; ++ DEBUG("Wait for NOISE_RDY interrupt.\n"); ++ do { ++ tmp = pdu_io_read32(priv->nisttrng.base + ++ NIST_TRNG_REG_ISTAT); ++ } while (!(tmp & (NIST_TRNG_REG_ISTAT_NOISE_RDY | ++ NIST_TRNG_REG_ISTAT_ALARMS)) && ++ --t); ++ ++ DEBUG("Read NPA_DATAx\n"); ++ for (j = 0; j < 16; j++) { ++ sprintf(buf + 128 * i + 8 * j, "%08lx", ++ pdu_io_read32(priv->nisttrng.base + ++ NIST_TRNG_REG_NPA_DATA0 + j)); ++ } ++ ++ // clear NOISE_RDY IRQ ++ DEBUG("Clear NOISE_RDY interrupt.\n"); ++ ret = nisttrng_wait_on_noise_rdy(&priv->nisttrng); ++ if (ret) ++ return -1; ++ } ++ ++ DEBUG("Wait for DONE\n"); ++ ret = nisttrng_wait_on_done(&priv->nisttrng); ++ if (ret) ++ return -1; ++ ++ strcat(buf, "\n"); ++ return strlen(buf); ++} ++ ++static ssize_t collect_ent_nsout_show(struct device *dev, ++ struct device_attribute *devattr, ++ char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ int rep; ++ int i; ++ int ret; ++ ++ // generate entropy ++ ret = nisttrng_get_entropy_input(&priv->nisttrng, NULL, 0); ++ if (ret) ++ return -1; ++ ++ // read NS_OUTPUTx ++ // 32 reads if sec_strength is 128 and 48 reads if it is 256 ++ if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES128) ++ rep = 32; ++ else if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES256) ++ rep = 48; ++ ++ for (i = 0; i < rep; i++) { ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_ADDR, ++ 0x70 + rep - 1 - i); ++ pdu_io_write32(priv->nisttrng.base + NIST_TRNG_REG_IA_CMD, ++ 0x80000000); ++ sprintf(buf + 8 * i, "%08lx", ++ pdu_io_read32(priv->nisttrng.base + ++ NIST_TRNG_REG_IA_RDATA)); ++ } ++ ++ strcat(buf, "\n"); ++ return strlen(buf); ++} ++ ++static ssize_t nonce_seed_with_df_store(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[9]; ++ u32 seed[48] = { 0 }; ++ int rep; ++ int i; ++ int ret; ++ ++ if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES128) ++ rep = 2; ++ else if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES256) ++ rep = 3; ++ ++ DEBUG("Number of char in input = %zu\n", count); ++ if (count != (rep * 128)) ++ return -1; ++ ++ foo[8] = 0; ++ for (i = 0; i < (rep * 16); i++) { ++ memcpy(foo, buf + i * 8, 8); ++ ret = kstrtouint(foo, 16, (seed + (rep * 16 - 1) - i)); ++ if (ret) ++ return ret; ++ } ++ ++ ret = nisttrng_get_entropy_input(&priv->nisttrng, seed, 1); ++ if (ret) ++ return -1; ++ ++ return count; ++} ++ ++static ssize_t nonce_seed_direct_store(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char foo[9]; ++ u32 seed[12] = { 0 }; ++ int rep; ++ int i; ++ int ret; ++ ++ if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES128) ++ rep = 2; ++ else if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES256) ++ rep = 3; ++ ++ DEBUG("Number of char in input = %zu\n", count); ++ if (count != (rep * 32)) ++ return -1; ++ ++ foo[8] = 0; ++ for (i = 0; i < (rep * 4); i++) { ++ memcpy(foo, buf + i * 8, 8); ++ ret = kstrtouint(foo, 16, (seed + (rep * 4 - 1) - i)); ++ if (ret) ++ return ret; ++ } ++ ++ ret = nisttrng_get_entropy_input(&priv->nisttrng, seed, 0); ++ if (ret) ++ return -1; ++ ++ return count; ++} ++ ++static ssize_t instantiate_store(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char opts_str[101]; ++ unsigned int opts_int; ++ int req_sec_strength = 256; ++ int pred_resist = 1; ++ bool ps_exists = 0; ++ u32 ps[12]; ++ unsigned int ps_length; ++ int i; ++ int ret; ++ ++ /* First 3 digits: ++ * they have to be 0 or 1 ++ * 2-1-0 --> 2: predictoin resistance, 1: security strength, 0: personilizatoin string existence ++ */ ++ opts_str[3] = 0; ++ memcpy(opts_str, buf, 3); ++ ret = kstrtouint(opts_str, 2, &opts_int); ++ if (ret) ++ return ret; ++ ++ if (((opts_str[0] != '0') && (opts_str[0] != '1')) || ++ ((opts_str[1] != '0') && (opts_str[1] != '1')) || ++ ((opts_str[2] != '0') && (opts_str[2] != '1'))) { ++ SYNHW_PRINT("Invalid input options: First 3 digits can only be 1 or 0\n"); ++ return -1; ++ } ++ ++ if (opts_int & 1) ++ ps_exists = 1; ++ else ++ ps_exists = 0; ++ ++ if (opts_int & 2) ++ req_sec_strength = 256; ++ else ++ req_sec_strength = 128; ++ ++ if (opts_int & 4) ++ pred_resist = 1; ++ else ++ pred_resist = 0; ++ ++ /* check input option length */ ++ if (!ps_exists) { ++ if (count != 3) { ++ SYNHW_PRINT("Invalid input options: If personilization string does not exist, options has to be 3 char.\n"); ++ return -1; ++ } ++ } else { ++ if (req_sec_strength == 128) { ++ if (count != 64 + 4) { // +4 for options and "-" ++ SYNHW_PRINT("Invalid input options: If personilization string exists and security strength is 128-bit, options has to be 68 char (not %zu char).\n", ++ count); ++ return -1; ++ } ++ } else if (req_sec_strength == 256) { ++ if (count != ++ 96 + 4) { // +4 for options and "-", +1 because of the termination char that count includes ++ SYNHW_PRINT("Invalid input options: If personilization string exists and security strength is 256-bit, options has to be 100 char (not %zu char).\n", ++ count); ++ return -1; ++ } ++ } else { ++ SYNHW_PRINT("Invalid input options\n"); ++ return -1; ++ } ++ } ++ ++ /* Personilization string */ ++ for (i = 0; i < 12; i++) ++ ps[i] = 0; ++ ++ if (req_sec_strength == 128) ++ ps_length = 64; ++ else if (req_sec_strength == 256) ++ ps_length = 96; ++ else ++ SYNHW_PRINT("Invalid security strength\n"); ++ ++ if (ps_exists) { ++ opts_str[1] = 0; ++ memcpy(opts_str, buf + 3, 1); ++ ++ if (opts_str[0] == '-') { ++ opts_str[8] = 0; ++ for (i = 0; i < ps_length / 8; i++) { ++ memcpy(opts_str, buf + 4 + i * 8, 8); ++ ret = kstrtouint(opts_str, 16, ++ ps + (ps_length / 8 - 1) - i); ++ if (ret) ++ return ret; ++ } ++ } else { ++ SYNHW_PRINT("4th character of input has to be \"-\" when personilization string exists\n"); ++ } ++ ++ ret = nisttrng_instantiate(&priv->nisttrng, ++ req_sec_strength, pred_resist, ++ ps); ++ if (ret) ++ return -1; ++ ++ } else { ++ ret = nisttrng_instantiate(&priv->nisttrng, ++ req_sec_strength, pred_resist, ++ NULL); ++ if (ret) ++ return -1; ++ } ++ ++ return count; ++} ++ ++static ssize_t uninstantiate_store(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ ++ nisttrng_uninstantiate(&priv->nisttrng); ++ ++ return count; ++} ++ ++static ssize_t reseed_store(struct device *dev, struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char opts_str[100]; ++ unsigned int opts_int; ++ int pred_resist = 1; ++ bool addin_exists = 0; ++ u32 addin[12]; ++ unsigned int addin_length; ++ int i; ++ int ret; ++ ++ /* First 2 digits: ++ * they have to be 0 or 1 ++ * 1-0 --> 1: predictoin resistance, 0: additional input string existence ++ */ ++ opts_str[2] = 0; ++ memcpy(opts_str, buf, 2); ++ ret = kstrtouint(opts_str, 2, &opts_int); ++ if (ret) ++ return ret; ++ ++ if (((opts_str[0] != '0') && (opts_str[0] != '1')) || ++ ((opts_str[1] != '0') && (opts_str[1] != '1'))) { ++ SYNHW_PRINT("Invalid input options: First 2 digits can only be 1 or 0\n"); ++ return -1; ++ } ++ ++ if (opts_int & 1) ++ addin_exists = 1; ++ else ++ addin_exists = 0; ++ ++ if (opts_int & 2) ++ pred_resist = 1; ++ else ++ pred_resist = 0; ++ ++ /* check input option length */ ++ if (!addin_exists) { ++ if (count != 2) { ++ SYNHW_PRINT("Invalid input options: If additional input does not exist, options has to be 2 char.\n"); ++ return -1; ++ } ++ } else { ++ if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES128) { ++ if (count != 64 + 3) { // +3 for options and "-" ++ SYNHW_PRINT("Invalid input options: If additional input exists and security strength is 128-bit, options has to be 67 char.\n"); ++ return -1; ++ } ++ } else if (priv->nisttrng.status.sec_strength == ++ SEC_STRNT_AES256) { ++ if (count != 96 + 3) { // +3 for options and "-" ++ SYNHW_PRINT("Invalid input options: If additional input exists and security strength is 256-bit, options has to be 99 char.\n"); ++ return -1; ++ } ++ } else { ++ SYNHW_PRINT("Invalid input options\n"); ++ return -1; ++ } ++ } ++ ++ /* Additional input */ ++ for (i = 0; i < 12; i++) ++ addin[i] = 0; ++ ++ if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES128) ++ addin_length = 64; ++ else if (priv->nisttrng.status.sec_strength == SEC_STRNT_AES256) ++ addin_length = 96; ++ else ++ SYNHW_PRINT("Invalid security strength\n"); ++ ++ if (addin_exists) { ++ opts_str[1] = 0; ++ memcpy(opts_str, buf + 2, 1); ++ ++ if (opts_str[0] == '-') { ++ opts_str[8] = 0; ++ for (i = 0; i < addin_length / 8; i++) { ++ memcpy(opts_str, buf + 3 + i * 8, 8); ++ ret = kstrtouint(opts_str, 16, addin + (addin_length / 8 - 1) - i); ++ if (ret) ++ return ret; ++ } ++ } else { ++ SYNHW_PRINT("3rd character of input has to be \"-\" when additional input exists\n"); ++ } ++ ++ ret = nisttrng_reseed(&priv->nisttrng, pred_resist, ++ addin); ++ if (ret) ++ return -1; ++ ++ } else { ++ ret = nisttrng_reseed(&priv->nisttrng, pred_resist, ++ NULL); ++ if (ret) ++ return -1; ++ } ++ ++ return count; ++} ++ ++static ssize_t generate_store(struct device *dev, ++ struct device_attribute *devattr, const char *buf, ++ size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char opts_str[101]; ++ unsigned int opts_int; ++ int req_sec_strength = 128; ++ int pred_resist = 1; ++ bool addin_exists = 0; ++ unsigned char out[num_gen_bytes]; ++ u32 addin[12]; ++ unsigned int addin_length; ++ int i; ++ int ret; ++ ++ /* First 3 digits: ++ * they have to be 0 or 1 ++ * 2-1-0 --> 2: predictoin resistance, 1: security strength, 0: additional input string existence ++ */ ++ opts_str[3] = 0; ++ memcpy(opts_str, buf, 3); ++ ret = kstrtouint(opts_str, 2, &opts_int); ++ if (ret) ++ return ret; ++ ++ if (((opts_str[0] != '0') && (opts_str[0] != '1')) || ++ ((opts_str[1] != '0') && (opts_str[1] != '1')) || ++ ((opts_str[2] != '0') && (opts_str[2] != '1'))) { ++ SYNHW_PRINT("Invalid input options: First 3 digits can only be 1 or 0\n"); ++ return -1; ++ } ++ ++ if (opts_int & 1) ++ addin_exists = 1; ++ else ++ addin_exists = 0; ++ ++ if (opts_int & 2) ++ req_sec_strength = 256; ++ else ++ req_sec_strength = 128; ++ ++ if (opts_int & 4) ++ pred_resist = 1; ++ else ++ pred_resist = 0; ++ ++ /* check input option length */ ++ if (!addin_exists) { ++ if (count != 3) { ++ SYNHW_PRINT("Invalid input options: If additional input does not exist, options has to be 3 char.\n"); ++ return -1; ++ } ++ } else { ++ if (req_sec_strength == 128) { ++ if (count != 64 + 4) { // +4 for options and "-" ++ SYNHW_PRINT("Invalid input options: If additional input exists and security strength is 128-bit, options has to be 68 char.\n"); ++ return -1; ++ } ++ } else if (req_sec_strength == 256) { ++ if (count != 96 + 4) { // +4 for options and "-" ++ SYNHW_PRINT("Invalid input options: If additional input exists and security strength is 256-bit, options has to be 100 char.\n"); ++ return -1; ++ } ++ } else { ++ SYNHW_PRINT("Invalid input options\n"); ++ return -1; ++ } ++ } ++ ++ /* Additional input */ ++ for (i = 0; i < 12; i++) ++ addin[i] = 0; ++ ++ if (req_sec_strength == 128) ++ addin_length = 64; ++ else if (req_sec_strength == 256) ++ addin_length = 96; ++ else ++ SYNHW_PRINT("Invalid security strength\n"); ++ ++ if (addin_exists) { ++ opts_str[1] = 0; ++ memcpy(opts_str, buf + 3, 1); ++ ++ if (opts_str[0] == '-') { ++ opts_str[8] = 0; ++ for (i = 0; i < addin_length / 8; i++) { ++ memcpy(opts_str, buf + 4 + i * 8, 8); ++ ret = kstrtouint(opts_str, 16, addin + (addin_length / 8 - 1) - i); ++ if (ret) ++ return ret; ++ } ++ } else { ++ SYNHW_PRINT("4th character of input has to be \"-\" when additional input exists\n"); ++ } ++ ++ ret = nisttrng_generate(&priv->nisttrng, (u32 *)out, ++ num_gen_bytes, req_sec_strength, ++ pred_resist, addin); ++ if (ret) ++ return -1; ++ ++ } else { ++ ret = nisttrng_generate(&priv->nisttrng, (u32 *)out, ++ num_gen_bytes, req_sec_strength, ++ pred_resist, NULL); ++ if (ret) ++ return -1; ++ } ++ ++ /* store the result */ ++ memcpy(priv->rand_out, out, sizeof(out)); ++ ++ return count; ++} ++ ++static ssize_t generate_pub_vtrng_store(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ char opts_str[2]; ++ unsigned int opts_int; ++ unsigned char out[num_gen_bytes]; ++ int ret; ++ ++ opts_str[1] = 0; ++ memcpy(opts_str, buf, 1); ++ ret = kstrtouint(opts_str, 16, &opts_int); ++ if (ret) ++ return ret; ++ ++ SYNHW_PRINT("%s %d %d %d %d\n", __func__, opts_str[0], ++ priv->nisttrng.config.edu_build_cfg0.public_vtrng_channels, ++ opts_str[1], opts_int); ++ ++ ret = nisttrng_generate_public_vtrng(&priv->nisttrng, ++ (u32 *)out, ++ num_gen_bytes, opts_int); ++ if (ret) ++ return -1; ++ ++ memcpy(priv->rand_out, out, sizeof(out)); ++ ++ return count; ++} ++ ++/* rand_out_show displays last generated random number (num_gen_bytes number of bytes), not just the last block. */ ++static ssize_t rand_out_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ unsigned int i, j; ++ unsigned long rand; ++ bool all_zero = true; ++ ++ /* If all bits of the rand_reg register are 0, display 0 */ ++ for (i = 0; i < 4; i++) { ++ rand = pdu_io_read32(priv->nisttrng.base + NIST_TRNG_REG_RAND0 + ++ (3 - i)); ++ if (rand != 0) { ++ all_zero = false; ++ break; ++ } ++ } ++ ++ if (all_zero) { ++ sprintf(buf + 2 * i, "%02x", 0); ++ } else { ++ for (i = 0; i < (num_gen_bytes / 16); i++) { ++ for (j = 0; j < 16; j++) { ++ sprintf(buf + 2 * (i * 16 + j), "%02x", ++ priv->rand_out[(i + 1) * 16 - 1 - j]); ++ } ++ } ++ j = 0; ++ while (i * 16 + j < num_gen_bytes) { ++ sprintf(buf + 2 * (i * 16 + j), "%02x", ++ priv->rand_out[num_gen_bytes - 1 - j]); ++ j++; ++ } ++ } ++ ++ strcat(buf, "\n"); ++ return strlen(buf); ++} ++ ++/* rand_out_vtrng_show displays last generated random number (num_gen_bytes number of bytes), not just the last block. */ ++static ssize_t rand_out_vtrng_show(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ unsigned int i, j; ++ ++ /* If all bits of the rand_reg register are 0, display 0 */ ++ ++ for (i = 0; i < (num_gen_bytes / 16); i++) { ++ for (j = 0; j < 16; j++) { ++ sprintf(buf + 2 * (i * 16 + j), "%02x", ++ priv->rand_out[(i + 1) * 16 - 1 - j]); ++ } ++ } ++ ++ j = 0; ++ while (i * 16 + j < num_gen_bytes) { ++ sprintf(buf + 2 * (i * 16 + j), "%02x", ++ priv->rand_out[num_gen_bytes - 1 - j]); ++ j++; ++ } ++ ++ strcat(buf, "\n"); ++ return strlen(buf); ++} ++ ++static ssize_t kat_store(struct device *dev, struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ int ret; ++ ++ if (sysfs_streq(buf, "full")) { ++ ret = nisttrng_full_kat(&priv->nisttrng); ++ if (ret) ++ return -1; ++ ++ } else if (sysfs_streq(buf, "00")) { ++ ret = nisttrng_kat(&priv->nisttrng, 0, 0); ++ if (ret) ++ return -1; ++ ++ } else if (sysfs_streq(buf, "01")) { ++ ret = nisttrng_kat(&priv->nisttrng, 0, 1); ++ if (ret) ++ return -1; ++ ++ } else if (sysfs_streq(buf, "10")) { ++ ret = nisttrng_kat(&priv->nisttrng, 1, 0); ++ if (ret) ++ return -1; ++ ++ } else if (sysfs_streq(buf, "11")) { ++ ret = nisttrng_kat(&priv->nisttrng, 1, 1); ++ if (ret) ++ return -1; ++ ++ } else { ++ ret = nisttrng_full_kat(&priv->nisttrng); ++ if (ret) ++ return -1; ++ } ++ ++ return count; ++} ++ ++static void str_to_384_bit(char *buf, u32 *out) ++{ ++ char foo[9]; ++ int i; ++ int ret; ++ ++ foo[8] = 0; ++ for (i = 0; i < 12; i++) { ++ memcpy(foo, buf + i * 8, 8); ++ ret = kstrtouint(foo, 16, out + 11 - i); ++ } ++} ++ ++/* This attribute is only for test purpuses */ ++static ssize_t test_attr_store(struct device *dev, ++ struct device_attribute *devattr, const char *buf, ++ size_t count) ++{ ++ struct synopsys_nisttrng_driver *priv = dev_get_drvdata(dev); ++ int i; ++ int err; ++ u32 addin[12]; ++ u32 ps[12]; ++ char *out; ++ ++ char buf_seed1[96] = ++ "c54805274bde00aa5289e0513579019707666d2fa7a1c8908865891c87c0c652335a4d3cc415bc30742b164647f8820f"; ++ char buf_ps1[96] = ++ "d63fb5afa2101fa4b8a6c3b89d9c250ac728fc1ddad0e7585b5d54728ed20c2f940e89155596e3b963635b6d6088164b"; ++ char buf_addin1[96] = ++ "744bfae3c23a5cc9a3b373b6c50795068d35eb8a339746ac810d16f864e880061082edf9d2687c211960aa83400f85f9"; ++ char buf_seed2[96] = ++ "b2ad31d1f20dcf30dd526ec9156c07f270216bdb59197325bab180675929888ab699c54fb21819b7d921d6346bff2f7f"; ++ char buf_addin2[96] = ++ "ad55c682962aa4fe9ebc227c9402e79b0aa7874844d33eaee7e2d15baf81d9d33936e4d93f28ad109657b512aee115a5"; ++ char buf_seed3[96] = ++ "eca449048d26fd38f8ca435237dce66eadec7069ee5dd0b70084b819a711c0820a7556bbd0ae20f06e5169278b593b71"; ++ u32 tmp[12]; ++ ++ for (i = 0; i < 12; i++) ++ addin[i] = i; ++ ++ for (i = 0; i < 12; i++) ++ ps[i] = i + 100; ++ ++ /* SDK doc example - Prediction Resistance not available, no Reseed */ ++ err = nisttrng_uninstantiate(&priv->nisttrng); ++ if (err && err != CRYPTO_NOT_INSTANTIATED) ++ return -1; ++ ++ if (nisttrng_instantiate(&priv->nisttrng, 128, 0, ps) < 0) ++ return -1; ++ ++ out = kmalloc(10, GFP_KERNEL); ++ if (nisttrng_generate(&priv->nisttrng, out, 10, 128, 0, addin) < 0) ++ return -1; ++ ++ DEBUG("----- Generate 10 bytes\n"); ++ for (i = 0; i < 10; i++) ++ DEBUG("%02x", out[i]); ++ ++ DEBUG("\n"); ++ kfree(out); ++ ++ out = kmalloc(512, GFP_KERNEL); ++ if (nisttrng_generate(&priv->nisttrng, out, 512, 128, 0, addin) < 0) ++ return -1; ++ ++ DEBUG("----- Generate 512 bytes\n"); ++ for (i = 0; i < 512; i++) ++ DEBUG("%02x", out[i]); ++ ++ DEBUG("\n"); ++ kfree(out); ++ ++ out = kmalloc(41, GFP_KERNEL); ++ if (nisttrng_generate(&priv->nisttrng, out, 41, 128, 0, addin) < 0) ++ return -1; ++ ++ DEBUG("----- Generate 41 bytes\n"); ++ for (i = 0; i < 41; i++) ++ DEBUG("%02x", out[i]); ++ ++ DEBUG("\n"); ++ kfree(out); ++ ++ err = nisttrng_uninstantiate(&priv->nisttrng); ++ if (err < 0 && err != CRYPTO_NOT_INSTANTIATED) ++ return -1; ++ ++ /* SDK doc example - DRBG Validation */ ++ err = nisttrng_uninstantiate(&priv->nisttrng); ++ if (err && err != CRYPTO_NOT_INSTANTIATED) ++ return -1; ++ ++ if (nisttrng_set_nonce_mode(&priv->nisttrng, 1) < 0) ++ return -1; ++ ++ out = kmalloc(64, GFP_KERNEL); ++ str_to_384_bit(buf_seed1, tmp); ++ if (nisttrng_get_entropy_input(&priv->nisttrng, tmp, 0) < 0) ++ return -1; ++ ++ str_to_384_bit(buf_ps1, tmp); ++ if (nisttrng_instantiate(&priv->nisttrng, 256, 1, tmp) < 0) ++ return -1; ++ ++ str_to_384_bit(buf_seed2, tmp); ++ if (nisttrng_get_entropy_input(&priv->nisttrng, tmp, 0) < 0) ++ return -1; ++ ++ str_to_384_bit(buf_addin1, tmp); ++ if (nisttrng_generate(&priv->nisttrng, out, 64, 256, 1, tmp) < 0) ++ return -1; ++ ++ str_to_384_bit(buf_seed3, tmp); ++ if (nisttrng_get_entropy_input(&priv->nisttrng, tmp, 0) < 0) ++ return -1; ++ ++ str_to_384_bit(buf_addin2, tmp); ++ if (nisttrng_generate(&priv->nisttrng, out, 64, 256, 1, tmp) < 0) ++ return -1; ++ ++ memcpy(priv->rand_out, out, 64); ++ ++ return count; ++} ++ ++static DEVICE_ATTR_RO(ckr); ++static DEVICE_ATTR_RO(features); ++static DEVICE_ATTR_RW(secure); ++static DEVICE_ATTR_RW(nonce); ++static DEVICE_ATTR_RW(sec_strength); ++ ++static DEVICE_ATTR_RW(mode_reg); ++static DEVICE_ATTR_RW(smode_reg); ++static DEVICE_ATTR_RW(alarm_reg); ++static DEVICE_ATTR_RO(rand_reg); ++static DEVICE_ATTR_RO(rand_out); ++static DEVICE_ATTR_RO(rand_out_vtrng); ++static DEVICE_ATTR_RW(seed_reg); ++static DEVICE_ATTR_RW(npa_data_reg); ++static DEVICE_ATTR_RW(ctrl_reg); ++static DEVICE_ATTR_RW(istat_reg); ++static DEVICE_ATTR_RO(stat_reg); ++static DEVICE_ATTR_RW(rnc_reg); ++static DEVICE_ATTR_RW(rbc_reg); ++ ++static DEVICE_ATTR_RW(ia_wdata_reg); ++static DEVICE_ATTR_RO(ia_rdata_reg); ++static DEVICE_ATTR_RW(ia_addr_reg); ++static DEVICE_ATTR_RW(ia_cmd_reg); ++static DEVICE_ATTR_RO(hw_state); ++ ++static DEVICE_ATTR_RO(collect_ent); ++static DEVICE_ATTR_RO(collect_ent_nsout); ++static DEVICE_ATTR_WO(nonce_seed_with_df); ++static DEVICE_ATTR_WO(nonce_seed_direct); ++static DEVICE_ATTR_WO(instantiate); ++static DEVICE_ATTR_WO(uninstantiate); ++static DEVICE_ATTR_WO(reseed); ++static DEVICE_ATTR_WO(generate); ++static DEVICE_ATTR_WO(generate_pub_vtrng); ++static DEVICE_ATTR_WO(kat); ++ ++static DEVICE_ATTR_RW(max_bits_per_req); ++static DEVICE_ATTR_RW(max_req_per_seed); ++ ++static DEVICE_ATTR_WO(test_attr); ++ ++static const struct attribute_group nisttrng_attr_group = { ++ .attrs = ++ (struct attribute *[]){ ++ &dev_attr_ckr.attr, ++ //&dev_attr_stepping.attr, ++ &dev_attr_features.attr, &dev_attr_secure.attr, ++ &dev_attr_nonce.attr, &dev_attr_sec_strength.attr, ++ ++ &dev_attr_mode_reg.attr, &dev_attr_smode_reg.attr, ++ &dev_attr_alarm_reg.attr, &dev_attr_rand_reg.attr, ++ &dev_attr_rand_out.attr, &dev_attr_rand_out_vtrng.attr, ++ &dev_attr_seed_reg.attr, &dev_attr_npa_data_reg.attr, ++ &dev_attr_ctrl_reg.attr, &dev_attr_istat_reg.attr, ++ &dev_attr_stat_reg.attr, &dev_attr_rnc_reg.attr, ++ &dev_attr_rbc_reg.attr, ++ ++ &dev_attr_ia_wdata_reg.attr, ++ &dev_attr_ia_rdata_reg.attr, &dev_attr_ia_addr_reg.attr, ++ &dev_attr_ia_cmd_reg.attr, &dev_attr_hw_state.attr, ++ ++ &dev_attr_collect_ent.attr, ++ &dev_attr_collect_ent_nsout.attr, ++ &dev_attr_nonce_seed_with_df.attr, ++ &dev_attr_nonce_seed_direct.attr, ++ &dev_attr_instantiate.attr, ++ &dev_attr_uninstantiate.attr, &dev_attr_reseed.attr, ++ &dev_attr_generate.attr, ++ &dev_attr_generate_pub_vtrng.attr, &dev_attr_kat.attr, ++ ++ &dev_attr_max_bits_per_req.attr, ++ &dev_attr_max_req_per_seed.attr, ++ ++ &dev_attr_test_attr.attr, NULL }, ++}; ++ ++static int nisttrng_self_test(struct nist_trng_state *nist_trng) ++{ ++ u32 seed[16], out[4], x, y; ++ ++ static const u32 exp128[10][4] = { ++ { 0x5db79bb2, 0xc3a0df1e, 0x099482b6, ++ 0xc319981e }, // The 1st generated output ++ { 0xb344d301, 0xdbd97ca0, 0x6e66e668, ++ 0x0bcd4625 }, // The 2nd generate output ++ { 0xec553f18, 0xa0e5c3cb, 0x752c03c2, ++ 0x5e7b04f7 }, // The 3rd generate output ++ { 0xcfe23e6e, 0x5302edc2, 0xdbf7b05b, ++ 0x2c817c0f }, // The 4th generate output ++ { 0xbd5a8726, 0x028c43d0, 0xb77ac4e3, ++ 0x0844ba2c }, // The 5th generate output ++ { 0xa63b4c0e, 0x8d11d0ba, 0x08b5a10f, ++ 0xab731aff }, // The 6th generate output ++ { 0xb7b56a2f, 0x1d84d1f0, 0xe48d1a0a, ++ 0x43a010a6 }, // The 7th generate output ++ { 0xcf66439d, 0xc937451d, 0x75c34d20, ++ 0x21a21398 }, // The 8th generate output ++ { 0xcb6f0a57, 0x5ff34705, 0x08838e49, ++ 0x21137614 }, // The 9th generate output ++ { 0x61c48b24, 0x25c18d29, 0xc6005e4e, ++ 0xae3b0389 }, // The 10th generate output ++ }; ++ ++ static const u32 exp256[10][4] = { ++ { 0x1f1a1441, 0xa0865ece, 0x9ff8d5b9, ++ 0x3f78ace6 }, // The 1st generated output ++ { 0xf8190a86, 0x6d6ded2a, 0xc4d0e9bf, ++ 0x24dab55c }, // The 2nd generate output ++ { 0xd3948b74, 0x3dfea516, 0x9c3b86a2, ++ 0xeb184b41 }, // The 3rd generate output ++ { 0x2eb82ab6, 0x2aceefda, 0xc0cf6a5f, ++ 0xa45cb333 }, // The 4th generate output ++ { 0xa49b1c7b, 0x5b51bac7, 0x7586770b, ++ 0x8cb2c392 }, // The 5th generate output ++ { 0x3f3ba09d, 0xa2c9ad29, 0x9687fb8f, ++ 0xa5ae3fd5 }, // The 6th generate output ++ { 0x11dd1076, 0xe37e86cb, 0xced0220a, ++ 0x00448c4f }, // The 7th generate output ++ { 0x955a5e52, 0x84ee38b1, 0xb3271e5f, ++ 0x097751e3 }, // The 8th generate output ++ { 0x5cd73ba8, 0xd8a36a1e, 0xa8a2d7c3, ++ 0xa96de048 }, // The 9th generate output ++ { 0xfb374c63, 0x827b85fa, 0x244e0c7a, ++ 0xa09afd39 }, // The 10th generate output ++ }; ++ ++ int ret, enable, rate, urun; ++ u32 tmp; ++ ++ for (x = 0; x < 16; x++) ++ seed[x] = 0x12345679 * (x + 1); ++ ++ DEBUG("Doing a self-test with security strength of 128\n"); ++ ret = nisttrng_uninstantiate(nist_trng); ++ if (ret && ret != CRYPTO_NOT_INSTANTIATED) ++ goto ERR; ++ ++ //if ((ret = nisttrng_set_secure_mode(nist_trng, 0))) { goto ERR; } ++ ret = nisttrng_set_nonce_mode(nist_trng, 1); ++ if (ret) ++ goto ERR; ++ ++ ret = nisttrng_set_sec_strength(nist_trng, 128); ++ if (ret) ++ goto ERR; ++ ++ ret = nisttrng_get_entropy_input(nist_trng, seed, 0); ++ if (ret) ++ goto ERR; ++ ++ ret = nisttrng_instantiate(nist_trng, 128, 0, NULL); ++ if (ret) ++ goto ERR; ++ ++ if (nist_trng->config.build_cfg0.edu_present) { ++ ret = nisttrng_wait_fifo_full(nist_trng); ++ if (ret) ++ goto ERR; ++ } ++ ++ ret = nisttrng_generate(nist_trng, out, 16, 128, 0, NULL); ++ if (ret) ++ goto ERR; ++ ++ if (nist_trng->config.features.extra_ps_present) { ++ DEBUG("skip KAT with extra_ps_present\n"); ++ } else { ++ DEBUG("nist_trng: AES-128 Self-test output: "); ++ for (x = 0; x < 4; x++) ++ DEBUG("0x%08lx ", (unsigned long)out[x]); ++ ++ if (nist_trng->config.build_cfg0.edu_present) { ++ if (nist_trng->config.edu_build_cfg0 ++ .esm_channel) { //if esm_channel is available the first random number goes to esm ++ for (x = 0; x < 4; x++) { ++ if (out[x] != exp128[1][x]) ++ ret = 1; ++ } ++ } ++ } else { ++ for (x = 0; x < 4; x++) { ++ if (out[x] != exp128[0][x]) ++ ret = 1; ++ } ++ } ++ ++ if (ret) { ++ SYNHW_PRINT("... FAILED comparison\n"); ++ ret = -1; ++ goto ERR; ++ } else { ++ DEBUG("... PASSED\n"); ++ } ++ } ++ ++ // if edu is available check all the pvtrng's ++ if (nist_trng->config.build_cfg0.edu_present) { ++ for (x = 0; ++ x < nist_trng->config.edu_build_cfg0.public_vtrng_channels; ++ x++) { ++ DEBUG("vtrng %d\n", x); ++ ret = nisttrng_generate_public_vtrng(nist_trng, out, 16, x); ++ if (ret) ++ goto ERR; ++ ++ for (y = 0; y < 4; y++) { ++ DEBUG("0x%08lx ", (unsigned long)out[y]); ++ if (out[y] != exp128[x + 2][y]) ++ ret = 1; ++ } ++ if (ret) { ++ SYNHW_PRINT("... FAILED comparison\n"); ++ ret = -1; ++ goto ERR; ++ } else { ++ DEBUG("... PASSED\n"); ++ } ++ } ++ } ++ // if edu is available empty the fifo before creating the new instance with strength of 256 ++ if (nist_trng->config.build_cfg0.edu_present) { ++ nisttrng_rnc(nist_trng, ++ NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_FINISH_TO_IDLE); ++ tmp = NIST_TRNG_REG_ISTAT_DONE; ++ //always clear the busy bit after disabling RNC ++ pdu_io_write32(nist_trng->base + NIST_TRNG_REG_ISTAT, tmp); ++ tmp = pdu_io_read32(nist_trng->base + NIST_TRNG_REG_ISTAT); ++ do { ++ ret = nisttrng_generate_public_vtrng(nist_trng, out, 16, 0); ++ if (ret) ++ goto ERR; ++ ++ tmp = pdu_io_read32(nist_trng->base + ++ NIST_TRNG_EDU_STAT); ++ ++ } while (!NIST_TRNG_EDU_STAT_FIFO_EMPTY(tmp)); ++ } ++ ++ if (nist_trng->config.features.drbg_arch == AES256) { ++ // test AES-256 mode ++ DEBUG("Doing a self-test with security strength of 256\n"); ++ ret = nisttrng_uninstantiate(nist_trng); ++ if (ret && ret != CRYPTO_NOT_INSTANTIATED) ++ goto ERR; ++ ++ ret = nisttrng_set_nonce_mode(nist_trng, 1); ++ if (ret) ++ goto ERR; ++ ++ ret = nisttrng_set_sec_strength(nist_trng, 256); ++ if (ret) ++ goto ERR; ++ ++ ret = nisttrng_get_entropy_input(nist_trng, seed, 0); ++ if (ret) ++ goto ERR; ++ ++ ret = nisttrng_instantiate(nist_trng, 256, 0, NULL); ++ if (ret) ++ goto ERR; ++ ++ ret = nisttrng_generate(nist_trng, out, 16, 256, 0, NULL); ++ if (ret) ++ goto ERR; ++ ++ if (nist_trng->config.features.extra_ps_present) { ++ DEBUG("skip KAT with extra_ps_present\n"); ++ } else { ++ DEBUG("nist_trng: AES-256 Self-test output: "); ++ for (x = 0; x < 4; x++) ++ DEBUG("0x%08lx ", (unsigned long)out[x]); ++ ++ for (x = 0; x < 4; x++) { ++ if (out[x] != exp256[0][x]) ++ ret = 1; ++ } ++ if (ret) { ++ SYNHW_PRINT("... FAILED comparison\n"); ++ ret = -1; ++ goto ERR; ++ } else { ++ DEBUG("... PASSED\n"); ++ } ++ } ++ } ++ ++ // if edu is available check all the pvtrng's ++ if (nist_trng->config.build_cfg0.edu_present) { ++ for (x = 0; ++ x < nist_trng->config.edu_build_cfg0.public_vtrng_channels; ++ x++) { ++ DEBUG("vtrng 256 %d\n", x); ++ ret = nisttrng_generate_public_vtrng(nist_trng, out, 16, x); ++ if (ret) ++ goto ERR; ++ ++ for (y = 0; y < 4; y++) { ++ DEBUG("0x%08lx ", (unsigned long)out[y]); ++ if (out[y] != exp256[x + 1][y]) ++ ret = 1; ++ } ++ if (ret) { ++ SYNHW_PRINT("... FAILED comparison\n"); ++ ret = -1; ++ goto ERR; ++ } else { ++ DEBUG("... PASSED\n"); ++ } ++ } ++ ++ //Test RBC channels ++ // enable RBC channels with rate of 2 and urun 1 ++ enable = 1; ++ rate = 2; ++ urun = 1; ++ for (x = 0; x < nist_trng->config.edu_build_cfg0.rbc_channels; ++ x++) { ++ ret = nisttrng_rbc(nist_trng, enable, x, rate, urun); ++ if (ret) ++ goto ERR; ++ ++ tmp = pdu_io_read32(nist_trng->base + ++ NIST_TRNG_EDU_RBC_CTRL); ++ ++ switch (x) { ++ case 0: ++ if (rate != NISTTRNG_EDU_RBC_CTRL_GET_CH_RATE(tmp, _NIST_TRNG_EDU_RBC_CTRL_CH0_RATE) || ++ urun != NISTTRNG_EDU_RBC_CTRL_GET_CH_URUN_BLANK(tmp, _NIST_TRNG_EDU_RBC_CTRL_CH0_URUN_BLANK)) { ++ goto ERR; ++ } ++ break; ++ case 1: ++ if (rate != NISTTRNG_EDU_RBC_CTRL_GET_CH_RATE(tmp, _NIST_TRNG_EDU_RBC_CTRL_CH1_RATE) || ++ urun != NISTTRNG_EDU_RBC_CTRL_GET_CH_URUN_BLANK(tmp, _NIST_TRNG_EDU_RBC_CTRL_CH1_URUN_BLANK)) { ++ goto ERR; ++ } ++ break; ++ case 2: ++ if (rate != NISTTRNG_EDU_RBC_CTRL_GET_CH_RATE(tmp, _NIST_TRNG_EDU_RBC_CTRL_CH2_RATE) || ++ urun != NISTTRNG_EDU_RBC_CTRL_GET_CH_URUN_BLANK(tmp, _NIST_TRNG_EDU_RBC_CTRL_CH2_URUN_BLANK)) { ++ goto ERR; ++ } ++ break; ++ default: ++ DEBUG("Incorrect rbc_num = %d\n", x); ++ goto ERR; ++ } ++ } ++ DEBUG("RBC test passed\n"); ++ } ++ ++ //IF RNCis not disable, disable it ++ if (pdu_io_read32(nist_trng->base + NIST_TRNG_EDU_RNC_CTRL) != ++ NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_FINISH_TO_IDLE) { ++ nisttrng_rnc(nist_trng, ++ NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_FINISH_TO_IDLE); ++ tmp = NIST_TRNG_REG_ISTAT_DONE; ++ //always clear the busy bit after disabling RNC ++ pdu_io_write32(nist_trng->base + NIST_TRNG_REG_ISTAT, tmp); ++ } ++ ++ /* back to the noise mode */ ++ ret = nisttrng_set_nonce_mode(nist_trng, 0); ++ if (ret) ++ goto ERR; ++ ++ ret = nisttrng_zeroize(nist_trng); ++ if (ret) ++ goto ERR; ++ERR: ++ return ret; ++} ++ ++static int nisttrng_driver_probe(struct platform_device *pdev) ++{ ++ struct synopsys_nisttrng_driver *data; ++ struct hwrng *hwrng_driver_info = 0; ++ struct resource *cfg, *irq; ++ u32 *base_addr; ++ int ret; ++ ++ // version ++ SYNHW_PRINT("DWC_TRNG_DriverSDK_%s\n", TRNG_VERSION); ++ ++ cfg = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ ++ if (!cfg || !irq) { ++ SYNHW_PRINT("no memory or IRQ resource\n"); ++ return -ENOMEM; ++ } ++ ++ DEBUG("=================================================================\n"); ++ DEBUG("nisttrng_probe: Device at %08lx(%08lx) of size %lu bytes\n", ++ (unsigned long)cfg->start, (unsigned long)cfg->end, ++ (unsigned long)resource_size(cfg)); ++ ++ data = devm_kzalloc(&pdev->dev, sizeof(struct synopsys_nisttrng_driver), ++ GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, data); ++ ++ base_addr = pdu_linux_map_regs(&pdev->dev, cfg); ++ if (IS_ERR(base_addr)) { ++ dev_err(&pdev->dev, "unable to remap io mem\n"); ++ return PTR_ERR(base_addr); ++ } ++ ++ ret = nisttrng_init(&data->nisttrng, (u32 *)base_addr); ++ if (ret) { ++ SYNHW_PRINT("NIST_TRNG init failed (%d)\n", ret); ++ devm_kfree(&pdev->dev, data); ++ return ret; ++ } ++ ++ /* if max_reads is not 0, change the max_req_per_seed according to max_reads */ ++ if (max_reads) { ++ ret = nisttrng_set_reminder_max_req_per_seed(&data->nisttrng, max_reads); ++ if (ret) { ++ SYNHW_PRINT("NIST_TRNG maximum request-per-seed setup failed (%d)\n", ++ ret); ++ devm_kfree(&pdev->dev, data); ++ return ret; ++ } ++ } ++ ++ // issue quick self test ++ ret = nisttrng_self_test(&data->nisttrng); ++ if (ret) { ++ devm_kfree(&pdev->dev, data); ++ return -ENOMEM; ++ } ++ ++ // ready the device for use ++ ret = nisttrng_instantiate(&data->nisttrng, ++ data->nisttrng.config.features.drbg_arch ? 256 : 128, 1, NULL); ++ if (ret) { ++ SYNHW_PRINT("NIST_TRNG instantiate failed (%d)\n", ret); ++ devm_kfree(&pdev->dev, data); ++ return -ENOMEM; ++ } ++ ++ // at this point the device should be ready for a call to gen_random ++ hwrng_driver_info = ++ devm_kzalloc(&pdev->dev, sizeof(struct hwrng), GFP_KERNEL); ++ if (!hwrng_driver_info) { ++ devm_kfree(&pdev->dev, data); ++ return -ENOMEM; ++ } ++ ++ hwrng_driver_info->name = devm_kzalloc(&pdev->dev, ++ sizeof(SYNOPSYS_HWRNG_DRIVER_NAME) + 1, GFP_KERNEL); ++ if (!hwrng_driver_info->name) { ++ devm_kfree(&pdev->dev, data); ++ devm_kfree(&pdev->dev, hwrng_driver_info); ++ return -ENOMEM; ++ } ++ ++ memset((void *)hwrng_driver_info->name, 0, ++ sizeof(SYNOPSYS_HWRNG_DRIVER_NAME) + 1); ++ strscpy((char *)hwrng_driver_info->name, SYNOPSYS_HWRNG_DRIVER_NAME, ++ sizeof(SYNOPSYS_HWRNG_DRIVER_NAME)); ++ ++ hwrng_driver_info->read = &nisttrng_hwrng_driver_read; ++ hwrng_driver_info->data_present = 0; ++ hwrng_driver_info->priv = (unsigned long)pdev; ++ hwrng_driver_info->quality = 1024; ++ ++ data->hwrng_drv = hwrng_driver_info; ++ ret = hwrng_register(hwrng_driver_info); ++ ++ if (ret) { ++ SYNHW_PRINT("unable to load HWRNG driver (error %d)\n", ret); ++ devm_kfree(&pdev->dev, (void *)hwrng_driver_info->name); ++ devm_kfree(&pdev->dev, hwrng_driver_info); ++ devm_kfree(&pdev->dev, data); ++ return ret; ++ } ++ ++ ret = sysfs_create_group(&pdev->dev.kobj, &nisttrng_attr_group); ++ if (ret < 0) { ++ SYNHW_PRINT("unable to initialize sysfs group (error %d)\n", ++ ret); ++ hwrng_unregister(hwrng_driver_info); ++ devm_kfree(&pdev->dev, (void *)hwrng_driver_info->name); ++ devm_kfree(&pdev->dev, hwrng_driver_info); ++ devm_kfree(&pdev->dev, data); ++ return ret; ++ } ++ SYNHW_PRINT("SYN NIST_TRNG registering HW_RANDOM\n"); ++ return 0; ++} ++ ++static void nisttrng_driver_remove(struct platform_device *pdev) ++{ ++ struct synopsys_nisttrng_driver *data = platform_get_drvdata(pdev); ++ struct hwrng *hwrng_driver_info = (struct hwrng *)data->hwrng_drv; ++ ++ SYNHW_PRINT("SYN NIST_TRNG unregistering from HW_RANDOM\n"); ++ hwrng_unregister(hwrng_driver_info); ++ sysfs_remove_group(&pdev->dev.kobj, &nisttrng_attr_group); ++ devm_kfree(&pdev->dev, (void *)hwrng_driver_info->name); ++ devm_kfree(&pdev->dev, hwrng_driver_info); ++ devm_kfree(&pdev->dev, data); ++} ++ ++static struct platform_driver s_nisttrng_platform_driver_info = { ++ .probe = nisttrng_driver_probe, ++ .remove = nisttrng_driver_remove, ++ .driver = { ++ .name = "nist_trng", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init nisttrng_platform_driver_start(void) ++{ ++ return platform_driver_register(&s_nisttrng_platform_driver_info); ++} ++ ++static void __exit nisttrng_platform_driver_end(void) ++{ ++ platform_driver_unregister(&s_nisttrng_platform_driver_info); ++} ++ ++module_init(nisttrng_platform_driver_start); ++module_exit(nisttrng_platform_driver_end); ++ ++module_param(max_reads, ulong, 0); ++MODULE_PARM_DESC(max_reads, "Max # of reads between reseeds (default is 128)"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Synopsys, Inc."); +diff --git a/drivers/char/hw_random/dwc/src/trng/trng/nist_trng.c b/drivers/char/hw_random/dwc/src/trng/trng/nist_trng.c +--- a/drivers/char/hw_random/dwc/src/trng/trng/nist_trng.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/trng/nist_trng.c 2026-04-08 18:03:46.630736014 +0000 +@@ -0,0 +1,956 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * This Synopsys software and associated documentation (hereinafter the ++ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. The ++ * Software IS NOT an item of Licensed Software or a Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Products ++ * with Synopsys or any supplement thereto. Synopsys is a registered trademark ++ * of Synopsys, Inc. Other names included in the SOFTWARE may be the ++ * trademarks of their respective owners. ++ * ++ * The contents of this file are dual-licensed; you may select either version ++ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license ++ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the ++ * SOFTWARE. The BSD License is copied below. ++ * ++ * BSD-3-Clause License: ++ * Copyright (c) 2012-2016 Synopsys, Inc. and/or its affiliates. ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions, and the following disclaimer, without ++ * modification. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * 3. The names of the above-listed copyright holders may not be used to ++ * endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "nisttrng_hw.h" ++#include "nisttrng.h" ++ ++/* Initialize the NIST_TRNG state structure */ ++int nisttrng_init(struct nist_trng_state *state, u32 *base) ++{ ++ int err; ++ u32 tmp; ++ ++ DEBUG(">> %s: initialize the NIST_TRNG\n", __func__); ++ ++ memset(state, 0, sizeof(*state)); ++ ++ state->base = base; ++ ++ /* make sure there is no alarm and the core is not busy */ ++ err = nisttrng_get_alarms(state); ++ if (err) ++ goto ERR; ++ ++ err = nisttrng_wait_on_busy(state); ++ if (err) ++ goto ERR; ++ ++ /* hardware features*/ ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_FEATURES); ++ ++ state->config.features.drbg_arch = NIST_TRNG_REG_FEATURES_AES_256(tmp); ++ state->config.features.extra_ps_present = ++ NIST_TRNG_REG_FEATURES_EXTRA_PS_PRESENT(tmp); ++ state->config.features.secure_rst_state = ++ NIST_TRNG_REG_FEATURES_SECURE_RST_STATE(tmp); ++ state->config.features.diag_level_basic_trng = ++ NIST_TRNG_REG_FEATURES_DIAG_LEVEL_BASIC_TRNG(tmp); ++ state->config.features.diag_level_stat_hlt = ++ NIST_TRNG_REG_FEATURES_DIAG_LEVEL_ST_HLT(tmp); ++ state->config.features.diag_level_ns = ++ NIST_TRNG_REG_FEATURES_DIAG_LEVEL_NS(tmp); ++ ++ /* corekit */ ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_COREKIT_REL); ++ state->config.corekit_rel.ext_enum = NIST_TRNG_REG_EXT_ENUM(tmp); ++ state->config.corekit_rel.ext_ver = NIST_TRNG_REG_EXT_VER(tmp); ++ state->config.corekit_rel.rel_num = NIST_TRNG_REG_REL_NUM(tmp); ++ ++ /* clear registers */ ++ pdu_io_write32(state->base + NIST_TRNG_REG_ALARM, 0xFFFFFFFF); ++ pdu_io_write32(state->base + NIST_TRNG_REG_ISTAT, 0xFFFFFFFF); ++ ++ /* setup the NIST_TRNG in secure mode, self seeding mode, with prediction resistance, maximum possible security strength */ ++ /* SMODE */ ++ tmp = 0; ++ tmp = NIST_TRNG_REG_SMODE_SET_SECURE_EN(tmp, 1); ++ tmp = NIST_TRNG_REG_SMODE_SET_NONCE(tmp, 0); ++ tmp = NIST_TRNG_REG_SMODE_SET_MAX_REJECTS(tmp, ++ NIST_TRNG_DFLT_MAX_REJECTS); ++ pdu_io_write32(state->base + NIST_TRNG_REG_SMODE, tmp); ++ state->status.secure_mode = 1; ++ state->status.nonce_mode = 0; ++ /* MODE */ ++ tmp = 0; ++ if (state->config.features.drbg_arch == AES256) { ++ tmp = NIST_TRNG_REG_MODE_SET_SEC_ALG(tmp, 1); ++ state->status.sec_strength = SEC_STRNT_AES256; ++ ++ } else if (state->config.features.drbg_arch == AES128) { ++ tmp = NIST_TRNG_REG_MODE_SET_SEC_ALG(tmp, 0); ++ state->status.sec_strength = SEC_STRNT_AES128; ++ ++ } else { ++ SYNHW_PRINT("Invalid DRBG architecture"); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ tmp = NIST_TRNG_REG_MODE_SET_PRED_RESIST(tmp, 1); ++ pdu_io_write32(state->base + NIST_TRNG_REG_MODE, 0); ++ state->status.pred_resist = 1; ++ /* rest of the status */ ++ state->status.alarm_code = 0; ++ state->status.pad_ps_addin = 0; ++ ++ /* reminders - set the counters to the standard's maximum values. An API is be provided to change those on demand.*/ ++ nisttrng_set_reminder_max_bits_per_req(state, ++ NIST_DFLT_MAX_BITS_PER_REQ); ++ nisttrng_set_reminder_max_req_per_seed(state, ++ NIST_DFLT_MAX_REQ_PER_SEED); ++ ++ /* display features */ ++ SYNHW_PRINT("NIST_TRNG: Hardware rel_num=0x%x, ext_ver=0x%x, ext_enum=0x%x\n", ++ state->config.corekit_rel.rel_num, ++ state->config.corekit_rel.ext_ver, ++ state->config.corekit_rel.ext_enum); ++ switch (state->config.features.drbg_arch) { ++ case AES128: ++ DEBUG("NIST_TRNG: DRBG Architecture=128-bit AES, Extra Personalization Existence=%u\n", ++ state->config.features.extra_ps_present); ++ break; ++ case AES256: ++ DEBUG("NIST_TRNG: DRBG Architecture=256-bit AES, Extra Personalization Existence=%u\n", ++ state->config.features.extra_ps_present); ++ break; ++ default: ++ SYNHW_PRINT("Invalid DRBG architecture"); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ DEBUG("initialization is done, going for a zeroize\n"); ++ ++ // BUILD_CFG0 ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_BUILD_CFG0); ++ state->config.build_cfg0.core_type = NIST_TRNG_REG_CFG0_CORE_TYPE(tmp); ++ state->config.build_cfg0.bg8 = NIST_TRNG_REG_CFG0_BG8(tmp); ++ state->config.build_cfg0.cdc_synch_depth = ++ NIST_TRNG_REG_CFG0_CDC_SYNCH_DEPTH(tmp); ++ state->config.build_cfg0.background_noise = ++ NIST_TRNG_REG_CFG0_BACGROUND_NOISE(tmp); ++ state->config.build_cfg0.edu_present = ++ NIST_TRNG_REG_CFG0_EDU_PRESENT(tmp); ++ state->config.build_cfg0.aes_datapath = ++ NIST_TRNG_REG_CFG0_AES_DATAPATH(tmp); ++ state->config.build_cfg0.aes_max_key_size = ++ NIST_TRNG_REG_CFG0_AES_MAX_KEY_SIZE(tmp); ++ state->config.build_cfg0.personilzation_str = ++ NIST_TRNG_REG_CFG0_PERSONILIZATION_STR(tmp); ++ DEBUG("NIST_TRNG: BUILD_CFG0 core_type=%u, bg8=%u, cdc_synch_depth=%u, background_noise=%u\n", ++ state->config.build_cfg0.core_type, state->config.build_cfg0.bg8, ++ state->config.build_cfg0.cdc_synch_depth, ++ state->config.build_cfg0.background_noise); ++ DEBUG("edu_present=%u, aes_datapath=%u, aes_max_key_size=%u, personilzation_str=%u\n", ++ state->config.build_cfg0.edu_present, ++ state->config.build_cfg0.aes_datapath, ++ state->config.build_cfg0.aes_max_key_size, ++ state->config.build_cfg0.personilzation_str); ++ ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_BUILD_CFG1); ++ DEBUG("NIST_TRNG: NIST_TRNG_REG_BUILD_CFG1=0x%x\n", tmp); ++ state->config.build_cfg1.num_raw_noise_blks = ++ NIST_TRNG_REG_CFG1_NUM_RAW_NOISE_BLKS(tmp); ++ state->config.build_cfg1.sticky_startup = ++ NIST_TRNG_REG_CFG1_STICKY_STARTUP(tmp); ++ state->config.build_cfg1.auto_correlation_test = ++ NIST_TRNG_REG_CFG1_AUTO_CORRELATION_TEST(tmp); ++ state->config.build_cfg1.mono_bit_test = ++ NIST_TRNG_REG_CFG1_MONO_BIT_TEST(tmp); ++ state->config.build_cfg1.run_test = NIST_TRNG_REG_CFG1_RUN_TEST(tmp); ++ state->config.build_cfg1.poker_test = ++ NIST_TRNG_REG_CFG1_POKER_TEST(tmp); ++ state->config.build_cfg1.raw_ht_adap_test = ++ NIST_TRNG_REG_CFG1_RAW_HT_ADAP_TEST(tmp); ++ state->config.build_cfg1.raw_ht_rep_test = ++ NIST_TRNG_REG_CFG1_RAW_HT_REP_TEST(tmp); ++ state->config.build_cfg1.ent_src_rep_smpl_size = ++ NIST_TRNG_REG_CFG1_ENT_SRC_REP_SMPL_SIZE(tmp); ++ state->config.build_cfg1.ent_src_rep_test = ++ NIST_TRNG_REG_CFG1_ENT_SRC_REP_TEST(tmp); ++ state->config.build_cfg1.ent_src_rep_min_entropy = ++ NIST_TRNG_REG_CFG1_ENT_SRC_REP_MIN_ENTROPY(tmp); ++ DEBUG("NIST_TRNG: BUILD_CFG1 num_raw_noise_blks=%u, sticky_startup=%u, auto_correlation_test=%u\n", ++ state->config.build_cfg1.num_raw_noise_blks, ++ state->config.build_cfg1.sticky_startup, ++ state->config.build_cfg1.auto_correlation_test); ++ DEBUG("mono_bit_test=%u, run_test=%u, poker_test=%u, raw_ht_adap_test=%u\n", ++ state->config.build_cfg1.mono_bit_test, ++ state->config.build_cfg1.run_test, ++ state->config.build_cfg1.poker_test, ++ state->config.build_cfg1.raw_ht_adap_test); ++ DEBUG("raw_ht_rep_test=%u, ent_src_rep_smpl_size=%u, ent_src_rep_test=%u, ent_src_rep_min_entropy=%u\n", ++ state->config.build_cfg1.raw_ht_rep_test, ++ state->config.build_cfg1.ent_src_rep_smpl_size, ++ state->config.build_cfg1.ent_src_rep_test, ++ state->config.build_cfg1.ent_src_rep_min_entropy); ++ ++ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_BUILD_CFG0); ++ state->config.edu_build_cfg0.rbc2_rate_width = ++ NIST_TRNG_REG_EDU_CFG0_RBC2_RATE_WIDTH(tmp); ++ state->config.edu_build_cfg0.rbc1_rate_width = ++ NIST_TRNG_REG_EDU_CFG0_RBC1_RATE_WIDTH(tmp); ++ state->config.edu_build_cfg0.rbc0_rate_width = ++ NIST_TRNG_REG_EDU_CFG0_RBC0_RATE_WIDTH(tmp); ++ state->config.edu_build_cfg0.public_vtrng_channels = ++ NIST_TRNG_REG_EDU_CFG0_PUBLIC_VTRNG_CHANNELS(tmp); ++ state->config.edu_build_cfg0.esm_channel = ++ NIST_TRNG_REG_EDU_CFG0_ESM_CHANNEL(tmp); ++ state->config.edu_build_cfg0.rbc_channels = ++ NIST_TRNG_REG_EDU_CFG0_RBC_CHANNELS(tmp); ++ state->config.edu_build_cfg0.fifo_depth = ++ NIST_TRNG_REG_EDU_CFG0_FIFO_DEPTH(tmp); ++ DEBUG("NIST_TRNG: EDU_BUILD_CFG0 rbc2_rate_width=%u, rbc1_rate_width=%u, rbc0_rate_width=%u\n", ++ state->config.edu_build_cfg0.rbc2_rate_width, ++ state->config.edu_build_cfg0.rbc1_rate_width, ++ state->config.edu_build_cfg0.rbc0_rate_width); ++ DEBUG("public_vtrng_channels=%u, esm_channel=%u, rbc_channels=%u, fifo_depth=%u\n", ++ state->config.edu_build_cfg0.public_vtrng_channels, ++ state->config.edu_build_cfg0.esm_channel, ++ state->config.edu_build_cfg0.rbc_channels, ++ state->config.edu_build_cfg0.fifo_depth); ++ ++ state->status.edu_vstat.seed_enum = ++ NIST_TRNG_REG_EDU_VSTAT_SEED_ENUM(tmp); ++ state->status.edu_vstat.rnc_enabled = ++ NIST_TRNG_REG_EDU_VSTAT_RNC_ENABLED(tmp); ++ ++ err = nisttrng_zeroize(state); ++ if (err) ++ goto ERR; ++ ++ err = CRYPTO_OK; ++ state->status.current_state = NIST_TRNG_STATE_INITIALIZE; ++ERR: ++ DEBUG("--- %s Return, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_init */ ++EXPORT_SYMBOL(nisttrng_init); ++ ++/* Instantiate the DRBG state */ ++int nisttrng_instantiate(struct nist_trng_state *state, int req_sec_strength, ++ int pred_resist, void *personal_str) ++{ ++ int err; ++ u32 tmp; ++ u32 zero_ps[12] = { 0 }; ++ int i = 0; ++ ++ DEBUG(">> %s: security strength = %u, pred_resist = %u, personilization string existence = %u\n", ++ __func__, req_sec_strength, pred_resist, (personal_str) ? 1 : 0); ++ ++ /* make sure there is no alarm and the core is not busy */ ++ err = nisttrng_get_alarms(state); ++ if (err) ++ goto ERR; ++ ++ err = nisttrng_wait_on_busy(state); ++ if (err) ++ goto ERR; ++ ++ /* If DRBG is already instantiated or if current state does not allow an instantiate, return error */ ++ if (DRBG_INSTANTIATED(state->status.current_state)) { ++ DEBUG("Initial check: DRBG state is already instantiated\n"); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ if (state->status.current_state != NIST_TRNG_STATE_INITIALIZE && ++ state->status.current_state != NIST_TRNG_STATE_UNINSTANTIATE) { ++ DEBUG("Cannot instantiate in the current state (%u)\n", ++ state->status.current_state); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* if hardware is not configured to accept extra personalization string, but personal_str is not NULL, return error */ ++ if (!state->config.features.extra_ps_present && personal_str) { ++ DEBUG("HW config does not allow extra PS\n"); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* Validate and set the security strength */ ++ err = nisttrng_set_sec_strength(state, req_sec_strength); ++ if (err) ++ goto ERR; ++ ++ /* get entropy - noise seeding. If the mode is nonce, get_entropy must be called by the user prior to the instantiate function */ ++ DEBUG("Seeding mode is: %s\n", ++ state->status.nonce_mode ? "Nonce" : "Noise"); ++ if (!state->status.nonce_mode) { /* noise seeding */ ++ err = nisttrng_get_entropy_input(state, NULL, 0); ++ if (err) ++ goto ERR; ++ } ++ ++ /* load the personilization string if hardware is configured to accept it */ ++ if (state->config.features.extra_ps_present) { ++ /* if HW is configured to accept personilizatoin string, it will use whatever is in the NPA_DATAx. So, if the string is NULL, just load 0. */ ++ if (!personal_str) ++ personal_str = &zero_ps[0]; ++ ++ err = nisttrng_load_ps_addin(state, personal_str); ++ if (err) ++ goto ERR; ++ } ++ ++ /* initiate the Create_State command and wait on done */ ++ DEBUG("Create the DRBG state\n"); ++ ++ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, ++ NIST_TRNG_REG_CTRL_CMD_CREATE_STATE); ++ err = nisttrng_wait_on_done(state); ++ if (err) ++ goto ERR; ++ ++ /* check STAT register to make sure DRBG is instantiated */ ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_STAT); ++ if (!NIST_TRNG_REG_STAT_GET_DRBG_STATE(tmp)) { ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* reset reminder and alarms counters */ ++ nisttrng_reset_counters(state); ++ ++ //if EDU is available enable RNC and disable prediction resistance , disable all RBC,s ++ //state->config.build_cfg0.edu_present = 0; ++ if (state->config.build_cfg0.edu_present) { ++ //disable prediction resistance ++ err = nisttrng_set_pred_resist(state, 0); ++ if (err) ++ goto ERR; ++ ++ //enable RNC ++ nisttrng_rnc(state, NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_ENABLE); ++ // disable all RBC,s ++ ++ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_RBC_CTRL); ++ for (i = 0; i < state->config.edu_build_cfg0.rbc_channels; ++ i++) { ++ err = nisttrng_rbc(state, 0, i, 0, ++ CHX_URUN_BLANK_AFTER_RESET); ++ if (err) ++ goto ERR; ++ } ++ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_RBC_CTRL); ++ ++ } else { ++ /* set the prediction resistance */ ++ err = nisttrng_set_pred_resist(state, pred_resist); ++ if (err) ++ goto ERR; ++ } ++ ++ err = CRYPTO_OK; ++ state->status.current_state = NIST_TRNG_STATE_INSTANTIATE; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_instantiate */ ++EXPORT_SYMBOL(nisttrng_instantiate); ++ ++/* Uninstantiate the DRBG state and zeroize */ ++int nisttrng_uninstantiate(struct nist_trng_state *state) ++{ ++ int err; ++ int err_tmp; ++ u32 tmp; ++ ++ DEBUG(">> %s: uninstantiate the DRBG and zeroize\n", __func__); ++ //printf(" nisttrng_uninstantiate: uninstantiate the DRBG and zeroize\n"); ++ err = CRYPTO_OK; ++ err_tmp = CRYPTO_OK; ++ ++ //disable RNC ++ if (state->config.build_cfg0.edu_present) { ++ if (state->status.edu_vstat.rnc_enabled) { ++ DEBUG("%s: disable RNC\n", __func__); ++ nisttrng_rnc(state, NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_FINISH_TO_IDLE); ++ //always clear the busy bit after disabling RNC ++ pdu_io_write32(state->base + NIST_TRNG_REG_ISTAT, tmp); ++ } ++ } ++ ++ /* if DRBG is instantiated, return CRYPTO_NOT_INSTANTIATED, but still do the zeroize */ ++ if (!DRBG_INSTANTIATED(state->status.current_state)) ++ err_tmp = CRYPTO_NOT_INSTANTIATED; ++ ++ /* zeroize */ ++ err = nisttrng_zeroize(state); ++ if (err) ++ goto ERR; ++ ++ if (err == CRYPTO_OK && err_tmp == CRYPTO_NOT_INSTANTIATED) ++ err = CRYPTO_NOT_INSTANTIATED; ++ ++ state->status.current_state = NIST_TRNG_STATE_UNINSTANTIATE; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_uninstantiate */ ++EXPORT_SYMBOL(nisttrng_uninstantiate); ++ ++/* enable/disable specific rbc ++ * rbc_num = rbc channel num ++ * urun_blnk = underrun blanking duration for rbc channel ++ * rate = sets rate of serial entropy output for rbc channel ++ */ ++int nisttrng_rbc(struct nist_trng_state *state, int enable, int rbc_num, int rate, ++ int urun_blnk) ++{ ++ int err = 0; ++ u32 tmp_rbc = 0; ++ ++ tmp_rbc = pdu_io_read32(state->base + NIST_TRNG_EDU_RBC_CTRL); ++ ++ if (enable) { ++ if (rate > 15) { ++ DEBUG("Incorrect rate = %d\n", rate); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ if (urun_blnk > 3) { ++ DEBUG("Incorrect urun_blnk = %d\n", urun_blnk); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ } else { //disable ++ rate = NISTTRNG_EDU_RBC_CTRL_GET_CH_RATE_AFTER_RESET; ++ urun_blnk = NISTTRNG_EDU_RBC_CTRL_SET_CH_URUN_BLANK_AFTER_RESET; ++ } ++ ++ switch (rbc_num) { ++ case 0: ++ tmp_rbc = NISTTRNG_EDU_RBC_CTRL_SET_CH_RATE(rate, tmp_rbc, _NIST_TRNG_EDU_RBC_CTRL_CH0_RATE); ++ tmp_rbc = NISTTRNG_EDU_RBC_CTRL_SET_CH_URUN_BLANK(urun_blnk, tmp_rbc, ++ _NIST_TRNG_EDU_RBC_CTRL_CH0_URUN_BLANK); ++ ++ break; ++ case 1: ++ tmp_rbc = NISTTRNG_EDU_RBC_CTRL_SET_CH_RATE(rate, tmp_rbc, _NIST_TRNG_EDU_RBC_CTRL_CH1_RATE); ++ tmp_rbc = NISTTRNG_EDU_RBC_CTRL_SET_CH_URUN_BLANK(urun_blnk, tmp_rbc, ++ _NIST_TRNG_EDU_RBC_CTRL_CH1_URUN_BLANK); ++ ++ break; ++ case 2: ++ tmp_rbc = NISTTRNG_EDU_RBC_CTRL_SET_CH_RATE(rate, tmp_rbc, _NIST_TRNG_EDU_RBC_CTRL_CH2_RATE); ++ tmp_rbc = NISTTRNG_EDU_RBC_CTRL_SET_CH_URUN_BLANK(urun_blnk, tmp_rbc, ++ _NIST_TRNG_EDU_RBC_CTRL_CH2_URUN_BLANK); ++ break; ++ default: ++ DEBUG("Incorrect rbc_num = %d\n", rbc_num); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ pdu_io_write32(state->base + NIST_TRNG_EDU_RBC_CTRL, tmp_rbc); ++ ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} ++ ++/* Reseed */ ++int nisttrng_reseed(struct nist_trng_state *state, int pred_resist, void *addin_str) ++{ ++ int rnc_flag = 0; ++ int err; ++ ++ DEBUG(">> %s: pred_resist = %u, additional strign existence = %u\n", ++ __func__, pred_resist, (addin_str) ? 1 : 0); ++ ++ if (state->config.build_cfg0.edu_present) { ++ if (state->status.edu_vstat.rnc_enabled) { ++ // disable_rnc ++ err = nisttrng_rnc(state, NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_DISABLE_TO_HOLD); ++ if (err) ++ goto ERR; ++ ++ rnc_flag = 1; ++ } ++ } ++ ++ /* make sure there is no alarm and the core is not busy */ ++ err = nisttrng_get_alarms(state); ++ if (err) ++ goto ERR; ++ ++ err = nisttrng_wait_on_busy(state); ++ if (err) ++ goto ERR; ++ ++ /* if the DRBG is not instantiated return error */ ++ if (!DRBG_INSTANTIATED(state->status.current_state)) { ++ DEBUG("DRBG is not instantiated\n"); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* if pred_resist is set but, pred_resist that the DRBG is instantiated with is not 1, return error */ ++ err = nisttrng_set_pred_resist(state, pred_resist); ++ if (err) ++ goto ERR; ++ ++ /* get entropy - noise seeding. If the mode is nonce, get_entropy must be called by the user prior to the instantiate function */ ++ if (!state->status.nonce_mode) { /* noise seeding */ ++ err = nisttrng_get_entropy_input(state, NULL, 0); ++ if (err) ++ goto ERR; ++ } ++ ++ /* if addin_str is not NULL, it means that the additionl input is available and has to be loaded */ ++ if (addin_str) { ++ /* set the ADDIN_PRESENT field of the MODE register to 1 */ ++ err = nisttrng_set_addin_present(state, 1); ++ if (err) ++ goto ERR; ++ ++ /* load the additional input */ ++ err = nisttrng_load_ps_addin(state, addin_str); ++ if (err) ++ goto ERR; ++ ++ } else { ++ /* set the ADDIN_PRESENT field of the MODE register to 0 */ ++ err = nisttrng_set_addin_present(state, 0); ++ if (err) ++ goto ERR; ++ } ++ ++ /* initiate the reseed and wait on done */ ++ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, ++ NIST_TRNG_REG_CTRL_CMD_RENEW_STATE); ++ err = nisttrng_wait_on_done(state); ++ if (err) ++ goto ERR; ++ ++ /* reset reminder and alarms counters */ ++ nisttrng_reset_counters(state); ++ ++ if (rnc_flag) { ++ // rnc_enable ++ err = nisttrng_rnc(state, NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_ENABLE); ++ if (err) ++ goto ERR; ++ } ++ ++ err = CRYPTO_OK; ++ state->status.current_state = NIST_TRNG_STATE_RESEED; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_reseed */ ++EXPORT_SYMBOL(nisttrng_reseed); ++ ++static int nisttrng_vtrng_wait_on_busy(struct nist_trng_state *state, int priv, int vtrng) ++{ ++ u32 tmp, t; ++ ++ t = NIST_TRNG_RETRY_MAX; ++ ++ if (priv) { //private vtrng ++ do { ++ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_VSTAT); ++ } while (NIST_TRNG_REG_EDU_VSTAT_BUSY(tmp) && --t); ++ ++ } else { //public vtrng ++ do { ++ tmp = pdu_io_read32(state->base + ++ NIST_TRNG_EDU_VTRNG_VSTAT0 + ++ 8 * vtrng); ++ } while (NIST_TRNG_REG_EDU_VSTAT_BUSY(tmp) && --t); ++ } ++ ++ if (t) ++ return CRYPTO_OK; ++ ++ SYNHW_PRINT("wait_on_: failed timeout: %08lx\n", ++ (unsigned long)tmp); ++ ++ return CRYPTO_TIMEOUT; ++} /* nisttrng_vtrng_wait_on_busy */ ++ ++int nisttrng_generate_public_vtrng(struct nist_trng_state *state, void *random_bits, ++ unsigned long req_num_bytes, int vtrng) ++{ ++ int err = 0; ++ u32 tmp; ++ unsigned int remained_bytes; ++ unsigned long req_num_blks; ++ int i, j; ++ ++ DEBUG(">> %s : requested number of bytes = %lu, vtrng num = %u\n", ++ __func__, req_num_bytes, vtrng); ++ ++ /* make sure random_bits is not NULL */ ++ if (!random_bits) { ++ DEBUG("random_bits pointer cannot be NULL\n"); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ if (vtrng > state->config.edu_build_cfg0.public_vtrng_channels) { ++ DEBUG("vtrng channel invalid (%u)\n", vtrng); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ if (state->status.edu_vstat.rnc_enabled == 0) { ++ DEBUG("rnc_disabled\n"); ++ } ++ ++ if (state->status.edu_vstat.seed_enum == 0) { ++ DEBUG("not seed_enum\n"); ++ } ++ ++ /* loop on generate to get the requested number of bits. Each generate gives NIST_TRNG_RAND_BLK_SIZE_BITS bits. */ ++ req_num_blks = ((req_num_bytes * 8) % NIST_TRNG_RAND_BLK_SIZE_BITS) ? ++ (((req_num_bytes * 8) / NIST_TRNG_RAND_BLK_SIZE_BITS) + 1) : ++ ((req_num_bytes * 8) / NIST_TRNG_RAND_BLK_SIZE_BITS); ++ ++ for (i = 0; i < req_num_blks; i++) { ++ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_VTRNG_VCTRL0 + ++ (vtrng * 8)); ++ tmp = NIST_TRNG_EDU_VTRNG_VCTRL_CMD_SET(tmp, NIST_TRNG_EDU_VTRNG_VCTRL_CMD_GET_RANDOM); ++ pdu_io_write32(state->base + NIST_TRNG_EDU_VTRNG_VCTRL0 + (vtrng * 8), ++ tmp); ++ ++ // check busy ++ err = nisttrng_vtrng_wait_on_busy(state, 0, vtrng); ++ if (err) ++ goto ERR; ++ ++ // check for error ++ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_VTRNG_VISTAT0 + ++ (vtrng * 8)); ++ if (NIST_TRNG_REG_EDU_VSTAT_ANY_RW1(tmp)) { ++ DEBUG("EDU_VSTAT_ANY_RW1 set 0x%x\n", tmp); ++ } ++ ++ // check that all valid ++ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_VTRNG_VSTAT0 + ++ 8 * vtrng); ++ if ((NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD0(tmp) == 0) || ++ (NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD1(tmp) == 0) || ++ (NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD2(tmp) == 0) || ++ (NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD3(tmp) == 0)) { ++ DEBUG("EDU_VSTAT_SLICE_VLD fail 0x%x\n", tmp); ++ } ++ ++ /* read the generated random number block and store */ ++ for (j = 0; j < (NIST_TRNG_RAND_BLK_SIZE_BITS / 32); j++) { ++ tmp = pdu_io_read32(state->base + ++ NIST_TRNG_EDU_VTRNG_VRAND0_0 + ++ (vtrng * 8) + j); ++ /* copy to random_bits byte-by-byte, until req_num_bytes are copied */ ++ remained_bytes = req_num_bytes - ++ (i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + ++ j * 4); ++ if (remained_bytes > 4) { ++ memcpy(random_bits + i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + ++ j * 4, &tmp, 4); ++ ++ /* decrement the bits counter and return error if generated more than the maximum*/ ++ state->counters.bits_per_req_left = ++ state->counters.bits_per_req_left - ++ 4 * 8; ++ if (state->counters.bits_per_req_left < 0) { ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ } else { ++ memcpy(random_bits + i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + ++ j * 4, &tmp, remained_bytes); ++ ++ /* decrement the bits counter and return error if generated more than the maximum*/ ++ state->counters.bits_per_req_left = ++ state->counters.bits_per_req_left - ++ remained_bytes * 8; ++ if (state->counters.bits_per_req_left < 0) { ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ break; ++ } ++ } ++ } ++ ++ err = CRYPTO_OK; ++ state->status.current_state = NIST_TRNG_STATE_GENERATE; ++ERR: ++ if (err) ++ random_bits = NULL; ++ ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} ++ ++static int nisttrng_generate_private_vtrng(struct nist_trng_state *state, void *random_bits, ++ unsigned long req_num_bytes) ++{ ++ int err; ++ u32 tmp; ++ unsigned int remained_bytes; ++ unsigned long req_num_blks; ++ int i, j; ++ ++ DEBUG(">> %s : requested number of bytes = %lu ", ++ __func__, req_num_bytes); ++ ++ /* requested number of bits has to be less that the programmed maximum */ ++ if ((req_num_bytes * 8) > state->counters.max_bits_per_req) { ++ SYNHW_PRINT("requested number of bits (%lu) is larger than the set maximum (%lu)\n", ++ (req_num_bytes * 8), state->counters.max_bits_per_req); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* make sure random_bits is not NULL */ ++ if (!random_bits) { ++ SYNHW_PRINT("random_bits pointer cannot be NULL\n"); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ if (state->status.edu_vstat.rnc_enabled == 0) { ++ DEBUG("rnc_disabled\n"); ++ } ++ ++ if (state->status.edu_vstat.seed_enum == 0) { ++ DEBUG("not seed_enum\n"); ++ } ++ ++ /* loop on generate to get the requested number of bits. Each generate gives NIST_TRNG_RAND_BLK_SIZE_BITS bits. */ ++ req_num_blks = ((req_num_bytes * 8) % NIST_TRNG_RAND_BLK_SIZE_BITS) ? ++ (((req_num_bytes * 8) / NIST_TRNG_RAND_BLK_SIZE_BITS) + 1) : ++ ((req_num_bytes * 8) / NIST_TRNG_RAND_BLK_SIZE_BITS); ++ ++ for (i = 0; i < req_num_blks; i++) { ++ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_VCTRL); ++ tmp = NIST_TRNG_EDU_VTRNG_VCTRL_CMD_SET(tmp, NIST_TRNG_EDU_VTRNG_VCTRL_CMD_GET_RANDOM); ++ pdu_io_write32(state->base + NIST_TRNG_EDU_VCTRL, tmp); ++ ++ // check busy ++ err = nisttrng_vtrng_wait_on_busy(state, 1, 0); ++ if (err) ++ goto ERR; ++ ++ // check for error ++ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_VISTAT); ++ if (NIST_TRNG_REG_EDU_VSTAT_ANY_RW1(tmp)) { ++ DEBUG("EDU_VSTAT_ANY_RW1 set 0x%x\n", tmp); ++ } ++ ++ //check that all valid ++ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_VSTAT); ++ if ((NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD0(tmp) == 0) || ++ (NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD1(tmp) == 0) || ++ (NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD2(tmp) == 0) || ++ (NIST_TRNG_REG_EDU_VSTAT_SLICE_VLD3(tmp) == 0)) { ++ DEBUG("EDU_VSTAT_SLICE_VLD fail 0x%x\n", tmp); ++ } ++ ++ /* read the generated random number block and store */ ++ for (j = 0; j < (NIST_TRNG_RAND_BLK_SIZE_BITS / 32); j++) { ++ tmp = pdu_io_read32(state->base + ++ NIST_TRNG_EDU_VRAND_0 + j); ++ /* copy to random_bits byte-by-byte, until req_num_bytes are copied */ ++ remained_bytes = req_num_bytes - ++ (i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + ++ j * 4); ++ if (remained_bytes > 4) { ++ memcpy(random_bits + i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + ++ j * 4, &tmp, 4); ++ ++ /* decrement the bits counter and return error if generated more than the maximum*/ ++ state->counters.bits_per_req_left = ++ state->counters.bits_per_req_left - ++ 4 * 8; ++ if (state->counters.bits_per_req_left < 0) { ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ } else { ++ memcpy(random_bits + i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + ++ j * 4, &tmp, remained_bytes); ++ ++ /* decrement the bits counter and return error if generated more than the maximum*/ ++ state->counters.bits_per_req_left = ++ state->counters.bits_per_req_left - ++ remained_bytes * 8; ++ if (state->counters.bits_per_req_left < 0) { ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ break; ++ } ++ } ++ } ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} ++ ++/* Generate */ ++int nisttrng_generate(struct nist_trng_state *state, void *random_bits, ++ unsigned long req_num_bytes, int req_sec_strength, ++ int pred_resist, void *addin_str) ++{ ++ int err; ++ int reseed_required; ++ ++ DEBUG(">> %s: requested number of bytes = %lu, security strength = %u, pred_resist = %u, additional string existence = %u\n", ++ __func__, req_num_bytes, req_sec_strength, pred_resist, ++ (addin_str) ? 1 : 0); ++ ++ /* make sure there is no alarm and the core is not busy */ ++ err = nisttrng_get_alarms(state); ++ if (err) ++ goto ERR; ++ ++ err = nisttrng_wait_on_busy(state); ++ if (err) ++ goto ERR; ++ ++ /* if the DRBG is not instantiated return error */ ++ if (!DRBG_INSTANTIATED(state->status.current_state)) { ++ SYNHW_PRINT("DRBG is not instantiated\n"); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* requested number of bits has to be less that the programmed maximum */ ++ if ((req_num_bytes * 8) > state->counters.max_bits_per_req) { ++ SYNHW_PRINT("requested number of bits (%lu) is larger than the set maximum (%lu)\n", ++ (req_num_bytes * 8), state->counters.max_bits_per_req); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* security strength has to be lower than what the DRBG is instantiated with. set_sec_strength function checks this. */ ++ err = nisttrng_set_sec_strength(state, req_sec_strength); ++ if (err) ++ goto ERR; ++ ++ /* set the prediction resistance - if pred_resist is set but, pred_resist that the DRBG is instantiated with is not 1, return error */ ++ err = nisttrng_set_pred_resist(state, pred_resist); ++ if (err) ++ goto ERR; ++ ++ /* make sure random_bits is not NULL */ ++ if (!random_bits) { ++ DEBUG("random_bits pointer cannot be NULL\n"); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* set the reseed required flag to 0. The loop is to check at the end whether a reseed is required at the end and jump back to reseed and generate if needed. This is the NIST mandated procedure */ ++ reseed_required = 0; ++ ++ if (!addin_str) { ++ /* set the ADDIN_PRESENT field of the MODE register to 1 */ ++ err = nisttrng_set_addin_present(state, 0); ++ if (err) ++ goto ERR; ++ } ++ ++ do { ++ void *generate_addin_str = addin_str; ++ ++ if (pred_resist | reseed_required) { ++ err = nisttrng_reseed(state, pred_resist, addin_str); ++ if (err) ++ goto ERR; ++ ++ /* SP800-90a says that if reseed is executed, any additional input string is only used in the reseed phase and replaced by NULL in the generate phase */ ++ generate_addin_str = NULL; ++ err = nisttrng_set_addin_present(state, 0); ++ if (err) ++ goto ERR; ++ ++ /* ADDIN_PRESENT field in MODE has to be set back to 0 to avoid illegal cmd sequence */ ++ reseed_required = 0; ++ } ++ ++ /* generate process */ ++ if (nisttrng_check_seed_lifetime(state) == CRYPTO_RESEED_REQUIRED) { ++ reseed_required = 1; ++ ++ } else { ++ reseed_required = 0; ++ ++ /* Refresh_Addin command if additional input is not NULL*/ ++ if (generate_addin_str) { ++ err = nisttrng_refresh_addin(state, generate_addin_str); ++ if (err) ++ goto ERR; ++ } ++ ++ /* Generate all random bits */ ++ /* if EDU present then get random number from private vtrng */ ++ ++ //state->config.build_cfg0.edu_present = 0; ++ if (state->config.build_cfg0.edu_present) { ++ err = nisttrng_generate_private_vtrng(state, random_bits, ++ req_num_bytes); ++ if (err) ++ goto ERR; ++ ++ } else { ++ err = nisttrng_gen_random(state, random_bits, ++ req_num_bytes); ++ if (err) ++ goto ERR; ++ ++ /* Advance the state - if it returns CRYPTO_RESEED_REQUIRED, have to jump back and do a reseed and generate */ ++ err = nisttrng_advance_state(state); ++ if (err) ++ goto ERR; ++ } ++ } ++ } while (reseed_required); ++ ++ err = CRYPTO_OK; ++ state->status.current_state = NIST_TRNG_STATE_GENERATE; ++ERR: ++ if (err) ++ random_bits = NULL; ++ ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_generate */ ++EXPORT_SYMBOL(nisttrng_generate); +diff --git a/drivers/char/hw_random/dwc/src/trng/trng/nist_trng_private.c b/drivers/char/hw_random/dwc/src/trng/trng/nist_trng_private.c +--- a/drivers/char/hw_random/dwc/src/trng/trng/nist_trng_private.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/trng/nist_trng_private.c 2026-04-08 18:03:46.630736014 +0000 +@@ -0,0 +1,1022 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * This Synopsys software and associated documentation (hereinafter the ++ * "Software") is an unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. The ++ * Software IS NOT an item of Licensed Software or a Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Products ++ * with Synopsys or any supplement thereto. Synopsys is a registered trademark ++ * of Synopsys, Inc. Other names included in the SOFTWARE may be the ++ * trademarks of their respective owners. ++ * ++ * The contents of this file are dual-licensed; you may select either version ++ * 2 of the GNU General Public License ("GPL") or the BSD-3-Clause license ++ * ("BSD-3-Clause"). The GPL is included in the COPYING file accompanying the ++ * SOFTWARE. The BSD License is copied below. ++ * ++ * BSD-3-Clause License: ++ * Copyright (c) 2012-2016 Synopsys, Inc. and/or its affiliates. ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions, and the following disclaimer, without ++ * modification. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * 3. The names of the above-listed copyright holders may not be used to ++ * endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "nisttrng_hw.h" ++#include "nisttrng.h" ++ ++/* Wait functions */ ++static int nisttrng_wait_on_(struct nist_trng_state *state, u32 mask) ++{ ++ u32 tmp; ++ int t; ++ ++ t = NIST_TRNG_RETRY_MAX; ++ ++ do { ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_ISTAT); ++ } while (!(tmp & (mask | NIST_TRNG_REG_ISTAT_ALARMS)) && --t); ++ ++ if (tmp & NIST_TRNG_REG_ISTAT_ALARMS) ++ return nisttrng_get_alarms(state); ++ ++ if (t) { ++ pdu_io_write32(state->base + NIST_TRNG_REG_ISTAT, mask); ++ return CRYPTO_OK; ++ ++ } else { ++ SYNHW_PRINT("wait_on_: failed timeout: %08lx\n", ++ (unsigned long)tmp); ++ return CRYPTO_TIMEOUT; ++ } ++} /* nisttrng_wait_on_ */ ++ ++int nisttrng_wait_on_done(struct nist_trng_state *state) ++{ ++ return nisttrng_wait_on_(state, NIST_TRNG_REG_ISTAT_DONE); ++} /* nisttrng_wait_on_done */ ++EXPORT_SYMBOL(nisttrng_wait_on_done); ++ ++int nisttrng_wait_on_noise_rdy(struct nist_trng_state *state) ++{ ++ return nisttrng_wait_on_(state, NIST_TRNG_REG_ISTAT_NOISE_RDY); ++} /* nisttrng_wait_on_noise_rdy */ ++ ++static int nisttrng_wait_on_zeroize(struct nist_trng_state *state) ++{ ++ return nisttrng_wait_on_(state, NIST_TRNG_REG_ISTAT_ZEROIZE); ++} /* nisttrng_wait_on_zeroize */ ++ ++static int nisttrng_wait_on_kat_completed(struct nist_trng_state *state) ++{ ++ return nisttrng_wait_on_(state, NIST_TRNG_REG_ISTAT_KAT_COMPLETE); ++} /* nisttrng_wait_on_kat_completed */ ++ ++int nisttrng_wait_on_busy(struct nist_trng_state *state) ++{ ++ u32 tmp, t; ++ ++ t = NIST_TRNG_RETRY_MAX; ++ ++ do { ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_STAT); ++ } while ((tmp & NIST_TRNG_REG_STAT_BUSY) && --t); ++ ++ if (t) ++ return CRYPTO_OK; ++ ++ SYNHW_PRINT("wait_on_busy: failed timeout: %08lx\n", ++ (unsigned long)tmp); ++ return CRYPTO_TIMEOUT; ++} /* nisttrng_wait_on_busy */ ++EXPORT_SYMBOL(nisttrng_wait_on_busy); ++ ++/* Read and return alarm. Zeroize if there is an alarm*/ ++int nisttrng_get_alarms(struct nist_trng_state *state) ++{ ++ u32 tmp; ++ ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_ISTAT); ++ if (tmp & NIST_TRNG_REG_ISTAT_ALARMS) { ++ // alarm happened ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_ALARM); ++ DEBUG("Received alarm: %lx\n", (unsigned long)tmp); ++ // clear istat ++ pdu_io_write32(state->base + NIST_TRNG_REG_ISTAT, ++ NIST_TRNG_REG_ISTAT_ALARMS); ++ pdu_io_write32(state->base + NIST_TRNG_REG_ALARM, 0x1F); ++ state->status.alarm_code = tmp & 0x1F; ++ ++ /* zeroize if there was an alarm */ ++ if (state->status.alarm_code != ++ NIST_TRNG_REG_ALARM_FAILED_TEST_ID_OK) { ++ nisttrng_zeroize(state); ++ } ++ } else { ++ state->status.alarm_code = 0; ++ } ++ ++ if (state->status.alarm_code) ++ return CRYPTO_FATAL; ++ else ++ return CRYPTO_OK; ++} /* nisttrng_get_alarms */ ++EXPORT_SYMBOL(nisttrng_get_alarms); ++ ++/* Reset reminder and alarm counters */ ++int nisttrng_reset_counters(struct nist_trng_state *state) ++{ ++ state->counters.bits_per_req_left = state->counters.max_bits_per_req; ++ state->counters.req_per_seed_left = state->counters.max_req_per_seed; ++ ++ return 0; ++} /* nisttrng_reset_counters */ ++EXPORT_SYMBOL(nisttrng_reset_counters); ++ ++/* When a zeroize happens some of the struct objects should reset */ ++int nisttrng_reset_state(struct nist_trng_state *state) ++{ ++ nisttrng_reset_counters(state); ++ state->status.pad_ps_addin = 0; ++ state->status.current_state = NIST_TRNG_STATE_UNINSTANTIATE; ++ ++ return 0; ++} /* nisttrng_reset_state */ ++ ++/* ---------- Set field APIs ---------- */ ++ ++/* ++ * Sets the security strength of the DRBG instance. ++ * > req_sec_strength has to be an integer. The API chooses one of SEC_STRNT_AES128 or SEC_STRNT_AES256 as follows: ++ * 0 < req_sec_strength <= 128 --> security strength = SEC_STRNT_AES128 ++ * 128 < req_sec_strength <= 256 --> security strength = SEC_STRNT_AES256 ++ * else --> Invalid security strength ++ * > If the DRBG is instantiated, a new security strength change request with greater security strength will return error. ++ */ ++int nisttrng_set_sec_strength(struct nist_trng_state *state, int req_sec_strength) ++{ ++ int err; ++ u32 tmp; ++ enum nisttrng_sec_strength chosen_sec_strength; ++ ++ DEBUG(">> %s: security strength = %i\n", __func__, ++ req_sec_strength); ++ ++ /* choose the security strength */ ++ /* set the security strength to the lowest security strength greater or equal to the req_sec_strenght from the set {128, 256} */ ++ if (REQ_SEC_STRENGTH_IS_VALID(req_sec_strength)) { ++ if (req_sec_strength > 0 && req_sec_strength <= 128) { ++ chosen_sec_strength = SEC_STRNT_AES128; ++ ++ } else if (((req_sec_strength > 128) && ++ (req_sec_strength <= 256)) && ++ (state->config.features.drbg_arch == AES256)) { ++ chosen_sec_strength = SEC_STRNT_AES256; ++ ++ } else { /* should not get here, because we have already checked the validity */ ++ DEBUG("Invalid security strength\n"); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ } else { ++ DEBUG("Invalid security strength\n"); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ DEBUG("chosen security strength = %u\n", chosen_sec_strength); ++ ++ /* set the security strenght - at this point security strength is validated and converted */ ++ if (DRBG_INSTANTIATED(state->status.current_state) && ++ chosen_sec_strength != state->status.sec_strength) { ++ /* security strength can only change when the DRBG is not instantiated. */ ++ /* if the new security strength is less that what the DRBG is instantiated with, accept it, but don't change in HW. If it's more, return error */ ++ if (chosen_sec_strength < state->status.sec_strength) { ++ DEBUG("Lowering the security strength. DRBG is already instantiated.\n"); ++ state->status.pad_ps_addin = 4; ++ state->status.sec_strength = chosen_sec_strength; ++ ++ } else { ++ state->status.pad_ps_addin = 0; ++ DEBUG("Cannot select a higher security strenght once the DRBG is instantiated\n"); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ } else { ++ DEBUG("Updating the security strength.\n"); ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_MODE); ++ tmp = NIST_TRNG_REG_MODE_SET_SEC_ALG(tmp, chosen_sec_strength); ++ pdu_io_write32(state->base + NIST_TRNG_REG_MODE, tmp); ++ ++ state->status.pad_ps_addin = 0; ++ state->status.sec_strength = chosen_sec_strength; ++ } ++ ++ err = CRYPTO_OK; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_set_sec_strength */ ++EXPORT_SYMBOL(nisttrng_set_sec_strength); ++ ++/* ++ * Sets the ADDIN_PRESENT field of the MODE register according to the addin_present input. ++ */ ++int nisttrng_set_addin_present(struct nist_trng_state *state, int addin_present) ++{ ++ u32 tmp; ++ ++ DEBUG(">> %s, adding_present = %u\n", __func__, ++ addin_present); ++ ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_MODE); ++ tmp = NIST_TRNG_REG_MODE_SET_ADDIN_PRESENT(tmp, addin_present); ++ pdu_io_write32(state->base + NIST_TRNG_REG_MODE, tmp); ++ ++ DEBUG("--- Return %s, err = %i\n", __func__, 0); ++ return 0; ++} /* nisttrng_set_addin_present */ ++EXPORT_SYMBOL(nisttrng_set_addin_present); ++ ++/* ++ * Sets the PRED_RESIST field of the MODE register according to the pred_resist input. ++ * > If the DRBG is instantiated with prediction resistance of 0, and a change to the prediction resistance of 1 is requested, ++ * the API will return an error. ++ */ ++int nisttrng_set_pred_resist(struct nist_trng_state *state, int pred_resist) ++{ ++ int err; ++ u32 tmp; ++ ++ DEBUG(">> %s: pred_resist = %u\n", __func__, pred_resist); ++ ++ /* if DRBG is instantiated, prediction resistance can only change from 1 to 0 and not vice versa. This is a NIST requirement. */ ++ if (DRBG_INSTANTIATED(state->status.current_state) && pred_resist && ++ !state->status.pred_resist) { ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_MODE); ++ tmp = NIST_TRNG_REG_MODE_SET_PRED_RESIST(tmp, pred_resist); ++ pdu_io_write32(state->base + NIST_TRNG_REG_MODE, tmp); ++ ++ state->status.pred_resist = pred_resist; ++ ++ err = CRYPTO_OK; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_set_pred_resist */ ++EXPORT_SYMBOL(nisttrng_set_pred_resist); ++ ++/* ++ * Puts the NIST_TRNG in either the SECURE or PROMISCUOUS mode. ++ * > A value of 1 for secure_mode puts the core in the SECURE mode and a value of 0 puts it in the PROMISCUOUS mode. ++ * > Any change to the secure mode of the NIST_TRNG will result in a complete zeroize, and will set the seeding mode to self-seeding. ++ * A zeroize will not destroy the programmed mode and ALARM register value. ++ * It keeps the programmed mode to avoid re-programming. ++ * It also, maintains the ALARM register value, so that the user can read the value to understand the reason of the occurred alarm. ++ */ ++int nisttrng_set_secure_mode(struct nist_trng_state *state, int secure_mode) ++{ ++ int err; ++ u32 tmp; ++ int t; ++ ++ DEBUG(">> %s: secure_mode = %u\n", __func__, secure_mode); ++ ++ t = NIST_TRNG_RETRY_MAX; ++ ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_SMODE); ++ tmp = NIST_TRNG_REG_SMODE_SET_SECURE_EN(tmp, secure_mode); ++ pdu_io_write32(state->base + NIST_TRNG_REG_SMODE, tmp); ++ ++ /* wait until STAT register indicates that the mode is applied */ ++ do { ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_STAT); ++ } while ((NIST_TRNG_REG_STAT_GET_SECURE(tmp) != secure_mode) && --t); ++ ++ if (!t) { ++ err = CRYPTO_TIMEOUT; ++ goto ERR; ++ } ++ ++ /* if secure mode changes, a zeroize will happen in HW. */ ++ if (state->status.secure_mode != secure_mode) { ++ DEBUG("secure mode changed. zeroize happened. reset sw state\n"); ++ /* nonce mode goes back to default. */ ++ state->status.nonce_mode = 0; ++ /* reset the SW state */ ++ nisttrng_reset_state(state); ++ } ++ ++ state->status.secure_mode = secure_mode; ++ ++ err = CRYPTO_OK; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_set_secure_mode */ ++EXPORT_SYMBOL(nisttrng_set_secure_mode); ++ ++/* ++ * To change the seeding mode of the NIST_TRNG. ++ * > A value of 1 for nonce_mode will put the NIST_TRNG in the nonce seeding mode, which means that the seed will be provided by the user, ++ * unlike the noise or self-seeding mode (normal mode of operation) in which the seed is generated by the internal entropy source. ++ * > Any transition to or from the nonce mode will zeroize the NIST_TRNG. ++ */ ++int nisttrng_set_nonce_mode(struct nist_trng_state *state, int nonce_mode) ++{ ++ int err; ++ u32 tmp; ++ int t; ++ ++ DEBUG(">> %s: nonce_mode = %u\n", __func__, nonce_mode); ++ ++ t = NIST_TRNG_RETRY_MAX; ++ ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_SMODE); ++ tmp = NIST_TRNG_REG_SMODE_SET_NONCE(tmp, nonce_mode); ++ pdu_io_write32(state->base + NIST_TRNG_REG_SMODE, tmp); ++ ++ /* wait until STAT register indicates that the mode is applied */ ++ do { ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_STAT); ++ } while ((NIST_TRNG_REG_STAT_GET_NONCE(tmp) != nonce_mode) && --t); ++ ++ if (!t) { ++ err = CRYPTO_TIMEOUT; ++ goto ERR; ++ } ++ ++ /* if nonce mode changes, a zeroize will happen in HW. */ ++ if (state->status.nonce_mode != nonce_mode) { ++ DEBUG("nonce mode changed. zeroize happened. reset sw state\n"); ++ /* reset the SW state */ ++ nisttrng_reset_state(state); ++ } ++ ++ state->status.nonce_mode = nonce_mode; ++ ++ err = CRYPTO_OK; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_set_nonce_mode */ ++EXPORT_SYMBOL(nisttrng_set_nonce_mode); ++ ++/* ---------- Load data APIs ---------- */ ++/* ++ * Loads the additional input or personalization string into the NPA_DATAx registers. ++ * > Loads the proper number of bits (256 or 384) according to the security strength stored in the state handle. ++ */ ++int nisttrng_load_ps_addin(struct nist_trng_state *state, void *input_str) ++{ ++ int err; ++ int i, j; ++ int str_size; ++ ++ DEBUG(">> %s starts...\n", __func__); ++ ++ /* return error if the pointer is NULL */ ++ if (!input_str) { ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* calculate the length based on the security strength */ ++ if (state->status.sec_strength == SEC_STRNT_AES128) ++ str_size = 8; /* 256/32 */ ++ else if (state->status.sec_strength == SEC_STRNT_AES256) ++ str_size = 12; /* 384/32 */ ++ ++ for (i = 0; i < str_size; i++) { ++ pdu_io_write32(state->base + NIST_TRNG_REG_NPA_DATA0 + i, ++ ((u32 *)input_str)[i]); ++ } ++ ++ j = str_size + state->status.pad_ps_addin; ++ /* if security strength is lowered after the DRBG is instantiated, pad PS and ADDIN with 0 at the MSB side */ ++ DEBUG("pad NPA_DATA with %u zeros at the MSB side\n", ++ state->status.pad_ps_addin); ++ for (i = str_size; i < j; i++) ++ pdu_io_write32(state->base + NIST_TRNG_REG_NPA_DATA0 + i, 0); ++ ++ err = CRYPTO_OK; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_load_ps_addin */ ++EXPORT_SYMBOL(nisttrng_load_ps_addin); ++ ++/* ---------- Command APIs ---------- */ ++/* ++ * Provides entropy and is used in both nonce and noise (self) seeding modes of operation: ++ * > If the NIST_TRNG is in the nonce mode, entropy must be provided by the user; otherwise (in the self-seeding mode) entropy will be generated by the internal entropy source of the NIST_TRNG. ++ * > In the noise mode, calling the API will initiate a seeding command. Depending on the programmed security strength, a 256 or 384-bit seed will be generated. ++ * > Inputs 2 and 3 are only used when the core is in the nonce mode. ++ * > In the nonce mode, the NIST_TRNG can be seeded either through 2 or 3 blocks of 512-bit nonce values which are passed to the internal derivation function to increase the entropy, ++ * or it can be seeded by a 256 or 384-bit nonce written directly into the SEEDx registers. ++ * Passing a value of 1 to nonce_operation selects the former scenario and a value of 0 selects the latter. ++ * > The input_nonce pointer must point to a memory location with a sufficient number of initialized bits. ++ * > Table below shows the required number of bits depending on the nonce_operation and the security strength values. ++ * nonce_operation | Security Strength | Bit length requirement ++ * ------------------------------------------------------------------------------------------ ++ * 1 (using the Derivation Function) | SEC_STRNT_AES128 | 2x512 = 1024 ++ * 1 (using the Derivation Function) | SEC_STRNT_AES256 | 3x512 = 1536 ++ * 0 (loading the seed into SEEDx) | SEC_STRNT_AES128 | 256 ++ * 0 (loading the seed into SEEDx) | SEC_STRNT_AES256 | 384 ++ * > Generated entropy is secret information held securely within the HW and remains inaccessible to the user, unless the HW core is in the PROMISCUOUS mode. ++ */ ++int nisttrng_get_entropy_input(struct nist_trng_state *state, void *input_nonce, ++ int nonce_operation) ++{ ++ int err; ++ int nonce_ld_cntr = 0; ++ int i, j; ++ ++ DEBUG(">> %s: seeding mode = %s, nonce_operation = %u\n", __func__, ++ (state->status.nonce_mode ? "Nonce" : "Noise"), nonce_operation); ++ ++ /* make sure there is no alarm and the core is not busy */ ++ err = nisttrng_get_alarms(state); ++ if (err) ++ goto ERR; ++ ++ err = nisttrng_wait_on_busy(state); ++ if (err) ++ goto ERR; ++ ++ /* --- Seeding --- */ ++ if (state->status.nonce_mode) { /* --- nonce mode --- */ ++ if (!input_nonce) { ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ nonce_ld_cntr = 0; ++ ++ if (state->status.sec_strength == SEC_STRNT_AES128) ++ nonce_ld_cntr = 2; ++ else if (state->status.sec_strength == SEC_STRNT_AES256) ++ nonce_ld_cntr = 3; ++ ++ if (nonce_operation) { /* load the noise inside NPA_DATAx register and issue gen_nonce command */ ++ for (i = 0; i < nonce_ld_cntr; i++) { ++ /* load the nonoce */ ++ for (j = 0; j < 16; j++) { ++ pdu_io_write32(state->base + ++ NIST_TRNG_REG_NPA_DATA0 + j, ++ ((u32 *)input_nonce)[16 * i + j]); ++ } ++ ++ /* issue the command and wait on done */ ++ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, ++ NIST_TRNG_REG_CTRL_CMD_GEN_NONCE); ++ ++ if (nisttrng_wait_on_done(state)) { ++ err = CRYPTO_FATAL; ++ goto ERR; ++ }; ++ } ++ ++ } else { ++ /* load the nonoce */ ++ for (i = 0; i < 4 * nonce_ld_cntr; i++) { ++ pdu_io_write32(state->base + NIST_TRNG_REG_SEED0 + i, ++ ((u32 *)input_nonce)[i]); ++ } ++ } ++ } else { /* --- noise mode --- */ ++ /* issue the command and wait on done */ ++ DEBUG("issue the Gen_Noise command\n"); ++ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, ++ NIST_TRNG_REG_CTRL_CMD_GEN_NOISE); ++ ++ if (nisttrng_wait_on_done(state)) { ++ err = CRYPTO_FATAL; ++ goto ERR; ++ }; ++ } ++ ++ err = CRYPTO_OK; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_get_entropy_input */ ++EXPORT_SYMBOL(nisttrng_get_entropy_input); ++ ++/* ++ * Generate Function: ++ * > The Generate function in NIST_TRNG HW is broken down into 3 steps: Refresh_Addin, Gen_Random and Advance_State. ++ * nisttrng_generate incorporates all these steps and some extra checks into one simple API. ++ * > There is one API for each step, below || ++ * \/ ++ */ ++/* ++ * Generate Part 1 - Refresh_Addin: Additional input string is used to add to the HW state entropy. ++ * > This API calls nisttrng_set_addin_present to set the ADDIN_PRESENT field of the MODE register to 1. ++ * > Then it loads the additional input provided by addin_str pointer into the NPA_DATAx by calling the nisttrng_load_ps_addin. ++ * > Then, it issues a Refresh_Addin command to the HW. ++ * > If the addin_str pointer is NULL, the API will return error. ++ */ ++int nisttrng_refresh_addin(struct nist_trng_state *state, void *addin_str) ++{ ++ int err; ++ ++ DEBUG(">> %s starts...\n", __func__); ++ ++ /* if the DRBG is not intantiated return error */ ++ if (!DRBG_INSTANTIATED(state->status.current_state)) { ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* make sure there is no alarm and the core is not busy */ ++ err = nisttrng_get_alarms(state); ++ if (err) ++ goto ERR; ++ ++ err = nisttrng_wait_on_busy(state); ++ if (err) ++ goto ERR; ++ ++ /* This API should not be called with a NULL additional input string */ ++ if (!addin_str) { ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* set the ADDIN_PRESENT field of the MODE register to 1 */ ++ err = nisttrng_set_addin_present(state, 1); ++ if (err) ++ goto ERR; ++ ++ err = nisttrng_load_ps_addin(state, addin_str); ++ if (err) ++ goto ERR; ++ ++ /* execute the command and wait on done*/ ++ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, ++ NIST_TRNG_REG_CTRL_CMD_REFRESH_ADDIN); ++ ++ err = nisttrng_wait_on_done(state); ++ if (err) ++ goto ERR; ++ ++ err = 0; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_refresh_addin */ ++EXPORT_SYMBOL(nisttrng_refresh_addin); ++ ++/* ++ * Generate Part 2 - Gen_Random: generates the requested number of bits. ++ * > This API issues the Gen_Random command to the HW as many times as indicated by req_num_bytes to generate the requested number of bits. ++ * > If the requested number of bits (i.e. 128×req_num_blks) is more than the maximum value specified by max_bits_per_req, the API will return error. ++ * > Random bits will be returned in random_bits. ++ */ ++int nisttrng_gen_random(struct nist_trng_state *state, void *random_bits, ++ unsigned long req_num_bytes) ++{ ++ int err; ++ int i, j; ++ u32 tmp; ++ unsigned int remained_bytes; ++ unsigned long req_num_blks; ++ ++ DEBUG(">> %s: req_num_bytes = %lu\n", __func__, req_num_bytes); ++ ++ /* if the DRBG is not intantiated return error */ ++ if (!DRBG_INSTANTIATED(state->status.current_state)) { ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* make sure there is no alarm and the core is not busy */ ++ err = nisttrng_get_alarms(state); ++ if (err) ++ goto ERR; ++ ++ err = nisttrng_wait_on_busy(state); ++ if (err) ++ goto ERR; ++ ++ /* requested number of bits has to be less that the programmed maximum */ ++ if ((req_num_bytes * 8) > state->counters.max_bits_per_req) { ++ DEBUG("requested number of bits (%lu) is larger than the set maximum (%lu)\n", ++ (req_num_bytes * 8), state->counters.max_bits_per_req); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* make sure random_bits is not NULL */ ++ if (!random_bits) { ++ DEBUG("random_bits pointer cannot be NULL\n"); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* loop on generate to get the requested number of bits. Each generate gives NIST_TRNG_RAND_BLK_SIZE_BITS bits. */ ++ req_num_blks = ++ ((req_num_bytes * 8) % NIST_TRNG_RAND_BLK_SIZE_BITS) ? ++ (((req_num_bytes * 8) / NIST_TRNG_RAND_BLK_SIZE_BITS) + ++ 1) : ++ ((req_num_bytes * 8) / NIST_TRNG_RAND_BLK_SIZE_BITS); ++ ++ for (i = 0; i < req_num_blks; i++) { ++ /* issue gen_random and wait on done */ ++ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, ++ NIST_TRNG_REG_CTRL_CMD_GEN_RANDOM); ++ ++ err = nisttrng_wait_on_done(state); ++ if (err) ++ goto ERR; ++ ++ /* read the generated random number block and store */ ++ for (j = 0; j < (NIST_TRNG_RAND_BLK_SIZE_BITS / 32); j++) { ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_RAND0 + j); ++ /* copy to random_bits byte-by-byte, until req_num_bytes are copied */ ++ remained_bytes = req_num_bytes - ++ (i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + ++ j * 4); ++ if (remained_bytes > 4) { ++ memcpy(random_bits + ++ i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + ++ j * 4, &tmp, 4); ++ ++ /* decrement the bits counter and return error if generated more than the maximum*/ ++ state->counters.bits_per_req_left = ++ state->counters.bits_per_req_left - ++ 4 * 8; ++ ++ if (state->counters.bits_per_req_left < 0) { ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ } else { ++ memcpy(random_bits + i * (NIST_TRNG_RAND_BLK_SIZE_BITS / 8) + ++ j * 4, &tmp, remained_bytes); ++ ++ /* decrement the bits counter and return error if generated more than the maximum*/ ++ state->counters.bits_per_req_left = ++ state->counters.bits_per_req_left - ++ remained_bytes * 8; ++ ++ if (state->counters.bits_per_req_left < 0) { ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ break; ++ } ++ } ++ } ++ ++ err = CRYPTO_OK; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_gen_random */ ++EXPORT_SYMBOL(nisttrng_gen_random); ++ ++/* ++ * Generate Part 3 - Advance the state: advances the state of the DRBG. ++ * > This API issues the Advance_State command to the HW. ++ * > Then it updates the counter for the number of generate requests per seed. ++ * > The counter must be checked every time before starting the Generate process and a reseed must be issued if the limit is reached. This check is incorporated inside nisttrng_generate API. ++ * > Note that we don't have to provide additional input again for this API, because if it had been provided in refresh_addin stage, HW will lock the NPA_DATAx, so it will be still available ++ */ ++int nisttrng_advance_state(struct nist_trng_state *state) ++{ ++ int err; ++ ++ DEBUG(">> %s starts...\n", __func__); ++ ++ /* if the DRBG is not intantiated return error */ ++ if (!DRBG_INSTANTIATED(state->status.current_state)) { ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* make sure there is no alarm and the core is not busy */ ++ err = nisttrng_get_alarms(state); ++ if (err) ++ goto ERR; ++ ++ err = nisttrng_wait_on_busy(state); ++ if (err) ++ goto ERR; ++ ++ /* issue advance_state and wait on done */ ++ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, ++ NIST_TRNG_REG_CTRL_CMD_ADVANCE_STATE); ++ err = nisttrng_wait_on_done(state); ++ if (err) ++ goto ERR; ++ ++ /* generate is finished, reset the bits_per_req_left counter */ ++ state->counters.bits_per_req_left = state->counters.max_bits_per_req; ++ ++ --state->counters.req_per_seed_left; ++ if (state->counters.req_per_seed_left < 0) { ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } /* just a check */ ++ ++ err = CRYPTO_OK; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_advance_state */ ++ ++int nisttrng_check_seed_lifetime(struct nist_trng_state *state) ++{ ++ int err; ++ ++ if (state->counters.req_per_seed_left <= 0) { ++ DEBUG("maximum number of requests per seed is reached\n"); ++ err = CRYPTO_RESEED_REQUIRED; ++ goto ERR; ++ } ++ ++ err = CRYPTO_OK; ++ERR: ++ return err; ++} ++EXPORT_SYMBOL(nisttrng_advance_state); ++ ++/* ++ * Perform Known Answer Test ++ * > The NIST_TRNG can perform a KAT on the DRBG and the derivation function inside the entropy source. There are also two different vectors available to do the KAT. ++ * > The kat_sel input selects whether the KAT should be performed on the DRBG or the derivation function. ++ * > The kat_vec input chooses the KAT vector. ++ * > Selections are done by writing the values to the MODE register. ++ * > If the KAT fails, the API returns error. ++ */ ++int nisttrng_kat(struct nist_trng_state *state, int kat_sel, int kat_vec) ++{ ++ int err; ++ u32 tmp; ++ ++ DEBUG(">> %s: kat_sel = %u, kat_vec = %u\n", __func__, ++ kat_sel, kat_vec); ++ ++ /* set KAT_SEL and KAT_VEC */ ++ tmp = pdu_io_read32(state->base + NIST_TRNG_REG_MODE); ++ tmp = NIST_TRNG_REG_MODE_SET_KAT_SEL(tmp, kat_sel); ++ tmp = NIST_TRNG_REG_MODE_SET_KAT_VEC(tmp, kat_vec); ++ pdu_io_write32(state->base + NIST_TRNG_REG_MODE, tmp); ++ ++ /* issue the command and wait on kat_completed */ ++ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, ++ NIST_TRNG_REG_CTRL_CMD_KAT); ++ ++ err = nisttrng_wait_on_kat_completed(state); ++ if (err) ++ goto ERR; ++ ++ /* check for alarms */ ++ err = nisttrng_get_alarms(state); ++ if (err) ++ goto ERR; ++ ++ err = CRYPTO_OK; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_kat */ ++EXPORT_SYMBOL(nisttrng_kat); ++ ++/* ++ * Performs a full KAT with all four combinations of the kat_sel and kat_vec ++ * > If any of the KAT fails, the API returns error. ++ */ ++int nisttrng_full_kat(struct nist_trng_state *state) ++{ ++ int err; ++ ++ DEBUG(">> %s starts...\n", __func__); ++ ++ /* SEL = 0, Vec = 0 */ ++ err = nisttrng_kat(state, 0, 0); ++ if (err) ++ goto ERR; ++ ++ /* SEL = 0, Vec = 1 */ ++ err = nisttrng_kat(state, 0, 1); ++ if (err) ++ goto ERR; ++ ++ /* SEL = 1, Vec = 0 */ ++ err = nisttrng_kat(state, 1, 0); ++ if (err) ++ goto ERR; ++ ++ /* SEL = 1, Vec = 1 */ ++ err = nisttrng_kat(state, 1, 1); ++ if (err) ++ goto ERR; ++ ++ err = CRYPTO_OK; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_full_kat */ ++EXPORT_SYMBOL(nisttrng_full_kat); ++ ++/* ++ * max_bits_per_req reminder initialized by nisttrng_init can change using this API. ++ * > If this API is called when the DRBG is instantiated, an error will be returned. ++ * > If the requested maximum is more than the standard's limit (determinded by NIST_TRNG_DFLT_MAX_BITS_PER_REQ), the API will return an error. ++ */ ++int nisttrng_set_reminder_max_bits_per_req(struct nist_trng_state *state, ++ unsigned long max_bits_per_req) ++{ ++ int err; ++ ++ DEBUG(">> %s: %lu\n", __func__, max_bits_per_req); ++ ++ /* if the DRBG is instantiated, cannot change the value */ ++ if (DRBG_INSTANTIATED(state->status.current_state)) { ++ DEBUG("cannot change the reminder value when DRBG is already instantiated\n"); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* requested value cannot be more than NIST's limit */ ++ if (max_bits_per_req > NIST_DFLT_MAX_BITS_PER_REQ) { ++ DEBUG("requested max_bits_per_req is more than standard's limit\n"); ++ err = CRYPTO_INVALID_ARGUMENT; ++ goto ERR; ++ } ++ ++ state->counters.max_bits_per_req = max_bits_per_req; ++ state->counters.bits_per_req_left = max_bits_per_req; ++ ++ err = CRYPTO_OK; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} ++EXPORT_SYMBOL(nisttrng_set_reminder_max_bits_per_req); ++ ++/* ++ * max_req_per_seed reminder initialized by nisttrng_init can change using this API. ++ * > If this API is called when the DRBG is instantiated, an error will be returned. ++ * > If the requested maximum is more than the standard's limit (determinded by NIST_TRNG_DFLT_MAX_REQ_PER_SEED), the API will return an error. ++ */ ++int nisttrng_set_reminder_max_req_per_seed(struct nist_trng_state *state, ++ unsigned long long max_req_per_seed) ++{ ++ int err; ++ ++ DEBUG(">> %s: %llu\n", __func__, max_req_per_seed); ++ ++ /* if the DRBG is instantiated, cannot change the value */ ++ if (DRBG_INSTANTIATED(state->status.current_state)) { ++ DEBUG("cannot change the reminder value when DRBG is already instantiated\n"); ++ err = CRYPTO_FAILED; ++ goto ERR; ++ } ++ ++ /* requested value cannot be more than NIST's limit */ ++ if (max_req_per_seed > NIST_DFLT_MAX_REQ_PER_SEED) { ++ DEBUG("requested max_req_per_seed is more than standard's limit\n"); ++ err = CRYPTO_INVALID_ARGUMENT; ++ goto ERR; ++ } ++ ++ state->counters.max_req_per_seed = max_req_per_seed; ++ state->counters.req_per_seed_left = max_req_per_seed; ++ ++ err = CRYPTO_OK; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} ++EXPORT_SYMBOL(nisttrng_set_reminder_max_req_per_seed); ++ ++/* ++ * Zeroize command ++ * > A zeroize will not destroy the programmed mode and ALARM register value. ++ * It keeps the programmed mode to avoid re-programming. ++ * It also, maintains the ALARM register value, so that the user can read the value to understand the reason of the occurred alarm. ++ */ ++int nisttrng_zeroize(struct nist_trng_state *state) ++{ ++ int err; ++ ++ DEBUG(">> %s: zeroize the core\n", __func__); ++ ++ /* issue zeroize command */ ++ pdu_io_write32(state->base + NIST_TRNG_REG_CTRL, ++ NIST_TRNG_REG_CTRL_CMD_ZEROIZE); ++ ++ /* wait on zeroize done */ ++ err = nisttrng_wait_on_zeroize(state); ++ if (err) ++ goto ERR; ++ ++ /* reset the SW state */ ++ nisttrng_reset_state(state); ++ ++ err = CRYPTO_OK; ++ state->status.current_state = NIST_TRNG_STATE_UNINSTANTIATE; ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} /* nisttrng_zeroize */ ++EXPORT_SYMBOL(nisttrng_zeroize); ++ ++int nisttrng_rnc(struct nist_trng_state *state, int rnc_ctrl_cmd) ++{ ++ int err = 0; ++ u32 tmp; ++ ++ DEBUG(">> %s cmd %d\n", __func__, rnc_ctrl_cmd); ++ ++ if (rnc_ctrl_cmd > NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_FINISH_TO_IDLE) { ++ DEBUG(">> Invalid cmd %d\n", rnc_ctrl_cmd); ++ err = -1; ++ goto ERR; ++ } ++ ++ if (!state->config.build_cfg0.edu_present) { ++ DEBUG(">> edu not present\n"); ++ err = -1; ++ goto ERR; ++ } ++ ++ pdu_io_write32(state->base + NIST_TRNG_EDU_RNC_CTRL, rnc_ctrl_cmd); ++ if (rnc_ctrl_cmd == NIST_TRNG_EDU_RNC_CTRL_CMD_RNC_ENABLE) { ++ // wait till rnc is enabled ++ do { ++ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_STAT); ++ } while (!NIST_TRNG_EDU_STAT_RNC_ENABLED(tmp)); ++ ++ state->status.edu_vstat.rnc_enabled = 1; ++ ++ } else { ++ // wait till rnc is idle (disabled) ++ do { ++ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_STAT); ++ } while (NIST_TRNG_EDU_STAT_RNC_ENABLED(tmp)); ++ ++ state->status.edu_vstat.rnc_enabled = 0; ++ } ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} ++EXPORT_SYMBOL(nisttrng_rnc); ++ ++int nisttrng_wait_fifo_full(struct nist_trng_state *state) ++{ ++ int err = 0; ++ u32 tmp, t; ++ ++ t = NIST_TRNG_RETRY_MAX; ++ ++ DEBUG(">> %s starts...\n", __func__); ++ ++ do { ++ tmp = pdu_io_read32(state->base + NIST_TRNG_EDU_STAT); ++ } while ((!NIST_TRNG_EDU_STAT_FIFO_FULL(tmp)) && --t); ++ ++ if (t) { ++ err = CRYPTO_OK; ++ } else { ++ DEBUG("wait_on_fifo_full: failed timeout: %08lx\n", ++ (unsigned long)tmp); ++ err = CRYPTO_TIMEOUT; ++ goto ERR; ++ } ++ ++ERR: ++ DEBUG("--- Return %s, err = %i\n", __func__, err); ++ return err; ++} +diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c +--- a/drivers/char/ipmi/kcs_bmc_aspeed.c 2025-08-01 08:48:47.000000000 +0000 ++++ b/drivers/char/ipmi/kcs_bmc_aspeed.c 2026-04-08 18:03:48.285705777 +0000 +@@ -1,13 +1,14 @@ + // SPDX-License-Identifier: GPL-2.0 + /* + * Copyright (c) 2015-2018, Intel Corporation. ++ * Copyright (c) 2023, Aspeed Technology Inc. + */ +- + #define pr_fmt(fmt) "aspeed-kcs-bmc: " fmt + + #include + #include #include +#include #include @@ -23731,7 +29939,7 @@ diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspe MODULE_DESCRIPTION("Aspeed device interface to the KCS BMC device"); diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig --- a/drivers/clk/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/clk/Kconfig 2025-12-23 10:16:07.639258736 +0000 ++++ b/drivers/clk/Kconfig 2026-04-08 18:03:32.729989759 +0000 @@ -277,6 +277,20 @@ The G4 and G5 series, including the ast2400 and ast2500, are supported by this driver. @@ -23753,24 +29961,9 @@ diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig config COMMON_CLK_S2MPS11 tristate "Clock driver for S2MPS1X/S5M8767 MFD" depends on MFD_SEC_CORE || COMPILE_TEST -@@ -342,6 +356,14 @@ - This driver supports the clocking features of the Cirrus Logic - Lochnagar audio development board. - -+config COMMON_CLK_NPCM8XX -+ tristate "Clock driver for the NPCM8XX SoC Family" -+ depends on ARCH_NPCM || COMPILE_TEST -+ help -+ This driver supports the clocks on the Nuvoton BMC NPCM8XX SoC Family, -+ all the clocks are initialized by the bootloader, so this driver -+ allows only reading of current settings directly from the hardware. -+ - config COMMON_CLK_LOONGSON2 - bool "Clock driver for Loongson-2 SoC" - depends on LOONGARCH || COMPILE_TEST diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile --- a/drivers/clk/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/clk/Makefile 2025-12-23 10:16:11.218198718 +0000 ++++ b/drivers/clk/Makefile 2026-04-08 18:03:37.201908196 +0000 @@ -48,6 +48,8 @@ obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o obj-$(CONFIG_COMMON_CLK_ASPEED) += clk-aspeed.o @@ -23780,17 +29973,9 @@ diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_CLK_HSDK) += clk-hsdk-pll.o obj-$(CONFIG_COMMON_CLK_K210) += clk-k210.o -@@ -62,6 +64,7 @@ - obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o - obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o - obj-$(CONFIG_ARCH_NPCM7XX) += clk-npcm7xx.o -+obj-$(CONFIG_COMMON_CLK_NPCM8XX) += clk-npcm8xx.o - obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o - obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o - obj-$(CONFIG_CLK_LS1028A_PLLDIG) += clk-plldig.o diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c --- a/drivers/clk/clk-aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/clk/clk-aspeed.c 2025-12-23 10:16:20.650040614 +0000 ++++ b/drivers/clk/clk-aspeed.c 2026-04-08 18:03:47.783714949 +0000 @@ -54,15 +54,15 @@ [ASPEED_CLK_GATE_DCLK] = { 5, -1, "dclk-gate", NULL, CLK_IS_CRITICAL }, /* DAC */ [ASPEED_CLK_GATE_REFCLK] = { 6, -1, "refclk-gate", "clkin", CLK_IS_CRITICAL }, @@ -23821,8 +30006,8 @@ diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c * SCUD4 resets start at an offset to separate them from diff --git a/drivers/clk/clk-ast1700.c b/drivers/clk/clk-ast1700.c --- a/drivers/clk/clk-ast1700.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/clk/clk-ast1700.c 2025-12-23 10:16:20.655040530 +0000 -@@ -0,0 +1,807 @@ ++++ b/drivers/clk/clk-ast1700.c 2026-04-08 18:03:47.788714857 +0000 +@@ -0,0 +1,809 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright ASPEED Technology + @@ -23850,6 +30035,8 @@ diff --git a/drivers/clk/clk-ast1700.c b/drivers/clk/clk-ast1700.c +#define AST1700_UXCLK_CTRL 0x330 +#define AST1700_HUXCLK_CTRL 0x334 + ++#define CREATE_CLK_NAME(id, suffix) kasprintf(GFP_KERNEL, "ast1700_%d-%s", id, suffix) ++ +static DEFINE_IDA(ast1700_clk_ida); + +/* Globally visible clocks */ @@ -23907,7 +30094,7 @@ diff --git a/drivers/clk/clk-ast1700.c b/drivers/clk/clk-ast1700.c + { 0 } +}; + -+static struct clk_hw *AST1700_calc_uclk(const char *name, u32 val) ++static struct clk_hw *AST1700_calc_uclk(int id, u32 val) +{ + unsigned int mult, div; + @@ -23918,10 +30105,11 @@ diff --git a/drivers/clk/clk-ast1700.c b/drivers/clk/clk-ast1700.c + mult = r; + div = n * 2; + -+ return clk_hw_register_fixed_factor(NULL, name, "ast1700-uxclk", 0, mult, div); ++ return clk_hw_register_fixed_factor(NULL, CREATE_CLK_NAME(id, "uartxclk"), ++ CREATE_CLK_NAME(id, "uxclk"), 0, mult, div); +}; + -+static struct clk_hw *AST1700_calc_huclk(const char *name, u32 val) ++static struct clk_hw *AST1700_calc_huclk(int id, u32 val) +{ + unsigned int mult, div; + @@ -23932,7 +30120,8 @@ diff --git a/drivers/clk/clk-ast1700.c b/drivers/clk/clk-ast1700.c + mult = r; + div = n * 2; + -+ return clk_hw_register_fixed_factor(NULL, name, "ast1700-huxclk", 0, mult, div); ++ return clk_hw_register_fixed_factor(NULL, CREATE_CLK_NAME(id, "huartxclk"), ++ CREATE_CLK_NAME(id, "huxclk"), 0, mult, div); +}; + +static struct clk_hw *AST1700_calc_pll(const char *name, const char *parent_name, u32 val) @@ -24105,8 +30294,6 @@ diff --git a/drivers/clk/clk-ast1700.c b/drivers/clk/clk-ast1700.c + "ast1700_1-hpll", +}; + -+#define CREATE_CLK_NAME(id, suffix) kasprintf(GFP_KERNEL, "ast1700_%d-%s", id, suffix) -+ +static int AST1700_clk_init(struct device_node *ast1700_node) +{ + struct clk_hw_onecell_data *clk_data; @@ -24187,7 +30374,7 @@ diff --git a/drivers/clk/clk-ast1700.c b/drivers/clk/clk-ast1700.c + 0, 2, 0, &ast1700_clk_lock); + + val = readl(clk_base + AST1700_UXCLK_CTRL); -+ clks[AST1700_CLK_UARTX] = AST1700_calc_uclk(CREATE_CLK_NAME(id, "uartxclk"), val); ++ clks[AST1700_CLK_UARTX] = AST1700_calc_uclk(id, val); + + /* huxclk mux selection */ + clks[AST1700_CLK_HUXCLK] = @@ -24198,7 +30385,7 @@ diff --git a/drivers/clk/clk-ast1700.c b/drivers/clk/clk-ast1700.c + 3, 2, 0, &ast1700_clk_lock); + + val = readl(clk_base + AST1700_HUXCLK_CTRL); -+ clks[AST1700_CLK_HUARTX] = AST1700_calc_huclk(CREATE_CLK_NAME(id, "huartxclk"), val); ++ clks[AST1700_CLK_HUARTX] = AST1700_calc_huclk(id, val); + + /* AHB CLK = 200Mhz */ + clks[AST1700_CLK_AHB] = @@ -24632,7 +30819,7 @@ diff --git a/drivers/clk/clk-ast1700.c b/drivers/clk/clk-ast1700.c + diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c --- a/drivers/clk/clk-ast2600.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/clk/clk-ast2600.c 2025-12-23 10:16:20.661040429 +0000 ++++ b/drivers/clk/clk-ast2600.c 2026-04-08 18:03:47.794714748 +0000 @@ -19,7 +19,7 @@ * This includes the gates (configured from aspeed_g6_gates), plus the * explicitly-configured clocks (ASPEED_CLK_HPLL and up). @@ -25160,8 +31347,8 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c if (ret) diff --git a/drivers/clk/clk-ast2700.c b/drivers/clk/clk-ast2700.c --- a/drivers/clk/clk-ast2700.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/clk/clk-ast2700.c 2025-12-23 10:16:20.644040714 +0000 -@@ -0,0 +1,1334 @@ ++++ b/drivers/clk/clk-ast2700.c 2026-04-08 18:03:47.777715058 +0000 +@@ -0,0 +1,1244 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024 ASPEED Technology Inc. @@ -25211,39 +31398,6 @@ diff --git a/drivers/clk/clk-ast2700.c b/drivers/clk/clk-ast2700.c +#define SCU1_UXCLK_CTRL 0x330 +#define SCU1_HUXCLK_CTRL 0x334 +#define SCU1_MAC12_CLK_DLY 0x390 -+#define SCU1_MAC12_CLK_DLY_100M 0x394 -+#define SCU1_MAC12_CLK_DLY_10M 0x398 -+ -+/* -+ * MAC Clock Delay settings -+ */ -+#define MAC_CLK_RMII1_50M_RCLK_O_CTRL BIT(30) -+#define MAC_CLK_RMII1_50M_RCLK_O_DIS 0 -+#define MAC_CLK_RMII1_50M_RCLK_O_EN 1 -+#define MAC_CLK_RMII0_50M_RCLK_O_CTRL BIT(29) -+#define MAC_CLK_RMII0_5M_RCLK_O_DIS 0 -+#define MAC_CLK_RMII0_5M_RCLK_O_EN 1 -+#define MAC_CLK_RMII_TXD_FALLING_2 BIT(27) -+#define MAC_CLK_RMII_TXD_FALLING_1 BIT(26) -+#define MAC_CLK_RXCLK_INV_2 BIT(25) -+#define MAC_CLK_RXCLK_INV_1 BIT(24) -+#define MAC_CLK_1G_INPUT_DELAY_2 GENMASK(23, 18) -+#define MAC_CLK_1G_INPUT_DELAY_1 GENMASK(17, 12) -+#define MAC_CLK_1G_OUTPUT_DELAY_2 GENMASK(11, 6) -+#define MAC_CLK_1G_OUTPUT_DELAY_1 GENMASK(5, 0) -+ -+#define MAC_CLK_100M_10M_RESERVED GENMASK(31, 26) -+#define MAC_CLK_100M_10M_RXCLK_INV_2 BIT(25) -+#define MAC_CLK_100M_10M_RXCLK_INV_1 BIT(24) -+#define MAC_CLK_100M_10M_INPUT_DELAY_2 GENMASK(23, 18) -+#define MAC_CLK_100M_10M_INPUT_DELAY_1 GENMASK(17, 12) -+#define MAC_CLK_100M_10M_OUTPUT_DELAY_2 GENMASK(11, 6) -+#define MAC_CLK_100M_10M_OUTPUT_DELAY_1 GENMASK(5, 0) -+ -+#define AST2700_DEF_MAC12_DELAY_1G_A0 0x00CF4D75 -+#define AST2700_DEF_MAC12_DELAY_1G_A1 0x005D6618 -+#define AST2700_DEF_MAC12_DELAY_100M 0x00410410 -+#define AST2700_DEF_MAC12_DELAY_10M 0x00410410 + +struct mac_delay_config { + u32 tx_delay_1000; @@ -26218,62 +32372,6 @@ diff --git a/drivers/clk/clk-ast2700.c b/drivers/clk/clk-ast2700.c + return hw; +} + -+static void ast2700_soc1_configure_mac01_clk(struct ast2700_clk_ctrl *clk_ctrl) -+{ -+ struct device_node *np = clk_ctrl->dev->of_node; -+ struct mac_delay_config mac_cfg; -+ u32 reg[3]; -+ int ret; -+ -+ if (readl(clk_ctrl->base) & REVISION_ID) { -+ if ((readl(clk_ctrl->base + SCU1_MAC12_CLK_DLY) & GENMASK(25, 0)) == 0) -+ reg[0] = AST2700_DEF_MAC12_DELAY_1G_A1; -+ else -+ reg[0] = readl(clk_ctrl->base + SCU1_MAC12_CLK_DLY); -+ } else { -+ reg[0] = AST2700_DEF_MAC12_DELAY_1G_A0; -+ } -+ reg[1] = AST2700_DEF_MAC12_DELAY_100M; -+ reg[2] = AST2700_DEF_MAC12_DELAY_10M; -+ -+ ret = of_property_read_u32_array(np, "mac0-clk-delay", (u32 *)&mac_cfg, -+ sizeof(mac_cfg) / sizeof(u32)); -+ if (!ret) { -+ reg[0] &= ~(MAC_CLK_1G_INPUT_DELAY_1 | MAC_CLK_1G_OUTPUT_DELAY_1); -+ reg[0] |= FIELD_PREP(MAC_CLK_1G_INPUT_DELAY_1, mac_cfg.rx_delay_1000) | -+ FIELD_PREP(MAC_CLK_1G_OUTPUT_DELAY_1, mac_cfg.tx_delay_1000); -+ -+ reg[1] &= ~(MAC_CLK_100M_10M_INPUT_DELAY_1 | MAC_CLK_100M_10M_OUTPUT_DELAY_1); -+ reg[1] |= FIELD_PREP(MAC_CLK_100M_10M_INPUT_DELAY_1, mac_cfg.rx_delay_100) | -+ FIELD_PREP(MAC_CLK_100M_10M_OUTPUT_DELAY_1, mac_cfg.tx_delay_100); -+ -+ reg[2] &= ~(MAC_CLK_100M_10M_INPUT_DELAY_1 | MAC_CLK_100M_10M_OUTPUT_DELAY_1); -+ reg[2] |= FIELD_PREP(MAC_CLK_100M_10M_INPUT_DELAY_1, mac_cfg.rx_delay_10) | -+ FIELD_PREP(MAC_CLK_100M_10M_OUTPUT_DELAY_1, mac_cfg.tx_delay_10); -+ } -+ -+ ret = of_property_read_u32_array(np, "mac1-clk-delay", (u32 *)&mac_cfg, -+ sizeof(mac_cfg) / sizeof(u32)); -+ if (!ret) { -+ reg[0] &= ~(MAC_CLK_1G_INPUT_DELAY_2 | MAC_CLK_1G_OUTPUT_DELAY_2); -+ reg[0] |= FIELD_PREP(MAC_CLK_1G_INPUT_DELAY_2, mac_cfg.rx_delay_1000) | -+ FIELD_PREP(MAC_CLK_1G_OUTPUT_DELAY_2, mac_cfg.tx_delay_1000); -+ -+ reg[1] &= ~(MAC_CLK_100M_10M_INPUT_DELAY_2 | MAC_CLK_100M_10M_OUTPUT_DELAY_2); -+ reg[1] |= FIELD_PREP(MAC_CLK_100M_10M_INPUT_DELAY_2, mac_cfg.rx_delay_100) | -+ FIELD_PREP(MAC_CLK_100M_10M_OUTPUT_DELAY_2, mac_cfg.tx_delay_100); -+ -+ reg[2] &= ~(MAC_CLK_100M_10M_INPUT_DELAY_2 | MAC_CLK_100M_10M_OUTPUT_DELAY_2); -+ reg[2] |= FIELD_PREP(MAC_CLK_100M_10M_INPUT_DELAY_2, mac_cfg.rx_delay_10) | -+ FIELD_PREP(MAC_CLK_100M_10M_OUTPUT_DELAY_2, mac_cfg.tx_delay_10); -+ } -+ -+ reg[0] |= (readl(clk_ctrl->base + SCU1_MAC12_CLK_DLY) & ~GENMASK(25, 0)); -+ writel(reg[0], clk_ctrl->base + SCU1_MAC12_CLK_DLY); -+ writel(reg[1], clk_ctrl->base + SCU1_MAC12_CLK_DLY_100M); -+ writel(reg[2], clk_ctrl->base + SCU1_MAC12_CLK_DLY_10M); -+} -+ +static void ast2700_soc1_configure_i3c_clk(struct ast2700_clk_ctrl *clk_ctrl) +{ + if (readl(clk_ctrl->base) & REVISION_ID) { @@ -26345,7 +32443,6 @@ diff --git a/drivers/clk/clk-ast2700.c b/drivers/clk/clk-ast2700.c + writel(val | uart_clk_source, clk_base + SCU1_CLK_SEL1); + } + -+ ast2700_soc1_configure_mac01_clk(clk_ctrl); + ast2700_soc1_configure_i3c_clk(clk_ctrl); + } + @@ -26498,7 +32595,7 @@ diff --git a/drivers/clk/clk-ast2700.c b/drivers/clk/clk-ast2700.c +arch_initcall(clk_ast2700_init); diff --git a/drivers/crypto/aspeed/Kconfig b/drivers/crypto/aspeed/Kconfig --- a/drivers/crypto/aspeed/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/crypto/aspeed/Kconfig 2025-12-23 10:16:21.140032401 +0000 ++++ b/drivers/crypto/aspeed/Kconfig 2026-04-08 18:03:48.327705009 +0000 @@ -1,26 +1,32 @@ config CRYPTO_DEV_ASPEED - tristate "Support for Aspeed cryptographic engine driver" @@ -26603,7 +32700,7 @@ diff --git a/drivers/crypto/aspeed/Kconfig b/drivers/crypto/aspeed/Kconfig +endif #CRYPTO_DEV_ASPEED diff --git a/drivers/crypto/aspeed/Makefile b/drivers/crypto/aspeed/Makefile --- a/drivers/crypto/aspeed/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/crypto/aspeed/Makefile 2025-12-23 10:16:21.140032401 +0000 ++++ b/drivers/crypto/aspeed/Makefile 2026-04-08 18:03:48.327705009 +0000 @@ -1,11 +1,14 @@ hace-hash-$(CONFIG_CRYPTO_DEV_ASPEED_HACE_HASH) := aspeed-hace-hash.o hace-crypto-$(CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO) := aspeed-hace-crypto.o @@ -26624,7 +32721,7 @@ diff --git a/drivers/crypto/aspeed/Makefile b/drivers/crypto/aspeed/Makefile +obj-$(CONFIG_CRYPTO_DEV_ASPEED_ECDSA) += aspeed-ecdsa.o diff --git a/drivers/crypto/aspeed/aspeed-acry.c b/drivers/crypto/aspeed/aspeed-acry.c --- a/drivers/crypto/aspeed/aspeed-acry.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-acry.c 2025-12-23 10:16:21.140032401 +0000 ++++ b/drivers/crypto/aspeed/aspeed-acry.c 2026-04-08 18:03:48.327705009 +0000 @@ -808,7 +808,7 @@ static struct platform_driver aspeed_acry_driver = { @@ -26636,7 +32733,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-acry.c b/drivers/crypto/aspeed/aspeed- .of_match_table = aspeed_acry_of_matches, diff --git a/drivers/crypto/aspeed/aspeed-ecdsa.c b/drivers/crypto/aspeed/aspeed-ecdsa.c --- a/drivers/crypto/aspeed/aspeed-ecdsa.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-ecdsa.c 2025-12-23 10:16:21.140032401 +0000 ++++ b/drivers/crypto/aspeed/aspeed-ecdsa.c 2026-04-08 18:03:48.327705009 +0000 @@ -0,0 +1,779 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* @@ -27419,7 +33516,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-ecdsa.c b/drivers/crypto/aspeed/aspeed +MODULE_LICENSE("GPL"); diff --git a/drivers/crypto/aspeed/aspeed-ecdsa.h b/drivers/crypto/aspeed/aspeed-ecdsa.h --- a/drivers/crypto/aspeed/aspeed-ecdsa.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-ecdsa.h 2025-12-23 10:16:21.140032401 +0000 ++++ b/drivers/crypto/aspeed/aspeed-ecdsa.h 2026-04-08 18:03:48.327705009 +0000 @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + @@ -27542,7 +33639,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-ecdsa.h b/drivers/crypto/aspeed/aspeed +#endif diff --git a/drivers/crypto/aspeed/aspeed-hace-crypto.c b/drivers/crypto/aspeed/aspeed-hace-crypto.c --- a/drivers/crypto/aspeed/aspeed-hace-crypto.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-hace-crypto.c 2025-12-23 10:16:21.140032401 +0000 ++++ b/drivers/crypto/aspeed/aspeed-hace-crypto.c 2026-04-08 18:03:48.327705009 +0000 @@ -24,6 +24,11 @@ dev_dbg((h)->dev, "%s() " fmt, __func__, ##__VA_ARGS__) #endif @@ -27860,7 +33957,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace-crypto.c b/drivers/crypto/aspeed/ +} diff --git a/drivers/crypto/aspeed/aspeed-hace-hash.c b/drivers/crypto/aspeed/aspeed-hace-hash.c --- a/drivers/crypto/aspeed/aspeed-hace-hash.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-hace-hash.c 2025-12-23 10:16:21.140032401 +0000 ++++ b/drivers/crypto/aspeed/aspeed-hace-hash.c 2026-04-08 18:03:48.327705009 +0000 @@ -138,21 +138,13 @@ return -EINVAL; } @@ -28335,7 +34432,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace-hash.c b/drivers/crypto/aspeed/as +} diff --git a/drivers/crypto/aspeed/aspeed-hace.c b/drivers/crypto/aspeed/aspeed-hace.c --- a/drivers/crypto/aspeed/aspeed-hace.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-hace.c 2025-12-23 10:16:21.140032401 +0000 ++++ b/drivers/crypto/aspeed/aspeed-hace.c 2026-04-08 18:03:48.327705009 +0000 @@ -6,15 +6,18 @@ #include "aspeed-hace.h" #include @@ -28667,7 +34764,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.c b/drivers/crypto/aspeed/aspeed- .of_match_table = aspeed_hace_of_matches, diff --git a/drivers/crypto/aspeed/aspeed-hace.h b/drivers/crypto/aspeed/aspeed-hace.h --- a/drivers/crypto/aspeed/aspeed-hace.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-hace.h 2025-12-23 10:16:21.140032401 +0000 ++++ b/drivers/crypto/aspeed/aspeed-hace.h 2026-04-08 18:03:48.327705009 +0000 @@ -10,6 +10,8 @@ #include #include @@ -28815,7 +34912,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.h b/drivers/crypto/aspeed/aspeed- #endif diff --git a/drivers/crypto/aspeed/aspeed-rsss-hash.c b/drivers/crypto/aspeed/aspeed-rsss-hash.c --- a/drivers/crypto/aspeed/aspeed-rsss-hash.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-rsss-hash.c 2025-12-23 10:16:21.140032401 +0000 ++++ b/drivers/crypto/aspeed/aspeed-rsss-hash.c 2026-04-08 18:03:48.327705009 +0000 @@ -0,0 +1,901 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* @@ -29720,8 +35817,8 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss-hash.c b/drivers/crypto/aspeed/as +} diff --git a/drivers/crypto/aspeed/aspeed-rsss-rsa.c b/drivers/crypto/aspeed/aspeed-rsss-rsa.c --- a/drivers/crypto/aspeed/aspeed-rsss-rsa.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-rsss-rsa.c 2025-12-23 10:16:21.140032401 +0000 -@@ -0,0 +1,608 @@ ++++ b/drivers/crypto/aspeed/aspeed-rsss-rsa.c 2026-04-08 18:03:48.328704991 +0000 +@@ -0,0 +1,575 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 Aspeed Technology Inc. @@ -29973,7 +36070,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss-rsa.c b/drivers/crypto/aspeed/asp + cipher = crypto_akcipher_reqtfm(req); + ctx = akcipher_tfm_ctx(cipher); + -+ if (!ctx->n || !ctx->n_sz) { ++ if (!ctx->n_sz) { + dev_err(rsss_dev->dev, "%s: key n is not set\n", __func__); + return -EINVAL; + } @@ -29997,7 +36094,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss-rsa.c b/drivers/crypto/aspeed/asp + req->dst_len = nm / 8; + + if (ctx->enc) { -+ if (!ctx->e || !ctx->e_sz) { ++ if (!ctx->e_sz) { + dev_err(rsss_dev->dev, "%s: key e is not set\n", + __func__); + return -EINVAL; @@ -30007,7 +36104,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss-rsa.c b/drivers/crypto/aspeed/asp + ctx->e, ctx->e_sz, + ASPEED_RSA_EXP_MODE); + } else { -+ if (!ctx->d || !ctx->d_sz) { ++ if (!ctx->d_sz) { + dev_err(rsss_dev->dev, "%s: key d is not set\n", + __func__); + return -EINVAL; @@ -30065,18 +36162,11 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss-rsa.c b/drivers/crypto/aspeed/asp + return aspeed_rsa_handle_queue(rsss_dev, req); +} + -+static u8 *aspeed_rsa_key_copy(u8 *src, size_t len) -+{ -+ return kmemdup(src, len, GFP_KERNEL); -+} -+ +static int aspeed_rsa_set_n(struct aspeed_rsa_ctx *ctx, u8 *value, + size_t len) +{ + ctx->n_sz = len; -+ ctx->n = aspeed_rsa_key_copy(value, len); -+ if (!ctx->n) -+ return -ENOMEM; ++ memcpy(ctx->n, value, len); + + return 0; +} @@ -30085,9 +36175,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss-rsa.c b/drivers/crypto/aspeed/asp + size_t len) +{ + ctx->e_sz = len; -+ ctx->e = aspeed_rsa_key_copy(value, len); -+ if (!ctx->e) -+ return -ENOMEM; ++ memcpy(ctx->e, value, len); + + return 0; +} @@ -30096,23 +36184,11 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss-rsa.c b/drivers/crypto/aspeed/asp + size_t len) +{ + ctx->d_sz = len; -+ ctx->d = aspeed_rsa_key_copy(value, len); -+ if (!ctx->d) -+ return -ENOMEM; ++ memcpy(ctx->d, value, len); + + return 0; +} + -+static void aspeed_rsa_key_free(struct aspeed_rsa_ctx *ctx) -+{ -+ kfree_sensitive(ctx->n); -+ kfree_sensitive(ctx->e); -+ kfree_sensitive(ctx->d); -+ ctx->n_sz = 0; -+ ctx->e_sz = 0; -+ ctx->d_sz = 0; -+} -+ +static int aspeed_rsa_setkey(struct crypto_akcipher *tfm, const void *key, + unsigned int keylen, int priv) +{ @@ -30140,29 +36216,17 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss-rsa.c b/drivers/crypto/aspeed/asp + return 0; + + hexdump("n", (u8 *)ctx->key.n, ctx->key.n_sz); -+ ret = aspeed_rsa_set_n(ctx, (u8 *)ctx->key.n, ctx->key.n_sz); -+ if (ret) -+ goto err; ++ aspeed_rsa_set_n(ctx, (u8 *)ctx->key.n, ctx->key.n_sz); + + hexdump("e", (u8 *)ctx->key.e, ctx->key.e_sz); -+ ret = aspeed_rsa_set_e(ctx, (u8 *)ctx->key.e, ctx->key.e_sz); -+ if (ret) -+ goto err; ++ aspeed_rsa_set_e(ctx, (u8 *)ctx->key.e, ctx->key.e_sz); + + if (priv) { + hexdump("d", (u8 *)ctx->key.d, ctx->key.d_sz); -+ ret = aspeed_rsa_set_d(ctx, (u8 *)ctx->key.d, ctx->key.d_sz); -+ if (ret) -+ goto err; ++ aspeed_rsa_set_d(ctx, (u8 *)ctx->key.d, ctx->key.d_sz); + } + + return 0; -+ -+err: -+ dev_err(rsss_dev->dev, "rsss set key failed\n"); -+ aspeed_rsa_key_free(ctx); -+ -+ return ret; +} + +static int aspeed_rsa_set_pub_key(struct crypto_akcipher *tfm, @@ -30332,7 +36396,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss-rsa.c b/drivers/crypto/aspeed/asp +} diff --git a/drivers/crypto/aspeed/aspeed-rsss.c b/drivers/crypto/aspeed/aspeed-rsss.c --- a/drivers/crypto/aspeed/aspeed-rsss.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-rsss.c 2025-12-23 10:16:21.140032401 +0000 ++++ b/drivers/crypto/aspeed/aspeed-rsss.c 2026-04-08 18:03:48.328704991 +0000 @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* @@ -30524,7 +36588,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss.c b/drivers/crypto/aspeed/aspeed- +MODULE_LICENSE("GPL"); diff --git a/drivers/crypto/aspeed/aspeed-rsss.h b/drivers/crypto/aspeed/aspeed-rsss.h --- a/drivers/crypto/aspeed/aspeed-rsss.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-rsss.h 2025-12-23 10:16:21.141032385 +0000 ++++ b/drivers/crypto/aspeed/aspeed-rsss.h 2026-04-08 18:03:48.328704991 +0000 @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + @@ -30735,9 +36799,9 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss.h b/drivers/crypto/aspeed/aspeed- + + struct rsa_key key; + int enc; -+ u8 *n; -+ u8 *e; -+ u8 *d; ++ u8 n[ASPEED_RSA_MAX_KEY_LEN]; ++ u8 e[ASPEED_RSA_MAX_KEY_LEN]; ++ u8 d[ASPEED_RSA_MAX_KEY_LEN]; + size_t n_sz; + size_t e_sz; + size_t d_sz; @@ -30802,8 +36866,8 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss.h b/drivers/crypto/aspeed/aspeed- + +#endif /* __ASPEED_RSSS_H__ */ diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig ---- a/drivers/edac/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/edac/Kconfig 2025-12-23 10:16:21.149032250 +0000 +--- a/drivers/edac/Kconfig 2026-04-08 18:03:23.257162538 +0000 ++++ b/drivers/edac/Kconfig 2026-04-08 18:03:33.347978488 +0000 @@ -520,6 +520,15 @@ First, ECC must be configured in the bootloader. Then, this driver will expose error counters via the EDAC kernel framework. @@ -30820,276 +36884,9 @@ diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig config EDAC_BLUEFIELD tristate "Mellanox BlueField Memory ECC" depends on ARM64 && ((MELLANOX_PLATFORM && ACPI) || COMPILE_TEST) -diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c ---- a/drivers/edac/altera_edac.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/edac/altera_edac.c 2025-12-23 10:16:21.149032250 +0000 -@@ -128,7 +128,6 @@ - - ptemp = dma_alloc_coherent(mci->pdev, 16, &dma_handle, GFP_KERNEL); - if (!ptemp) { -- dma_free_coherent(mci->pdev, 16, ptemp, dma_handle); - edac_printk(KERN_ERR, EDAC_MC, - "Inject: Buffer Allocation error\n"); - return -ENOMEM; -diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c ---- a/drivers/edac/edac_mc_sysfs.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/edac/edac_mc_sysfs.c 2025-12-23 10:16:21.151032217 +0000 -@@ -305,6 +305,14 @@ - channel_dimm_label_show, channel_dimm_label_store, 10); - DEVICE_CHANNEL(ch11_dimm_label, S_IRUGO | S_IWUSR, - channel_dimm_label_show, channel_dimm_label_store, 11); -+DEVICE_CHANNEL(ch12_dimm_label, S_IRUGO | S_IWUSR, -+ channel_dimm_label_show, channel_dimm_label_store, 12); -+DEVICE_CHANNEL(ch13_dimm_label, S_IRUGO | S_IWUSR, -+ channel_dimm_label_show, channel_dimm_label_store, 13); -+DEVICE_CHANNEL(ch14_dimm_label, S_IRUGO | S_IWUSR, -+ channel_dimm_label_show, channel_dimm_label_store, 14); -+DEVICE_CHANNEL(ch15_dimm_label, S_IRUGO | S_IWUSR, -+ channel_dimm_label_show, channel_dimm_label_store, 15); - - /* Total possible dynamic DIMM Label attribute file table */ - static struct attribute *dynamic_csrow_dimm_attr[] = { -@@ -320,6 +328,10 @@ - &dev_attr_legacy_ch9_dimm_label.attr.attr, - &dev_attr_legacy_ch10_dimm_label.attr.attr, - &dev_attr_legacy_ch11_dimm_label.attr.attr, -+ &dev_attr_legacy_ch12_dimm_label.attr.attr, -+ &dev_attr_legacy_ch13_dimm_label.attr.attr, -+ &dev_attr_legacy_ch14_dimm_label.attr.attr, -+ &dev_attr_legacy_ch15_dimm_label.attr.attr, - NULL - }; - -@@ -348,6 +360,14 @@ - channel_ce_count_show, NULL, 10); - DEVICE_CHANNEL(ch11_ce_count, S_IRUGO, - channel_ce_count_show, NULL, 11); -+DEVICE_CHANNEL(ch12_ce_count, S_IRUGO, -+ channel_ce_count_show, NULL, 12); -+DEVICE_CHANNEL(ch13_ce_count, S_IRUGO, -+ channel_ce_count_show, NULL, 13); -+DEVICE_CHANNEL(ch14_ce_count, S_IRUGO, -+ channel_ce_count_show, NULL, 14); -+DEVICE_CHANNEL(ch15_ce_count, S_IRUGO, -+ channel_ce_count_show, NULL, 15); - - /* Total possible dynamic ce_count attribute file table */ - static struct attribute *dynamic_csrow_ce_count_attr[] = { -@@ -363,6 +383,10 @@ - &dev_attr_legacy_ch9_ce_count.attr.attr, - &dev_attr_legacy_ch10_ce_count.attr.attr, - &dev_attr_legacy_ch11_ce_count.attr.attr, -+ &dev_attr_legacy_ch12_ce_count.attr.attr, -+ &dev_attr_legacy_ch13_ce_count.attr.attr, -+ &dev_attr_legacy_ch14_ce_count.attr.attr, -+ &dev_attr_legacy_ch15_ce_count.attr.attr, - NULL - }; - -diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c ---- a/drivers/edac/i10nm_base.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/edac/i10nm_base.c 2025-12-23 10:16:21.152032200 +0000 -@@ -967,6 +967,15 @@ - return !!GET_BITFIELD(mcmtr, 2, 2); - } - -+static bool i10nm_channel_disabled(struct skx_imc *imc, int chan) -+{ -+ u32 mcmtr = I10NM_GET_MCMTR(imc, chan); -+ -+ edac_dbg(1, "mc%d ch%d mcmtr reg %x\n", imc->mc, chan, mcmtr); -+ -+ return (mcmtr == ~0 || GET_BITFIELD(mcmtr, 18, 18)); -+} -+ - static int i10nm_get_dimm_config(struct mem_ctl_info *mci, - struct res_config *cfg) - { -@@ -980,6 +989,11 @@ - if (!imc->mbase) - continue; - -+ if (i10nm_channel_disabled(imc, i)) { -+ edac_dbg(1, "mc%d ch%d is disabled.\n", imc->mc, i); -+ continue; -+ } -+ - ndimms = 0; - - if (res_cfg->type != GNR) -diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c ---- a/drivers/edac/synopsys_edac.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/edac/synopsys_edac.c 2025-12-23 10:16:21.155032150 +0000 -@@ -332,20 +332,26 @@ - #endif - }; - -+enum synps_platform_type { -+ ZYNQ, -+ ZYNQMP, -+ SYNPS, -+}; -+ - /** - * struct synps_platform_data - synps platform data structure. -+ * @platform: Identifies the target hardware platform - * @get_error_info: Get EDAC error info. - * @get_mtype: Get mtype. - * @get_dtype: Get dtype. -- * @get_ecc_state: Get ECC state. - * @get_mem_info: Get EDAC memory info - * @quirks: To differentiate IPs. - */ - struct synps_platform_data { -+ enum synps_platform_type platform; - int (*get_error_info)(struct synps_edac_priv *priv); - enum mem_type (*get_mtype)(const void __iomem *base); - enum dev_type (*get_dtype)(const void __iomem *base); -- bool (*get_ecc_state)(void __iomem *base); - #ifdef CONFIG_EDAC_DEBUG - u64 (*get_mem_info)(struct synps_edac_priv *priv); - #endif -@@ -720,51 +726,38 @@ - return dt; - } - --/** -- * zynq_get_ecc_state - Return the controller ECC enable/disable status. -- * @base: DDR memory controller base address. -- * -- * Get the ECC enable/disable status of the controller. -- * -- * Return: true if enabled, otherwise false. -- */ --static bool zynq_get_ecc_state(void __iomem *base) --{ -- enum dev_type dt; -- u32 ecctype; -- -- dt = zynq_get_dtype(base); -- if (dt == DEV_UNKNOWN) -- return false; -- -- ecctype = readl(base + SCRUB_OFST) & SCRUB_MODE_MASK; -- if ((ecctype == SCRUB_MODE_SECDED) && (dt == DEV_X2)) -- return true; -- -- return false; --} -- --/** -- * zynqmp_get_ecc_state - Return the controller ECC enable/disable status. -- * @base: DDR memory controller base address. -- * -- * Get the ECC enable/disable status for the controller. -- * -- * Return: a ECC status boolean i.e true/false - enabled/disabled. -- */ --static bool zynqmp_get_ecc_state(void __iomem *base) -+static bool get_ecc_state(struct synps_edac_priv *priv) - { -+ u32 ecctype, clearval; - enum dev_type dt; -- u32 ecctype; - -- dt = zynqmp_get_dtype(base); -- if (dt == DEV_UNKNOWN) -- return false; -- -- ecctype = readl(base + ECC_CFG0_OFST) & SCRUB_MODE_MASK; -- if ((ecctype == SCRUB_MODE_SECDED) && -- ((dt == DEV_X2) || (dt == DEV_X4) || (dt == DEV_X8))) -- return true; -+ if (priv->p_data->platform == ZYNQ) { -+ dt = zynq_get_dtype(priv->baseaddr); -+ if (dt == DEV_UNKNOWN) -+ return false; -+ -+ ecctype = readl(priv->baseaddr + SCRUB_OFST) & SCRUB_MODE_MASK; -+ if (ecctype == SCRUB_MODE_SECDED && dt == DEV_X2) { -+ clearval = ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_UE_ERR; -+ writel(clearval, priv->baseaddr + ECC_CTRL_OFST); -+ writel(0x0, priv->baseaddr + ECC_CTRL_OFST); -+ return true; -+ } -+ } else { -+ dt = zynqmp_get_dtype(priv->baseaddr); -+ if (dt == DEV_UNKNOWN) -+ return false; -+ -+ ecctype = readl(priv->baseaddr + ECC_CFG0_OFST) & SCRUB_MODE_MASK; -+ if (ecctype == SCRUB_MODE_SECDED && -+ (dt == DEV_X2 || dt == DEV_X4 || dt == DEV_X8)) { -+ clearval = readl(priv->baseaddr + ECC_CLR_OFST) | -+ ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT | -+ ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT; -+ writel(clearval, priv->baseaddr + ECC_CLR_OFST); -+ return true; -+ } -+ } - - return false; - } -@@ -934,18 +927,18 @@ - } - - static const struct synps_platform_data zynq_edac_def = { -+ .platform = ZYNQ, - .get_error_info = zynq_get_error_info, - .get_mtype = zynq_get_mtype, - .get_dtype = zynq_get_dtype, -- .get_ecc_state = zynq_get_ecc_state, - .quirks = 0, - }; - - static const struct synps_platform_data zynqmp_edac_def = { -+ .platform = ZYNQMP, - .get_error_info = zynqmp_get_error_info, - .get_mtype = zynqmp_get_mtype, - .get_dtype = zynqmp_get_dtype, -- .get_ecc_state = zynqmp_get_ecc_state, - #ifdef CONFIG_EDAC_DEBUG - .get_mem_info = zynqmp_get_mem_info, - #endif -@@ -957,10 +950,10 @@ - }; - - static const struct synps_platform_data synopsys_edac_def = { -+ .platform = SYNPS, - .get_error_info = zynqmp_get_error_info, - .get_mtype = zynqmp_get_mtype, - .get_dtype = zynqmp_get_dtype, -- .get_ecc_state = zynqmp_get_ecc_state, - .quirks = (DDR_ECC_INTR_SUPPORT | DDR_ECC_INTR_SELF_CLEAR - #ifdef CONFIG_EDAC_DEBUG - | DDR_ECC_DATA_POISON_SUPPORT -@@ -1390,10 +1383,6 @@ - if (!p_data) - return -ENODEV; - -- if (!p_data->get_ecc_state(baseaddr)) { -- edac_printk(KERN_INFO, EDAC_MC, "ECC not enabled\n"); -- return -ENXIO; -- } - - layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; - layers[0].size = SYNPS_EDAC_NR_CSROWS; -@@ -1413,6 +1402,12 @@ - priv = mci->pvt_info; - priv->baseaddr = baseaddr; - priv->p_data = p_data; -+ if (!get_ecc_state(priv)) { -+ edac_printk(KERN_INFO, EDAC_MC, "ECC not enabled\n"); -+ rc = -ENODEV; -+ goto free_edac_mc; -+ } -+ - spin_lock_init(&priv->reglock); - - mc_init(mci, pdev); diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c --- a/drivers/fsi/fsi-master-aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/fsi/fsi-master-aspeed.c 2025-12-23 10:16:20.956035485 +0000 ++++ b/drivers/fsi/fsi-master-aspeed.c 2026-04-08 18:03:48.122708755 +0000 @@ -3,6 +3,7 @@ // FSI master driver for AST2600 @@ -31204,7 +37001,7 @@ diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c MODULE_DEVICE_TABLE(of, fsi_master_aspeed_match); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig --- a/drivers/gpio/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpio/Kconfig 2025-12-23 10:16:09.425228786 +0000 ++++ b/drivers/gpio/Kconfig 2026-04-08 18:03:35.024947902 +0000 @@ -180,6 +180,14 @@ help Say Y here to support Aspeed AST2500 SGPIO functionality. @@ -31222,7 +37019,7 @@ diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig default y if ATH79 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile --- a/drivers/gpio/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpio/Makefile 2025-12-23 10:16:13.585159025 +0000 ++++ b/drivers/gpio/Makefile 2026-04-08 18:03:39.921858587 +0000 @@ -36,6 +36,7 @@ obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o @@ -31233,7 +37030,7 @@ diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile obj-$(CONFIG_GPIO_BCM_XGS_IPROC) += gpio-xgs-iproc.o diff --git a/drivers/gpio/gpio-aspeed-ltpi.c b/drivers/gpio/gpio-aspeed-ltpi.c --- a/drivers/gpio/gpio-aspeed-ltpi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/gpio/gpio-aspeed-ltpi.c 2025-12-23 10:16:21.060033742 +0000 ++++ b/drivers/gpio/gpio-aspeed-ltpi.c 2026-04-08 18:03:48.238706636 +0000 @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0 +/* @@ -31706,12 +37503,24 @@ diff --git a/drivers/gpio/gpio-aspeed-ltpi.c b/drivers/gpio/gpio-aspeed-ltpi.c +MODULE_DESCRIPTION("Aspeed LTPI GPIO Driver"); diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c --- a/drivers/gpio/gpio-aspeed-sgpio.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpio/gpio-aspeed-sgpio.c 2025-12-23 10:16:21.056033809 +0000 -@@ -18,7 +18,51 @@ ++++ b/drivers/gpio/gpio-aspeed-sgpio.c 2026-04-08 18:03:48.233706727 +0000 +@@ -6,6 +6,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -18,7 +19,35 @@ #include #include -#define ASPEED_SGPIO_CTRL 0x54 ++/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */ ++#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) ++#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) ++ +#define SGPIO_G7_IRQ_STS_BASE 0x40 +#define SGPIO_G7_IRQ_STS_OFFSET(x) (SGPIO_G7_IRQ_STS_BASE + (x) * 0x4) +#define SGPIO_G7_CTRL_REG_BASE 0x80 @@ -31733,502 +37542,598 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c +#define SGPIO_G7_PARALLEL_OUT_SEL GENMASK(19, 18) +#define SELECT_FROM_CSR 0 +#define SELECT_FROM_PARALLEL_IN 1 -+#define SELECT_FROM_SERIAL_IN 1 -+ -+#define BMC_CONTROL_START_INDEX 128 -+#define BMC_CONTROL_END_INDEX 143 -+ -+static inline u32 field_get(u32 _mask, u32 _val) -+{ -+ return (((_val) & (_mask)) >> (ffs(_mask) - 1)); -+} -+ -+static inline u32 field_prep(u32 _mask, u32 _val) -+{ -+ return (((_val) << (ffs(_mask) - 1)) & (_mask)); -+} ++#define SELECT_FROM_SERIAL_IN 2 + -+static inline void ast_write_bits(void __iomem *addr, u32 mask, u32 val) -+{ -+ iowrite32((ioread32(addr) & ~(mask)) | field_prep(mask, val), addr); -+} -+ -+static inline void ast_clr_bits(void __iomem *addr, u32 mask) -+{ -+ iowrite32((ioread32(addr) & ~(mask)), addr); -+} ++#define ASPEED_SGPIO_G4_CFG_OFFSET 0x54 ++#define ASPEED_SGPIO_G7_CFG_OFFSET 0x0 #define ASPEED_SGPIO_CLK_DIV_MASK GENMASK(31, 16) #define ASPEED_SGPIO_ENABLE BIT(0) -@@ -26,6 +70,9 @@ +@@ -26,6 +55,8 @@ struct aspeed_sgpio_pdata { const u32 pin_mask; -+ const u16 ctrl_reg; -+ const int version; -+ const bool slave; ++ const struct aspeed_sgpio_llops *llops; ++ const u32 cfg_offset; }; struct aspeed_sgpio { -@@ -35,6 +82,8 @@ +@@ -35,6 +66,7 @@ raw_spinlock_t lock; void __iomem *base; int irq; -+ int version; + const struct aspeed_sgpio_pdata *pdata; }; struct aspeed_sgpio_bank { -@@ -166,19 +215,39 @@ - return !(offset % 2); +@@ -42,7 +74,6 @@ + u16 rdata_reg; + u16 irq_regs; + u16 tolerance_regs; +- const char names[4][3]; + }; + + /* +@@ -58,28 +89,24 @@ + .rdata_reg = 0x0070, + .irq_regs = 0x0004, + .tolerance_regs = 0x0018, +- .names = { "A", "B", "C", "D" }, + }, + { + .val_regs = 0x001C, + .rdata_reg = 0x0074, + .irq_regs = 0x0020, + .tolerance_regs = 0x0034, +- .names = { "E", "F", "G", "H" }, + }, + { + .val_regs = 0x0038, + .rdata_reg = 0x0078, + .irq_regs = 0x003C, + .tolerance_regs = 0x0050, +- .names = { "I", "J", "K", "L" }, + }, + { + .val_regs = 0x0090, + .rdata_reg = 0x007C, + .irq_regs = 0x0094, + .tolerance_regs = 0x00A8, +- .names = { "M", "N", "O", "P" }, + }, + }; + +@@ -94,6 +121,15 @@ + reg_tolerance, + }; + ++struct aspeed_sgpio_llops { ++ void (*reg_bit_set)(struct aspeed_sgpio *gpio, unsigned int offset, ++ const enum aspeed_sgpio_reg reg, bool val); ++ bool (*reg_bit_get)(struct aspeed_sgpio *gpio, unsigned int offset, ++ const enum aspeed_sgpio_reg reg); ++ int (*reg_bank_get)(struct aspeed_sgpio *gpio, unsigned int offset, ++ const enum aspeed_sgpio_reg reg); ++}; ++ + #define GPIO_VAL_VALUE 0x00 + #define GPIO_IRQ_ENABLE 0x00 + #define GPIO_IRQ_TYPE0 0x04 +@@ -101,9 +137,9 @@ + #define GPIO_IRQ_TYPE2 0x0C + #define GPIO_IRQ_STATUS 0x10 + +-static void __iomem *bank_reg(struct aspeed_sgpio *gpio, +- const struct aspeed_sgpio_bank *bank, +- const enum aspeed_sgpio_reg reg) ++static void __iomem *aspeed_sgpio_g4_bank_reg(struct aspeed_sgpio *gpio, ++ const struct aspeed_sgpio_bank *bank, ++ const enum aspeed_sgpio_reg reg) + { + switch (reg) { + case reg_val: +@@ -128,6 +164,30 @@ + } } -+static bool aspeed_sgpios_ctrl_by_csr(unsigned int offset) ++static u32 aspeed_sgpio_g7_reg_mask(const enum aspeed_sgpio_reg reg) +{ -+ if (offset >= BMC_CONTROL_START_INDEX && -+ offset <= BMC_CONTROL_END_INDEX) -+ return true; -+ return false; ++ switch (reg) { ++ case reg_val: ++ case reg_rdata: ++ return SGPIO_G7_OUT_DATA; ++ case reg_irq_enable: ++ return SGPIO_G7_IRQ_EN; ++ case reg_irq_type0: ++ return SGPIO_G7_IRQ_TYPE0; ++ case reg_irq_type1: ++ return SGPIO_G7_IRQ_TYPE1; ++ case reg_irq_type2: ++ return SGPIO_G7_IRQ_TYPE2; ++ case reg_irq_status: ++ return SGPIO_G7_IRQ_STS; ++ case reg_tolerance: ++ return SGPIO_G7_RST_TOLERANCE; ++ default: ++ WARN_ON_ONCE(1); ++ return 0; ++ } +} + + #define GPIO_BANK(x) ((x) >> 6) + #define GPIO_OFFSET(x) ((x) & GENMASK(5, 0)) + #define GPIO_BIT(x) BIT(GPIO_OFFSET(x) >> 1) +@@ -169,17 +229,13 @@ static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset) { struct aspeed_sgpio *gpio = gpiochip_get_data(gc); - const struct aspeed_sgpio_bank *bank = to_bank(offset); -+ const struct aspeed_sgpio_bank *bank; -+ void __iomem *addr = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(offset >> 1); - unsigned long flags; +- unsigned long flags; enum aspeed_sgpio_reg reg; int rc = 0; - raw_spin_lock_irqsave(&gpio->lock, flags); +- raw_spin_lock_irqsave(&gpio->lock, flags); ++ guard(raw_spinlock_irqsave)(&gpio->lock); -- reg = aspeed_sgpio_is_input(offset) ? reg_val : reg_rdata; + reg = aspeed_sgpio_is_input(offset) ? reg_val : reg_rdata; - rc = !!(ioread32(bank_reg(gpio, bank, reg)) & GPIO_BIT(offset)); - -+ if (gpio->version == 7) { -+ if (gpio->pdata->slave && !aspeed_sgpios_ctrl_by_csr(offset)) -+ reg = aspeed_sgpio_is_input(offset) ? -+ SGPIO_G7_PARALLEL_IN_DATA : -+ SGPIO_G7_PARALLEL_OUT_DATA; -+ else -+ reg = aspeed_sgpio_is_input(offset) ? SGPIO_G7_IN_DATA : -+ SGPIO_G7_OUT_DATA; -+ rc = !!(field_get(reg, ioread32(addr))); -+ } else { -+ bank = to_bank(offset); -+ reg = aspeed_sgpio_is_input(offset) ? reg_val : reg_rdata; -+ rc = !!(ioread32(bank_reg(gpio, bank, reg)) & GPIO_BIT(offset)); -+ } - raw_spin_unlock_irqrestore(&gpio->lock, flags); +- raw_spin_unlock_irqrestore(&gpio->lock, flags); ++ rc = gpio->pdata->llops->reg_bit_get(gpio, offset, reg); + + return rc; + } +@@ -187,26 +243,11 @@ + static int sgpio_set_value(struct gpio_chip *gc, unsigned int offset, int val) + { + struct aspeed_sgpio *gpio = gpiochip_get_data(gc); +- const struct aspeed_sgpio_bank *bank = to_bank(offset); +- void __iomem *addr_r, *addr_w; +- u32 reg = 0; + + if (aspeed_sgpio_is_input(offset)) + return -EINVAL; + +- /* Since this is an output, read the cached value from rdata, then +- * update val. */ +- addr_r = bank_reg(gpio, bank, reg_rdata); +- addr_w = bank_reg(gpio, bank, reg_val); +- +- reg = ioread32(addr_r); +- +- if (val) +- reg |= GPIO_BIT(offset); +- else +- reg &= ~GPIO_BIT(offset); +- +- iowrite32(reg, addr_w); ++ gpio->pdata->llops->reg_bit_set(gpio, offset, reg_val, val); - return rc; -@@ -211,6 +280,38 @@ return 0; } - -+static int sgpio_g7_set_value(struct gpio_chip *gc, unsigned int offset, -+ int val) -+{ -+ struct aspeed_sgpio *gpio = gpiochip_get_data(gc); -+ void __iomem *addr = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(offset >> 1); -+ u32 reg = 0, out_data; -+ -+ if (aspeed_sgpio_is_input(offset)) -+ return -EINVAL; -+ -+ if (gpio->pdata->slave && !aspeed_sgpios_ctrl_by_csr(offset)) { -+ // Ensure the parallel out value control by the software. -+ ast_write_bits(addr, SGPIO_G7_PARALLEL_OUT_SEL, -+ SELECT_FROM_CSR); -+ out_data = SGPIO_G7_PARALLEL_OUT_DATA; -+ } else { -+ // Ensure the serial out value control by the software. -+ ast_write_bits(addr, SGPIO_G7_SERIAL_OUT_SEL, SELECT_FROM_CSR); -+ out_data = SGPIO_G7_OUT_DATA; -+ } -+ reg = ioread32(addr); -+ -+ if (val) -+ reg |= out_data; -+ else -+ reg &= ~out_data; -+ -+ iowrite32(reg, addr); -+ -+ return 0; -+} -+ +@@ -214,13 +255,11 @@ static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) { struct aspeed_sgpio *gpio = gpiochip_get_data(gc); -@@ -218,7 +319,10 @@ - - raw_spin_lock_irqsave(&gpio->lock, flags); +- unsigned long flags; +- +- raw_spin_lock_irqsave(&gpio->lock, flags); -- sgpio_set_value(gc, offset, val); -+ if (gpio->version == 7) -+ sgpio_g7_set_value(gc, offset, val); -+ else -+ sgpio_set_value(gc, offset, val); ++ guard(raw_spinlock_irqsave)(&gpio->lock); + sgpio_set_value(gc, offset, val); - raw_spin_unlock_irqrestore(&gpio->lock, flags); +- raw_spin_unlock_irqrestore(&gpio->lock, flags); ++ return; } -@@ -238,7 +342,10 @@ + + static int aspeed_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset) +@@ -231,15 +270,14 @@ + static int aspeed_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val) + { + struct aspeed_sgpio *gpio = gpiochip_get_data(gc); +- unsigned long flags; + int rc; + + /* No special action is required for setting the direction; we'll * error-out in sgpio_set_value if this isn't an output GPIO */ - raw_spin_lock_irqsave(&gpio->lock, flags); -- rc = sgpio_set_value(gc, offset, val); -+ if (gpio->version == 7) -+ rc = sgpio_g7_set_value(gc, offset, val); -+ else -+ rc = sgpio_set_value(gc, offset, val); - raw_spin_unlock_irqrestore(&gpio->lock, flags); +- raw_spin_lock_irqsave(&gpio->lock, flags); ++ guard(raw_spinlock_irqsave)(&gpio->lock); ++ + rc = sgpio_set_value(gc, offset, val); +- raw_spin_unlock_irqrestore(&gpio->lock, flags); return rc; -@@ -265,6 +372,19 @@ - *bit = GPIO_BIT(*offset); + } +@@ -249,75 +287,34 @@ + return !!aspeed_sgpio_is_input(offset); } -+static void irqd_to_aspeed_g7_sgpio_data(struct irq_data *d, -+ struct aspeed_sgpio **gpio, -+ int *offset) -+{ -+ struct aspeed_sgpio *internal; -+ -+ *offset = irqd_to_hwirq(d); -+ internal = irq_data_get_irq_chip_data(d); -+ WARN_ON(!internal); -+ -+ *gpio = internal; -+} -+ +-static void irqd_to_aspeed_sgpio_data(struct irq_data *d, +- struct aspeed_sgpio **gpio, +- const struct aspeed_sgpio_bank **bank, +- u32 *bit, int *offset) +-{ +- struct aspeed_sgpio *internal; +- +- *offset = irqd_to_hwirq(d); +- internal = irq_data_get_irq_chip_data(d); +- WARN_ON(!internal); +- +- *gpio = internal; +- *bank = to_bank(*offset); +- *bit = GPIO_BIT(*offset); +-} + static void aspeed_sgpio_irq_ack(struct irq_data *d) { - const struct aspeed_sgpio_bank *bank; -@@ -285,6 +405,24 @@ - raw_spin_unlock_irqrestore(&gpio->lock, flags); +- const struct aspeed_sgpio_bank *bank; +- struct aspeed_sgpio *gpio; +- unsigned long flags; +- void __iomem *status_addr; +- int offset; +- u32 bit; +- +- irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); +- +- status_addr = bank_reg(gpio, bank, reg_irq_status); +- +- raw_spin_lock_irqsave(&gpio->lock, flags); ++ struct aspeed_sgpio *gpio = irq_data_get_irq_chip_data(d); ++ int offset = irqd_to_hwirq(d); + +- iowrite32(bit, status_addr); ++ guard(raw_spinlock_irqsave)(&gpio->lock); + +- raw_spin_unlock_irqrestore(&gpio->lock, flags); ++ gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_status, 1); } -+static void aspeed_g7_sgpio_irq_ack(struct irq_data *d) -+{ -+ struct aspeed_sgpio *gpio; -+ unsigned long flags; -+ void __iomem *status_addr; -+ int offset; -+ -+ irqd_to_aspeed_g7_sgpio_data(d, &gpio, &offset); -+ -+ status_addr = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(offset >> 1); -+ -+ raw_spin_lock_irqsave(&gpio->lock, flags); -+ -+ ast_write_bits(status_addr, SGPIO_G7_IRQ_STS, 1); -+ -+ raw_spin_unlock_irqrestore(&gpio->lock, flags); -+} -+ static void aspeed_sgpio_irq_set_mask(struct irq_data *d, bool set) { - const struct aspeed_sgpio_bank *bank; -@@ -320,6 +458,32 @@ +- const struct aspeed_sgpio_bank *bank; +- struct aspeed_sgpio *gpio; +- unsigned long flags; +- u32 reg, bit; +- void __iomem *addr; +- int offset; +- +- irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); +- addr = bank_reg(gpio, bank, reg_irq_enable); ++ struct aspeed_sgpio *gpio = irq_data_get_irq_chip_data(d); ++ int offset = irqd_to_hwirq(d); - } + /* Unmasking the IRQ */ + if (set) +- gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(d)); +- +- raw_spin_lock_irqsave(&gpio->lock, flags); +- +- reg = ioread32(addr); +- if (set) +- reg |= bit; +- else +- reg &= ~bit; +- +- iowrite32(reg, addr); +- +- raw_spin_unlock_irqrestore(&gpio->lock, flags); ++ gpiochip_enable_irq(&gpio->chip, offset); ++ scoped_guard(raw_spinlock_irqsave, &gpio->lock) ++ { ++ gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_enable, ++ set); ++ } -+static void aspeed_g7_sgpio_irq_set_mask(struct irq_data *d, bool set) -+{ -+ struct aspeed_sgpio *gpio; -+ unsigned long flags; -+ void __iomem *addr; -+ int offset; -+ -+ irqd_to_aspeed_g7_sgpio_data(d, &gpio, &offset); -+ addr = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(offset >> 1); -+ -+ /* Unmasking the IRQ */ -+ if (set) -+ gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(d)); -+ -+ raw_spin_lock_irqsave(&gpio->lock, flags); -+ if (set) -+ ast_write_bits(addr, SGPIO_G7_IRQ_EN, 1); -+ else -+ ast_clr_bits(addr, SGPIO_G7_IRQ_EN); -+ raw_spin_unlock_irqrestore(&gpio->lock, flags); -+ -+ /* Masking the IRQ */ -+ if (!set) -+ gpiochip_disable_irq(&gpio->chip, irqd_to_hwirq(d)); -+} -+ - static void aspeed_sgpio_irq_mask(struct irq_data *d) - { - aspeed_sgpio_irq_set_mask(d, false); -@@ -330,6 +494,16 @@ - aspeed_sgpio_irq_set_mask(d, true); + /* Masking the IRQ */ + if (!set) +- gpiochip_disable_irq(&gpio->chip, irqd_to_hwirq(d)); +- +- ++ gpiochip_disable_irq(&gpio->chip, offset); } -+static void aspeed_g7_sgpio_irq_mask(struct irq_data *d) -+{ -+ aspeed_g7_sgpio_irq_set_mask(d, false); -+} -+ -+static void aspeed_g7_sgpio_irq_unmask(struct irq_data *d) -+{ -+ aspeed_g7_sgpio_irq_set_mask(d, true); -+} -+ - static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type) - { + static void aspeed_sgpio_irq_mask(struct irq_data *d) +@@ -335,55 +332,36 @@ u32 type0 = 0; -@@ -390,6 +564,53 @@ - return 0; - } + u32 type1 = 0; + u32 type2 = 0; +- u32 bit, reg; +- const struct aspeed_sgpio_bank *bank; + irq_flow_handler_t handler; +- struct aspeed_sgpio *gpio; +- unsigned long flags; +- void __iomem *addr; +- int offset; +- +- irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); ++ struct aspeed_sgpio *gpio = irq_data_get_irq_chip_data(d); ++ int offset = irqd_to_hwirq(d); -+static int aspeed_g7_sgpio_set_type(struct irq_data *d, unsigned int type) -+{ -+ u32 type0 = 0; -+ u32 type1 = 0; -+ u32 type2 = 0; -+ irq_flow_handler_t handler; -+ struct aspeed_sgpio *gpio; -+ unsigned long flags; -+ void __iomem *addr; -+ int offset; -+ -+ irqd_to_aspeed_g7_sgpio_data(d, &gpio, &offset); -+ addr = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(offset >> 1); -+ -+ switch (type & IRQ_TYPE_SENSE_MASK) { -+ case IRQ_TYPE_EDGE_BOTH: + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_BOTH: +- type2 |= bit; + type2 = 1; -+ fallthrough; -+ case IRQ_TYPE_EDGE_RISING: + fallthrough; + case IRQ_TYPE_EDGE_RISING: +- type0 |= bit; + type0 = 1; -+ fallthrough; -+ case IRQ_TYPE_EDGE_FALLING: -+ handler = handle_edge_irq; -+ break; -+ case IRQ_TYPE_LEVEL_HIGH: + fallthrough; + case IRQ_TYPE_EDGE_FALLING: + handler = handle_edge_irq; + break; + case IRQ_TYPE_LEVEL_HIGH: +- type0 |= bit; + type0 = 1; -+ fallthrough; -+ case IRQ_TYPE_LEVEL_LOW: + fallthrough; + case IRQ_TYPE_LEVEL_LOW: +- type1 |= bit; + type1 = 1; -+ handler = handle_level_irq; -+ break; -+ default: -+ return -EINVAL; + handler = handle_level_irq; + break; + default: + return -EINVAL; + } + +- raw_spin_lock_irqsave(&gpio->lock, flags); +- +- addr = bank_reg(gpio, bank, reg_irq_type0); +- reg = ioread32(addr); +- reg = (reg & ~bit) | type0; +- iowrite32(reg, addr); +- +- addr = bank_reg(gpio, bank, reg_irq_type1); +- reg = ioread32(addr); +- reg = (reg & ~bit) | type1; +- iowrite32(reg, addr); +- +- addr = bank_reg(gpio, bank, reg_irq_type2); +- reg = ioread32(addr); +- reg = (reg & ~bit) | type2; +- iowrite32(reg, addr); +- +- raw_spin_unlock_irqrestore(&gpio->lock, flags); ++ scoped_guard(raw_spinlock_irqsave, &gpio->lock) { ++ gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_type0, type0); ++ gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_type1, type1); ++ gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_type2, type2); + } -+ -+ raw_spin_lock_irqsave(&gpio->lock, flags); -+ -+ ast_write_bits(addr, SGPIO_G7_IRQ_TYPE2, type2); -+ ast_write_bits(addr, SGPIO_G7_IRQ_TYPE1, type1); -+ ast_write_bits(addr, SGPIO_G7_IRQ_TYPE0, type0); -+ -+ raw_spin_unlock_irqrestore(&gpio->lock, flags); -+ -+ irq_set_handler_locked(d, handler); -+ return 0; -+} -+ - static void aspeed_sgpio_irq_handler(struct irq_desc *desc) - { - struct gpio_chip *gc = irq_desc_get_handler_data(desc); -@@ -412,6 +633,29 @@ - chained_irq_exit(ic, desc); - } -+static void aspeed_g7_sgpio_irq_handler(struct irq_desc *desc) -+{ -+ struct gpio_chip *gc = irq_desc_get_handler_data(desc); -+ struct irq_chip *ic = irq_desc_get_chip(desc); -+ struct aspeed_sgpio *gpio = gpiochip_get_data(gc); + irq_set_handler_locked(d, handler); + +@@ -395,15 +373,14 @@ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct irq_chip *ic = irq_desc_get_chip(desc); + struct aspeed_sgpio *data = gpiochip_get_data(gc); +- unsigned int i, p; + unsigned int i, p, banks; -+ unsigned long reg; -+ void __iomem *addr; -+ -+ chained_irq_enter(ic, desc); -+ -+ banks = DIV_ROUND_UP(gpio->chip.ngpio >> 1, 32); + unsigned long reg; + + chained_irq_enter(ic, desc); + +- for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { +- const struct aspeed_sgpio_bank *bank = &aspeed_sgpio_banks[i]; +- +- reg = ioread32(bank_reg(data, bank, reg_irq_status)); ++ banks = DIV_ROUND_UP(gc->ngpio, 64); + for (i = 0; i < banks; i++) { -+ addr = gpio->base + SGPIO_G7_IRQ_STS_OFFSET(i); -+ -+ reg = ioread32(addr); -+ -+ for_each_set_bit(p, ®, 32) -+ generic_handle_domain_irq(gc->irq.domain, (i * 32 + p) * 2); -+ } -+ chained_irq_exit(ic, desc); -+} -+ ++ reg = data->pdata->llops->reg_bank_get(data, i << 6, reg_irq_status); + + for_each_set_bit(p, ®, 32) + generic_handle_domain_irq(gc->irq.domain, (i * 32 + p) * 2); +@@ -414,13 +391,9 @@ + static void aspeed_sgpio_irq_print_chip(struct irq_data *d, struct seq_file *p) { - const struct aspeed_sgpio_bank *bank; -@@ -423,6 +667,15 @@ - seq_printf(p, dev_name(gpio->dev)); +- const struct aspeed_sgpio_bank *bank; +- struct aspeed_sgpio *gpio; +- u32 bit; +- int offset; ++ struct aspeed_sgpio *gpio = irq_data_get_irq_chip_data(d); + +- irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); +- seq_printf(p, dev_name(gpio->dev)); ++ seq_puts(p, dev_name(gpio->dev)); } -+static void aspeed_g7_sgpio_irq_print_chip(struct irq_data *d, struct seq_file *p) -+{ -+ struct aspeed_sgpio *gpio; -+ int offset; -+ -+ irqd_to_aspeed_g7_sgpio_data(d, &gpio, &offset); -+ seq_printf(p, dev_name(gpio->dev)); -+} -+ static const struct irq_chip aspeed_sgpio_irq_chip = { - .irq_ack = aspeed_sgpio_irq_ack, - .irq_mask = aspeed_sgpio_irq_mask, -@@ -433,6 +686,16 @@ - GPIOCHIP_IRQ_RESOURCE_HELPERS, - }; - -+static const struct irq_chip aspeed_g7_sgpio_irq_chip = { -+ .irq_ack = aspeed_g7_sgpio_irq_ack, -+ .irq_mask = aspeed_g7_sgpio_irq_mask, -+ .irq_unmask = aspeed_g7_sgpio_irq_unmask, -+ .irq_set_type = aspeed_g7_sgpio_set_type, -+ .irq_print_chip = aspeed_g7_sgpio_irq_print_chip, -+ .flags = IRQCHIP_IMMUTABLE, -+ GPIOCHIP_IRQ_RESOURCE_HELPERS, -+}; -+ - static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio, +@@ -437,7 +410,6 @@ struct platform_device *pdev) { -@@ -446,41 +709,49 @@ + int rc, i; +- const struct aspeed_sgpio_bank *bank; + struct gpio_irq_chip *irq; + rc = platform_get_irq(pdev, 0); +@@ -447,12 +419,11 @@ gpio->irq = rc; -- /* Disable IRQ and clear Interrupt status registers for all SGPIO Pins. */ + /* Disable IRQ and clear Interrupt status registers for all SGPIO Pins. */ - for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { - bank = &aspeed_sgpio_banks[i]; -- /* disable irq enable bits */ ++ for (i = 0; i < gpio->chip.ngpio; i += 2) { + /* disable irq enable bits */ - iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_enable)); -- /* clear status bits */ ++ gpio->pdata->llops->reg_bit_set(gpio, i, reg_irq_enable, 0); + /* clear status bits */ - iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_status)); -- } -+ if (gpio->version != 7) -+ /* Disable IRQ and clear Interrupt status registers for all SGPIO Pins. */ -+ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { -+ bank = &aspeed_sgpio_banks[i]; -+ /* disable irq enable bits */ -+ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_enable)); -+ /* clear status bits */ -+ iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_status)); -+ } ++ gpio->pdata->llops->reg_bit_set(gpio, i, reg_irq_status, 1); + } irq = &gpio->chip.irq; -- gpio_irq_chip_set_chip(irq, &aspeed_sgpio_irq_chip); -+ if (gpio->version == 7) -+ gpio_irq_chip_set_chip(irq, &aspeed_g7_sgpio_irq_chip); -+ else -+ gpio_irq_chip_set_chip(irq, &aspeed_sgpio_irq_chip); - irq->init_valid_mask = aspeed_sgpio_irq_init_valid_mask; - irq->handler = handle_bad_irq; - irq->default_type = IRQ_TYPE_NONE; -- irq->parent_handler = aspeed_sgpio_irq_handler; -+ irq->parent_handler = (gpio->version == 7) ? -+ aspeed_g7_sgpio_irq_handler : -+ aspeed_sgpio_irq_handler; - irq->parent_handler_data = gpio; - irq->parents = &gpio->irq; +@@ -466,45 +437,91 @@ irq->num_parents = 1; -- /* Apply default IRQ settings */ + /* Apply default IRQ settings */ - for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { - bank = &aspeed_sgpio_banks[i]; -- /* set falling or level-low irq */ ++ for (i = 0; i < gpio->chip.ngpio; i += 2) { + /* set falling or level-low irq */ - iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type0)); -- /* trigger type is edge */ ++ gpio->pdata->llops->reg_bit_set(gpio, i, reg_irq_type0, 0); + /* trigger type is edge */ - iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type1)); -- /* single edge trigger */ ++ gpio->pdata->llops->reg_bit_set(gpio, i, reg_irq_type1, 0); + /* single edge trigger */ - iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type2)); -- } -+ if (gpio->version != 7) -+ /* Apply default IRQ settings */ -+ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { -+ bank = &aspeed_sgpio_banks[i]; -+ /* set falling or level-low irq */ -+ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type0)); -+ /* trigger type is edge */ -+ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type1)); -+ /* single edge trigger */ -+ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type2)); -+ } ++ gpio->pdata->llops->reg_bit_set(gpio, i, reg_irq_type2, 0); + } return 0; } ++static void aspeed_sgpio_g4_reg_bit_set(struct aspeed_sgpio *gpio, unsigned int offset, ++ const enum aspeed_sgpio_reg reg, bool val) ++{ ++ const struct aspeed_sgpio_bank *bank = to_bank(offset); ++ void __iomem *addr = aspeed_sgpio_g4_bank_reg(gpio, bank, reg); ++ u32 temp; ++ ++ if (reg == reg_val) { ++ /* Since this is an output, read the cached value from rdata, then update val. */ ++ addr = aspeed_sgpio_g4_bank_reg(gpio, bank, reg_rdata); ++ temp = ioread32(addr); ++ if (val) ++ temp |= GPIO_BIT(offset); ++ else ++ temp &= ~GPIO_BIT(offset); ++ ++ addr = aspeed_sgpio_g4_bank_reg(gpio, bank, reg_val); ++ iowrite32(temp, addr); ++ } else if (reg == reg_irq_status) { ++ if (val) ++ iowrite32(GPIO_BIT(offset), addr); ++ } else { ++ /* When setting other registers, we read from the register itself */ ++ temp = ioread32(addr); ++ if (val) ++ temp |= GPIO_BIT(offset); ++ else ++ temp &= ~GPIO_BIT(offset); ++ iowrite32(temp, addr); ++ } ++} ++ ++static bool aspeed_sgpio_g4_reg_bit_get(struct aspeed_sgpio *gpio, unsigned int offset, ++ const enum aspeed_sgpio_reg reg) ++{ ++ const struct aspeed_sgpio_bank *bank = to_bank(offset); ++ void __iomem *addr = aspeed_sgpio_g4_bank_reg(gpio, bank, reg); ++ ++ return !!(ioread32(addr) & GPIO_BIT(offset)); ++} ++ ++static int aspeed_sgpio_g4_reg_bank_get(struct aspeed_sgpio *gpio, unsigned int offset, ++ const enum aspeed_sgpio_reg reg) ++{ ++ const struct aspeed_sgpio_bank *bank = to_bank(offset); ++ void __iomem *addr = aspeed_sgpio_g4_bank_reg(gpio, bank, reg); ++ ++ if (reg == reg_irq_status) ++ return ioread32(addr); ++ else ++ return -EOPNOTSUPP; ++} ++ ++static const struct aspeed_sgpio_llops aspeed_sgpio_g4_llops = { ++ .reg_bit_set = aspeed_sgpio_g4_reg_bit_set, ++ .reg_bit_get = aspeed_sgpio_g4_reg_bit_get, ++ .reg_bank_get = aspeed_sgpio_g4_reg_bank_get, ++}; ++ static const struct aspeed_sgpio_pdata ast2400_sgpio_pdata = { .pin_mask = GENMASK(9, 6), -+ .ctrl_reg = 0x54, ++ .llops = &aspeed_sgpio_g4_llops, ++ .cfg_offset = ASPEED_SGPIO_G4_CFG_OFFSET, }; static int aspeed_sgpio_reset_tolerance(struct gpio_chip *chip, -@@ -509,38 +780,81 @@ + unsigned int offset, bool enable) + { + struct aspeed_sgpio *gpio = gpiochip_get_data(chip); +- unsigned long flags; +- void __iomem *reg; +- u32 val; +- +- reg = bank_reg(gpio, to_bank(offset), reg_tolerance); +- +- raw_spin_lock_irqsave(&gpio->lock, flags); +- +- val = readl(reg); + +- if (enable) +- val |= GPIO_BIT(offset); +- else +- val &= ~GPIO_BIT(offset); +- +- writel(val, reg); ++ guard(raw_spinlock_irqsave)(&gpio->lock); + +- raw_spin_unlock_irqrestore(&gpio->lock, flags); ++ gpio->pdata->llops->reg_bit_set(gpio, offset, reg_tolerance, enable); + return 0; } +@@ -523,21 +540,77 @@ -+static int aspeed_g7_sgpio_reset_tolerance(struct gpio_chip *chip, -+ unsigned int offset, bool enable) + static const struct aspeed_sgpio_pdata ast2600_sgpiom_pdata = { + .pin_mask = GENMASK(10, 6), ++ .llops = &aspeed_sgpio_g4_llops, ++ .cfg_offset = ASPEED_SGPIO_G4_CFG_OFFSET, ++}; ++ ++static void aspeed_sgpio_g7_reg_bit_set(struct aspeed_sgpio *gpio, unsigned int offset, ++ const enum aspeed_sgpio_reg reg, bool val) +{ -+ struct aspeed_sgpio *gpio = gpiochip_get_data(chip); -+ unsigned long flags; -+ void __iomem *reg; ++ u32 mask = aspeed_sgpio_g7_reg_mask(reg); ++ void __iomem *addr = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(offset >> 1); ++ u32 write_val; + -+ reg = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(offset >> 1); ++ if (mask) { ++ write_val = (ioread32(addr) & ~(mask)) | field_prep(mask, val); ++ iowrite32(write_val, addr); ++ } ++} + -+ raw_spin_lock_irqsave(&gpio->lock, flags); ++static bool aspeed_sgpio_g7_reg_bit_get(struct aspeed_sgpio *gpio, unsigned int offset, ++ const enum aspeed_sgpio_reg reg) ++{ ++ u32 mask = aspeed_sgpio_g7_reg_mask(reg); ++ void __iomem *addr; + -+ if (enable) -+ ast_write_bits(reg, SGPIO_G7_RST_TOLERANCE, 1); ++ addr = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(offset >> 1); ++ if (reg == reg_val) ++ mask = SGPIO_G7_IN_DATA; ++ ++ if (mask) ++ return field_get(mask, ioread32(addr)); + else -+ ast_clr_bits(reg, SGPIO_G7_RST_TOLERANCE); ++ return 0; ++} + -+ raw_spin_unlock_irqrestore(&gpio->lock, flags); ++static int aspeed_sgpio_g7_reg_bank_get(struct aspeed_sgpio *gpio, unsigned int offset, ++ const enum aspeed_sgpio_reg reg) ++{ ++ void __iomem *addr; + -+ return 0; ++ if (reg == reg_irq_status) { ++ addr = gpio->base + SGPIO_G7_IRQ_STS_OFFSET(offset >> 6); ++ return ioread32(addr); ++ } else { ++ return -EOPNOTSUPP; ++ } +} + - static int aspeed_sgpio_set_config(struct gpio_chip *chip, unsigned int offset, - unsigned long config) - { -+ struct aspeed_sgpio *gpio = gpiochip_get_data(chip); - unsigned long param = pinconf_to_config_param(config); - u32 arg = pinconf_to_config_argument(config); - -- if (param == PIN_CONFIG_PERSIST_STATE) -- return aspeed_sgpio_reset_tolerance(chip, offset, arg); -+ if (param == PIN_CONFIG_PERSIST_STATE) { -+ if (gpio->version == 7) -+ return aspeed_g7_sgpio_reset_tolerance(chip, offset, -+ arg); -+ else -+ return aspeed_sgpio_reset_tolerance(chip, offset, arg); -+ } - - return -ENOTSUPP; - } - - static const struct aspeed_sgpio_pdata ast2600_sgpiom_pdata = { - .pin_mask = GENMASK(10, 6), -+ .ctrl_reg = 0x54, ++static const struct aspeed_sgpio_llops aspeed_sgpio_g7_llops = { ++ .reg_bit_set = aspeed_sgpio_g7_reg_bit_set, ++ .reg_bit_get = aspeed_sgpio_g7_reg_bit_get, ++ .reg_bank_get = aspeed_sgpio_g7_reg_bank_get, +}; + +static const struct aspeed_sgpio_pdata ast2700_sgpiom_pdata = { + .pin_mask = GENMASK(11, 6), -+ .ctrl_reg = 0x0, -+ .version = 7, -+}; -+ -+static const struct aspeed_sgpio_pdata ast2700_sgpios_pdata = { -+ .pin_mask = GENMASK(11, 6), -+ .ctrl_reg = 0x0, -+ .version = 7, -+ .slave = 1, ++ .llops = &aspeed_sgpio_g7_llops, ++ .cfg_offset = ASPEED_SGPIO_G7_CFG_OFFSET, }; static const struct of_device_id aspeed_sgpio_of_table[] = { @@ -32236,7 +38141,6 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c { .compatible = "aspeed,ast2500-sgpio", .data = &ast2400_sgpio_pdata, }, { .compatible = "aspeed,ast2600-sgpiom", .data = &ast2600_sgpiom_pdata, }, + { .compatible = "aspeed,ast2700-sgpiom", .data = &ast2700_sgpiom_pdata, }, -+ { .compatible = "aspeed,ast2700-sgpios", .data = &ast2700_sgpios_pdata, }, {} }; @@ -32249,13 +38153,8 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c - const struct aspeed_sgpio_pdata *pdata; struct aspeed_sgpio *gpio; unsigned long apb_freq; -- int rc; -+ void __iomem *addr; -+ int rc, i; - - gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); - if (!gpio) -@@ -552,11 +866,12 @@ + int rc; +@@ -552,12 +625,11 @@ gpio->dev = &pdev->dev; @@ -32266,99 +38165,21 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c return -EINVAL; - pin_mask = pdata->pin_mask; +- + pin_mask = gpio->pdata->pin_mask; -+ gpio->version = gpio->pdata->version; - rc = device_property_read_u32(&pdev->dev, "ngpios", &nr_gpios); if (rc < 0) { -@@ -568,41 +883,53 @@ - return -EINVAL; - } - -- rc = device_property_read_u32(&pdev->dev, "bus-frequency", &sgpio_freq); -- if (rc < 0) { -- dev_err(&pdev->dev, "Could not read bus-frequency property\n"); -- return -EINVAL; -+ if (gpio->version == 7 && !gpio->pdata->slave) -+ for (i = 0; i < nr_gpios; i++) { -+ addr = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(i); -+ ast_write_bits(addr, SGPIO_G7_SERIAL_OUT_SEL, -+ SELECT_FROM_CSR); -+ } -+ -+ if (!gpio->pdata->slave) { -+ rc = device_property_read_u32(&pdev->dev, "bus-frequency", &sgpio_freq); -+ if (rc < 0) { -+ dev_err(&pdev->dev, "Could not read bus-frequency property\n"); -+ return -EINVAL; -+ } -+ -+ gpio->pclk = devm_clk_get(&pdev->dev, NULL); -+ if (IS_ERR(gpio->pclk)) { -+ dev_err(&pdev->dev, "devm_clk_get failed\n"); -+ return PTR_ERR(gpio->pclk); -+ } -+ -+ apb_freq = clk_get_rate(gpio->pclk); -+ -+ /* -+ * From the datasheet, -+ * SGPIO period = 1/PCLK * 2 * (GPIO254[31:16] + 1) -+ * period = 2 * (GPIO254[31:16] + 1) / PCLK -+ * frequency = 1 / (2 * (GPIO254[31:16] + 1) / PCLK) -+ * frequency = PCLK / (2 * (GPIO254[31:16] + 1)) -+ * frequency * 2 * (GPIO254[31:16] + 1) = PCLK -+ * GPIO254[31:16] = PCLK / (frequency * 2) - 1 -+ */ -+ if (sgpio_freq == 0) -+ return -EINVAL; -+ -+ sgpio_clk_div = (apb_freq / (sgpio_freq * 2)) - 1; -+ -+ if (sgpio_clk_div > (1 << 16) - 1) -+ return -EINVAL; -+ -+ gpio_cnt_regval = ((nr_gpios / 8) << ASPEED_SGPIO_PINS_SHIFT) & pin_mask; -+ iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, sgpio_clk_div) | -+ gpio_cnt_regval | ASPEED_SGPIO_ENABLE, -+ gpio->base + gpio->pdata->ctrl_reg); -+ } else { -+ iowrite32(ASPEED_SGPIO_ENABLE, gpio->base + gpio->pdata->ctrl_reg); - } + dev_err(&pdev->dev, "Could not read ngpios property\n"); +@@ -601,7 +673,7 @@ -- gpio->pclk = devm_clk_get(&pdev->dev, NULL); -- if (IS_ERR(gpio->pclk)) { -- dev_err(&pdev->dev, "devm_clk_get failed\n"); -- return PTR_ERR(gpio->pclk); -- } -- -- apb_freq = clk_get_rate(gpio->pclk); -- -- /* -- * From the datasheet, -- * SGPIO period = 1/PCLK * 2 * (GPIO254[31:16] + 1) -- * period = 2 * (GPIO254[31:16] + 1) / PCLK -- * frequency = 1 / (2 * (GPIO254[31:16] + 1) / PCLK) -- * frequency = PCLK / (2 * (GPIO254[31:16] + 1)) -- * frequency * 2 * (GPIO254[31:16] + 1) = PCLK -- * GPIO254[31:16] = PCLK / (frequency * 2) - 1 -- */ -- if (sgpio_freq == 0) -- return -EINVAL; -- -- sgpio_clk_div = (apb_freq / (sgpio_freq * 2)) - 1; -- -- if (sgpio_clk_div > (1 << 16) - 1) -- return -EINVAL; -- -- gpio_cnt_regval = ((nr_gpios / 8) << ASPEED_SGPIO_PINS_SHIFT) & pin_mask; -- iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, sgpio_clk_div) | gpio_cnt_regval | + gpio_cnt_regval = ((nr_gpios / 8) << ASPEED_SGPIO_PINS_SHIFT) & pin_mask; + iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, sgpio_clk_div) | gpio_cnt_regval | - ASPEED_SGPIO_ENABLE, gpio->base + ASPEED_SGPIO_CTRL); -- ++ ASPEED_SGPIO_ENABLE, gpio->base + gpio->pdata->cfg_offset); + raw_spin_lock_init(&gpio->lock); - gpio->chip.parent = &pdev->dev; -@@ -629,11 +956,12 @@ +@@ -629,11 +701,12 @@ } static struct platform_driver aspeed_sgpio_driver = { @@ -32374,7 +38195,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c MODULE_DESCRIPTION("Aspeed Serial GPIO Driver"); diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c --- a/drivers/gpio/gpio-aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpio/gpio-aspeed.c 2025-12-23 10:16:21.049033926 +0000 ++++ b/drivers/gpio/gpio-aspeed.c 2026-04-08 18:03:48.226706855 +0000 @@ -30,6 +30,27 @@ #include #include "gpiolib.h" @@ -33451,7 +39272,7 @@ diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/aspeed/Kconfig b/drivers/gpu/drm/aspeed/Kconfig --- a/drivers/gpu/drm/aspeed/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpu/drm/aspeed/Kconfig 2025-12-23 10:16:09.077234622 +0000 ++++ b/drivers/gpu/drm/aspeed/Kconfig 2026-04-08 18:03:34.489957659 +0000 @@ -4,6 +4,7 @@ depends on DRM && OF depends on (COMPILE_TEST || ARCH_ASPEED) @@ -33462,8 +39283,8 @@ diff --git a/drivers/gpu/drm/aspeed/Kconfig b/drivers/gpu/drm/aspeed/Kconfig select DMA_CMA if HAVE_DMA_CONTIGUOUS diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx.h b/drivers/gpu/drm/aspeed/aspeed_gfx.h --- a/drivers/gpu/drm/aspeed/aspeed_gfx.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpu/drm/aspeed/aspeed_gfx.h 2025-12-23 10:16:20.986034982 +0000 -@@ -8,14 +8,31 @@ ++++ b/drivers/gpu/drm/aspeed/aspeed_gfx.h 2026-04-08 18:03:48.158708097 +0000 +@@ -8,14 +8,32 @@ struct drm_device drm; void __iomem *base; struct clk *clk; @@ -33471,6 +39292,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx.h b/drivers/gpu/drm/aspeed/aspeed + struct reset_control *rst_crt; + struct reset_control *rst_engine; struct regmap *scu; ++ struct regmap *scu1; + struct regmap *dp; + struct regmap *dpmcu; + struct regmap *pcie_ep; @@ -33496,7 +39318,18 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx.h b/drivers/gpu/drm/aspeed/aspeed struct drm_simple_display_pipe pipe; struct drm_connector connector; -@@ -106,3 +123,58 @@ +@@ -83,6 +101,10 @@ + #define CRT_CTRL_VBLANK_LINE(x) (((x) << 20) & CRT_CTRL_VBLANK_LINE_MASK) + #define CRT_CTRL_VBLANK_LINE_MASK GENMASK(31, 20) + ++/* CURSOR define */ ++#define CRT_CTRL_CURSOR0 0x3f3f ++#define CRT_CTRL_CURSOR1 0xfff1fff ++ + /* CRT_HORIZ0 */ + #define CRT_H_TOTAL(x) (x) + #define CRT_H_DE(x) ((x) << 16) +@@ -106,3 +128,58 @@ /* CRT_THROD */ #define CRT_THROD_LOW(x) (x) #define CRT_THROD_HIGH(x) ((x) << 8) @@ -33557,8 +39390,8 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx.h b/drivers/gpu/drm/aspeed/aspeed +#define DP_1024 0x010a0020 /* 1024 x 768 70Hz */ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c --- a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c 2025-12-23 10:16:20.979035100 +0000 -@@ -23,6 +23,40 @@ ++++ b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c 2026-04-08 18:03:48.152708207 +0000 +@@ -23,6 +23,44 @@ return container_of(pipe, struct aspeed_gfx, pipe); } @@ -33591,15 +39424,19 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/a + +static void aspeed_gfx_set_g7_clock(struct aspeed_gfx *priv) +{ -+ /* apply 800 x 600 @ 62 on ast2700 */ ++ /* apply 800 x 600 @ 60 on ast2700 */ + regmap_update_bits(priv->scu, 0x288, BIT(14), BIT(14)); -+ regmap_write(priv->scu, 0x340, 0x00130002); ++ regmap_write(priv->scu, 0x340, 0x00190002); ++ ++ /* apply 800 x 600 @ 60 on ast2700 DAC */ ++ regmap_update_bits(priv->scu1, 0xd0, BIT(10), BIT(10)); ++ regmap_write(priv->scu1, 0x320, 0x1048000F); +} + static int aspeed_gfx_set_pixel_fmt(struct aspeed_gfx *priv, u32 *bpp) { struct drm_crtc *crtc = &priv->pipe.crtc; -@@ -59,8 +93,12 @@ +@@ -59,11 +97,22 @@ u32 ctrl1 = readl(priv->base + CRT_CTRL1); u32 ctrl2 = readl(priv->base + CRT_CTRL2); @@ -33611,10 +39448,20 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/a + if (priv->dp_support) + regmap_update_bits(priv->scu, priv->dac_reg, priv->soc_dp_bit, priv->soc_dp_bit); + } ++ ++ /* remove the cursor and osd usage */ ++ ctrl1 &= ~(CRT_CTRL_HW_CURSOR_EN | CRT_CTRL_OSD_EN); writel(ctrl1 | CRT_CTRL_EN, priv->base + CRT_CTRL1); writel(ctrl2 | CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2); -@@ -74,7 +112,42 @@ ++ ++ /* trigger cursor position to apply cursor disable */ ++ writel(CRT_CTRL_CURSOR0, priv->base + CRT_CURSOR0); ++ writel(CRT_CTRL_CURSOR1, priv->base + CRT_CURSOR1); + } + + static void aspeed_gfx_disable_controller(struct aspeed_gfx *priv) +@@ -74,7 +123,42 @@ writel(ctrl1 & ~CRT_CTRL_EN, priv->base + CRT_CTRL1); writel(ctrl2 & ~CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2); @@ -33658,7 +39505,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/a } static void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv) -@@ -87,6 +160,8 @@ +@@ -87,6 +171,8 @@ if (err) return; @@ -33667,7 +39514,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/a #if 0 /* TODO: we have only been able to test with the 40MHz USB clock. The * clock is fixed, so we cannot adjust it here. */ -@@ -137,6 +212,10 @@ +@@ -137,6 +223,10 @@ * per line, rounded up) */ writel(priv->throd_val, priv->base + CRT_THROD); @@ -33678,7 +39525,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/a } static void aspeed_gfx_pipe_enable(struct drm_simple_display_pipe *pipe, -@@ -187,12 +266,17 @@ +@@ -187,12 +277,17 @@ gem = drm_fb_dma_get_gem_obj(fb, 0); if (!gem) return; @@ -33697,7 +39544,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/a u32 reg = readl(priv->base + CRT_CTRL1); /* Clear pending VBLANK IRQ */ -@@ -207,6 +291,7 @@ +@@ -207,6 +302,7 @@ static void aspeed_gfx_disable_vblank(struct drm_simple_display_pipe *pipe) { struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); @@ -33707,7 +39554,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/a reg &= ~CRT_CTRL_VERTICAL_INTR_EN; diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c --- a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c 2025-12-23 10:16:20.992034882 +0000 ++++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c 2026-04-08 18:03:48.165707969 +0000 @@ -63,6 +63,15 @@ u32 vga_scratch_reg; /* VGA scratch register in SCU */ u32 throd_val; /* Default Threshold Seting */ @@ -33804,7 +39651,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/as int ret; ret = drmm_mode_config_init(drm); -@@ -113,13 +168,63 @@ +@@ -113,13 +168,67 @@ drm->mode_config.min_width = 0; drm->mode_config.min_height = 0; @@ -33848,6 +39695,8 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/as + + /*Change the CRT back to host*/ + regmap_update_bits(priv->scu, priv->dac_reg, priv->soc_crt_bit, 0); ++ if ((priv->flags & CLK_MASK) == CLK_G7) ++ regmap_update_bits(priv->scu1, 0xD0, BIT(10), 0); + } else if (reg & priv->pcie_int_h_to_l) { + dev_dbg(drm->dev, "pcie de-active.\n"); + /*Change the DP into host*/ @@ -33860,6 +39709,8 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/as + + /*Change the CRT into soc*/ + regmap_update_bits(priv->scu, priv->dac_reg, priv->soc_crt_bit, priv->soc_crt_bit); ++ if ((priv->flags & CLK_MASK) == CLK_G7) ++ regmap_update_bits(priv->scu1, 0xD0, BIT(10), BIT(10)); + } + return IRQ_HANDLED; + } @@ -33870,7 +39721,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/as static irqreturn_t aspeed_gfx_irq_handler(int irq, void *data) { struct drm_device *drm = data; -@@ -137,6 +242,145 @@ +@@ -137,6 +246,158 @@ return IRQ_NONE; } @@ -33908,7 +39759,10 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/as + +static int aspeed_adaptor_detect(struct drm_device *drm) +{ ++ struct platform_device *pdev = to_platform_device(drm->dev); + struct aspeed_gfx *priv = to_aspeed_gfx(drm); ++ struct device_node *np = pdev->dev.of_node; ++ + u32 dp_status_offset = 0, reg = 0; + + switch (priv->flags & CLK_MASK) { @@ -33965,6 +39819,16 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/as + regmap_update_bits(priv->dp, DP_SOURCE, DP_CONTROL_FROM_SOC, DP_CONTROL_FROM_SOC); + } + ++ /* get the scu1 for DAC setting */ ++ priv->scu1 = syscon_regmap_lookup_by_phandle(np, "syscon_io"); ++ if (IS_ERR(priv->scu1)) { ++ priv->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2700-scu1"); ++ if (IS_ERR(priv->scu1)) { ++ dev_err(drm->dev, "failed to find SCU1 regmap\n"); ++ return PTR_ERR(priv->scu1); ++ } ++ } ++ + break; + default: + priv->dp_support = 0x0; @@ -34016,7 +39880,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/as static int aspeed_gfx_load(struct drm_device *drm) { struct platform_device *pdev = to_platform_device(drm->dev); -@@ -144,6 +388,7 @@ +@@ -144,6 +405,7 @@ struct device_node *np = pdev->dev.of_node; const struct aspeed_gfx_config *config; struct resource *res; @@ -34024,7 +39888,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/as int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -@@ -160,13 +405,40 @@ +@@ -160,13 +422,40 @@ priv->vga_scratch_reg = config->vga_scratch_reg; priv->throd_val = config->throd_val; priv->scan_line_max = config->scan_line_max; @@ -34068,7 +39932,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/as } } -@@ -177,19 +449,18 @@ +@@ -177,19 +466,18 @@ return ret; } @@ -34092,7 +39956,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/as priv->clk = devm_clk_get(drm->dev, NULL); if (IS_ERR(priv->clk)) { -@@ -199,6 +470,22 @@ +@@ -199,6 +487,22 @@ } clk_prepare_enable(priv->clk); @@ -34115,7 +39979,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/as /* Sanitize control registers */ writel(0, priv->base + CRT_CTRL1); writel(0, priv->base + CRT_CTRL2); -@@ -232,6 +519,23 @@ +@@ -232,6 +536,23 @@ return ret; } @@ -34139,7 +40003,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/as drm_mode_config_reset(drm); return 0; -@@ -239,6 +543,12 @@ +@@ -239,6 +560,12 @@ static void aspeed_gfx_unload(struct drm_device *drm) { @@ -34154,7 +40018,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/as diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_out.c b/drivers/gpu/drm/aspeed/aspeed_gfx_out.c --- a/drivers/gpu/drm/aspeed/aspeed_gfx_out.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpu/drm/aspeed/aspeed_gfx_out.c 2025-12-23 10:16:20.973035200 +0000 ++++ b/drivers/gpu/drm/aspeed/aspeed_gfx_out.c 2026-04-08 18:03:48.145708335 +0000 @@ -10,7 +10,19 @@ static int aspeed_gfx_get_modes(struct drm_connector *connector) @@ -34178,7 +40042,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_out.c b/drivers/gpu/drm/aspeed/as static const struct diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig --- a/drivers/hwmon/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/hwmon/Kconfig 2025-12-23 10:16:08.059251693 +0000 ++++ b/drivers/hwmon/Kconfig 2026-04-08 18:03:33.210980986 +0000 @@ -423,6 +423,16 @@ This driver can also be built as a module. If so, the module will be called aspeed_g6_pwm_tach. @@ -34198,7 +40062,7 @@ diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile --- a/drivers/hwmon/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/hwmon/Makefile 2025-12-23 10:16:11.821188606 +0000 ++++ b/drivers/hwmon/Makefile 2026-04-08 18:03:37.718898767 +0000 @@ -55,6 +55,7 @@ obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o obj-$(CONFIG_SENSORS_ASPEED) += aspeed-pwm-tacho.o @@ -34209,7 +40073,7 @@ diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile obj-$(CONFIG_SENSORS_AXI_FAN_CONTROL) += axi-fan-control.o diff --git a/drivers/hwmon/aspeed-chassis.c b/drivers/hwmon/aspeed-chassis.c --- a/drivers/hwmon/aspeed-chassis.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/hwmon/aspeed-chassis.c 2025-12-23 10:16:20.680040111 +0000 ++++ b/drivers/hwmon/aspeed-chassis.c 2026-04-08 18:03:47.815714364 +0000 @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* @@ -34434,7 +40298,7 @@ diff --git a/drivers/hwmon/aspeed-chassis.c b/drivers/hwmon/aspeed-chassis.c +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/aspeed-g6-pwm-tach.c b/drivers/hwmon/aspeed-g6-pwm-tach.c --- a/drivers/hwmon/aspeed-g6-pwm-tach.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/hwmon/aspeed-g6-pwm-tach.c 2025-12-23 10:16:20.686040010 +0000 ++++ b/drivers/hwmon/aspeed-g6-pwm-tach.c 2026-04-08 18:03:47.821714254 +0000 @@ -56,6 +56,7 @@ #include #include @@ -34581,9 +40445,9 @@ diff --git a/drivers/hwmon/aspeed-g6-pwm-tach.c b/drivers/hwmon/aspeed-g6-pwm-ta }; MODULE_DEVICE_TABLE(of, aspeed_pwm_tach_match); diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig ---- a/drivers/i2c/busses/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/i2c/busses/Kconfig 2025-12-23 10:16:07.612259189 +0000 -@@ -411,6 +411,17 @@ +--- a/drivers/i2c/busses/Kconfig 2026-04-08 18:03:23.246162739 +0000 ++++ b/drivers/i2c/busses/Kconfig 2026-04-08 18:03:32.670990835 +0000 +@@ -421,6 +421,17 @@ This driver can also be built as a module. If so, the module will be called i2c-altera. @@ -34602,8 +40466,8 @@ diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig tristate "Aspeed I2C Controller" depends on ARCH_ASPEED || COMPILE_TEST diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile ---- a/drivers/i2c/busses/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/i2c/busses/Makefile 2025-12-23 10:16:11.163199640 +0000 +--- a/drivers/i2c/busses/Makefile 2026-04-08 18:03:23.246162739 +0000 ++++ b/drivers/i2c/busses/Makefile 2026-04-08 18:03:37.112909819 +0000 @@ -39,6 +39,7 @@ obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o obj-$(CONFIG_I2C_AMD_MP2) += i2c-amd-mp2-pci.o i2c-amd-mp2-plat.o @@ -34614,8 +40478,8 @@ diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile ifeq ($(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL),y) diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c --- a/drivers/i2c/busses/i2c-ast2600.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/i2c/busses/i2c-ast2600.c 2025-12-23 10:16:20.640040781 +0000 -@@ -0,0 +1,2421 @@ ++++ b/drivers/i2c/busses/i2c-ast2600.c 2026-04-08 18:03:47.772715150 +0000 +@@ -0,0 +1,2449 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ASPEED AST2600 new register set I2C controller driver @@ -34673,6 +40537,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c +#define AST2600_I2CCG_DIV_CTRL 0xC6411208 +#define AST2700_I2CCG_DIV_CTRL 0xC6220904 +#define AST2700_MIN_AC_TIMING 12000 ++#define AST_DEFAULT_AC_TIMING 100000 + +/* 0x00 : I2CC Controller/Target Function Control Register */ +#define AST2600_I2CC_FUN_CTRL 0x00 @@ -34911,6 +40776,13 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + +#define AST2600_I2C_TARGET_COUNT 0x3 + ++#define HIGH_MIN_DIV 100000000 ++#define STANDARD_HIGH_MIN_400NS 400 ++#define FAST_HIGH_MIN_60NS 60 ++#define FAST_PLUS_HIGH_MIN_26NS 26 ++ ++#define DATAHOLD_MAX_LEVEL 3 ++ +enum xfer_mode { + BYTE_MODE, + BUFF_MODE, @@ -34938,7 +40810,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + struct i2c_timings timing_info; + struct completion cmd_complete; + struct i2c_msg *msgs; -+ u8 *controller_safe_buf; ++ u8 *controller_dma_buf; + dma_addr_t controller_dma_addr; + u32 apb_clk; + struct i2c_divisor clk_divisor; @@ -34954,6 +40826,8 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + enum i2c_version version; + bool multi_master; + u32 debounce_level; ++ u32 manual_min_high; ++ u32 manual_data_hold; + /* Buffer mode */ + void __iomem *buf_base; + /* smbus alert */ @@ -34971,15 +40845,40 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c +#endif +}; + ++static u32 i2c_cal_high_min_config(struct ast2600_i2c_bus *i2c_bus, u64 base_clk) ++{ ++ u64 cal, spec_high_min = STANDARD_HIGH_MIN_400NS; ++ ++ /* fill the spec high min value */ ++ if (i2c_bus->timing_info.bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ && ++ i2c_bus->timing_info.bus_freq_hz > I2C_MAX_STANDARD_MODE_FREQ) ++ spec_high_min = FAST_HIGH_MIN_60NS; ++ else if (i2c_bus->timing_info.bus_freq_hz <= I2C_MAX_FAST_MODE_PLUS_FREQ && ++ i2c_bus->timing_info.bus_freq_hz > I2C_MAX_FAST_MODE_FREQ) ++ spec_high_min = FAST_PLUS_HIGH_MIN_26NS; ++ ++ /* round down high minimum value to minimize the impact on the clock high time */ ++ cal = base_clk * spec_high_min; ++ return (u32)DIV_ROUND_DOWN_ULL(cal, HIGH_MIN_DIV); ++} ++ +static void ast2600_i2c_ac_timing_config(struct ast2600_i2c_bus *i2c_bus) +{ + unsigned long base_clk[16]; + int baseclk_idx = 0; + int divisor = 0; + u32 clk_div_reg; -+ u32 scl_low; -+ u32 scl_high; -+ u32 data; ++ u32 scl_low = 0; ++ u32 scl_high = 0; ++ u32 scl_high_min = 0; ++ u32 sda_data_hold = 0; ++ u32 data = 0; ++ ++ /* Check the maxmum i2c bus frequency */ ++ if (i2c_bus->timing_info.bus_freq_hz > I2C_MAX_ULTRA_FAST_MODE_FREQ) { ++ dev_err(i2c_bus->dev, "The frequency over spec. Set to 100KHz.\n"); ++ i2c_bus->timing_info.bus_freq_hz = AST_DEFAULT_AC_TIMING; ++ } + + regmap_read(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, &clk_div_reg); + @@ -34994,6 +40893,9 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + if ((base_clk[i] / i2c_bus->timing_info.bus_freq_hz) <= 32) { + baseclk_idx = i; + divisor = DIV_ROUND_UP(base_clk[i], i2c_bus->timing_info.bus_freq_hz); ++ /* calculate the high min value */ ++ scl_high_min = i2c_cal_high_min_config(i2c_bus, (u64)base_clk[i]); ++ dev_dbg(i2c_bus->dev, "scl_high_min: %d\n", scl_high_min); + break; + } + } @@ -35002,13 +40904,32 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + divisor = min(divisor, 32); + scl_low = min(divisor * 9 / 16 - 1, 15); + scl_high = (divisor - scl_low - 2) & GENMASK(3, 0); -+ data = (scl_high - 1) << 20 | scl_high << 16 | scl_low << 12 | baseclk_idx; ++ ++ if (i2c_bus->manual_min_high) { ++ if (i2c_bus->manual_min_high > scl_high) ++ dev_info(i2c_bus->dev, "invalid manual high min: %d\n", ++ i2c_bus->manual_min_high); ++ else ++ scl_high_min = i2c_bus->manual_min_high; ++ } ++ ++ if (i2c_bus->manual_data_hold) { ++ if (i2c_bus->manual_data_hold > DATAHOLD_MAX_LEVEL) ++ dev_info(i2c_bus->dev, "invalid manual data hold: %d\n", ++ i2c_bus->manual_data_hold); ++ else ++ sda_data_hold = i2c_bus->manual_data_hold; ++ } ++ ++ data = baseclk_idx; ++ data |= scl_high_min << 20 | scl_high << 16 | scl_low << 12 | sda_data_hold << 10; + + if (i2c_bus->timeout) { + i2c_bus->timeout = min(i2c_bus->timeout, 31); + data |= AST2600_I2CC_TTIMEOUT(i2c_bus->timeout); + data |= AST2600_I2CC_TOUTBASECLK(AST2600_I2C_TIMEOUT_CLK); + } ++ dev_dbg(i2c_bus->dev, "ac: 0x%x\n", data); + + writel(data, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING); +} @@ -35018,14 +40939,20 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + unsigned long base_clk; + int baseclk_idx = 0; + int divisor = 0; -+ u32 clk_div_reg; -+ u32 scl_low; -+ u32 scl_high; -+ u32 data; ++ u32 clk_div_reg = 0; ++ u32 scl_low = 0; ++ u32 scl_high = 0; ++ u32 scl_high_min = 0; ++ u32 sda_data_hold = 0; ++ u32 data = 0; + u8 divid_term = 0; + ++ /* Check the maxmum i2c bus frequency */ + /* The i2c minmum ac-timing is 12KHz */ -+ if (i2c_bus->timing_info.bus_freq_hz < AST2700_MIN_AC_TIMING) { ++ if (i2c_bus->timing_info.bus_freq_hz > I2C_MAX_ULTRA_FAST_MODE_FREQ) { ++ dev_err(i2c_bus->dev, "The frequency over spec. Set to 100KHz.\n"); ++ i2c_bus->timing_info.bus_freq_hz = AST_DEFAULT_AC_TIMING; ++ } else if (i2c_bus->timing_info.bus_freq_hz < AST2700_MIN_AC_TIMING) { + dev_err(i2c_bus->dev, "The frequency could not be lower than 12KHz.\n"); + i2c_bus->timing_info.bus_freq_hz = AST2700_MIN_AC_TIMING; + } @@ -35055,11 +40982,35 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + } + } + ++ /* calculate the high min value */ ++ scl_high_min = i2c_cal_high_min_config(i2c_bus, (u64)base_clk); ++ dev_dbg(i2c_bus->dev, "scl_high_min: %d\n", scl_high_min); ++ + baseclk_idx = min(baseclk_idx, 0xff); + divisor = min(divisor, 32); + scl_low = min((DIV_ROUND_UP(divisor * 9, 16)) - 1, 15); + scl_high = (divisor - scl_low - 2) & GENMASK(3, 0); -+ data = (scl_high - 1) << 20 | scl_high << 16 | scl_low << 12 | baseclk_idx; ++ ++ /* fill manual min high value */ ++ if (i2c_bus->manual_min_high) { ++ if (i2c_bus->manual_min_high > scl_high) ++ dev_info(i2c_bus->dev, ++ "invalid manual high min: %d\n", i2c_bus->manual_min_high); ++ else ++ scl_high_min = i2c_bus->manual_min_high; ++ } ++ ++ /* fill manual sda hold value */ ++ if (i2c_bus->manual_data_hold) { ++ if (i2c_bus->manual_data_hold > DATAHOLD_MAX_LEVEL) ++ dev_info(i2c_bus->dev, ++ "invalid manual data hold: %d\n", i2c_bus->manual_data_hold); ++ else ++ sda_data_hold = i2c_bus->manual_data_hold; ++ } ++ ++ data = baseclk_idx; ++ data |= scl_high_min << 20 | scl_high << 16 | scl_low << 12 | sda_data_hold << 10; + + if (i2c_bus->timeout) { + i2c_bus->timeout = min(i2c_bus->timeout, 255); @@ -35068,6 +41019,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + /* timeout_base set as 1ms */ + data |= AST2600_I2CC_TOUTBASECLK(AST2700_I2C_TIMEOUT_CLK); + } ++ dev_dbg(i2c_bus->dev, "ac: 0x%x\n", data); + + writel(data, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING); +} @@ -35075,11 +41027,17 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c +static int ast2600_i2c_recover_bus(struct ast2600_i2c_bus *i2c_bus) +{ + u32 state = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF); ++ u32 ctrl; + int ret = 0; + int r; + + dev_dbg(i2c_bus->dev, "%d-bus recovery bus [%x]\n", i2c_bus->adap.nr, state); + ++ /* reset i2c controller to avoid the bus getting stuck */ ++ ctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL); ++ writel(ctrl & ~AST2600_I2CC_MASTER_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL); ++ writel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL); ++ + reinit_completion(&i2c_bus->cmd_complete); + i2c_bus->cmd_err = 0; + @@ -35157,11 +41115,12 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); + writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS); + /* clear sirq log */ -+ while (readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG)) { ++ while ((sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG))) { + /* assign the target client*/ + if (sirq_log & SADDR_HIT) { + if (!i2c_bus->target) -+ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ ast2700_i2c_get_target(i2c_bus, ++ sirq_log >> SLAVE_ADDR_SHIFT); + } + }; + writel(isr, i2c_bus->reg_base + AST2600_I2CS_ISR); @@ -35179,11 +41138,12 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); + writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS); + /* clear sirq log */ -+ while (readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG)) { ++ while ((sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG))) { + /* assign the target client*/ + if (sirq_log & SADDR_HIT) { + if (!i2c_bus->target) -+ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ ast2700_i2c_get_target(i2c_bus, ++ sirq_log >> SLAVE_ADDR_SHIFT); + } + }; + writel(isr, i2c_bus->reg_base + AST2600_I2CS_ISR); @@ -36009,7 +41969,6 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c +{ + struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index]; + int xfer_len = msg->len - i2c_bus->controller_xfer_cnt; -+ int ret; + + cmd |= AST2600_I2CM_PKT_EN; + @@ -36018,32 +41977,14 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + else if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count) + cmd |= AST2600_I2CM_STOP_CMD; + -+ if (cmd & AST2600_I2CM_START_CMD) { ++ if (cmd & AST2600_I2CM_START_CMD) + cmd |= AST2600_I2CM_PKT_ADDR(msg->addr); -+ if (xfer_len) { -+ i2c_bus->controller_safe_buf = i2c_get_dma_safe_msg_buf(msg, 1); -+ if (!i2c_bus->controller_safe_buf) -+ return -ENOMEM; -+ i2c_bus->controller_dma_addr = -+ dma_map_single(i2c_bus->dev, i2c_bus->controller_safe_buf, -+ msg->len, DMA_TO_DEVICE); -+ ret = dma_mapping_error(i2c_bus->dev, i2c_bus->controller_dma_addr); -+ if (ret) { -+ i2c_put_dma_safe_msg_buf(i2c_bus->controller_safe_buf, msg, false); -+ i2c_bus->controller_safe_buf = NULL; -+ return ret; -+ } -+ } -+ } + + if (xfer_len) { ++ memcpy(i2c_bus->controller_dma_buf, msg->buf, xfer_len); + cmd |= AST2600_I2CM_TX_DMA_EN | AST2600_I2CM_TX_CMD; + writel(AST2600_I2CM_SET_TX_DMA_LEN(xfer_len - 1), + i2c_bus->reg_base + AST2600_I2CM_DMA_LEN); -+ writel(lower_32_bits(i2c_bus->controller_dma_addr), -+ i2c_bus->reg_base + AST2600_I2CM_TX_DMA); -+ writel(upper_32_bits(i2c_bus->controller_dma_addr), -+ i2c_bus->reg_base + AST2600_I2CM_TX_DMA_H); + } + + writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS); @@ -36076,7 +42017,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + * and write the remaining unaligned data at the end. + */ + if (readl(i2c_bus->reg_base + AST2600_I2CS_ISR)) -+ return -ENOMEM; ++ return -EBUSY; + for (i = 0; i < xfer_len; i += 4) { + int xfer_cnt = i2c_bus->controller_xfer_cnt + i; + @@ -36097,13 +42038,13 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + writel(wbuf_dword, i2c_bus->buf_base + i); + } + if (readl(i2c_bus->reg_base + AST2600_I2CS_ISR)) -+ return -ENOMEM; ++ return -EBUSY; + writel(AST2600_I2CC_SET_TX_BUF_LEN(xfer_len), + i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL); + } + + if (readl(i2c_bus->reg_base + AST2600_I2CS_ISR)) -+ return -ENOMEM; ++ return -EBUSY; + + writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS); + @@ -36141,7 +42082,6 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c +{ + struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index]; + int xfer_len = msg->len - i2c_bus->controller_xfer_cnt; -+ int ret; + + cmd |= AST2600_I2CM_PKT_EN | AST2600_I2CM_RX_DMA_EN | AST2600_I2CM_RX_CMD; + @@ -36156,33 +42096,8 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + + writel(AST2600_I2CM_SET_RX_DMA_LEN(xfer_len - 1), i2c_bus->reg_base + AST2600_I2CM_DMA_LEN); + -+ if (cmd & AST2600_I2CM_START_CMD) { ++ if (cmd & AST2600_I2CM_START_CMD) + cmd |= AST2600_I2CM_PKT_ADDR(msg->addr); -+ i2c_bus->controller_safe_buf = i2c_get_dma_safe_msg_buf(msg, 1); -+ if (!i2c_bus->controller_safe_buf) -+ return -ENOMEM; -+ if (msg->flags & I2C_M_RECV_LEN) -+ i2c_bus->controller_dma_addr = -+ dma_map_single(i2c_bus->dev, i2c_bus->controller_safe_buf, -+ I2C_SMBUS_BLOCK_MAX + 3, DMA_FROM_DEVICE); -+ else -+ i2c_bus->controller_dma_addr = -+ dma_map_single(i2c_bus->dev, i2c_bus->controller_safe_buf, -+ msg->len, DMA_FROM_DEVICE); -+ ret = dma_mapping_error(i2c_bus->dev, i2c_bus->controller_dma_addr); -+ if (ret) { -+ i2c_put_dma_safe_msg_buf(i2c_bus->controller_safe_buf, msg, false); -+ i2c_bus->controller_safe_buf = NULL; -+ return -ENOMEM; -+ } -+ } -+ -+ writel(lower_32_bits(i2c_bus->controller_dma_addr + -+ i2c_bus->controller_xfer_cnt), -+ i2c_bus->reg_base + AST2600_I2CM_RX_DMA); -+ writel(upper_32_bits(i2c_bus->controller_dma_addr + -+ i2c_bus->controller_xfer_cnt), -+ i2c_bus->reg_base + AST2600_I2CM_RX_DMA_H); + + writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS); + @@ -36319,7 +42234,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + i2c_bus->msgs_index++; + if (i2c_bus->msgs_index < i2c_bus->msgs_count) { + if (ast2600_i2c_do_start(i2c_bus)) { -+ i2c_bus->cmd_err = -ENOMEM; ++ i2c_bus->cmd_err = -EBUSY; + complete(&i2c_bus->cmd_complete); + } + } else { @@ -36329,31 +42244,26 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + break; + case AST2600_I2CM_TX_ACK: + case AST2600_I2CM_TX_ACK | AST2600_I2CM_NORMAL_STOP: -+ if (i2c_bus->mode == DMA_MODE) ++ if (i2c_bus->mode == DMA_MODE) { + xfer_len = AST2600_I2C_GET_TX_DMA_LEN(readl(i2c_bus->reg_base + -+ AST2600_I2CM_DMA_LEN_STS)); -+ else if (i2c_bus->mode == BUFF_MODE) ++ AST2600_I2CM_DMA_LEN_STS)); ++ } else if (i2c_bus->mode == BUFF_MODE) { + xfer_len = AST2600_I2CC_GET_TX_BUF_LEN(readl(i2c_bus->reg_base + + AST2600_I2CC_BUFF_CTRL)); -+ else ++ } else { + xfer_len = 1; ++ } + + i2c_bus->controller_xfer_cnt += xfer_len; + + if (i2c_bus->controller_xfer_cnt == msg->len) { -+ if (i2c_bus->mode == DMA_MODE) { -+ dma_unmap_single(i2c_bus->dev, i2c_bus->controller_dma_addr, msg->len, -+ DMA_TO_DEVICE); -+ i2c_put_dma_safe_msg_buf(i2c_bus->controller_safe_buf, msg, true); -+ i2c_bus->controller_safe_buf = NULL; -+ } + i2c_bus->msgs_index++; + if (i2c_bus->msgs_index == i2c_bus->msgs_count) { + i2c_bus->cmd_err = i2c_bus->msgs_index; + complete(&i2c_bus->cmd_complete); + } else { + if (ast2600_i2c_do_start(i2c_bus)) { -+ i2c_bus->cmd_err = -ENOMEM; ++ i2c_bus->cmd_err = -EBUSY; + complete(&i2c_bus->cmd_complete); + } + } @@ -36386,6 +42296,8 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + if (i2c_bus->mode == DMA_MODE) { + xfer_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + + AST2600_I2CM_DMA_LEN_STS)); ++ memcpy(&msg->buf[i2c_bus->controller_xfer_cnt], ++ i2c_bus->controller_dma_buf, xfer_len); + } else if (i2c_bus->mode == BUFF_MODE) { + xfer_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base + + AST2600_I2CC_BUFF_CTRL)); @@ -36422,20 +42334,13 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + } + + if (i2c_bus->controller_xfer_cnt == msg->len) { -+ if (i2c_bus->mode == DMA_MODE) { -+ dma_unmap_single(i2c_bus->dev, i2c_bus->controller_dma_addr, msg->len, -+ DMA_FROM_DEVICE); -+ i2c_put_dma_safe_msg_buf(i2c_bus->controller_safe_buf, msg, true); -+ i2c_bus->controller_safe_buf = NULL; -+ } -+ + i2c_bus->msgs_index++; + if (i2c_bus->msgs_index == i2c_bus->msgs_count) { + i2c_bus->cmd_err = i2c_bus->msgs_index; + complete(&i2c_bus->cmd_complete); + } else { + if (ast2600_i2c_do_start(i2c_bus)) { -+ i2c_bus->cmd_err = -ENOMEM; ++ i2c_bus->cmd_err = -EBUSY; + complete(&i2c_bus->cmd_complete); + } + } @@ -36580,36 +42485,12 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + dev_dbg(i2c_bus->dev, "timeout isr[%x], sts[%x]\n", + readl(i2c_bus->reg_base + AST2600_I2CM_ISR), + readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF)); -+ writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL); ++ writel(ctrl & ~AST2600_I2CC_MASTER_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL); + writel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL); -+ if (i2c_bus->multi_master && ++ if (i2c_bus->multi_master && !i2c_bus->target_operate && + (readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) & + AST2600_I2CC_BUS_BUSY_STS)) + ast2600_i2c_recover_bus(i2c_bus); -+#if IS_ENABLED(CONFIG_I2C_SLAVE) -+ if (ctrl & AST2600_I2CC_SLAVE_EN) { -+ u32 cmd = TARGET_TRIGGER_CMD; -+ -+ if (i2c_bus->mode == DMA_MODE) { -+ cmd |= AST2600_I2CS_RX_DMA_EN; -+ writel(lower_32_bits(i2c_bus->target_dma_addr), -+ i2c_bus->reg_base + AST2600_I2CS_RX_DMA); -+ writel(upper_32_bits(i2c_bus->target_dma_addr), -+ i2c_bus->reg_base + AST2600_I2CS_RX_DMA_H); -+ writel(lower_32_bits(i2c_bus->target_dma_addr), -+ i2c_bus->reg_base + AST2600_I2CS_TX_DMA); -+ writel(upper_32_bits(i2c_bus->target_dma_addr), -+ i2c_bus->reg_base + AST2600_I2CS_TX_DMA_H); -+ writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE), -+ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); -+ } else if (i2c_bus->mode == BUFF_MODE) { -+ cmd = TARGET_TRIGGER_CMD; -+ } else { -+ cmd &= ~AST2600_I2CS_PKT_MODE_EN; -+ } -+ writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS); -+ } -+#endif + ret = -ETIMEDOUT; + } else { + ret = i2c_bus->cmd_err; @@ -36618,26 +42499,10 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + dev_dbg(i2c_bus->dev, "bus%d-m: %d end\n", i2c_bus->adap.nr, i2c_bus->cmd_err); + +controller_out: -+ if (i2c_bus->mode == DMA_MODE) { -+ /* still have controller_safe_buf need to be released */ -+ if (i2c_bus->controller_safe_buf) { -+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index]; -+ -+ if (msg->flags & I2C_M_RD) -+ dma_unmap_single(i2c_bus->dev, i2c_bus->controller_dma_addr, msg->len, -+ DMA_FROM_DEVICE); -+ else -+ dma_unmap_single(i2c_bus->dev, i2c_bus->controller_dma_addr, msg->len, -+ DMA_TO_DEVICE); -+ i2c_put_dma_safe_msg_buf(i2c_bus->controller_safe_buf, msg, true); -+ i2c_bus->controller_safe_buf = NULL; -+ } -+ } -+ + return ret; +} + -+static void ast2600_i2c_init(struct ast2600_i2c_bus *i2c_bus) ++static int ast2600_i2c_init(struct ast2600_i2c_bus *i2c_bus) +{ + struct platform_device *pdev = to_platform_device(i2c_bus->dev); + u32 fun_ctrl = AST2600_I2CC_BUS_AUTO_RELEASE | AST2600_I2CC_MASTER_EN; @@ -36649,6 +42514,16 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + if (!i2c_bus->multi_master) + fun_ctrl |= AST2600_I2CC_MULTI_MASTER_DIS; + ++ /* I2C manual minimum SCL high */ ++ if (device_property_read_u32(&pdev->dev, "manual-min-high", ++ &i2c_bus->manual_min_high)) ++ i2c_bus->manual_min_high = 0; ++ ++ /* I2C manual data hold */ ++ if (device_property_read_u32(&pdev->dev, "manual-data-hold", ++ &i2c_bus->manual_data_hold)) ++ i2c_bus->manual_data_hold = 0; ++ + /* I2C Debounce level */ + if (i2c_bus->version != AST2600) { + if (!device_property_read_u32(&pdev->dev, "debounce-level", @@ -36684,6 +42559,22 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + else + ast2600_i2c_ac_timing_config(i2c_bus); + ++ if (i2c_bus->mode == DMA_MODE) { ++ i2c_bus->controller_dma_buf = ++ dmam_alloc_coherent(i2c_bus->dev, AST2600_I2C_DMA_SIZE, ++ &i2c_bus->controller_dma_addr, GFP_KERNEL); ++ if (!i2c_bus->controller_dma_buf) ++ return -ENOMEM; ++ writel(lower_32_bits(i2c_bus->controller_dma_addr), ++ i2c_bus->reg_base + AST2600_I2CM_TX_DMA); ++ writel(upper_32_bits(i2c_bus->controller_dma_addr), ++ i2c_bus->reg_base + AST2600_I2CM_TX_DMA_H); ++ writel(lower_32_bits(i2c_bus->controller_dma_addr), ++ i2c_bus->reg_base + AST2600_I2CM_RX_DMA); ++ writel(upper_32_bits(i2c_bus->controller_dma_addr), ++ i2c_bus->reg_base + AST2600_I2CM_RX_DMA_H); ++ } ++ + /* Clear Interrupt */ + writel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CM_ISR); + @@ -36694,7 +42585,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + dmam_alloc_coherent(i2c_bus->dev, I2C_TARGET_MSG_BUF_SIZE, + &i2c_bus->target_dma_addr, GFP_KERNEL); + if (!i2c_bus->target_dma_buf) -+ return; ++ return -ENOMEM; + } + + writel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CS_ISR); @@ -36704,6 +42595,8 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + else + writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER); +#endif ++ ++ return 0; +} + +#if IS_ENABLED(CONFIG_I2C_SLAVE) @@ -36724,7 +42617,8 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + for (i = 0; i < AST2600_I2C_TARGET_COUNT; i++) { + if (i2c_bus->multi_target[i]) { + if (i2c_bus->multi_target[i]->addr == client->addr) { -+ dev_dbg(i2c_bus->dev, "duplicate address [%x] on %d\n", client->addr, i); ++ dev_dbg(i2c_bus->dev, "duplicate address [%x] on %d\n", ++ client->addr, i); + return -EINVAL; + } + } @@ -36777,14 +42671,20 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + /* trigger rx buffer */ + if (i2c_bus->mode == DMA_MODE) { + cmd |= AST2600_I2CS_RX_DMA_EN; -+ writel(lower_32_bits(i2c_bus->target_dma_addr), i2c_bus->reg_base + AST2600_I2CS_RX_DMA); -+ writel(upper_32_bits(i2c_bus->target_dma_addr), i2c_bus->reg_base + AST2600_I2CS_RX_DMA_H); -+ writel(lower_32_bits(i2c_bus->target_dma_addr), i2c_bus->reg_base + AST2600_I2CS_TX_DMA); -+ writel(upper_32_bits(i2c_bus->target_dma_addr), i2c_bus->reg_base + AST2600_I2CS_TX_DMA_H); ++ writel(lower_32_bits(i2c_bus->target_dma_addr), ++ i2c_bus->reg_base + AST2600_I2CS_RX_DMA); ++ writel(upper_32_bits(i2c_bus->target_dma_addr), ++ i2c_bus->reg_base + AST2600_I2CS_RX_DMA_H); ++ writel(lower_32_bits(i2c_bus->target_dma_addr), ++ i2c_bus->reg_base + AST2600_I2CS_TX_DMA); ++ writel(upper_32_bits(i2c_bus->target_dma_addr), ++ i2c_bus->reg_base + AST2600_I2CS_TX_DMA_H); + writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE), + i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); + } else if (i2c_bus->mode == BUFF_MODE) { + cmd = TARGET_TRIGGER_CMD; ++ if (i2c_bus->version == AST2700) ++ cmd |= AST2600_I2CS_RX_DMA_EN; + } else { + cmd &= ~AST2600_I2CS_PKT_MODE_EN; + } @@ -36813,20 +42713,8 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + i2c_bus->multi_target[i] = NULL; + target_unreg = true; + dev_dbg(i2c_bus->dev, "un-reg [%x] from %d\n", client->addr, i); -+ + /* remove target addr by index */ -+ switch (i) { -+ case 0: -+ target_addr &= ~(AST2600_I2CS_ADDR1_MASK | AST2600_I2CS_ADDR1_ENABLE); -+ break; -+ case 1: -+ target_addr &= ~(AST2600_I2CS_ADDR2_MASK | AST2600_I2CS_ADDR2_ENABLE); -+ break; -+ case 2: -+ target_addr &= ~(AST2600_I2CS_ADDR3_MASK | AST2600_I2CS_ADDR3_ENABLE); -+ break; -+ } -+ ++ target_addr &= ~(0xff << 8 * i); + writel(target_addr, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL); + break; + } @@ -36897,9 +42785,11 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + if ((global_ctrl & AST2600_GLOBAL_INIT) != AST2600_GLOBAL_INIT) { + regmap_write(i2c_bus->global_regs, AST2600_I2CG_CTRL, AST2600_GLOBAL_INIT); + if (i2c_bus->version == AST2600) -+ regmap_write(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, AST2600_I2CCG_DIV_CTRL); ++ regmap_write(i2c_bus->global_regs, ++ AST2600_I2CG_CLK_DIV_CTRL, AST2600_I2CCG_DIV_CTRL); + else -+ regmap_write(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, AST2700_I2CCG_DIV_CTRL); ++ regmap_write(i2c_bus->global_regs, ++ AST2600_I2CG_CLK_DIV_CTRL, AST2700_I2CCG_DIV_CTRL); + } + +#if IS_ENABLED(CONFIG_I2C_SLAVE) @@ -36972,7 +42862,9 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + i2c_set_adapdata(&i2c_bus->adap, i2c_bus); + dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + -+ ast2600_i2c_init(i2c_bus); ++ ret = ast2600_i2c_init(i2c_bus); ++ if (ret < 0) ++ return dev_err_probe(dev, ret, "Unable to initial i2c %d\n", ret); + + ret = devm_request_irq(dev, i2c_bus->irq, ast2600_i2c_bus_irq, 0, + dev_name(dev), i2c_bus); @@ -37039,7 +42931,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c +MODULE_LICENSE("GPL"); diff --git a/drivers/i3c/master/ast2600-i3c-master.c b/drivers/i3c/master/ast2600-i3c-master.c --- a/drivers/i3c/master/ast2600-i3c-master.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/i3c/master/ast2600-i3c-master.c 2025-12-23 10:16:21.088033273 +0000 ++++ b/drivers/i3c/master/ast2600-i3c-master.c 2026-04-08 18:03:48.267706106 +0000 @@ -10,6 +10,8 @@ #include #include @@ -37049,7 +42941,7 @@ diff --git a/drivers/i3c/master/ast2600-i3c-master.c b/drivers/i3c/master/ast260 #include "dw-i3c-master.h" -@@ -33,18 +35,93 @@ +@@ -33,18 +35,97 @@ #define AST2600_I3CG_REG1_SA_EN BIT(15) #define AST2600_I3CG_REG1_INST_ID_MASK GENMASK(19, 16) #define AST2600_I3CG_REG1_INST_ID(x) (((x) << 16) & AST2600_I3CG_REG1_INST_ID_MASK) @@ -37095,12 +42987,16 @@ diff --git a/drivers/i3c/master/ast2600-i3c-master.c b/drivers/i3c/master/ast260 +#define ADDR_HID(x) ((x) & ADDR_HID_MASK) + +#define IBI_QUEUE_STATUS 0x18 ++#define IBI_QUEUE_STATUS_DATA_LEN(x) ((x) & GENMASK(7, 0)) + +#define IBI_SIR_REQ_REJECT 0x30 +#define INTR_STATUS_EN 0x40 +#define INTR_SIGNAL_EN 0x44 +#define INTR_IBI_THLD_STAT BIT(2) + ++#define QUEUE_STATUS_LEVEL 0x4c ++#define QUEUE_STATUS_IBI_STATUS_CNT(x) (((x) & GENMASK(28, 24)) >> 24) ++ +#define PRESENT_STATE 0x54 +#define CM_TFR_STS GENMASK(13, 8) +#define CM_TFR_STS_MASTER_SERV_IBI 0xe @@ -37143,7 +43039,7 @@ diff --git a/drivers/i3c/master/ast2600-i3c-master.c b/drivers/i3c/master/ast260 static struct ast2600_i3c *to_ast2600_i3c(struct dw_i3c_master *dw) { return container_of(dw, struct ast2600_i3c, dw); -@@ -117,9 +194,537 @@ +@@ -117,9 +198,559 @@ } } @@ -37293,6 +43189,20 @@ diff --git a/drivers/i3c/master/ast2600-i3c-master.c b/drivers/i3c/master/ast260 + SDA_IN_SW_MODE_EN | SCL_IN_SW_MODE_EN, 0); +} + ++static void aspeed_i3c_drain_ibi_queue(struct dw_i3c_master *dw) ++{ ++ /* ++ * Clear the IBI queue to avoid any stale IBI data when ++ * re-enabling the controller. ++ */ ++ u32 ibi_status = readl(dw->regs + IBI_QUEUE_STATUS); ++ u8 length = IBI_QUEUE_STATUS_DATA_LEN(ibi_status); ++ int i, nwords = (length + 3) >> 2; ++ ++ for (i = 0; i < nwords; i++) ++ readl(dw->regs + IBI_QUEUE_STATUS); ++} ++ +static bool ast2600_i3c_fsm_exit_serv_ibi(struct dw_i3c_master *dw) +{ + u32 state; @@ -37301,7 +43211,7 @@ diff --git a/drivers/i3c/master/ast2600-i3c-master.c b/drivers/i3c/master/ast260 + * Clear the IBI queue to enable the hardware to generate SCL and + * begin detecting the T-bit low to stop reading IBI data. + */ -+ readl(dw->regs + IBI_QUEUE_STATUS); ++ aspeed_i3c_drain_ibi_queue(dw); + state = FIELD_GET(CM_TFR_STS, readl(dw->regs + PRESENT_STATE)); + if (state == CM_TFR_STS_MASTER_SERV_IBI) + return false; @@ -37312,7 +43222,8 @@ diff --git a/drivers/i3c/master/ast2600-i3c-master.c b/drivers/i3c/master/ast260 +static void ast2600_i3c_gen_tbits_in(struct dw_i3c_master *dw) +{ + struct ast2600_i3c *i3c = to_ast2600_i3c(dw); -+ bool is_idle; ++ bool is_halted; ++ u32 nibi, i; + int ret; + + regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx), @@ -37323,14 +43234,21 @@ diff --git a/drivers/i3c/master/ast2600-i3c-master.c b/drivers/i3c/master/ast260 + regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx), + SDA_IN_SW_MODE_VAL, 0); + ret = readx_poll_timeout_atomic(ast2600_i3c_fsm_exit_serv_ibi, dw, -+ is_idle, is_idle, 0, 2000000); ++ is_halted, is_halted, 0, 2000000); + regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx), + SDA_IN_SW_MODE_EN, 0); -+ if (ret) ++ if (ret) { + dev_err(&dw->base.dev, + "Failed to exit the I3C fsm from %lx(MASTER_SERV_IBI): %d", + FIELD_GET(CM_TFR_STS, readl(dw->regs + PRESENT_STATE)), + ret); ++ } else { ++ /* Clear the dummy data generated in this recovery process */ ++ nibi = readl(dw->regs + QUEUE_STATUS_LEVEL); ++ nibi = QUEUE_STATUS_IBI_STATUS_CNT(nibi); ++ for (i = 0; i < nibi; i++) ++ aspeed_i3c_drain_ibi_queue(dw); ++ } +} + +static void ast2600_i3c_set_ibi_mdb(struct dw_i3c_master *dw, u8 mdb) @@ -37681,7 +43599,7 @@ diff --git a/drivers/i3c/master/ast2600-i3c-master.c b/drivers/i3c/master/ast260 }; static int ast2600_i3c_probe(struct platform_device *pdev) -@@ -156,6 +761,10 @@ +@@ -156,6 +787,10 @@ i3c->sda_pullup); i3c->dw.platform_ops = &ast2600_i3c_ops; @@ -37694,7 +43612,7 @@ diff --git a/drivers/i3c/master/ast2600-i3c-master.c b/drivers/i3c/master/ast260 diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h --- a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h 2025-12-23 10:16:21.082033373 +0000 ++++ b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h 2026-04-08 18:03:48.261706215 +0000 @@ -0,0 +1,413 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#ifndef VENDOR_ASPEED_H @@ -38111,7 +44029,7 @@ diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h b/drivers/i3c/maste +#endif diff --git a/drivers/i3c/mctp/Kconfig b/drivers/i3c/mctp/Kconfig --- a/drivers/i3c/mctp/Kconfig 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/i3c/mctp/Kconfig 2025-12-23 10:16:19.325062821 +0000 ++++ b/drivers/i3c/mctp/Kconfig 2026-04-08 18:03:46.423739796 +0000 @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0-only +config I3C_MCTP @@ -38138,14 +44056,14 @@ diff --git a/drivers/i3c/mctp/Kconfig b/drivers/i3c/mctp/Kconfig + configured as an I3C Target Device on the I3C Bus. diff --git a/drivers/i3c/mctp/Makefile b/drivers/i3c/mctp/Makefile --- a/drivers/i3c/mctp/Makefile 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/i3c/mctp/Makefile 2025-12-23 10:16:19.325062821 +0000 ++++ b/drivers/i3c/mctp/Makefile 2026-04-08 18:03:46.423739796 +0000 @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_I3C_MCTP) += i3c-mctp.o +obj-$(CONFIG_I3C_TARGET_MCTP) += i3c-target-mctp.o diff --git a/drivers/i3c/mctp/i3c-mctp.c b/drivers/i3c/mctp/i3c-mctp.c --- a/drivers/i3c/mctp/i3c-mctp.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/i3c/mctp/i3c-mctp.c 2025-12-23 10:16:19.325062821 +0000 ++++ b/drivers/i3c/mctp/i3c-mctp.c 2026-04-08 18:03:46.423739796 +0000 @@ -0,0 +1,697 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2022 Intel Corporation.*/ @@ -38846,7 +44764,7 @@ diff --git a/drivers/i3c/mctp/i3c-mctp.c b/drivers/i3c/mctp/i3c-mctp.c +MODULE_LICENSE("GPL"); diff --git a/drivers/i3c/mctp/i3c-target-mctp.c b/drivers/i3c/mctp/i3c-target-mctp.c --- a/drivers/i3c/mctp/i3c-target-mctp.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/i3c/mctp/i3c-target-mctp.c 2025-12-23 10:16:19.325062821 +0000 ++++ b/drivers/i3c/mctp/i3c-target-mctp.c 2026-04-08 18:03:46.423739796 +0000 @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2022 Intel Corporation.*/ @@ -39335,7 +45253,7 @@ diff --git a/drivers/i3c/mctp/i3c-target-mctp.c b/drivers/i3c/mctp/i3c-target-mc +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c --- a/drivers/iio/adc/aspeed_adc.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/iio/adc/aspeed_adc.c 2025-12-23 10:16:20.950035586 +0000 ++++ b/drivers/iio/adc/aspeed_adc.c 2026-04-08 18:03:48.116708865 +0000 @@ -72,6 +72,8 @@ #define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13) #define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16) @@ -39822,22 +45740,34 @@ diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c { } }; MODULE_DEVICE_TABLE(of, aspeed_adc_matches); +diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig +--- a/drivers/irqchip/Kconfig 2026-04-08 18:03:23.262162447 +0000 ++++ b/drivers/irqchip/Kconfig 2026-04-08 18:03:34.708953665 +0000 +@@ -734,6 +734,9 @@ + Support for the Apple Interrupt Controller found on Apple Silicon SoCs, + such as the M1. + ++config AST2700_IRQ ++ bool ++ + config MCHP_EIC + bool "Microchip External Interrupt Controller" + depends on ARCH_AT91 || COMPILE_TEST diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile ---- a/drivers/irqchip/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/irqchip/Makefile 2025-12-23 10:16:13.379162479 +0000 -@@ -83,8 +83,7 @@ - obj-$(CONFIG_MVEBU_SEI) += irq-mvebu-sei.o - obj-$(CONFIG_LS_EXTIRQ) += irq-ls-extirq.o +--- a/drivers/irqchip/Makefile 2026-04-08 18:03:23.262162447 +0000 ++++ b/drivers/irqchip/Makefile 2026-04-08 18:03:39.587864679 +0000 +@@ -85,6 +85,8 @@ obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o --obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o irq-aspeed-scu-ic.o --obj-$(CONFIG_STM32MP_EXTI) += irq-stm32mp-exti.o + obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o irq-aspeed-scu-ic.o + obj-$(CONFIG_STM32MP_EXTI) += irq-stm32mp-exti.o +obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o irq-aspeed-scu-ic.o irq-aspeed-intc.o irq-aspeed-e2m-ic.o ++obj-$(CONFIG_AST2700_IRQ) += irq-ast2700-intc0.o irq-ast2700-intc1.o obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o obj-$(CONFIG_IRQ_UNIPHIER_AIDET) += irq-uniphier-aidet.o diff --git a/drivers/irqchip/irq-aspeed-e2m-ic.c b/drivers/irqchip/irq-aspeed-e2m-ic.c --- a/drivers/irqchip/irq-aspeed-e2m-ic.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/irqchip/irq-aspeed-e2m-ic.c 2025-12-23 10:16:21.166031966 +0000 ++++ b/drivers/irqchip/irq-aspeed-e2m-ic.c 2026-04-08 18:03:48.342704735 +0000 @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* @@ -40019,7 +45949,7 @@ diff --git a/drivers/irqchip/irq-aspeed-e2m-ic.c b/drivers/irqchip/irq-aspeed-e2 + aspeed_ast2700_e2m_ic_of_init); diff --git a/drivers/irqchip/irq-aspeed-intc.c b/drivers/irqchip/irq-aspeed-intc.c --- a/drivers/irqchip/irq-aspeed-intc.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/irqchip/irq-aspeed-intc.c 2025-12-23 10:16:21.173031848 +0000 ++++ b/drivers/irqchip/irq-aspeed-intc.c 2026-04-08 18:03:48.349704608 +0000 @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* @@ -40110,7 +46040,7 @@ diff --git a/drivers/irqchip/irq-aspeed-intc.c b/drivers/irqchip/irq-aspeed-intc + struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); + unsigned int mask; + -+ guard(raw_spinlock)(&intc_ic->intc_lock); ++ guard(raw_spinlock_irqsave)(&intc_ic->intc_lock); + mask = readl(intc_ic->base + INTC_INT_ENABLE_REG) & ~BIT(data->hwirq); + writel(mask, intc_ic->base + INTC_INT_ENABLE_REG); +} @@ -40120,7 +46050,7 @@ diff --git a/drivers/irqchip/irq-aspeed-intc.c b/drivers/irqchip/irq-aspeed-intc + struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); + unsigned int unmask; + -+ guard(raw_spinlock)(&intc_ic->intc_lock); ++ guard(raw_spinlock_irqsave)(&intc_ic->intc_lock); + unmask = readl(intc_ic->base + INTC_INT_ENABLE_REG) | BIT(data->hwirq); + writel(unmask, intc_ic->base + INTC_INT_ENABLE_REG); +} @@ -40211,7 +46141,7 @@ diff --git a/drivers/irqchip/irq-aspeed-intc.c b/drivers/irqchip/irq-aspeed-intc +IRQCHIP_DECLARE(ast2700_intc_ic, "aspeed,ast2700-intc-ic", aspeed_intc_ic_of_init); diff --git a/drivers/irqchip/irq-aspeed-scu-ic.c b/drivers/irqchip/irq-aspeed-scu-ic.c --- a/drivers/irqchip/irq-aspeed-scu-ic.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/irqchip/irq-aspeed-scu-ic.c 2025-12-23 10:16:21.177031781 +0000 ++++ b/drivers/irqchip/irq-aspeed-scu-ic.c 2026-04-08 18:03:48.352704553 +0000 @@ -1,61 +1,76 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* @@ -40580,9 +46510,780 @@ diff --git a/drivers/irqchip/irq-aspeed-scu-ic.c b/drivers/irqchip/irq-aspeed-sc +IRQCHIP_DECLARE(ast2700_scu_ic1, "aspeed,ast2700-scu-ic1", aspeed_scu_ic_of_init); +IRQCHIP_DECLARE(ast2700_scu_ic2, "aspeed,ast2700-scu-ic2", aspeed_scu_ic_of_init); +IRQCHIP_DECLARE(ast2700_scu_ic3, "aspeed,ast2700-scu-ic3", aspeed_scu_ic_of_init); +diff --git a/drivers/irqchip/irq-ast2700-intc0.c b/drivers/irqchip/irq-ast2700-intc0.c +--- a/drivers/irqchip/irq-ast2700-intc0.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/irqchip/irq-ast2700-intc0.c 2026-04-08 18:03:48.199707348 +0000 +@@ -0,0 +1,509 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Aspeed Interrupt Controller. ++ * ++ * Copyright (C) 2023 ASPEED Technology Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define INT_NUM 480 ++#define SWINT_NUM 16 ++#define INTM_NUM 50 ++ ++#define SWINT_BASE (INT_NUM) ++#define INTM_BASE (INT_NUM + SWINT_NUM) ++#define INT0_NUM (INT_NUM + SWINT_NUM + INTM_NUM) ++ ++#define GIC_P2P_SPI_END 128 ++#define GIC_SWINT_SPI_BASE 144 ++#define GIC_SWINT_SPI_NUM 16 ++#define GIC_INTM_SPI_BASE 192 ++ ++#define INTC0_SWINT_IER 0x10 ++#define INTC0_SWINT_ISR 0x14 ++#define INTC0_INTBANKX_IER 0x1000 ++#define INTC0_INTBANK_GROUPS 11 ++#define INTC0_INTBANKS_PER_GRP 3 ++#define INTC0_IMTMX_IER 0x1b00 ++#define INTC0_IMTMX_ISR 0x1b04 ++#define INTC0_IMTM_BANK_NUM 3 ++#define INTM_IRQS_PER_BANK 10 ++ ++struct intc0_pin_region { ++ u32 int_base; ++ u32 gic_base; ++ u32 cnt; ++}; ++ ++struct aspeed_intc_ic { ++ void __iomem *base; ++ raw_spinlock_t intc_lock; ++ struct irq_domain *irq_domain; ++ struct intc0_pin_region *pin_region; ++ int pin_region_cnt; ++}; ++ ++static void aspeed_swint_irq_mask(struct irq_data *data) ++{ ++ struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); ++ int bit = data->hwirq - SWINT_BASE; ++ unsigned int mask; ++ ++ guard(raw_spinlock_irqsave)(&intc_ic->intc_lock); ++ mask = readl(intc_ic->base + INTC0_SWINT_IER) & ~BIT(bit); ++ writel(mask, intc_ic->base + INTC0_SWINT_IER); ++ irq_chip_mask_parent(data); ++} ++ ++static void aspeed_swint_irq_unmask(struct irq_data *data) ++{ ++ struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); ++ int bit = data->hwirq - SWINT_BASE; ++ unsigned int unmask; ++ ++ guard(raw_spinlock_irqsave)(&intc_ic->intc_lock); ++ unmask = readl(intc_ic->base + INTC0_SWINT_IER) | BIT(bit); ++ writel(unmask, intc_ic->base + INTC0_SWINT_IER); ++ irq_chip_unmask_parent(data); ++} ++ ++static void aspeed_swint_irq_eoi(struct irq_data *data) ++{ ++ struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); ++ int bit = data->hwirq - SWINT_BASE; ++ ++ writel(BIT(bit), intc_ic->base + INTC0_SWINT_ISR); ++ irq_chip_eoi_parent(data); ++} ++ ++static struct irq_chip aspeed_swint_chip = { ++ .name = "ast2700-swint", ++ .irq_eoi = aspeed_swint_irq_eoi, ++ .irq_mask = aspeed_swint_irq_mask, ++ .irq_unmask = aspeed_swint_irq_unmask, ++ .irq_set_affinity = irq_chip_set_affinity_parent, ++ .flags = IRQCHIP_SET_TYPE_MASKED, ++}; ++ ++static void aspeed_intc0_irq_mask(struct irq_data *data) ++{ ++ struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); ++ int bank = (data->hwirq - INTM_BASE) / INTM_IRQS_PER_BANK; ++ int bit = (data->hwirq - INTM_BASE) % INTM_IRQS_PER_BANK; ++ unsigned int mask; ++ ++ guard(raw_spinlock_irqsave)(&intc_ic->intc_lock); ++ mask = readl(intc_ic->base + INTC0_IMTMX_IER + bank * 0x10) & ~BIT(bit); ++ writel(mask, intc_ic->base + INTC0_IMTMX_IER + bank * 0x10); ++ irq_chip_mask_parent(data); ++} ++ ++static void aspeed_intc0_irq_unmask(struct irq_data *data) ++{ ++ struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); ++ int bank = (data->hwirq - INTM_BASE) / INTM_IRQS_PER_BANK; ++ int bit = (data->hwirq - INTM_BASE) % INTM_IRQS_PER_BANK; ++ unsigned int unmask; ++ ++ guard(raw_spinlock_irqsave)(&intc_ic->intc_lock); ++ unmask = readl(intc_ic->base + INTC0_IMTMX_IER + bank * 0x10) | BIT(bit); ++ writel(unmask, intc_ic->base + INTC0_IMTMX_IER + bank * 0x10); ++ irq_chip_unmask_parent(data); ++} ++ ++static void aspeed_intc0_irq_eoi(struct irq_data *data) ++{ ++ struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); ++ int bank = (data->hwirq - INTM_BASE) / INTM_IRQS_PER_BANK; ++ int bit = (data->hwirq - INTM_BASE) % INTM_IRQS_PER_BANK; ++ ++ /* ++ * TODO: This a WA to prevnet potential race conditions when ++ * multiple interrupts are processed in multi-core environment. ++ */ ++ raw_spin_lock(&intc_ic->intc_lock); ++ writel(BIT(bit), intc_ic->base + INTC0_IMTMX_ISR + bank * 0x10); ++ raw_spin_unlock(&intc_ic->intc_lock); ++ ++ irq_chip_eoi_parent(data); ++} ++ ++static struct irq_chip aspeed_intm_chip = { ++ .name = "ast2700-intmerge", ++ .irq_eoi = aspeed_intc0_irq_eoi, ++ .irq_mask = aspeed_intc0_irq_mask, ++ .irq_unmask = aspeed_intc0_irq_unmask, ++ .irq_set_affinity = irq_chip_set_affinity_parent, ++ .flags = IRQCHIP_SET_TYPE_MASKED, ++ ++}; ++ ++static struct irq_chip linear_intr_irq_chip = { ++ .name = "ast2700-int", ++ .irq_eoi = irq_chip_eoi_parent, ++ .irq_mask = irq_chip_mask_parent, ++ .irq_unmask = irq_chip_unmask_parent, ++ .irq_set_affinity = irq_chip_set_affinity_parent, ++ .flags = IRQCHIP_SET_TYPE_MASKED, ++}; ++ ++static int aspeed_intc_ic0_map_irq_domain(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) ++{ ++ if (hwirq < GIC_P2P_SPI_END) ++ irq_set_chip_and_handler(irq, &linear_intr_irq_chip, handle_level_irq); ++ else if (hwirq < SWINT_BASE) ++ return -EINVAL; ++ else if (hwirq < INTM_BASE) ++ irq_set_chip_and_handler(irq, &aspeed_swint_chip, handle_level_irq); ++ else if (hwirq < INT0_NUM) ++ irq_set_chip_and_handler(irq, &aspeed_intm_chip, handle_level_irq); ++ else ++ return -EINVAL; ++ ++ irq_set_chip_data(irq, domain->host_data); ++ return 0; ++} ++ ++static int aspeed_intc0_irq_domain_translate(struct irq_domain *domain, ++ struct irq_fwspec *fwspec, ++ unsigned long *hwirq, ++ unsigned int *type) ++{ ++ if (fwspec->param_count != 1) ++ return -EINVAL; ++ ++ *hwirq = fwspec->param[0]; ++ *type = IRQ_TYPE_NONE; ++ return 0; ++} ++ ++static int get_parent_hwirq(struct aspeed_intc_ic *intc_ic, ++ u32 local_hwirq, ++ irq_hw_number_t *parent_hwirq) ++{ ++ int i; ++ ++ for (i = 0; i < intc_ic->pin_region_cnt; i++) { ++ u32 int_base = intc_ic->pin_region[i].int_base; ++ u32 cnt = intc_ic->pin_region[i].cnt; ++ ++ if (local_hwirq >= int_base && local_hwirq < int_base + cnt) { ++ *parent_hwirq = intc_ic->pin_region[i].gic_base + ++ (local_hwirq - int_base); ++ return 0; ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++static int aspeed_intc0_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, ++ unsigned int nr_irqs, void *data) ++{ ++ struct aspeed_intc_ic *intc_ic = domain->host_data; ++ struct irq_fwspec *fwspec = data; ++ struct irq_fwspec parent_fwspec; ++ irq_hw_number_t parent_hwirq; ++ struct irq_chip *chip; ++ unsigned long hwirq; ++ unsigned int type; ++ int ret; ++ ++ ret = aspeed_intc0_irq_domain_translate(domain, fwspec, &hwirq, &type); ++ if (ret) ++ return ret; ++ ++ if (hwirq >= GIC_P2P_SPI_END && hwirq < INT_NUM) ++ return -EINVAL; ++ ++ if (hwirq < SWINT_BASE) ++ chip = &linear_intr_irq_chip; ++ else if (hwirq < INTM_BASE) ++ chip = &aspeed_swint_chip; ++ else ++ chip = &aspeed_intm_chip; ++ ++ ret = get_parent_hwirq(intc_ic, (u32)hwirq, &parent_hwirq); ++ if (ret) ++ return irq_domain_disconnect_hierarchy(domain->parent, virq); ++ ++ parent_fwspec.fwnode = domain->parent->fwnode; ++ parent_fwspec.param_count = 3; ++ parent_fwspec.param[0] = GIC_SPI; ++ parent_fwspec.param[1] = parent_hwirq; ++ parent_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; ++ ++ ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_fwspec); ++ if (ret) ++ return ret; ++ ++ for (int i = 0; i < nr_irqs; ++i, ++hwirq, ++virq) { ++ ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, ++ chip, ++ domain->host_data); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int aspeed_intc0_irq_domain_activate(struct irq_domain *domain, ++ struct irq_data *data, bool reserve) ++{ ++ struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); ++ ++ if (data->hwirq < GIC_P2P_SPI_END) { ++ int bank = data->hwirq / 32; ++ int bit = data->hwirq % 32; ++ u32 mask = BIT(bit); ++ ++ guard(raw_spinlock_irqsave)(&intc_ic->intc_lock); ++ for (int i = 0; i < 3; i++) { ++ void __iomem *sel = intc_ic->base + 0x200 + bank * 4 + 0x100 * i; ++ ++ if (readl(sel) & mask) { ++ writel(readl(sel) & ~mask, sel); ++ if (readl(sel) & mask) ++ return -EACCES; ++ } ++ } ++ } else if (data->hwirq < INT_NUM) { ++ return -EINVAL; ++ } else if (data->hwirq < INT0_NUM) { ++ return 0; ++ } else { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops aspeed_intc0_ic_irq_domain_ops = { ++ .translate = aspeed_intc0_irq_domain_translate, ++ .alloc = aspeed_intc0_irq_domain_alloc, ++ .free = irq_domain_free_irqs_common, ++ .map = aspeed_intc_ic0_map_irq_domain, ++ .activate = aspeed_intc0_irq_domain_activate, ++}; ++ ++static int aspeed_intc0_init_gic_ranges(struct aspeed_intc_ic *intc_ic, ++ struct device_node *node, ++ struct device_node *parent_node) ++{ ++ struct intc0_pin_region *pin_region; ++ int region_cnt = 0; ++ int i, n, ret; ++ ++ if (!of_device_is_compatible(parent_node, "arm,gic-v3")) ++ return -ENOENT; ++ ++ n = of_property_count_elems_of_size(node, "aspeed,interrupt-ranges", sizeof(u32)); ++ if (n <= 0 || n % 6) ++ return -EINVAL; ++ ++ /* ++ * Each range is described as: ++ * ++ * and we only care about ranges whose phandle matches ++ * this controller's interrupt-parent (&gic). ++ */ ++ for (i = 0; i < n / 6; i++) { ++ struct device_node *target; ++ phandle parent_handle; ++ u32 gic_type; ++ ++ ret = of_property_read_u32_index(node, "aspeed,interrupt-ranges", ++ i * 6 + 2, &parent_handle); ++ if (ret) ++ return ret; ++ ++ target = of_find_node_by_phandle(parent_handle); ++ if (!target) ++ continue; ++ ++ if (target != parent_node) { ++ of_node_put(target); ++ continue; ++ } ++ ++ ret = of_property_read_u32_index(node, "aspeed,interrupt-ranges", ++ i * 6 + 3, &gic_type); ++ of_node_put(target); ++ if (ret) ++ return ret; ++ ++ if (gic_type != GIC_SPI) ++ continue; ++ ++ region_cnt++; ++ } ++ ++ if (!region_cnt) ++ return -EINVAL; ++ ++ pin_region = kcalloc(region_cnt, sizeof(*pin_region), GFP_KERNEL); ++ if (!pin_region) ++ return -ENOMEM; ++ ++ intc_ic->pin_region_cnt = region_cnt; ++ intc_ic->pin_region = pin_region; ++ ++ region_cnt = 0; ++ for (i = 0; i < n / 6; i++) { ++ struct device_node *target; ++ phandle parent_handle; ++ u32 gic_flags; ++ u32 gic_type; ++ ++ ret = of_property_read_u32_index(node, "aspeed,interrupt-ranges", ++ i * 6 + 2, &parent_handle); ++ if (ret) ++ return ret; ++ ++ target = of_find_node_by_phandle(parent_handle); ++ if (!target) ++ continue; ++ ++ if (target != parent_node) { ++ of_node_put(target); ++ continue; ++ } ++ ++ ret = of_property_read_u32_index(node, "aspeed,interrupt-ranges", ++ i * 6 + 0, ++ &pin_region[region_cnt].int_base); ++ if (ret) ++ goto out_put; ++ ++ ret = of_property_read_u32_index(node, "aspeed,interrupt-ranges", ++ i * 6 + 1, ++ &pin_region[region_cnt].cnt); ++ if (ret) ++ goto out_put; ++ ++ ret = of_property_read_u32_index(node, "aspeed,interrupt-ranges", ++ i * 6 + 3, &gic_type); ++ if (ret) ++ goto out_put; ++ ++ if (gic_type != GIC_SPI) ++ goto out_put; ++ ++ ret = of_property_read_u32_index(node, "aspeed,interrupt-ranges", ++ i * 6 + 4, ++ &pin_region[region_cnt].gic_base); ++ if (ret) ++ goto out_put; ++ ++ ret = of_property_read_u32_index(node, "aspeed,interrupt-ranges", ++ i * 6 + 5, &gic_flags); ++ if (ret) ++ goto out_put; ++ ++ if (gic_flags != IRQ_TYPE_LEVEL_HIGH) ++ goto out_put; ++ ++ region_cnt++; ++out_put: ++ of_node_put(target); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void aspeed_intc0_disable_swint(struct aspeed_intc_ic *intc_ic) ++{ ++ writel(0, intc_ic->base + INTC0_SWINT_IER); ++} ++ ++static void aspeed_intc0_disable_intbank(struct aspeed_intc_ic *intc_ic) ++{ ++ int i, j; ++ ++ for (i = 0; i < INTC0_INTBANK_GROUPS; i++) { ++ for (j = 0; j < INTC0_INTBANKS_PER_GRP; j++) { ++ u32 base = INTC0_INTBANKX_IER + (0x100 * i) + (0x10 * j); ++ ++ writel(0, intc_ic->base + base); ++ } ++ } ++} ++ ++static void aspeed_intc0_disable_intm(struct aspeed_intc_ic *intc_ic) ++{ ++ int i; ++ ++ for (i = 0; i < INTC0_IMTM_BANK_NUM; i++) ++ writel(0, intc_ic->base + INTC0_IMTMX_IER + (0x10 * i)); ++} ++ ++static int aspeed_intc0_ic_of_init(struct device_node *node, struct device_node *parent) ++{ ++ struct irq_domain *parent_domain; ++ struct aspeed_intc_ic *intc_ic; ++ int ret; ++ ++ if (!parent) { ++ pr_err("missing parent interrupt node\n"); ++ return -ENODEV; ++ } ++ ++ intc_ic = kzalloc(sizeof(*intc_ic), GFP_KERNEL); ++ if (!intc_ic) ++ return -ENOMEM; ++ ++ intc_ic->base = of_iomap(node, 0); ++ if (!intc_ic->base) { ++ ret = -ENOMEM; ++ goto err_free_ic; ++ } ++ ++ aspeed_intc0_disable_swint(intc_ic); ++ aspeed_intc0_disable_intbank(intc_ic); ++ aspeed_intc0_disable_intm(intc_ic); ++ ++ raw_spin_lock_init(&intc_ic->intc_lock); ++ ++ parent_domain = irq_find_host(parent); ++ if (!parent_domain) { ++ pr_err("unable to obtain parent domain\n"); ++ return -ENODEV; ++ } ++ ++ intc_ic->irq_domain = irq_domain_create_hierarchy(parent_domain, 0, INT0_NUM, ++ of_fwnode_handle(node), ++ &aspeed_intc0_ic_irq_domain_ops, ++ intc_ic); ++ if (!intc_ic->irq_domain) { ++ ret = -ENOMEM; ++ goto err_ioumap; ++ } ++ ++ ret = aspeed_intc0_init_gic_ranges(intc_ic, node, parent); ++ if (ret < 0) ++ goto err_ioumap; ++ ++ return 0; ++ ++err_ioumap: ++ iounmap(intc_ic->base); ++err_free_ic: ++ kfree(intc_ic); ++ return ret; ++} ++ ++IRQCHIP_DECLARE(ast2700_intc0_ic, "aspeed,ast2700-intc0-ic", aspeed_intc0_ic_of_init); +diff --git a/drivers/irqchip/irq-ast2700-intc1.c b/drivers/irqchip/irq-ast2700-intc1.c +--- a/drivers/irqchip/irq-ast2700-intc1.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/irqchip/irq-ast2700-intc1.c 2026-04-08 18:03:48.193707458 +0000 +@@ -0,0 +1,254 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Aspeed Interrupt Controller. ++ * ++ * Copyright (C) 2023 ASPEED Technology Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define INTC1_IER 0x100 ++#define INTC1_ISR 0x104 ++#define INTC1_IRQS_PER_BANK 32 ++#define INTC1_BANK_NUM 6 ++ ++struct aspeed_intc_ic { ++ void __iomem *base; ++ raw_spinlock_t intc_lock; ++ struct irq_domain *irq_domain; ++}; ++ ++static void aspeed_intc1_ic_irq_handler(struct irq_desc *desc) ++{ ++ struct aspeed_intc_ic *intc_ic = irq_desc_get_handler_data(desc); ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ unsigned long bit, status; ++ ++ chained_irq_enter(chip, desc); ++ ++ for (int bank = 0; bank < INTC1_BANK_NUM; bank++) { ++ status = readl(intc_ic->base + INTC1_ISR + (0x10 * bank)); ++ if (!status) ++ continue; ++ ++ for_each_set_bit(bit, &status, INTC1_IRQS_PER_BANK) { ++ generic_handle_domain_irq(intc_ic->irq_domain, ++ (bank * INTC1_IRQS_PER_BANK) + bit); ++ writel(BIT(bit), intc_ic->base + INTC1_ISR + (0x10 * bank)); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void aspeed_intc1_irq_mask(struct irq_data *data) ++{ ++ struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); ++ int bit = data->hwirq % INTC1_IRQS_PER_BANK; ++ int bank = data->hwirq / INTC1_IRQS_PER_BANK; ++ unsigned int mask; ++ ++ guard(raw_spinlock_irqsave)(&intc_ic->intc_lock); ++ mask = readl(intc_ic->base + INTC1_IER + (0x10 * bank)) & ~BIT(bit); ++ writel(mask, intc_ic->base + INTC1_IER + (0x10 * bank)); ++} ++ ++static void aspeed_intc1_irq_unmask(struct irq_data *data) ++{ ++ struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); ++ int bit = data->hwirq % INTC1_IRQS_PER_BANK; ++ int bank = data->hwirq / INTC1_IRQS_PER_BANK; ++ unsigned int unmask; ++ ++ guard(raw_spinlock_irqsave)(&intc_ic->intc_lock); ++ unmask = readl(intc_ic->base + INTC1_IER + (0x10 * bank)) | BIT(bit); ++ writel(unmask, intc_ic->base + INTC1_IER + (0x10 * bank)); ++} ++ ++static struct irq_chip aspeed_intc_chip = { ++ .name = "ASPEED INTC1", ++ .irq_mask = aspeed_intc1_irq_mask, ++ .irq_unmask = aspeed_intc1_irq_unmask, ++}; ++ ++static int aspeed_intc1_irq_domain_translate(struct irq_domain *domain, struct irq_fwspec *fwspec, ++ unsigned long *hwirq, unsigned int *type) ++{ ++ if (fwspec->param_count != 1) ++ return -EINVAL; ++ ++ *hwirq = fwspec->param[0]; ++ *type = IRQ_TYPE_LEVEL_HIGH; ++ return 0; ++} ++ ++static int aspeed_intc1_ic_map_irq_domain(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) ++{ ++ irq_domain_set_info(domain, irq, hwirq, &aspeed_intc_chip, ++ domain->host_data, handle_level_irq, NULL, NULL); ++ return 0; ++} ++ ++static int aspeed_intc1_irq_domain_activate(struct irq_domain *domain, ++ struct irq_data *data, bool reserve) ++{ ++ struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); ++ int bank = data->hwirq / INTC1_IRQS_PER_BANK; ++ int bit = data->hwirq % INTC1_IRQS_PER_BANK; ++ u32 mask = BIT(bit); ++ ++ guard(raw_spinlock_irqsave)(&intc_ic->intc_lock); ++ for (int i = 0; i < 3; i++) { ++ void __iomem *sel = intc_ic->base + 0x80 + bank * 4 + 0x20 * i; ++ ++ if (readl(sel) & mask) { ++ writel(readl(sel) & ~mask, sel); ++ if (readl(sel) & mask) ++ return -EACCES; ++ } ++ } ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops aspeed_intc1_ic_irq_domain_ops = { ++ .map = aspeed_intc1_ic_map_irq_domain, ++ .translate = aspeed_intc1_irq_domain_translate, ++ .activate = aspeed_intc1_irq_domain_activate, ++}; ++ ++static int aspeed_intc1_interrupt_ranges(struct aspeed_intc_ic *intc_ic, ++ struct device_node *node, ++ struct device_node *parent_node) ++{ ++ struct of_phandle_args parent_irq; ++ int i, j, n, ret; ++ ++ if (!of_device_is_compatible(parent_node, "aspeed,ast2700-intc0-ic")) ++ return -ENOENT; ++ ++ n = of_property_count_elems_of_size(node, "aspeed,interrupt-ranges", sizeof(u32)); ++ if (n <= 0 || n % 4) ++ return -EINVAL; ++ ++ /* ++ * Each range is described as: ++ * ++ * and we only care about ranges whose phandle matches ++ * this controller's interrupt-parent (&intc0). ++ */ ++ for (i = 0; i < n / 4; i++) { ++ struct device_node *target; ++ phandle parent_handle; ++ u32 base_irq; ++ u32 count; ++ ++ ret = of_property_read_u32_index(node, "aspeed,interrupt-ranges", ++ i * 4 + 2, &parent_handle); ++ if (ret) ++ return ret; ++ ++ target = of_find_node_by_phandle(parent_handle); ++ if (!target) ++ continue; ++ ++ if (target != parent_node) { ++ of_node_put(target); ++ continue; ++ } ++ ++ ret = of_property_read_u32_index(node, "aspeed,interrupt-ranges", ++ i * 4 + 1, &count); ++ if (ret) ++ return ret; ++ ++ ret = of_property_read_u32_index(node, "aspeed,interrupt-ranges", ++ i * 4 + 3, &base_irq); ++ if (ret) ++ return ret; ++ ++ for (j = 0; j < count; j++) { ++ int irq; ++ ++ parent_irq.np = parent_node; ++ parent_irq.args_count = 1; ++ parent_irq.args[0] = base_irq + j; ++ irq = irq_create_of_mapping(&parent_irq); ++ if (!irq) ++ continue; ++ ++ irq_set_chained_handler_and_data(irq, ++ aspeed_intc1_ic_irq_handler, ++ intc_ic); ++ } ++ ++ of_node_put(target); ++ } ++ ++ return 0; ++} ++ ++static void aspeed_intc1_disable_int(struct aspeed_intc_ic *intc_ic) ++{ ++ for (int i = 0; i < INTC1_BANK_NUM; i++) ++ writel(0x0, intc_ic->base + INTC1_IER + (0x10 * i)); ++} ++ ++static int aspeed_intc1_ic_of_init(struct device_node *node, struct device_node *parent) ++{ ++ struct aspeed_intc_ic *intc_ic; ++ int ret = 0; ++ ++ if (!parent) { ++ pr_err("missing parent interrupt node\n"); ++ return -ENODEV; ++ } ++ ++ if (!irq_find_host(parent)) ++ return -ENODEV; ++ ++ intc_ic = kzalloc(sizeof(*intc_ic), GFP_KERNEL); ++ ++ if (!intc_ic) ++ return -ENOMEM; ++ ++ intc_ic->base = of_iomap(node, 0); ++ if (!intc_ic->base) { ++ pr_err("Failed to iomap intc_ic base\n"); ++ ret = -ENOMEM; ++ goto err_free_ic; ++ } ++ ++ raw_spin_lock_init(&intc_ic->intc_lock); ++ ++ aspeed_intc1_disable_int(intc_ic); ++ intc_ic->irq_domain = irq_domain_create_linear(of_fwnode_handle(node), ++ INTC1_BANK_NUM * INTC1_IRQS_PER_BANK, ++ &aspeed_intc1_ic_irq_domain_ops, intc_ic); ++ if (!intc_ic->irq_domain) { ++ ret = -ENOMEM; ++ goto err_iounmap; ++ } ++ ++ ret = aspeed_intc1_interrupt_ranges(intc_ic, node, parent); ++ if (ret < 0) ++ goto err_iounmap; ++ ++ return 0; ++ ++err_iounmap: ++ iounmap(intc_ic->base); ++err_free_ic: ++ kfree(intc_ic); ++ return ret; ++} ++ ++IRQCHIP_DECLARE(ast2700_intc1_ic, "aspeed,ast2700-intc1-ic", aspeed_intc1_ic_of_init); diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig --- a/drivers/jtag/Kconfig 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/jtag/Kconfig 2025-12-23 10:16:17.210098268 +0000 ++++ b/drivers/jtag/Kconfig 2026-04-08 18:03:44.029783536 +0000 @@ -0,0 +1,31 @@ +menuconfig JTAG + tristate "JTAG support" @@ -40617,13 +47318,13 @@ diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig + be called jtag-aspeed. diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile --- a/drivers/jtag/Makefile 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/jtag/Makefile 2025-12-23 10:16:17.210098268 +0000 ++++ b/drivers/jtag/Makefile 2026-04-08 18:03:44.029783536 +0000 @@ -0,0 +1,2 @@ +obj-$(CONFIG_JTAG) += jtag.o +obj-$(CONFIG_JTAG_ASPEED) += jtag-aspeed.o diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c --- a/drivers/jtag/jtag-aspeed.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/jtag/jtag-aspeed.c 2025-12-23 10:16:20.997034798 +0000 ++++ b/drivers/jtag/jtag-aspeed.c 2026-04-08 18:03:48.170707878 +0000 @@ -0,0 +1,1657 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Mellanox Technologies. All rights reserved. @@ -42284,7 +48985,7 @@ diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c +MODULE_LICENSE("GPL v2"); diff --git a/drivers/jtag/jtag.c b/drivers/jtag/jtag.c --- a/drivers/jtag/jtag.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/jtag/jtag.c 2025-12-23 10:16:17.210098268 +0000 ++++ b/drivers/jtag/jtag.c 2026-04-08 18:03:44.029783536 +0000 @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018 Mellanox Technologies. All rights reserved. @@ -42675,7 +49376,7 @@ diff --git a/drivers/jtag/jtag.c b/drivers/jtag/jtag.c +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig --- a/drivers/mailbox/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/mailbox/Kconfig 2025-12-23 10:16:09.399229222 +0000 ++++ b/drivers/mailbox/Kconfig 2026-04-08 18:03:34.959949087 +0000 @@ -295,4 +295,12 @@ acts as an interrupt controller for receiving interrupts from clients. Say Y here if you want to build this driver. @@ -42691,7 +49392,7 @@ diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile --- a/drivers/mailbox/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/mailbox/Makefile 2025-12-23 10:16:13.555159528 +0000 ++++ b/drivers/mailbox/Makefile 2026-04-08 18:03:39.858859736 +0000 @@ -64,3 +64,5 @@ obj-$(CONFIG_QCOM_CPUCP_MBOX) += qcom-cpucp-mbox.o @@ -42700,7 +49401,7 @@ diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile +obj-$(CONFIG_AST2700_MBOX) += ast2700-mailbox.o diff --git a/drivers/mailbox/ast2700-mailbox.c b/drivers/mailbox/ast2700-mailbox.c --- a/drivers/mailbox/ast2700-mailbox.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/mailbox/ast2700-mailbox.c 2025-12-23 10:16:21.043034027 +0000 ++++ b/drivers/mailbox/ast2700-mailbox.c 2026-04-08 18:03:48.220706964 +0000 @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* @@ -42939,7 +49640,7 @@ diff --git a/drivers/mailbox/ast2700-mailbox.c b/drivers/mailbox/ast2700-mailbox +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c --- a/drivers/media/platform/aspeed/aspeed-video.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/media/platform/aspeed/aspeed-video.c 2025-12-23 10:16:21.131032552 +0000 ++++ b/drivers/media/platform/aspeed/aspeed-video.c 2026-04-08 18:03:48.318705174 +0000 @@ -4,6 +4,7 @@ #include @@ -43172,15 +49873,15 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo static const struct aspeed_video_config ast2600_config = { + .version = 6, -+ .jpeg_mode = AST2500_VE_SEQ_CTRL_JPEG_MODE, -+ .comp_size_read = AST2600_VE_COMP_SIZE_READ_BACK, + .jpeg_mode = AST2500_VE_SEQ_CTRL_JPEG_MODE, + .comp_size_read = AST2600_VE_COMP_SIZE_READ_BACK, + .compare_only = AST2600_VE_CTRL_EN_COMPARE_ONLY, +}; + +static const struct aspeed_video_config ast2700_config = { + .version = 7, - .jpeg_mode = AST2500_VE_SEQ_CTRL_JPEG_MODE, - .comp_size_read = AST2600_VE_COMP_SIZE_READ_BACK, ++ .jpeg_mode = AST2500_VE_SEQ_CTRL_JPEG_MODE, ++ .comp_size_read = AST2600_VE_COMP_SIZE_READ_BACK, + .compare_only = AST2600_VE_CTRL_EN_COMPARE_ONLY, }; @@ -43199,7 +49900,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo static bool aspeed_video_alloc_buf(struct aspeed_video *video, struct aspeed_video_addr *addr, -@@ -495,6 +596,22 @@ +@@ -495,6 +596,54 @@ static void aspeed_video_free_buf(struct aspeed_video *video, struct aspeed_video_addr *addr); @@ -43218,11 +49919,43 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo + return addr; +#endif +} ++ ++static void aspeed_video_support_vga_ctrl(struct aspeed_video *v, bool yes) ++{ ++ u32 val = yes ? BIT(0) : 0; ++ u32 reg; ++ ++ if (!v || v->version < 6) ++ return; ++ ++ if (v->version == 7) ++ reg = (v->id == 1) ? 0x914 : 0x904; ++ else ++ reg = 0x104; ++ ++ regmap_update_bits(v->scu, reg, BIT(0), val); ++} ++ ++static void aspeed_video_set_vga_on(struct aspeed_video *v, bool on) ++{ ++ u32 val = on ? BIT(1) : 0; ++ u32 reg; ++ ++ if (!v || v->version < 6) ++ return; ++ ++ if (v->version == 7) ++ reg = (v->id == 1) ? 0x914 : 0x904; ++ else ++ reg = 0x104; ++ ++ regmap_update_bits(v->scu, reg, BIT(1), val); ++} + static void aspeed_video_init_jpeg_table(u32 *table, bool yuv420) { int i; -@@ -576,13 +693,58 @@ +@@ -576,13 +725,58 @@ p->duration); } @@ -43282,7 +50015,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo if (video->v4l2_input_status) { v4l2_dbg(1, debug, &video->v4l2_dev, "No signal; don't start frame\n"); -@@ -595,18 +757,14 @@ +@@ -595,18 +789,14 @@ return -EBUSY; } @@ -43309,7 +50042,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo } spin_lock_irqsave(&video->lock, flags); -@@ -624,15 +782,26 @@ +@@ -624,15 +814,26 @@ aspeed_video_write(video, VE_COMP_PROC_OFFSET, 0); aspeed_video_write(video, VE_COMP_OFFSET, 0); @@ -43341,7 +50074,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo return 0; } -@@ -660,6 +829,9 @@ +@@ -660,6 +861,9 @@ aspeed_video_write(video, VE_INTERRUPT_CTRL, 0); aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xffffffff); @@ -43351,7 +50084,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo /* Turn off the relevant clocks */ clk_disable(video->eclk); clk_disable(video->vclk); -@@ -676,7 +848,68 @@ +@@ -676,7 +880,68 @@ clk_enable(video->vclk); clk_enable(video->eclk); @@ -43420,7 +50153,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo } static void aspeed_video_bufs_done(struct aspeed_video *video, -@@ -684,11 +917,18 @@ +@@ -684,11 +949,18 @@ { unsigned long flags; struct aspeed_video_buffer *buf; @@ -43439,7 +50172,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo spin_unlock_irqrestore(&video->lock, flags); } -@@ -701,12 +941,96 @@ +@@ -701,12 +973,96 @@ video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; @@ -43537,7 +50270,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo static void aspeed_video_swap_src_buf(struct aspeed_video *v) { if (v->format == VIDEO_FMT_STANDARD) -@@ -716,24 +1040,100 @@ +@@ -716,24 +1072,100 @@ if (IS_ALIGNED(v->sequence, 8)) memset((u8 *)v->bcd.virt, 0x00, VE_BCD_BUFF_SIZE); @@ -43555,9 +50288,9 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo - aspeed_video_write(v, VE_SRC1_ADDR, v->srcs[1].dma); + aspeed_video_write(v, VE_SRC0_ADDR, _make_addr(v->srcs[0].dma)); + aspeed_video_write(v, VE_SRC1_ADDR, _make_addr(v->srcs[1].dma)); - } - } - ++ } ++} ++ +static void aspeed_video_frame_done_handler(struct aspeed_video *video, + bool buf_done) +{ @@ -43604,7 +50337,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo + list_add_tail(&box->link, &video->boxes); + } + } -+ } + } + spin_unlock(&video->lock); + + aspeed_video_swap_src_buf(video); @@ -43630,8 +50363,8 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo + aspeed_video_start_frame(v); + + return IRQ_HANDLED; -+} -+ + } + static irqreturn_t aspeed_video_irq(int irq, void *arg) { struct aspeed_video *video = arg; @@ -43646,7 +50379,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo sts &= aspeed_video_read(video, VE_INTERRUPT_CTRL); v4l2_dbg(2, debug, &video->v4l2_dev, "irq sts=%#x %s%s%s%s\n", sts, -@@ -755,8 +1155,6 @@ +@@ -755,8 +1187,6 @@ if (test_bit(VIDEO_RES_DETECT, &video->flags)) { aspeed_video_update(video, VE_INTERRUPT_CTRL, VE_INTERRUPT_MODE_DETECT, 0); @@ -43655,7 +50388,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo sts &= ~VE_INTERRUPT_MODE_DETECT; set_bit(VIDEO_MODE_DETECT_DONE, &video->flags); wake_up_interruptible_all(&video->wait); -@@ -772,41 +1170,13 @@ +@@ -772,41 +1202,13 @@ } if (sts & VE_INTERRUPT_COMP_COMPLETE) { @@ -43703,7 +50436,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo aspeed_video_update(video, VE_SEQ_CTRL, VE_SEQ_CTRL_TRIG_CAPTURE | -@@ -814,17 +1184,60 @@ +@@ -814,17 +1216,60 @@ VE_SEQ_CTRL_TRIG_COMP, 0); aspeed_video_update(video, VE_INTERRUPT_CTRL, VE_INTERRUPT_COMP_COMPLETE, 0); @@ -43770,7 +50503,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo } static void aspeed_video_check_and_set_polarity(struct aspeed_video *video) -@@ -896,7 +1309,7 @@ +@@ -896,7 +1341,7 @@ /* * Get the minimum HW-supported compression buffer size for the frame size. @@ -43779,7 +50512,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo * plenty even for maximum quality; any worse and the engine will simply return * incomplete JPEGs. */ -@@ -908,7 +1321,7 @@ +@@ -908,7 +1353,7 @@ unsigned int size; const unsigned int num_compression_packets = 4; const unsigned int compression_packet_size = 1024; @@ -43788,7 +50521,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo video->max_compressed_size = UINT_MAX; -@@ -929,6 +1342,7 @@ +@@ -929,6 +1374,7 @@ aspeed_video_write(video, VE_STREAM_BUF_SIZE, compression_buffer_size_reg); @@ -43796,7 +50529,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo v4l2_dbg(1, debug, &video->v4l2_dev, "Max compressed size: %#x\n", video->max_compressed_size); } -@@ -1026,9 +1440,23 @@ +@@ -1026,9 +1472,23 @@ } } @@ -43821,7 +50554,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo { bool invalid_resolution = true; int rc; -@@ -1036,7 +1464,6 @@ +@@ -1036,7 +1496,6 @@ u32 mds; u32 src_lr_edge; u32 src_tb_edge; @@ -43829,7 +50562,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo det->width = MIN_WIDTH; det->height = MIN_HEIGHT; -@@ -1107,20 +1534,51 @@ +@@ -1107,20 +1566,51 @@ return; } @@ -43887,7 +50620,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo v4l2_dbg(1, debug, &video->v4l2_dev, "Got resolution: %dx%d\n", det->width, det->height); -@@ -1130,6 +1588,7 @@ +@@ -1130,6 +1620,7 @@ { struct v4l2_bt_timings *act = &video->active_timings; unsigned int size = act->width * ALIGN(act->height, 8); @@ -43895,7 +50628,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo /* Set capture/compression frame sizes */ aspeed_video_calc_compressed_size(video, size); -@@ -1156,7 +1615,8 @@ +@@ -1156,7 +1647,8 @@ aspeed_video_write(video, VE_SRC_SCANLINE_OFFSET, act->width * 4); /* Don't use direct mode below 1024 x 768 (irqs don't fire) */ @@ -43905,7 +50638,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo v4l2_dbg(1, debug, &video->v4l2_dev, "Capture: Sync Mode\n"); aspeed_video_write(video, VE_TGS_0, FIELD_PREP(VE_TGS_FIRST, -@@ -1170,41 +1630,50 @@ +@@ -1170,41 +1662,50 @@ aspeed_video_update(video, VE_CTRL, VE_CTRL_INT_DE | VE_CTRL_DIRECT_FETCH, VE_CTRL_INT_DE); @@ -43933,8 +50666,9 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo aspeed_video_update(video, VE_CTRL, VE_CTRL_INT_DE | VE_CTRL_DIRECT_FETCH, - VE_CTRL_DIRECT_FETCH); -- } -- ++ ctrl); + } + - size *= 4; - - if (size != video->srcs[0].size) { @@ -43954,9 +50688,8 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo - &video->srcs[1].dma, video->srcs[1].size); - aspeed_video_write(video, VE_SRC0_ADDR, video->srcs[0].dma); - aspeed_video_write(video, VE_SRC1_ADDR, video->srcs[1].dma); -+ ctrl); - } - +- } +- - return; + aspeed_video_write(video, VE_SRC0_ADDR, _make_addr(video->srcs[0].dma)); + aspeed_video_write(video, VE_SRC1_ADDR, _make_addr(video->srcs[1].dma)); @@ -43983,7 +50716,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo } static void aspeed_video_update_regs(struct aspeed_video *video) -@@ -1219,6 +1688,8 @@ +@@ -1219,6 +1720,8 @@ u32 ctrl = 0; u32 seq_ctrl = 0; @@ -43992,7 +50725,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo v4l2_dbg(1, debug, &video->v4l2_dev, "framerate(%d)\n", video->frame_rate); v4l2_dbg(1, debug, &video->v4l2_dev, "jpeg format(%s) subsample(%s)\n", -@@ -1234,14 +1705,26 @@ +@@ -1234,14 +1737,26 @@ else aspeed_video_update(video, VE_BCD_CTRL, VE_BCD_CTRL_EN_BCD, 0); @@ -44019,7 +50752,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo if (video->yuv420) seq_ctrl |= VE_SEQ_CTRL_YUV420; -@@ -1252,7 +1735,9 @@ +@@ -1252,7 +1767,9 @@ aspeed_video_update(video, VE_SEQ_CTRL, video->jpeg_mode | VE_SEQ_CTRL_YUV420, seq_ctrl); @@ -44030,7 +50763,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo aspeed_video_update(video, VE_COMP_CTRL, VE_COMP_CTRL_DCT_LUM | VE_COMP_CTRL_DCT_CHR | VE_COMP_CTRL_EN_HQ | VE_COMP_CTRL_HQ_DCT_LUM | -@@ -1278,8 +1763,16 @@ +@@ -1278,8 +1795,16 @@ aspeed_video_write(video, VE_COMP_OFFSET, 0); aspeed_video_write(video, VE_JPEG_ADDR, video->jpeg.dma); @@ -44047,7 +50780,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo aspeed_video_write(video, VE_CTRL, ctrl); aspeed_video_write(video, VE_COMP_CTRL, VE_COMP_CTRL_RSVD); -@@ -1311,12 +1804,7 @@ +@@ -1311,12 +1836,7 @@ aspeed_video_get_resolution(video); /* Set timings since the device is being opened for the first time */ @@ -44061,7 +50794,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo } static void aspeed_video_stop(struct aspeed_video *video) -@@ -1326,15 +1814,6 @@ +@@ -1326,15 +1846,6 @@ aspeed_video_off(video); @@ -44077,7 +50810,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; video->flags = 0; } -@@ -1342,10 +1821,14 @@ +@@ -1342,10 +1853,14 @@ static int aspeed_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { @@ -44092,7 +50825,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo return 0; } -@@ -1383,7 +1866,8 @@ +@@ -1383,7 +1898,8 @@ switch (f->fmt.pix.pixelformat) { case V4L2_PIX_FMT_JPEG: @@ -44102,7 +50835,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo break; case V4L2_PIX_FMT_AJPG: video->format = VIDEO_FMT_ASPEED; -@@ -1401,10 +1885,10 @@ +@@ -1401,10 +1917,10 @@ { struct aspeed_video *video = video_drvdata(file); @@ -44115,7 +50848,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo inp->type = V4L2_INPUT_TYPE_CAMERA; inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; inp->status = video->v4l2_input_status; -@@ -1414,15 +1898,116 @@ +@@ -1414,16 +1930,111 @@ static int aspeed_video_get_input(struct file *file, void *fh, unsigned int *i) { @@ -44133,17 +50866,11 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo + struct aspeed_video *video = video_drvdata(file); + + if (i >= VIDEO_INPUT_MAX) -+ return -EINVAL; -+ -+ if (i == video->input) -+ return 0; -+ -+ if (vb2_is_busy(&video->queue)) -+ return -EBUSY; -+ + return -EINVAL; + + if (IS_ERR(video->scu)) { + v4l2_dbg(1, debug, &video->v4l2_dev, "%s: scu isn't ready for input-control\n", __func__); - return -EINVAL; ++ return -EINVAL; + } + + if (IS_ERR(video->gfx) && i == VIDEO_INPUT_GFX) { @@ -44231,10 +50958,11 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo + + if (video->input == VIDEO_INPUT_MEM) + aspeed_video_start_frame(video); - ++ return 0; } -@@ -1520,6 +2105,12 @@ + +@@ -1520,6 +2131,12 @@ { struct aspeed_video *video = video_drvdata(file); @@ -44247,7 +50975,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo if (timings->bt.width == video->active_timings.width && timings->bt.height == video->active_timings.height) return 0; -@@ -1527,13 +2118,7 @@ +@@ -1527,13 +2144,7 @@ if (vb2_is_busy(&video->queue)) return -EBUSY; @@ -44262,7 +50990,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo timings->type = V4L2_DV_BT_656_1120; -@@ -1589,6 +2174,37 @@ +@@ -1589,6 +2200,37 @@ NULL, NULL); } @@ -44300,7 +51028,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo static int aspeed_video_dv_timings_cap(struct file *file, void *fh, struct v4l2_dv_timings_cap *cap) { -@@ -1641,6 +2257,8 @@ +@@ -1641,6 +2283,8 @@ .vidioc_enum_dv_timings = aspeed_video_enum_dv_timings, .vidioc_dv_timings_cap = aspeed_video_dv_timings_cap, @@ -44309,7 +51037,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo .vidioc_subscribe_event = aspeed_video_sub_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -@@ -1710,6 +2328,9 @@ +@@ -1710,6 +2354,9 @@ struct delayed_work *dwork = to_delayed_work(work); struct aspeed_video *video = container_of(dwork, struct aspeed_video, res_work); @@ -44319,7 +51047,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo aspeed_video_on(video); -@@ -1723,8 +2344,14 @@ +@@ -1723,8 +2370,14 @@ aspeed_video_get_resolution(video); @@ -44336,7 +51064,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo static const struct v4l2_event ev = { .type = V4L2_EVENT_SOURCE_CHANGE, .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, -@@ -1740,6 +2367,32 @@ +@@ -1740,6 +2393,32 @@ done: clear_bit(VIDEO_RES_CHANGE, &video->flags); wake_up_interruptible_all(&video->wait); @@ -44369,7 +51097,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo } static int aspeed_video_open(struct file *file) -@@ -1785,7 +2438,7 @@ +@@ -1785,7 +2464,7 @@ .read = vb2_fop_read, .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, @@ -44378,7 +51106,12 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo .open = aspeed_video_open, .release = aspeed_video_release, }; -@@ -1830,13 +2483,17 @@ +@@ -1827,16 +2506,22 @@ + int rc; + struct aspeed_video *video = vb2_get_drv_priv(q); + ++ aspeed_video_set_vga_on(video, true); ++ video->sequence = 0; video->perf.duration_max = 0; video->perf.duration_min = 0xffffffff; @@ -44400,7 +51133,16 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo } set_bit(VIDEO_STREAMING, &video->flags); -@@ -1860,8 +2517,7 @@ +@@ -1848,6 +2533,8 @@ + int rc; + struct aspeed_video *video = vb2_get_drv_priv(q); + ++ aspeed_video_set_vga_on(video, false); ++ + clear_bit(VIDEO_STREAMING, &video->flags); + + rc = wait_event_timeout(video->wait, +@@ -1860,8 +2547,7 @@ * Need to force stop any DMA and try and get HW into a good * state for future calls to start streaming again. */ @@ -44410,7 +51152,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo aspeed_video_init_regs(video); -@@ -1885,7 +2541,8 @@ +@@ -1885,7 +2571,8 @@ spin_unlock_irqrestore(&video->lock, flags); if (test_bit(VIDEO_STREAMING, &video->flags) && @@ -44420,7 +51162,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo aspeed_video_start_frame(video); } -@@ -1911,6 +2568,7 @@ +@@ -1911,6 +2598,7 @@ val08 = aspeed_video_read(v, VE_CTRL); if (FIELD_GET(VE_CTRL_DIRECT_FETCH, val08)) { seq_printf(s, " %-20s:\tDirect fetch\n", "Mode"); @@ -44428,7 +51170,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo seq_printf(s, " %-20s:\t%s\n", "VGA bpp mode", FIELD_GET(VE_CTRL_INT_DE, val08) ? "16" : "32"); } else { -@@ -1962,19 +2620,19 @@ +@@ -1962,19 +2650,19 @@ } DEFINE_SHOW_ATTRIBUTE(aspeed_video_debugfs); @@ -44455,7 +51197,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo } #else static void aspeed_video_debugfs_remove(struct aspeed_video *video) { } -@@ -2028,6 +2686,8 @@ +@@ -2028,6 +2716,8 @@ vbq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vbq->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; vbq->dev = v4l2_dev->dev; @@ -44464,7 +51206,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo vbq->lock = &video->video_lock; vbq->ops = &aspeed_video_vb2_ops; vbq->mem_ops = &vb2_dma_contig_memops; -@@ -2070,11 +2730,30 @@ +@@ -2070,11 +2760,30 @@ return 0; } @@ -44495,7 +51237,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo irq = irq_of_parse_and_map(dev->of_node, 0); if (!irq) { -@@ -2082,14 +2761,36 @@ +@@ -2082,14 +2791,36 @@ return -ENODEV; } @@ -44534,7 +51276,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo video->eclk = devm_clk_get(dev, "eclk"); if (IS_ERR(video->eclk)) { dev_err(dev, "Unable to get ECLK\n"); -@@ -2111,22 +2812,53 @@ +@@ -2111,22 +2842,53 @@ if (rc) goto err_unprepare_eclk; @@ -44594,7 +51336,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo aspeed_video_init_jpeg_table(video->jpeg.virt, video->yuv420); -@@ -2134,6 +2866,9 @@ +@@ -2134,6 +2896,9 @@ err_release_reserved_mem: of_reserved_mem_device_release(dev); @@ -44604,7 +51346,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo clk_unprepare(video->vclk); err_unprepare_eclk: clk_unprepare(video->eclk); -@@ -2145,6 +2880,7 @@ +@@ -2145,6 +2910,7 @@ { .compatible = "aspeed,ast2400-video-engine", .data = &ast2400_config }, { .compatible = "aspeed,ast2500-video-engine", .data = &ast2500_config }, { .compatible = "aspeed,ast2600-video-engine", .data = &ast2600_config }, @@ -44612,7 +51354,13 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo {} }; MODULE_DEVICE_TABLE(of, aspeed_video_of_match); -@@ -2159,6 +2895,10 @@ +@@ -2154,11 +2920,16 @@ + const struct aspeed_video_config *config; + struct aspeed_video *video; + int rc; ++ bool vga_ctrl; + + video = devm_kzalloc(&pdev->dev, sizeof(*video), GFP_KERNEL); if (!video) return -ENOMEM; @@ -44623,7 +51371,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo video->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(video->base)) return PTR_ERR(video->base); -@@ -2167,8 +2907,10 @@ +@@ -2167,8 +2938,10 @@ if (!config) return -ENODEV; @@ -44634,7 +51382,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo video->frame_rate = 30; video->jpeg_hq_quality = 1; -@@ -2178,6 +2920,19 @@ +@@ -2178,14 +2951,31 @@ init_waitqueue_head(&video->wait); INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work); INIT_LIST_HEAD(&video->buffers); @@ -44654,15 +51402,20 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo rc = aspeed_video_init(video); if (rc) -@@ -2185,7 +2940,6 @@ + return rc; ++ /* vga_ctrl is a property to say if VGA will be on/off with KVM */ ++ vga_ctrl = of_find_property(pdev->dev.of_node, "vga_ctrl", NULL) ? 1 : 0; ++ aspeed_video_support_vga_ctrl(video, vga_ctrl); ++ aspeed_video_set_vga_on(video, false); ++ rc = aspeed_video_setup_video(video); if (rc) { - aspeed_video_free_buf(video, &video->jpeg); clk_unprepare(video->vclk); clk_unprepare(video->eclk); return rc; -@@ -2193,6 +2947,9 @@ +@@ -2193,6 +2983,9 @@ aspeed_video_debugfs_create(video); @@ -44672,7 +51425,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo return 0; } -@@ -2204,8 +2961,12 @@ +@@ -2204,8 +2997,12 @@ aspeed_video_off(video); @@ -44685,7 +51438,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo clk_unprepare(video->vclk); clk_unprepare(video->eclk); -@@ -2215,7 +2976,7 @@ +@@ -2215,7 +3012,7 @@ v4l2_device_unregister(v4l2_dev); @@ -44696,7 +51449,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo } diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c --- a/drivers/mmc/host/sdhci-of-aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/mmc/host/sdhci-of-aspeed.c 2025-12-23 10:16:20.668040312 +0000 ++++ b/drivers/mmc/host/sdhci-of-aspeed.c 2026-04-08 18:03:47.802714601 +0000 @@ -11,8 +11,10 @@ #include #include @@ -45347,7 +52100,7 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe .name = "sd-controller-aspeed", diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig --- a/drivers/net/can/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/net/can/Kconfig 2025-12-23 10:16:08.679241296 +0000 ++++ b/drivers/net/can/Kconfig 2026-04-08 18:03:33.980966943 +0000 @@ -66,6 +66,12 @@ if CAN_NETLINK @@ -45363,7 +52116,7 @@ diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig default y diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile --- a/drivers/net/can/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/net/can/Makefile 2025-12-23 10:16:12.642174838 +0000 ++++ b/drivers/net/can/Makefile 2026-04-08 18:03:38.662881549 +0000 @@ -15,6 +15,7 @@ obj-y += usb/ obj-y += softing/ @@ -45374,7 +52127,7 @@ diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile obj-$(CONFIG_CAN_CAN327) += can327.o diff --git a/drivers/net/can/aspeed_can.c b/drivers/net/can/aspeed_can.c --- a/drivers/net/can/aspeed_can.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/net/can/aspeed_can.c 2025-12-23 10:16:20.944035686 +0000 ++++ b/drivers/net/can/aspeed_can.c 2026-04-08 18:03:48.110708974 +0000 @@ -0,0 +1,1720 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* ASPEED CAN device driver @@ -47098,7 +53851,7 @@ diff --git a/drivers/net/can/aspeed_can.c b/drivers/net/can/aspeed_can.c +MODULE_DESCRIPTION("ASPEED CAN interface"); diff --git a/drivers/net/ethernet/faraday/Kconfig b/drivers/net/ethernet/faraday/Kconfig --- a/drivers/net/ethernet/faraday/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/net/ethernet/faraday/Kconfig 2025-12-23 10:16:21.145032318 +0000 ++++ b/drivers/net/ethernet/faraday/Kconfig 2026-04-08 18:03:48.332704918 +0000 @@ -6,7 +6,7 @@ config NET_VENDOR_FARADAY bool "Faraday devices" @@ -47124,7 +53877,7 @@ diff --git a/drivers/net/ethernet/faraday/Kconfig b/drivers/net/ethernet/faraday This driver supports the FTGMAC100 Gigabit Ethernet controller diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c --- a/drivers/net/ethernet/faraday/ftgmac100.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/net/ethernet/faraday/ftgmac100.c 2025-12-23 10:16:21.145032318 +0000 ++++ b/drivers/net/ethernet/faraday/ftgmac100.c 2026-04-08 18:03:48.332704918 +0000 @@ -9,6 +9,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -47133,7 +53886,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far #include #include #include -@@ -19,6 +20,7 @@ +@@ -19,12 +20,16 @@ #include #include #include @@ -47141,7 +53894,16 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far #include #include #include -@@ -98,6 +100,7 @@ + #include + #include + #include ++#include ++#include ++#include + #include + #include + +@@ -98,6 +103,7 @@ struct work_struct reset_task; struct mii_bus *mii_bus; struct clk *clk; @@ -47149,7 +53911,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far /* AST2500/AST2600 RMII ref clock gate */ struct clk *rclk; -@@ -119,6 +122,9 @@ +@@ -119,6 +125,9 @@ /* Misc */ bool need_mac_restart; bool is_aspeed; @@ -47159,7 +53921,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far }; static int ftgmac100_reset_mac(struct ftgmac100 *priv, u32 maccr) -@@ -148,6 +154,23 @@ +@@ -148,6 +157,23 @@ { u32 maccr = 0; @@ -47183,7 +53945,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far switch (priv->cur_speed) { case SPEED_10: case 0: /* no link */ -@@ -265,10 +288,12 @@ +@@ -265,10 +291,12 @@ iowrite32(reg, priv->base + FTGMAC100_OFFSET_ISR); /* Setup RX ring buffer base */ @@ -47198,7 +53960,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far /* Configure RX buffer size */ iowrite32(FTGMAC100_RBSR_SIZE(RX_BUF_SIZE), -@@ -321,6 +346,7 @@ +@@ -321,6 +349,7 @@ static void ftgmac100_start_hw(struct ftgmac100 *priv) { u32 maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR); @@ -47206,7 +53968,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far /* Keep the original GMAC and FAST bits */ maccr &= (FTGMAC100_MACCR_FAST_MODE | FTGMAC100_MACCR_GIGA_MODE); -@@ -349,6 +375,10 @@ +@@ -349,6 +378,10 @@ if (priv->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) maccr |= FTGMAC100_MACCR_RM_VLAN; @@ -47217,7 +53979,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far /* Hit the HW */ iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR); } -@@ -425,7 +455,9 @@ +@@ -425,7 +458,9 @@ priv->rx_skbs[entry] = skb; /* Store DMA address into RX desc */ @@ -47228,7 +53990,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far /* Ensure the above is ordered vs clearing the OWN bit */ dma_wmb(); -@@ -551,7 +583,8 @@ +@@ -551,7 +586,8 @@ csum_vlan & 0xffff); /* Tear down DMA mapping, do necessary cache management */ @@ -47238,7 +54000,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far #if defined(CONFIG_ARM) && !defined(CONFIG_ARM_DMA_USE_IOMMU) /* When we don't have an iommu, we can save cycles by not -@@ -628,9 +661,12 @@ +@@ -628,9 +664,12 @@ struct ftgmac100_txdes *txdes, u32 ctl_stat) { @@ -47252,7 +54014,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far if (ctl_stat & FTGMAC100_TXDES0_FTS) { len = skb_headlen(skb); dma_unmap_single(priv->dev, map, len, DMA_TO_DEVICE); -@@ -784,7 +820,9 @@ +@@ -784,7 +823,9 @@ f_ctl_stat |= FTGMAC100_TXDES0_FTS; if (nfrags == 0) f_ctl_stat |= FTGMAC100_TXDES0_LTS; @@ -47263,7 +54025,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far txdes->txdes1 = cpu_to_le32(csum_vlan); /* Next descriptor */ -@@ -812,7 +850,9 @@ +@@ -812,7 +853,9 @@ ctl_stat |= FTGMAC100_TXDES0_LTS; txdes->txdes0 = cpu_to_le32(ctl_stat); txdes->txdes1 = 0; @@ -47274,7 +54036,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far /* Next one */ pointer = ftgmac100_next_tx_pointer(priv, pointer); -@@ -887,7 +927,10 @@ +@@ -887,7 +930,10 @@ for (i = 0; i < priv->rx_q_entries; i++) { struct ftgmac100_rxdes *rxdes = &priv->rxdes[i]; struct sk_buff *skb = priv->rx_skbs[i]; @@ -47286,7 +54048,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far if (!skb) continue; -@@ -986,7 +1029,9 @@ +@@ -986,7 +1032,9 @@ for (i = 0; i < priv->rx_q_entries; i++) { rxdes = &priv->rxdes[i]; rxdes->rxdes0 = 0; @@ -47297,7 +54059,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far } /* Mark the end of the ring */ rxdes->rxdes0 |= cpu_to_le32(priv->rxdes0_edorr_mask); -@@ -1730,16 +1775,21 @@ +@@ -1730,16 +1778,21 @@ static void ftgmac100_phy_disconnect(struct net_device *netdev) { struct ftgmac100 *priv = netdev_priv(netdev); @@ -47326,7 +54088,385 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far } static void ftgmac100_destroy_mdio(struct net_device *netdev) -@@ -1882,7 +1932,8 @@ +@@ -1812,12 +1865,377 @@ + return ret; + } + ++static int ftgmac100_get_ast2600_rgmii_flag(u32 delay) ++{ ++ if ((delay > 500 && delay < 1500) || ++ (delay > 2500 && delay < 7500)) ++ return AST2600_RGMII_KEEP_DELAY; ++ ++ return AST2600_RGMII_DIS_DELAY; ++} ++ ++static int ftgmac100_check_ast2600_rgmii_delay(struct regmap *scu, ++ u32 delay_unit, ++ int mac_id, int dly_reg) ++{ ++ u32 delay_value; ++ u32 tx_delay; ++ u32 rx_delay; ++ int tx_flag; ++ int rx_flag; ++ ++ regmap_read(scu, dly_reg, &delay_value); ++ if (mac_id == 0 || mac_id == 2) { ++ tx_delay = FIELD_GET(ASPEED_MAC0_2_TX_DLY, delay_value); ++ rx_delay = FIELD_GET(ASPEED_MAC0_2_RX_DLY, delay_value); ++ } else { ++ tx_delay = FIELD_GET(ASPEED_MAC1_3_TX_DLY, delay_value); ++ rx_delay = FIELD_GET(ASPEED_MAC1_3_RX_DLY, delay_value); ++ } ++ ++ /* Due to the hardware design reason, for MAC2/3 on AST2600, ++ * the zero delay ns on RX is configured by setting value 0x1a. ++ * List as below: ++ * 0x1a, 0x1b, ... , 0x1f, 0x00, 0x01, ... , 0x19 ++ * Covert for calculation purpose. ++ * 0x00, 0x01, ... , 0x19, 0x1a, 0x1b, ... , 0x1f ++ */ ++ if (mac_id == 2 || mac_id == 3) ++ rx_delay = (rx_delay + 0x06) & 0x1f; ++ ++ tx_delay *= delay_unit; ++ rx_delay *= delay_unit; ++ ++ tx_flag = ftgmac100_get_ast2600_rgmii_flag(tx_delay); ++ rx_flag = ftgmac100_get_ast2600_rgmii_flag(rx_delay); ++ ++ if (tx_flag == AST2600_RGMII_KEEP_DELAY || ++ rx_flag == AST2600_RGMII_KEEP_DELAY) { ++ return AST2600_RGMII_KEEP_DELAY; ++ } ++ ++ return AST2600_RGMII_DIS_DELAY; ++} ++ ++static int ftgmac100_set_ast2600_rgmii_delay(struct ftgmac100 *priv, ++ s32 rgmii_tx_delay, ++ s32 rgmii_rx_delay, ++ phy_interface_t *phy_intf) ++{ ++ struct device *dev = priv->dev; ++ struct device_node *np; ++ u32 rgmii_delay_unit; ++ u32 rx_delay_index; ++ u32 tx_delay_index; ++ struct regmap *scu; ++ int dly_mask; ++ int dly_reg; ++ int mac_id; ++ int err; ++ ++ np = dev->of_node; ++ ++ err = of_get_phy_mode(np, phy_intf); ++ if (err) { ++ dev_err(priv->dev, "Failed to get phy mode: %d\n", err); ++ return err; ++ } ++ ++ scu = syscon_regmap_lookup_by_phandle(np, "aspeed,scu"); ++ if (IS_ERR(scu)) { ++ dev_err(dev, "failed to get aspeed,scu"); ++ return PTR_ERR(scu); ++ } ++ ++ /* According to the register base address to specify the corresponding ++ * values. ++ */ ++ switch (priv->res->start) { ++ case AST2600_MAC0_BASE_ADDR: ++ mac_id = 0; ++ rgmii_delay_unit = AST2600_MAC01_CLK_DLY_UNIT; ++ dly_reg = AST2600_MAC01_CLK_DLY; ++ break; ++ case AST2600_MAC1_BASE_ADDR: ++ mac_id = 1; ++ rgmii_delay_unit = AST2600_MAC01_CLK_DLY_UNIT; ++ dly_reg = AST2600_MAC01_CLK_DLY; ++ break; ++ case AST2600_MAC2_BASE_ADDR: ++ mac_id = 2; ++ rgmii_delay_unit = AST2600_MAC23_CLK_DLY_UNIT; ++ dly_reg = AST2600_MAC23_CLK_DLY; ++ break; ++ case AST2600_MAC3_BASE_ADDR: ++ mac_id = 3; ++ rgmii_delay_unit = AST2600_MAC23_CLK_DLY_UNIT; ++ dly_reg = AST2600_MAC23_CLK_DLY; ++ break; ++ default: ++ dev_err(dev, "Invalid mac base address"); ++ return -EINVAL; ++ } ++ ++ if (of_phy_is_fixed_link(np)) { ++ if (rgmii_tx_delay < 0 || rgmii_rx_delay < 0) { ++ dev_err(dev, ++ "Add rx/tx-internal-delay-ps for fixed-link\n"); ++ /* Keep original RGMII delay value*/ ++ return 0; ++ } ++ ++ /* Must have both of rx/tx-internal-delay-ps for fixed-link */ ++ goto conf_delay; ++ } ++ ++ if (*phy_intf == PHY_INTERFACE_MODE_RGMII_RXID || ++ *phy_intf == PHY_INTERFACE_MODE_RGMII_TXID) ++ goto out_warn; ++ ++ if (*phy_intf != PHY_INTERFACE_MODE_RGMII && ++ *phy_intf != PHY_INTERFACE_MODE_RGMII_ID) ++ return 0; ++ ++ /* Both rx/tx-internal-delay-ps are not existed. */ ++ if (rgmii_tx_delay < 0 && rgmii_rx_delay < 0) { ++ int flag; ++ ++ flag = ftgmac100_check_ast2600_rgmii_delay(scu, ++ rgmii_delay_unit, ++ mac_id, ++ dly_reg); ++ if (flag == AST2600_RGMII_KEEP_DELAY) ++ goto out_warn; ++ ++ if (*phy_intf == PHY_INTERFACE_MODE_RGMII) { ++ dev_err(dev, "Update phy-mode to 'rgmii-id'\n"); ++ /* Forced phy interface to RGMII_ID and MAC will disable ++ * RGMII delay. ++ */ ++ *phy_intf = PHY_INTERFACE_MODE_RGMII_ID; ++ } ++ } else { ++ /* Please refer to ethernet-controller.yaml. */ ++ if (*phy_intf == PHY_INTERFACE_MODE_RGMII && ++ (rgmii_tx_delay == 2000 || rgmii_rx_delay == 2000)) { ++ dev_warn(dev, ++ "RX/TX delay cannot set to 2000 on 'rgmii'\n"); ++ return -EINVAL; ++ } ++ } ++ ++ /* The value is negative, which means the rx/tx-internal-delay-ps ++ * property is not existed in dts. Therefore, set to default 0. ++ */ ++ if (rgmii_tx_delay < 0) ++ rgmii_tx_delay = 0; ++ if (rgmii_rx_delay < 0) ++ rgmii_rx_delay = 0; ++ ++conf_delay: ++ tx_delay_index = DIV_ROUND_CLOSEST(rgmii_tx_delay, rgmii_delay_unit); ++ if (tx_delay_index >= 32) { ++ dev_err(dev, "The %u ps of TX delay is out of range\n", ++ rgmii_tx_delay); ++ return -EINVAL; ++ } ++ ++ rx_delay_index = DIV_ROUND_CLOSEST(rgmii_rx_delay, rgmii_delay_unit); ++ if (rx_delay_index >= 32) { ++ dev_err(dev, "The %u ps of RX delay is out of range\n", ++ rgmii_rx_delay); ++ return -EINVAL; ++ } ++ ++ /* Due to the hardware design reason, for MAC2/3 on AST2600, the zero ++ * delay ns on RX is configured by setting value 0x1a. ++ * List as below: ++ * 0x1a -> 0 ns, 0x1b -> 0.25 ns, ... , 0x1f -> 1.25 ns, ++ * 0x00 -> 1.5 ns, 0x01 -> 1.75 ns, ... , 0x19 -> 7.75 ns, 0x1a -> 0 ns ++ */ ++ if (mac_id == 2 || mac_id == 3) ++ rx_delay_index = (AST2600_MAC23_RX_DLY_0_NS + rx_delay_index) & ++ AST2600_MAC_TX_RX_DLY_MASK; ++ ++ if (mac_id == 0 || mac_id == 2) { ++ dly_mask = ASPEED_MAC0_2_TX_DLY | ASPEED_MAC0_2_RX_DLY; ++ tx_delay_index = FIELD_PREP(ASPEED_MAC0_2_TX_DLY, tx_delay_index); ++ rx_delay_index = FIELD_PREP(ASPEED_MAC0_2_RX_DLY, rx_delay_index); ++ } else { ++ dly_mask = ASPEED_MAC1_3_TX_DLY | ASPEED_MAC1_3_RX_DLY; ++ tx_delay_index = FIELD_PREP(ASPEED_MAC1_3_TX_DLY, tx_delay_index); ++ rx_delay_index = FIELD_PREP(ASPEED_MAC1_3_RX_DLY, rx_delay_index); ++ } ++ ++ regmap_update_bits(scu, dly_reg, dly_mask, tx_delay_index | rx_delay_index); ++ ++ return 0; ++ ++out_warn: ++ /* Print the warning message. Keep the phy-mode and the RGMII delay value. */ ++ dev_warn(dev, "Update phy-mode to 'rgmii-id' and add rx/tx-internal-delay-ps\n"); ++ ++ return 0; ++} ++ ++static int ftgmac100_set_ast2700_rgmii_delay(struct ftgmac100 *priv, ++ s32 rgmii_tx_delay, ++ s32 rgmii_rx_delay) ++{ ++ struct device *dev = priv->dev; ++ struct device_node *np; ++ u32 rgmii_delay_tx_unit, rgmii_delay_rx_unit; ++ u32 rgmii_delay_tx_dis, rgmii_delay_rx_dis; ++ u32 tx_delay_index, rx_delay_index; ++ u32 reg_unit, reg_val; ++ struct regmap *scu; ++ int dly_mask; ++ int mac_id; ++ ++ np = dev->of_node; ++ ++ scu = syscon_regmap_lookup_by_phandle(np, "aspeed,scu"); ++ if (IS_ERR(scu)) { ++ dev_err(dev, "failed to get aspeed,scu"); ++ return PTR_ERR(scu); ++ } ++ ++ /* According to the register base address to specify the corresponding ++ * values. ++ */ ++ switch (priv->res->start) { ++ case AST2700_MAC0_BASE_ADDR: ++ mac_id = 0; ++ regmap_read(scu, AST2700_MAC0_DLY_UNIT, ®_unit); ++ regmap_read(scu, AST2700_MAC0_DLY_VAL, ®_val); ++ break; ++ case AST2700_MAC1_BASE_ADDR: ++ mac_id = 1; ++ regmap_read(scu, AST2700_MAC1_DLY_UNIT, ®_unit); ++ regmap_read(scu, AST2700_MAC1_DLY_VAL, ®_val); ++ break; ++ case AST2700_MAC2_BASE_ADDR: ++ /* Only support SGMII */ ++ return 0; ++ default: ++ dev_err(dev, "Invalid mac base address"); ++ return -EINVAL; ++ } ++ ++ rgmii_delay_tx_unit = AST2700_MAC_TX_DLY_UNIT(reg_unit); ++ rgmii_delay_rx_unit = AST2700_MAC_RX_DLY_UNIT(reg_unit); ++ rgmii_delay_tx_dis = AST2700_MAC_TX_DLY_DIS(reg_val); ++ rgmii_delay_rx_dis = AST2700_MAC_RX_DLY_DIS(reg_val); ++ ++ /* The value is negative, which means the rx/tx-internal-delay-ps ++ * property is not existed in dts. Therefore, set to default 0. ++ */ ++ if (rgmii_tx_delay < 0) ++ rgmii_tx_delay = 0; ++ if (rgmii_rx_delay < 0) ++ rgmii_rx_delay = 0; ++ ++ tx_delay_index = DIV_ROUND_CLOSEST(rgmii_tx_delay, rgmii_delay_tx_unit); ++ tx_delay_index += rgmii_delay_tx_dis; ++ if (tx_delay_index >= 32) { ++ dev_err(dev, "The %u ps of TX delay is out of range\n", ++ rgmii_tx_delay); ++ return -EINVAL; ++ } ++ ++ rx_delay_index = DIV_ROUND_CLOSEST(rgmii_rx_delay, rgmii_delay_rx_unit); ++ rx_delay_index += rgmii_delay_rx_dis; ++ if (rx_delay_index >= 32) { ++ dev_err(dev, "The %u ps of RX delay is out of range\n", ++ rgmii_rx_delay); ++ return -EINVAL; ++ } ++ ++ if (mac_id == 0) { ++ dly_mask = ASPEED_MAC0_2_TX_DLY | ASPEED_MAC0_2_RX_DLY; ++ tx_delay_index = FIELD_PREP(ASPEED_MAC0_2_TX_DLY, tx_delay_index); ++ rx_delay_index = FIELD_PREP(ASPEED_MAC0_2_RX_DLY, rx_delay_index); ++ } else { ++ dly_mask = ASPEED_MAC1_3_TX_DLY | ASPEED_MAC1_3_RX_DLY; ++ tx_delay_index = FIELD_PREP(ASPEED_MAC1_3_TX_DLY, tx_delay_index); ++ rx_delay_index = FIELD_PREP(ASPEED_MAC1_3_RX_DLY, rx_delay_index); ++ } ++ ++ regmap_update_bits(scu, AST2700_MAC01_CLK_DLY, dly_mask, ++ tx_delay_index | rx_delay_index); ++ ++ return 0; ++} ++ ++static int ftgmac100_set_internal_delay(struct ftgmac100 *priv, ++ phy_interface_t *phy_intf) ++{ ++ struct device_node *np = priv->dev->of_node; ++ s32 rgmii_tx_delay; ++ s32 rgmii_rx_delay; ++ int err = 0; ++ ++ /* NCSI mode is based on RMII, not neet to set delay for RMII */ ++ if (of_get_property(np, "use-ncsi", NULL)) ++ return 0; ++ ++ /* AST2600 needs to know if the "tx/rx-internal-delay-ps" properties ++ * are existed in dts. If not existed, set -1 and delay is equal to 0. ++ */ ++ if (of_property_read_u32(np, "tx-internal-delay-ps", &rgmii_tx_delay)) ++ rgmii_tx_delay = -1; ++ if (of_property_read_u32(np, "rx-internal-delay-ps", &rgmii_rx_delay)) ++ rgmii_rx_delay = -1; ++ ++ if ((of_device_is_compatible(np, "aspeed,ast2600-mac"))) ++ err = ftgmac100_set_ast2600_rgmii_delay(priv, ++ rgmii_tx_delay, ++ rgmii_rx_delay, ++ phy_intf); ++ else if ((of_device_is_compatible(np, "aspeed,ast2700-mac"))) ++ err = ftgmac100_set_ast2700_rgmii_delay(priv, ++ rgmii_tx_delay, ++ rgmii_rx_delay); ++ ++ return err; ++} ++ ++static struct phy_device *ftgmac100_ast2600_phy_get(struct net_device *dev, ++ struct device_node *np, ++ void (*hndlr)(struct net_device *), ++ phy_interface_t phy_intf) ++{ ++ struct device_node *phy_np; ++ struct phy_device *phy; ++ int ret; ++ ++ if (of_phy_is_fixed_link(np)) { ++ ret = of_phy_register_fixed_link(np); ++ if (ret < 0) { ++ netdev_err(dev, "broken fixed-link specification\n"); ++ return NULL; ++ } ++ phy_np = of_node_get(np); ++ } else { ++ phy_np = of_parse_phandle(np, "phy-handle", 0); ++ if (!phy_np) ++ return NULL; ++ } ++ ++ phy = of_phy_connect(dev, phy_np, hndlr, 0, phy_intf); ++ ++ of_node_put(phy_np); ++ ++ return phy; ++} ++ + static int ftgmac100_probe(struct platform_device *pdev) + { + struct resource *res; + int irq; + struct net_device *netdev; + struct phy_device *phydev; ++ phy_interface_t phy_intf; + struct ftgmac100 *priv; + struct device_node *np; + int err = 0; +@@ -1882,10 +2300,15 @@ np = pdev->dev.of_node; if (np && (of_device_is_compatible(np, "aspeed,ast2400-mac") || of_device_is_compatible(np, "aspeed,ast2500-mac") || @@ -47336,7 +54476,14 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far priv->rxdes0_edorr_mask = BIT(30); priv->txdes0_edotr_mask = BIT(30); priv->is_aspeed = true; -@@ -1913,40 +1964,22 @@ ++ /* Configure RGMII delay if there are the corresponding compatibles */ ++ err = ftgmac100_set_internal_delay(priv, &phy_intf); ++ if (err) ++ goto err_phy_connect; + } else { + priv->rxdes0_edorr_mask = BIT(15); + priv->txdes0_edotr_mask = BIT(15); +@@ -1913,47 +2336,37 @@ goto err_phy_connect; } err = phy_connect_direct(netdev, phydev, ftgmac100_adjust_link, @@ -47383,7 +54530,24 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far err = ftgmac100_setup_mdio(netdev); if (err) goto err_setup_mdio; -@@ -1995,6 +2028,54 @@ + } + +- phy = of_phy_get_and_connect(priv->netdev, np, +- &ftgmac100_adjust_link); ++ /* Because AST2600 will use the RGMII delay to determine ++ * which phy interface to use. ++ */ ++ if (of_device_is_compatible(np, "aspeed,ast2600-mac")) ++ phy = ftgmac100_ast2600_phy_get(priv->netdev, np, ++ &ftgmac100_adjust_link, ++ phy_intf); ++ else ++ phy = of_phy_get_and_connect(priv->netdev, np, ++ &ftgmac100_adjust_link); + if (!phy) { + dev_err(&pdev->dev, "Failed to connect to phy\n"); + err = -EINVAL; +@@ -1995,6 +2408,54 @@ if (of_device_is_compatible(np, "aspeed,ast2600-mac")) iowrite32(FTGMAC100_TM_DEFAULT, priv->base + FTGMAC100_OFFSET_TM); @@ -47438,7 +54602,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far } /* Default ring sizes */ -@@ -2021,6 +2102,12 @@ +@@ -2021,6 +2482,12 @@ netdev->hw_features &= ~(NETIF_F_HW_CSUM | NETIF_F_RXCSUM); netdev->features |= netdev->hw_features; @@ -47453,7 +54617,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far if (err) { diff --git a/drivers/net/ethernet/faraday/ftgmac100.h b/drivers/net/ethernet/faraday/ftgmac100.h --- a/drivers/net/ethernet/faraday/ftgmac100.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/net/ethernet/faraday/ftgmac100.h 2025-12-23 10:16:21.145032318 +0000 ++++ b/drivers/net/ethernet/faraday/ftgmac100.h 2026-04-08 18:03:48.332704918 +0000 @@ -57,6 +57,13 @@ #define FTGMAC100_OFFSET_RX_RUNT 0xc0 #define FTGMAC100_OFFSET_RX_CRCER_FTL 0xc4 @@ -47484,16 +54648,55 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.h b/drivers/net/ethernet/far /* * Receive descriptor, aligned to 16 bytes */ -@@ -271,4 +280,5 @@ +@@ -271,4 +280,44 @@ #define FTGMAC100_RXDES1_UDP_CHKSUM_ERR (1 << 26) #define FTGMAC100_RXDES1_IP_CHKSUM_ERR (1 << 27) +#define FTGMAC100_RXDES2_RXBUF_BADR_HI GENMASK(18, 16) ++ ++/* Aspeed SCU */ ++#define AST2600_MAC01_CLK_DLY 0x340 ++#define AST2600_MAC23_CLK_DLY 0x350 ++#define AST2600_MAC01_CLK_DLY_UNIT 45 /* ps */ ++#define AST2600_MAC01_TX_DLY_0_NS 0 ++#define AST2600_MAC01_RX_DLY_0_NS 0 ++#define AST2600_MAC23_CLK_DLY_UNIT 250 /* ps */ ++#define AST2600_MAC23_TX_DLY_0_NS 0 ++#define AST2600_MAC23_RX_DLY_0_NS 0x1a ++#define AST2600_MAC_TX_RX_DLY_MASK 0x1f ++#define ASPEED_MAC0_2_TX_DLY GENMASK(5, 0) ++#define ASPEED_MAC0_2_RX_DLY GENMASK(17, 12) ++#define ASPEED_MAC1_3_TX_DLY GENMASK(11, 6) ++#define ASPEED_MAC1_3_RX_DLY GENMASK(23, 18) ++ ++#define AST2600_MAC0_BASE_ADDR 0x1e660000 ++#define AST2600_MAC1_BASE_ADDR 0x1e680000 ++#define AST2600_MAC2_BASE_ADDR 0x1e670000 ++#define AST2600_MAC3_BASE_ADDR 0x1e690000 ++ ++/* Keep original delay */ ++#define AST2600_RGMII_KEEP_DELAY 0x01 ++/* Need to disable delay on MAC side */ ++#define AST2600_RGMII_DIS_DELAY 0x02 ++ ++#define AST2700_MAC0_DLY_UNIT 0x190 ++#define AST2700_MAC_TX_DLY_UNIT(x) FIELD_GET(GENMASK(15, 0), (x)) ++#define AST2700_MAC_RX_DLY_UNIT(x) FIELD_GET(GENMASK(31, 16), (x)) ++#define AST2700_MAC0_DLY_VAL 0x194 ++#define AST2700_MAC_TX_DLY_DIS(x) FIELD_GET(GENMASK(7, 0), (x)) ++#define AST2700_MAC_RX_DLY_DIS(x) FIELD_GET(GENMASK(23, 16), (x)) ++#define AST2700_MAC1_DLY_UNIT 0x198 ++#define AST2700_MAC1_DLY_VAL 0x19c ++#define AST2700_MAC01_CLK_DLY 0x390 ++#define AST2700_MAC0_BASE_ADDR 0x14050000 ++#define AST2700_MAC1_BASE_ADDR 0x14060000 ++#define AST2700_MAC2_BASE_ADDR 0x14070000 ++ #endif /* __FTGMAC100_H */ diff --git a/drivers/net/mctp/mctp-pcie-vdm.c b/drivers/net/mctp/mctp-pcie-vdm.c --- a/drivers/net/mctp/mctp-pcie-vdm.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/net/mctp/mctp-pcie-vdm.c 2025-12-23 10:16:21.981018306 +0000 -@@ -0,0 +1,363 @@ ++++ b/drivers/net/mctp/mctp-pcie-vdm.c 2026-04-08 18:03:49.259687981 +0000 +@@ -0,0 +1,361 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mctp-pcie-vdm.c - MCTP-over-PCIe-VDM (DMTF DSP0238) transport binding driver. @@ -47529,7 +54732,8 @@ diff --git a/drivers/net/mctp/mctp-pcie-vdm.c b/drivers/net/mctp/mctp-pcie-vdm.c +#include +#include + -+#define MCTP_PCIE_VDM_MIN_MTU 64 ++// 64bytes mctp payload + 4bytes mctp header ++#define MCTP_PCIE_VDM_MIN_MTU (64 + 4) +#define MCTP_PCIE_VDM_MAX_MTU 512 +/* 16byte */ +#define MCTP_PCIE_VDM_HDR_SIZE 16 @@ -47807,12 +55011,9 @@ diff --git a/drivers/net/mctp/mctp-pcie-vdm.c b/drivers/net/mctp/mctp-pcie-vdm.c + + cb = __mctp_cb(skb); + cb->halen = 3; // route type | bdf address -+ cb->haddr[0] = vdm_hdr->route_type; -+ // address is also converted to little-endian, but we want to keep it as big-endian -+ // because kernel network layer assumes address in big-endian format -+ cb->haddr[1] = vdm_hdr->pci_req_id & 0xFF; -+ cb->haddr[2] = vdm_hdr->pci_req_id >> 8; -+ ++ cb->haddr[0] = vdm_hdr->route_type & GENMASK(2, 0); ++ cb->haddr[1] = vdm_hdr->pci_req_id >> 8; ++ cb->haddr[2] = vdm_hdr->pci_req_id & 0xFF; + net_status = netif_rx(skb); + if (net_status == NET_RX_SUCCESS) { + stats->rx_packets++; @@ -47859,7 +55060,7 @@ diff --git a/drivers/net/mctp/mctp-pcie-vdm.c b/drivers/net/mctp/mctp-pcie-vdm.c +EXPORT_SYMBOL_GPL(mctp_pcie_vdm_remove_dev); diff --git a/drivers/net/mdio/mdio-aspeed.c b/drivers/net/mdio/mdio-aspeed.c --- a/drivers/net/mdio/mdio-aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/net/mdio/mdio-aspeed.c 2025-12-23 10:16:20.939035770 +0000 ++++ b/drivers/net/mdio/mdio-aspeed.c 2026-04-08 18:03:48.105709065 +0000 @@ -62,6 +62,8 @@ | FIELD_PREP(ASPEED_MDIO_DATA_MIIRDATA, data); @@ -47871,26 +55072,33 @@ diff --git a/drivers/net/mdio/mdio-aspeed.c b/drivers/net/mdio/mdio-aspeed.c !(ctrl & ASPEED_MDIO_CTRL_FIRE), diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig --- a/drivers/pci/controller/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pci/controller/Kconfig 2025-12-23 10:16:09.638225214 +0000 -@@ -47,6 +47,15 @@ ++++ b/drivers/pci/controller/Kconfig 2026-04-08 18:03:35.347942010 +0000 +@@ -47,6 +47,22 @@ If unsure, say Y if you have an Apple Silicon system. +config PCIE_ASPEED + bool "ASPEED PCIe controller" -+ depends on PCI -+ depends on OF || COMPILE_TEST -+ select PCI_MSI_ARCH_FALLBACKS ++ depends on ARCH_ASPEED || COMPILE_TEST ++ depends on OF ++ depends on PCI_MSI ++ select IRQ_MSI_LIB + help -+ Say Y here if you want PCIe controller support on -+ ASPEED SoCs. ++ Enable this option to support the PCIe controller found on ASPEED ++ SoCs. ++ ++ This driver provides initialization and management for PCIe ++ Root Complex functionality, including INTx and MSI support. ++ ++ Select Y if your platform uses an ASPEED SoC and requires PCIe ++ connectivity. + config PCI_VERSATILE bool "ARM Versatile PB PCI controller" depends on ARCH_VERSATILE || COMPILE_TEST diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile --- a/drivers/pci/controller/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pci/controller/Makefile 2025-12-23 10:16:13.816155152 +0000 ++++ b/drivers/pci/controller/Makefile 2026-04-08 18:03:40.287851903 +0000 @@ -39,6 +39,7 @@ obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o @@ -47901,8 +55109,8 @@ diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile obj-y += dwc/ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie-aspeed.c --- a/drivers/pci/controller/pcie-aspeed.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/pci/controller/pcie-aspeed.c 2025-12-23 10:16:21.109032921 +0000 -@@ -0,0 +1,1185 @@ ++++ b/drivers/pci/controller/pcie-aspeed.c 2026-04-08 18:03:48.290705685 +0000 +@@ -0,0 +1,1182 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PCIe host controller driver for ASPEED PCIe Bridge @@ -48055,6 +55263,7 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- + int reg_intx_sts; + int reg_msi_en; + int reg_msi_sts; ++ int msi_support; + int msi_address; +}; + @@ -48089,14 +55298,6 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- + DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_HOST_IRQS); +}; + -+static void aspeed_pcie_intx_ack_irq(struct irq_data *d) -+{ -+ struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(d); -+ int intx_en = pcie->platform->reg_intx_en; -+ -+ writel(readl(pcie->reg + intx_en) | BIT(d->hwirq), pcie->reg + intx_en); -+} -+ +static void aspeed_pcie_intx_mask_irq(struct irq_data *d) +{ + struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(d); @@ -48115,7 +55316,6 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- + +static struct irq_chip aspeed_intx_irq_chip = { + .name = "ASPEED:IntX", -+ .irq_ack = aspeed_pcie_intx_ack_irq, + .irq_mask = aspeed_pcie_intx_mask_irq, + .irq_unmask = aspeed_pcie_intx_unmask_irq, +}; @@ -48679,6 +55879,9 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- + writel(0, pcie->reg + pcie->platform->reg_intx_en); + writel(~0, pcie->reg + pcie->platform->reg_intx_sts); + ++ if (!pcie->platform->msi_support) ++ return 0; ++ +#ifdef CONFIG_PCI_MSI + pcie->dev_domain = + irq_domain_add_linear(NULL, MAX_MSI_HOST_IRQS, &aspeed_msi_domain_ops, pcie); @@ -49059,6 +56262,7 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- + .reg_intx_sts = 0x08, + .reg_msi_en = 0x20, + .reg_msi_sts = 0x28, ++ .msi_support = false, + .msi_address = 0x1e77005c, +}; + @@ -49068,6 +56272,7 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- + .reg_intx_sts = 0x48, + .reg_msi_en = 0x50, + .reg_msi_sts = 0x58, ++ .msi_support = true, + .msi_address = 0x000000f0, +}; + @@ -49090,7 +56295,7 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- +module_platform_driver(aspeed_pcie_driver); diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig --- a/drivers/phy/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/phy/Kconfig 2025-12-23 10:16:09.358229909 +0000 ++++ b/drivers/phy/Kconfig 2026-04-08 18:03:34.885950437 +0000 @@ -84,6 +84,7 @@ source "drivers/phy/allwinner/Kconfig" @@ -49101,7 +56306,7 @@ diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig source "drivers/phy/freescale/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile --- a/drivers/phy/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/phy/Makefile 2025-12-23 10:16:13.519160131 +0000 ++++ b/drivers/phy/Makefile 2026-04-08 18:03:39.794860903 +0000 @@ -13,6 +13,7 @@ obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o obj-y += allwinner/ \ @@ -49112,7 +56317,7 @@ diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile freescale/ \ diff --git a/drivers/phy/aspeed/Kconfig b/drivers/phy/aspeed/Kconfig --- a/drivers/phy/aspeed/Kconfig 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/phy/aspeed/Kconfig 2025-12-23 10:16:19.139065938 +0000 ++++ b/drivers/phy/aspeed/Kconfig 2026-04-08 18:03:46.226743396 +0000 @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0-only + @@ -49139,238 +56344,16 @@ diff --git a/drivers/phy/aspeed/Kconfig b/drivers/phy/aspeed/Kconfig + Enable driver support for Aspeed AST2700 PHY USB3. diff --git a/drivers/phy/aspeed/Makefile b/drivers/phy/aspeed/Makefile --- a/drivers/phy/aspeed/Makefile 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/phy/aspeed/Makefile 2025-12-23 10:16:19.139065938 +0000 ++++ b/drivers/phy/aspeed/Makefile 2026-04-08 18:03:46.226743396 +0000 @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + -+obj-$(CONFIG_PHY_ASPEED_SGMII) += aspeed-sgmii.o ++obj-$(CONFIG_PHY_ASPEED_SGMII) += phy-aspeed-sgmii.o +obj-$(CONFIG_PHY_ASPEED_USB3) += aspeed-usb-phy3.o \ No newline at end of file -diff --git a/drivers/phy/aspeed/aspeed-sgmii.c b/drivers/phy/aspeed/aspeed-sgmii.c ---- a/drivers/phy/aspeed/aspeed-sgmii.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/phy/aspeed/aspeed-sgmii.c 2025-12-23 10:16:21.033034195 +0000 -@@ -0,0 +1,218 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Copyright 2023 Aspeed Technology Inc. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define SGMII_CFG 0x00 -+#define SGMII_LINK_TIMER 0x08 -+#define SGMII_NWAY_ACK 0x0c -+#define SGMII_PHY_CFG1 0x18 -+#define SGMII_PHY_PIPE_CTL 0x20 -+#define SGMII_FIFO_DELAY_THREHOLD 0x28 -+#define SGMII_MODE 0x30 -+ -+#define SGMII_CFG_FIFO_MODE BIT(0) -+#define SGMII_CFG_SPEED_10M 0 -+#define SGMII_CFG_SPEED_100M BIT(4) -+#define SGMII_CFG_SPEED_1G BIT(5) -+#define SGMII_CFG_PWR_DOWN BIT(11) -+#define SGMII_CFG_AN_ENABLE BIT(12) -+#define SGMII_CFG_SW_RESET BIT(15) -+#define SGMII_PCTL_TX_NO_DEEMPH BIT(7) -+#define SGMII_MODE_ENABLE BIT(0) -+#define SGMII_MODE_USE_LOCAL_CONFIG BIT(2) -+ -+#define PLDA_CLK 0x268 -+ -+#define PLDA_CLK_SEL_INTERNAL_25M BIT(8) -+#define PLDA_CLK_FREQ_MULTI GENMASK(7, 0) -+ -+struct aspeed_sgmii { -+ struct device *dev; -+ void __iomem *regs; -+ struct regmap *plda_regmap; -+}; -+ -+static void aspeed_sgmii_set_nway(struct phy *phy) -+{ -+ struct aspeed_sgmii *sgmii = phy_get_drvdata(phy); -+ u32 reg; -+ -+ /* -+ * The PLDA frequency multiplication is X xor 0x19. -+ * (X xor 0x19) * clock source = data rate. -+ * SGMII data rate is 1.25G, so (0x2b xor 0x19) * 25MHz is equal 1.25G. -+ */ -+ reg = PLDA_CLK_SEL_INTERNAL_25M | FIELD_PREP(PLDA_CLK_FREQ_MULTI, 0x2b); -+ regmap_write(sgmii->plda_regmap, PLDA_CLK, reg); -+ -+ writel(0, sgmii->regs + SGMII_MODE); -+ -+ writel(0, sgmii->regs + SGMII_CFG); -+ reg = SGMII_CFG_SW_RESET | SGMII_CFG_PWR_DOWN; -+ writel(reg, sgmii->regs + SGMII_CFG); -+ -+ reg = SGMII_CFG_AN_ENABLE; -+ writel(reg, sgmii->regs + SGMII_CFG); -+ -+ writel(0x0c, sgmii->regs + SGMII_FIFO_DELAY_THREHOLD); -+ -+ writel(SGMII_PCTL_TX_NO_DEEMPH, sgmii->regs + SGMII_PHY_PIPE_CTL); -+ -+ /* Set link timer for Nway state change */ -+ writel(0x100, sgmii->regs + SGMII_LINK_TIMER); -+ -+ /* Bit 0 always sets to 1 in ACK message */ -+ writel(0x1, sgmii->regs + SGMII_NWAY_ACK); -+ -+ reg = SGMII_MODE_ENABLE; -+ writel(reg, sgmii->regs + SGMII_MODE); -+} -+ -+static int aspeed_sgmii_phy_init(struct phy *phy) -+{ -+ aspeed_sgmii_set_nway(phy); -+ -+ return 0; -+} -+ -+static int aspeed_sgmii_phy_exit(struct phy *phy) -+{ -+ struct aspeed_sgmii *sgmii = phy_get_drvdata(phy); -+ -+ /* Disable SGMII controller */ -+ writel(0, sgmii->regs + SGMII_MODE); -+ -+ return 0; -+} -+ -+static int aspeed_sgmii_phy_set_speed(struct phy *phy, int speed) -+{ -+ struct aspeed_sgmii *sgmii = phy_get_drvdata(phy); -+ u32 reg; -+ -+ reg = PLDA_CLK_SEL_INTERNAL_25M | FIELD_PREP(PLDA_CLK_FREQ_MULTI, 0x2b); -+ regmap_write(sgmii->plda_regmap, PLDA_CLK, reg); -+ -+ switch (speed) { -+ case SPEED_10: -+ reg = SGMII_CFG_SPEED_10M; -+ break; -+ case SPEED_100: -+ reg = SGMII_CFG_SPEED_100M; -+ break; -+ case SPEED_1000: -+ reg = SGMII_CFG_SPEED_1G; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ writel(0, sgmii->regs + SGMII_MODE); -+ -+ writel((reg >> 2), sgmii->regs + SGMII_PHY_CFG1); -+ -+ writel(0, sgmii->regs + SGMII_CFG); -+ writel(SGMII_CFG_SW_RESET | SGMII_CFG_PWR_DOWN, sgmii->regs + SGMII_CFG); -+ writel(reg, sgmii->regs + SGMII_CFG); -+ -+ writel(0x0c, sgmii->regs + SGMII_FIFO_DELAY_THREHOLD); -+ writel(SGMII_PCTL_TX_NO_DEEMPH, sgmii->regs + SGMII_PHY_PIPE_CTL); -+ -+ /* Set link timer for Nway state change */ -+ writel(0x100, sgmii->regs + SGMII_LINK_TIMER); -+ -+ /* Bit 0 always sets to 1 in ACK message */ -+ writel(0x1, sgmii->regs + SGMII_NWAY_ACK); -+ -+ writel(SGMII_MODE_ENABLE | SGMII_MODE_USE_LOCAL_CONFIG, sgmii->regs + SGMII_MODE); -+ -+ return 0; -+} -+ -+static const struct phy_ops aspeed_sgmii_phyops = { -+ .init = aspeed_sgmii_phy_init, -+ .set_speed = aspeed_sgmii_phy_set_speed, -+ .exit = aspeed_sgmii_phy_exit, -+ .owner = THIS_MODULE, -+}; -+ -+static int aspeed_sgmii_probe(struct platform_device *pdev) -+{ -+ struct aspeed_sgmii *sgmii; -+ struct resource *res; -+ struct device *dev; -+ struct device_node *np; -+ struct phy_provider *provider; -+ struct phy *phy; -+ -+ dev = &pdev->dev; -+ -+ sgmii = devm_kzalloc(dev, sizeof(*sgmii), GFP_KERNEL); -+ if (!sgmii) -+ return -ENOMEM; -+ -+ sgmii->dev = dev; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) { -+ dev_err(dev, "cannot get resource\n"); -+ return -ENODEV; -+ } -+ -+ sgmii->regs = devm_ioremap_resource(dev, res); -+ if (IS_ERR(sgmii->regs)) { -+ dev_err(dev, "cannot map registers\n"); -+ return PTR_ERR(sgmii->regs); -+ } -+ -+ np = pdev->dev.of_node; -+ sgmii->plda_regmap = syscon_regmap_lookup_by_phandle(np, "aspeed,plda"); -+ if (IS_ERR(sgmii->plda_regmap)) { -+ dev_err(sgmii->dev, "Unable to find plda regmap (%ld)\n", -+ PTR_ERR(sgmii->plda_regmap)); -+ return PTR_ERR(sgmii->plda_regmap); -+ } -+ -+ phy = devm_phy_create(dev, NULL, &aspeed_sgmii_phyops); -+ if (IS_ERR(phy)) { -+ dev_err(&pdev->dev, "failed to create PHY\n"); -+ return PTR_ERR(phy); -+ } -+ -+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); -+ if (IS_ERR(provider)) -+ return PTR_ERR(provider); -+ -+ phy_set_drvdata(phy, sgmii); -+ -+ dev_info(dev, "module loaded\n"); -+ -+ return 0; -+} -+ -+static const struct of_device_id aspeed_sgmii_of_matches[] = { -+ { .compatible = "aspeed,ast2700-sgmii" }, -+ { }, -+}; -+ -+static struct platform_driver aspeed_sgmii_driver = { -+ .probe = aspeed_sgmii_probe, -+ .driver = { -+ .name = "aspeed-sgmii", -+ .of_match_table = aspeed_sgmii_of_matches, -+ }, -+}; -+ -+module_platform_driver(aspeed_sgmii_driver); -+ -+MODULE_AUTHOR("Jacky Chou "); -+MODULE_DESCRIPTION("Control of ASPEED SGMII Device"); -+MODULE_LICENSE("GPL"); diff --git a/drivers/phy/aspeed/aspeed-usb-phy3.c b/drivers/phy/aspeed/aspeed-usb-phy3.c --- a/drivers/phy/aspeed/aspeed-usb-phy3.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/phy/aspeed/aspeed-usb-phy3.c 2025-12-23 10:16:21.038034111 +0000 ++++ b/drivers/phy/aspeed/aspeed-usb-phy3.c 2026-04-08 18:03:48.216707037 +0000 @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* @@ -49629,9 +56612,247 @@ diff --git a/drivers/phy/aspeed/aspeed-usb-phy3.c b/drivers/phy/aspeed/aspeed-us + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Joe Wang "); +diff --git a/drivers/phy/aspeed/phy-aspeed-sgmii.c b/drivers/phy/aspeed/phy-aspeed-sgmii.c +--- a/drivers/phy/aspeed/phy-aspeed-sgmii.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/phy/aspeed/phy-aspeed-sgmii.c 2026-04-08 18:03:48.211707129 +0000 +@@ -0,0 +1,234 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2023 Aspeed Technology Inc. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SCU_HW_REVISION_ID GENMASK(23, 16) ++ ++#define SGMII_CFG 0x00 ++#define SGMII_CFG_FIFO_MODE BIT(0) ++#define SGMII_CFG_SPEED_SEL_MASK GENMASK(5, 4) ++#define SGMII_CFG_SPEED_SEL(x) FIELD_PREP(SGMII_CFG_SPEED_SEL_MASK, (x)) ++#define SGMII_CFG_PWR_DOWN BIT(11) ++#define SGMII_CFG_AN_ENABLE BIT(12) ++#define SGMII_CFG_SW_RESET BIT(15) ++#define SGMII_LINK_TIMER 0x08 ++#define SGMII_NWAY_ACK 0x0c ++#define SGMII_PHY_CFG1 0x18 ++#define SGMII_PHY_SPEED_MASK GENMASK(3, 2) ++#define SGMII_PHY_SPEED(x) FIELD_PREP(SGMII_PHY_SPEED_MASK, (x)) ++#define SGMII_PHY_PIPE_CTL 0x20 ++#define SGMII_PCTL_TX_NO_DEEMPH BIT(7) ++#define SGMII_FIFO_DELAY_THREHOLD 0x28 ++#define SGMII_MODE 0x30 ++#define SGMII_MODE_ENABLE BIT(0) ++#define SGMII_MODE_USE_LOCAL_CONFIG BIT(2) ++ ++#define PEHR280 0x280 ++#define SGMII_INTERNAL_CLK_EN BIT(26) ++#define PCIEPHY_CLK 0x268 ++#define PCIEPHY_CLK_FREQ_MULTI_MASK GENMASK(7, 0) ++#define PCIEPHY_CLK_FREQ_MULTI(x) FIELD_PREP(PCIEPHY_CLK_FREQ_MULTI_MASK, (x)) ++#define PCIEPHY_CLK_SEL_INTERNAL_25M BIT(8) ++ ++#define SGMII_SPEED_10M 0x00 ++#define SGMII_SPEED_100M 0x01 ++#define SGMII_SPEED_1G 0x02 ++ ++struct aspeed_sgmii { ++ struct device *dev; ++ void __iomem *regs; ++ struct regmap *pcie_phy_regmap; ++ u8 revision; ++}; ++ ++static int aspeed_sgmii_conf(struct phy *phy, bool nway, int speed) ++{ ++ struct aspeed_sgmii *sgmii = phy_get_drvdata(phy); ++ u32 cfg; ++ ++ writel(0, sgmii->regs + SGMII_MODE); ++ ++ writel(0, sgmii->regs + SGMII_CFG); ++ writel(SGMII_CFG_SW_RESET | SGMII_CFG_PWR_DOWN, sgmii->regs + SGMII_CFG); ++ if (nway) { ++ if (sgmii->revision == 1) ++ writel(SGMII_CFG_AN_ENABLE, sgmii->regs + SGMII_CFG); ++ else ++ writel(SGMII_CFG_AN_ENABLE | SGMII_CFG_FIFO_MODE, ++ sgmii->regs + SGMII_CFG); ++ } else { ++ switch (speed) { ++ case SPEED_10: ++ cfg = SGMII_SPEED_10M; ++ break; ++ case SPEED_100: ++ cfg = SGMII_SPEED_100M; ++ break; ++ case SPEED_1000: ++ cfg = SGMII_SPEED_1G; ++ break; ++ default: ++ return -EINVAL; ++ } ++ writel(SGMII_PHY_SPEED(cfg), sgmii->regs + SGMII_PHY_CFG1); ++ if (sgmii->revision == 1) ++ writel(SGMII_CFG_SPEED_SEL(cfg), ++ sgmii->regs + SGMII_CFG); ++ else ++ writel(SGMII_CFG_SPEED_SEL(cfg) | SGMII_CFG_FIFO_MODE, ++ sgmii->regs + SGMII_CFG); ++ } ++ ++ if (sgmii->revision == 1) ++ writel(0x0c, sgmii->regs + SGMII_FIFO_DELAY_THREHOLD); ++ else ++ writel(0x0e, sgmii->regs + SGMII_FIFO_DELAY_THREHOLD); ++ writel(SGMII_PCTL_TX_NO_DEEMPH, sgmii->regs + SGMII_PHY_PIPE_CTL); ++ ++ /* Set link timer for state change */ ++ writel(0x100, sgmii->regs + SGMII_LINK_TIMER); ++ ++ /* Bit 0 always sets to 1 in ACK message */ ++ writel(0x1, sgmii->regs + SGMII_NWAY_ACK); ++ ++ cfg = SGMII_MODE_ENABLE; ++ if (!nway) ++ cfg |= SGMII_MODE_USE_LOCAL_CONFIG; ++ writel(cfg, sgmii->regs + SGMII_MODE); ++ ++ return 0; ++} ++ ++static int aspeed_sgmii_phy_init(struct phy *phy) ++{ ++ /* Default to enable Nway, not need configure speed */ ++ return aspeed_sgmii_conf(phy, true, 0); ++} ++ ++static int aspeed_sgmii_phy_set_speed(struct phy *phy, int speed) ++{ ++ return aspeed_sgmii_conf(phy, false, speed); ++} ++ ++static int aspeed_sgmii_phy_exit(struct phy *phy) ++{ ++ struct aspeed_sgmii *sgmii = phy_get_drvdata(phy); ++ ++ /* Disable SGMII controller */ ++ writel(0, sgmii->regs + SGMII_MODE); ++ ++ return 0; ++} ++ ++static const struct phy_ops aspeed_sgmii_phyops = { ++ .init = aspeed_sgmii_phy_init, ++ .set_speed = aspeed_sgmii_phy_set_speed, ++ .exit = aspeed_sgmii_phy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static int aspeed_sgmii_probe(struct platform_device *pdev) ++{ ++ struct phy_provider *provider; ++ struct aspeed_sgmii *sgmii; ++ struct regmap *scu_regmap; ++ struct device_node *np; ++ struct resource *res; ++ struct device *dev; ++ struct phy *phy; ++ u32 reg; ++ ++ dev = &pdev->dev; ++ ++ sgmii = devm_kzalloc(dev, sizeof(*sgmii), GFP_KERNEL); ++ if (!sgmii) ++ return -ENOMEM; ++ ++ sgmii->dev = dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(dev, "cannot get resource\n"); ++ return -ENODEV; ++ } ++ ++ sgmii->regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR(sgmii->regs)) { ++ dev_err(dev, "cannot map registers\n"); ++ return PTR_ERR(sgmii->regs); ++ } ++ ++ np = pdev->dev.of_node; ++ sgmii->pcie_phy_regmap = syscon_regmap_lookup_by_phandle(np, "phys"); ++ if (IS_ERR(sgmii->pcie_phy_regmap)) { ++ dev_err(sgmii->dev, "Unable to find phys regmap (%ld)\n", ++ PTR_ERR(sgmii->pcie_phy_regmap)); ++ return PTR_ERR(sgmii->pcie_phy_regmap); ++ } ++ ++ scu_regmap = syscon_regmap_lookup_by_phandle(np, "aspeed,scu"); ++ if (IS_ERR(scu_regmap)) { ++ dev_err(sgmii->dev, "Unable to find SCU regmap (%ld)\n", ++ PTR_ERR(scu_regmap)); ++ return PTR_ERR(scu_regmap); ++ } ++ ++ regmap_read(scu_regmap, 0x00, ®); ++ sgmii->revision = FIELD_GET(SCU_HW_REVISION_ID, reg); ++ ++ phy = devm_phy_create(dev, NULL, &aspeed_sgmii_phyops); ++ if (IS_ERR(phy)) { ++ dev_err(&pdev->dev, "failed to create PHY\n"); ++ return PTR_ERR(phy); ++ } ++ ++ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(provider)) ++ return PTR_ERR(provider); ++ ++ phy_set_drvdata(phy, sgmii); ++ ++ /* ++ * The PLDA frequency multiplication is X xor 0x19. ++ * (X xor 0x19) * clock source = data rate. ++ * SGMII data rate is 1.25G, so (0x2b xor 0x19) * 25MHz is equal 1.25G. ++ */ ++ reg = PCIEPHY_CLK_SEL_INTERNAL_25M | PCIEPHY_CLK_FREQ_MULTI(0x2b); ++ regmap_write(sgmii->pcie_phy_regmap, PCIEPHY_CLK, reg); ++ if (sgmii->revision > 1) { ++ regmap_read(sgmii->pcie_phy_regmap, PEHR280, ®); ++ reg |= SGMII_INTERNAL_CLK_EN; ++ regmap_write(sgmii->pcie_phy_regmap, PEHR280, reg); ++ } ++ ++ dev_info(dev, "module loaded\n"); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_sgmii_of_matches[] = { ++ { .compatible = "aspeed,ast2700-sgmii" }, ++ { }, ++}; ++ ++static struct platform_driver aspeed_sgmii_driver = { ++ .probe = aspeed_sgmii_probe, ++ .driver = { ++ .name = "aspeed-sgmii", ++ .of_match_table = aspeed_sgmii_of_matches, ++ }, ++}; ++ ++module_platform_driver(aspeed_sgmii_driver); ++ ++MODULE_AUTHOR("Jacky Chou "); ++MODULE_DESCRIPTION("ASPEED SGMII Serdes/PHY controller"); ++MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/aspeed/Kconfig b/drivers/pinctrl/aspeed/Kconfig --- a/drivers/pinctrl/aspeed/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/Kconfig 2025-12-23 10:16:21.135032485 +0000 ++++ b/drivers/pinctrl/aspeed/Kconfig 2026-04-08 18:03:48.322705101 +0000 @@ -31,3 +31,11 @@ help Say Y here to enable pin controller support for Aspeed's 6th @@ -49646,7 +56867,7 @@ diff --git a/drivers/pinctrl/aspeed/Kconfig b/drivers/pinctrl/aspeed/Kconfig + generation SoCs. GPIO is provided by a separate GPIO driver. diff --git a/drivers/pinctrl/aspeed/Makefile b/drivers/pinctrl/aspeed/Makefile --- a/drivers/pinctrl/aspeed/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/Makefile 2025-12-23 10:16:21.135032485 +0000 ++++ b/drivers/pinctrl/aspeed/Makefile 2026-04-08 18:03:48.322705101 +0000 @@ -6,3 +6,4 @@ obj-$(CONFIG_PINCTRL_ASPEED_G4) += pinctrl-aspeed-g4.o obj-$(CONFIG_PINCTRL_ASPEED_G5) += pinctrl-aspeed-g5.o @@ -49655,7 +56876,7 @@ diff --git a/drivers/pinctrl/aspeed/Makefile b/drivers/pinctrl/aspeed/Makefile \ No newline at end of file diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c --- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c 2025-12-23 10:16:21.136032468 +0000 ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c 2026-04-08 18:03:48.322705101 +0000 @@ -17,6 +17,8 @@ #include "../pinctrl-utils.h" #include "pinctrl-aspeed.h" @@ -50793,7 +58014,7 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c b/drivers/pinctrl/aspeed ASPEED_SB_PINCONF(PIN_CONFIG_BIAS_DISABLE, Y1, Y4, SCU40C, 4), diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c --- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c 2025-12-23 10:16:21.136032468 +0000 ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c 2026-04-08 18:03:48.322705101 +0000 @@ -0,0 +1,1156 @@ +// SPDX-License-Identifier: GPL-2.0 + @@ -51953,7 +59174,7 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c b/drivers/pinctrl/a +arch_initcall(aspeed_g7_ltpi_pinctrl_register); diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c --- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c 2025-12-23 10:16:21.136032468 +0000 ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c 2026-04-08 18:03:48.322705101 +0000 @@ -0,0 +1,503 @@ +// SPDX-License-Identifier: GPL-2.0 + @@ -52460,7 +59681,7 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c b/drivers/pinctrl/a +arch_initcall(aspeed_g7_soc0_pinctrl_register); diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c --- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c 2025-12-23 10:16:21.136032468 +0000 ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c 2026-04-08 18:03:48.323705083 +0000 @@ -0,0 +1,2533 @@ +// SPDX-License-Identifier: GPL-2.0 + @@ -54997,7 +62218,7 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/a +arch_initcall(aspeed_g7_soc1_pinctrl_register); diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.c b/drivers/pinctrl/aspeed/pinctrl-aspeed.c --- a/drivers/pinctrl/aspeed/pinctrl-aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.c 2025-12-23 10:16:21.136032468 +0000 ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.c 2026-04-08 18:03:48.323705083 +0000 @@ -285,6 +285,32 @@ return 0; } @@ -55061,7 +62282,7 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.c b/drivers/pinctrl/aspeed/pi struct aspeed_pinctrl_data *pdata) diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.h b/drivers/pinctrl/aspeed/pinctrl-aspeed.h --- a/drivers/pinctrl/aspeed/pinctrl-aspeed.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.h 2025-12-23 10:16:21.136032468 +0000 ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.h 2026-04-08 18:03:48.323705083 +0000 @@ -70,15 +70,15 @@ struct regmap *scu; @@ -55095,7 +62316,7 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.h b/drivers/pinctrl/aspeed/pi struct aspeed_pinctrl_data *pdata); diff --git a/drivers/pinctrl/aspeed/pinmux-aspeed.h b/drivers/pinctrl/aspeed/pinmux-aspeed.h --- a/drivers/pinctrl/aspeed/pinmux-aspeed.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/pinmux-aspeed.h 2025-12-23 10:16:21.136032468 +0000 ++++ b/drivers/pinctrl/aspeed/pinmux-aspeed.h 2026-04-08 18:03:48.323705083 +0000 @@ -792,6 +792,33 @@ const struct aspeed_sig_expr *expr, bool enabled); }; @@ -55149,7 +62370,7 @@ diff --git a/drivers/pinctrl/aspeed/pinmux-aspeed.h b/drivers/pinctrl/aspeed/pin int aspeed_sig_desc_eval(const struct aspeed_sig_desc *desc, bool enabled, diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c --- a/drivers/pwm/core.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pwm/core.c 2025-12-23 10:16:22.024017585 +0000 ++++ b/drivers/pwm/core.c 2026-04-08 18:03:49.301687214 +0000 @@ -426,9 +426,8 @@ * chip. A negative error code is returned if the index is not valid for the * specified PWM chip or if the PWM device cannot be requested. @@ -55171,8 +62392,8 @@ diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c struct pwm_device * of_pwm_xlate_with_flags(struct pwm_chip *chip, const struct of_phandle_args *args) diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig ---- a/drivers/reset/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/reset/Kconfig 2025-12-23 10:16:09.724223772 +0000 +--- a/drivers/reset/Kconfig 2026-04-08 18:03:23.253162611 +0000 ++++ b/drivers/reset/Kconfig 2026-04-08 18:03:35.468939804 +0000 @@ -22,6 +22,13 @@ This option enables support for the external reset functions for peripheral PHYs on the Altera Arria10 System Resource Chip. @@ -55187,43 +62408,9 @@ diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig config RESET_ATH79 bool "AR71xx Reset Driver" if COMPILE_TEST default ATH79 -@@ -51,8 +58,8 @@ - - config RESET_BRCMSTB - tristate "Broadcom STB reset controller" -- depends on ARCH_BRCMSTB || COMPILE_TEST -- default ARCH_BRCMSTB -+ depends on ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST -+ default ARCH_BRCMSTB || ARCH_BCM2835 - help - This enables the reset controller driver for Broadcom STB SoCs using - a SUN_TOP_CTRL_SW_INIT style controller. -@@ -60,11 +67,11 @@ - config RESET_BRCMSTB_RESCAL - tristate "Broadcom STB RESCAL reset controller" - depends on HAS_IOMEM -- depends on ARCH_BRCMSTB || COMPILE_TEST -- default ARCH_BRCMSTB -+ depends on ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST -+ default ARCH_BRCMSTB || ARCH_BCM2835 - help - This enables the RESCAL reset controller for SATA, PCIe0, or PCIe1 on -- BCM7216. -+ BCM7216 or the BCM2712. - - config RESET_EYEQ - bool "Mobileye EyeQ reset controller" -@@ -170,6 +177,7 @@ - config RESET_NPCM - bool "NPCM BMC Reset Driver" if COMPILE_TEST - default ARCH_NPCM -+ select AUXILIARY_BUS - help - This enables the reset controller driver for Nuvoton NPCM - BMC SoCs. diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile ---- a/drivers/reset/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/reset/Makefile 2025-12-23 10:16:13.896153811 +0000 +--- a/drivers/reset/Makefile 2026-04-08 18:03:23.253162611 +0000 ++++ b/drivers/reset/Makefile 2026-04-08 18:03:40.401849820 +0000 @@ -5,6 +5,7 @@ obj-y += sti/ obj-y += tegra/ @@ -55234,7 +62421,7 @@ diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o diff --git a/drivers/reset/reset-aspeed.c b/drivers/reset/reset-aspeed.c --- a/drivers/reset/reset-aspeed.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/reset/reset-aspeed.c 2025-12-23 10:16:21.120032737 +0000 ++++ b/drivers/reset/reset-aspeed.c 2026-04-08 18:03:48.301705484 +0000 @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* @@ -55548,7 +62735,7 @@ diff --git a/drivers/reset/reset-aspeed.c b/drivers/reset/reset-aspeed.c +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-aspeed.c b/drivers/rtc/rtc-aspeed.c --- a/drivers/rtc/rtc-aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/rtc/rtc-aspeed.c 2025-12-23 10:16:21.094033172 +0000 ++++ b/drivers/rtc/rtc-aspeed.c 2026-04-08 18:03:48.273705996 +0000 @@ -10,14 +10,64 @@ struct aspeed_rtc { struct rtc_device *rtc_dev; @@ -55634,7 +62821,7 @@ diff --git a/drivers/rtc/rtc-aspeed.c b/drivers/rtc/rtc-aspeed.c cent = (tm->tm_year + 1900) / 100; year = tm->tm_year % 100; -@@ -77,40 +129,211 @@ +@@ -77,40 +129,215 @@ return 0; } @@ -55814,21 +63001,25 @@ diff --git a/drivers/rtc/rtc-aspeed.c b/drivers/rtc/rtc-aspeed.c + * In rtc_read_time, run aspeed_rtc_read_time and check the rtc_time. + * As a result, need to enable and initialize RTC time. + * ++ * If the RTC_ENABLE has been set, it means that the RTC has been initialed + * Enable and unlock RTC to initialize RTC time to 1970-01-01T01:01:01 + * and re-lock and ensure enable is set now that a time is programmed. + */ + ctrl = readl(rtc->base + RTC_CTRL); -+ writel(ctrl | RTC_UNLOCK, rtc->base + RTC_CTRL); + -+ /* -+ * Initial value set to year:70,mon:0,mday:1,hour:1,min:1,sec:1 -+ * rtc_valid_tm check whether in suitable range or not. -+ */ -+ writel(0x01010101, rtc->base + RTC_TIME); -+ writel(0x00134601, rtc->base + RTC_YEAR); ++ if (!(ctrl & RTC_ENABLE)) { ++ writel(ctrl | RTC_UNLOCK, rtc->base + RTC_CTRL); ++ ++ /* ++ * Initial value set to year:70,mon:0,mday:1,hour:1,min:1,sec:1 ++ * rtc_valid_tm check whether in suitable range or not. ++ */ ++ writel(0x01010101, rtc->base + RTC_TIME); ++ writel(0x00134601, rtc->base + RTC_YEAR); + -+ /* Re-lock and ensure enable is set now that a time is programmed */ -+ writel(ctrl | RTC_ENABLE, rtc->base + RTC_CTRL); ++ /* Re-lock and ensure enable is set now that a time is programmed */ ++ writel(ctrl | RTC_ENABLE, rtc->base + RTC_CTRL); ++ } + + rc = devm_rtc_register_device(rtc->rtc_dev); + if (rc) { @@ -55852,7 +63043,7 @@ diff --git a/drivers/rtc/rtc-aspeed.c b/drivers/rtc/rtc-aspeed.c MODULE_DEVICE_TABLE(of, aspeed_rtc_match); diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig --- a/drivers/soc/aspeed/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/soc/aspeed/Kconfig 2025-12-23 10:16:21.124032669 +0000 ++++ b/drivers/soc/aspeed/Kconfig 2026-04-08 18:03:48.310705320 +0000 @@ -4,6 +4,12 @@ menu "ASPEED SoC drivers" @@ -55897,7 +63088,7 @@ diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig config ASPEED_P2A_CTRL tristate "ASPEED P2A (VGA MMIO to BMC) bridge control" select REGMAP -@@ -52,6 +75,134 @@ +@@ -52,6 +75,100 @@ help Say yes to support decoding of ASPEED BMC information. @@ -55970,41 +63161,6 @@ diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig +config ASPEED_PCIE_MMBI + tristate "ASPEED PCIE MMBI" + -+config ASPEED_ESPI -+ tristate "ASPEED eSPI slave driver" -+ select AST2500_ESPI if MACH_ASPEED_G5 -+ select AST2600_ESPI if MACH_ASPEED_G6 -+ default n -+ -+config AST2500_ESPI -+ tristate -+ depends on ASPEED_ESPI -+ help -+ Enable driver support for Aspeed AST2500 eSPI engine. -+ -+config AST2600_ESPI -+ tristate "ASPEED AST2600 eSPI slave driver" -+ help -+ Enable driver support for Aspeed AST2600 eSPI engine. The eSPI engine -+ plays as a slave device in BMC to communicate with the Host over -+ the eSPI interface. The four eSPI channels, namely peripheral, -+ virtual wire, out-of-band, and flash are supported. -+ -+config AST2700_ESPI -+ tristate "ASPEED AST2700 eSPI slave driver" -+ help -+ Enable driver support for Aspeed AST2700 eSPI engine. The eSPI engine -+ plays as a slave device in BMC to communicate with the Host over -+ the eSPI interface. The four eSPI channels, namely peripheral, -+ virtual wire, out-of-band, and flash are supported. -+ -+config AST2700_RTC_OVER_ESPI -+ tristate "ASPEED AST2700 RTC over eSPI drvier" -+ depends on HAS_IOMEM -+ help -+ Enable driver support for Aspeed AST2700 RTC over eSPI function. -+ Periodically copies RAM content from a RTC tm to a memory-mapped eSPI region. -+ +config ASPEED_OTP + tristate + help @@ -56027,6 +63183,7 @@ diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig + help + Enable driver support for Aspeed AST2700 OTP driver. + ++source "drivers/soc/aspeed/espi/Kconfig" +source "drivers/soc/aspeed/rvas/Kconfig" + endmenu @@ -56034,8 +63191,8 @@ diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig endif diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile --- a/drivers/soc/aspeed/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/soc/aspeed/Makefile 2025-12-23 10:16:21.183031681 +0000 -@@ -1,6 +1,26 @@ ++++ b/drivers/soc/aspeed/Makefile 2026-04-08 18:03:48.360704407 +0000 +@@ -1,6 +1,23 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_ASPEED_BMC_DEV) += aspeed-bmc-dev.o +obj-$(CONFIG_ASPEED_HOST_BMC_DEV) += aspeed-host-bmc-dev.o @@ -56049,9 +63206,6 @@ diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o +obj-$(CONFIG_ASPEED_XDMA) += aspeed-xdma.o -+obj-$(CONFIG_AST2500_ESPI) += ast2500-espi.o -+obj-$(CONFIG_AST2600_ESPI) += ast2600-espi.o -+obj-$(CONFIG_AST2700_ESPI) += ast2700-espi.o +obj-$(CONFIG_ASPEED_RVAS) += rvas/ +obj-$(CONFIG_ARCH_ASPEED) += aspeed-usb-phy.o +obj-$(CONFIG_ARCH_ASPEED) += aspeed-usb-hp.o @@ -56059,13 +63213,13 @@ diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile +obj-$(CONFIG_ASPEED_DISP_INTF) += aspeed-disp-intf.o +obj-$(CONFIG_ASPEED_PCIE_MMBI) += aspeed-pcie-mmbi.o +obj-$(CONFIG_ASPEED_MBOX) += aspeed-mbox.o -+obj-$(CONFIG_AST2700_RTC_OVER_ESPI) += ast2700-rtc-over-espi.o +obj-$(CONFIG_AST2600_OTP) += ast2600-otp.o +obj-$(CONFIG_AST2700_OTP) += ast2700-otp.o ++obj-y += espi/ diff --git a/drivers/soc/aspeed/aspeed-bmc-dev.c b/drivers/soc/aspeed/aspeed-bmc-dev.c --- a/drivers/soc/aspeed/aspeed-bmc-dev.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-bmc-dev.c 2025-12-23 10:16:21.124032669 +0000 -@@ -0,0 +1,699 @@ ++++ b/drivers/soc/aspeed/aspeed-bmc-dev.c 2026-04-08 18:03:48.310705320 +0000 +@@ -0,0 +1,703 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (C) ASPEED Technology Inc. + @@ -56188,6 +63342,7 @@ diff --git a/drivers/soc/aspeed/aspeed-bmc-dev.c b/drivers/soc/aspeed/aspeed-bmc + void __iomem *reg_base; + dma_addr_t bmc_mem_phy; + phys_addr_t bmc_mem_size; ++ void *bmc_mem_cpu; + + int pcie2lpc; + int irq; @@ -56214,18 +63369,15 @@ diff --git a/drivers/soc/aspeed/aspeed-bmc-dev.c b/drivers/soc/aspeed/aspeed-bmc +{ + struct aspeed_bmc_device *bmc_device = file_aspeed_bmc_device(file); + unsigned long vsize = vma->vm_end - vma->vm_start; -+ pgprot_t prot = vma->vm_page_prot; + -+ if (((vma->vm_pgoff << PAGE_SHIFT) + vsize) > bmc_device->bmc_mem_size) ++ if (vsize > bmc_device->bmc_mem_size) + return -EINVAL; + -+ prot = pgprot_noncached(prot); -+ -+ if (remap_pfn_range(vma, vma->vm_start, -+ (bmc_device->bmc_mem_phy >> PAGE_SHIFT) + vma->vm_pgoff, vsize, prot)) -+ return -EAGAIN; ++ return dma_mmap_coherent(bmc_device->dev, vma, ++ bmc_device->bmc_mem_cpu, ++ bmc_device->bmc_mem_phy, ++ bmc_device->bmc_mem_size); + -+ return 0; +} + +static const struct file_operations aspeed_bmc_device_fops = { @@ -56649,42 +63801,53 @@ diff --git a/drivers/soc/aspeed/aspeed-bmc-dev.c b/drivers/soc/aspeed/aspeed-bmc + bmc_device->dev = dev; + bmc_device->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(bmc_device->reg_base)) -+ goto out_region; ++ return PTR_ERR(bmc_device->reg_base); ++ ++ ret = of_reserved_mem_device_init(dev); ++ if (ret) { ++ dev_err(dev, "of_reserved_mem_device_init failed: %d\n", ret); ++ return ret; ++ } + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (ret) { + dev_err(dev, "cannot set 64-bits DMA mask\n"); -+ goto out_region; ++ return ret; + } + + np = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!np || of_address_to_resource(np, 0, &res)) { + dev_err(dev, "Failed to find memory-region.\n"); -+ ret = -ENOMEM; -+ goto out_region; ++ return -ENOMEM; + } + + of_node_put(np); + -+ bmc_device->bmc_mem_phy = res.start; + bmc_device->bmc_mem_size = resource_size(&res); ++ bmc_device->bmc_mem_cpu = dmam_alloc_coherent(dev, bmc_device->bmc_mem_size, ++ &bmc_device->bmc_mem_phy, GFP_KERNEL); ++ if (!bmc_device->bmc_mem_cpu) { ++ dev_err(dev, "Failed to allocate BMC memory.\n"); ++ return -ENOMEM; ++ } + + bmc_device->irq = platform_get_irq(pdev, 0); + if (bmc_device->irq < 0) { + dev_err(&pdev->dev, "platform get of irq[=%d] failed!\n", bmc_device->irq); -+ goto out_unmap; ++ return bmc_device->irq; + } ++ + ret = devm_request_irq(&pdev->dev, bmc_device->irq, aspeed_bmc_dev_isr, 0, + dev_name(&pdev->dev), bmc_device); + if (ret) { + dev_err(dev, "aspeed bmc device Unable to get IRQ"); -+ goto out_unmap; ++ return ret; + } + + ret = aspeed_bmc_device_setup_queue(pdev); + if (ret) { + dev_err(dev, "Cannot setup queue message"); -+ goto out_irq; ++ goto out; + } + + ret = aspeed_bmc_device_setup_memory_mapping(pdev); @@ -56725,12 +63888,7 @@ diff --git a/drivers/soc/aspeed/aspeed-bmc-dev.c b/drivers/soc/aspeed/aspeed-bmc +out_free_queue: + for (i = 0; i < ASPEED_QUEUE_NUM; i++) + sysfs_remove_bin_file(&pdev->dev.kobj, &bmc_device->queue[i].bin); -+out_irq: -+ devm_free_irq(&pdev->dev, bmc_device->irq, bmc_device); -+out_unmap: -+ iounmap(bmc_device->reg_base); -+out_region: -+ devm_kfree(&pdev->dev, bmc_device); ++out: + dev_warn(dev, "aspeed bmc device: driver init failed (ret=%d)!\n", ret); + return ret; +} @@ -56767,7 +63925,7 @@ diff --git a/drivers/soc/aspeed/aspeed-bmc-dev.c b/drivers/soc/aspeed/aspeed-bmc +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/aspeed/aspeed-disp-intf.c b/drivers/soc/aspeed/aspeed-disp-intf.c --- a/drivers/soc/aspeed/aspeed-disp-intf.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-disp-intf.c 2025-12-23 10:16:21.124032669 +0000 ++++ b/drivers/soc/aspeed/aspeed-disp-intf.c 2026-04-08 18:03:48.310705320 +0000 @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (C) ASPEED Technology Inc. @@ -57024,219 +64182,9 @@ diff --git a/drivers/soc/aspeed/aspeed-disp-intf.c b/drivers/soc/aspeed/aspeed-d +MODULE_AUTHOR("Jammy Huang "); +MODULE_DESCRIPTION("ASPEED Display Interface Driver"); +MODULE_LICENSE("GPL"); -diff --git a/drivers/soc/aspeed/aspeed-espi-comm.h b/drivers/soc/aspeed/aspeed-espi-comm.h ---- a/drivers/soc/aspeed/aspeed-espi-comm.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-espi-comm.h 2025-12-23 10:16:21.124032669 +0000 -@@ -0,0 +1,206 @@ -+/* SPDX-License-Identifier: GPL-2.0+ */ -+/* -+ * Copyright 2023 Aspeed Technology Inc. -+ */ -+#ifndef __ASPEED_ESPI_COMM_H__ -+#define __ASPEED_ESPI_COMM_H__ -+ -+#include -+#include -+ -+/* -+ * eSPI cycle type encoding -+ * -+ * Section 5.1 Cycle Types and Packet Format, -+ * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. -+ */ -+#define ESPI_PERIF_MEMRD32 0x00 -+#define ESPI_PERIF_MEMRD64 0x02 -+#define ESPI_PERIF_MEMWR32 0x01 -+#define ESPI_PERIF_MEMWR64 0x03 -+#define ESPI_PERIF_MSG 0x10 -+#define ESPI_PERIF_MSG_D 0x11 -+#define ESPI_PERIF_SUC_CMPLT 0x06 -+#define ESPI_PERIF_SUC_CMPLT_D_MIDDLE 0x09 -+#define ESPI_PERIF_SUC_CMPLT_D_FIRST 0x0b -+#define ESPI_PERIF_SUC_CMPLT_D_LAST 0x0d -+#define ESPI_PERIF_SUC_CMPLT_D_ONLY 0x0f -+#define ESPI_PERIF_UNSUC_CMPLT 0x0c -+#define ESPI_OOB_MSG 0x21 -+#define ESPI_FLASH_READ 0x00 -+#define ESPI_FLASH_WRITE 0x01 -+#define ESPI_FLASH_ERASE 0x02 -+#define ESPI_FLASH_SUC_CMPLT 0x06 -+#define ESPI_FLASH_SUC_CMPLT_D_MIDDLE 0x09 -+#define ESPI_FLASH_SUC_CMPLT_D_FIRST 0x0b -+#define ESPI_FLASH_SUC_CMPLT_D_LAST 0x0d -+#define ESPI_FLASH_SUC_CMPLT_D_ONLY 0x0f -+#define ESPI_FLASH_UNSUC_CMPLT 0x0c -+ -+/* -+ * eSPI packet format structure -+ * -+ * Section 5.1 Cycle Types and Packet Format, -+ * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. -+ */ -+struct espi_comm_hdr { -+ uint8_t cyc; -+ uint8_t len_h : 4; -+ uint8_t tag : 4; -+ uint8_t len_l; -+}; -+ -+struct espi_perif_mem32 { -+ uint8_t cyc; -+ uint8_t len_h : 4; -+ uint8_t tag : 4; -+ uint8_t len_l; -+ uint32_t addr_be; -+ uint8_t data[]; -+} __packed; -+ -+struct espi_perif_mem64 { -+ uint8_t cyc; -+ uint8_t len_h : 4; -+ uint8_t tag : 4; -+ uint8_t len_l; -+ uint32_t addr_be; -+ uint8_t data[]; -+} __packed; -+ -+struct espi_perif_msg { -+ uint8_t cyc; -+ uint8_t len_h : 4; -+ uint8_t tag : 4; -+ uint8_t len_l; -+ uint8_t msg_code; -+ uint8_t msg_byte[4]; -+ uint8_t data[]; -+} __packed; -+ -+struct espi_perif_cmplt { -+ uint8_t cyc; -+ uint8_t len_h : 4; -+ uint8_t tag : 4; -+ uint8_t len_l; -+ uint8_t data[]; -+} __packed; -+ -+struct espi_oob_msg { -+ uint8_t cyc; -+ uint8_t len_h : 4; -+ uint8_t tag : 4; -+ uint8_t len_l; -+ uint8_t data[]; -+}; -+ -+struct espi_flash_rwe { -+ uint8_t cyc; -+ uint8_t len_h : 4; -+ uint8_t tag : 4; -+ uint8_t len_l; -+ uint32_t addr_be; -+ uint8_t data[]; -+} __packed; -+ -+struct espi_flash_cmplt { -+ uint8_t cyc; -+ uint8_t len_h : 4; -+ uint8_t tag : 4; -+ uint8_t len_l; -+ uint8_t data[]; -+} __packed; -+ -+#define ESPI_MAX_PLD_LEN BIT(12) -+ -+/* -+ * Aspeed IOCTL for eSPI raw packet send/receive -+ * -+ * This IOCTL interface works in the eSPI packet in/out paradigm. -+ * -+ * Only the virtual wire IOCTL is a special case which does not send -+ * or receive an eSPI packet. However, to keep a more consisten use from -+ * userspace, we make all of the four channel drivers serve through the -+ * IOCTL interface. -+ * -+ * For the eSPI packet format, refer to -+ * Section 5.1 Cycle Types and Packet Format, -+ * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. -+ * -+ * For the example user apps using these IOCTL, refer to -+ * https://github.com/AspeedTech-BMC/aspeed_app/tree/master/espi_test -+ */ -+#define __ASPEED_ESPI_IOCTL_MAGIC 0xb8 -+ -+/* -+ * we choose the longest header and the max payload size -+ * based on the Intel specification to define the maximum -+ * eSPI packet length -+ */ -+#define ESPI_MAX_PKT_LEN (sizeof(struct espi_perif_msg) + ESPI_MAX_PLD_LEN) -+ -+struct aspeed_espi_ioc { -+ uint32_t pkt_len; -+ uint8_t *pkt; -+}; -+ -+/* -+ * Peripheral Channel (CH0) -+ * - ASPEED_ESPI_PERIF_PC_GET_RX -+ * Receive an eSPI Posted/Completion packet -+ * - ASPEED_ESPI_PERIF_PC_PUT_TX -+ * Transmit an eSPI Posted/Completion packet -+ * - ASPEED_ESPI_PERIF_NP_PUT_TX -+ * Transmit an eSPI Non-Posted packet -+ */ -+#define ASPEED_ESPI_PERIF_PC_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x00, struct aspeed_espi_ioc) -+#define ASPEED_ESPI_PERIF_PC_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x01, struct aspeed_espi_ioc) -+#define ASPEED_ESPI_PERIF_NP_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x02, struct aspeed_espi_ioc) -+/* -+ * Virtual Wire Channel (CH1) -+ * - ASPEED_ESPI_VW_GET_GPIO_VAL -+ * Read the input value of GPIO over the VW channel -+ * - ASPEED_ESPI_VW_PUT_GPIO_VAL -+ * Write the output value of GPIO over the VW channel -+ * - ASPEED_ESPI_VW_GET_GPIO_VAL1 (new feature in AST2700) -+ * Read the input value1 of GPIO over the VW channel -+ * - ASPEED_ESPI_VW_PUT_GPIO_VAL1 (new feature in AST2700) -+ * Write the output value1 of GPIO over the VW channel -+ */ -+#define ASPEED_ESPI_VW_GET_GPIO_VAL _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x10, uint32_t) -+#define ASPEED_ESPI_VW_PUT_GPIO_VAL _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x11, uint32_t) -+#ifdef CONFIG_ARM64 -+#define ASPEED_ESPI_VW_GET_GPIO_VAL1 _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x12, uint32_t) -+#define ASPEED_ESPI_VW_PUT_GPIO_VAL1 _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x13, uint32_t) -+#endif -+/* -+ * Out-of-band Channel (CH2) -+ * - ASPEED_ESPI_OOB_GET_RX -+ * Receive an eSPI OOB packet -+ * - ASPEED_ESPI_OOB_PUT_TX -+ * Transmit an eSPI OOB packet -+ */ -+#define ASPEED_ESPI_OOB_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x20, struct aspeed_espi_ioc) -+#define ASPEED_ESPI_OOB_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x21, struct aspeed_espi_ioc) -+/* -+ * Flash Channel (CH3) -+ * - ASPEED_ESPI_FLASH_GET_RX -+ * Receive an eSPI flash packet -+ * - ASPEED_ESPI_FLASH_PUT_TX -+ * Transmit an eSPI flash packet -+ */ -+#define ASPEED_ESPI_FLASH_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x30, struct aspeed_espi_ioc) -+#define ASPEED_ESPI_FLASH_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x31, struct aspeed_espi_ioc) -+ -+#endif diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspeed-host-bmc-dev.c --- a/drivers/soc/aspeed/aspeed-host-bmc-dev.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-host-bmc-dev.c 2025-12-23 10:16:21.124032669 +0000 ++++ b/drivers/soc/aspeed/aspeed-host-bmc-dev.c 2026-04-08 18:03:48.310705320 +0000 @@ -0,0 +1,1435 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (C) ASPEED Technology Inc. @@ -58652,9880 +65600,9860 @@ diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspee +static struct pci_driver aspeed_host_bmc_dev_driver = { + .name = DRIVER_NAME, + .id_table = aspeed_host_bmc_dev_pci_ids, -+ .probe = aspeed_pci_host_bmc_device_probe, -+ .remove = aspeed_pci_host_bmc_device_remove, -+}; -+ -+static int __init aspeed_host_bmc_device_init(void) -+{ -+ return pci_register_driver(&aspeed_host_bmc_dev_driver); -+} -+ -+static void aspeed_host_bmc_device_exit(void) -+{ -+ /* unregister pci driver */ -+ pci_unregister_driver(&aspeed_host_bmc_dev_driver); -+} -+ -+late_initcall(aspeed_host_bmc_device_init); -+module_exit(aspeed_host_bmc_device_exit); -+ -+MODULE_AUTHOR("Ryan Chen "); -+MODULE_DESCRIPTION("ASPEED Host BMC DEVICE Driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/soc/aspeed/aspeed-lpc-ctrl.c b/drivers/soc/aspeed/aspeed-lpc-ctrl.c ---- a/drivers/soc/aspeed/aspeed-lpc-ctrl.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-lpc-ctrl.c 2025-12-23 10:16:21.124032669 +0000 -@@ -353,7 +353,7 @@ - .of_match_table = aspeed_lpc_ctrl_match, - }, - .probe = aspeed_lpc_ctrl_probe, -- .remove_new = aspeed_lpc_ctrl_remove, -+ .remove = aspeed_lpc_ctrl_remove, - }; - - module_platform_driver(aspeed_lpc_ctrl_driver); -diff --git a/drivers/soc/aspeed/aspeed-lpc-mbox.c b/drivers/soc/aspeed/aspeed-lpc-mbox.c ---- a/drivers/soc/aspeed/aspeed-lpc-mbox.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-lpc-mbox.c 2025-12-23 10:16:21.124032669 +0000 -@@ -0,0 +1,406 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright 2017 IBM Corporation -+ * Copyright 2021 Aspeed Technology Inc. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define DEVICE_NAME "aspeed-mbox" -+ -+#define ASPEED_MBOX_DR(dr, n) (dr + (n * 4)) -+#define ASPEED_MBOX_STR(str, n) (str + (n / 8) * 4) -+#define ASPEED_MBOX_BIE(bie, n) (bie + (n / 8) * 4) -+#define ASPEED_MBOX_HIE(hie, n) (hie + (n / 8) * 4) -+ -+#define ASPEED_MBOX_BCR_RECV BIT(7) -+#define ASPEED_MBOX_BCR_MASK BIT(1) -+#define ASPEED_MBOX_BCR_SEND BIT(0) -+ -+/* ioctl code */ -+#define ASPEED_MBOX_IOCTL 0xA3 -+#define ASPEED_MBOX_IOCTL_GET_SIZE \ -+ _IOR(ASPEED_MBOX_IOCTL, 0, struct aspeed_mbox_ioctl_data) -+ -+struct aspeed_mbox_ioctl_data { -+ unsigned int data; -+}; -+ -+struct aspeed_mbox_model { -+ unsigned int dr_num; -+ -+ /* offsets to the MBOX registers */ -+ unsigned int dr; -+ unsigned int str; -+ unsigned int bcr; -+ unsigned int hcr; -+ unsigned int bie; -+ unsigned int hie; -+}; -+ -+struct aspeed_mbox { -+ struct miscdevice miscdev; -+ struct regmap *map; -+ unsigned int base; -+ wait_queue_head_t queue; -+ struct mutex mutex; -+ const struct aspeed_mbox_model *model; -+}; -+ -+static atomic_t aspeed_mbox_open_count = ATOMIC_INIT(0); -+ -+static u8 aspeed_mbox_inb(struct aspeed_mbox *mbox, int reg) -+{ -+ /* -+ * The mbox registers are actually only one byte but are addressed -+ * four bytes apart. The other three bytes are marked 'reserved', -+ * they *should* be zero but lets not rely on it. -+ * I am going to rely on the fact we can casually read/write to them... -+ */ -+ unsigned int val = 0xff; /* If regmap throws an error return 0xff */ -+ int rc = regmap_read(mbox->map, mbox->base + reg, &val); -+ -+ if (rc) -+ dev_err(mbox->miscdev.parent, "regmap_read() failed with " -+ "%d (reg: 0x%08x)\n", rc, reg); -+ -+ return val & 0xff; -+} -+ -+static void aspeed_mbox_outb(struct aspeed_mbox *mbox, u8 data, int reg) -+{ -+ int rc = regmap_write(mbox->map, mbox->base + reg, data); -+ -+ if (rc) -+ dev_err(mbox->miscdev.parent, "regmap_write() failed with " -+ "%d (data: %u reg: 0x%08x)\n", rc, data, reg); -+} -+ -+static struct aspeed_mbox *file_mbox(struct file *file) -+{ -+ return container_of(file->private_data, struct aspeed_mbox, miscdev); -+} -+ -+static int aspeed_mbox_open(struct inode *inode, struct file *file) -+{ -+ struct aspeed_mbox *mbox = file_mbox(file); -+ const struct aspeed_mbox_model *model = mbox->model; -+ -+ if (atomic_inc_return(&aspeed_mbox_open_count) == 1) { -+ /* -+ * Clear the interrupt status bit if it was left on and unmask -+ * interrupts. -+ * ASPEED_MBOX_BCR_RECV bit is W1C, this also unmasks in 1 step -+ */ -+ aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_RECV, model->bcr); -+ return 0; -+ } -+ -+ atomic_dec(&aspeed_mbox_open_count); -+ return -EBUSY; -+} -+ -+static ssize_t aspeed_mbox_read(struct file *file, char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ struct aspeed_mbox *mbox = file_mbox(file); -+ const struct aspeed_mbox_model *model = mbox->model; -+ char __user *p = buf; -+ ssize_t ret; -+ int i; -+ -+ if (!access_ok(buf, count)) -+ return -EFAULT; -+ -+ if (count + *ppos > model->dr_num) -+ return -EINVAL; -+ -+ if (file->f_flags & O_NONBLOCK) { -+ if (!(aspeed_mbox_inb(mbox, model->bcr) & -+ ASPEED_MBOX_BCR_RECV)) -+ return -EAGAIN; -+ } else if (wait_event_interruptible(mbox->queue, -+ aspeed_mbox_inb(mbox, model->bcr) & -+ ASPEED_MBOX_BCR_RECV)) { -+ return -ERESTARTSYS; -+ } -+ -+ mutex_lock(&mbox->mutex); -+ -+ for (i = *ppos; count > 0 && i < model->dr_num; i++) { -+ uint8_t reg = aspeed_mbox_inb(mbox, ASPEED_MBOX_DR(model->dr, i)); -+ -+ ret = __put_user(reg, p); -+ if (ret) -+ goto out_unlock; -+ -+ p++; -+ count--; -+ } -+ -+ /* ASPEED_MBOX_BCR_RECV bit is write to clear, this also unmasks in 1 step */ -+ aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_RECV, model->bcr); -+ ret = p - buf; -+ -+out_unlock: -+ mutex_unlock(&mbox->mutex); -+ return ret; -+} -+ -+static ssize_t aspeed_mbox_write(struct file *file, const char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ struct aspeed_mbox *mbox = file_mbox(file); -+ const struct aspeed_mbox_model *model = mbox->model; -+ const char __user *p = buf; -+ ssize_t ret; -+ char c; -+ int i; -+ -+ if (!access_ok(buf, count)) -+ return -EFAULT; -+ -+ if (count + *ppos > model->dr_num) -+ return -EINVAL; -+ -+ mutex_lock(&mbox->mutex); -+ -+ for (i = *ppos; count > 0 && i < model->dr_num; i++) { -+ ret = __get_user(c, p); -+ if (ret) -+ goto out_unlock; -+ -+ aspeed_mbox_outb(mbox, c, ASPEED_MBOX_DR(model->dr, i)); -+ p++; -+ count--; -+ } -+ -+ aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_SEND, model->bcr); -+ ret = p - buf; -+ -+out_unlock: -+ mutex_unlock(&mbox->mutex); -+ return ret; -+} -+ -+static __poll_t aspeed_mbox_poll(struct file *file, poll_table *wait) -+{ -+ struct aspeed_mbox *mbox = file_mbox(file); -+ const struct aspeed_mbox_model *model = mbox->model; -+ __poll_t mask = 0; -+ -+ poll_wait(file, &mbox->queue, wait); -+ -+ if (aspeed_mbox_inb(mbox, model->bcr) & ASPEED_MBOX_BCR_RECV) -+ mask |= POLLIN; -+ -+ return mask; -+} -+ -+static int aspeed_mbox_release(struct inode *inode, struct file *file) -+{ -+ atomic_dec(&aspeed_mbox_open_count); -+ return 0; -+} -+ -+static long aspeed_mbox_ioctl(struct file *file, unsigned int cmd, -+ unsigned long param) -+{ -+ long ret = 0; -+ struct aspeed_mbox *mbox = file_mbox(file); -+ const struct aspeed_mbox_model *model = mbox->model; -+ struct aspeed_mbox_ioctl_data data; -+ -+ switch (cmd) { -+ case ASPEED_MBOX_IOCTL_GET_SIZE: -+ data.data = model->dr_num; -+ if (copy_to_user((void __user *)param, &data, sizeof(data))) -+ ret = -EFAULT; -+ break; -+ default: -+ ret = -ENOTTY; -+ break; -+ } -+ -+ return ret; -+} -+ -+static const struct file_operations aspeed_mbox_fops = { -+ .owner = THIS_MODULE, -+ .llseek = no_seek_end_llseek, -+ .read = aspeed_mbox_read, -+ .write = aspeed_mbox_write, -+ .open = aspeed_mbox_open, -+ .release = aspeed_mbox_release, -+ .poll = aspeed_mbox_poll, -+ .unlocked_ioctl = aspeed_mbox_ioctl, -+}; -+ -+static irqreturn_t aspeed_mbox_irq(int irq, void *arg) -+{ -+ struct aspeed_mbox *mbox = arg; -+ const struct aspeed_mbox_model *model = mbox->model; -+ -+ if (!(aspeed_mbox_inb(mbox, model->bcr) & ASPEED_MBOX_BCR_RECV)) -+ return IRQ_NONE; -+ -+ /* -+ * Leave the status bit set so that we know the data is for us, -+ * clear it once it has been read. -+ */ -+ -+ /* Mask it off, we'll clear it when we the data gets read */ -+ aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_MASK, model->bcr); -+ -+ wake_up(&mbox->queue); -+ return IRQ_HANDLED; -+} -+ -+static int aspeed_mbox_config_irq(struct aspeed_mbox *mbox, -+ struct platform_device *pdev) -+{ -+ const struct aspeed_mbox_model *model = mbox->model; -+ struct device *dev = &pdev->dev; -+ int i, rc, irq; -+ -+ irq = irq_of_parse_and_map(dev->of_node, 0); -+ if (!irq) -+ return -ENODEV; -+ -+ rc = devm_request_irq(dev, irq, aspeed_mbox_irq, -+ IRQF_SHARED, DEVICE_NAME, mbox); -+ if (rc < 0) { -+ dev_err(dev, "Unable to request IRQ %d\n", irq); -+ return rc; -+ } -+ -+ /* -+ * Disable all register based interrupts. -+ */ -+ for (i = 0; i < model->dr_num / 8; ++i) -+ aspeed_mbox_outb(mbox, 0x00, ASPEED_MBOX_BIE(model->bie, i)); -+ -+ /* These registers are write one to clear. Clear them. */ -+ for (i = 0; i < model->dr_num / 8; ++i) -+ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STR(model->str, i)); -+ -+ aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_RECV, model->bcr); -+ return 0; -+} -+ -+static int aspeed_mbox_probe(struct platform_device *pdev) -+{ -+ struct aspeed_mbox *mbox; -+ struct device *dev; -+ int rc; -+ -+ dev = &pdev->dev; -+ -+ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); -+ if (!mbox) -+ return -ENOMEM; -+ -+ dev_set_drvdata(&pdev->dev, mbox); -+ -+ rc = of_property_read_u32(dev->of_node, "reg", &mbox->base); -+ if (rc) { -+ dev_err(dev, "Couldn't read reg device tree property\n"); -+ return rc; -+ } -+ -+ mbox->model = of_device_get_match_data(dev); -+ if (IS_ERR(mbox->model)) { -+ dev_err(dev, "Couldn't get model data\n"); -+ return -ENODEV; -+ } -+ -+ mbox->map = syscon_node_to_regmap( -+ pdev->dev.parent->of_node); -+ if (IS_ERR(mbox->map)) { -+ dev_err(dev, "Couldn't get regmap\n"); -+ return -ENODEV; -+ } -+ -+ mutex_init(&mbox->mutex); -+ init_waitqueue_head(&mbox->queue); -+ -+ mbox->miscdev.minor = MISC_DYNAMIC_MINOR; -+ mbox->miscdev.name = DEVICE_NAME; -+ mbox->miscdev.fops = &aspeed_mbox_fops; -+ mbox->miscdev.parent = dev; -+ rc = misc_register(&mbox->miscdev); -+ if (rc) { -+ dev_err(dev, "Unable to register device\n"); -+ return rc; -+ } -+ -+ rc = aspeed_mbox_config_irq(mbox, pdev); -+ if (rc) { -+ dev_err(dev, "Failed to configure IRQ\n"); -+ misc_deregister(&mbox->miscdev); -+ return rc; -+ } -+ -+ return 0; -+} -+ -+static void aspeed_mbox_remove(struct platform_device *pdev) -+{ -+ struct aspeed_mbox *mbox = dev_get_drvdata(&pdev->dev); -+ -+ misc_deregister(&mbox->miscdev); -+} -+ -+static const struct aspeed_mbox_model ast2400_model = { -+ .dr_num = 16, -+ .dr = 0x0, -+ .str = 0x40, -+ .bcr = 0x48, -+ .hcr = 0x4c, -+ .bie = 0x50, -+ .hie = 0x58, -+}; -+ -+static const struct aspeed_mbox_model ast2600_model = { -+ .dr_num = 32, -+ .dr = 0x0, -+ .str = 0x80, -+ .bcr = 0x90, -+ .hcr = 0x94, -+ .bie = 0xa0, -+ .hie = 0xb0, -+}; -+ -+static const struct of_device_id aspeed_mbox_match[] = { -+ { .compatible = "aspeed,ast2400-mbox", -+ .data = &ast2400_model }, -+ { .compatible = "aspeed,ast2500-mbox", -+ .data = &ast2400_model }, -+ { .compatible = "aspeed,ast2600-mbox", -+ .data = &ast2600_model }, -+ { }, ++ .probe = aspeed_pci_host_bmc_device_probe, ++ .remove = aspeed_pci_host_bmc_device_remove, +}; + -+static struct platform_driver aspeed_mbox_driver = { -+ .driver = { -+ .name = DEVICE_NAME, -+ .of_match_table = aspeed_mbox_match, -+ }, -+ .probe = aspeed_mbox_probe, -+ .remove = aspeed_mbox_remove, -+}; ++static int __init aspeed_host_bmc_device_init(void) ++{ ++ return pci_register_driver(&aspeed_host_bmc_dev_driver); ++} + -+module_platform_driver(aspeed_mbox_driver); -+MODULE_DEVICE_TABLE(of, aspeed_mbox_match); ++static void aspeed_host_bmc_device_exit(void) ++{ ++ /* unregister pci driver */ ++ pci_unregister_driver(&aspeed_host_bmc_dev_driver); ++} ++ ++late_initcall(aspeed_host_bmc_device_init); ++module_exit(aspeed_host_bmc_device_exit); ++ ++MODULE_AUTHOR("Ryan Chen "); ++MODULE_DESCRIPTION("ASPEED Host BMC DEVICE Driver"); +MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Cyril Bur "); -+MODULE_AUTHOR("Chia-Wei Wang -+#include +#include -+#include -+#include +#include +#include +#include -+#include ++#include +#include -+#include +#include +#include +#include -+#include -+#include ++#include + -+#define DEVICE_NAME "aspeed-lpc-pcc" ++#define DEVICE_NAME "aspeed-mbox" + -+static DEFINE_IDA(aspeed_pcc_ida); ++#define ASPEED_MBOX_DR(dr, n) (dr + (n * 4)) ++#define ASPEED_MBOX_STR(str, n) (str + (n / 8) * 4) ++#define ASPEED_MBOX_BIE(bie, n) (bie + (n / 8) * 4) ++#define ASPEED_MBOX_HIE(hie, n) (hie + (n / 8) * 4) + -+#define HICR5 0x80 -+#define HICR5_EN_SNP0W BIT(0) -+#define HICR5_EN_SNP1W BIT(2) -+#define HICR6 0x084 -+#define HICR6_EN2BMODE BIT(19) -+#define SNPWADR 0x090 -+#define PCCR6 0x0c4 -+#define PCCR6_DMA_CUR_ADDR GENMASK(27, 0) -+#define PCCR4 0x0d0 -+#define PCCR4_DMA_ADDRL_MASK GENMASK(31, 0) -+#define PCCR4_DMA_ADDRL_SHIFT 0 -+#define PCCR5 0x0d4 -+#define PCCR5_DMA_ADDRH_MASK GENMASK(27, 24) -+#define PCCR5_DMA_ADDRH_SHIFT 24 -+#define PCCR5_DMA_LEN_MASK GENMASK(23, 0) -+#define PCCR5_DMA_LEN_SHIFT 0 -+#define HICRB 0x100 -+#define HICRB_ENSNP0D BIT(14) -+#define HICRB_ENSNP1D BIT(15) -+#define PCCR0 0x130 -+#define PCCR0_EN_DMA_INT BIT(31) -+#define PCCR0_EN_DMA_MODE BIT(14) -+#define PCCR0_ADDR_SEL_MASK GENMASK(13, 12) -+#define PCCR0_ADDR_SEL_SHIFT 12 -+#define PCCR0_RX_TRIG_LVL_MASK GENMASK(10, 8) -+#define PCCR0_RX_TRIG_LVL_SHIFT 8 -+#define PCCR0_CLR_RX_FIFO BIT(7) -+#define PCCR0_MODE_SEL_MASK GENMASK(5, 4) -+#define PCCR0_MODE_SEL_SHIFT 4 -+#define PCCR0_EN_RX_TMOUT_INT BIT(2) -+#define PCCR0_EN_RX_AVAIL_INT BIT(1) -+#define PCCR0_EN BIT(0) -+#define PCCR1 0x134 -+#define PCCR1_BASE_ADDR_MASK GENMASK(15, 0) -+#define PCCR1_BASE_ADDR_SHIFT 0 -+#define PCCR1_DONT_CARE_BITS_MASK GENMASK(21, 16) -+#define PCCR1_DONT_CARE_BITS_SHIFT 16 -+#define PCCR2 0x138 -+#define PCCR2_INT_STATUS_PATTERN_B BIT(16) -+#define PCCR2_INT_STATUS_PATTERN_A BIT(8) -+#define PCCR2_INT_STATUS_DMA_DONE BIT(4) -+#define PCCR2_INT_STATUS_DATA_RDY PCCR2_INT_STATUS_DMA_DONE -+#define PCCR2_INT_STATUS_RX_OVER BIT(3) -+#define PCCR2_INT_STATUS_RX_TMOUT BIT(2) -+#define PCCR2_INT_STATUS_RX_AVAIL BIT(1) -+#define PCCR3 0x13c -+#define PCCR3_FIFO_DATA_MASK GENMASK(7, 0) ++#define ASPEED_MBOX_BCR_RECV BIT(7) ++#define ASPEED_MBOX_BCR_MASK BIT(1) ++#define ASPEED_MBOX_BCR_SEND BIT(0) + -+#define PCC_DMA_BUFSZ (256 * SZ_1K) ++/* ioctl code */ ++#define ASPEED_MBOX_IOCTL 0xA3 ++#define ASPEED_MBOX_IOCTL_GET_SIZE \ ++ _IOR(ASPEED_MBOX_IOCTL, 0, struct aspeed_mbox_ioctl_data) + -+enum pcc_fifo_threshold { -+ PCC_FIFO_THR_1_BYTE, -+ PCC_FIFO_THR_1_EIGHTH, -+ PCC_FIFO_THR_2_EIGHTH, -+ PCC_FIFO_THR_3_EIGHTH, -+ PCC_FIFO_THR_4_EIGHTH, -+ PCC_FIFO_THR_5_EIGHTH, -+ PCC_FIFO_THR_6_EIGHTH, -+ PCC_FIFO_THR_7_EIGHTH, -+ PCC_FIFO_THR_8_EIGHTH, ++struct aspeed_mbox_ioctl_data { ++ unsigned int data; +}; + -+enum pcc_record_mode { -+ PCC_REC_1B, -+ PCC_REC_2B, -+ PCC_REC_4B, -+ PCC_REC_FULL, -+}; ++struct aspeed_mbox_model { ++ unsigned int dr_num; + -+enum pcc_port_hbits_select { -+ PCC_PORT_HBITS_SEL_NONE, -+ PCC_PORT_HBITS_SEL_45, -+ PCC_PORT_HBITS_SEL_67, -+ PCC_PORT_HBITS_SEL_89, ++ /* offsets to the MBOX registers */ ++ unsigned int dr; ++ unsigned int str; ++ unsigned int bcr; ++ unsigned int hcr; ++ unsigned int bie; ++ unsigned int hie; +}; + -+struct aspeed_pcc_dma { -+ uint32_t rptr; -+ uint8_t *virt; -+ dma_addr_t addr; -+ uint32_t size; ++struct aspeed_mbox { ++ struct miscdevice miscdev; ++ struct regmap *map; ++ unsigned int base; ++ wait_queue_head_t queue; ++ struct mutex mutex; ++ const struct aspeed_mbox_model *model; +}; + -+struct aspeed_pcc_ctrl { -+ struct device *dev; -+ struct regmap *regmap; -+ int irq; -+ uint32_t port; -+ struct aspeed_pcc_dma dma; -+ struct kfifo fifo; -+ wait_queue_head_t wq; -+ struct miscdevice mdev; -+ int mdev_id; -+}; ++static atomic_t aspeed_mbox_open_count = ATOMIC_INIT(0); + -+static inline bool is_valid_rec_mode(uint32_t mode) ++static u8 aspeed_mbox_inb(struct aspeed_mbox *mbox, int reg) +{ -+ return (mode > PCC_REC_FULL) ? false : true; -+} ++ /* ++ * The mbox registers are actually only one byte but are addressed ++ * four bytes apart. The other three bytes are marked 'reserved', ++ * they *should* be zero but lets not rely on it. ++ * I am going to rely on the fact we can casually read/write to them... ++ */ ++ unsigned int val = 0xff; /* If regmap throws an error return 0xff */ ++ int rc = regmap_read(mbox->map, mbox->base + reg, &val); + -+static inline bool is_valid_high_bits_select(uint32_t sel) -+{ -+ return (sel > PCC_PORT_HBITS_SEL_89) ? false : true; ++ if (rc) ++ dev_err(mbox->miscdev.parent, "regmap_read() failed with " ++ "%d (reg: 0x%08x)\n", rc, reg); ++ ++ return val & 0xff; +} + -+static ssize_t aspeed_pcc_file_read(struct file *file, char __user *buffer, -+ size_t count, loff_t *ppos) ++static void aspeed_mbox_outb(struct aspeed_mbox *mbox, u8 data, int reg) +{ -+ int rc; -+ unsigned int copied; -+ struct aspeed_pcc_ctrl *pcc = container_of(file->private_data, -+ struct aspeed_pcc_ctrl, -+ mdev); -+ -+ if (kfifo_is_empty(&pcc->fifo)) { -+ if (file->f_flags & O_NONBLOCK) -+ return -EAGAIN; -+ -+ rc = wait_event_interruptible(pcc->wq, -+ !kfifo_is_empty(&pcc->fifo)); -+ if (rc == -ERESTARTSYS) -+ return -EINTR; -+ } ++ int rc = regmap_write(mbox->map, mbox->base + reg, data); + -+ rc = kfifo_to_user(&pcc->fifo, buffer, count, &copied); ++ if (rc) ++ dev_err(mbox->miscdev.parent, "regmap_write() failed with " ++ "%d (data: %u reg: 0x%08x)\n", rc, data, reg); ++} + -+ return rc ? rc : copied; ++static struct aspeed_mbox *file_mbox(struct file *file) ++{ ++ return container_of(file->private_data, struct aspeed_mbox, miscdev); +} + -+static __poll_t aspeed_pcc_file_poll(struct file *file, -+ struct poll_table_struct *pt) ++static int aspeed_mbox_open(struct inode *inode, struct file *file) +{ -+ struct aspeed_pcc_ctrl *pcc = container_of(file->private_data, -+ struct aspeed_pcc_ctrl, -+ mdev); ++ struct aspeed_mbox *mbox = file_mbox(file); ++ const struct aspeed_mbox_model *model = mbox->model; + -+ poll_wait(file, &pcc->wq, pt); ++ if (atomic_inc_return(&aspeed_mbox_open_count) == 1) { ++ /* ++ * Clear the interrupt status bit if it was left on and unmask ++ * interrupts. ++ * ASPEED_MBOX_BCR_RECV bit is W1C, this also unmasks in 1 step ++ */ ++ aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_RECV, model->bcr); ++ return 0; ++ } + -+ return !kfifo_is_empty(&pcc->fifo) ? POLLIN : 0; ++ atomic_dec(&aspeed_mbox_open_count); ++ return -EBUSY; +} + -+static const struct file_operations pcc_fops = { -+ .owner = THIS_MODULE, -+ .read = aspeed_pcc_file_read, -+ .poll = aspeed_pcc_file_poll, -+}; -+ -+static irqreturn_t aspeed_pcc_dma_isr(int irq, void *arg) ++static ssize_t aspeed_mbox_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) +{ -+ uint32_t reg, rptr, wptr; -+ struct aspeed_pcc_ctrl *pcc = (struct aspeed_pcc_ctrl *)arg; -+ struct kfifo *fifo = &pcc->fifo; ++ struct aspeed_mbox *mbox = file_mbox(file); ++ const struct aspeed_mbox_model *model = mbox->model; ++ char __user *p = buf; ++ ssize_t ret; ++ int i; + -+ regmap_write_bits(pcc->regmap, PCCR2, PCCR2_INT_STATUS_DMA_DONE, PCCR2_INT_STATUS_DMA_DONE); ++ if (!access_ok(buf, count)) ++ return -EFAULT; + -+ regmap_read(pcc->regmap, PCCR6, ®); -+ wptr = (reg & PCCR6_DMA_CUR_ADDR) - (pcc->dma.addr & PCCR6_DMA_CUR_ADDR); -+ rptr = pcc->dma.rptr; ++ if (count + *ppos > model->dr_num) ++ return -EINVAL; + -+ do { -+ if (kfifo_is_full(fifo)) -+ kfifo_skip(fifo); ++ if (file->f_flags & O_NONBLOCK) { ++ if (!(aspeed_mbox_inb(mbox, model->bcr) & ++ ASPEED_MBOX_BCR_RECV)) ++ return -EAGAIN; ++ } else if (wait_event_interruptible(mbox->queue, ++ aspeed_mbox_inb(mbox, model->bcr) & ++ ASPEED_MBOX_BCR_RECV)) { ++ return -ERESTARTSYS; ++ } ++ ++ mutex_lock(&mbox->mutex); + -+ kfifo_put(fifo, pcc->dma.virt[rptr]); ++ for (i = *ppos; count > 0 && i < model->dr_num; i++) { ++ uint8_t reg = aspeed_mbox_inb(mbox, ASPEED_MBOX_DR(model->dr, i)); + -+ rptr = (rptr + 1) % pcc->dma.size; -+ } while (rptr != wptr); ++ ret = __put_user(reg, p); ++ if (ret) ++ goto out_unlock; + -+ pcc->dma.rptr = rptr; ++ p++; ++ count--; ++ } + -+ wake_up_interruptible(&pcc->wq); ++ /* ASPEED_MBOX_BCR_RECV bit is write to clear, this also unmasks in 1 step */ ++ aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_RECV, model->bcr); ++ ret = p - buf; + -+ return IRQ_HANDLED; ++out_unlock: ++ mutex_unlock(&mbox->mutex); ++ return ret; +} + -+static irqreturn_t aspeed_pcc_isr(int irq, void *arg) ++static ssize_t aspeed_mbox_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) +{ -+ uint32_t sts; -+ struct aspeed_pcc_ctrl *pcc = (struct aspeed_pcc_ctrl *)arg; ++ struct aspeed_mbox *mbox = file_mbox(file); ++ const struct aspeed_mbox_model *model = mbox->model; ++ const char __user *p = buf; ++ ssize_t ret; ++ char c; ++ int i; + -+ regmap_read(pcc->regmap, PCCR2, &sts); ++ if (!access_ok(buf, count)) ++ return -EFAULT; + -+ if (!(sts & (PCCR2_INT_STATUS_RX_TMOUT | -+ PCCR2_INT_STATUS_RX_AVAIL | -+ PCCR2_INT_STATUS_DMA_DONE))) -+ return IRQ_NONE; ++ if (count + *ppos > model->dr_num) ++ return -EINVAL; + -+ return aspeed_pcc_dma_isr(irq, arg); -+} ++ mutex_lock(&mbox->mutex); + -+/* -+ * A2600-15 AP note -+ * -+ * SW workaround to prevent generating Non-Fatal-Error (NFE) -+ * eSPI response when PCC is used for port I/O byte snooping -+ * over eSPI. -+ */ -+static int aspeed_a2600_15(struct aspeed_pcc_ctrl *pcc, struct device *dev) -+{ -+ u32 hicr5_en, hicrb_en; ++ for (i = *ppos; count > 0 && i < model->dr_num; i++) { ++ ret = __get_user(c, p); ++ if (ret) ++ goto out_unlock; + -+ /* abort if snoop is enabled */ -+ regmap_read(pcc->regmap, HICR5, &hicr5_en); -+ if (hicr5_en & (HICR5_EN_SNP0W | HICR5_EN_SNP1W)) { -+ dev_err(dev, "A2600-15 should be applied with snoop disabled\n"); -+ return -EPERM; ++ aspeed_mbox_outb(mbox, c, ASPEED_MBOX_DR(model->dr, i)); ++ p++; ++ count--; + } + -+ /* set SNPWADR of snoop device */ -+ regmap_write(pcc->regmap, SNPWADR, pcc->port | ((pcc->port + 2) << 16)); -+ -+ /* set HICRB[15:14]=11b to enable ACCEPT response for SNPWADR */ -+ hicrb_en = HICRB_ENSNP0D | HICRB_ENSNP1D; -+ regmap_update_bits(pcc->regmap, HICRB, hicrb_en, hicrb_en); -+ -+ /* set HICR6[19] to extend SNPWADR to 2x range */ -+ regmap_update_bits(pcc->regmap, HICR6, HICR6_EN2BMODE, HICR6_EN2BMODE); ++ aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_SEND, model->bcr); ++ ret = p - buf; + -+ return 0; ++out_unlock: ++ mutex_unlock(&mbox->mutex); ++ return ret; +} + -+static int aspeed_pcc_enable(struct aspeed_pcc_ctrl *pcc, struct device *dev) ++static __poll_t aspeed_mbox_poll(struct file *file, poll_table *wait) +{ -+ int rc; -+ -+ rc = aspeed_a2600_15(pcc, dev); -+ if (rc) -+ return rc; -+ -+ /* record mode: Set 2-Byte mode. */ -+ regmap_update_bits(pcc->regmap, PCCR0, -+ PCCR0_MODE_SEL_MASK, -+ PCC_REC_2B << PCCR0_MODE_SEL_SHIFT); -+ -+ /* port address */ -+ regmap_update_bits(pcc->regmap, PCCR1, -+ PCCR1_BASE_ADDR_MASK, -+ pcc->port << PCCR1_BASE_ADDR_SHIFT); -+ -+ /* Set address high bits selection to 0b01 for address bit[5:4] */ -+ regmap_update_bits(pcc->regmap, PCCR0, -+ PCCR0_ADDR_SEL_MASK, -+ PCC_PORT_HBITS_SEL_45 << PCCR0_ADDR_SEL_SHIFT); -+ -+ /* Set LPC don't care address to 0x3 for port 80~83h */ -+ regmap_update_bits(pcc->regmap, PCCR1, -+ PCCR1_DONT_CARE_BITS_MASK, -+ 0x3 << PCCR1_DONT_CARE_BITS_SHIFT); ++ struct aspeed_mbox *mbox = file_mbox(file); ++ const struct aspeed_mbox_model *model = mbox->model; ++ __poll_t mask = 0; + -+ /* set DMA ring buffer size and enable interrupts */ -+ regmap_write(pcc->regmap, PCCR4, pcc->dma.addr & 0xffffffff); -+#ifdef CONFIG_ARM64 -+ regmap_update_bits(pcc->regmap, PCCR5, PCCR5_DMA_ADDRH_MASK, -+ (pcc->dma.addr >> 32) << PCCR5_DMA_ADDRH_SHIFT); -+#endif -+ regmap_update_bits(pcc->regmap, PCCR5, PCCR5_DMA_LEN_MASK, -+ (pcc->dma.size / 4) << PCCR5_DMA_LEN_SHIFT); -+ regmap_update_bits(pcc->regmap, PCCR0, -+ PCCR0_EN_DMA_INT | PCCR0_EN_DMA_MODE, -+ PCCR0_EN_DMA_INT | PCCR0_EN_DMA_MODE); ++ poll_wait(file, &mbox->queue, wait); + -+ regmap_update_bits(pcc->regmap, PCCR0, PCCR0_EN, PCCR0_EN); ++ if (aspeed_mbox_inb(mbox, model->bcr) & ASPEED_MBOX_BCR_RECV) ++ mask |= POLLIN; + -+ return 0; ++ return mask; +} + -+static int aspeed_pcc_disable(struct aspeed_pcc_ctrl *pcc) ++static int aspeed_mbox_release(struct inode *inode, struct file *file) +{ -+ /* Disable PCC and DMA Mode for safety */ -+ regmap_update_bits(pcc->regmap, PCCR0, PCCR0_EN | PCCR0_EN_DMA_MODE, 0); -+ -+ /* Clear Rx FIFO. */ -+ regmap_update_bits(pcc->regmap, PCCR0, PCCR0_CLR_RX_FIFO, 1); -+ -+ /* Clear All interrupts status. */ -+ regmap_write(pcc->regmap, PCCR2, -+ PCCR2_INT_STATUS_RX_OVER | PCCR2_INT_STATUS_DMA_DONE | -+ PCCR2_INT_STATUS_PATTERN_A | PCCR2_INT_STATUS_PATTERN_B); -+ ++ atomic_dec(&aspeed_mbox_open_count); + return 0; +} + -+static int aspeed_pcc_probe(struct platform_device *pdev) ++static long aspeed_mbox_ioctl(struct file *file, unsigned int cmd, ++ unsigned long param) +{ -+ int rc; -+ struct aspeed_pcc_ctrl *pcc; -+ struct device *dev = &pdev->dev; -+ uint32_t fifo_size = PAGE_SIZE; -+ -+ pcc = devm_kzalloc(dev, sizeof(*pcc), GFP_KERNEL); -+ if (!pcc) -+ return -ENOMEM; -+ -+ pcc->regmap = syscon_node_to_regmap(dev->parent->of_node); -+ if (IS_ERR(pcc->regmap)) -+ return dev_err_probe(dev, PTR_ERR(pcc->regmap), "Couldn't get regmap\n"); -+ -+ rc = of_property_read_u32(dev->of_node, "pcc-ports", &pcc->port); -+ if (rc) { -+ dev_err(dev, "no pcc ports configured\n"); -+ return rc; -+ } -+ -+ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); -+ if (rc) { -+ dev_err(dev, "cannot set 64-bits DMA mask\n"); -+ return rc; -+ } -+ -+ pcc->dma.size = PCC_DMA_BUFSZ; -+ pcc->dma.virt = dmam_alloc_coherent(dev, -+ pcc->dma.size, -+ &pcc->dma.addr, -+ GFP_KERNEL); -+ if (!pcc->dma.virt) { -+ dev_err(dev, "cannot allocate DMA buffer\n"); -+ return -ENOMEM; -+ } -+ -+ fifo_size = roundup(pcc->dma.size, PAGE_SIZE); -+ rc = kfifo_alloc(&pcc->fifo, fifo_size, GFP_KERNEL); -+ if (rc) -+ return rc; -+ -+ /* Disable PCC to clean up DMA buffer before request IRQ. */ -+ rc = aspeed_pcc_disable(pcc); -+ if (rc) { -+ dev_err(dev, "Couldn't disable PCC\n"); -+ goto err_free_kfifo; -+ } -+ -+ pcc->irq = platform_get_irq(pdev, 0); -+ if (pcc->irq < 0) { -+ rc = pcc->irq; -+ goto err_free_kfifo; -+ } -+ -+ rc = devm_request_irq(dev, pcc->irq, aspeed_pcc_isr, 0, DEVICE_NAME, pcc); -+ if (rc < 0) { -+ dev_err(dev, "Couldn't request IRQ %d\n", pcc->irq); -+ goto err_free_kfifo; -+ } -+ -+ init_waitqueue_head(&pcc->wq); -+ -+ pcc->mdev_id = ida_alloc(&aspeed_pcc_ida, GFP_KERNEL); -+ if (pcc->mdev_id < 0) { -+ dev_err(dev, "Couldn't allocate ID\n"); -+ goto err_free_kfifo; -+ } ++ long ret = 0; ++ struct aspeed_mbox *mbox = file_mbox(file); ++ const struct aspeed_mbox_model *model = mbox->model; ++ struct aspeed_mbox_ioctl_data data; + -+ pcc->mdev.parent = dev; -+ pcc->mdev.minor = MISC_DYNAMIC_MINOR; -+ pcc->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, -+ pcc->mdev_id); -+ pcc->mdev.fops = &pcc_fops; -+ rc = misc_register(&pcc->mdev); -+ if (rc) { -+ dev_err(dev, "Couldn't register misc device\n"); -+ goto err_free_ida; ++ switch (cmd) { ++ case ASPEED_MBOX_IOCTL_GET_SIZE: ++ data.data = model->dr_num; ++ if (copy_to_user((void __user *)param, &data, sizeof(data))) ++ ret = -EFAULT; ++ break; ++ default: ++ ret = -ENOTTY; ++ break; + } + -+ rc = aspeed_pcc_enable(pcc, dev); -+ if (rc) { -+ dev_err(dev, "Couldn't enable PCC\n"); -+ goto err_dereg_mdev; -+ } ++ return ret; ++} + -+ dev_set_drvdata(dev, pcc); ++static const struct file_operations aspeed_mbox_fops = { ++ .owner = THIS_MODULE, ++ .llseek = no_seek_end_llseek, ++ .read = aspeed_mbox_read, ++ .write = aspeed_mbox_write, ++ .open = aspeed_mbox_open, ++ .release = aspeed_mbox_release, ++ .poll = aspeed_mbox_poll, ++ .unlocked_ioctl = aspeed_mbox_ioctl, ++}; + -+ return 0; ++static irqreturn_t aspeed_mbox_irq(int irq, void *arg) ++{ ++ struct aspeed_mbox *mbox = arg; ++ const struct aspeed_mbox_model *model = mbox->model; + -+err_dereg_mdev: -+ misc_deregister(&pcc->mdev); ++ if (!(aspeed_mbox_inb(mbox, model->bcr) & ASPEED_MBOX_BCR_RECV)) ++ return IRQ_NONE; + -+err_free_ida: -+ ida_free(&aspeed_pcc_ida, pcc->mdev_id); ++ /* ++ * Leave the status bit set so that we know the data is for us, ++ * clear it once it has been read. ++ */ + -+err_free_kfifo: -+ kfifo_free(&pcc->fifo); ++ /* Mask it off, we'll clear it when we the data gets read */ ++ aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_MASK, model->bcr); + -+ return rc; ++ wake_up(&mbox->queue); ++ return IRQ_HANDLED; +} + -+static void aspeed_pcc_remove(struct platform_device *pdev) ++static int aspeed_mbox_config_irq(struct aspeed_mbox *mbox, ++ struct platform_device *pdev) +{ ++ const struct aspeed_mbox_model *model = mbox->model; + struct device *dev = &pdev->dev; -+ struct aspeed_pcc_ctrl *pcc = dev_get_drvdata(dev); ++ int i, rc, irq; + -+ kfifo_free(&pcc->fifo); -+ ida_free(&aspeed_pcc_ida, pcc->mdev_id); -+ misc_deregister(&pcc->mdev); -+} ++ irq = irq_of_parse_and_map(dev->of_node, 0); ++ if (!irq) ++ return -ENODEV; + -+static const struct of_device_id aspeed_pcc_table[] = { -+ { .compatible = "aspeed,ast2600-lpc-pcc" }, -+ { }, -+}; ++ rc = devm_request_irq(dev, irq, aspeed_mbox_irq, ++ IRQF_SHARED, DEVICE_NAME, mbox); ++ if (rc < 0) { ++ dev_err(dev, "Unable to request IRQ %d\n", irq); ++ return rc; ++ } + -+static struct platform_driver aspeed_pcc_driver = { -+ .driver = { -+ .name = "aspeed-pcc", -+ .of_match_table = aspeed_pcc_table, -+ }, -+ .probe = aspeed_pcc_probe, -+ .remove = aspeed_pcc_remove, -+}; ++ /* ++ * Disable all register based interrupts. ++ */ ++ for (i = 0; i < model->dr_num / 8; ++i) ++ aspeed_mbox_outb(mbox, 0x00, ASPEED_MBOX_BIE(model->bie, i)); + -+module_platform_driver(aspeed_pcc_driver); ++ /* These registers are write one to clear. Clear them. */ ++ for (i = 0; i < model->dr_num / 8; ++i) ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STR(model->str, i)); + -+MODULE_AUTHOR("Chia-Wei Wang "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Driver for Aspeed Post Code Capture"); -diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c ---- a/drivers/soc/aspeed/aspeed-lpc-snoop.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c 2025-12-23 10:16:21.124032669 +0000 -@@ -11,7 +11,7 @@ - */ - - #include --#include -+#include - #include - #include - #include -@@ -25,7 +25,6 @@ - - #define DEVICE_NAME "aspeed-lpc-snoop" - --#define NUM_SNOOP_CHANNELS 2 - #define SNOOP_FIFO_SIZE 2048 - - #define HICR5 0x80 -@@ -36,6 +35,7 @@ - #define HICR6 0x84 - #define HICR6_STR_SNP0W BIT(0) - #define HICR6_STR_SNP1W BIT(1) -+#define HICR6_NFE_WA BIT(20) - #define SNPWADR 0x90 - #define SNPWADR_CH0_MASK GENMASK(15, 0) - #define SNPWADR_CH0_SHIFT 0 -@@ -57,7 +57,22 @@ - unsigned int has_hicrb_ensnp; - }; - -+enum aspeed_lpc_snoop_index { -+ ASPEED_LPC_SNOOP_INDEX_0 = 0, -+ ASPEED_LPC_SNOOP_INDEX_1 = 1, -+ ASPEED_LPC_SNOOP_INDEX_MAX = ASPEED_LPC_SNOOP_INDEX_1, -+}; ++ aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_RECV, model->bcr); ++ return 0; ++} + -+struct aspeed_lpc_snoop_channel_cfg { -+ enum aspeed_lpc_snoop_index index; -+ u32 hicr5_en; -+ u32 snpwadr_mask; -+ u32 snpwadr_shift; -+ u32 hicrb_en; -+}; ++static int aspeed_mbox_probe(struct platform_device *pdev) ++{ ++ struct aspeed_mbox *mbox; ++ struct device *dev; ++ int rc; + - struct aspeed_lpc_snoop_channel { -+ const struct aspeed_lpc_snoop_channel_cfg *cfg; - bool enabled; - struct kfifo fifo; - wait_queue_head_t wq; -@@ -67,10 +82,28 @@ - struct aspeed_lpc_snoop { - struct regmap *regmap; - int irq; -- struct clk *clk; -- struct aspeed_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS]; -+ struct aspeed_lpc_snoop_channel chan[ASPEED_LPC_SNOOP_INDEX_MAX + 1]; - }; - -+static const struct aspeed_lpc_snoop_channel_cfg channel_cfgs[ASPEED_LPC_SNOOP_INDEX_MAX + 1] = { -+ { -+ .index = ASPEED_LPC_SNOOP_INDEX_0, -+ .hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W, -+ .snpwadr_mask = SNPWADR_CH0_MASK, -+ .snpwadr_shift = SNPWADR_CH0_SHIFT, -+ .hicrb_en = HICRB_ENSNP0D, -+ }, -+ { -+ .index = ASPEED_LPC_SNOOP_INDEX_1, -+ .hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W, -+ .snpwadr_mask = SNPWADR_CH1_MASK, -+ .snpwadr_shift = SNPWADR_CH1_SHIFT, -+ .hicrb_en = HICRB_ENSNP1D, -+ }, -+}; ++ dev = &pdev->dev; + -+static DEFINE_IDA(aspeed_lpc_snoop_ida); ++ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); ++ if (!mbox) ++ return -ENOMEM; + - static struct aspeed_lpc_snoop_channel *snoop_file_to_chan(struct file *file) - { - return container_of(file->private_data, -@@ -136,12 +169,19 @@ - return IRQ_NONE; - - /* Check if one of the snoop channels is interrupting */ -- reg &= (HICR6_STR_SNP0W | HICR6_STR_SNP1W); -- if (!reg) -+ if (!(reg & (HICR6_STR_SNP0W | HICR6_STR_SNP1W))) - return IRQ_NONE; - -- /* Ack pending IRQs */ -- regmap_write(lpc_snoop->regmap, HICR6, reg); -+ /* Check if NFE WA is set */ -+ if (reg & HICR6_NFE_WA) { -+ /* Ack pending IRQs with keeping NFE WA */ -+ regmap_write(lpc_snoop->regmap, HICR6, -+ (HICR6_STR_SNP0W | HICR6_STR_SNP1W | HICR6_NFE_WA)); -+ } else { -+ /* Ack pending IRQs */ -+ regmap_write(lpc_snoop->regmap, HICR6, -+ (HICR6_STR_SNP0W | HICR6_STR_SNP1W)); ++ dev_set_drvdata(&pdev->dev, mbox); ++ ++ rc = of_property_read_u32(dev->of_node, "reg", &mbox->base); ++ if (rc) { ++ dev_err(dev, "Couldn't read reg device tree property\n"); ++ return rc; + } - - /* Read and save most recent snoop'ed data byte to FIFO */ - regmap_read(lpc_snoop->regmap, SNPWDR, &data); -@@ -182,108 +222,92 @@ - return 0; - } - --static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop, -- struct device *dev, -- int channel, u16 lpc_port) -+__attribute__((nonnull)) -+static int aspeed_lpc_enable_snoop(struct device *dev, -+ struct aspeed_lpc_snoop *lpc_snoop, -+ struct aspeed_lpc_snoop_channel *channel, -+ const struct aspeed_lpc_snoop_channel_cfg *cfg, -+ u16 lpc_port) - { -- int rc = 0; -- u32 hicr5_en, snpwadr_mask, snpwadr_shift, hicrb_en; -- const struct aspeed_lpc_snoop_model_data *model_data = -- of_device_get_match_data(dev); -+ const struct aspeed_lpc_snoop_model_data *model_data; -+ int rc = 0, id; - -- if (WARN_ON(lpc_snoop->chan[channel].enabled)) -+ if (WARN_ON(channel->enabled)) - return -EBUSY; - -- init_waitqueue_head(&lpc_snoop->chan[channel].wq); -- /* Create FIFO datastructure */ -- rc = kfifo_alloc(&lpc_snoop->chan[channel].fifo, -- SNOOP_FIFO_SIZE, GFP_KERNEL); -+ init_waitqueue_head(&channel->wq); + -+ channel->cfg = cfg; -+ channel->miscdev.minor = MISC_DYNAMIC_MINOR; -+ channel->miscdev.fops = &snoop_fops; -+ channel->miscdev.parent = dev; ++ mbox->model = of_device_get_match_data(dev); ++ if (IS_ERR(mbox->model)) { ++ dev_err(dev, "Couldn't get model data\n"); ++ return -ENODEV; ++ } + -+ id = ida_alloc(&aspeed_lpc_snoop_ida, GFP_KERNEL); -+ if (id < 0) -+ return id; ++ mbox->map = syscon_node_to_regmap( ++ pdev->dev.parent->of_node); ++ if (IS_ERR(mbox->map)) { ++ dev_err(dev, "Couldn't get regmap\n"); ++ return -ENODEV; ++ } + -+ channel->miscdev.name = -+ devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, id); -+ if (!channel->miscdev.name) -+ return -ENOMEM; ++ mutex_init(&mbox->mutex); ++ init_waitqueue_head(&mbox->queue); + -+ rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL); - if (rc) - return rc; - -- lpc_snoop->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR; -- lpc_snoop->chan[channel].miscdev.name = -- devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel); -- if (!lpc_snoop->chan[channel].miscdev.name) { -- rc = -ENOMEM; -- goto err_free_fifo; -- } -- lpc_snoop->chan[channel].miscdev.fops = &snoop_fops; -- lpc_snoop->chan[channel].miscdev.parent = dev; -- rc = misc_register(&lpc_snoop->chan[channel].miscdev); -+ rc = misc_register(&channel->miscdev); - if (rc) - goto err_free_fifo; - - /* Enable LPC snoop channel at requested port */ -- switch (channel) { -- case 0: -- hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W; -- snpwadr_mask = SNPWADR_CH0_MASK; -- snpwadr_shift = SNPWADR_CH0_SHIFT; -- hicrb_en = HICRB_ENSNP0D; -- break; -- case 1: -- hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W; -- snpwadr_mask = SNPWADR_CH1_MASK; -- snpwadr_shift = SNPWADR_CH1_SHIFT; -- hicrb_en = HICRB_ENSNP1D; -- break; -- default: -- rc = -EINVAL; -- goto err_misc_deregister; -- } -- -- regmap_update_bits(lpc_snoop->regmap, HICR5, hicr5_en, hicr5_en); -- regmap_update_bits(lpc_snoop->regmap, SNPWADR, snpwadr_mask, -- lpc_port << snpwadr_shift); -- if (model_data->has_hicrb_ensnp) -- regmap_update_bits(lpc_snoop->regmap, HICRB, -- hicrb_en, hicrb_en); -+ regmap_set_bits(lpc_snoop->regmap, HICR5, cfg->hicr5_en); -+ regmap_update_bits(lpc_snoop->regmap, SNPWADR, cfg->snpwadr_mask, -+ lpc_port << cfg->snpwadr_shift); ++ mbox->miscdev.minor = MISC_DYNAMIC_MINOR; ++ mbox->miscdev.name = DEVICE_NAME; ++ mbox->miscdev.fops = &aspeed_mbox_fops; ++ mbox->miscdev.parent = dev; ++ rc = misc_register(&mbox->miscdev); ++ if (rc) { ++ dev_err(dev, "Unable to register device\n"); ++ return rc; ++ } + -+ model_data = of_device_get_match_data(dev); -+ if (model_data && model_data->has_hicrb_ensnp) -+ regmap_set_bits(lpc_snoop->regmap, HICRB, cfg->hicrb_en); - -- lpc_snoop->chan[channel].enabled = true; -+ channel->enabled = true; - - return 0; - --err_misc_deregister: -- misc_deregister(&lpc_snoop->chan[channel].miscdev); - err_free_fifo: -- kfifo_free(&lpc_snoop->chan[channel].fifo); -+ kfifo_free(&channel->fifo); - return rc; - } - -+__attribute__((nonnull)) - static void aspeed_lpc_disable_snoop(struct aspeed_lpc_snoop *lpc_snoop, -- int channel) -+ struct aspeed_lpc_snoop_channel *channel) - { -- if (!lpc_snoop->chan[channel].enabled) -+ if (!channel->enabled) - return; - -- switch (channel) { -- case 0: -- regmap_update_bits(lpc_snoop->regmap, HICR5, -- HICR5_EN_SNP0W | HICR5_ENINT_SNP0W, -- 0); -- break; -- case 1: -- regmap_update_bits(lpc_snoop->regmap, HICR5, -- HICR5_EN_SNP1W | HICR5_ENINT_SNP1W, -- 0); -- break; -- default: -- return; -- } -+ /* Disable interrupts along with the device */ -+ regmap_clear_bits(lpc_snoop->regmap, HICR5, channel->cfg->hicr5_en); - -- lpc_snoop->chan[channel].enabled = false; -+ channel->enabled = false; - /* Consider improving safety wrt concurrent reader(s) */ -- misc_deregister(&lpc_snoop->chan[channel].miscdev); -- kfifo_free(&lpc_snoop->chan[channel].fifo); -+ misc_deregister(&channel->miscdev); -+ kfifo_free(&channel->fifo); ++ rc = aspeed_mbox_config_irq(mbox, pdev); ++ if (rc) { ++ dev_err(dev, "Failed to configure IRQ\n"); ++ misc_deregister(&mbox->miscdev); ++ return rc; ++ } ++ ++ return 0; +} + -+static void aspeed_lpc_snoop_remove(struct platform_device *pdev) ++static void aspeed_mbox_remove(struct platform_device *pdev) +{ -+ struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev); ++ struct aspeed_mbox *mbox = dev_get_drvdata(&pdev->dev); + -+ /* Disable both snoop channels */ -+ aspeed_lpc_disable_snoop(lpc_snoop, &lpc_snoop->chan[0]); -+ aspeed_lpc_disable_snoop(lpc_snoop, &lpc_snoop->chan[1]); - } - - static int aspeed_lpc_snoop_probe(struct platform_device *pdev) - { - struct aspeed_lpc_snoop *lpc_snoop; -- struct device *dev; - struct device_node *np; -- u32 port; -+ struct device *dev; -+ int idx; - int rc; - - dev = &pdev->dev; -@@ -293,77 +317,40 @@ - return -ENOMEM; - - np = pdev->dev.parent->of_node; -- if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") && -- !of_device_is_compatible(np, "aspeed,ast2500-lpc-v2") && -- !of_device_is_compatible(np, "aspeed,ast2600-lpc-v2")) { -- dev_err(dev, "unsupported LPC device binding\n"); -- return -ENODEV; -- } - - lpc_snoop->regmap = syscon_node_to_regmap(np); -- if (IS_ERR(lpc_snoop->regmap)) { -- dev_err(dev, "Couldn't get regmap\n"); -- return -ENODEV; -- } -+ if (IS_ERR(lpc_snoop->regmap)) -+ return dev_err_probe(dev, PTR_ERR(lpc_snoop->regmap), "Couldn't get regmap\n"); - - dev_set_drvdata(&pdev->dev, lpc_snoop); - -- rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port); -- if (rc) { -- dev_err(dev, "no snoop ports configured\n"); -- return -ENODEV; -- } -- -- lpc_snoop->clk = devm_clk_get(dev, NULL); -- if (IS_ERR(lpc_snoop->clk)) { -- rc = PTR_ERR(lpc_snoop->clk); -- if (rc != -EPROBE_DEFER) -- dev_err(dev, "couldn't get clock\n"); -- return rc; -- } -- rc = clk_prepare_enable(lpc_snoop->clk); -- if (rc) { -- dev_err(dev, "couldn't enable clock\n"); -- return rc; -- } -- - rc = aspeed_lpc_snoop_config_irq(lpc_snoop, pdev); - if (rc) -- goto err; -+ return rc; - -- rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 0, port); -- if (rc) -- goto err; -+ static_assert(ARRAY_SIZE(channel_cfgs) == ARRAY_SIZE(lpc_snoop->chan), -+ "Broken implementation assumption regarding cfg count"); -+ for (idx = ASPEED_LPC_SNOOP_INDEX_0; idx <= ASPEED_LPC_SNOOP_INDEX_MAX; idx++) { -+ u32 port; ++ misc_deregister(&mbox->miscdev); ++} + -+ rc = of_property_read_u32_index(dev->of_node, "snoop-ports", idx, &port); -+ if (rc) -+ break; - -- /* Configuration of 2nd snoop channel port is optional */ -- if (of_property_read_u32_index(dev->of_node, "snoop-ports", -- 1, &port) == 0) { -- rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 1, port); -- if (rc) { -- aspeed_lpc_disable_snoop(lpc_snoop, 0); -- goto err; -- } -+ rc = aspeed_lpc_enable_snoop(dev, lpc_snoop, &lpc_snoop->chan[idx], -+ &channel_cfgs[idx], port); -+ if (rc) -+ goto cleanup_channels; - } - -- return 0; -+ return idx == ASPEED_LPC_SNOOP_INDEX_0 ? -ENODEV : 0; - --err: -- clk_disable_unprepare(lpc_snoop->clk); -+cleanup_channels: -+ aspeed_lpc_snoop_remove(pdev); - - return rc; - } - --static void aspeed_lpc_snoop_remove(struct platform_device *pdev) --{ -- struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev); -- -- /* Disable both snoop channels */ -- aspeed_lpc_disable_snoop(lpc_snoop, 0); -- aspeed_lpc_disable_snoop(lpc_snoop, 1); -- -- clk_disable_unprepare(lpc_snoop->clk); --} -- - static const struct aspeed_lpc_snoop_model_data ast2400_model_data = { - .has_hicrb_ensnp = 0, - }; -@@ -388,7 +375,7 @@ - .of_match_table = aspeed_lpc_snoop_match, - }, - .probe = aspeed_lpc_snoop_probe, -- .remove_new = aspeed_lpc_snoop_remove, -+ .remove = aspeed_lpc_snoop_remove, - }; - - module_platform_driver(aspeed_lpc_snoop_driver); -diff --git a/drivers/soc/aspeed/aspeed-mbox.c b/drivers/soc/aspeed/aspeed-mbox.c ---- a/drivers/soc/aspeed/aspeed-mbox.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-mbox.c 2025-12-23 10:16:21.124032669 +0000 -@@ -0,0 +1,312 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later ++static const struct aspeed_mbox_model ast2400_model = { ++ .dr_num = 16, ++ .dr = 0x0, ++ .str = 0x40, ++ .bcr = 0x48, ++ .hcr = 0x4c, ++ .bie = 0x50, ++ .hie = 0x58, ++}; ++ ++static const struct aspeed_mbox_model ast2600_model = { ++ .dr_num = 32, ++ .dr = 0x0, ++ .str = 0x80, ++ .bcr = 0x90, ++ .hcr = 0x94, ++ .bie = 0xa0, ++ .hie = 0xb0, ++}; ++ ++static const struct of_device_id aspeed_mbox_match[] = { ++ { .compatible = "aspeed,ast2400-mbox", ++ .data = &ast2400_model }, ++ { .compatible = "aspeed,ast2500-mbox", ++ .data = &ast2400_model }, ++ { .compatible = "aspeed,ast2600-mbox", ++ .data = &ast2600_model }, ++ { }, ++}; ++ ++static struct platform_driver aspeed_mbox_driver = { ++ .driver = { ++ .name = DEVICE_NAME, ++ .of_match_table = aspeed_mbox_match, ++ }, ++ .probe = aspeed_mbox_probe, ++ .remove = aspeed_mbox_remove, ++}; ++ ++module_platform_driver(aspeed_mbox_driver); ++MODULE_DEVICE_TABLE(of, aspeed_mbox_match); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Cyril Bur "); ++MODULE_AUTHOR("Chia-Wei Wang -+#include ++#include ++#include ++#include +#include -+#include -+#include -+#include ++#include ++#include +#include +#include -+#include +#include ++#include ++#include +#include +#include -+#include -+#include -+#include -+#include ++#include ++#include ++#include + -+#define __ASPEED_MBOX_IOCTL_MAGIC 'X' -+#define ASPEED_MBOX_IOCTL_CAPS _IOR(__ASPEED_MBOX_IOCTL_MAGIC, 0, uint32_t[4]) -+#define ASPEED_MBOX_IOCTL_SEND _IOW(__ASPEED_MBOX_IOCTL_MAGIC, 1, uint32_t[8]) -+#define ASPEED_MBOX_IOCTL_RECV _IOR(__ASPEED_MBOX_IOCTL_MAGIC, 2, uint32_t[8]) ++#define DEVICE_NAME "aspeed-lpc-pcc" + -+/** -+ * struct aspeed_mem - Description of a ASPEED memory buffer -+ * @buf: Shared memory base address -+ * @size: Shared memory byte size -+ */ -+struct aspeed_mem { -+ void __iomem *buf; -+ phys_addr_t phys_addr; -+ resource_size_t size; -+}; ++static DEFINE_IDA(aspeed_pcc_ida); + -+/* -+ * Message prototype of IPC. It should be defined per your usage. -+ * cmd: type of message -+ * len: length of message -+ */ -+struct ast_mbox_msg { -+ u32 cmd; -+ u32 len; -+}; ++#define HICR5 0x80 ++#define HICR5_EN_SNP0W BIT(0) ++#define HICR5_EN_SNP1W BIT(2) ++#define HICR6 0x084 ++#define HICR6_EN2BMODE BIT(19) ++#define SNPWADR 0x090 ++#define PCCR6 0x0c4 ++#define PCCR6_DMA_CUR_ADDR GENMASK(27, 0) ++#define PCCR4 0x0d0 ++#define PCCR4_DMA_ADDRL_MASK GENMASK(31, 0) ++#define PCCR4_DMA_ADDRL_SHIFT 0 ++#define PCCR5 0x0d4 ++#define PCCR5_DMA_ADDRH_MASK GENMASK(27, 24) ++#define PCCR5_DMA_ADDRH_SHIFT 24 ++#define PCCR5_DMA_LEN_MASK GENMASK(23, 0) ++#define PCCR5_DMA_LEN_SHIFT 0 ++#define HICRB 0x100 ++#define HICRB_ENSNP0D BIT(14) ++#define HICRB_ENSNP1D BIT(15) ++#define PCCR0 0x130 ++#define PCCR0_EN_DMA_INT BIT(31) ++#define PCCR0_EN_DMA_MODE BIT(14) ++#define PCCR0_ADDR_SEL_MASK GENMASK(13, 12) ++#define PCCR0_ADDR_SEL_SHIFT 12 ++#define PCCR0_RX_TRIG_LVL_MASK GENMASK(10, 8) ++#define PCCR0_RX_TRIG_LVL_SHIFT 8 ++#define PCCR0_CLR_RX_FIFO BIT(7) ++#define PCCR0_MODE_SEL_MASK GENMASK(5, 4) ++#define PCCR0_MODE_SEL_SHIFT 4 ++#define PCCR0_EN_RX_TMOUT_INT BIT(2) ++#define PCCR0_EN_RX_AVAIL_INT BIT(1) ++#define PCCR0_EN BIT(0) ++#define PCCR1 0x134 ++#define PCCR1_BASE_ADDR_MASK GENMASK(15, 0) ++#define PCCR1_BASE_ADDR_SHIFT 0 ++#define PCCR1_DONT_CARE_BITS_MASK GENMASK(21, 16) ++#define PCCR1_DONT_CARE_BITS_SHIFT 16 ++#define PCCR2 0x138 ++#define PCCR2_INT_STATUS_PATTERN_B BIT(16) ++#define PCCR2_INT_STATUS_PATTERN_A BIT(8) ++#define PCCR2_INT_STATUS_DMA_DONE BIT(4) ++#define PCCR2_INT_STATUS_DATA_RDY PCCR2_INT_STATUS_DMA_DONE ++#define PCCR2_INT_STATUS_RX_OVER BIT(3) ++#define PCCR2_INT_STATUS_RX_TMOUT BIT(2) ++#define PCCR2_INT_STATUS_RX_AVAIL BIT(1) ++#define PCCR3 0x13c ++#define PCCR3_FIFO_DATA_MASK GENMASK(7, 0) + -+/* -+ * struct ast_mbox_chan - Description of a ASPEED mailbox channel -+ * @cl: Mailbox client -+ * @mdev: Misc device -+ * @chan: Mailbox channel -+ * @tx_base: Information of tx shmem -+ * @rx_base: Information of rx shmem -+ * @rx_buffer: buffer to store data on callback -+ * @rx_wait: Wait queue for receiving messages -+ * @rx_msg_lock: Spinlock to protect rx_buffer ++#define PCC_DMA_BUFSZ (256 * SZ_1K) ++ ++/* Except for the 1-byte threshold, the rest represent fractions of the FIFO. ++ * Ex. PCC_FIFO_THR_1_EIGHTH means 1/8th of the FIFO size. + */ -+struct ast_mbox_info { -+ struct device *dev; -+ struct mbox_client cl; -+ struct miscdevice mdev; -+ struct mbox_chan *chan; -+ struct aspeed_mem tx_base; -+ struct aspeed_mem rx_base; -+ char *rx_buffer; -+ wait_queue_head_t rx_wait; -+ spinlock_t rx_msg_lock; /* spinlock to protect rx_buffer */ ++enum pcc_fifo_threshold { ++ PCC_FIFO_THR_1_BYTE, ++ PCC_FIFO_THR_1_EIGHTH, ++ PCC_FIFO_THR_2_EIGHTH, ++ PCC_FIFO_THR_3_EIGHTH, ++ PCC_FIFO_THR_4_EIGHTH, ++ PCC_FIFO_THR_5_EIGHTH, ++ PCC_FIFO_THR_6_EIGHTH, ++ PCC_FIFO_THR_7_EIGHTH, +}; + -+static ssize_t mbox_read(struct file *fp, char __user *buf, size_t nbytes, loff_t *off) -+{ -+ struct ast_mbox_info *info = container_of(fp->private_data, struct ast_mbox_info, mdev); -+ int ret; -+ -+ if (nbytes == 0) -+ return 0; ++enum pcc_record_mode { ++ PCC_REC_1B, ++ PCC_REC_2B, ++ PCC_REC_4B, ++ PCC_REC_FULL, ++}; + -+ if (!info->rx_base.buf) { -+ dev_err(info->dev, "No RX shmem\n"); -+ return -EINVAL; -+ } ++enum pcc_port_hbits_select { ++ PCC_PORT_HBITS_SEL_NONE, ++ PCC_PORT_HBITS_SEL_45, ++ PCC_PORT_HBITS_SEL_67, ++ PCC_PORT_HBITS_SEL_89, ++}; + -+ if (nbytes > info->rx_base.size) { -+ dev_warn(info->dev, "Read size %zu exceeds RX shmem size %llu\n", -+ nbytes, info->rx_base.size); -+ nbytes = info->rx_base.size; -+ } ++struct aspeed_pcc_dma { ++ uint32_t rptr; ++ uint8_t *virt; ++ dma_addr_t addr; ++ uint32_t size; ++}; + -+ ret = copy_to_user((void __user *)buf, info->rx_base.buf, nbytes); -+ if (ret) -+ return -EFAULT; ++struct aspeed_pcc_ctrl { ++ struct device *dev; ++ struct regmap *regmap; ++ int irq; ++ uint32_t port; ++ struct aspeed_pcc_dma dma; ++ struct kfifo fifo; ++ wait_queue_head_t wq; ++ struct miscdevice mdev; ++ int mdev_id; ++ spinlock_t lock; /* protects access to the FIFO and DMA pointer */ ++}; + -+ return nbytes; ++static inline bool is_valid_rec_mode(uint32_t mode) ++{ ++ return (mode > PCC_REC_FULL) ? false : true; +} + -+static ssize_t mbox_write(struct file *fp, const char *buf, size_t nbytes, loff_t *off) ++static inline bool is_valid_high_bits_select(uint32_t sel) +{ -+ struct ast_mbox_info *info = container_of(fp->private_data, struct ast_mbox_info, mdev); -+ int ret; ++ return (sel > PCC_PORT_HBITS_SEL_89) ? false : true; ++} + -+ if (nbytes == 0) -+ return 0; ++static ssize_t aspeed_pcc_file_read(struct file *file, char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ int rc; ++ unsigned int copied; ++ struct aspeed_pcc_ctrl *pcc = container_of(file->private_data, ++ struct aspeed_pcc_ctrl, ++ mdev); + -+ if (!info->tx_base.buf) { -+ dev_err(info->dev, "No TX shmem\n"); -+ return -EINVAL; -+ } ++ if (kfifo_is_empty(&pcc->fifo)) { ++ if (file->f_flags & O_NONBLOCK) ++ return -EAGAIN; + -+ if (nbytes > info->tx_base.size) { -+ dev_warn(info->dev, "Write size %zu exceeds TX shmem size %llu\n", -+ nbytes, info->tx_base.size); -+ nbytes = info->tx_base.size; ++ rc = wait_event_interruptible(pcc->wq, ++ !kfifo_is_empty(&pcc->fifo)); ++ if (rc == -ERESTARTSYS) ++ return -EINTR; + } + -+ ret = copy_from_user(info->tx_base.buf, (void __user *)buf, nbytes); -+ if (ret) -+ return -EFAULT; ++ rc = kfifo_to_user(&pcc->fifo, buffer, count, &copied); + -+ return nbytes; ++ return rc ? rc : copied; +} + -+static __poll_t mbox_poll(struct file *fp, struct poll_table_struct *wait) ++static __poll_t aspeed_pcc_file_poll(struct file *file, ++ struct poll_table_struct *pt) +{ -+ struct ast_mbox_info *info = container_of(fp->private_data, struct ast_mbox_info, mdev); -+ __poll_t mask = 0; -+ -+ poll_wait(fp, &info->rx_wait, wait); ++ struct aspeed_pcc_ctrl *pcc = container_of(file->private_data, ++ struct aspeed_pcc_ctrl, ++ mdev); + -+ if (info->rx_buffer) -+ mask |= POLLIN | POLLRDNORM; ++ poll_wait(file, &pcc->wq, pt); + -+ return mask; ++ return !kfifo_is_empty(&pcc->fifo) ? POLLIN : 0; +} + -+static long mbox_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) -+{ -+ struct ast_mbox_info *info = container_of(fp->private_data, struct ast_mbox_info, mdev); -+ u32 *data; -+ u32 msg[8]; -+ unsigned long flags; -+ int ret = 0; ++static const struct file_operations pcc_fops = { ++ .owner = THIS_MODULE, ++ .read = aspeed_pcc_file_read, ++ .poll = aspeed_pcc_file_poll, ++}; + -+ switch (cmd) { -+ case ASPEED_MBOX_IOCTL_CAPS: -+ data = (u32 *)arg; -+ data[0] = (info->tx_base.phys_addr) >> 8; -+ data[1] = info->tx_base.size; -+ data[2] = (info->rx_base.phys_addr) >> 8; -+ data[3] = info->rx_base.size; -+ break; -+ case ASPEED_MBOX_IOCTL_SEND: -+ ret = copy_from_user(msg, (void __user *)arg, sizeof(msg)); -+ if (ret) { -+ dev_dbg(info->dev, "Failed to copy message from user\n"); -+ return -EFAULT; -+ } ++static irqreturn_t aspeed_pcc_dma_isr(int irq, void *arg) ++{ ++ uint32_t reg, rptr, wptr; ++ struct aspeed_pcc_ctrl *pcc = (struct aspeed_pcc_ctrl *)arg; ++ struct kfifo *fifo = &pcc->fifo; + -+ ret = mbox_send_message(info->chan, msg); -+ if (ret < 0) -+ dev_dbg(info->dev, "Failed to send message via mailbox\n"); -+ break; -+ case ASPEED_MBOX_IOCTL_RECV: -+ spin_lock_irqsave(&info->rx_msg_lock, flags); -+ if (!info->rx_buffer) { -+ spin_unlock_irqrestore(&info->rx_msg_lock, flags); -+ dev_dbg(info->dev, "No message received\n"); -+ return -EAGAIN; -+ } -+ ret = copy_to_user((void __user *)arg, info->rx_buffer, sizeof(msg)); -+ info->rx_buffer = NULL; -+ spin_unlock_irqrestore(&info->rx_msg_lock, flags); -+ if (ret) { -+ dev_dbg(info->dev, "Failed to copy message to user\n"); -+ return -EFAULT; -+ } -+ break; -+ default: -+ dev_err(info->dev, "Unsupported cmd: %x\n", cmd); -+ ret = -EINVAL; -+ } ++ spin_lock(&pcc->lock); ++ regmap_write_bits(pcc->regmap, PCCR2, PCCR2_INT_STATUS_DMA_DONE, PCCR2_INT_STATUS_DMA_DONE); + -+ return ret; -+} ++ regmap_read(pcc->regmap, PCCR6, ®); ++ wptr = (reg & PCCR6_DMA_CUR_ADDR) - (pcc->dma.addr & PCCR6_DMA_CUR_ADDR); ++ rptr = pcc->dma.rptr; + -+static const struct file_operations aspeed_mbox_fops = { -+ .owner = THIS_MODULE, -+ .read = mbox_read, -+ .write = mbox_write, -+ .poll = mbox_poll, -+ .unlocked_ioctl = mbox_ioctl, -+}; ++ /* If kfifo is empty or has enough space, insert new data; ++ * otherwise, discard the new data. ++ */ ++ if (rptr <= wptr) { ++ kfifo_in(fifo, pcc->dma.virt + rptr, wptr - rptr); ++ } else { ++ /* Handle wrap-around case */ ++ kfifo_in(fifo, pcc->dma.virt + rptr, pcc->dma.size - rptr); ++ kfifo_in(fifo, pcc->dma.virt, wptr); ++ } + -+static void mbox_rx_callback(struct mbox_client *client, void *message) -+{ -+ struct ast_mbox_info *info = container_of(client, struct ast_mbox_info, cl); -+ unsigned long flags; ++ pcc->dma.rptr = wptr; ++ spin_unlock(&pcc->lock); + -+ spin_lock_irqsave(&info->rx_msg_lock, flags); -+ info->rx_buffer = message; -+ spin_unlock_irqrestore(&info->rx_msg_lock, flags); ++ wake_up_interruptible(&pcc->wq); + -+ wake_up_interruptible(&info->rx_wait); ++ return IRQ_HANDLED; +} + -+static int aspeed_mbox_probe(struct platform_device *pdev) ++static irqreturn_t aspeed_pcc_isr(int irq, void *arg) +{ -+ struct ast_mbox_info *info; -+ struct resource *res; -+ struct device *dev = &pdev->dev; -+ int ret; -+ u32 tx_tout = -1; -+ -+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); -+ if (!info) -+ return -ENOMEM; -+ -+ info->tx_base.buf = devm_platform_get_and_ioremap_resource(pdev, 0, &res); -+ if (PTR_ERR(info->tx_base.buf) == -EBUSY) { -+ /* if reserved area in SRAM, try just ioremap */ -+ info->tx_base.size = resource_size(res); -+ info->tx_base.buf = devm_ioremap(dev, res->start, info->tx_base.size); -+ } -+ if (IS_ERR(info->tx_base.buf)) { -+ info->tx_base.buf = NULL; -+ } else { -+ info->tx_base.phys_addr = res->start; -+ info->tx_base.size = resource_size(res); -+ } ++ uint32_t sts; ++ struct aspeed_pcc_ctrl *pcc = (struct aspeed_pcc_ctrl *)arg; + -+ info->rx_base.buf = devm_platform_get_and_ioremap_resource(pdev, 1, &res); -+ if (PTR_ERR(info->rx_base.buf) == -EBUSY) { -+ /* if reserved area in SRAM, try just ioremap */ -+ info->rx_base.size = resource_size(res); -+ info->rx_base.buf = devm_ioremap(dev, res->start, info->rx_base.size); -+ } -+ if (IS_ERR(info->rx_base.buf)) { -+ info->rx_base = info->tx_base; -+ } else { -+ info->rx_base.phys_addr = res->start; -+ info->rx_base.size = resource_size(res); -+ } ++ regmap_read(pcc->regmap, PCCR2, &sts); + -+ if (device_property_read_u32(dev, "aspeed,tx-timeout", &tx_tout)) -+ tx_tout = -1; ++ if (!(sts & (PCCR2_INT_STATUS_RX_TMOUT | ++ PCCR2_INT_STATUS_RX_AVAIL | ++ PCCR2_INT_STATUS_DMA_DONE))) ++ return IRQ_NONE; + -+ dev_info(dev, "TX shmem: phys 0x%pa size %llu\n", -+ &info->tx_base.phys_addr, info->tx_base.size); -+ dev_info(dev, "RX shmem: phys 0x%pa size %llu\n", -+ &info->rx_base.phys_addr, info->rx_base.size); -+ dev_info(dev, "TX timeout: %u ms\n", tx_tout); ++ return aspeed_pcc_dma_isr(irq, arg); ++} + -+ info->cl.dev = dev; -+ info->cl.rx_callback = mbox_rx_callback; -+ info->cl.tx_block = true; -+ info->cl.knows_txdone = false; -+ info->cl.tx_tout = tx_tout; ++/* ++ * A2600-15 AP note ++ * ++ * SW workaround to prevent generating Non-Fatal-Error (NFE) ++ * eSPI response when PCC is used for port I/O byte snooping ++ * over eSPI. ++ */ ++static int aspeed_a2600_15(struct aspeed_pcc_ctrl *pcc, struct device *dev) ++{ ++ u32 hicr5_en, hicrb_en; + -+ info->chan = mbox_request_channel(&info->cl, 0); -+ if (IS_ERR(info->chan)) { -+ dev_err(dev, "failed to request channel err %ld\n", -+ PTR_ERR(info->chan)); -+ return -EPROBE_DEFER; ++ /* abort if snoop is enabled */ ++ regmap_read(pcc->regmap, HICR5, &hicr5_en); ++ if (hicr5_en & (HICR5_EN_SNP0W | HICR5_EN_SNP1W)) { ++ dev_err(dev, "A2600-15 should be applied with snoop disabled\n"); ++ return -EPERM; + } + -+ info->rx_buffer = NULL; -+ init_waitqueue_head(&info->rx_wait); -+ spin_lock_init(&info->rx_msg_lock); -+ info->dev = dev; -+ platform_set_drvdata(pdev, info); ++ /* set SNPWADR of snoop device */ ++ regmap_write(pcc->regmap, SNPWADR, pcc->port | ((pcc->port + 2) << 16)); + -+ info->mdev.parent = dev; -+ info->mdev.minor = MISC_DYNAMIC_MINOR; -+ info->mdev.name = dev_name(dev); -+ info->mdev.fops = &aspeed_mbox_fops; -+ ret = misc_register(&info->mdev); -+ if (ret) { -+ dev_err(dev, "failed to register misc device\n"); -+ return ret; -+ } ++ /* set HICRB[15:14]=11b to enable ACCEPT response for SNPWADR */ ++ hicrb_en = HICRB_ENSNP0D | HICRB_ENSNP1D; ++ regmap_update_bits(pcc->regmap, HICRB, hicrb_en, hicrb_en); ++ ++ /* set HICR6[19] to extend SNPWADR to 2x range */ ++ regmap_update_bits(pcc->regmap, HICR6, HICR6_EN2BMODE, HICR6_EN2BMODE); + + return 0; +} + -+static void aspeed_mbox_remove(struct platform_device *pdev) ++static int aspeed_pcc_enable(struct aspeed_pcc_ctrl *pcc, struct device *dev) +{ -+ struct ast_mbox_info *info = platform_get_drvdata(pdev); -+ -+ mbox_free_channel(info->chan); -+ misc_deregister(&info->mdev); -+} ++ int rc; + -+static const struct of_device_id mbox_cl_match[] = { -+ { .compatible = "aspeed,aspeed-mbox" }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, mbox_test_match); ++ rc = aspeed_a2600_15(pcc, dev); ++ if (rc) ++ return rc; + -+static struct platform_driver aspeed_mbox_driver = { -+ .driver = { -+ .name = "aspeed_mbox_client", -+ .of_match_table = mbox_cl_match, -+ }, -+ .probe = aspeed_mbox_probe, -+ .remove = aspeed_mbox_remove, -+}; -+module_platform_driver(aspeed_mbox_driver); ++ /* record mode: Set 2-Byte mode. */ ++ regmap_update_bits(pcc->regmap, PCCR0, ++ PCCR0_MODE_SEL_MASK, ++ PCC_REC_2B << PCCR0_MODE_SEL_SHIFT); + -+MODULE_AUTHOR("Jammy Huang "); -+MODULE_DESCRIPTION("ASPEED MBOX CLIENT driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c ---- a/drivers/soc/aspeed/aspeed-mctp.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-mctp.c 2025-12-23 10:16:21.124032669 +0000 -@@ -0,0 +1,2658 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2020, Intel Corporation. ++ /* port address */ ++ regmap_update_bits(pcc->regmap, PCCR1, ++ PCCR1_BASE_ADDR_MASK, ++ pcc->port << PCCR1_BASE_ADDR_SHIFT); + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++ /* Set address high bits selection to 0b01 for address bit[5:4] */ ++ regmap_update_bits(pcc->regmap, PCCR0, ++ PCCR0_ADDR_SEL_MASK, ++ PCC_PORT_HBITS_SEL_45 << PCCR0_ADDR_SEL_SHIFT); + -+#include ++ /* Set LPC don't care address to 0x3 for port 80~83h */ ++ regmap_update_bits(pcc->regmap, PCCR1, ++ PCCR1_DONT_CARE_BITS_MASK, ++ 0x3 << PCCR1_DONT_CARE_BITS_SHIFT); + -+/* AST2600 MCTP Controller registers */ -+#define ASPEED_MCTP_CTRL 0x000 -+#define TX_CMD_TRIGGER BIT(0) -+#define RX_CMD_READY BIT(4) -+#define MATCHING_EID BIT(9) ++ /* set DMA ring buffer size and enable interrupts */ ++ regmap_write(pcc->regmap, PCCR4, pcc->dma.addr & 0xffffffff); ++#ifdef CONFIG_ARM64 ++ regmap_update_bits(pcc->regmap, PCCR5, PCCR5_DMA_ADDRH_MASK, ++ (pcc->dma.addr >> 32) << PCCR5_DMA_ADDRH_SHIFT); ++#endif ++ regmap_update_bits(pcc->regmap, PCCR5, PCCR5_DMA_LEN_MASK, ++ (pcc->dma.size / 4) << PCCR5_DMA_LEN_SHIFT); ++ regmap_update_bits(pcc->regmap, PCCR0, ++ PCCR0_EN_DMA_INT | PCCR0_EN_DMA_MODE, ++ PCCR0_EN_DMA_INT | PCCR0_EN_DMA_MODE); + -+#define ASPEED_MCTP_TX_CMD 0x004 -+#define ASPEED_MCTP_RX_CMD 0x008 ++ regmap_update_bits(pcc->regmap, PCCR0, ++ PCCR0_RX_TRIG_LVL_MASK, ++ PCC_FIFO_THR_2_EIGHTH << PCCR0_RX_TRIG_LVL_SHIFT); + -+#define ASPEED_MCTP_INT_STS 0x00c -+#define ASPEED_MCTP_INT_EN 0x010 -+#define TX_CMD_SENT_INT BIT(0) -+#define TX_CMD_LAST_INT BIT(1) -+#define TX_CMD_WRONG_INT BIT(2) -+#define RX_CMD_RECEIVE_INT BIT(8) -+#define RX_CMD_NO_MORE_INT BIT(9) ++ regmap_update_bits(pcc->regmap, PCCR0, PCCR0_EN, PCCR0_EN); + -+#define ASPEED_MCTP_EID 0x014 -+#define MEMORY_SPACE_MAPPING GENMASK(31, 28) -+#define MCTP_EID GENMASK(7, 0) -+#define ASPEED_MCTP_OBFF_CTRL 0x018 ++ return 0; ++} + -+#define ASPEED_MCTP_ENGINE_CTRL 0x01c -+#define TX_MAX_PAYLOAD_SIZE_SHIFT 0 -+#define TX_MAX_PAYLOAD_SIZE_MASK GENMASK(1, TX_MAX_PAYLOAD_SIZE_SHIFT) -+#define TX_MAX_PAYLOAD_SIZE(x) \ -+ (((x) << TX_MAX_PAYLOAD_SIZE_SHIFT) & TX_MAX_PAYLOAD_SIZE_MASK) -+#define RX_MAX_PAYLOAD_SIZE_SHIFT 4 -+#define RX_MAX_PAYLOAD_SIZE_MASK GENMASK(5, RX_MAX_PAYLOAD_SIZE_SHIFT) -+#define RX_MAX_PAYLOAD_SIZE(x) \ -+ (((x) << RX_MAX_PAYLOAD_SIZE_SHIFT) & RX_MAX_PAYLOAD_SIZE_MASK) -+#define FIFO_LAYOUT_SHIFT 8 -+#define FIFO_LAYOUT_MASK GENMASK(9, FIFO_LAYOUT_SHIFT) -+#define FIFO_LAYOUT(x) \ -+ (((x) << FIFO_LAYOUT_SHIFT) & FIFO_LAYOUT_MASK) ++static int aspeed_pcc_disable(struct aspeed_pcc_ctrl *pcc) ++{ ++ /* Disable PCC and DMA Mode for safety */ ++ regmap_update_bits(pcc->regmap, PCCR0, PCCR0_EN | PCCR0_EN_DMA_MODE, 0); + -+#define ASPEED_MCTP_RX_BUF_ADDR 0x08 -+#define ASPEED_MCTP_RX_BUF_HI_ADDR 0x020 -+#define ASPEED_MCTP_RX_BUF_SIZE 0x024 -+#define ASPEED_MCTP_RX_BUF_RD_PTR 0x028 -+#define UPDATE_RX_RD_PTR BIT(31) -+#define RX_BUF_RD_PTR_MASK GENMASK(11, 0) -+#define ASPEED_MCTP_RX_BUF_WR_PTR 0x02c -+#define RX_BUF_WR_PTR_MASK GENMASK(11, 0) ++ /* Clear Rx FIFO. */ ++ regmap_update_bits(pcc->regmap, PCCR0, PCCR0_CLR_RX_FIFO, 1); + -+#define ASPEED_MCTP_TX_BUF_ADDR 0x04 -+#define ASPEED_MCTP_TX_BUF_HI_ADDR 0x030 -+#define ASPEED_MCTP_TX_BUF_SIZE 0x034 -+#define ASPEED_MCTP_TX_BUF_RD_PTR 0x038 -+#define UPDATE_TX_RD_PTR BIT(31) -+#define TX_BUF_RD_PTR_MASK GENMASK(11, 0) -+#define ASPEED_MCTP_TX_BUF_WR_PTR 0x03c -+#define TX_BUF_WR_PTR_MASK GENMASK(11, 0) -+#define ASPEED_G7_MCTP_PCIE_BDF 0x04c ++ /* Clear All interrupts status. */ ++ regmap_write(pcc->regmap, PCCR2, ++ PCCR2_INT_STATUS_RX_OVER | PCCR2_INT_STATUS_DMA_DONE | ++ PCCR2_INT_STATUS_PATTERN_A | PCCR2_INT_STATUS_PATTERN_B); + -+#define ADDR_LEN GENMASK(26, 0) -+#define DATA_ADDR(x) (((x) >> 4) & ADDR_LEN) ++ return 0; ++} + -+/* TX command */ -+#define TX_LAST_CMD BIT(31) -+#define TX_DATA_ADDR_SHIFT 4 -+#define TX_DATA_ADDR_MASK GENMASK(30, TX_DATA_ADDR_SHIFT) -+#define TX_DATA_ADDR(x) \ -+ ((DATA_ADDR(x) << TX_DATA_ADDR_SHIFT) & TX_DATA_ADDR_MASK) -+#define TX_RESERVED_1_MASK GENMASK(1, 0) /* must be 1 */ -+#define TX_RESERVED_1 1 -+#define TX_STOP_AFTER_CMD BIT(16) -+#define TX_INTERRUPT_AFTER_CMD BIT(15) -+#define TX_PACKET_SIZE_SHIFT 2 -+#define TX_PACKET_SIZE_MASK GENMASK(12, TX_PACKET_SIZE_SHIFT) -+#define TX_PACKET_SIZE(x) \ -+ (((x) << TX_PACKET_SIZE_SHIFT) & TX_PACKET_SIZE_MASK) -+#define TX_RESERVED_0_MASK GENMASK(1, 0) /* MBZ */ -+#define TX_RESERVED_0 0 ++static int aspeed_pcc_probe(struct platform_device *pdev) ++{ ++ int rc; ++ struct aspeed_pcc_ctrl *pcc; ++ struct device *dev = &pdev->dev; ++ uint32_t fifo_size = PAGE_SIZE; + -+/* RX command */ -+#define RX_INTERRUPT_AFTER_CMD BIT(2) -+#define RX_DATA_ADDR_SHIFT 4 -+#define RX_DATA_ADDR_MASK GENMASK(30, RX_DATA_ADDR_SHIFT) -+#define RX_DATA_ADDR(x) \ -+ ((DATA_ADDR(x) << RX_DATA_ADDR_SHIFT) & RX_DATA_ADDR_MASK) ++ pcc = devm_kzalloc(dev, sizeof(*pcc), GFP_KERNEL); ++ if (!pcc) ++ return -ENOMEM; + -+#define ADDR_LEN_2500 GENMASK(23, 0) -+#define DATA_ADDR_2500(x) (((x) >> 7) & ADDR_LEN_2500) ++ pcc->regmap = syscon_node_to_regmap(dev->parent->of_node); ++ if (IS_ERR(pcc->regmap)) ++ return dev_err_probe(dev, PTR_ERR(pcc->regmap), "Couldn't get regmap\n"); + -+/* TX command for ast2500 */ -+#define TX_DATA_ADDR_MASK_2500 GENMASK(30, 8) -+#define TX_DATA_ADDR_2500(x) \ -+ FIELD_PREP(TX_DATA_ADDR_MASK_2500, DATA_ADDR_2500(x)) -+#define TX_PACKET_SIZE_2500(x) \ -+ FIELD_PREP(GENMASK(11, 2), x) -+#define TX_PACKET_DEST_EID GENMASK(7, 0) -+#define TX_PACKET_TARGET_ID GENMASK(31, 16) -+#define TX_PACKET_ROUTING_TYPE BIT(14) -+#define TX_PACKET_TAG_OWNER BIT(13) -+#define TX_PACKET_PADDING_LEN GENMASK(1, 0) ++ rc = of_property_read_u32(dev->of_node, "pcc-ports", &pcc->port); ++ if (rc) { ++ dev_err(dev, "no pcc ports configured\n"); ++ return rc; ++ } + -+/* Rx command for ast2500 */ -+#define RX_LAST_CMD BIT(31) -+#define RX_DATA_ADDR_MASK_2500 GENMASK(29, 7) -+#define RX_DATA_ADDR_2500(x) \ -+ FIELD_PREP(RX_DATA_ADDR_MASK_2500, DATA_ADDR_2500(x)) -+#define RX_PACKET_SIZE GENMASK(30, 24) -+#define RX_PACKET_SRC_EID GENMASK(23, 16) -+#define RX_PACKET_ROUTING_TYPE GENMASK(15, 14) -+#define RX_PACKET_TAG_OWNER BIT(13) -+#define RX_PACKET_SEQ_NUMBER GENMASK(12, 11) -+#define RX_PACKET_MSG_TAG GENMASK(10, 8) -+#define RX_PACKET_SOM BIT(7) -+#define RX_PACKET_EOM BIT(6) -+#define RX_PACKET_PADDING_LEN GENMASK(5, 4) ++ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); ++ if (rc) { ++ dev_err(dev, "cannot set 64-bits DMA mask\n"); ++ return rc; ++ } + -+/* HW buffer sizes */ -+#define TX_PACKET_COUNT 48 -+#define RX_PACKET_COUNT 96 -+#if (RX_PACKET_COUNT % 4 != 0) -+#error The Rx buffer size should be 4-aligned. -+#error 1.Make runaway wrap boundary can be determined in Ast2600 A1/A2. -+#error 2.Fix the runaway read pointer bug in Ast2600 A3. -+#endif -+#define TX_MAX_PACKET_COUNT (TX_BUF_RD_PTR_MASK + 1) -+#define RX_MAX_PACKET_COUNT (RX_BUF_RD_PTR_MASK + 1) ++ pcc->dma.size = PCC_DMA_BUFSZ; ++ pcc->dma.virt = dmam_alloc_coherent(dev, ++ pcc->dma.size, ++ &pcc->dma.addr, ++ GFP_KERNEL); ++ if (!pcc->dma.virt) { ++ dev_err(dev, "cannot allocate DMA buffer\n"); ++ return -ENOMEM; ++ } + -+/* Per client packet cache sizes */ -+#define RX_RING_COUNT 64 -+#define TX_RING_COUNT 64 ++ fifo_size = roundup(pcc->dma.size, PAGE_SIZE); ++ rc = kfifo_alloc(&pcc->fifo, fifo_size, GFP_KERNEL); ++ if (rc) ++ return rc; + -+/* PCIe Host Controller registers */ -+#define ASPEED_PCIE_LINK 0x0c0 -+#define PCIE_LINK_STS BIT(5) -+#define ASPEED_PCIE_MISC_STS_1 0x0c4 ++ spin_lock_init(&pcc->lock); + -+/* PCIe Host Controller registers */ -+#define ASPEED_G7_PCIE_LOCATE 0x300 -+#define PCIE_LOCATE_IO BIT(0) -+#define ASPEED_G7_PCIE_LINK 0x358 -+#define PCIE_G7_LINK_STS BIT(8) -+#define ASPEED_G7_IO_PCIE_LINK 0x344 -+#define PCIE_G7_IO_LINK_STS BIT(18) ++ /* Disable PCC to clean up DMA buffer before request IRQ. */ ++ rc = aspeed_pcc_disable(pcc); ++ if (rc) { ++ dev_err(dev, "Couldn't disable PCC\n"); ++ goto err_free_kfifo; ++ } + -+/* PCI address definitions */ -+#define PCI_DEV_NUM_MASK GENMASK(4, 0) -+#define PCI_BUS_NUM_SHIFT 5 -+#define PCI_BUS_NUM_MASK GENMASK(12, PCI_BUS_NUM_SHIFT) -+#define GET_PCI_DEV_NUM(x) ((x) & PCI_DEV_NUM_MASK) -+#define GET_PCI_BUS_NUM(x) (((x) & PCI_BUS_NUM_MASK) >> PCI_BUS_NUM_SHIFT) ++ pcc->irq = platform_get_irq(pdev, 0); ++ if (pcc->irq < 0) { ++ rc = pcc->irq; ++ goto err_free_kfifo; ++ } + -+/* MCTP header definitions */ -+#define MCTP_HDR_SRC_EID_OFFSET 14 -+#define MCTP_HDR_TAG_OFFSET 15 -+#define MCTP_HDR_SOM BIT(7) -+#define MCTP_HDR_EOM BIT(6) -+#define MCTP_HDR_SOM_EOM (MCTP_HDR_SOM | MCTP_HDR_EOM) -+#define MCTP_PAYLOAD_TYPE_OFFSET 0 -+#define MCTP_HDR_TYPE_CONTROL 0 -+#define MCTP_HDR_TYPE_VDM_PCI 0x7e -+#define MCTP_HDR_TYPE_SPDM 0x5 -+#define MCTP_HDR_TYPE_BASE_LAST MCTP_HDR_TYPE_SPDM -+#define MCTP_PAYLOAD_VENDOR_OFFSET 1 -+#define MCTP_PAYLOAD_VDM_TYPE_OFFSET 3 ++ rc = devm_request_irq(dev, pcc->irq, aspeed_pcc_isr, 0, DEVICE_NAME, pcc); ++ if (rc < 0) { ++ dev_err(dev, "Couldn't request IRQ %d\n", pcc->irq); ++ goto err_free_kfifo; ++ } + -+/* MCTP header DW little endian mask definitions */ -+/* 0th DW */ -+#define MCTP_HDR_DW_LE_ROUTING_TYPE GENMASK(26, 24) -+#define MCTP_HDR_DW_LE_PACKET_SIZE GENMASK(9, 0) -+/* 1st DW */ -+#define MCTP_HDR_DW_LE_PADDING_LEN GENMASK(13, 12) -+/* 2nd DW */ -+#define MCTP_HDR_DW_LE_TARGET_ID GENMASK(31, 16) -+/* 3rd DW */ -+#define MCTP_HDR_DW_LE_TAG_OWNER BIT(3) -+#define MCTP_HDR_DW_LE_DEST_EID GENMASK(23, 16) ++ init_waitqueue_head(&pcc->wq); + -+#define ASPEED_MCTP_2600 0 -+#define ASPEED_MCTP_2600A3 1 ++ pcc->mdev_id = ida_alloc(&aspeed_pcc_ida, GFP_KERNEL); ++ if (pcc->mdev_id < 0) { ++ dev_err(dev, "Couldn't allocate ID\n"); ++ goto err_free_kfifo; ++ } + -+#define ASPEED_REVISION_ID0 0x04 -+#define ASPEED_REVISION_ID1 0x14 -+#define ID0_AST2600A0 0x05000303 -+#define ID1_AST2600A0 0x05000303 -+#define ID0_AST2600A1 0x05010303 -+#define ID1_AST2600A1 0x05010303 -+#define ID0_AST2600A2 0x05010303 -+#define ID1_AST2600A2 0x05020303 -+#define ID0_AST2600A3 0x05030303 -+#define ID1_AST2600A3 0x05030303 -+#define ID0_AST2620A1 0x05010203 -+#define ID1_AST2620A1 0x05010203 -+#define ID0_AST2620A2 0x05010203 -+#define ID1_AST2620A2 0x05020203 -+#define ID0_AST2620A3 0x05030203 -+#define ID1_AST2620A3 0x05030203 -+#define ID0_AST2605A2 0x05010103 -+#define ID1_AST2605A2 0x05020103 -+#define ID0_AST2605A3 0x05030103 -+#define ID1_AST2605A3 0x05030103 -+#define ID0_AST2625A3 0x05030403 -+#define ID1_AST2625A3 0x05030403 ++ pcc->mdev.parent = dev; ++ pcc->mdev.minor = MISC_DYNAMIC_MINOR; ++ pcc->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, ++ pcc->mdev_id); ++ pcc->mdev.fops = &pcc_fops; ++ rc = misc_register(&pcc->mdev); ++ if (rc) { ++ dev_err(dev, "Couldn't register misc device\n"); ++ goto err_free_ida; ++ } + -+#define ASPEED_G7_SCU_PCIE0_CTRL_OFFSET 0xa60 -+#define ASPEED_G7_SCU_PCIE1_CTRL_OFFSET 0xae0 -+#define ASPEED_G7_SCU_PCIE_CTRL_VDM_EN BIT(1) ++ rc = aspeed_pcc_enable(pcc, dev); ++ if (rc) { ++ dev_err(dev, "Couldn't enable PCC\n"); ++ goto err_dereg_mdev; ++ } + -+struct aspeed_mctp_match_data { -+ u32 rx_cmd_size; -+ u32 tx_cmd_size; -+ u32 packet_unit_size; -+ bool need_address_mapping; -+ bool vdm_hdr_direct_xfer; -+ bool fifo_auto_surround; -+ bool dma_need_64bits_width; -+ u32 scu_pcie_ctrl_offset; -+}; ++ dev_set_drvdata(dev, pcc); + -+struct aspeed_mctp_rx_cmd { -+ u32 rx_lo; -+ u32 rx_hi; -+}; ++ return 0; + -+struct aspeed_mctp_tx_cmd { -+ u32 tx_lo; -+ u32 tx_hi; -+}; ++err_dereg_mdev: ++ misc_deregister(&pcc->mdev); + -+struct aspeed_g7_mctp_tx_cmd { -+ u32 tx_lo; -+ u32 tx_mid; -+ u32 tx_hi; -+ u32 reserved; -+}; ++err_free_ida: ++ ida_free(&aspeed_pcc_ida, pcc->mdev_id); + -+struct mctp_buffer { -+ void *vaddr; -+ dma_addr_t dma_handle; -+}; ++err_free_kfifo: ++ kfifo_free(&pcc->fifo); + -+struct mctp_channel { -+ struct mctp_buffer data; -+ struct mctp_buffer cmd; -+ struct tasklet_struct tasklet; -+ u32 buffer_count; -+ u32 rd_ptr; -+ u32 wr_ptr; -+ bool stopped; -+}; ++ return rc; ++} + -+struct aspeed_mctp { -+ struct device *dev; -+ struct miscdevice mctp_miscdev; -+ const struct aspeed_mctp_match_data *match_data; -+ struct regmap *map; -+ struct reset_control *reset; -+ /* -+ * The reset of the dma block in the MCTP-RC is connected to -+ * another reset pin. -+ */ -+ struct reset_control *reset_dma; -+ struct mctp_channel tx; -+ struct mctp_channel rx; -+ struct list_head clients; -+ struct mctp_client *default_client; -+ struct list_head mctp_type_handlers; -+ /* -+ * clients_lock protects list of clients, list of type handlers -+ * and default client -+ */ -+ spinlock_t clients_lock; -+ struct list_head endpoints; -+ size_t endpoints_count; -+ /* -+ * endpoints_lock protects list of endpoints -+ */ -+ struct mutex endpoints_lock; -+ struct { -+ struct regmap *map; -+ struct delayed_work rst_dwork; -+ bool need_uevent; -+ } pcie; -+ struct { -+ bool enable; -+ bool first_loop; -+ int packet_counter; -+ } rx_runaway_wa; -+ bool rx_warmup; -+ u8 eid; -+ struct platform_device *peci_mctp; -+ /* Use the flag to identify RC or EP */ -+ bool rc_f; -+ /* Use the flag to identify the support of MCTP interrupt */ -+ bool miss_mctp_int; -+ /* Rx hardware buffer size */ -+ u32 rx_packet_count; -+ /* Rx pointer ring size */ -+ u32 rx_ring_count; -+ /* Tx pointer ring size */ -+ u32 tx_ring_count; -+ /* Delayed work for periodic detection of Rx packets */ -+ struct delayed_work rx_det_dwork; -+ u32 rx_det_period_us; -+#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM -+ struct net_device *ndev; -+#endif -+}; ++static void aspeed_pcc_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct aspeed_pcc_ctrl *pcc = dev_get_drvdata(dev); + -+struct mctp_client { -+ struct kref ref; -+ struct aspeed_mctp *priv; -+ struct ptr_ring tx_queue; -+ struct ptr_ring rx_queue; -+ struct list_head link; -+ wait_queue_head_t wait_queue; -+}; ++ kfifo_free(&pcc->fifo); ++ ida_free(&aspeed_pcc_ida, pcc->mdev_id); ++ misc_deregister(&pcc->mdev); ++} + -+struct mctp_type_handler { -+ u8 mctp_type; -+ u16 pci_vendor_id; -+ u16 vdm_type; -+ u16 vdm_mask; -+ struct mctp_client *client; -+ struct list_head link; ++static const struct of_device_id aspeed_pcc_table[] = { ++ { .compatible = "aspeed,ast2600-lpc-pcc" }, ++ { }, +}; + -+union aspeed_mctp_eid_data_info { -+ struct aspeed_mctp_eid_info eid_info; -+ struct aspeed_mctp_eid_ext_info eid_ext_info; ++static struct platform_driver aspeed_pcc_driver = { ++ .driver = { ++ .name = "aspeed-pcc", ++ .of_match_table = aspeed_pcc_table, ++ }, ++ .probe = aspeed_pcc_probe, ++ .remove = aspeed_pcc_remove, +}; + -+enum mctp_address_type { -+ ASPEED_MCTP_GENERIC_ADDR_FORMAT = 0, -+ ASPEED_MCTP_EXTENDED_ADDR_FORMAT = 1 -+}; ++module_platform_driver(aspeed_pcc_driver); + -+struct aspeed_mctp_endpoint { -+ union aspeed_mctp_eid_data_info data; -+ struct list_head link; ++MODULE_AUTHOR("Chia-Wei Wang "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Driver for Aspeed Post Code Capture"); +diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c +--- a/drivers/soc/aspeed/aspeed-lpc-snoop.c 2025-08-01 08:48:47.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c 2026-04-08 18:03:48.310705320 +0000 +@@ -11,7 +11,7 @@ + */ + + #include +-#include ++#include + #include + #include + #include +@@ -25,7 +25,6 @@ + + #define DEVICE_NAME "aspeed-lpc-snoop" + +-#define NUM_SNOOP_CHANNELS 2 + #define SNOOP_FIFO_SIZE 2048 + + #define HICR5 0x80 +@@ -36,6 +35,7 @@ + #define HICR6 0x84 + #define HICR6_STR_SNP0W BIT(0) + #define HICR6_STR_SNP1W BIT(1) ++#define HICR6_NFE_WA BIT(20) + #define SNPWADR 0x90 + #define SNPWADR_CH0_MASK GENMASK(15, 0) + #define SNPWADR_CH0_SHIFT 0 +@@ -57,7 +57,22 @@ + unsigned int has_hicrb_ensnp; + }; + ++enum aspeed_lpc_snoop_index { ++ ASPEED_LPC_SNOOP_INDEX_0 = 0, ++ ASPEED_LPC_SNOOP_INDEX_1 = 1, ++ ASPEED_LPC_SNOOP_INDEX_MAX = ASPEED_LPC_SNOOP_INDEX_1, +}; + -+struct kmem_cache *packet_cache; -+ -+static void data_dump(struct aspeed_mctp *priv, struct mctp_pcie_packet_data *data) -+{ -+ int i; -+ -+ dev_dbg(priv->dev, "Address %zu", (size_t)data); -+ dev_dbg(priv->dev, "VDM header:"); -+ for (i = 0; i < PCIE_VDM_HDR_SIZE_DW; i++) { -+ dev_dbg(priv->dev, "%02x %02x %02x %02x", data->hdr[i] & 0xff, -+ (data->hdr[i] >> 8) & 0xff, (data->hdr[i] >> 16) & 0xff, -+ (data->hdr[i] >> 24) & 0xff); -+ } -+ dev_dbg(priv->dev, "Data payload:"); -+ for (i = 0; i < PCIE_VDM_DATA_SIZE_DW; i++) { -+ dev_dbg(priv->dev, "%02x %02x %02x %02x", -+ data->payload[i] & 0xff, (data->payload[i] >> 8) & 0xff, -+ (data->payload[i] >> 16) & 0xff, -+ (data->payload[i] >> 24) & 0xff); -+ } -+} -+ -+void *aspeed_mctp_packet_alloc(gfp_t flags) -+{ -+ return kmem_cache_alloc(packet_cache, flags); -+} -+EXPORT_SYMBOL_GPL(aspeed_mctp_packet_alloc); ++struct aspeed_lpc_snoop_channel_cfg { ++ enum aspeed_lpc_snoop_index index; ++ u32 hicr5_en; ++ u32 snpwadr_mask; ++ u32 snpwadr_shift; ++ u32 hicrb_en; ++}; + -+void aspeed_mctp_packet_free(void *packet) -+{ -+ kmem_cache_free(packet_cache, packet); -+} -+EXPORT_SYMBOL_GPL(aspeed_mctp_packet_free); + struct aspeed_lpc_snoop_channel { ++ const struct aspeed_lpc_snoop_channel_cfg *cfg; + bool enabled; + struct kfifo fifo; + wait_queue_head_t wq; +@@ -67,10 +82,28 @@ + struct aspeed_lpc_snoop { + struct regmap *regmap; + int irq; +- struct clk *clk; +- struct aspeed_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS]; ++ struct aspeed_lpc_snoop_channel chan[ASPEED_LPC_SNOOP_INDEX_MAX + 1]; + }; + ++static const struct aspeed_lpc_snoop_channel_cfg channel_cfgs[ASPEED_LPC_SNOOP_INDEX_MAX + 1] = { ++ { ++ .index = ASPEED_LPC_SNOOP_INDEX_0, ++ .hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W, ++ .snpwadr_mask = SNPWADR_CH0_MASK, ++ .snpwadr_shift = SNPWADR_CH0_SHIFT, ++ .hicrb_en = HICRB_ENSNP0D, ++ }, ++ { ++ .index = ASPEED_LPC_SNOOP_INDEX_1, ++ .hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W, ++ .snpwadr_mask = SNPWADR_CH1_MASK, ++ .snpwadr_shift = SNPWADR_CH1_SHIFT, ++ .hicrb_en = HICRB_ENSNP1D, ++ }, ++}; + -+static int _get_bdf(struct aspeed_mctp *priv) -+{ -+ u32 reg; -+ u16 bdf, devfn; ++static DEFINE_IDA(aspeed_lpc_snoop_ida); + -+ if (priv->match_data->dma_need_64bits_width) { -+ regmap_read(priv->pcie.map, ASPEED_G7_PCIE_LOCATE, ®); -+ if (!(reg & PCIE_LOCATE_IO)) { -+ regmap_read(priv->pcie.map, ASPEED_G7_PCIE_LINK, ®); -+ if (!(reg & PCIE_G7_LINK_STS)) -+ return -ENETDOWN; -+ regmap_read(priv->map, ASPEED_G7_MCTP_PCIE_BDF, ®); -+ bdf = PCI_DEVID(PCI_BUS_NUM(reg), reg & 0xff); -+ } else { -+ regmap_read(priv->pcie.map, ASPEED_G7_IO_PCIE_LINK, -+ ®); -+ if (!(reg & PCIE_G7_IO_LINK_STS)) -+ return -ENETDOWN; -+ regmap_read(priv->map, ASPEED_G7_MCTP_PCIE_BDF, ®); -+ bdf = PCI_DEVID(PCI_BUS_NUM(reg), reg & 0xff); -+ } + static struct aspeed_lpc_snoop_channel *snoop_file_to_chan(struct file *file) + { + return container_of(file->private_data, +@@ -136,12 +169,19 @@ + return IRQ_NONE; + + /* Check if one of the snoop channels is interrupting */ +- reg &= (HICR6_STR_SNP0W | HICR6_STR_SNP1W); +- if (!reg) ++ if (!(reg & (HICR6_STR_SNP0W | HICR6_STR_SNP1W))) + return IRQ_NONE; + +- /* Ack pending IRQs */ +- regmap_write(lpc_snoop->regmap, HICR6, reg); ++ /* Check if NFE WA is set */ ++ if (reg & HICR6_NFE_WA) { ++ /* Ack pending IRQs with keeping NFE WA */ ++ regmap_write(lpc_snoop->regmap, HICR6, ++ (HICR6_STR_SNP0W | HICR6_STR_SNP1W | HICR6_NFE_WA)); + } else { -+ regmap_read(priv->pcie.map, ASPEED_PCIE_LINK, ®); -+ if (!(reg & PCIE_LINK_STS)) -+ return -ENETDOWN; -+ regmap_read(priv->pcie.map, ASPEED_PCIE_MISC_STS_1, ®); -+ -+ reg = reg & (PCI_BUS_NUM_MASK | PCI_DEV_NUM_MASK); -+ /* only support function 0 */ -+ devfn = GET_PCI_DEV_NUM(reg) << 3; -+ bdf = PCI_DEVID(GET_PCI_BUS_NUM(reg), devfn); ++ /* Ack pending IRQs */ ++ regmap_write(lpc_snoop->regmap, HICR6, ++ (HICR6_STR_SNP0W | HICR6_STR_SNP1W)); + } + + /* Read and save most recent snoop'ed data byte to FIFO */ + regmap_read(lpc_snoop->regmap, SNPWDR, &data); +@@ -182,108 +222,92 @@ + return 0; + } + +-static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop, +- struct device *dev, +- int channel, u16 lpc_port) ++__attribute__((nonnull)) ++static int aspeed_lpc_enable_snoop(struct device *dev, ++ struct aspeed_lpc_snoop *lpc_snoop, ++ struct aspeed_lpc_snoop_channel *channel, ++ const struct aspeed_lpc_snoop_channel_cfg *cfg, ++ u16 lpc_port) + { +- int rc = 0; +- u32 hicr5_en, snpwadr_mask, snpwadr_shift, hicrb_en; +- const struct aspeed_lpc_snoop_model_data *model_data = +- of_device_get_match_data(dev); ++ const struct aspeed_lpc_snoop_model_data *model_data; ++ int rc = 0, id; + +- if (WARN_ON(lpc_snoop->chan[channel].enabled)) ++ if (WARN_ON(channel->enabled)) + return -EBUSY; + +- init_waitqueue_head(&lpc_snoop->chan[channel].wq); +- /* Create FIFO datastructure */ +- rc = kfifo_alloc(&lpc_snoop->chan[channel].fifo, +- SNOOP_FIFO_SIZE, GFP_KERNEL); ++ init_waitqueue_head(&channel->wq); + -+ return bdf; -+} ++ channel->cfg = cfg; ++ channel->miscdev.minor = MISC_DYNAMIC_MINOR; ++ channel->miscdev.fops = &snoop_fops; ++ channel->miscdev.parent = dev; + -+static uint32_t chip_version(struct device *dev) -+{ -+ struct regmap *scu; -+ u32 revid0, revid1; ++ id = ida_alloc(&aspeed_lpc_snoop_ida, GFP_KERNEL); ++ if (id < 0) ++ return id; + -+ scu = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu"); -+ if (IS_ERR(scu)) { -+ dev_err(dev, "failed to find 2600 SCU regmap\n"); -+ return PTR_ERR(scu); -+ } -+ regmap_read(scu, ASPEED_REVISION_ID0, &revid0); -+ regmap_read(scu, ASPEED_REVISION_ID1, &revid1); -+ if (revid0 == ID0_AST2600A3 && revid1 == ID1_AST2600A3) { -+ /* AST2600-A3 */ -+ return ASPEED_MCTP_2600A3; -+ } else if (revid0 == ID0_AST2620A3 && revid1 == ID1_AST2620A3) { -+ /* AST2620-A3 */ -+ return ASPEED_MCTP_2600A3; -+ } else if (revid0 == ID0_AST2605A3 && revid1 == ID1_AST2605A3) { -+ /* AST2605-A3 */ -+ return ASPEED_MCTP_2600A3; -+ } else if (revid0 == ID0_AST2625A3 && revid1 == ID1_AST2625A3) { -+ /* AST2605-A3 */ -+ return ASPEED_MCTP_2600A3; -+ } -+ return ASPEED_MCTP_2600; ++ channel->miscdev.name = ++ devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, id); ++ if (!channel->miscdev.name) ++ return -ENOMEM; ++ ++ rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL); + if (rc) + return rc; + +- lpc_snoop->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR; +- lpc_snoop->chan[channel].miscdev.name = +- devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel); +- if (!lpc_snoop->chan[channel].miscdev.name) { +- rc = -ENOMEM; +- goto err_free_fifo; +- } +- lpc_snoop->chan[channel].miscdev.fops = &snoop_fops; +- lpc_snoop->chan[channel].miscdev.parent = dev; +- rc = misc_register(&lpc_snoop->chan[channel].miscdev); ++ rc = misc_register(&channel->miscdev); + if (rc) + goto err_free_fifo; + + /* Enable LPC snoop channel at requested port */ +- switch (channel) { +- case 0: +- hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W; +- snpwadr_mask = SNPWADR_CH0_MASK; +- snpwadr_shift = SNPWADR_CH0_SHIFT; +- hicrb_en = HICRB_ENSNP0D; +- break; +- case 1: +- hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W; +- snpwadr_mask = SNPWADR_CH1_MASK; +- snpwadr_shift = SNPWADR_CH1_SHIFT; +- hicrb_en = HICRB_ENSNP1D; +- break; +- default: +- rc = -EINVAL; +- goto err_misc_deregister; +- } +- +- regmap_update_bits(lpc_snoop->regmap, HICR5, hicr5_en, hicr5_en); +- regmap_update_bits(lpc_snoop->regmap, SNPWADR, snpwadr_mask, +- lpc_port << snpwadr_shift); +- if (model_data->has_hicrb_ensnp) +- regmap_update_bits(lpc_snoop->regmap, HICRB, +- hicrb_en, hicrb_en); ++ regmap_set_bits(lpc_snoop->regmap, HICR5, cfg->hicr5_en); ++ regmap_update_bits(lpc_snoop->regmap, SNPWADR, cfg->snpwadr_mask, ++ lpc_port << cfg->snpwadr_shift); ++ ++ model_data = of_device_get_match_data(dev); ++ if (model_data && model_data->has_hicrb_ensnp) ++ regmap_set_bits(lpc_snoop->regmap, HICRB, cfg->hicrb_en); + +- lpc_snoop->chan[channel].enabled = true; ++ channel->enabled = true; + + return 0; + +-err_misc_deregister: +- misc_deregister(&lpc_snoop->chan[channel].miscdev); + err_free_fifo: +- kfifo_free(&lpc_snoop->chan[channel].fifo); ++ kfifo_free(&channel->fifo); + return rc; + } + ++__attribute__((nonnull)) + static void aspeed_lpc_disable_snoop(struct aspeed_lpc_snoop *lpc_snoop, +- int channel) ++ struct aspeed_lpc_snoop_channel *channel) + { +- if (!lpc_snoop->chan[channel].enabled) ++ if (!channel->enabled) + return; + +- switch (channel) { +- case 0: +- regmap_update_bits(lpc_snoop->regmap, HICR5, +- HICR5_EN_SNP0W | HICR5_ENINT_SNP0W, +- 0); +- break; +- case 1: +- regmap_update_bits(lpc_snoop->regmap, HICR5, +- HICR5_EN_SNP1W | HICR5_ENINT_SNP1W, +- 0); +- break; +- default: +- return; +- } ++ /* Disable interrupts along with the device */ ++ regmap_clear_bits(lpc_snoop->regmap, HICR5, channel->cfg->hicr5_en); + +- lpc_snoop->chan[channel].enabled = false; ++ channel->enabled = false; + /* Consider improving safety wrt concurrent reader(s) */ +- misc_deregister(&lpc_snoop->chan[channel].miscdev); +- kfifo_free(&lpc_snoop->chan[channel].fifo); ++ misc_deregister(&channel->miscdev); ++ kfifo_free(&channel->fifo); +} + -+static int pcie_vdm_enable(struct device *dev) ++static void aspeed_lpc_snoop_remove(struct platform_device *pdev) +{ -+ int ret = 0; -+ struct regmap *scu; -+ const struct aspeed_mctp_match_data *match_data = -+ of_device_get_match_data(dev); ++ struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev); + -+ scu = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu"); -+ if (IS_ERR(scu)) { -+ dev_err(dev, "failed to find SCU regmap\n"); -+ return PTR_ERR(scu); -+ } -+ ret = regmap_update_bits(scu, match_data->scu_pcie_ctrl_offset, -+ ASPEED_G7_SCU_PCIE_CTRL_VDM_EN, -+ ASPEED_G7_SCU_PCIE_CTRL_VDM_EN); -+ return ret; -+} ++ /* Disable both snoop channels */ ++ aspeed_lpc_disable_snoop(lpc_snoop, &lpc_snoop->chan[0]); ++ aspeed_lpc_disable_snoop(lpc_snoop, &lpc_snoop->chan[1]); + } + + static int aspeed_lpc_snoop_probe(struct platform_device *pdev) + { + struct aspeed_lpc_snoop *lpc_snoop; +- struct device *dev; + struct device_node *np; +- u32 port; ++ struct device *dev; ++ int idx; + int rc; + + dev = &pdev->dev; +@@ -293,77 +317,40 @@ + return -ENOMEM; + + np = pdev->dev.parent->of_node; +- if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") && +- !of_device_is_compatible(np, "aspeed,ast2500-lpc-v2") && +- !of_device_is_compatible(np, "aspeed,ast2600-lpc-v2")) { +- dev_err(dev, "unsupported LPC device binding\n"); +- return -ENODEV; +- } + + lpc_snoop->regmap = syscon_node_to_regmap(np); +- if (IS_ERR(lpc_snoop->regmap)) { +- dev_err(dev, "Couldn't get regmap\n"); +- return -ENODEV; +- } ++ if (IS_ERR(lpc_snoop->regmap)) ++ return dev_err_probe(dev, PTR_ERR(lpc_snoop->regmap), "Couldn't get regmap\n"); + + dev_set_drvdata(&pdev->dev, lpc_snoop); + +- rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port); +- if (rc) { +- dev_err(dev, "no snoop ports configured\n"); +- return -ENODEV; +- } +- +- lpc_snoop->clk = devm_clk_get(dev, NULL); +- if (IS_ERR(lpc_snoop->clk)) { +- rc = PTR_ERR(lpc_snoop->clk); +- if (rc != -EPROBE_DEFER) +- dev_err(dev, "couldn't get clock\n"); +- return rc; +- } +- rc = clk_prepare_enable(lpc_snoop->clk); +- if (rc) { +- dev_err(dev, "couldn't enable clock\n"); +- return rc; +- } +- + rc = aspeed_lpc_snoop_config_irq(lpc_snoop, pdev); + if (rc) +- goto err; ++ return rc; + +- rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 0, port); +- if (rc) +- goto err; ++ static_assert(ARRAY_SIZE(channel_cfgs) == ARRAY_SIZE(lpc_snoop->chan), ++ "Broken implementation assumption regarding cfg count"); ++ for (idx = ASPEED_LPC_SNOOP_INDEX_0; idx <= ASPEED_LPC_SNOOP_INDEX_MAX; idx++) { ++ u32 port; + ++ rc = of_property_read_u32_index(dev->of_node, "snoop-ports", idx, &port); ++ if (rc) ++ break; + +- /* Configuration of 2nd snoop channel port is optional */ +- if (of_property_read_u32_index(dev->of_node, "snoop-ports", +- 1, &port) == 0) { +- rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 1, port); +- if (rc) { +- aspeed_lpc_disable_snoop(lpc_snoop, 0); +- goto err; +- } ++ rc = aspeed_lpc_enable_snoop(dev, lpc_snoop, &lpc_snoop->chan[idx], ++ &channel_cfgs[idx], port); ++ if (rc) ++ goto cleanup_channels; + } + +- return 0; ++ return idx == ASPEED_LPC_SNOOP_INDEX_0 ? -ENODEV : 0; + +-err: +- clk_disable_unprepare(lpc_snoop->clk); ++cleanup_channels: ++ aspeed_lpc_snoop_remove(pdev); + + return rc; + } + +-static void aspeed_lpc_snoop_remove(struct platform_device *pdev) +-{ +- struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev); +- +- /* Disable both snoop channels */ +- aspeed_lpc_disable_snoop(lpc_snoop, 0); +- aspeed_lpc_disable_snoop(lpc_snoop, 1); +- +- clk_disable_unprepare(lpc_snoop->clk); +-} +- + static const struct aspeed_lpc_snoop_model_data ast2400_model_data = { + .has_hicrb_ensnp = 0, + }; +@@ -388,7 +375,7 @@ + .of_match_table = aspeed_lpc_snoop_match, + }, + .probe = aspeed_lpc_snoop_probe, +- .remove_new = aspeed_lpc_snoop_remove, ++ .remove = aspeed_lpc_snoop_remove, + }; + + module_platform_driver(aspeed_lpc_snoop_driver); +diff --git a/drivers/soc/aspeed/aspeed-mbox.c b/drivers/soc/aspeed/aspeed-mbox.c +--- a/drivers/soc/aspeed/aspeed-mbox.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-mbox.c 2026-04-08 18:03:48.310705320 +0000 +@@ -0,0 +1,312 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later +/* -+ * HW produces and expects VDM header in little endian and payload in network order. -+ * To allow userspace to use network order for the whole packet, PCIe VDM header needs -+ * to be swapped. ++ * Copyright Aspeed Technology Inc. (C) 2025. All rights reserved + */ -+static void aspeed_mctp_swap_pcie_vdm_hdr(struct mctp_pcie_packet_data *data) -+{ -+ int i; + -+ for (i = 0; i < PCIE_VDM_HDR_SIZE_DW; i++) -+ data->hdr[i] = swab32(data->hdr[i]); -+} ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+static void aspeed_mctp_rx_trigger(struct mctp_channel *rx) -+{ -+ struct aspeed_mctp *priv = container_of(rx, typeof(*priv), rx); -+ u32 reg; ++#define __ASPEED_MBOX_IOCTL_MAGIC 'X' ++#define ASPEED_MBOX_IOCTL_CAPS _IOR(__ASPEED_MBOX_IOCTL_MAGIC, 0, uint32_t[4]) ++#define ASPEED_MBOX_IOCTL_SEND _IOW(__ASPEED_MBOX_IOCTL_MAGIC, 1, uint32_t[8]) ++#define ASPEED_MBOX_IOCTL_RECV _IOR(__ASPEED_MBOX_IOCTL_MAGIC, 2, uint32_t[8]) + -+ /* -+ * Even though rx_buf_addr doesn't change, if we don't do the write -+ * here, the HW doesn't trigger RX. We're also clearing the -+ * RX_CMD_READY bit, otherwise we're observing a rare case where -+ * trigger isn't registered by the HW, and we're ending up with stuck -+ * HW (not reacting to wr_ptr writes). -+ * Also, note that we're writing 0 as wr_ptr. If we're writing other -+ * value, the HW behaves in a bizarre way that's hard to explain... -+ */ -+ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, RX_CMD_READY, 0); -+ if (priv->match_data->fifo_auto_surround) { -+ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_ADDR, -+ rx->cmd.dma_handle); -+ if (priv->match_data->dma_need_64bits_width) -+ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_HI_ADDR, -+ upper_32_bits(rx->cmd.dma_handle)); -+ } else { -+ regmap_read(priv->map, ASPEED_MCTP_RX_BUF_ADDR, ®); -+ if (!reg) { -+ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_ADDR, -+ rx->cmd.dma_handle); -+ } else if (reg == (rx->cmd.dma_handle & GENMASK(28, 3))) { -+ dev_info(priv->dev, -+ "Already initialized - skipping rx dma set\n"); -+ } else { -+ dev_err(priv->dev, -+ "The memory of rx dma can't be changed after the controller is activated\n"); -+ return; -+ } -+ } -+ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_WR_PTR, 0); ++/** ++ * struct aspeed_mem - Description of a ASPEED memory buffer ++ * @buf: Shared memory base address ++ * @size: Shared memory byte size ++ */ ++struct aspeed_mem { ++ void __iomem *buf; ++ phys_addr_t phys_addr; ++ resource_size_t size; ++}; + -+ /* After re-enabling RX we need to restart WA logic */ -+ if (priv->rx_runaway_wa.enable) -+ priv->rx.buffer_count = priv->rx_packet_count; -+ /* -+ * When Rx warmup MCTP controller may store first packet into the 0th to the -+ * 3rd cmd. In ast2600 A3, If the packet isn't stored in the 0th cmd we need -+ * to change the rx buffer size to avoid rx runaway in first loop. In ast2600 -+ * A1/A2, after first loop hardware is guaranteed to use (RX_PACKET_COUNT - 4) -+ * buffers. -+ */ -+ priv->rx_warmup = true; -+ priv->rx_runaway_wa.first_loop = true; -+ priv->rx_runaway_wa.packet_counter = 0; ++/* ++ * Message prototype of IPC. It should be defined per your usage. ++ * cmd: type of message ++ * len: length of message ++ */ ++struct ast_mbox_msg { ++ u32 cmd; ++ u32 len; ++}; + -+ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, RX_CMD_READY, -+ RX_CMD_READY); -+} ++/* ++ * struct ast_mbox_chan - Description of a ASPEED mailbox channel ++ * @cl: Mailbox client ++ * @mdev: Misc device ++ * @chan: Mailbox channel ++ * @tx_base: Information of tx shmem ++ * @rx_base: Information of rx shmem ++ * @rx_buffer: buffer to store data on callback ++ * @rx_wait: Wait queue for receiving messages ++ * @rx_msg_lock: Spinlock to protect rx_buffer ++ */ ++struct ast_mbox_info { ++ struct device *dev; ++ struct mbox_client cl; ++ struct miscdevice mdev; ++ struct mbox_chan *chan; ++ struct aspeed_mem tx_base; ++ struct aspeed_mem rx_base; ++ char *rx_buffer; ++ wait_queue_head_t rx_wait; ++ spinlock_t rx_msg_lock; /* spinlock to protect rx_buffer */ ++}; + -+static void aspeed_mctp_tx_trigger(struct mctp_channel *tx, bool notify) ++static ssize_t mbox_read(struct file *fp, char __user *buf, size_t nbytes, loff_t *off) +{ -+ struct aspeed_mctp *priv = container_of(tx, typeof(*priv), tx); -+ u32 ctrl_val; ++ struct ast_mbox_info *info = container_of(fp->private_data, struct ast_mbox_info, mdev); + int ret; + -+ if (notify) { -+ if (priv->match_data->dma_need_64bits_width) { -+ struct aspeed_g7_mctp_tx_cmd *last_cmd; -+ -+ last_cmd = (struct aspeed_g7_mctp_tx_cmd *)tx->cmd.vaddr + -+ (tx->wr_ptr - 1) % TX_PACKET_COUNT; -+ last_cmd->tx_lo |= TX_INTERRUPT_AFTER_CMD; -+ } else { -+ struct aspeed_mctp_tx_cmd *last_cmd; ++ if (nbytes == 0) ++ return 0; + -+ last_cmd = (struct aspeed_mctp_tx_cmd *)tx->cmd.vaddr + -+ (tx->wr_ptr - 1) % TX_PACKET_COUNT; -+ last_cmd->tx_lo |= TX_INTERRUPT_AFTER_CMD; -+ } ++ if (!info->rx_base.buf) { ++ dev_err(info->dev, "No RX shmem\n"); ++ return -EINVAL; + } -+ if (priv->match_data->fifo_auto_surround) -+ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_WR_PTR, tx->wr_ptr); -+ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, TX_CMD_TRIGGER, -+ TX_CMD_TRIGGER); -+ ret = regmap_read_poll_timeout_atomic(priv->map, ASPEED_MCTP_CTRL, -+ ctrl_val, -+ !(ctrl_val & TX_CMD_TRIGGER), 0, -+ 1000000); -+ if (ret) { -+ u32 rd_ptr, wr_ptr; + -+ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_RD_PTR, UPDATE_RX_RD_PTR); -+ regmap_read(priv->map, ASPEED_MCTP_TX_BUF_RD_PTR, &rd_ptr); -+ rd_ptr &= RX_BUF_RD_PTR_MASK; -+ regmap_read(priv->map, ASPEED_MCTP_TX_BUF_WR_PTR, &wr_ptr); -+ wr_ptr &= TX_BUF_RD_PTR_MASK; -+ dev_warn(priv->dev, -+ "Wait tx completed timeout rd_ptr = %x, wr_ptr = %x\n", -+ rd_ptr, wr_ptr); -+ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, TX_CMD_TRIGGER, -+ 0); ++ if (nbytes > info->rx_base.size) { ++ dev_warn(info->dev, "Read size %zu exceeds RX shmem size %llu\n", ++ nbytes, info->rx_base.size); ++ nbytes = info->rx_base.size; + } -+} -+ -+static void aspeed_mctp_tx_cmd_prep(u32 *tx_hdr, struct aspeed_mctp_tx_cmd *tx_cmd) -+{ -+ u32 packet_size, target_id; -+ u8 dest_eid, padding_len, routing_type, tag_owner; + -+ packet_size = FIELD_GET(MCTP_HDR_DW_LE_PACKET_SIZE, tx_hdr[0]); -+ routing_type = FIELD_GET(MCTP_HDR_DW_LE_ROUTING_TYPE, tx_hdr[0]); -+ routing_type = routing_type ? routing_type - 1 : 0; -+ padding_len = FIELD_GET(MCTP_HDR_DW_LE_PADDING_LEN, tx_hdr[1]); -+ target_id = FIELD_GET(MCTP_HDR_DW_LE_TARGET_ID, tx_hdr[2]); -+ tag_owner = FIELD_GET(MCTP_HDR_DW_LE_TAG_OWNER, tx_hdr[3]); -+ dest_eid = FIELD_GET(MCTP_HDR_DW_LE_DEST_EID, tx_hdr[3]); ++ ret = copy_to_user((void __user *)buf, info->rx_base.buf, nbytes); ++ if (ret) ++ return -EFAULT; + -+ tx_cmd->tx_hi = FIELD_PREP(TX_PACKET_DEST_EID, dest_eid); -+ tx_cmd->tx_lo = FIELD_PREP(TX_PACKET_TARGET_ID, target_id) | -+ TX_INTERRUPT_AFTER_CMD | -+ FIELD_PREP(TX_PACKET_ROUTING_TYPE, routing_type) | -+ FIELD_PREP(TX_PACKET_TAG_OWNER, tag_owner) | -+ TX_PACKET_SIZE_2500(packet_size) | -+ FIELD_PREP(TX_PACKET_PADDING_LEN, padding_len); ++ return nbytes; +} + -+static void aspeed_mctp_emit_tx_cmd(struct mctp_channel *tx, -+ struct mctp_pcie_packet *packet) ++static ssize_t mbox_write(struct file *fp, const char *buf, size_t nbytes, loff_t *off) +{ -+ struct aspeed_mctp *priv = container_of(tx, typeof(*priv), tx); -+ struct aspeed_mctp_tx_cmd *tx_cmd = -+ (struct aspeed_mctp_tx_cmd *)tx->cmd.vaddr + tx->wr_ptr; -+ struct aspeed_g7_mctp_tx_cmd *tx_cmd_g7 = -+ (struct aspeed_g7_mctp_tx_cmd *)tx->cmd.vaddr + tx->wr_ptr; -+ u32 packet_sz_dw = packet->size / sizeof(u32) - -+ sizeof(packet->data.hdr) / sizeof(u32); -+ u32 offset; ++ struct ast_mbox_info *info = container_of(fp->private_data, struct ast_mbox_info, mdev); ++ int ret; + -+ data_dump(priv, &packet->data); -+ aspeed_mctp_swap_pcie_vdm_hdr(&packet->data); ++ if (nbytes == 0) ++ return 0; + -+ if (priv->match_data->vdm_hdr_direct_xfer) { -+ offset = tx->wr_ptr * sizeof(packet->data); -+ memcpy((u8 *)tx->data.vaddr + offset, &packet->data, -+ sizeof(packet->data)); -+ if (priv->match_data->dma_need_64bits_width) { -+ tx_cmd_g7->tx_lo = TX_PACKET_SIZE(packet_sz_dw); -+ tx_cmd_g7->tx_mid = TX_RESERVED_1; -+ tx_cmd_g7->tx_mid |= ((tx->data.dma_handle + offset) & -+ GENMASK(31, 4)); -+ tx_cmd_g7->tx_hi = upper_32_bits((tx->data.dma_handle + offset)); -+ } else { -+ tx_cmd->tx_lo = TX_PACKET_SIZE(packet_sz_dw); -+ tx_cmd->tx_hi = TX_RESERVED_1; -+ tx_cmd->tx_hi |= TX_DATA_ADDR(tx->data.dma_handle + offset); -+ } -+ } else { -+ offset = tx->wr_ptr * sizeof(struct mctp_pcie_packet_data_2500); -+ memcpy((u8 *)tx->data.vaddr + offset, packet->data.payload, -+ sizeof(packet->data.payload)); -+ aspeed_mctp_tx_cmd_prep(packet->data.hdr, tx_cmd); -+ tx_cmd->tx_hi |= TX_DATA_ADDR_2500(tx->data.dma_handle + offset); -+ if (tx->wr_ptr == TX_PACKET_COUNT - 1) -+ tx_cmd->tx_hi |= TX_LAST_CMD; ++ if (!info->tx_base.buf) { ++ dev_err(info->dev, "No TX shmem\n"); ++ return -EINVAL; + } -+ dev_dbg(priv->dev, "tx->wr_prt: %d, tx_cmd: hi:%08x lo:%08x\n", -+ tx->wr_ptr, tx_cmd->tx_hi, tx_cmd->tx_lo); + -+ tx->wr_ptr = (tx->wr_ptr + 1) % TX_PACKET_COUNT; ++ if (nbytes > info->tx_base.size) { ++ dev_warn(info->dev, "Write size %zu exceeds TX shmem size %llu\n", ++ nbytes, info->tx_base.size); ++ nbytes = info->tx_base.size; ++ } ++ ++ ret = copy_from_user(info->tx_base.buf, (void __user *)buf, nbytes); ++ if (ret) ++ return -EFAULT; ++ ++ return nbytes; +} + -+static struct mctp_client *aspeed_mctp_client_alloc(struct aspeed_mctp *priv) ++static __poll_t mbox_poll(struct file *fp, struct poll_table_struct *wait) +{ -+ struct mctp_client *client; ++ struct ast_mbox_info *info = container_of(fp->private_data, struct ast_mbox_info, mdev); ++ __poll_t mask = 0; + -+ client = kzalloc(sizeof(*client), GFP_KERNEL); -+ if (!client) -+ goto out; ++ poll_wait(fp, &info->rx_wait, wait); + -+ kref_init(&client->ref); -+ client->priv = priv; -+ ptr_ring_init(&client->tx_queue, priv->tx_ring_count, GFP_KERNEL); -+ ptr_ring_init(&client->rx_queue, priv->rx_ring_count, GFP_ATOMIC); ++ if (info->rx_buffer) ++ mask |= POLLIN | POLLRDNORM; + -+out: -+ return client; ++ return mask; +} + -+static void aspeed_mctp_client_free(struct kref *ref) ++static long mbox_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ -+ struct mctp_client *client = container_of(ref, typeof(*client), ref); -+ -+ ptr_ring_cleanup(&client->rx_queue, &aspeed_mctp_packet_free); -+ ptr_ring_cleanup(&client->tx_queue, &aspeed_mctp_packet_free); ++ struct ast_mbox_info *info = container_of(fp->private_data, struct ast_mbox_info, mdev); ++ u32 *data; ++ u32 msg[8]; ++ unsigned long flags; ++ int ret = 0; + -+ kfree(client); -+} ++ switch (cmd) { ++ case ASPEED_MBOX_IOCTL_CAPS: ++ data = (u32 *)arg; ++ data[0] = (info->tx_base.phys_addr) >> 8; ++ data[1] = info->tx_base.size; ++ data[2] = (info->rx_base.phys_addr) >> 8; ++ data[3] = info->rx_base.size; ++ break; ++ case ASPEED_MBOX_IOCTL_SEND: ++ ret = copy_from_user(msg, (void __user *)arg, sizeof(msg)); ++ if (ret) { ++ dev_dbg(info->dev, "Failed to copy message from user\n"); ++ return -EFAULT; ++ } + -+static void aspeed_mctp_client_get(struct mctp_client *client) -+{ -+ lockdep_assert_held(&client->priv->clients_lock); ++ ret = mbox_send_message(info->chan, msg); ++ if (ret < 0) ++ dev_dbg(info->dev, "Failed to send message via mailbox\n"); ++ break; ++ case ASPEED_MBOX_IOCTL_RECV: ++ spin_lock_irqsave(&info->rx_msg_lock, flags); ++ if (!info->rx_buffer) { ++ spin_unlock_irqrestore(&info->rx_msg_lock, flags); ++ dev_dbg(info->dev, "No message received\n"); ++ return -EAGAIN; ++ } ++ ret = copy_to_user((void __user *)arg, info->rx_buffer, sizeof(msg)); ++ info->rx_buffer = NULL; ++ spin_unlock_irqrestore(&info->rx_msg_lock, flags); ++ if (ret) { ++ dev_dbg(info->dev, "Failed to copy message to user\n"); ++ return -EFAULT; ++ } ++ break; ++ default: ++ dev_err(info->dev, "Unsupported cmd: %x\n", cmd); ++ ret = -EINVAL; ++ } + -+ kref_get(&client->ref); ++ return ret; +} + -+static void aspeed_mctp_client_put(struct mctp_client *client) -+{ -+ kref_put(&client->ref, &aspeed_mctp_client_free); -+} ++static const struct file_operations aspeed_mbox_fops = { ++ .owner = THIS_MODULE, ++ .read = mbox_read, ++ .write = mbox_write, ++ .poll = mbox_poll, ++ .unlocked_ioctl = mbox_ioctl, ++}; + -+static struct mctp_client * -+aspeed_mctp_find_handler(struct aspeed_mctp *priv, -+ struct mctp_pcie_packet *packet) ++static void mbox_rx_callback(struct mbox_client *client, void *message) +{ -+ struct mctp_type_handler *handler; -+ u8 *payload = (u8 *)packet->data.payload; -+ struct mctp_client *client = NULL; -+ u8 mctp_type; -+ u16 vendor = 0; -+ u16 vdm_type = 0; -+ -+ lockdep_assert_held(&priv->clients_lock); ++ struct ast_mbox_info *info = container_of(client, struct ast_mbox_info, cl); ++ unsigned long flags; + -+ mctp_type = payload[MCTP_PAYLOAD_TYPE_OFFSET]; -+ if (mctp_type == MCTP_HDR_TYPE_VDM_PCI) { -+ vendor = *((u16 *)&payload[MCTP_PAYLOAD_VENDOR_OFFSET]); -+ vdm_type = *((u16 *)&payload[MCTP_PAYLOAD_VDM_TYPE_OFFSET]); -+ } ++ spin_lock_irqsave(&info->rx_msg_lock, flags); ++ info->rx_buffer = message; ++ spin_unlock_irqrestore(&info->rx_msg_lock, flags); + -+ list_for_each_entry(handler, &priv->mctp_type_handlers, link) { -+ if (handler->mctp_type == mctp_type && -+ handler->pci_vendor_id == vendor && -+ handler->vdm_type == (vdm_type & handler->vdm_mask)) { -+ dev_dbg(priv->dev, "Found client for type %x vdm %x\n", -+ mctp_type, handler->vdm_type); -+ client = handler->client; -+ break; -+ } -+ } -+ return client; ++ wake_up_interruptible(&info->rx_wait); +} + -+static void aspeed_mctp_dispatch_packet(struct aspeed_mctp *priv, -+ struct mctp_pcie_packet *packet) ++static int aspeed_mbox_probe(struct platform_device *pdev) +{ -+ struct mctp_client *client; ++ struct ast_mbox_info *info; ++ struct resource *res; ++ struct device *dev = &pdev->dev; + int ret; ++ u32 tx_tout = -1; + -+ spin_lock(&priv->clients_lock); -+ -+ client = aspeed_mctp_find_handler(priv, packet); -+ -+ if (!client) -+ client = priv->default_client; -+ -+ if (client) -+ aspeed_mctp_client_get(client); -+ -+ spin_unlock(&priv->clients_lock); ++ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; + -+ if (client) { -+ ret = ptr_ring_produce(&client->rx_queue, packet); -+ if (ret) { -+ /* -+ * This can happen if client process does not -+ * consume packets fast enough -+ */ -+ dev_dbg(priv->dev, "Failed to store packet in client RX queue\n"); -+ aspeed_mctp_packet_free(packet); -+ } else { -+ wake_up_all(&client->wait_queue); -+ } -+#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM -+ mctp_pcie_vdm_receive_packet(priv->ndev); -+#endif -+ aspeed_mctp_client_put(client); -+ } else { -+ dev_dbg(priv->dev, "Failed to dispatch RX packet\n"); -+ aspeed_mctp_packet_free(packet); ++ info->tx_base.buf = devm_platform_get_and_ioremap_resource(pdev, 0, &res); ++ if (PTR_ERR(info->tx_base.buf) == -EBUSY) { ++ /* if reserved area in SRAM, try just ioremap */ ++ info->tx_base.size = resource_size(res); ++ info->tx_base.buf = devm_ioremap(dev, res->start, info->tx_base.size); + } -+} -+ -+static void aspeed_mctp_tx_tasklet(unsigned long data) -+{ -+ struct mctp_channel *tx = (struct mctp_channel *)data; -+ struct aspeed_mctp *priv = container_of(tx, typeof(*priv), tx); -+ struct mctp_client *client; -+ bool trigger = false; -+ bool full = false; -+ u32 rd_ptr; -+ -+ if (priv->match_data->fifo_auto_surround) { -+ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_RD_PTR, UPDATE_RX_RD_PTR); -+ regmap_read(priv->map, ASPEED_MCTP_TX_BUF_RD_PTR, &rd_ptr); -+ rd_ptr &= TX_BUF_RD_PTR_MASK; ++ if (IS_ERR(info->tx_base.buf)) { ++ info->tx_base.buf = NULL; + } else { -+ rd_ptr = tx->rd_ptr; ++ info->tx_base.phys_addr = res->start; ++ info->tx_base.size = resource_size(res); + } + -+ spin_lock(&priv->clients_lock); -+ -+ list_for_each_entry(client, &priv->clients, link) { -+ while (!(full = (tx->wr_ptr + 1) % TX_PACKET_COUNT == rd_ptr)) { -+ struct mctp_pcie_packet *packet; -+ -+ packet = ptr_ring_consume(&client->tx_queue); -+ if (!packet) -+ break; -+ -+ aspeed_mctp_emit_tx_cmd(tx, packet); -+ aspeed_mctp_packet_free(packet); -+ trigger = true; -+ } ++ info->rx_base.buf = devm_platform_get_and_ioremap_resource(pdev, 1, &res); ++ if (PTR_ERR(info->rx_base.buf) == -EBUSY) { ++ /* if reserved area in SRAM, try just ioremap */ ++ info->rx_base.size = resource_size(res); ++ info->rx_base.buf = devm_ioremap(dev, res->start, info->rx_base.size); ++ } ++ if (IS_ERR(info->rx_base.buf)) { ++ info->rx_base = info->tx_base; ++ } else { ++ info->rx_base.phys_addr = res->start; ++ info->rx_base.size = resource_size(res); + } + -+ spin_unlock(&priv->clients_lock); -+ -+ if (trigger) -+ aspeed_mctp_tx_trigger(tx, full); -+} -+ -+static void aspeed_mctp_rx_hdr_prep(struct aspeed_mctp *priv, u8 *hdr, u32 rx_lo) -+{ -+ u16 bdf; -+ u8 routing_type; -+ -+ /* -+ * MCTP controller will map the routing type to reduce one bit -+ * 0 (Route to RC) -> 0, -+ * 2 (Route by ID) -> 1, -+ * 3 (Broadcast from RC) -> 2 -+ */ -+ routing_type = FIELD_GET(RX_PACKET_ROUTING_TYPE, rx_lo); -+ routing_type = routing_type ? routing_type + 1 : 0; -+ bdf = _get_bdf(priv); -+ /* Length[7:0] */ -+ hdr[0] = FIELD_GET(RX_PACKET_SIZE, rx_lo); -+ /* TD:EP:ATTR[1:0]:R or AT[1:0]:Length[9:8] */ -+ hdr[1] = 0; -+ /* R or T9:TC[2:0]:R[3:0] */ -+ hdr[2] = 0; -+ /* R or Fmt[2]:Fmt[1:0]=b'11:Type[4:3]=b'10:Type[2:0] */ -+ hdr[3] = 0x70 | routing_type; -+ /* VDM message code = 0x7f */ -+ hdr[4] = 0x7f; -+ /* R[1:0]:Pad len[1:0]:MCTP VDM Code[3:0] */ -+ hdr[5] = FIELD_GET(RX_PACKET_PADDING_LEN, rx_lo) << 4; -+ /* TODO: PCI Requester ID: HW didn't get this information */ -+ hdr[6] = 0; -+ hdr[7] = 5; -+ /* Vendor ID: 0x1AB4 */ -+ hdr[8] = 0xb4; -+ hdr[9] = 0x1a; -+ /* PCI Target ID */ -+ hdr[10] = bdf & 0xff; -+ hdr[11] = bdf >> 8 & 0xff; -+ /* SOM:EOM:Pkt Seq#[1:0]:TO:Msg Tag[2:0]*/ -+ hdr[12] = FIELD_GET(RX_PACKET_SOM, rx_lo) << 7 | -+ FIELD_GET(RX_PACKET_EOM, rx_lo) << 6 | -+ FIELD_GET(RX_PACKET_SEQ_NUMBER, rx_lo) << 4 | -+ FIELD_GET(RX_PACKET_TAG_OWNER, rx_lo) << 3 | -+ FIELD_GET(RX_PACKET_MSG_TAG, rx_lo); -+ /* Source Endpoint ID */ -+ hdr[13] = FIELD_GET(RX_PACKET_SRC_EID, rx_lo); -+ /* Destination Endpoint ID: HW didn't get this information*/ -+ hdr[14] = priv->eid; -+ /* TODO: R[3:0]: header version[3:0] */ -+ hdr[15] = 1; -+} -+ -+static void aspeed_mctp_rx_tasklet(unsigned long data) -+{ -+ struct mctp_channel *rx = (struct mctp_channel *)data; -+ struct aspeed_mctp *priv = container_of(rx, typeof(*priv), rx); -+ struct mctp_pcie_packet *rx_packet; -+ struct aspeed_mctp_rx_cmd *rx_cmd; -+ u32 hw_read_ptr; -+ u32 *hdr, *payload; -+ bool rx_full; -+ -+ /* initialized as false */ -+ rx_full = false; -+ -+ if (priv->match_data->vdm_hdr_direct_xfer && priv->match_data->fifo_auto_surround) { -+ struct mctp_pcie_packet_data *rx_buf; -+ u32 residual_cmds = 0; -+ -+ /* Trigger HW read pointer update, must be done before RX loop */ -+ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_RD_PTR, UPDATE_RX_RD_PTR); -+ -+ /* -+ * rx->stopped indicates if rx ring is full or not. -+ * Use rx_full to note ring status before consuming packet. -+ */ -+ rx_full = rx->stopped; -+ -+ /* -+ * XXX: Using rd_ptr obtained from HW is unreliable so we need to -+ * maintain the state of buffer on our own by peeking into the buffer -+ * and checking where the packet was written. -+ */ -+ rx_buf = (struct mctp_pcie_packet_data *)rx->data.vaddr; -+ hdr = (u32 *)&rx_buf[rx->wr_ptr]; -+ if (!*hdr && priv->rx_warmup) { -+ u32 tmp_wr_ptr = rx->wr_ptr; -+ -+ /* -+ * HACK: Right after start the RX hardware can put received -+ * packet into an unexpected offset - in order to locate -+ * received packet driver has to scan all RX data buffers. -+ */ -+ do { -+ tmp_wr_ptr = (tmp_wr_ptr + 1) % priv->rx_packet_count; -+ -+ hdr = (u32 *)&rx_buf[tmp_wr_ptr]; -+ } while (!*hdr && tmp_wr_ptr != rx->wr_ptr); -+ -+ if (tmp_wr_ptr != rx->wr_ptr) { -+ dev_warn(priv->dev, -+ "Runaway RX packet found %d -> %d\n", -+ rx->wr_ptr, tmp_wr_ptr); -+ residual_cmds = abs(tmp_wr_ptr - rx->wr_ptr); -+ rx->wr_ptr = tmp_wr_ptr; -+ if (!priv->rx_runaway_wa.enable && -+ priv->rx_warmup) -+ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_SIZE, -+ rx->buffer_count - residual_cmds); -+ priv->rx_warmup = false; -+ } -+ } else { -+ priv->rx_warmup = false; -+ } -+ -+ if (priv->rx_runaway_wa.packet_counter > priv->rx_packet_count && -+ priv->rx_runaway_wa.first_loop) { -+ if (priv->rx_runaway_wa.enable) -+ /* -+ * Once we receive RX_PACKET_COUNT packets, hardware is -+ * guaranteed to use (RX_PACKET_COUNT - 4) buffers. Decrease -+ * buffer count by 4, then we can turn off scanning of RX -+ * buffers. RX buffer scanning should be enabled every time -+ * RX hardware is started. -+ * This is just a performance optimization - we could keep -+ * scanning RX buffers forever, but under heavy traffic it is -+ * fairly common that rx_tasklet is executed while RX buffer -+ * ring is empty. -+ */ -+ rx->buffer_count = priv->rx_packet_count - 4; -+ else -+ /* -+ * Once we receive RX_PACKET_COUNT packets, we need to restore the -+ * RX buffer size to 4 byte aligned value to avoid rx runaway. -+ */ -+ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_SIZE, -+ rx->buffer_count); -+ priv->rx_runaway_wa.first_loop = false; -+ } -+ -+ while (*hdr != 0) { -+ if (FIELD_GET(MCTP_HDR_DW_LE_PACKET_SIZE, hdr[0]) * 4 > -+ ASPEED_MCTP_MTU) -+ dev_warn(priv->dev, -+ "Rx length %ld > MTU size %d\n", -+ FIELD_GET(MCTP_HDR_DW_LE_PACKET_SIZE, -+ hdr[0]) * -+ 4, -+ ASPEED_MCTP_MTU); -+ rx_packet = aspeed_mctp_packet_alloc(GFP_ATOMIC); -+ if (rx_packet) { -+ memcpy(&rx_packet->data, hdr, sizeof(rx_packet->data)); -+ aspeed_mctp_swap_pcie_vdm_hdr(&rx_packet->data); -+ -+ aspeed_mctp_dispatch_packet(priv, rx_packet); -+ } else { -+ dev_dbg(priv->dev, "Failed to allocate RX packet\n"); -+ } -+ data_dump(priv, &rx_packet->data); -+ *hdr = 0; -+ rx->wr_ptr = (rx->wr_ptr + 1) % rx->buffer_count; -+ hdr = (u32 *)&rx_buf[rx->wr_ptr]; ++ if (device_property_read_u32(dev, "aspeed,tx-timeout", &tx_tout)) ++ tx_tout = -1; + -+ priv->rx_runaway_wa.packet_counter++; -+ } ++ dev_info(dev, "TX shmem: phys 0x%pa size %llu\n", ++ &info->tx_base.phys_addr, info->tx_base.size); ++ dev_info(dev, "RX shmem: phys 0x%pa size %llu\n", ++ &info->rx_base.phys_addr, info->rx_base.size); ++ dev_info(dev, "TX timeout: %u ms\n", tx_tout); + -+ /* -+ * Update HW write pointer, this can be done only after driver consumes -+ * packets from RX ring. -+ */ -+ regmap_read(priv->map, ASPEED_MCTP_RX_BUF_RD_PTR, &hw_read_ptr); -+ hw_read_ptr &= RX_BUF_RD_PTR_MASK; -+ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_WR_PTR, (hw_read_ptr)); ++ info->cl.dev = dev; ++ info->cl.rx_callback = mbox_rx_callback; ++ info->cl.tx_block = true; ++ info->cl.knows_txdone = false; ++ info->cl.tx_tout = tx_tout; + -+ dev_dbg(priv->dev, "RX hw ptr %02d, sw ptr %2d\n", -+ hw_read_ptr, rx->wr_ptr); -+ } else { -+ struct mctp_pcie_packet_data_2500 *rx_buf; ++ info->chan = mbox_request_channel(&info->cl, 0); ++ if (IS_ERR(info->chan)) { ++ dev_err(dev, "failed to request channel err %ld\n", ++ PTR_ERR(info->chan)); ++ return -EPROBE_DEFER; ++ } + -+ rx_buf = (struct mctp_pcie_packet_data_2500 *)rx->data.vaddr; -+ payload = (u32 *)&rx_buf[rx->wr_ptr]; -+ rx_cmd = (struct aspeed_mctp_rx_cmd *)rx->cmd.vaddr; -+ hdr = (u32 *)&((rx_cmd + rx->wr_ptr)->rx_lo); ++ info->rx_buffer = NULL; ++ init_waitqueue_head(&info->rx_wait); ++ spin_lock_init(&info->rx_msg_lock); ++ info->dev = dev; ++ platform_set_drvdata(pdev, info); + -+ if (!*hdr) { -+ u32 tmp_wr_ptr = rx->wr_ptr; ++ info->mdev.parent = dev; ++ info->mdev.minor = MISC_DYNAMIC_MINOR; ++ info->mdev.name = dev_name(dev); ++ info->mdev.fops = &aspeed_mbox_fops; ++ ret = misc_register(&info->mdev); ++ if (ret) { ++ dev_err(dev, "failed to register misc device\n"); ++ return ret; ++ } + -+ /* -+ * HACK: Right after start the RX hardware can put received -+ * packet into an unexpected offset - in order to locate -+ * received packet driver has to scan all RX data buffers. -+ */ -+ do { -+ tmp_wr_ptr = (tmp_wr_ptr + 1) % rx->buffer_count; ++ return 0; ++} + -+ hdr = (u32 *)&((rx_cmd + tmp_wr_ptr)->rx_lo); -+ } while (!*hdr && tmp_wr_ptr != rx->wr_ptr); ++static void aspeed_mbox_remove(struct platform_device *pdev) ++{ ++ struct ast_mbox_info *info = platform_get_drvdata(pdev); + -+ if (tmp_wr_ptr != rx->wr_ptr) { -+ dev_warn(priv->dev, -+ "Runaway RX packet found %d -> %d\n", -+ rx->wr_ptr, tmp_wr_ptr); -+ rx->wr_ptr = tmp_wr_ptr; -+ } -+ } ++ mbox_free_channel(info->chan); ++ misc_deregister(&info->mdev); ++} + -+ while (*hdr != 0) { -+ rx_packet = aspeed_mctp_packet_alloc(GFP_ATOMIC); -+ if (rx_packet) { -+ memcpy(rx_packet->data.payload, payload, -+ sizeof(rx_packet->data.payload)); ++static const struct of_device_id mbox_cl_match[] = { ++ { .compatible = "aspeed,aspeed-mbox" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, mbox_cl_match); + -+ aspeed_mctp_rx_hdr_prep(priv, (u8 *)rx_packet->data.hdr, *hdr); ++static struct platform_driver aspeed_mbox_driver = { ++ .driver = { ++ .name = "aspeed_mbox_client", ++ .of_match_table = mbox_cl_match, ++ }, ++ .probe = aspeed_mbox_probe, ++ .remove = aspeed_mbox_remove, ++}; ++module_platform_driver(aspeed_mbox_driver); + -+ aspeed_mctp_swap_pcie_vdm_hdr(&rx_packet->data); ++MODULE_AUTHOR("Jammy Huang "); ++MODULE_DESCRIPTION("ASPEED MBOX CLIENT driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c +--- a/drivers/soc/aspeed/aspeed-mctp.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-mctp.c 2026-04-08 18:03:48.310705320 +0000 +@@ -0,0 +1,2658 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (c) 2020, Intel Corporation. + -+ aspeed_mctp_dispatch_packet(priv, rx_packet); -+ } else { -+ dev_dbg(priv->dev, "Failed to allocate RX packet\n"); -+ } -+ dev_dbg(priv->dev, -+ "rx->wr_ptr = %d, rx_cmd->rx_lo = %08x", -+ rx->wr_ptr, *hdr); -+ data_dump(priv, &rx_packet->data); -+ *hdr = 0; -+ rx->wr_ptr = (rx->wr_ptr + 1) % rx->buffer_count; -+ payload = (u32 *)&rx_buf[rx->wr_ptr]; -+ hdr = (u32 *)&((rx_cmd + rx->wr_ptr)->rx_lo); -+ } -+ } ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ /* Kick RX if it was stopped due to ring full condition */ -+ if (rx->stopped) { -+ if (!rx_full) { -+ /* -+ * RX ring may still be full in here as the HW keeps producing when Tasklet consumes the packets. -+ * Use rx_full to detect if RX ring is already full before or after Tasklet consumption. -+ * Schedule another tasklet here to consume RX ring before restarting reception if ring is full after the while loop, -+ * in case that RX_CMD_NO_MORE_INT interrupts tasklet after tasklet consumes packets. -+ * Use flag cause we cannot control if ASPEED_MCTP_RX_BUF_WR_PTR can be updated before ring full occurs. -+ * Example of problematic scenario: -+ * 1. Tasklet executing, found *hdr==0 at wr_ptr=14, break the while loop and going forward. -+ * 2. After leaving the loop, Tasklet spend time doing some time-consuming stuffs like printing log. -+ * 3. HW keep receiving during step2, and triggered RX_CMD_NO_MORE_INT to set rx->stopped to true in the IRQ handler. -+ * 4. CPU returns to Tasklet, after step2 the tasklet sees rx->stopped == true, therefore kick RX_READY to restart RX. -+ * 5. Issue reproduced, RX restarted without stored packets consumed, and get overwritten later. -+ */ -+ tasklet_hi_schedule(&priv->rx.tasklet); -+ } else { -+ rx->stopped = false; -+ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, RX_CMD_READY, -+ RX_CMD_READY); -+ } -+ } -+} ++#include + -+static void aspeed_mctp_rx_chan_init(struct mctp_channel *rx) -+{ -+ struct aspeed_mctp *priv = container_of(rx, typeof(*priv), rx); -+ u32 *rx_cmd = (u32 *)rx->cmd.vaddr; -+ struct aspeed_mctp_rx_cmd *rx_cmd_64 = -+ (struct aspeed_mctp_rx_cmd *)rx->cmd.vaddr; -+ u32 data_size = priv->match_data->packet_unit_size; -+ u32 hw_rx_count = priv->rx_packet_count; -+ struct mctp_pcie_packet_data *rx_buf = (struct mctp_pcie_packet_data *)rx->data.vaddr; -+ int i; ++/* AST2600 MCTP Controller registers */ ++#define ASPEED_MCTP_CTRL 0x000 ++#define TX_CMD_TRIGGER BIT(0) ++#define RX_CMD_READY BIT(4) ++#define MATCHING_EID BIT(9) + -+ if (priv->match_data->vdm_hdr_direct_xfer) { -+ if (priv->match_data->dma_need_64bits_width) { -+ for (i = 0; i < priv->rx_packet_count; i++) { -+ rx_cmd_64->rx_hi = -+ upper_32_bits((rx->data.dma_handle + data_size * i)); -+ rx_cmd_64->rx_lo = -+ (rx->data.dma_handle + data_size * i) & -+ GENMASK(31, 4); -+ rx_cmd_64->rx_lo |= RX_INTERRUPT_AFTER_CMD; -+ rx_cmd_64++; -+ } -+ } else { -+ for (i = 0; i < priv->rx_packet_count; i++) { -+ *rx_cmd = RX_DATA_ADDR(rx->data.dma_handle + data_size * i); -+ *rx_cmd |= RX_INTERRUPT_AFTER_CMD; -+ rx_cmd++; -+ } -+ } -+ } else { -+ for (i = 0; i < priv->rx_packet_count; i++) { -+ rx_cmd_64->rx_hi = RX_DATA_ADDR_2500(rx->data.dma_handle + data_size * i); -+ rx_cmd_64->rx_lo = 0; -+ if (i == priv->rx_packet_count - 1) -+ rx_cmd_64->rx_hi |= RX_LAST_CMD; -+ rx_cmd_64++; -+ } -+ } -+ /* Clear the header of rx data */ -+ for (i = 0; i < priv->rx_packet_count; i++) -+ *(u32 *)&rx_buf[i] = 0; -+ rx->wr_ptr = 0; -+ rx->buffer_count = priv->rx_packet_count; -+ if (priv->match_data->fifo_auto_surround) { -+ /* -+ * TODO: Once read pointer runaway bug is fixed in some future AST2x00 -+ * stepping then add chip revision detection and turn on this -+ * workaround only when needed -+ */ -+ if (priv->match_data->dma_need_64bits_width) -+ priv->rx_runaway_wa.enable = false; -+ else -+ priv->rx_runaway_wa.enable = -+ (chip_version(priv->dev) == ASPEED_MCTP_2600) ? -+ true : -+ false; ++#define ASPEED_MCTP_TX_CMD 0x004 ++#define ASPEED_MCTP_RX_CMD 0x008 + -+ /* -+ * Hardware does not wrap around ASPEED_MCTP_RX_BUF_SIZE -+ * correctly - we have to set number of buffers to n/4 -1 -+ */ -+ if (priv->rx_runaway_wa.enable) -+ hw_rx_count = (priv->rx_packet_count / 4 - 1); ++#define ASPEED_MCTP_INT_STS 0x00c ++#define ASPEED_MCTP_INT_EN 0x010 ++#define TX_CMD_SENT_INT BIT(0) ++#define TX_CMD_LAST_INT BIT(1) ++#define TX_CMD_WRONG_INT BIT(2) ++#define RX_CMD_RECEIVE_INT BIT(8) ++#define RX_CMD_NO_MORE_INT BIT(9) + -+ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_SIZE, hw_rx_count); -+ } -+} ++#define ASPEED_MCTP_EID 0x014 ++#define MEMORY_SPACE_MAPPING GENMASK(31, 28) ++#define MCTP_EID GENMASK(7, 0) ++#define ASPEED_MCTP_OBFF_CTRL 0x018 + -+static void aspeed_mctp_tx_chan_init(struct mctp_channel *tx) -+{ -+ struct aspeed_mctp *priv = container_of(tx, typeof(*priv), tx); ++#define ASPEED_MCTP_ENGINE_CTRL 0x01c ++#define TX_MAX_PAYLOAD_SIZE_SHIFT 0 ++#define TX_MAX_PAYLOAD_SIZE_MASK GENMASK(1, TX_MAX_PAYLOAD_SIZE_SHIFT) ++#define TX_MAX_PAYLOAD_SIZE(x) \ ++ (((x) << TX_MAX_PAYLOAD_SIZE_SHIFT) & TX_MAX_PAYLOAD_SIZE_MASK) ++#define RX_MAX_PAYLOAD_SIZE_SHIFT 4 ++#define RX_MAX_PAYLOAD_SIZE_MASK GENMASK(5, RX_MAX_PAYLOAD_SIZE_SHIFT) ++#define RX_MAX_PAYLOAD_SIZE(x) \ ++ (((x) << RX_MAX_PAYLOAD_SIZE_SHIFT) & RX_MAX_PAYLOAD_SIZE_MASK) ++#define FIFO_LAYOUT_SHIFT 8 ++#define FIFO_LAYOUT_MASK GENMASK(9, FIFO_LAYOUT_SHIFT) ++#define FIFO_LAYOUT(x) \ ++ (((x) << FIFO_LAYOUT_SHIFT) & FIFO_LAYOUT_MASK) + -+ tx->wr_ptr = 0; -+ tx->rd_ptr = 0; -+ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, TX_CMD_TRIGGER, 0); -+ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_ADDR, tx->cmd.dma_handle); -+ if (priv->match_data->dma_need_64bits_width) -+ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_HI_ADDR, -+ upper_32_bits(tx->cmd.dma_handle)); -+ if (priv->match_data->fifo_auto_surround) { -+ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_SIZE, TX_PACKET_COUNT); -+ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_WR_PTR, 0); -+ } -+} ++#define ASPEED_MCTP_RX_BUF_ADDR 0x08 ++#define ASPEED_MCTP_RX_BUF_HI_ADDR 0x020 ++#define ASPEED_MCTP_RX_BUF_SIZE 0x024 ++#define ASPEED_MCTP_RX_BUF_RD_PTR 0x028 ++#define UPDATE_RX_RD_PTR BIT(31) ++#define RX_BUF_RD_PTR_MASK GENMASK(11, 0) ++#define ASPEED_MCTP_RX_BUF_WR_PTR 0x02c ++#define RX_BUF_WR_PTR_MASK GENMASK(11, 0) + -+struct mctp_client *aspeed_mctp_create_client(struct aspeed_mctp *priv) -+{ -+ struct mctp_client *client; ++#define ASPEED_MCTP_TX_BUF_ADDR 0x04 ++#define ASPEED_MCTP_TX_BUF_HI_ADDR 0x030 ++#define ASPEED_MCTP_TX_BUF_SIZE 0x034 ++#define ASPEED_MCTP_TX_BUF_RD_PTR 0x038 ++#define UPDATE_TX_RD_PTR BIT(31) ++#define TX_BUF_RD_PTR_MASK GENMASK(11, 0) ++#define ASPEED_MCTP_TX_BUF_WR_PTR 0x03c ++#define TX_BUF_WR_PTR_MASK GENMASK(11, 0) ++#define ASPEED_G7_MCTP_PCIE_BDF 0x04c + -+ client = aspeed_mctp_client_alloc(priv); -+ if (!client) -+ return NULL; ++#define ADDR_LEN GENMASK(26, 0) ++#define DATA_ADDR(x) (((x) >> 4) & ADDR_LEN) + -+ init_waitqueue_head(&client->wait_queue); ++/* TX command */ ++#define TX_LAST_CMD BIT(31) ++#define TX_DATA_ADDR_SHIFT 4 ++#define TX_DATA_ADDR_MASK GENMASK(30, TX_DATA_ADDR_SHIFT) ++#define TX_DATA_ADDR(x) \ ++ ((DATA_ADDR(x) << TX_DATA_ADDR_SHIFT) & TX_DATA_ADDR_MASK) ++#define TX_RESERVED_1_MASK GENMASK(1, 0) /* must be 1 */ ++#define TX_RESERVED_1 1 ++#define TX_STOP_AFTER_CMD BIT(16) ++#define TX_INTERRUPT_AFTER_CMD BIT(15) ++#define TX_PACKET_SIZE_SHIFT 2 ++#define TX_PACKET_SIZE_MASK GENMASK(12, TX_PACKET_SIZE_SHIFT) ++#define TX_PACKET_SIZE(x) \ ++ (((x) << TX_PACKET_SIZE_SHIFT) & TX_PACKET_SIZE_MASK) ++#define TX_RESERVED_0_MASK GENMASK(1, 0) /* MBZ */ ++#define TX_RESERVED_0 0 + -+ spin_lock_bh(&priv->clients_lock); -+ list_add_tail(&client->link, &priv->clients); -+ spin_unlock_bh(&priv->clients_lock); ++/* RX command */ ++#define RX_INTERRUPT_AFTER_CMD BIT(2) ++#define RX_DATA_ADDR_SHIFT 4 ++#define RX_DATA_ADDR_MASK GENMASK(30, RX_DATA_ADDR_SHIFT) ++#define RX_DATA_ADDR(x) \ ++ ((DATA_ADDR(x) << RX_DATA_ADDR_SHIFT) & RX_DATA_ADDR_MASK) + -+ return client; -+} -+EXPORT_SYMBOL_GPL(aspeed_mctp_create_client); ++#define ADDR_LEN_2500 GENMASK(23, 0) ++#define DATA_ADDR_2500(x) (((x) >> 7) & ADDR_LEN_2500) + -+static int aspeed_mctp_open(struct inode *inode, struct file *file) -+{ -+ struct miscdevice *misc = file->private_data; -+ struct platform_device *pdev = to_platform_device(misc->parent); -+ struct aspeed_mctp *priv = platform_get_drvdata(pdev); -+ struct mctp_client *client; ++/* TX command for ast2500 */ ++#define TX_DATA_ADDR_MASK_2500 GENMASK(30, 8) ++#define TX_DATA_ADDR_2500(x) \ ++ FIELD_PREP(TX_DATA_ADDR_MASK_2500, DATA_ADDR_2500(x)) ++#define TX_PACKET_SIZE_2500(x) \ ++ FIELD_PREP(GENMASK(11, 2), x) ++#define TX_PACKET_DEST_EID GENMASK(7, 0) ++#define TX_PACKET_TARGET_ID GENMASK(31, 16) ++#define TX_PACKET_ROUTING_TYPE BIT(14) ++#define TX_PACKET_TAG_OWNER BIT(13) ++#define TX_PACKET_PADDING_LEN GENMASK(1, 0) + -+ client = aspeed_mctp_create_client(priv); -+ if (!client) -+ return -ENOMEM; ++/* Rx command for ast2500 */ ++#define RX_LAST_CMD BIT(31) ++#define RX_DATA_ADDR_MASK_2500 GENMASK(29, 7) ++#define RX_DATA_ADDR_2500(x) \ ++ FIELD_PREP(RX_DATA_ADDR_MASK_2500, DATA_ADDR_2500(x)) ++#define RX_PACKET_SIZE GENMASK(30, 24) ++#define RX_PACKET_SRC_EID GENMASK(23, 16) ++#define RX_PACKET_ROUTING_TYPE GENMASK(15, 14) ++#define RX_PACKET_TAG_OWNER BIT(13) ++#define RX_PACKET_SEQ_NUMBER GENMASK(12, 11) ++#define RX_PACKET_MSG_TAG GENMASK(10, 8) ++#define RX_PACKET_SOM BIT(7) ++#define RX_PACKET_EOM BIT(6) ++#define RX_PACKET_PADDING_LEN GENMASK(5, 4) + -+ file->private_data = client; ++/* HW buffer sizes */ ++#define TX_PACKET_COUNT 48 ++#define RX_PACKET_COUNT 96 ++#if (RX_PACKET_COUNT % 4 != 0) ++#error The Rx buffer size should be 4-aligned. ++#error 1.Make runaway wrap boundary can be determined in Ast2600 A1/A2. ++#error 2.Fix the runaway read pointer bug in Ast2600 A3. ++#endif ++#define TX_MAX_PACKET_COUNT (TX_BUF_RD_PTR_MASK + 1) ++#define RX_MAX_PACKET_COUNT (RX_BUF_RD_PTR_MASK + 1) + -+ return 0; -+} ++/* Per client packet cache sizes */ ++#define RX_RING_COUNT 64 ++#define TX_RING_COUNT 64 + -+void aspeed_mctp_delete_client(struct mctp_client *client) -+{ -+ struct aspeed_mctp *priv = client->priv; -+ struct mctp_type_handler *handler, *tmp; ++/* PCIe Host Controller registers */ ++#define ASPEED_PCIE_LINK 0x0c0 ++#define PCIE_LINK_STS BIT(5) ++#define ASPEED_PCIE_MISC_STS_1 0x0c4 + -+ spin_lock_bh(&priv->clients_lock); ++/* PCIe Host Controller registers */ ++#define ASPEED_G7_PCIE_LOCATE 0x300 ++#define PCIE_LOCATE_IO BIT(0) ++#define ASPEED_G7_PCIE_LINK 0x358 ++#define PCIE_G7_LINK_STS BIT(8) ++#define ASPEED_G7_IO_PCIE_LINK 0x344 ++#define PCIE_G7_IO_LINK_STS BIT(18) + -+ list_del(&client->link); ++/* PCI address definitions */ ++#define PCI_DEV_NUM_MASK GENMASK(4, 0) ++#define PCI_BUS_NUM_SHIFT 5 ++#define PCI_BUS_NUM_MASK GENMASK(12, PCI_BUS_NUM_SHIFT) ++#define GET_PCI_DEV_NUM(x) ((x) & PCI_DEV_NUM_MASK) ++#define GET_PCI_BUS_NUM(x) (((x) & PCI_BUS_NUM_MASK) >> PCI_BUS_NUM_SHIFT) + -+ if (priv->default_client == client) -+ priv->default_client = NULL; ++/* MCTP header definitions */ ++#define MCTP_HDR_SRC_EID_OFFSET 14 ++#define MCTP_HDR_TAG_OFFSET 15 ++#define MCTP_HDR_SOM BIT(7) ++#define MCTP_HDR_EOM BIT(6) ++#define MCTP_HDR_SOM_EOM (MCTP_HDR_SOM | MCTP_HDR_EOM) ++#define MCTP_PAYLOAD_TYPE_OFFSET 0 ++#define MCTP_HDR_TYPE_CONTROL 0 ++#define MCTP_HDR_TYPE_VDM_PCI 0x7e ++#define MCTP_HDR_TYPE_SPDM 0x5 ++#define MCTP_HDR_TYPE_BASE_LAST MCTP_HDR_TYPE_SPDM ++#define MCTP_PAYLOAD_VENDOR_OFFSET 1 ++#define MCTP_PAYLOAD_VDM_TYPE_OFFSET 3 + -+ list_for_each_entry_safe(handler, tmp, &priv->mctp_type_handlers, -+ link) { -+ if (handler->client == client) { -+ list_del(&handler->link); -+ kfree(handler); -+ } -+ } -+ spin_unlock_bh(&priv->clients_lock); ++/* MCTP header DW little endian mask definitions */ ++/* 0th DW */ ++#define MCTP_HDR_DW_LE_ROUTING_TYPE GENMASK(26, 24) ++#define MCTP_HDR_DW_LE_PACKET_SIZE GENMASK(9, 0) ++/* 1st DW */ ++#define MCTP_HDR_DW_LE_PADDING_LEN GENMASK(13, 12) ++/* 2nd DW */ ++#define MCTP_HDR_DW_LE_TARGET_ID GENMASK(31, 16) ++/* 3rd DW */ ++#define MCTP_HDR_DW_LE_TAG_OWNER BIT(3) ++#define MCTP_HDR_DW_LE_DEST_EID GENMASK(23, 16) + -+ /* Disable the tasklet to appease lockdep */ -+ local_bh_disable(); -+ aspeed_mctp_client_put(client); -+ local_bh_enable(); -+} -+EXPORT_SYMBOL_GPL(aspeed_mctp_delete_client); ++#define ASPEED_MCTP_2600 0 ++#define ASPEED_MCTP_2600A3 1 + -+static int aspeed_mctp_release(struct inode *inode, struct file *file) -+{ -+ struct mctp_client *client = file->private_data; ++#define ASPEED_REVISION_ID0 0x04 ++#define ASPEED_REVISION_ID1 0x14 ++#define ID0_AST2600A0 0x05000303 ++#define ID1_AST2600A0 0x05000303 ++#define ID0_AST2600A1 0x05010303 ++#define ID1_AST2600A1 0x05010303 ++#define ID0_AST2600A2 0x05010303 ++#define ID1_AST2600A2 0x05020303 ++#define ID0_AST2600A3 0x05030303 ++#define ID1_AST2600A3 0x05030303 ++#define ID0_AST2620A1 0x05010203 ++#define ID1_AST2620A1 0x05010203 ++#define ID0_AST2620A2 0x05010203 ++#define ID1_AST2620A2 0x05020203 ++#define ID0_AST2620A3 0x05030203 ++#define ID1_AST2620A3 0x05030203 ++#define ID0_AST2605A2 0x05010103 ++#define ID1_AST2605A2 0x05020103 ++#define ID0_AST2605A3 0x05030103 ++#define ID1_AST2605A3 0x05030103 ++#define ID0_AST2625A3 0x05030403 ++#define ID1_AST2625A3 0x05030403 + -+ aspeed_mctp_delete_client(client); ++#define ASPEED_G7_SCU_PCIE0_CTRL_OFFSET 0xa60 ++#define ASPEED_G7_SCU_PCIE1_CTRL_OFFSET 0xae0 ++#define ASPEED_G7_SCU_PCIE_CTRL_VDM_EN BIT(1) + -+ return 0; -+} ++struct aspeed_mctp_match_data { ++ u32 rx_cmd_size; ++ u32 tx_cmd_size; ++ u32 packet_unit_size; ++ bool need_address_mapping; ++ bool vdm_hdr_direct_xfer; ++ bool fifo_auto_surround; ++ bool dma_need_64bits_width; ++ u32 scu_pcie_ctrl_offset; ++}; + -+#define LEN_MASK_HI GENMASK(9, 8) -+#define LEN_MASK_LO GENMASK(7, 0) -+#define PCI_VDM_HDR_LEN_MASK_LO GENMASK(31, 24) -+#define PCI_VDM_HDR_LEN_MASK_HI GENMASK(17, 16) -+#define PCIE_VDM_HDR_REQUESTER_BDF_MASK GENMASK(31, 16) ++struct aspeed_mctp_rx_cmd { ++ u32 rx_lo; ++ u32 rx_hi; ++}; + -+int aspeed_mctp_send_packet(struct mctp_client *client, -+ struct mctp_pcie_packet *packet) -+{ -+ struct aspeed_mctp *priv = client->priv; -+ u32 *hdr_dw = (u32 *)packet->data.hdr; -+ u8 *hdr = (u8 *)packet->data.hdr; -+ u8 *payload = (u8 *)packet->data.payload; -+ u16 packet_data_sz_dw; -+ u16 pci_data_len_dw; -+ int ret; -+ u16 bdf; ++struct aspeed_mctp_tx_cmd { ++ u32 tx_lo; ++ u32 tx_hi; ++}; ++ ++struct aspeed_g7_mctp_tx_cmd { ++ u32 tx_lo; ++ u32 tx_mid; ++ u32 tx_hi; ++ u32 reserved; ++}; + -+ ret = _get_bdf(priv); -+ if (ret < 0) -+ return ret; -+ bdf = ret; ++struct mctp_buffer { ++ void *vaddr; ++ dma_addr_t dma_handle; ++}; ++ ++struct mctp_channel { ++ struct mctp_buffer data; ++ struct mctp_buffer cmd; ++ struct tasklet_struct tasklet; ++ u32 buffer_count; ++ u32 rd_ptr; ++ u32 wr_ptr; ++ bool stopped; ++}; + ++struct aspeed_mctp { ++ struct device *dev; ++ struct miscdevice mctp_miscdev; ++ const struct aspeed_mctp_match_data *match_data; ++ struct regmap *map; ++ struct reset_control *reset; + /* -+ * If the data size is different from contents of PCIe VDM header, -+ * aspeed_mctp_tx_cmd will be programmed incorrectly. This may cause -+ * MCTP HW to stop working. ++ * The reset of the dma block in the MCTP-RC is connected to ++ * another reset pin. + */ -+ pci_data_len_dw = FIELD_PREP(LEN_MASK_LO, FIELD_GET(PCI_VDM_HDR_LEN_MASK_LO, hdr_dw[0])) | -+ FIELD_PREP(LEN_MASK_HI, FIELD_GET(PCI_VDM_HDR_LEN_MASK_HI, hdr_dw[0])); -+ if (pci_data_len_dw == 0) /* According to PCIe Spec, 0 means 1024 DW */ -+ pci_data_len_dw = SZ_1K; -+ -+ packet_data_sz_dw = packet->size / sizeof(u32) - sizeof(packet->data.hdr) / sizeof(u32); -+ if (packet_data_sz_dw != pci_data_len_dw) -+ return -EINVAL; -+ -+ be32p_replace_bits(&hdr_dw[1], bdf, PCIE_VDM_HDR_REQUESTER_BDF_MASK); -+ ++ struct reset_control *reset_dma; ++ struct mctp_channel tx; ++ struct mctp_channel rx; ++ struct list_head clients; ++ struct mctp_client *default_client; ++ struct list_head mctp_type_handlers; + /* -+ * XXX Don't update EID for MCTP Control messages - old EID may -+ * interfere with MCTP discovery flow. ++ * clients_lock protects list of clients, list of type handlers ++ * and default client + */ -+ if (priv->eid && payload[MCTP_PAYLOAD_TYPE_OFFSET] != MCTP_HDR_TYPE_CONTROL) -+ hdr[MCTP_HDR_SRC_EID_OFFSET] = priv->eid; -+ -+ ret = ptr_ring_produce_bh(&client->tx_queue, packet); -+ if (!ret) -+ tasklet_hi_schedule(&priv->tx.tasklet); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(aspeed_mctp_send_packet); ++ spinlock_t clients_lock; ++ struct list_head endpoints; ++ size_t endpoints_count; ++ /* ++ * endpoints_lock protects list of endpoints ++ */ ++ struct mutex endpoints_lock; ++ struct { ++ struct regmap *map; ++ struct delayed_work rst_dwork; ++ bool need_uevent; ++ } pcie; ++ struct { ++ bool enable; ++ bool first_loop; ++ int packet_counter; ++ } rx_runaway_wa; ++ bool rx_warmup; ++ u8 eid; ++ struct platform_device *peci_mctp; ++ /* Use the flag to identify RC or EP */ ++ bool rc_f; ++ /* Use the flag to identify the support of MCTP interrupt */ ++ bool miss_mctp_int; ++ /* Rx hardware buffer size */ ++ u32 rx_packet_count; ++ /* Rx pointer ring size */ ++ u32 rx_ring_count; ++ /* Tx pointer ring size */ ++ u32 tx_ring_count; ++ /* Delayed work for periodic detection of Rx packets */ ++ struct delayed_work rx_det_dwork; ++ u32 rx_det_period_us; ++#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM ++ struct net_device *ndev; ++#endif ++}; + -+struct mctp_pcie_packet *aspeed_mctp_receive_packet(struct mctp_client *client, -+ unsigned long timeout) -+{ -+ struct aspeed_mctp *priv = client->priv; -+ int ret; ++struct mctp_client { ++ struct kref ref; ++ struct aspeed_mctp *priv; ++ struct ptr_ring tx_queue; ++ struct ptr_ring rx_queue; ++ struct list_head link; ++ wait_queue_head_t wait_queue; ++}; + -+ ret = _get_bdf(priv); -+ if (ret < 0) -+ return ERR_PTR(ret); ++struct mctp_type_handler { ++ u8 mctp_type; ++ u16 pci_vendor_id; ++ u16 vdm_type; ++ u16 vdm_mask; ++ struct mctp_client *client; ++ struct list_head link; ++}; + -+ ret = wait_event_interruptible_timeout(client->wait_queue, -+ __ptr_ring_peek(&client->rx_queue), -+ timeout); -+ if (ret < 0) -+ return ERR_PTR(ret); -+ else if (ret == 0) -+ return ERR_PTR(-ETIME); ++union aspeed_mctp_eid_data_info { ++ struct aspeed_mctp_eid_info eid_info; ++ struct aspeed_mctp_eid_ext_info eid_ext_info; ++}; + -+ return ptr_ring_consume_bh(&client->rx_queue); -+} -+EXPORT_SYMBOL_GPL(aspeed_mctp_receive_packet); ++enum mctp_address_type { ++ ASPEED_MCTP_GENERIC_ADDR_FORMAT = 0, ++ ASPEED_MCTP_EXTENDED_ADDR_FORMAT = 1 ++}; + -+void aspeed_mctp_flush_rx_queue(struct mctp_client *client) -+{ -+ struct mctp_pcie_packet *packet; ++struct aspeed_mctp_endpoint { ++ union aspeed_mctp_eid_data_info data; ++ struct list_head link; ++}; + -+ while ((packet = ptr_ring_consume_bh(&client->rx_queue))) -+ aspeed_mctp_packet_free(packet); -+} -+EXPORT_SYMBOL_GPL(aspeed_mctp_flush_rx_queue); ++struct kmem_cache *packet_cache; + -+static ssize_t aspeed_mctp_read(struct file *file, char __user *buf, -+ size_t count, loff_t *ppos) ++static void data_dump(struct aspeed_mctp *priv, struct mctp_pcie_packet_data *data) +{ -+ struct mctp_client *client = file->private_data; -+ struct aspeed_mctp *priv = client->priv; -+ struct mctp_pcie_packet *rx_packet; -+ u32 mctp_ctrl; -+ u32 mctp_int_sts; -+ -+ if (count < PCIE_MCTP_MIN_PACKET_SIZE) -+ return -EINVAL; -+ -+ if (count > sizeof(rx_packet->data)) -+ count = sizeof(rx_packet->data); ++ int i; + -+ if (priv->miss_mctp_int) { -+ regmap_read(priv->map, ASPEED_MCTP_CTRL, &mctp_ctrl); -+ if (!(mctp_ctrl & RX_CMD_READY)) -+ priv->rx.stopped = true; -+ /* Polling the RX_CMD_RECEIVE_INT to ensure rx_tasklet can find the data */ -+ regmap_read(priv->map, ASPEED_MCTP_INT_STS, &mctp_int_sts); -+ if (mctp_int_sts & RX_CMD_RECEIVE_INT) -+ regmap_write(priv->map, ASPEED_MCTP_INT_STS, -+ mctp_int_sts); ++ dev_dbg(priv->dev, "Address %zu", (size_t)data); ++ dev_dbg(priv->dev, "VDM header:"); ++ for (i = 0; i < PCIE_VDM_HDR_SIZE_DW; i++) { ++ dev_dbg(priv->dev, "%02x %02x %02x %02x", data->hdr[i] & 0xff, ++ (data->hdr[i] >> 8) & 0xff, (data->hdr[i] >> 16) & 0xff, ++ (data->hdr[i] >> 24) & 0xff); + } -+ -+ tasklet_hi_schedule(&priv->rx.tasklet); -+ rx_packet = ptr_ring_consume_bh(&client->rx_queue); -+ if (!rx_packet) -+ return -EAGAIN; -+ -+ if (copy_to_user(buf, &rx_packet->data, count)) { -+ dev_err(priv->dev, "copy to user failed\n"); -+ count = -EFAULT; ++ dev_dbg(priv->dev, "Data payload:"); ++ for (i = 0; i < PCIE_VDM_DATA_SIZE_DW; i++) { ++ dev_dbg(priv->dev, "%02x %02x %02x %02x", ++ data->payload[i] & 0xff, (data->payload[i] >> 8) & 0xff, ++ (data->payload[i] >> 16) & 0xff, ++ (data->payload[i] >> 24) & 0xff); + } -+ -+ aspeed_mctp_packet_free(rx_packet); -+ -+ return count; +} + -+static void aspeed_mctp_flush_tx_queue(struct mctp_client *client) ++void *aspeed_mctp_packet_alloc(gfp_t flags) +{ -+ struct mctp_pcie_packet *packet; -+ -+ while ((packet = ptr_ring_consume_bh(&client->tx_queue))) -+ aspeed_mctp_packet_free(packet); ++ return kmem_cache_alloc(packet_cache, flags); +} ++EXPORT_SYMBOL_GPL(aspeed_mctp_packet_alloc); + -+static void aspeed_mctp_flush_all_tx_queues(struct aspeed_mctp *priv) ++void aspeed_mctp_packet_free(void *packet) +{ -+ struct mctp_client *client; -+ -+ spin_lock_bh(&priv->clients_lock); -+ list_for_each_entry(client, &priv->clients, link) -+ aspeed_mctp_flush_tx_queue(client); -+ spin_unlock_bh(&priv->clients_lock); ++ kmem_cache_free(packet_cache, packet); +} ++EXPORT_SYMBOL_GPL(aspeed_mctp_packet_free); + -+static ssize_t aspeed_mctp_write(struct file *file, const char __user *buf, -+ size_t count, loff_t *ppos) ++static int _get_bdf(struct aspeed_mctp *priv) +{ -+ struct mctp_client *client = file->private_data; -+ struct aspeed_mctp *priv = client->priv; -+ struct mctp_pcie_packet *tx_packet; -+ int ret; -+ -+ if (count < PCIE_MCTP_MIN_PACKET_SIZE) -+ return -EINVAL; -+ -+ if (count > sizeof(tx_packet->data)) -+ return -ENOSPC; ++ u32 reg; ++ u16 bdf, devfn; + -+ tx_packet = aspeed_mctp_packet_alloc(GFP_KERNEL); -+ if (!tx_packet) { -+ ret = -ENOMEM; -+ goto out; -+ } ++ if (priv->match_data->dma_need_64bits_width) { ++ regmap_read(priv->pcie.map, ASPEED_G7_PCIE_LOCATE, ®); ++ if (!(reg & PCIE_LOCATE_IO)) { ++ regmap_read(priv->pcie.map, ASPEED_G7_PCIE_LINK, ®); ++ if (!(reg & PCIE_G7_LINK_STS)) ++ return -ENETDOWN; ++ regmap_read(priv->map, ASPEED_G7_MCTP_PCIE_BDF, ®); ++ bdf = PCI_DEVID(PCI_BUS_NUM(reg), reg & 0xff); ++ } else { ++ regmap_read(priv->pcie.map, ASPEED_G7_IO_PCIE_LINK, ++ ®); ++ if (!(reg & PCIE_G7_IO_LINK_STS)) ++ return -ENETDOWN; ++ regmap_read(priv->map, ASPEED_G7_MCTP_PCIE_BDF, ®); ++ bdf = PCI_DEVID(PCI_BUS_NUM(reg), reg & 0xff); ++ } ++ } else { ++ regmap_read(priv->pcie.map, ASPEED_PCIE_LINK, ®); ++ if (!(reg & PCIE_LINK_STS)) ++ return -ENETDOWN; ++ regmap_read(priv->pcie.map, ASPEED_PCIE_MISC_STS_1, ®); + -+ if (copy_from_user(&tx_packet->data, buf, count)) { -+ dev_err(priv->dev, "copy from user failed\n"); -+ ret = -EFAULT; -+ goto out_packet; ++ reg = reg & (PCI_BUS_NUM_MASK | PCI_DEV_NUM_MASK); ++ /* only support function 0 */ ++ devfn = GET_PCI_DEV_NUM(reg) << 3; ++ bdf = PCI_DEVID(GET_PCI_BUS_NUM(reg), devfn); + } + -+ tx_packet->size = count; -+ -+ ret = aspeed_mctp_send_packet(client, tx_packet); -+ if (ret) -+ goto out_packet; -+ -+ return count; -+ -+out_packet: -+ aspeed_mctp_packet_free(tx_packet); -+out: -+ return ret; ++ return bdf; +} + -+int aspeed_mctp_add_type_handler(struct mctp_client *client, u8 mctp_type, -+ u16 pci_vendor_id, u16 vdm_type, u16 vdm_mask) ++static uint32_t chip_version(struct device *dev) +{ -+ struct aspeed_mctp *priv = client->priv; -+ struct mctp_type_handler *handler, *new_handler; -+ int ret = 0; ++ struct regmap *scu; ++ u32 revid0, revid1; + -+ if (mctp_type <= MCTP_HDR_TYPE_BASE_LAST) { -+ /* Vendor, type and type mask must be zero for types 0-5 */ -+ if (pci_vendor_id != 0 || vdm_type != 0 || vdm_mask != 0) -+ return -EINVAL; -+ } else if (mctp_type == MCTP_HDR_TYPE_VDM_PCI) { -+ /* For Vendor Defined PCI type the vendor ID must be nonzero */ -+ if (pci_vendor_id == 0 || pci_vendor_id == 0xffff) -+ return -EINVAL; -+ } else { -+ return -EINVAL; ++ scu = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu"); ++ if (IS_ERR(scu)) { ++ dev_err(dev, "failed to find 2600 SCU regmap\n"); ++ return PTR_ERR(scu); + } -+ -+ new_handler = kzalloc(sizeof(*new_handler), GFP_KERNEL); -+ if (!new_handler) -+ return -ENOMEM; -+ new_handler->mctp_type = mctp_type; -+ new_handler->pci_vendor_id = pci_vendor_id; -+ new_handler->vdm_type = vdm_type & vdm_mask; -+ new_handler->vdm_mask = vdm_mask; -+ new_handler->client = client; -+ -+ spin_lock_bh(&priv->clients_lock); -+ list_for_each_entry(handler, &priv->mctp_type_handlers, link) { -+ if (handler->mctp_type == new_handler->mctp_type && -+ handler->pci_vendor_id == new_handler->pci_vendor_id && -+ handler->vdm_type == new_handler->vdm_type) { -+ if (handler->client != new_handler->client) -+ ret = -EBUSY; -+ kfree(new_handler); -+ goto out_unlock; -+ } ++ regmap_read(scu, ASPEED_REVISION_ID0, &revid0); ++ regmap_read(scu, ASPEED_REVISION_ID1, &revid1); ++ if (revid0 == ID0_AST2600A3 && revid1 == ID1_AST2600A3) { ++ /* AST2600-A3 */ ++ return ASPEED_MCTP_2600A3; ++ } else if (revid0 == ID0_AST2620A3 && revid1 == ID1_AST2620A3) { ++ /* AST2620-A3 */ ++ return ASPEED_MCTP_2600A3; ++ } else if (revid0 == ID0_AST2605A3 && revid1 == ID1_AST2605A3) { ++ /* AST2605-A3 */ ++ return ASPEED_MCTP_2600A3; ++ } else if (revid0 == ID0_AST2625A3 && revid1 == ID1_AST2625A3) { ++ /* AST2605-A3 */ ++ return ASPEED_MCTP_2600A3; + } -+ list_add_tail(&new_handler->link, &priv->mctp_type_handlers); -+out_unlock: -+ spin_unlock_bh(&priv->clients_lock); -+ -+ return ret; ++ return ASPEED_MCTP_2600; +} -+EXPORT_SYMBOL_GPL(aspeed_mctp_add_type_handler); + -+static int aspeed_mctp_remove_type_handler(struct mctp_client *client, -+ u8 mctp_type, u16 pci_vendor_id, -+ u16 vdm_type, u16 vdm_mask) ++static int pcie_vdm_enable(struct device *dev) +{ -+ struct aspeed_mctp *priv = client->priv; -+ struct mctp_type_handler *handler, *tmp; -+ int ret = -EINVAL; -+ -+ vdm_type &= vdm_mask; ++ int ret = 0; ++ struct regmap *scu; ++ const struct aspeed_mctp_match_data *match_data = ++ of_device_get_match_data(dev); + -+ spin_lock_bh(&priv->clients_lock); -+ list_for_each_entry_safe(handler, tmp, &priv->mctp_type_handlers, -+ link) { -+ if (handler->client == client && -+ handler->mctp_type == mctp_type && -+ handler->pci_vendor_id == pci_vendor_id && -+ handler->vdm_type == vdm_type) { -+ list_del(&handler->link); -+ kfree(handler); -+ ret = 0; -+ break; -+ } ++ scu = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu"); ++ if (IS_ERR(scu)) { ++ dev_err(dev, "failed to find SCU regmap\n"); ++ return PTR_ERR(scu); + } -+ spin_unlock_bh(&priv->clients_lock); ++ ret = regmap_update_bits(scu, match_data->scu_pcie_ctrl_offset, ++ ASPEED_G7_SCU_PCIE_CTRL_VDM_EN, ++ ASPEED_G7_SCU_PCIE_CTRL_VDM_EN); + return ret; +} + -+int aspeed_mctp_register_default_handler(struct mctp_client *client) ++/* ++ * HW produces and expects VDM header in little endian and payload in network order. ++ * To allow userspace to use network order for the whole packet, PCIe VDM header needs ++ * to be swapped. ++ */ ++static void aspeed_mctp_swap_pcie_vdm_hdr(struct mctp_pcie_packet_data *data) +{ -+ struct aspeed_mctp *priv = client->priv; -+ int ret = 0; ++ int i; + -+ spin_lock_bh(&priv->clients_lock); ++ for (i = 0; i < PCIE_VDM_HDR_SIZE_DW; i++) ++ data->hdr[i] = swab32(data->hdr[i]); ++} + -+ if (!priv->default_client) -+ priv->default_client = client; -+ else if (priv->default_client != client) -+ ret = -EBUSY; ++static void aspeed_mctp_rx_trigger(struct mctp_channel *rx) ++{ ++ struct aspeed_mctp *priv = container_of(rx, typeof(*priv), rx); ++ u32 reg; + -+ spin_unlock_bh(&priv->clients_lock); ++ /* ++ * Even though rx_buf_addr doesn't change, if we don't do the write ++ * here, the HW doesn't trigger RX. We're also clearing the ++ * RX_CMD_READY bit, otherwise we're observing a rare case where ++ * trigger isn't registered by the HW, and we're ending up with stuck ++ * HW (not reacting to wr_ptr writes). ++ * Also, note that we're writing 0 as wr_ptr. If we're writing other ++ * value, the HW behaves in a bizarre way that's hard to explain... ++ */ ++ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, RX_CMD_READY, 0); ++ if (priv->match_data->fifo_auto_surround) { ++ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_ADDR, ++ rx->cmd.dma_handle); ++ if (priv->match_data->dma_need_64bits_width) ++ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_HI_ADDR, ++ upper_32_bits(rx->cmd.dma_handle)); ++ } else { ++ regmap_read(priv->map, ASPEED_MCTP_RX_BUF_ADDR, ®); ++ if (!reg) { ++ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_ADDR, ++ rx->cmd.dma_handle); ++ } else if (reg == (rx->cmd.dma_handle & GENMASK(28, 3))) { ++ dev_info(priv->dev, ++ "Already initialized - skipping rx dma set\n"); ++ } else { ++ dev_err(priv->dev, ++ "The memory of rx dma can't be changed after the controller is activated\n"); ++ return; ++ } ++ } ++ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_WR_PTR, 0); + -+ return ret; ++ /* After re-enabling RX we need to restart WA logic */ ++ if (priv->rx_runaway_wa.enable) ++ priv->rx.buffer_count = priv->rx_packet_count; ++ /* ++ * When Rx warmup MCTP controller may store first packet into the 0th to the ++ * 3rd cmd. In ast2600 A3, If the packet isn't stored in the 0th cmd we need ++ * to change the rx buffer size to avoid rx runaway in first loop. In ast2600 ++ * A1/A2, after first loop hardware is guaranteed to use (RX_PACKET_COUNT - 4) ++ * buffers. ++ */ ++ priv->rx_warmup = true; ++ priv->rx_runaway_wa.first_loop = true; ++ priv->rx_runaway_wa.packet_counter = 0; ++ ++ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, RX_CMD_READY, ++ RX_CMD_READY); +} -+EXPORT_SYMBOL_GPL(aspeed_mctp_register_default_handler); + -+static int -+aspeed_mctp_register_type_handler(struct mctp_client *client, -+ void __user *userbuf) ++static void aspeed_mctp_tx_trigger(struct mctp_channel *tx, bool notify) +{ -+ struct aspeed_mctp *priv = client->priv; -+ struct aspeed_mctp_type_handler_ioctl handler; ++ struct aspeed_mctp *priv = container_of(tx, typeof(*priv), tx); ++ u32 ctrl_val; ++ int ret; + -+ if (copy_from_user(&handler, userbuf, sizeof(handler))) { -+ dev_err(priv->dev, "copy from user failed\n"); -+ return -EFAULT; ++ if (notify) { ++ if (priv->match_data->dma_need_64bits_width) { ++ struct aspeed_g7_mctp_tx_cmd *last_cmd; ++ ++ last_cmd = (struct aspeed_g7_mctp_tx_cmd *)tx->cmd.vaddr + ++ (tx->wr_ptr - 1) % TX_PACKET_COUNT; ++ last_cmd->tx_lo |= TX_INTERRUPT_AFTER_CMD; ++ } else { ++ struct aspeed_mctp_tx_cmd *last_cmd; ++ ++ last_cmd = (struct aspeed_mctp_tx_cmd *)tx->cmd.vaddr + ++ (tx->wr_ptr - 1) % TX_PACKET_COUNT; ++ last_cmd->tx_lo |= TX_INTERRUPT_AFTER_CMD; ++ } + } ++ if (priv->match_data->fifo_auto_surround) ++ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_WR_PTR, tx->wr_ptr); ++ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, TX_CMD_TRIGGER, ++ TX_CMD_TRIGGER); ++ ret = regmap_read_poll_timeout_atomic(priv->map, ASPEED_MCTP_CTRL, ++ ctrl_val, ++ !(ctrl_val & TX_CMD_TRIGGER), 0, ++ 1000000); ++ if (ret) { ++ u32 rd_ptr, wr_ptr; + -+ return aspeed_mctp_add_type_handler(client, handler.mctp_type, -+ handler.pci_vendor_id, -+ handler.vendor_type, -+ handler.vendor_type_mask); ++ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_RD_PTR, UPDATE_RX_RD_PTR); ++ regmap_read(priv->map, ASPEED_MCTP_TX_BUF_RD_PTR, &rd_ptr); ++ rd_ptr &= RX_BUF_RD_PTR_MASK; ++ regmap_read(priv->map, ASPEED_MCTP_TX_BUF_WR_PTR, &wr_ptr); ++ wr_ptr &= TX_BUF_RD_PTR_MASK; ++ dev_warn(priv->dev, ++ "Wait tx completed timeout rd_ptr = %x, wr_ptr = %x\n", ++ rd_ptr, wr_ptr); ++ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, TX_CMD_TRIGGER, ++ 0); ++ } +} + -+static int -+aspeed_mctp_unregister_type_handler(struct mctp_client *client, -+ void __user *userbuf) ++static void aspeed_mctp_tx_cmd_prep(u32 *tx_hdr, struct aspeed_mctp_tx_cmd *tx_cmd) +{ -+ struct aspeed_mctp *priv = client->priv; -+ struct aspeed_mctp_type_handler_ioctl handler; ++ u32 packet_size, target_id; ++ u8 dest_eid, padding_len, routing_type, tag_owner; + -+ if (copy_from_user(&handler, userbuf, sizeof(handler))) { -+ dev_err(priv->dev, "copy from user failed\n"); -+ return -EFAULT; -+ } ++ packet_size = FIELD_GET(MCTP_HDR_DW_LE_PACKET_SIZE, tx_hdr[0]); ++ routing_type = FIELD_GET(MCTP_HDR_DW_LE_ROUTING_TYPE, tx_hdr[0]); ++ routing_type = routing_type ? routing_type - 1 : 0; ++ padding_len = FIELD_GET(MCTP_HDR_DW_LE_PADDING_LEN, tx_hdr[1]); ++ target_id = FIELD_GET(MCTP_HDR_DW_LE_TARGET_ID, tx_hdr[2]); ++ tag_owner = FIELD_GET(MCTP_HDR_DW_LE_TAG_OWNER, tx_hdr[3]); ++ dest_eid = FIELD_GET(MCTP_HDR_DW_LE_DEST_EID, tx_hdr[3]); + -+ return aspeed_mctp_remove_type_handler(client, handler.mctp_type, -+ handler.pci_vendor_id, -+ handler.vendor_type, -+ handler.vendor_type_mask); ++ tx_cmd->tx_hi = FIELD_PREP(TX_PACKET_DEST_EID, dest_eid); ++ tx_cmd->tx_lo = FIELD_PREP(TX_PACKET_TARGET_ID, target_id) | ++ TX_INTERRUPT_AFTER_CMD | ++ FIELD_PREP(TX_PACKET_ROUTING_TYPE, routing_type) | ++ FIELD_PREP(TX_PACKET_TAG_OWNER, tag_owner) | ++ TX_PACKET_SIZE_2500(packet_size) | ++ FIELD_PREP(TX_PACKET_PADDING_LEN, padding_len); +} + -+static int -+aspeed_mctp_filter_eid(struct aspeed_mctp *priv, void __user *userbuf) ++static void aspeed_mctp_emit_tx_cmd(struct mctp_channel *tx, ++ struct mctp_pcie_packet *packet) +{ -+ struct aspeed_mctp_filter_eid eid; ++ struct aspeed_mctp *priv = container_of(tx, typeof(*priv), tx); ++ struct aspeed_mctp_tx_cmd *tx_cmd = ++ (struct aspeed_mctp_tx_cmd *)tx->cmd.vaddr + tx->wr_ptr; ++ struct aspeed_g7_mctp_tx_cmd *tx_cmd_g7 = ++ (struct aspeed_g7_mctp_tx_cmd *)tx->cmd.vaddr + tx->wr_ptr; ++ u32 packet_sz_dw = packet->size / sizeof(u32) - ++ sizeof(packet->data.hdr) / sizeof(u32); ++ u32 offset; + -+ if (copy_from_user(&eid, userbuf, sizeof(eid))) { -+ dev_err(priv->dev, "copy from user failed\n"); -+ return -EFAULT; -+ } ++ data_dump(priv, &packet->data); ++ aspeed_mctp_swap_pcie_vdm_hdr(&packet->data); + -+ if (eid.enable) { -+ regmap_update_bits(priv->map, ASPEED_MCTP_EID, -+ MCTP_EID, eid.eid); -+ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, -+ MATCHING_EID, MATCHING_EID); ++ if (priv->match_data->vdm_hdr_direct_xfer) { ++ offset = tx->wr_ptr * sizeof(packet->data); ++ memcpy((u8 *)tx->data.vaddr + offset, &packet->data, ++ sizeof(packet->data)); ++ if (priv->match_data->dma_need_64bits_width) { ++ tx_cmd_g7->tx_lo = TX_PACKET_SIZE(packet_sz_dw); ++ tx_cmd_g7->tx_mid = TX_RESERVED_1; ++ tx_cmd_g7->tx_mid |= ((tx->data.dma_handle + offset) & ++ GENMASK(31, 4)); ++ tx_cmd_g7->tx_hi = upper_32_bits((tx->data.dma_handle + offset)); ++ } else { ++ tx_cmd->tx_lo = TX_PACKET_SIZE(packet_sz_dw); ++ tx_cmd->tx_hi = TX_RESERVED_1; ++ tx_cmd->tx_hi |= TX_DATA_ADDR(tx->data.dma_handle + offset); ++ } + } else { -+ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, -+ MATCHING_EID, 0); ++ offset = tx->wr_ptr * sizeof(struct mctp_pcie_packet_data_2500); ++ memcpy((u8 *)tx->data.vaddr + offset, packet->data.payload, ++ sizeof(packet->data.payload)); ++ aspeed_mctp_tx_cmd_prep(packet->data.hdr, tx_cmd); ++ tx_cmd->tx_hi |= TX_DATA_ADDR_2500(tx->data.dma_handle + offset); ++ if (tx->wr_ptr == TX_PACKET_COUNT - 1) ++ tx_cmd->tx_hi |= TX_LAST_CMD; + } -+ return 0; ++ dev_dbg(priv->dev, "tx->wr_prt: %d, tx_cmd: hi:%08x lo:%08x\n", ++ tx->wr_ptr, tx_cmd->tx_hi, tx_cmd->tx_lo); ++ ++ tx->wr_ptr = (tx->wr_ptr + 1) % TX_PACKET_COUNT; +} + -+static int aspeed_mctp_get_bdf(struct aspeed_mctp *priv, void __user *userbuf) ++static struct mctp_client *aspeed_mctp_client_alloc(struct aspeed_mctp *priv) +{ -+ struct aspeed_mctp_get_bdf bdf = { _get_bdf(priv) }; ++ struct mctp_client *client; + -+ if (copy_to_user(userbuf, &bdf, sizeof(bdf))) { -+ dev_err(priv->dev, "copy to user failed\n"); -+ return -EFAULT; -+ } -+ return 0; -+} ++ client = kzalloc(sizeof(*client), GFP_KERNEL); ++ if (!client) ++ goto out; + -+static int -+aspeed_mctp_get_medium_id(struct aspeed_mctp *priv, void __user *userbuf) -+{ -+ struct aspeed_mctp_get_medium_id id = { 0x09 }; /* PCIe revision 2.0 */ ++ kref_init(&client->ref); ++ client->priv = priv; ++ ptr_ring_init(&client->tx_queue, priv->tx_ring_count, GFP_KERNEL); ++ ptr_ring_init(&client->rx_queue, priv->rx_ring_count, GFP_ATOMIC); + -+ if (copy_to_user(userbuf, &id, sizeof(id))) { -+ dev_err(priv->dev, "copy to user failed\n"); -+ return -EFAULT; -+ } -+ return 0; ++out: ++ return client; +} + -+static int -+aspeed_mctp_get_mtu(struct aspeed_mctp *priv, void __user *userbuf) ++static void aspeed_mctp_client_free(struct kref *ref) +{ -+ struct aspeed_mctp_get_mtu id = { ASPEED_MCTP_MTU }; ++ struct mctp_client *client = container_of(ref, typeof(*client), ref); + -+ if (copy_to_user(userbuf, &id, sizeof(id))) { -+ dev_err(priv->dev, "copy to user failed\n"); -+ return -EFAULT; -+ } -+ return 0; ++ ptr_ring_cleanup(&client->rx_queue, &aspeed_mctp_packet_free); ++ ptr_ring_cleanup(&client->tx_queue, &aspeed_mctp_packet_free); ++ ++ kfree(client); +} + -+int aspeed_mctp_get_eid_bdf(struct mctp_client *client, u8 eid, u16 *bdf) ++static void aspeed_mctp_client_get(struct mctp_client *client) +{ -+ struct aspeed_mctp_endpoint *endpoint; -+ int ret = -ENOENT; ++ lockdep_assert_held(&client->priv->clients_lock); + -+ mutex_lock(&client->priv->endpoints_lock); -+ list_for_each_entry(endpoint, &client->priv->endpoints, link) { -+ if (endpoint->data.eid_info.eid == eid) { -+ *bdf = endpoint->data.eid_info.bdf; -+ ret = 0; -+ break; -+ } -+ } -+ mutex_unlock(&client->priv->endpoints_lock); ++ kref_get(&client->ref); ++} + -+ return ret; ++static void aspeed_mctp_client_put(struct mctp_client *client) ++{ ++ kref_put(&client->ref, &aspeed_mctp_client_free); +} -+EXPORT_SYMBOL_GPL(aspeed_mctp_get_eid_bdf); + -+int aspeed_mctp_get_eid(struct mctp_client *client, u16 bdf, -+ u8 domain_id, u8 *eid) ++static struct mctp_client * ++aspeed_mctp_find_handler(struct aspeed_mctp *priv, ++ struct mctp_pcie_packet *packet) +{ -+ struct aspeed_mctp_endpoint *endpoint; -+ int ret = -ENOENT; ++ struct mctp_type_handler *handler; ++ u8 *payload = (u8 *)packet->data.payload; ++ struct mctp_client *client = NULL; ++ u8 mctp_type; ++ u16 vendor = 0; ++ u16 vdm_type = 0; + -+ mutex_lock(&client->priv->endpoints_lock); ++ lockdep_assert_held(&priv->clients_lock); + -+ list_for_each_entry(endpoint, &client->priv->endpoints, link) { -+ if (endpoint->data.eid_ext_info.domain_id == domain_id && -+ endpoint->data.eid_ext_info.bdf == bdf) { -+ *eid = endpoint->data.eid_ext_info.eid; -+ ret = 0; ++ mctp_type = payload[MCTP_PAYLOAD_TYPE_OFFSET]; ++ if (mctp_type == MCTP_HDR_TYPE_VDM_PCI) { ++ vendor = *((u16 *)&payload[MCTP_PAYLOAD_VENDOR_OFFSET]); ++ vdm_type = *((u16 *)&payload[MCTP_PAYLOAD_VDM_TYPE_OFFSET]); ++ } ++ ++ list_for_each_entry(handler, &priv->mctp_type_handlers, link) { ++ if (handler->mctp_type == mctp_type && ++ handler->pci_vendor_id == vendor && ++ handler->vdm_type == (vdm_type & handler->vdm_mask)) { ++ dev_dbg(priv->dev, "Found client for type %x vdm %x\n", ++ mctp_type, handler->vdm_type); ++ client = handler->client; + break; + } + } -+ -+ mutex_unlock(&client->priv->endpoints_lock); -+ return ret; ++ return client; +} -+EXPORT_SYMBOL_GPL(aspeed_mctp_get_eid); + -+static int -+aspeed_mctp_get_eid_info(struct aspeed_mctp *priv, void __user *userbuf, -+ enum mctp_address_type addr_format) ++static void aspeed_mctp_dispatch_packet(struct aspeed_mctp *priv, ++ struct mctp_pcie_packet *packet) +{ -+ int count = 0; -+ int ret = 0; -+ struct aspeed_mctp_get_eid_info get_eid; -+ struct aspeed_mctp_endpoint *endpoint; -+ void *user_ptr; -+ size_t count_to_copy; ++ struct mctp_client *client; ++ int ret; + -+ if (copy_from_user(&get_eid, userbuf, sizeof(get_eid))) { -+ dev_err(priv->dev, "copy from user failed\n"); -+ return -EFAULT; -+ } ++ spin_lock(&priv->clients_lock); + -+ mutex_lock(&priv->endpoints_lock); ++ client = aspeed_mctp_find_handler(priv, packet); + -+ if (get_eid.count == 0) { -+ count = priv->endpoints_count; -+ goto out_unlock; -+ } ++ if (!client) ++ client = priv->default_client; + -+ user_ptr = u64_to_user_ptr(get_eid.ptr); -+ count_to_copy = get_eid.count > priv->endpoints_count ? -+ priv->endpoints_count : get_eid.count; -+ list_for_each_entry(endpoint, &priv->endpoints, link) { -+ if (endpoint->data.eid_info.eid < get_eid.start_eid) -+ continue; -+ if (count >= count_to_copy) -+ break; ++ if (client) ++ aspeed_mctp_client_get(client); + -+ if (addr_format == ASPEED_MCTP_EXTENDED_ADDR_FORMAT) -+ ret = copy_to_user(&(((struct aspeed_mctp_eid_ext_info *) -+ user_ptr)[count]), -+ &endpoint->data, -+ sizeof(struct aspeed_mctp_eid_ext_info)); -+ else -+ ret = copy_to_user(&(((struct aspeed_mctp_eid_info *) -+ user_ptr)[count]), -+ &endpoint->data, -+ sizeof(struct aspeed_mctp_eid_info)); ++ spin_unlock(&priv->clients_lock); + ++ if (client) { ++ ret = ptr_ring_produce(&client->rx_queue, packet); + if (ret) { -+ dev_err(priv->dev, "copy to user failed\n"); -+ ret = -EFAULT; -+ goto out_unlock; ++ /* ++ * This can happen if client process does not ++ * consume packets fast enough ++ */ ++ dev_dbg(priv->dev, "Failed to store packet in client RX queue\n"); ++ aspeed_mctp_packet_free(packet); ++ } else { ++ wake_up_all(&client->wait_queue); + } -+ count++; -+ } -+ -+out_unlock: -+ get_eid.count = count; -+ if (copy_to_user(userbuf, &get_eid, sizeof(get_eid))) { -+ dev_err(priv->dev, "copy to user failed\n"); -+ ret = -EFAULT; ++#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM ++ mctp_pcie_vdm_receive_packet(priv->ndev); ++#endif ++ aspeed_mctp_client_put(client); ++ } else { ++ dev_dbg(priv->dev, "Failed to dispatch RX packet\n"); ++ aspeed_mctp_packet_free(packet); + } -+ -+ mutex_unlock(&priv->endpoints_lock); -+ return ret; +} + -+static int -+eid_info_cmp(void *priv, const struct list_head *a, const struct list_head *b) ++static void aspeed_mctp_tx_tasklet(unsigned long data) +{ -+ struct aspeed_mctp_endpoint *endpoint_a; -+ struct aspeed_mctp_endpoint *endpoint_b; -+ -+ if (a == b) -+ return 0; ++ struct mctp_channel *tx = (struct mctp_channel *)data; ++ struct aspeed_mctp *priv = container_of(tx, typeof(*priv), tx); ++ struct mctp_client *client; ++ bool trigger = false; ++ bool full = false; ++ u32 rd_ptr; + -+ endpoint_a = list_entry(a, typeof(*endpoint_a), link); -+ endpoint_b = list_entry(b, typeof(*endpoint_b), link); ++ if (priv->match_data->fifo_auto_surround) { ++ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_RD_PTR, UPDATE_RX_RD_PTR); ++ regmap_read(priv->map, ASPEED_MCTP_TX_BUF_RD_PTR, &rd_ptr); ++ rd_ptr &= TX_BUF_RD_PTR_MASK; ++ } else { ++ rd_ptr = tx->rd_ptr; ++ } + -+ if (endpoint_a->data.eid_info.eid < endpoint_b->data.eid_info.eid) -+ return -1; -+ else if (endpoint_a->data.eid_info.eid > endpoint_b->data.eid_info.eid) -+ return 1; ++ spin_lock(&priv->clients_lock); + -+ return 0; -+} ++ list_for_each_entry(client, &priv->clients, link) { ++ while (!(full = (tx->wr_ptr + 1) % TX_PACKET_COUNT == rd_ptr)) { ++ struct mctp_pcie_packet *packet; + -+static void aspeed_mctp_eid_info_list_remove(struct list_head *list) -+{ -+ struct aspeed_mctp_endpoint *endpoint; -+ struct aspeed_mctp_endpoint *tmp; ++ packet = ptr_ring_consume(&client->tx_queue); ++ if (!packet) ++ break; + -+ list_for_each_entry_safe(endpoint, tmp, list, link) { -+ list_del(&endpoint->link); -+ kfree(endpoint); ++ aspeed_mctp_emit_tx_cmd(tx, packet); ++ aspeed_mctp_packet_free(packet); ++ trigger = true; ++ } + } -+} + -+static bool -+aspeed_mctp_eid_info_list_valid(struct list_head *list) -+{ -+ struct aspeed_mctp_endpoint *endpoint; -+ struct aspeed_mctp_endpoint *next; ++ spin_unlock(&priv->clients_lock); + -+ list_for_each_entry(endpoint, list, link) { -+ next = list_next_entry(endpoint, link); -+ if (&next->link == list) -+ break; ++ if (trigger) ++ aspeed_mctp_tx_trigger(tx, full); ++} + -+ /* duplicted eids */ -+ if (next->data.eid_info.eid == endpoint->data.eid_info.eid) -+ return false; -+ } ++static void aspeed_mctp_rx_hdr_prep(struct aspeed_mctp *priv, u8 *hdr, u32 rx_lo) ++{ ++ u16 bdf; ++ u8 routing_type; + -+ return true; ++ /* ++ * MCTP controller will map the routing type to reduce one bit ++ * 0 (Route to RC) -> 0, ++ * 2 (Route by ID) -> 1, ++ * 3 (Broadcast from RC) -> 2 ++ */ ++ routing_type = FIELD_GET(RX_PACKET_ROUTING_TYPE, rx_lo); ++ routing_type = routing_type ? routing_type + 1 : 0; ++ bdf = _get_bdf(priv); ++ /* Length[7:0] */ ++ hdr[0] = FIELD_GET(RX_PACKET_SIZE, rx_lo); ++ /* TD:EP:ATTR[1:0]:R or AT[1:0]:Length[9:8] */ ++ hdr[1] = 0; ++ /* R or T9:TC[2:0]:R[3:0] */ ++ hdr[2] = 0; ++ /* R or Fmt[2]:Fmt[1:0]=b'11:Type[4:3]=b'10:Type[2:0] */ ++ hdr[3] = 0x70 | routing_type; ++ /* VDM message code = 0x7f */ ++ hdr[4] = 0x7f; ++ /* R[1:0]:Pad len[1:0]:MCTP VDM Code[3:0] */ ++ hdr[5] = FIELD_GET(RX_PACKET_PADDING_LEN, rx_lo) << 4; ++ /* TODO: PCI Requester ID: HW didn't get this information */ ++ hdr[6] = 0; ++ hdr[7] = 5; ++ /* Vendor ID: 0x1AB4 */ ++ hdr[8] = 0xb4; ++ hdr[9] = 0x1a; ++ /* PCI Target ID */ ++ hdr[10] = bdf & 0xff; ++ hdr[11] = bdf >> 8 & 0xff; ++ /* SOM:EOM:Pkt Seq#[1:0]:TO:Msg Tag[2:0]*/ ++ hdr[12] = FIELD_GET(RX_PACKET_SOM, rx_lo) << 7 | ++ FIELD_GET(RX_PACKET_EOM, rx_lo) << 6 | ++ FIELD_GET(RX_PACKET_SEQ_NUMBER, rx_lo) << 4 | ++ FIELD_GET(RX_PACKET_TAG_OWNER, rx_lo) << 3 | ++ FIELD_GET(RX_PACKET_MSG_TAG, rx_lo); ++ /* Source Endpoint ID */ ++ hdr[13] = FIELD_GET(RX_PACKET_SRC_EID, rx_lo); ++ /* Destination Endpoint ID: HW didn't get this information*/ ++ hdr[14] = priv->eid; ++ /* TODO: R[3:0]: header version[3:0] */ ++ hdr[15] = 1; +} + -+static int -+aspeed_mctp_set_eid_info(struct aspeed_mctp *priv, void __user *userbuf, -+ enum mctp_address_type addr_format) ++static void aspeed_mctp_rx_tasklet(unsigned long data) +{ -+ struct list_head list = LIST_HEAD_INIT(list); -+ struct aspeed_mctp_set_eid_info set_eid; -+ void *user_ptr; -+ struct aspeed_mctp_endpoint *endpoint; -+ int ret = 0; -+ u8 eid = 0; -+ size_t i; ++ struct mctp_channel *rx = (struct mctp_channel *)data; ++ struct aspeed_mctp *priv = container_of(rx, typeof(*priv), rx); ++ struct mctp_pcie_packet *rx_packet; ++ struct aspeed_mctp_rx_cmd *rx_cmd; ++ u32 hw_read_ptr; ++ u32 *hdr, *payload; ++ bool rx_full; + -+ if (copy_from_user(&set_eid, userbuf, sizeof(set_eid))) { -+ dev_err(priv->dev, "copy from user failed\n"); -+ return -EFAULT; -+ } ++ /* initialized as false */ ++ rx_full = false; + -+ if (set_eid.count > ASPEED_MCTP_EID_INFO_MAX) -+ return -EINVAL; ++ if (priv->match_data->vdm_hdr_direct_xfer && priv->match_data->fifo_auto_surround) { ++ struct mctp_pcie_packet_data *rx_buf; ++ u32 residual_cmds = 0; + -+ user_ptr = u64_to_user_ptr(set_eid.ptr); -+ for (i = 0; i < set_eid.count; i++) { -+ endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL); -+ if (!endpoint) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ memset(endpoint, 0, sizeof(*endpoint)); ++ /* Trigger HW read pointer update, must be done before RX loop */ ++ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_RD_PTR, UPDATE_RX_RD_PTR); + -+ if (addr_format == ASPEED_MCTP_EXTENDED_ADDR_FORMAT) -+ ret = copy_from_user(&endpoint->data, -+ &(((struct aspeed_mctp_eid_ext_info *) -+ user_ptr)[i]), -+ sizeof(struct aspeed_mctp_eid_ext_info)); -+ else -+ ret = copy_from_user(&endpoint->data, -+ &(((struct aspeed_mctp_eid_info *) -+ user_ptr)[i]), -+ sizeof(struct aspeed_mctp_eid_info)); ++ /* ++ * rx->stopped indicates if rx ring is full or not. ++ * Use rx_full to note ring status before consuming packet. ++ */ ++ rx_full = rx->stopped; + -+ if (ret) { -+ dev_err(priv->dev, "copy from user failed\n"); -+ kfree(endpoint); -+ ret = -EFAULT; -+ goto out; -+ } ++ /* ++ * XXX: Using rd_ptr obtained from HW is unreliable so we need to ++ * maintain the state of buffer on our own by peeking into the buffer ++ * and checking where the packet was written. ++ */ ++ rx_buf = (struct mctp_pcie_packet_data *)rx->data.vaddr; ++ hdr = (u32 *)&rx_buf[rx->wr_ptr]; ++ if (!*hdr && priv->rx_warmup) { ++ u32 tmp_wr_ptr = rx->wr_ptr; + -+ /* Detect self EID */ -+ if (_get_bdf(priv) == endpoint->data.eid_info.bdf) { + /* -+ * XXX Use smallest EID with matching BDF. -+ * On some platforms there could be multiple endpoints -+ * with same BDF in routing table. ++ * HACK: Right after start the RX hardware can put received ++ * packet into an unexpected offset - in order to locate ++ * received packet driver has to scan all RX data buffers. + */ -+ if (eid == 0 || endpoint->data.eid_info.eid < eid) -+ eid = endpoint->data.eid_info.eid; -+ } ++ do { ++ tmp_wr_ptr = (tmp_wr_ptr + 1) % priv->rx_packet_count; + -+ list_add_tail(&endpoint->link, &list); -+ } ++ hdr = (u32 *)&rx_buf[tmp_wr_ptr]; ++ } while (!*hdr && tmp_wr_ptr != rx->wr_ptr); + -+ list_sort(NULL, &list, eid_info_cmp); -+ if (!aspeed_mctp_eid_info_list_valid(&list)) { -+ ret = -EINVAL; -+ goto out; -+ } ++ if (tmp_wr_ptr != rx->wr_ptr) { ++ dev_warn(priv->dev, ++ "Runaway RX packet found %d -> %d\n", ++ rx->wr_ptr, tmp_wr_ptr); ++ residual_cmds = abs(tmp_wr_ptr - rx->wr_ptr); ++ rx->wr_ptr = tmp_wr_ptr; ++ if (!priv->rx_runaway_wa.enable && ++ priv->rx_warmup) ++ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_SIZE, ++ rx->buffer_count - residual_cmds); ++ priv->rx_warmup = false; ++ } ++ } else { ++ priv->rx_warmup = false; ++ } + -+ mutex_lock(&priv->endpoints_lock); -+ if (list_empty(&priv->endpoints)) -+ list_splice_init(&list, &priv->endpoints); -+ else -+ list_swap(&list, &priv->endpoints); -+ priv->endpoints_count = set_eid.count; -+ priv->eid = eid; -+ mutex_unlock(&priv->endpoints_lock); -+out: -+ aspeed_mctp_eid_info_list_remove(&list); -+ return ret; -+} ++ if (priv->rx_runaway_wa.packet_counter > priv->rx_packet_count && ++ priv->rx_runaway_wa.first_loop) { ++ if (priv->rx_runaway_wa.enable) ++ /* ++ * Once we receive RX_PACKET_COUNT packets, hardware is ++ * guaranteed to use (RX_PACKET_COUNT - 4) buffers. Decrease ++ * buffer count by 4, then we can turn off scanning of RX ++ * buffers. RX buffer scanning should be enabled every time ++ * RX hardware is started. ++ * This is just a performance optimization - we could keep ++ * scanning RX buffers forever, but under heavy traffic it is ++ * fairly common that rx_tasklet is executed while RX buffer ++ * ring is empty. ++ */ ++ rx->buffer_count = priv->rx_packet_count - 4; ++ else ++ /* ++ * Once we receive RX_PACKET_COUNT packets, we need to restore the ++ * RX buffer size to 4 byte aligned value to avoid rx runaway. ++ */ ++ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_SIZE, ++ rx->buffer_count); ++ priv->rx_runaway_wa.first_loop = false; ++ } ++ ++ while (*hdr != 0) { ++ if (FIELD_GET(MCTP_HDR_DW_LE_PACKET_SIZE, hdr[0]) * 4 > ++ ASPEED_MCTP_MTU) ++ dev_warn(priv->dev, ++ "Rx length %ld > MTU size %d\n", ++ FIELD_GET(MCTP_HDR_DW_LE_PACKET_SIZE, ++ hdr[0]) * ++ 4, ++ ASPEED_MCTP_MTU); ++ rx_packet = aspeed_mctp_packet_alloc(GFP_ATOMIC); ++ if (rx_packet) { ++ memcpy(&rx_packet->data, hdr, sizeof(rx_packet->data)); ++ aspeed_mctp_swap_pcie_vdm_hdr(&rx_packet->data); + -+static int aspeed_mctp_set_own_eid(struct aspeed_mctp *priv, void __user *userbuf) -+{ -+ struct aspeed_mctp_set_own_eid data; ++ aspeed_mctp_dispatch_packet(priv, rx_packet); ++ } else { ++ dev_dbg(priv->dev, "Failed to allocate RX packet\n"); ++ } ++ data_dump(priv, &rx_packet->data); ++ *hdr = 0; ++ rx->wr_ptr = (rx->wr_ptr + 1) % rx->buffer_count; ++ hdr = (u32 *)&rx_buf[rx->wr_ptr]; + -+ if (copy_from_user(&data, userbuf, sizeof(data))) { -+ dev_err(priv->dev, "copy from user failed\n"); -+ return -EFAULT; -+ } ++ priv->rx_runaway_wa.packet_counter++; ++ } + -+ priv->eid = data.eid; ++ /* ++ * Update HW write pointer, this can be done only after driver consumes ++ * packets from RX ring. ++ */ ++ regmap_read(priv->map, ASPEED_MCTP_RX_BUF_RD_PTR, &hw_read_ptr); ++ hw_read_ptr &= RX_BUF_RD_PTR_MASK; ++ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_WR_PTR, (hw_read_ptr)); + -+ return 0; -+} ++ dev_dbg(priv->dev, "RX hw ptr %02d, sw ptr %2d\n", ++ hw_read_ptr, rx->wr_ptr); ++ } else { ++ struct mctp_pcie_packet_data_2500 *rx_buf; + -+static long -+aspeed_mctp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ struct mctp_client *client = file->private_data; -+ struct aspeed_mctp *priv = client->priv; -+ void __user *userbuf = (void __user *)arg; -+ int ret; ++ rx_buf = (struct mctp_pcie_packet_data_2500 *)rx->data.vaddr; ++ payload = (u32 *)&rx_buf[rx->wr_ptr]; ++ rx_cmd = (struct aspeed_mctp_rx_cmd *)rx->cmd.vaddr; ++ hdr = (u32 *)&((rx_cmd + rx->wr_ptr)->rx_lo); + -+ switch (cmd) { -+ case ASPEED_MCTP_IOCTL_FILTER_EID: -+ ret = aspeed_mctp_filter_eid(priv, userbuf); -+ break; ++ if (!*hdr) { ++ u32 tmp_wr_ptr = rx->wr_ptr; + -+ case ASPEED_MCTP_IOCTL_GET_BDF: -+ ret = aspeed_mctp_get_bdf(priv, userbuf); -+ break; ++ /* ++ * HACK: Right after start the RX hardware can put received ++ * packet into an unexpected offset - in order to locate ++ * received packet driver has to scan all RX data buffers. ++ */ ++ do { ++ tmp_wr_ptr = (tmp_wr_ptr + 1) % rx->buffer_count; + -+ case ASPEED_MCTP_IOCTL_GET_MEDIUM_ID: -+ ret = aspeed_mctp_get_medium_id(priv, userbuf); -+ break; ++ hdr = (u32 *)&((rx_cmd + tmp_wr_ptr)->rx_lo); ++ } while (!*hdr && tmp_wr_ptr != rx->wr_ptr); + -+ case ASPEED_MCTP_IOCTL_GET_MTU: -+ ret = aspeed_mctp_get_mtu(priv, userbuf); -+ break; ++ if (tmp_wr_ptr != rx->wr_ptr) { ++ dev_warn(priv->dev, ++ "Runaway RX packet found %d -> %d\n", ++ rx->wr_ptr, tmp_wr_ptr); ++ rx->wr_ptr = tmp_wr_ptr; ++ } ++ } + -+ case ASPEED_MCTP_IOCTL_REGISTER_DEFAULT_HANDLER: -+ ret = aspeed_mctp_register_default_handler(client); -+ break; ++ while (*hdr != 0) { ++ rx_packet = aspeed_mctp_packet_alloc(GFP_ATOMIC); ++ if (rx_packet) { ++ memcpy(rx_packet->data.payload, payload, ++ sizeof(rx_packet->data.payload)); + -+ case ASPEED_MCTP_IOCTL_REGISTER_TYPE_HANDLER: -+ ret = aspeed_mctp_register_type_handler(client, userbuf); -+ break; ++ aspeed_mctp_rx_hdr_prep(priv, (u8 *)rx_packet->data.hdr, *hdr); + -+ case ASPEED_MCTP_IOCTL_UNREGISTER_TYPE_HANDLER: -+ ret = aspeed_mctp_unregister_type_handler(client, userbuf); -+ break; ++ aspeed_mctp_swap_pcie_vdm_hdr(&rx_packet->data); + -+ case ASPEED_MCTP_IOCTL_GET_EID_INFO: -+ ret = aspeed_mctp_get_eid_info(priv, userbuf, ASPEED_MCTP_GENERIC_ADDR_FORMAT); -+ break; ++ aspeed_mctp_dispatch_packet(priv, rx_packet); ++ } else { ++ dev_dbg(priv->dev, "Failed to allocate RX packet\n"); ++ } ++ dev_dbg(priv->dev, ++ "rx->wr_ptr = %d, rx_cmd->rx_lo = %08x", ++ rx->wr_ptr, *hdr); ++ data_dump(priv, &rx_packet->data); ++ *hdr = 0; ++ rx->wr_ptr = (rx->wr_ptr + 1) % rx->buffer_count; ++ payload = (u32 *)&rx_buf[rx->wr_ptr]; ++ hdr = (u32 *)&((rx_cmd + rx->wr_ptr)->rx_lo); ++ } ++ } + -+ case ASPEED_MCTP_IOCTL_GET_EID_EXT_INFO: -+ ret = aspeed_mctp_get_eid_info(priv, userbuf, ASPEED_MCTP_EXTENDED_ADDR_FORMAT); -+ break; ++ /* Kick RX if it was stopped due to ring full condition */ ++ if (rx->stopped) { ++ if (!rx_full) { ++ /* ++ * RX ring may still be full in here as the HW keeps producing when Tasklet consumes the packets. ++ * Use rx_full to detect if RX ring is already full before or after Tasklet consumption. ++ * Schedule another tasklet here to consume RX ring before restarting reception if ring is full after the while loop, ++ * in case that RX_CMD_NO_MORE_INT interrupts tasklet after tasklet consumes packets. ++ * Use flag cause we cannot control if ASPEED_MCTP_RX_BUF_WR_PTR can be updated before ring full occurs. ++ * Example of problematic scenario: ++ * 1. Tasklet executing, found *hdr==0 at wr_ptr=14, break the while loop and going forward. ++ * 2. After leaving the loop, Tasklet spend time doing some time-consuming stuffs like printing log. ++ * 3. HW keep receiving during step2, and triggered RX_CMD_NO_MORE_INT to set rx->stopped to true in the IRQ handler. ++ * 4. CPU returns to Tasklet, after step2 the tasklet sees rx->stopped == true, therefore kick RX_READY to restart RX. ++ * 5. Issue reproduced, RX restarted without stored packets consumed, and get overwritten later. ++ */ ++ tasklet_hi_schedule(&priv->rx.tasklet); ++ } else { ++ rx->stopped = false; ++ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, RX_CMD_READY, ++ RX_CMD_READY); ++ } ++ } ++} + -+ case ASPEED_MCTP_IOCTL_SET_EID_INFO: -+ ret = aspeed_mctp_set_eid_info(priv, userbuf, ASPEED_MCTP_GENERIC_ADDR_FORMAT); -+ break; ++static void aspeed_mctp_rx_chan_init(struct mctp_channel *rx) ++{ ++ struct aspeed_mctp *priv = container_of(rx, typeof(*priv), rx); ++ u32 *rx_cmd = (u32 *)rx->cmd.vaddr; ++ struct aspeed_mctp_rx_cmd *rx_cmd_64 = ++ (struct aspeed_mctp_rx_cmd *)rx->cmd.vaddr; ++ u32 data_size = priv->match_data->packet_unit_size; ++ u32 hw_rx_count = priv->rx_packet_count; ++ struct mctp_pcie_packet_data *rx_buf = (struct mctp_pcie_packet_data *)rx->data.vaddr; ++ int i; + -+ case ASPEED_MCTP_IOCTL_SET_EID_EXT_INFO: -+ ret = aspeed_mctp_set_eid_info(priv, userbuf, ASPEED_MCTP_EXTENDED_ADDR_FORMAT); -+ break; ++ if (priv->match_data->vdm_hdr_direct_xfer) { ++ if (priv->match_data->dma_need_64bits_width) { ++ for (i = 0; i < priv->rx_packet_count; i++) { ++ rx_cmd_64->rx_hi = ++ upper_32_bits((rx->data.dma_handle + data_size * i)); ++ rx_cmd_64->rx_lo = ++ (rx->data.dma_handle + data_size * i) & ++ GENMASK(31, 4); ++ rx_cmd_64->rx_lo |= RX_INTERRUPT_AFTER_CMD; ++ rx_cmd_64++; ++ } ++ } else { ++ for (i = 0; i < priv->rx_packet_count; i++) { ++ *rx_cmd = RX_DATA_ADDR(rx->data.dma_handle + data_size * i); ++ *rx_cmd |= RX_INTERRUPT_AFTER_CMD; ++ rx_cmd++; ++ } ++ } ++ } else { ++ for (i = 0; i < priv->rx_packet_count; i++) { ++ rx_cmd_64->rx_hi = RX_DATA_ADDR_2500(rx->data.dma_handle + data_size * i); ++ rx_cmd_64->rx_lo = 0; ++ if (i == priv->rx_packet_count - 1) ++ rx_cmd_64->rx_hi |= RX_LAST_CMD; ++ rx_cmd_64++; ++ } ++ } ++ /* Clear the header of rx data */ ++ for (i = 0; i < priv->rx_packet_count; i++) ++ *(u32 *)&rx_buf[i] = 0; ++ rx->wr_ptr = 0; ++ rx->buffer_count = priv->rx_packet_count; ++ if (priv->match_data->fifo_auto_surround) { ++ /* ++ * TODO: Once read pointer runaway bug is fixed in some future AST2x00 ++ * stepping then add chip revision detection and turn on this ++ * workaround only when needed ++ */ ++ if (priv->match_data->dma_need_64bits_width) ++ priv->rx_runaway_wa.enable = false; ++ else ++ priv->rx_runaway_wa.enable = ++ (chip_version(priv->dev) == ASPEED_MCTP_2600) ? ++ true : ++ false; + -+ case ASPEED_MCTP_IOCTL_SET_OWN_EID: -+ ret = aspeed_mctp_set_own_eid(priv, userbuf); -+ break; ++ /* ++ * Hardware does not wrap around ASPEED_MCTP_RX_BUF_SIZE ++ * correctly - we have to set number of buffers to n/4 -1 ++ */ ++ if (priv->rx_runaway_wa.enable) ++ hw_rx_count = (priv->rx_packet_count / 4 - 1); + -+ default: -+ dev_err(priv->dev, "Command not found\n"); -+ ret = -ENOTTY; ++ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_SIZE, hw_rx_count); + } -+ -+ return ret; +} + -+static __poll_t aspeed_mctp_poll(struct file *file, -+ struct poll_table_struct *pt) ++static void aspeed_mctp_tx_chan_init(struct mctp_channel *tx) +{ -+ struct mctp_client *client = file->private_data; -+ __poll_t ret = 0; -+ struct aspeed_mctp *priv = client->priv; -+ struct mctp_channel *rx = &priv->rx; -+ u32 mctp_ctrl; -+ u32 mctp_int_sts; ++ struct aspeed_mctp *priv = container_of(tx, typeof(*priv), tx); + -+ if (priv->miss_mctp_int) { -+ regmap_read(priv->map, ASPEED_MCTP_CTRL, &mctp_ctrl); -+ if (!(mctp_ctrl & RX_CMD_READY)) -+ rx->stopped = true; -+ /* Polling the RX_CMD_RECEIVE_INT to ensure rx_tasklet can find the data */ -+ regmap_read(priv->map, ASPEED_MCTP_INT_STS, &mctp_int_sts); -+ if (mctp_int_sts & RX_CMD_RECEIVE_INT) -+ regmap_write(priv->map, ASPEED_MCTP_INT_STS, -+ mctp_int_sts); ++ tx->wr_ptr = 0; ++ tx->rd_ptr = 0; ++ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, TX_CMD_TRIGGER, 0); ++ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_ADDR, tx->cmd.dma_handle); ++ if (priv->match_data->dma_need_64bits_width) ++ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_HI_ADDR, ++ upper_32_bits(tx->cmd.dma_handle)); ++ if (priv->match_data->fifo_auto_surround) { ++ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_SIZE, TX_PACKET_COUNT); ++ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_WR_PTR, 0); + } ++} + -+ tasklet_hi_schedule(&priv->rx.tasklet); -+ poll_wait(file, &client->wait_queue, pt); ++struct mctp_client *aspeed_mctp_create_client(struct aspeed_mctp *priv) ++{ ++ struct mctp_client *client; + -+ if (!ptr_ring_full_bh(&client->tx_queue)) -+ ret |= EPOLLOUT; ++ client = aspeed_mctp_client_alloc(priv); ++ if (!client) ++ return NULL; + -+ if (__ptr_ring_peek(&client->rx_queue)) -+ ret |= EPOLLIN; ++ init_waitqueue_head(&client->wait_queue); + -+ return ret; ++ spin_lock_bh(&priv->clients_lock); ++ list_add_tail(&client->link, &priv->clients); ++ spin_unlock_bh(&priv->clients_lock); ++ ++ return client; +} ++EXPORT_SYMBOL_GPL(aspeed_mctp_create_client); + -+#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM -+static int aspeed_mctp_pcie_vdm_op_send_pkt(struct device *dev, -+ u8 *data, size_t size) ++static int aspeed_mctp_open(struct inode *inode, struct file *file) +{ -+ struct mctp_pcie_packet *packet; -+ struct platform_device *pdev; -+ struct aspeed_mctp *priv; -+ int rc; -+ -+ pdev = to_platform_device(dev); -+ priv = platform_get_drvdata(pdev); -+ // freed at aspeed-mctp tx tasklet or send failure -+ packet = aspeed_mctp_packet_alloc(GFP_KERNEL); ++ struct miscdevice *misc = file->private_data; ++ struct platform_device *pdev = to_platform_device(misc->parent); ++ struct aspeed_mctp *priv = platform_get_drvdata(pdev); ++ struct mctp_client *client; + -+ if (!packet) { -+ dev_err(priv->dev, "failed to alloc packet\n"); ++ client = aspeed_mctp_create_client(priv); ++ if (!client) + return -ENOMEM; -+ } + -+ memcpy((u8 *)&packet->data.hdr, data, PCIE_VDM_HDR_SIZE); -+ memcpy((u8 *)&packet->data.payload, data + PCIE_VDM_HDR_SIZE, size); -+ packet->size = (size + PCIE_VDM_HDR_SIZE); ++ file->private_data = client; + -+ rc = aspeed_mctp_send_packet(priv->default_client, packet); -+ if (rc) { -+ dev_err(priv->dev, "failed to send packet\n"); -+ aspeed_mctp_packet_free(packet); -+ return rc; -+ } + return 0; +} + -+static u8 *aspeed_mctp_pcie_vdm_op_recv_pkt(struct device *dev) ++void aspeed_mctp_delete_client(struct mctp_client *client) +{ -+ struct platform_device *pdev; -+ struct aspeed_mctp *priv; -+ struct mctp_pcie_packet *rx_packet; ++ struct aspeed_mctp *priv = client->priv; ++ struct mctp_type_handler *handler, *tmp; + -+ pdev = to_platform_device(dev); -+ priv = platform_get_drvdata(pdev); -+ rx_packet = aspeed_mctp_receive_packet(priv->default_client, 0); ++ spin_lock_bh(&priv->clients_lock); + -+ if (IS_ERR(rx_packet)) { -+ if (PTR_ERR(rx_packet) == -ETIME) { -+ dev_dbg(priv->dev, "no packet received\n"); -+ } else { -+ dev_err(priv->dev, "failed to receive packet: %ld\n", -+ PTR_ERR(rx_packet)); ++ list_del(&client->link); ++ ++ if (priv->default_client == client) ++ priv->default_client = NULL; ++ ++ list_for_each_entry_safe(handler, tmp, &priv->mctp_type_handlers, ++ link) { ++ if (handler->client == client) { ++ list_del(&handler->link); ++ kfree(handler); + } -+ return (u8 *)rx_packet; + } -+ return (u8 *)&rx_packet->data; ++ spin_unlock_bh(&priv->clients_lock); ++ ++ /* Disable the tasklet to appease lockdep */ ++ local_bh_disable(); ++ aspeed_mctp_client_put(client); ++ local_bh_enable(); +} ++EXPORT_SYMBOL_GPL(aspeed_mctp_delete_client); + -+static void aspeed_mctp_pcie_vdm_op_uninit(struct device *dev) ++static int aspeed_mctp_release(struct inode *inode, struct file *file) +{ -+ struct platform_device *pdev; -+ struct aspeed_mctp *priv; ++ struct mctp_client *client = file->private_data; + -+ pdev = to_platform_device(dev); -+ priv = platform_get_drvdata(pdev); ++ aspeed_mctp_delete_client(client); + -+ aspeed_mctp_flush_all_tx_queues(priv); -+ aspeed_mctp_flush_rx_queue(priv->default_client); -+ aspeed_mctp_delete_client(priv->default_client); ++ return 0; +} + -+static const struct mctp_pcie_vdm_ops aspeed_mctp_pcie_vdm_ops = { -+ .send_packet = aspeed_mctp_pcie_vdm_op_send_pkt, -+ .recv_packet = aspeed_mctp_pcie_vdm_op_recv_pkt, -+ .free_packet = aspeed_mctp_packet_free, -+ .uninit = aspeed_mctp_pcie_vdm_op_uninit, -+}; ++#define LEN_MASK_HI GENMASK(9, 8) ++#define LEN_MASK_LO GENMASK(7, 0) ++#define PCI_VDM_HDR_LEN_MASK_LO GENMASK(31, 24) ++#define PCI_VDM_HDR_LEN_MASK_HI GENMASK(17, 16) ++#define PCIE_VDM_HDR_REQUESTER_BDF_MASK GENMASK(31, 16) + -+#endif ++int aspeed_mctp_send_packet(struct mctp_client *client, ++ struct mctp_pcie_packet *packet) ++{ ++ struct aspeed_mctp *priv = client->priv; ++ u32 *hdr_dw = (u32 *)packet->data.hdr; ++ u8 *hdr = (u8 *)packet->data.hdr; ++ u8 *payload = (u8 *)packet->data.payload; ++ u16 packet_data_sz_dw; ++ u16 pci_data_len_dw; ++ int ret; ++ u16 bdf; + -+static const struct file_operations aspeed_mctp_fops = { -+ .owner = THIS_MODULE, -+ .open = aspeed_mctp_open, -+ .release = aspeed_mctp_release, -+ .read = aspeed_mctp_read, -+ .write = aspeed_mctp_write, -+ .unlocked_ioctl = aspeed_mctp_ioctl, -+ .poll = aspeed_mctp_poll, -+}; ++ ret = _get_bdf(priv); ++ if (ret < 0) ++ return ret; ++ bdf = ret; + -+static const struct regmap_config aspeed_mctp_regmap_cfg = { -+ .reg_bits = 32, -+ .reg_stride = 4, -+ .val_bits = 32, -+ .max_register = ASPEED_G7_MCTP_PCIE_BDF, -+}; ++ /* ++ * If the data size is different from contents of PCIe VDM header, ++ * aspeed_mctp_tx_cmd will be programmed incorrectly. This may cause ++ * MCTP HW to stop working. ++ */ ++ pci_data_len_dw = FIELD_PREP(LEN_MASK_LO, FIELD_GET(PCI_VDM_HDR_LEN_MASK_LO, hdr_dw[0])) | ++ FIELD_PREP(LEN_MASK_HI, FIELD_GET(PCI_VDM_HDR_LEN_MASK_HI, hdr_dw[0])); ++ if (pci_data_len_dw == 0) /* According to PCIe Spec, 0 means 1024 DW */ ++ pci_data_len_dw = SZ_1K; + -+struct device_type aspeed_mctp_type = { -+ .name = "aspeed-mctp", -+}; ++ packet_data_sz_dw = packet->size / sizeof(u32) - sizeof(packet->data.hdr) / sizeof(u32); ++ if (packet_data_sz_dw != pci_data_len_dw) ++ return -EINVAL; + -+static void aspeed_mctp_send_pcie_uevent(struct kobject *kobj, bool ready) -+{ -+ char buf[32]; -+ char *envp[2]; ++ be32p_replace_bits(&hdr_dw[1], bdf, PCIE_VDM_HDR_REQUESTER_BDF_MASK); + -+ snprintf(buf, sizeof(buf), ASPEED_MCTP_READY "=%d", ready ? 1 : 0); -+ envp[0] = buf; -+ envp[1] = NULL; ++ /* ++ * XXX Don't update EID for MCTP Control messages - old EID may ++ * interfere with MCTP discovery flow. ++ */ ++ if (priv->eid && payload[MCTP_PAYLOAD_TYPE_OFFSET] != MCTP_HDR_TYPE_CONTROL) ++ hdr[MCTP_HDR_SRC_EID_OFFSET] = priv->eid; + -+ kobject_uevent_env(kobj, KOBJ_CHANGE, envp); ++ ret = ptr_ring_produce_bh(&client->tx_queue, packet); ++ if (!ret) ++ tasklet_hi_schedule(&priv->tx.tasklet); ++ ++ return ret; +} ++EXPORT_SYMBOL_GPL(aspeed_mctp_send_packet); + -+static void aspeed_mctp_irq_enable(struct aspeed_mctp *priv) ++struct mctp_pcie_packet *aspeed_mctp_receive_packet(struct mctp_client *client, ++ unsigned long timeout) +{ -+ u32 enable = TX_CMD_SENT_INT | TX_CMD_WRONG_INT | -+ RX_CMD_RECEIVE_INT | RX_CMD_NO_MORE_INT; ++ struct aspeed_mctp *priv = client->priv; ++ int ret; + -+ regmap_write(priv->map, ASPEED_MCTP_INT_EN, enable); -+} ++ ret = _get_bdf(priv); ++ if (ret < 0) ++ return ERR_PTR(ret); + -+static void aspeed_mctp_irq_disable(struct aspeed_mctp *priv) -+{ -+ regmap_write(priv->map, ASPEED_MCTP_INT_EN, 0); ++ ret = wait_event_interruptible_timeout(client->wait_queue, ++ __ptr_ring_peek(&client->rx_queue), ++ timeout); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ else if (ret == 0) ++ return ERR_PTR(-ETIME); ++ ++ return ptr_ring_consume_bh(&client->rx_queue); +} ++EXPORT_SYMBOL_GPL(aspeed_mctp_receive_packet); + -+static void aspeed_mctp_pcie_setup(struct aspeed_mctp *priv) ++void aspeed_mctp_flush_rx_queue(struct mctp_client *client) +{ -+ int ret; -+ u8 tx_max_payload_size; -+ u8 rx_max_payload_size; -+ struct kobject *kobj = &priv->mctp_miscdev.this_device->kobj; ++ struct mctp_pcie_packet *packet; + -+ ret = _get_bdf(priv); ++ while ((packet = ptr_ring_consume_bh(&client->rx_queue))) ++ aspeed_mctp_packet_free(packet); ++} ++EXPORT_SYMBOL_GPL(aspeed_mctp_flush_rx_queue); + -+ if (ret >= 0) { -+ cancel_delayed_work(&priv->pcie.rst_dwork); -+ if (priv->match_data->need_address_mapping) -+ regmap_update_bits(priv->map, ASPEED_MCTP_EID, -+ MEMORY_SPACE_MAPPING, BIT(31)); ++static ssize_t aspeed_mctp_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct mctp_client *client = file->private_data; ++ struct aspeed_mctp *priv = client->priv; ++ struct mctp_pcie_packet *rx_packet; ++ u32 mctp_ctrl; ++ u32 mctp_int_sts; + -+ /* Only set TX MPS since HW will parse RX packet to decide how many bytes to receive -+ * based on the length field in PCIe VDM header. -+ */ -+ if (priv->match_data->dma_need_64bits_width) { -+ tx_max_payload_size = -+ FIELD_GET(TX_MAX_PAYLOAD_SIZE_MASK, -+ ilog2(ASPEED_MCTP_MTU >> 6)); -+ } else { -+ /* -+ * In ast2600, tx som and eom will not match expected result. -+ * e.g. When Maximum Transmit Unit (MTU) set to 64 byte, and then transfer -+ * size set between 61 ~ 124 (MTU-3 ~ 2*MTU-4), the engine will set all -+ * packet vdm header eom to 1, no matter what it setted. To fix that -+ * issue, the driver set MTU to next level(e.g. 64 to 128). -+ */ -+ tx_max_payload_size = -+ FIELD_GET(TX_MAX_PAYLOAD_SIZE_MASK, -+ fls(ASPEED_MCTP_MTU >> 6)); -+ } ++ if (count < PCIE_MCTP_MIN_PACKET_SIZE) ++ return -EINVAL; + -+ regmap_update_bits(priv->map, ASPEED_MCTP_ENGINE_CTRL, -+ TX_MAX_PAYLOAD_SIZE_MASK | RX_MAX_PAYLOAD_SIZE_MASK, -+ (rx_max_payload_size << RX_MAX_PAYLOAD_SIZE_SHIFT) | tx_max_payload_size); ++ if (count > sizeof(rx_packet->data)) ++ count = sizeof(rx_packet->data); + -+ aspeed_mctp_flush_all_tx_queues(priv); -+ if (!priv->miss_mctp_int) { -+ aspeed_mctp_irq_enable(priv); -+ } else { -+ if (priv->rx_det_period_us) -+ schedule_delayed_work(&priv->rx_det_dwork, -+ usecs_to_jiffies(priv->rx_det_period_us)); -+ } -+ aspeed_mctp_rx_trigger(&priv->rx); -+ aspeed_mctp_send_pcie_uevent(kobj, true); -+ } else { -+ schedule_delayed_work(&priv->pcie.rst_dwork, -+ msecs_to_jiffies(1000)); ++ if (priv->miss_mctp_int) { ++ regmap_read(priv->map, ASPEED_MCTP_CTRL, &mctp_ctrl); ++ if (!(mctp_ctrl & RX_CMD_READY)) ++ priv->rx.stopped = true; ++ /* Polling the RX_CMD_RECEIVE_INT to ensure rx_tasklet can find the data */ ++ regmap_read(priv->map, ASPEED_MCTP_INT_STS, &mctp_int_sts); ++ if (mctp_int_sts & RX_CMD_RECEIVE_INT) ++ regmap_write(priv->map, ASPEED_MCTP_INT_STS, ++ mctp_int_sts); + } -+} + -+static void aspeed_mctp_reset_work(struct work_struct *work) -+{ -+ struct aspeed_mctp *priv = container_of(work, typeof(*priv), -+ pcie.rst_dwork.work); -+ struct kobject *kobj = &priv->mctp_miscdev.this_device->kobj; ++ tasklet_hi_schedule(&priv->rx.tasklet); ++ rx_packet = ptr_ring_consume_bh(&client->rx_queue); ++ if (!rx_packet) ++ return -EAGAIN; + -+ if (priv->pcie.need_uevent) { -+ aspeed_mctp_send_pcie_uevent(kobj, false); -+ priv->pcie.need_uevent = false; ++ if (copy_to_user(buf, &rx_packet->data, count)) { ++ dev_err(priv->dev, "copy to user failed\n"); ++ count = -EFAULT; + } + -+ aspeed_mctp_pcie_setup(priv); ++ aspeed_mctp_packet_free(rx_packet); ++ ++ return count; +} + -+static void aspeed_mctp_rx_detect_work(struct work_struct *work) ++static void aspeed_mctp_flush_tx_queue(struct mctp_client *client) +{ -+ struct aspeed_mctp *priv = -+ container_of(work, typeof(*priv), rx_det_dwork.work); ++ struct mctp_pcie_packet *packet; + -+ tasklet_hi_schedule(&priv->rx.tasklet); -+ schedule_delayed_work(&priv->rx_det_dwork, -+ usecs_to_jiffies(priv->rx_det_period_us)); ++ while ((packet = ptr_ring_consume_bh(&client->tx_queue))) ++ aspeed_mctp_packet_free(packet); +} + -+static void aspeed_mctp_channels_init(struct aspeed_mctp *priv) ++static void aspeed_mctp_flush_all_tx_queues(struct aspeed_mctp *priv) +{ -+ aspeed_mctp_rx_chan_init(&priv->rx); -+ aspeed_mctp_tx_chan_init(&priv->tx); ++ struct mctp_client *client; ++ ++ spin_lock_bh(&priv->clients_lock); ++ list_for_each_entry(client, &priv->clients, link) ++ aspeed_mctp_flush_tx_queue(client); ++ spin_unlock_bh(&priv->clients_lock); +} + -+static irqreturn_t aspeed_mctp_irq_handler(int irq, void *arg) ++static ssize_t aspeed_mctp_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) +{ -+ struct aspeed_mctp *priv = arg; -+ u32 handled = 0; -+ u32 status; -+ -+ regmap_read(priv->map, ASPEED_MCTP_INT_STS, &status); -+ regmap_write(priv->map, ASPEED_MCTP_INT_STS, status); ++ struct mctp_client *client = file->private_data; ++ struct aspeed_mctp *priv = client->priv; ++ struct mctp_pcie_packet *tx_packet; ++ int ret; + -+ if (status & TX_CMD_SENT_INT) { -+ tasklet_hi_schedule(&priv->tx.tasklet); -+ if (!priv->match_data->fifo_auto_surround) -+ priv->tx.rd_ptr = (priv->tx.rd_ptr + 1) % TX_PACKET_COUNT; -+ handled |= TX_CMD_SENT_INT; -+ } ++ if (count < PCIE_MCTP_MIN_PACKET_SIZE) ++ return -EINVAL; + -+ if (status & TX_CMD_WRONG_INT) { -+ /* TODO: print the actual command */ -+ dev_warn(priv->dev, "TX wrong"); ++ if (count > sizeof(tx_packet->data)) ++ return -ENOSPC; + -+ handled |= TX_CMD_WRONG_INT; ++ tx_packet = aspeed_mctp_packet_alloc(GFP_KERNEL); ++ if (!tx_packet) { ++ ret = -ENOMEM; ++ goto out; + } + -+ if (status & RX_CMD_RECEIVE_INT) { -+ tasklet_hi_schedule(&priv->rx.tasklet); -+ -+ handled |= RX_CMD_RECEIVE_INT; ++ if (copy_from_user(&tx_packet->data, buf, count)) { ++ dev_err(priv->dev, "copy from user failed\n"); ++ ret = -EFAULT; ++ goto out_packet; + } + -+ if (status & RX_CMD_NO_MORE_INT) { -+ dev_dbg(priv->dev, "RX full"); -+ priv->rx.stopped = true; -+ tasklet_hi_schedule(&priv->rx.tasklet); ++ tx_packet->size = count; + -+ handled |= RX_CMD_NO_MORE_INT; -+ } ++ ret = aspeed_mctp_send_packet(client, tx_packet); ++ if (ret) ++ goto out_packet; + -+ if (!handled) -+ return IRQ_NONE; ++ return count; + -+ return IRQ_HANDLED; ++out_packet: ++ aspeed_mctp_packet_free(tx_packet); ++out: ++ return ret; +} + -+static irqreturn_t aspeed_mctp_pcie_rst_irq_handler(int irq, void *arg) ++int aspeed_mctp_add_type_handler(struct mctp_client *client, u8 mctp_type, ++ u16 pci_vendor_id, u16 vdm_type, u16 vdm_mask) +{ -+ struct aspeed_mctp *priv = arg; ++ struct aspeed_mctp *priv = client->priv; ++ struct mctp_type_handler *handler, *new_handler; ++ int ret = 0; + -+ aspeed_mctp_channels_init(priv); ++ if (mctp_type <= MCTP_HDR_TYPE_BASE_LAST) { ++ /* Vendor, type and type mask must be zero for types 0-5 */ ++ if (pci_vendor_id != 0 || vdm_type != 0 || vdm_mask != 0) ++ return -EINVAL; ++ } else if (mctp_type == MCTP_HDR_TYPE_VDM_PCI) { ++ /* For Vendor Defined PCI type the vendor ID must be nonzero */ ++ if (pci_vendor_id == 0 || pci_vendor_id == 0xffff) ++ return -EINVAL; ++ } else { ++ return -EINVAL; ++ } + -+ priv->pcie.need_uevent = true; -+ priv->eid = 0; ++ new_handler = kzalloc(sizeof(*new_handler), GFP_KERNEL); ++ if (!new_handler) ++ return -ENOMEM; ++ new_handler->mctp_type = mctp_type; ++ new_handler->pci_vendor_id = pci_vendor_id; ++ new_handler->vdm_type = vdm_type & vdm_mask; ++ new_handler->vdm_mask = vdm_mask; ++ new_handler->client = client; + -+ schedule_delayed_work(&priv->pcie.rst_dwork, 0); ++ spin_lock_bh(&priv->clients_lock); ++ list_for_each_entry(handler, &priv->mctp_type_handlers, link) { ++ if (handler->mctp_type == new_handler->mctp_type && ++ handler->pci_vendor_id == new_handler->pci_vendor_id && ++ handler->vdm_type == new_handler->vdm_type) { ++ if (handler->client != new_handler->client) ++ ret = -EBUSY; ++ kfree(new_handler); ++ goto out_unlock; ++ } ++ } ++ list_add_tail(&new_handler->link, &priv->mctp_type_handlers); ++out_unlock: ++ spin_unlock_bh(&priv->clients_lock); + -+ return IRQ_HANDLED; ++ return ret; +} ++EXPORT_SYMBOL_GPL(aspeed_mctp_add_type_handler); + -+static void aspeed_mctp_drv_init(struct aspeed_mctp *priv) ++static int aspeed_mctp_remove_type_handler(struct mctp_client *client, ++ u8 mctp_type, u16 pci_vendor_id, ++ u16 vdm_type, u16 vdm_mask) +{ -+ INIT_LIST_HEAD(&priv->clients); -+ INIT_LIST_HEAD(&priv->mctp_type_handlers); -+ INIT_LIST_HEAD(&priv->endpoints); -+ -+ spin_lock_init(&priv->clients_lock); -+ mutex_init(&priv->endpoints_lock); ++ struct aspeed_mctp *priv = client->priv; ++ struct mctp_type_handler *handler, *tmp; ++ int ret = -EINVAL; + -+ INIT_DELAYED_WORK(&priv->pcie.rst_dwork, aspeed_mctp_reset_work); ++ vdm_type &= vdm_mask; + -+ tasklet_init(&priv->tx.tasklet, aspeed_mctp_tx_tasklet, -+ (unsigned long)&priv->tx); -+ tasklet_init(&priv->rx.tasklet, aspeed_mctp_rx_tasklet, -+ (unsigned long)&priv->rx); ++ spin_lock_bh(&priv->clients_lock); ++ list_for_each_entry_safe(handler, tmp, &priv->mctp_type_handlers, ++ link) { ++ if (handler->client == client && ++ handler->mctp_type == mctp_type && ++ handler->pci_vendor_id == pci_vendor_id && ++ handler->vdm_type == vdm_type) { ++ list_del(&handler->link); ++ kfree(handler); ++ ret = 0; ++ break; ++ } ++ } ++ spin_unlock_bh(&priv->clients_lock); ++ return ret; +} + -+static void aspeed_mctp_drv_fini(struct aspeed_mctp *priv) ++int aspeed_mctp_register_default_handler(struct mctp_client *client) +{ -+ aspeed_mctp_eid_info_list_remove(&priv->endpoints); -+ tasklet_disable(&priv->tx.tasklet); -+ tasklet_kill(&priv->tx.tasklet); -+ tasklet_disable(&priv->rx.tasklet); -+ tasklet_kill(&priv->rx.tasklet); ++ struct aspeed_mctp *priv = client->priv; ++ int ret = 0; + -+ cancel_delayed_work_sync(&priv->pcie.rst_dwork); -+ if (priv->miss_mctp_int) -+ cancel_delayed_work_sync(&priv->rx_det_dwork); ++ spin_lock_bh(&priv->clients_lock); ++ ++ if (!priv->default_client) ++ priv->default_client = client; ++ else if (priv->default_client != client) ++ ret = -EBUSY; ++ ++ spin_unlock_bh(&priv->clients_lock); ++ ++ return ret; +} ++EXPORT_SYMBOL_GPL(aspeed_mctp_register_default_handler); + -+static int aspeed_mctp_resources_init(struct aspeed_mctp *priv) ++static int ++aspeed_mctp_register_type_handler(struct mctp_client *client, ++ void __user *userbuf) +{ -+ struct platform_device *pdev = to_platform_device(priv->dev); -+ void __iomem *regs; ++ struct aspeed_mctp *priv = client->priv; ++ struct aspeed_mctp_type_handler_ioctl handler; + -+ regs = devm_platform_ioremap_resource(pdev, 0); -+ if (IS_ERR(regs)) { -+ dev_err(priv->dev, "Failed to get regmap!\n"); -+ return PTR_ERR(regs); ++ if (copy_from_user(&handler, userbuf, sizeof(handler))) { ++ dev_err(priv->dev, "copy from user failed\n"); ++ return -EFAULT; + } + -+ priv->map = devm_regmap_init_mmio(priv->dev, regs, -+ &aspeed_mctp_regmap_cfg); -+ if (IS_ERR(priv->map)) -+ return PTR_ERR(priv->map); ++ return aspeed_mctp_add_type_handler(client, handler.mctp_type, ++ handler.pci_vendor_id, ++ handler.vendor_type, ++ handler.vendor_type_mask); ++} + -+ priv->reset = -+ priv->rc_f ? -+ devm_reset_control_get_by_index(priv->dev, 0) : -+ devm_reset_control_get_shared_by_index(priv->dev, 0); -+ if (IS_ERR(priv->reset)) { -+ dev_err(priv->dev, "Failed to get reset!\n"); -+ return PTR_ERR(priv->reset); -+ } ++static int ++aspeed_mctp_unregister_type_handler(struct mctp_client *client, ++ void __user *userbuf) ++{ ++ struct aspeed_mctp *priv = client->priv; ++ struct aspeed_mctp_type_handler_ioctl handler; + -+ if (priv->rc_f) { -+ priv->reset_dma = devm_reset_control_get_shared_by_index(priv->dev, 1); -+ if (IS_ERR(priv->reset_dma)) { -+ dev_err(priv->dev, "Failed to get ep reset!\n"); -+ return PTR_ERR(priv->reset_dma); -+ } -+ } -+ priv->pcie.map = -+ syscon_regmap_lookup_by_phandle(priv->dev->of_node, -+ "aspeed,pcieh"); -+ if (IS_ERR(priv->pcie.map)) { -+ dev_err(priv->dev, "Failed to find PCIe Host regmap!\n"); -+ return PTR_ERR(priv->pcie.map); ++ if (copy_from_user(&handler, userbuf, sizeof(handler))) { ++ dev_err(priv->dev, "copy from user failed\n"); ++ return -EFAULT; + } + -+ platform_set_drvdata(pdev, priv); ++ return aspeed_mctp_remove_type_handler(client, handler.mctp_type, ++ handler.pci_vendor_id, ++ handler.vendor_type, ++ handler.vendor_type_mask); ++} ++ ++static int ++aspeed_mctp_filter_eid(struct aspeed_mctp *priv, void __user *userbuf) ++{ ++ struct aspeed_mctp_filter_eid eid; ++ ++ if (copy_from_user(&eid, userbuf, sizeof(eid))) { ++ dev_err(priv->dev, "copy from user failed\n"); ++ return -EFAULT; ++ } + ++ if (eid.enable) { ++ regmap_update_bits(priv->map, ASPEED_MCTP_EID, ++ MCTP_EID, eid.eid); ++ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, ++ MATCHING_EID, MATCHING_EID); ++ } else { ++ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, ++ MATCHING_EID, 0); ++ } + return 0; +} + -+static void aspeed_release_rmem(void *d) ++static int aspeed_mctp_get_bdf(struct aspeed_mctp *priv, void __user *userbuf) +{ -+ of_reserved_mem_device_release(d); ++ struct aspeed_mctp_get_bdf bdf = { _get_bdf(priv) }; ++ ++ if (copy_to_user(userbuf, &bdf, sizeof(bdf))) { ++ dev_err(priv->dev, "copy to user failed\n"); ++ return -EFAULT; ++ } ++ return 0; +} + -+static int aspeed_mctp_dma_init(struct aspeed_mctp *priv) ++static int ++aspeed_mctp_get_medium_id(struct aspeed_mctp *priv, void __user *userbuf) +{ -+ struct mctp_channel *tx = &priv->tx; -+ struct mctp_channel *rx = &priv->rx; -+ size_t alloc_size; -+ int ret = -ENOMEM; -+ -+ BUILD_BUG_ON(TX_PACKET_COUNT >= TX_MAX_PACKET_COUNT); -+ BUILD_BUG_ON(RX_PACKET_COUNT >= RX_MAX_PACKET_COUNT); ++ struct aspeed_mctp_get_medium_id id = { 0x09 }; /* PCIe revision 2.0 */ + -+ ret = of_reserved_mem_device_init(priv->dev); -+ if (ret) { -+ dev_err(priv->dev, "device does not have specific DMA pool: %d\n", -+ ret); -+ return ret; ++ if (copy_to_user(userbuf, &id, sizeof(id))) { ++ dev_err(priv->dev, "copy to user failed\n"); ++ return -EFAULT; + } ++ return 0; ++} + -+ ret = devm_add_action_or_reset(priv->dev, aspeed_release_rmem, -+ priv->dev); -+ if (ret) -+ return ret; ++static int ++aspeed_mctp_get_mtu(struct aspeed_mctp *priv, void __user *userbuf) ++{ ++ struct aspeed_mctp_get_mtu id = { ASPEED_MCTP_MTU }; + -+ ret = dma_set_mask_and_coherent(priv->dev, DMA_BIT_MASK(64)); -+ if (ret) { -+ dev_err(priv->dev, "cannot set 64-bits DMA mask\n"); -+ return ret; ++ if (copy_to_user(userbuf, &id, sizeof(id))) { ++ dev_err(priv->dev, "copy to user failed\n"); ++ return -EFAULT; + } ++ return 0; ++} + -+ alloc_size = PAGE_ALIGN(priv->rx_packet_count * priv->match_data->packet_unit_size); -+ rx->data.vaddr = -+ dma_alloc_coherent(priv->dev, alloc_size, &rx->data.dma_handle, GFP_KERNEL); ++int aspeed_mctp_get_eid_bdf(struct mctp_client *client, u8 eid, u16 *bdf) ++{ ++ struct aspeed_mctp_endpoint *endpoint; ++ int ret = -ENOENT; + -+ if (!rx->data.vaddr) -+ return -ENOMEM; ++ mutex_lock(&client->priv->endpoints_lock); ++ list_for_each_entry(endpoint, &client->priv->endpoints, link) { ++ if (endpoint->data.eid_info.eid == eid) { ++ *bdf = endpoint->data.eid_info.bdf; ++ ret = 0; ++ break; ++ } ++ } ++ mutex_unlock(&client->priv->endpoints_lock); + -+ alloc_size = PAGE_ALIGN(priv->rx_packet_count * priv->match_data->rx_cmd_size); -+ rx->cmd.vaddr = dma_alloc_coherent(priv->dev, alloc_size, &rx->cmd.dma_handle, GFP_KERNEL); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(aspeed_mctp_get_eid_bdf); + -+ if (!rx->cmd.vaddr) -+ goto out_rx_cmd; ++int aspeed_mctp_get_eid(struct mctp_client *client, u16 bdf, ++ u8 domain_id, u8 *eid) ++{ ++ struct aspeed_mctp_endpoint *endpoint; ++ int ret = -ENOENT; + -+ alloc_size = PAGE_ALIGN(TX_PACKET_COUNT * priv->match_data->packet_unit_size); -+ tx->data.vaddr = -+ dma_alloc_coherent(priv->dev, alloc_size, &tx->data.dma_handle, GFP_KERNEL); ++ mutex_lock(&client->priv->endpoints_lock); + -+ if (!tx->data.vaddr) -+ goto out_tx_data; -+ alloc_size = PAGE_ALIGN(TX_PACKET_COUNT * priv->match_data->tx_cmd_size); -+ tx->cmd.vaddr = dma_alloc_coherent(priv->dev, alloc_size, &tx->cmd.dma_handle, GFP_KERNEL); ++ list_for_each_entry(endpoint, &client->priv->endpoints, link) { ++ if (endpoint->data.eid_ext_info.domain_id == domain_id && ++ endpoint->data.eid_ext_info.bdf == bdf) { ++ *eid = endpoint->data.eid_ext_info.eid; ++ ret = 0; ++ break; ++ } ++ } + -+ if (!tx->cmd.vaddr) -+ goto out_tx_cmd; ++ mutex_unlock(&client->priv->endpoints_lock); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(aspeed_mctp_get_eid); + -+ return 0; -+out_tx_cmd: -+ alloc_size = PAGE_ALIGN(TX_PACKET_COUNT * -+ priv->match_data->packet_unit_size); -+ dma_free_coherent(priv->dev, alloc_size, tx->data.vaddr, -+ tx->data.dma_handle); ++static int ++aspeed_mctp_get_eid_info(struct aspeed_mctp *priv, void __user *userbuf, ++ enum mctp_address_type addr_format) ++{ ++ int count = 0; ++ int ret = 0; ++ struct aspeed_mctp_get_eid_info get_eid; ++ struct aspeed_mctp_endpoint *endpoint; ++ void *user_ptr; ++ size_t count_to_copy; + -+out_tx_data: -+ alloc_size = PAGE_ALIGN(priv->rx_packet_count * -+ priv->match_data->rx_cmd_size); -+ dma_free_coherent(priv->dev, alloc_size, rx->cmd.vaddr, -+ rx->cmd.dma_handle); ++ if (copy_from_user(&get_eid, userbuf, sizeof(get_eid))) { ++ dev_err(priv->dev, "copy from user failed\n"); ++ return -EFAULT; ++ } + -+out_rx_cmd: -+ alloc_size = PAGE_ALIGN(priv->rx_packet_count * -+ priv->match_data->packet_unit_size); -+ dma_free_coherent(priv->dev, alloc_size, rx->data.vaddr, -+ rx->data.dma_handle); ++ mutex_lock(&priv->endpoints_lock); + -+ return -ENOMEM; -+} ++ if (get_eid.count == 0) { ++ count = priv->endpoints_count; ++ goto out_unlock; ++ } + -+static void aspeed_mctp_dma_fini(struct aspeed_mctp *priv) -+{ -+ struct mctp_channel *tx = &priv->tx; -+ struct mctp_channel *rx = &priv->rx; -+ size_t free_size; ++ user_ptr = u64_to_user_ptr(get_eid.ptr); ++ count_to_copy = get_eid.count > priv->endpoints_count ? ++ priv->endpoints_count : get_eid.count; ++ list_for_each_entry(endpoint, &priv->endpoints, link) { ++ if (endpoint->data.eid_info.eid < get_eid.start_eid) ++ continue; ++ if (count >= count_to_copy) ++ break; + -+ free_size = PAGE_ALIGN(TX_PACKET_COUNT * priv->match_data->tx_cmd_size); -+ dma_free_coherent(priv->dev, free_size, tx->cmd.vaddr, -+ tx->cmd.dma_handle); ++ if (addr_format == ASPEED_MCTP_EXTENDED_ADDR_FORMAT) ++ ret = copy_to_user(&(((struct aspeed_mctp_eid_ext_info *) ++ user_ptr)[count]), ++ &endpoint->data, ++ sizeof(struct aspeed_mctp_eid_ext_info)); ++ else ++ ret = copy_to_user(&(((struct aspeed_mctp_eid_info *) ++ user_ptr)[count]), ++ &endpoint->data, ++ sizeof(struct aspeed_mctp_eid_info)); + -+ free_size = PAGE_ALIGN(priv->rx_packet_count * -+ priv->match_data->rx_cmd_size); -+ dma_free_coherent(priv->dev, free_size, rx->cmd.vaddr, -+ rx->cmd.dma_handle); ++ if (ret) { ++ dev_err(priv->dev, "copy to user failed\n"); ++ ret = -EFAULT; ++ goto out_unlock; ++ } ++ count++; ++ } + -+ free_size = PAGE_ALIGN(TX_PACKET_COUNT * -+ priv->match_data->packet_unit_size); -+ dma_free_coherent(priv->dev, free_size, tx->data.vaddr, -+ tx->data.dma_handle); ++out_unlock: ++ get_eid.count = count; ++ if (copy_to_user(userbuf, &get_eid, sizeof(get_eid))) { ++ dev_err(priv->dev, "copy to user failed\n"); ++ ret = -EFAULT; ++ } + -+ free_size = PAGE_ALIGN(priv->rx_packet_count * -+ priv->match_data->packet_unit_size); -+ dma_free_coherent(priv->dev, free_size, rx->data.vaddr, -+ rx->data.dma_handle); ++ mutex_unlock(&priv->endpoints_lock); ++ return ret; +} + -+static int aspeed_mctp_irq_init(struct aspeed_mctp *priv) ++static int ++eid_info_cmp(void *priv, const struct list_head *a, const struct list_head *b) +{ -+ struct platform_device *pdev = to_platform_device(priv->dev); -+ int irq, ret; ++ struct aspeed_mctp_endpoint *endpoint_a; ++ struct aspeed_mctp_endpoint *endpoint_b; + -+ irq = platform_get_irq_byname_optional(pdev, "mctp"); -+ if (irq < 0) { -+ /* mctp irq is option */ -+ priv->miss_mctp_int = 1; -+ INIT_DELAYED_WORK(&priv->rx_det_dwork, aspeed_mctp_rx_detect_work); -+ } else { -+ ret = devm_request_irq(priv->dev, irq, aspeed_mctp_irq_handler, -+ IRQF_SHARED, dev_name(&pdev->dev), priv); -+ if (ret) -+ return ret; -+ aspeed_mctp_irq_enable(priv); -+ } -+ irq = platform_get_irq_byname(pdev, "pcie"); -+ if (!irq) -+ return -ENODEV; ++ if (a == b) ++ return 0; + -+ ret = devm_request_irq(priv->dev, irq, aspeed_mctp_pcie_rst_irq_handler, -+ IRQF_SHARED, dev_name(&pdev->dev), priv); -+ if (ret) -+ return ret; ++ endpoint_a = list_entry(a, typeof(*endpoint_a), link); ++ endpoint_b = list_entry(b, typeof(*endpoint_b), link); ++ ++ if (endpoint_a->data.eid_info.eid < endpoint_b->data.eid_info.eid) ++ return -1; ++ else if (endpoint_a->data.eid_info.eid > endpoint_b->data.eid_info.eid) ++ return 1; + + return 0; +} + -+static int aspeed_mctp_hw_reset(struct aspeed_mctp *priv) ++static void aspeed_mctp_eid_info_list_remove(struct list_head *list) +{ -+ int ret = 0; -+ -+ ret = reset_control_deassert(priv->reset); -+ if (ret) { -+ dev_warn(priv->dev, "Failed to deassert reset\n"); -+ return ret; -+ } ++ struct aspeed_mctp_endpoint *endpoint; ++ struct aspeed_mctp_endpoint *tmp; + -+ if (priv->rc_f) { -+ ret = reset_control_deassert(priv->reset_dma); -+ if (ret) { -+ dev_warn(priv->dev, "Failed to deassert ep reset\n"); -+ return ret; -+ } ++ list_for_each_entry_safe(endpoint, tmp, list, link) { ++ list_del(&endpoint->link); ++ kfree(endpoint); + } -+ -+ if (priv->match_data->dma_need_64bits_width) -+ ret = pcie_vdm_enable(priv->dev); -+ -+ return ret; +} + -+static int aspeed_mctp_probe(struct platform_device *pdev) ++static bool ++aspeed_mctp_eid_info_list_valid(struct list_head *list) +{ -+ struct aspeed_mctp *priv; -+ int ret, id; -+ const char *name; ++ struct aspeed_mctp_endpoint *endpoint; ++ struct aspeed_mctp_endpoint *next; + -+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ priv->dev = &pdev->dev; -+ priv->rc_f = -+ of_find_property(priv->dev->of_node, "pcie_rc", NULL) ? 1 : 0; -+ priv->match_data = of_device_get_match_data(priv->dev); ++ list_for_each_entry(endpoint, list, link) { ++ next = list_next_entry(endpoint, link); ++ if (&next->link == list) ++ break; + -+ ret = device_property_read_u32(priv->dev, "aspeed,rx-packet-count", -+ &priv->rx_packet_count); -+ if (ret) { -+ priv->rx_packet_count = RX_PACKET_COUNT; -+ } else if (priv->rx_packet_count % 4 || -+ priv->rx_packet_count >= RX_MAX_PACKET_COUNT) { -+ dev_err(priv->dev, -+ "The aspeed,rx-packet-count:%d should be 4-aligned and less than %ld", -+ priv->rx_packet_count, RX_MAX_PACKET_COUNT); -+ ret = -EINVAL; -+ goto out; ++ /* duplicted eids */ ++ if (next->data.eid_info.eid == endpoint->data.eid_info.eid) ++ return false; + } + -+ ret = device_property_read_u32(priv->dev, "aspeed,rx-ring-count", -+ &priv->rx_ring_count); -+ if (ret) -+ priv->rx_ring_count = RX_RING_COUNT; ++ return true; ++} + -+ ret = device_property_read_u32(priv->dev, "aspeed,tx-ring-count", -+ &priv->tx_ring_count); -+ if (ret) -+ priv->tx_ring_count = TX_RING_COUNT; ++static int ++aspeed_mctp_set_eid_info(struct aspeed_mctp *priv, void __user *userbuf, ++ enum mctp_address_type addr_format) ++{ ++ struct list_head list = LIST_HEAD_INIT(list); ++ struct aspeed_mctp_set_eid_info set_eid; ++ void *user_ptr; ++ struct aspeed_mctp_endpoint *endpoint; ++ int ret = 0; ++ u8 eid = 0; ++ size_t i; + -+ ret = device_property_read_u32(priv->dev, "aspeed,rx-det-period-us", -+ &priv->rx_det_period_us); -+ if (ret) -+ priv->rx_det_period_us = 1000; ++ if (copy_from_user(&set_eid, userbuf, sizeof(set_eid))) { ++ dev_err(priv->dev, "copy from user failed\n"); ++ return -EFAULT; ++ } + -+ aspeed_mctp_drv_init(priv); ++ if (set_eid.count > ASPEED_MCTP_EID_INFO_MAX) ++ return -EINVAL; + -+ ret = aspeed_mctp_resources_init(priv); -+ if (ret) { -+ dev_err(priv->dev, "Failed to init resources\n"); -+ goto out_drv; -+ } ++ user_ptr = u64_to_user_ptr(set_eid.ptr); ++ for (i = 0; i < set_eid.count; i++) { ++ endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL); ++ if (!endpoint) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ memset(endpoint, 0, sizeof(*endpoint)); + -+#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM -+ struct net_device *ndev; -+ struct mctp_client *client; ++ if (addr_format == ASPEED_MCTP_EXTENDED_ADDR_FORMAT) ++ ret = copy_from_user(&endpoint->data, ++ &(((struct aspeed_mctp_eid_ext_info *) ++ user_ptr)[i]), ++ sizeof(struct aspeed_mctp_eid_ext_info)); ++ else ++ ret = copy_from_user(&endpoint->data, ++ &(((struct aspeed_mctp_eid_info *) ++ user_ptr)[i]), ++ sizeof(struct aspeed_mctp_eid_info)); + -+ /** use priv's default client to send/receive mctp packets */ -+ client = aspeed_mctp_create_client(priv); -+ aspeed_mctp_register_default_handler(client); ++ if (ret) { ++ dev_err(priv->dev, "copy from user failed\n"); ++ kfree(endpoint); ++ ret = -EFAULT; ++ goto out; ++ } + -+ ndev = mctp_pcie_vdm_add_dev(priv->dev, &aspeed_mctp_pcie_vdm_ops); -+ if (IS_ERR(ndev)) { -+ dev_err(priv->dev, "Failed to add mctp pcie vdm device Err %ld\n", PTR_ERR(ndev)); -+ goto out_drv; -+ } -+ priv->ndev = ndev; -+#endif ++ /* Detect self EID */ ++ if (_get_bdf(priv) == endpoint->data.eid_info.bdf) { ++ /* ++ * XXX Use smallest EID with matching BDF. ++ * On some platforms there could be multiple endpoints ++ * with same BDF in routing table. ++ */ ++ if (eid == 0 || endpoint->data.eid_info.eid < eid) ++ eid = endpoint->data.eid_info.eid; ++ } + -+ ret = aspeed_mctp_dma_init(priv); -+ if (ret) { -+ dev_err(priv->dev, "Failed to init DMA\n"); -+ goto out_drv; ++ list_add_tail(&endpoint->link, &list); + } + -+ ret = aspeed_mctp_hw_reset(priv); -+ if (ret) -+ goto out_drv; ++ list_sort(NULL, &list, eid_info_cmp); ++ if (!aspeed_mctp_eid_info_list_valid(&list)) { ++ ret = -EINVAL; ++ goto out; ++ } + -+ aspeed_mctp_channels_init(priv); ++ mutex_lock(&priv->endpoints_lock); ++ if (list_empty(&priv->endpoints)) ++ list_splice_init(&list, &priv->endpoints); ++ else ++ list_swap(&list, &priv->endpoints); ++ priv->endpoints_count = set_eid.count; ++ priv->eid = eid; ++ mutex_unlock(&priv->endpoints_lock); ++out: ++ aspeed_mctp_eid_info_list_remove(&list); ++ return ret; ++} + -+ id = of_alias_get_id(priv->dev->of_node, "mctp"); -+ if (id < 0) -+ return id; -+ priv->mctp_miscdev.parent = priv->dev; -+ priv->mctp_miscdev.minor = MISC_DYNAMIC_MINOR; -+ priv->mctp_miscdev.name = devm_kasprintf(priv->dev, GFP_KERNEL, "aspeed-mctp%d", id); -+ priv->mctp_miscdev.fops = &aspeed_mctp_fops; -+ ret = misc_register(&priv->mctp_miscdev); -+ if (ret) { -+ dev_err(priv->dev, "Failed to register miscdev\n"); -+ goto out_dma; -+ } -+ priv->mctp_miscdev.this_device->type = &aspeed_mctp_type; ++static int aspeed_mctp_set_own_eid(struct aspeed_mctp *priv, void __user *userbuf) ++{ ++ struct aspeed_mctp_set_own_eid data; + -+ ret = aspeed_mctp_irq_init(priv); -+ if (ret) { -+ dev_err(priv->dev, "Failed to init IRQ!\n"); -+ goto out_dma; ++ if (copy_from_user(&data, userbuf, sizeof(data))) { ++ dev_err(priv->dev, "copy from user failed\n"); ++ return -EFAULT; + } -+ aspeed_mctp_pcie_setup(priv); + -+ name = devm_kasprintf(priv->dev, GFP_KERNEL, "peci-mctp%d", id); -+ priv->peci_mctp = -+ platform_device_register_data(priv->dev, name, PLATFORM_DEVID_NONE, NULL, 0); -+ if (IS_ERR(priv->peci_mctp)) -+ dev_err(priv->dev, "Failed to register peci-mctp device\n"); ++ priv->eid = data.eid; + + return 0; -+ -+out_dma: -+ aspeed_mctp_dma_fini(priv); -+out_drv: -+ aspeed_mctp_drv_fini(priv); -+out: -+ dev_err(&pdev->dev, "Failed to probe Aspeed MCTP: %d\n", ret); -+ return ret; +} + -+static void aspeed_mctp_remove(struct platform_device *pdev) ++static long ++aspeed_mctp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ -+ struct aspeed_mctp *priv = platform_get_drvdata(pdev); ++ struct mctp_client *client = file->private_data; ++ struct aspeed_mctp *priv = client->priv; ++ void __user *userbuf = (void __user *)arg; ++ int ret; + -+#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM -+ mctp_pcie_vdm_remove_dev(priv->ndev); -+#endif ++ switch (cmd) { ++ case ASPEED_MCTP_IOCTL_FILTER_EID: ++ ret = aspeed_mctp_filter_eid(priv, userbuf); ++ break; + -+ platform_device_unregister(priv->peci_mctp); ++ case ASPEED_MCTP_IOCTL_GET_BDF: ++ ret = aspeed_mctp_get_bdf(priv, userbuf); ++ break; + -+ misc_deregister(&priv->mctp_miscdev); ++ case ASPEED_MCTP_IOCTL_GET_MEDIUM_ID: ++ ret = aspeed_mctp_get_medium_id(priv, userbuf); ++ break; + -+ aspeed_mctp_irq_disable(priv); ++ case ASPEED_MCTP_IOCTL_GET_MTU: ++ ret = aspeed_mctp_get_mtu(priv, userbuf); ++ break; + -+ aspeed_mctp_dma_fini(priv); ++ case ASPEED_MCTP_IOCTL_REGISTER_DEFAULT_HANDLER: ++ ret = aspeed_mctp_register_default_handler(client); ++ break; + -+ aspeed_mctp_drv_fini(priv); -+} ++ case ASPEED_MCTP_IOCTL_REGISTER_TYPE_HANDLER: ++ ret = aspeed_mctp_register_type_handler(client, userbuf); ++ break; + -+static const struct aspeed_mctp_match_data ast2500_mctp_match_data = { -+ .rx_cmd_size = sizeof(struct aspeed_mctp_rx_cmd), -+ .tx_cmd_size = sizeof(struct aspeed_mctp_tx_cmd), -+ .packet_unit_size = 128, -+ .need_address_mapping = true, -+ .vdm_hdr_direct_xfer = false, -+ .fifo_auto_surround = false, -+}; ++ case ASPEED_MCTP_IOCTL_UNREGISTER_TYPE_HANDLER: ++ ret = aspeed_mctp_unregister_type_handler(client, userbuf); ++ break; + -+static const struct aspeed_mctp_match_data ast2600_mctp_match_data = { -+ .rx_cmd_size = sizeof(u32), -+ .tx_cmd_size = sizeof(struct aspeed_mctp_tx_cmd), -+ .packet_unit_size = sizeof(struct mctp_pcie_packet_data), -+ .need_address_mapping = false, -+ .vdm_hdr_direct_xfer = true, -+ .fifo_auto_surround = true, -+}; ++ case ASPEED_MCTP_IOCTL_GET_EID_INFO: ++ ret = aspeed_mctp_get_eid_info(priv, userbuf, ASPEED_MCTP_GENERIC_ADDR_FORMAT); ++ break; + -+static const struct aspeed_mctp_match_data ast2700_mctp0_match_data = { -+ .rx_cmd_size = sizeof(struct aspeed_mctp_rx_cmd), -+ .tx_cmd_size = sizeof(struct aspeed_g7_mctp_tx_cmd), -+ .packet_unit_size = sizeof(struct mctp_pcie_packet_data), -+ .need_address_mapping = false, -+ .vdm_hdr_direct_xfer = true, -+ .fifo_auto_surround = true, -+ .dma_need_64bits_width = true, -+ .scu_pcie_ctrl_offset = ASPEED_G7_SCU_PCIE0_CTRL_OFFSET, -+}; ++ case ASPEED_MCTP_IOCTL_GET_EID_EXT_INFO: ++ ret = aspeed_mctp_get_eid_info(priv, userbuf, ASPEED_MCTP_EXTENDED_ADDR_FORMAT); ++ break; + -+static const struct aspeed_mctp_match_data ast2700_mctp1_match_data = { -+ .rx_cmd_size = sizeof(struct aspeed_mctp_rx_cmd), -+ .tx_cmd_size = sizeof(struct aspeed_g7_mctp_tx_cmd), -+ .packet_unit_size = sizeof(struct mctp_pcie_packet_data), -+ .need_address_mapping = false, -+ .vdm_hdr_direct_xfer = true, -+ .fifo_auto_surround = true, -+ .dma_need_64bits_width = true, -+ .scu_pcie_ctrl_offset = ASPEED_G7_SCU_PCIE1_CTRL_OFFSET, -+}; ++ case ASPEED_MCTP_IOCTL_SET_EID_INFO: ++ ret = aspeed_mctp_set_eid_info(priv, userbuf, ASPEED_MCTP_GENERIC_ADDR_FORMAT); ++ break; + -+static const struct of_device_id aspeed_mctp_match_table[] = { -+ { .compatible = "aspeed,ast2500-mctp", .data = &ast2500_mctp_match_data}, -+ { .compatible = "aspeed,ast2600-mctp", .data = &ast2600_mctp_match_data}, -+ { .compatible = "aspeed,ast2700-mctp0", .data = &ast2700_mctp0_match_data}, -+ { .compatible = "aspeed,ast2700-mctp1", .data = &ast2700_mctp1_match_data}, -+ { } -+}; ++ case ASPEED_MCTP_IOCTL_SET_EID_EXT_INFO: ++ ret = aspeed_mctp_set_eid_info(priv, userbuf, ASPEED_MCTP_EXTENDED_ADDR_FORMAT); ++ break; + -+static struct platform_driver aspeed_mctp_driver = { -+ .driver = { -+ .name = "aspeed-mctp", -+ .of_match_table = of_match_ptr(aspeed_mctp_match_table), -+ }, -+ .probe = aspeed_mctp_probe, -+ .remove = aspeed_mctp_remove, -+}; ++ case ASPEED_MCTP_IOCTL_SET_OWN_EID: ++ ret = aspeed_mctp_set_own_eid(priv, userbuf); ++ break; + -+static int __init aspeed_mctp_init(void) -+{ -+ packet_cache = -+ kmem_cache_create_usercopy("mctp-packet", -+ sizeof(struct mctp_pcie_packet), -+ 0, 0, 0, -+ sizeof(struct mctp_pcie_packet), -+ NULL); -+ if (!packet_cache) -+ return -ENOMEM; ++ default: ++ dev_err(priv->dev, "Command not found\n"); ++ ret = -ENOTTY; ++ } + -+ return platform_driver_register(&aspeed_mctp_driver); ++ return ret; +} + -+static void __exit aspeed_mctp_exit(void) ++static __poll_t aspeed_mctp_poll(struct file *file, ++ struct poll_table_struct *pt) +{ -+ platform_driver_unregister(&aspeed_mctp_driver); -+ kmem_cache_destroy(packet_cache); ++ struct mctp_client *client = file->private_data; ++ __poll_t ret = 0; ++ struct aspeed_mctp *priv = client->priv; ++ struct mctp_channel *rx = &priv->rx; ++ u32 mctp_ctrl; ++ u32 mctp_int_sts; ++ ++ if (priv->miss_mctp_int) { ++ regmap_read(priv->map, ASPEED_MCTP_CTRL, &mctp_ctrl); ++ if (!(mctp_ctrl & RX_CMD_READY)) ++ rx->stopped = true; ++ /* Polling the RX_CMD_RECEIVE_INT to ensure rx_tasklet can find the data */ ++ regmap_read(priv->map, ASPEED_MCTP_INT_STS, &mctp_int_sts); ++ if (mctp_int_sts & RX_CMD_RECEIVE_INT) ++ regmap_write(priv->map, ASPEED_MCTP_INT_STS, ++ mctp_int_sts); ++ } ++ ++ tasklet_hi_schedule(&priv->rx.tasklet); ++ poll_wait(file, &client->wait_queue, pt); ++ ++ if (!ptr_ring_full_bh(&client->tx_queue)) ++ ret |= EPOLLOUT; ++ ++ if (__ptr_ring_peek(&client->rx_queue)) ++ ret |= EPOLLIN; ++ ++ return ret; +} + -+module_init(aspeed_mctp_init) -+module_exit(aspeed_mctp_exit) ++#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM ++static int aspeed_mctp_pcie_vdm_op_send_pkt(struct device *dev, ++ u8 *data, size_t size) ++{ ++ struct mctp_pcie_packet *packet; ++ struct platform_device *pdev; ++ struct aspeed_mctp *priv; ++ int rc; + -+MODULE_DEVICE_TABLE(of, aspeed_mctp_match_table); -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Iwona Winiarska "); -+MODULE_DESCRIPTION("Aspeed MCTP driver"); -diff --git a/drivers/soc/aspeed/aspeed-p2a-ctrl.c b/drivers/soc/aspeed/aspeed-p2a-ctrl.c ---- a/drivers/soc/aspeed/aspeed-p2a-ctrl.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-p2a-ctrl.c 2025-12-23 10:16:21.124032669 +0000 -@@ -431,7 +431,7 @@ - .of_match_table = aspeed_p2a_ctrl_match, - }, - .probe = aspeed_p2a_ctrl_probe, -- .remove_new = aspeed_p2a_ctrl_remove, -+ .remove = aspeed_p2a_ctrl_remove, - }; - - module_platform_driver(aspeed_p2a_ctrl_driver); -diff --git a/drivers/soc/aspeed/aspeed-pcie-mmbi.c b/drivers/soc/aspeed/aspeed-pcie-mmbi.c ---- a/drivers/soc/aspeed/aspeed-pcie-mmbi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-pcie-mmbi.c 2025-12-23 10:16:21.125032653 +0000 -@@ -0,0 +1,962 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+// Copyright (C) ASPEED Technology Inc. ++ pdev = to_platform_device(dev); ++ priv = platform_get_drvdata(pdev); ++ // freed at aspeed-mctp tx tasklet or send failure ++ packet = aspeed_mctp_packet_alloc(GFP_KERNEL); + -+#include -+#include -+#include -+#include ++ if (!packet) { ++ dev_err(priv->dev, "failed to alloc packet\n"); ++ return -ENOMEM; ++ } + -+#include -+#include -+#include -+#include -+#include -+#include ++ memcpy((u8 *)&packet->data.hdr, data, PCIE_VDM_HDR_SIZE); ++ memcpy((u8 *)&packet->data.payload, data + PCIE_VDM_HDR_SIZE, size); ++ packet->size = (size + PCIE_VDM_HDR_SIZE); + -+#include -+#include ++ rc = aspeed_mctp_send_packet(priv->default_client, packet); ++ if (rc) { ++ dev_err(priv->dev, "failed to send packet\n"); ++ aspeed_mctp_packet_free(packet); ++ return rc; ++ } ++ return 0; ++} + -+#include -+#include -+#include -+#include -+#include -+#include ++static u8 *aspeed_mctp_pcie_vdm_op_recv_pkt(struct device *dev) ++{ ++ struct platform_device *pdev; ++ struct aspeed_mctp *priv; ++ struct mctp_pcie_packet *rx_packet; + -+#include -+#include -+#include -+#include -+#include ++ pdev = to_platform_device(dev); ++ priv = platform_get_drvdata(pdev); ++ rx_packet = aspeed_mctp_receive_packet(priv->default_client, 0); + -+#include "aspeed-pcie-mmbi.h" ++ if (IS_ERR(rx_packet)) { ++ if (PTR_ERR(rx_packet) == -ETIME) { ++ dev_dbg(priv->dev, "no packet received\n"); ++ } else { ++ dev_err(priv->dev, "failed to receive packet: %ld\n", ++ PTR_ERR(rx_packet)); ++ } ++ return (u8 *)rx_packet; ++ } ++ return (u8 *)&rx_packet->data; ++} + -+/* AST2700 E2M */ -+#define ASPEED_E2M_EVENT 0x0D0 -+#define ASPEED_E2M_EVENT_SET 0x0D4 -+#define ASPEED_E2M_EVENT_CLR 0x0D8 -+#define ASPEED_E2M_EVENT_EN 0x0DC -+#define ASPEED_E2M_ADRMAP00 0x100 -+#define ASPEED_E2M_WIRQA0 0x180 -+#define ASPEED_E2M_WIRQV0 0x1C0 -+#define ASPEED_E2M_SPROT_SIDG0 0x210 -+#define ASPEED_E2M_SPROT_CTL0 0x280 -+#define ASPEED_E2M_SPROT_ADR0 0x2C0 ++static void aspeed_mctp_pcie_vdm_op_uninit(struct device *dev) ++{ ++ struct platform_device *pdev; ++ struct aspeed_mctp *priv; + -+/* AST2700 SCU */ -+#define ASPEED_SCU_DECODE_DEV BIT(18) -+#define ASPEED_SCU_INT_EN BIT(23) -+struct aspeed_platform { -+ int (*mmbi_init)(struct platform_device *pdev); ++ pdev = to_platform_device(dev); ++ priv = platform_get_drvdata(pdev); ++ ++ aspeed_mctp_flush_all_tx_queues(priv); ++ aspeed_mctp_flush_rx_queue(priv->default_client); ++ aspeed_mctp_delete_client(priv->default_client); ++} ++ ++static const struct mctp_pcie_vdm_ops aspeed_mctp_pcie_vdm_ops = { ++ .send_packet = aspeed_mctp_pcie_vdm_op_send_pkt, ++ .recv_packet = aspeed_mctp_pcie_vdm_op_recv_pkt, ++ .free_packet = aspeed_mctp_packet_free, ++ .uninit = aspeed_mctp_pcie_vdm_op_uninit, +}; + -+struct aspeed_pcie_mmbi { -+ struct device *dev; -+ struct regmap *device; -+ struct regmap *e2m; -+ int irq; -+ const struct aspeed_platform *platform; -+ /* E2M index */ -+ int id; -+ int pid; -+ int scu_bar_offset; -+ int e2m_index; -+ int e2m_h2b_int; ++#endif + -+ /* Memory Mapping */ -+ void __iomem *mem_virt; -+ dma_addr_t mem_phy; -+ phys_addr_t mem_size; ++static const struct file_operations aspeed_mctp_fops = { ++ .owner = THIS_MODULE, ++ .open = aspeed_mctp_open, ++ .release = aspeed_mctp_release, ++ .read = aspeed_mctp_read, ++ .write = aspeed_mctp_write, ++ .unlocked_ioctl = aspeed_mctp_ioctl, ++ .poll = aspeed_mctp_poll, ++}; + -+ struct aspeed_mmbi_channel chan; ++static const struct regmap_config aspeed_mctp_regmap_cfg = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = ASPEED_G7_MCTP_PCIE_BDF, +}; + -+static void mmbi_desc_init(struct aspeed_mmbi_channel *chan); ++struct device_type aspeed_mctp_type = { ++ .name = "aspeed-mctp", ++}; + -+static u8 mmbi_get_bmc_up(struct aspeed_mmbi_channel *chan) ++static void aspeed_mctp_send_pcie_uevent(struct kobject *kobj, bool ready) +{ -+ struct host_ros hros; ++ char buf[32]; ++ char *envp[2]; + -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); ++ snprintf(buf, sizeof(buf), ASPEED_MCTP_READY "=%d", ready ? 1 : 0); ++ envp[0] = buf; ++ envp[1] = NULL; + -+ return hros.b_up; ++ kobject_uevent_env(kobj, KOBJ_CHANGE, envp); +} + -+static u8 mmbi_get_bmc_rdy(struct aspeed_mmbi_channel *chan) ++static void aspeed_mctp_irq_enable(struct aspeed_mctp *priv) +{ -+ struct host_ros hros; -+ -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); ++ u32 enable = TX_CMD_SENT_INT | TX_CMD_WRONG_INT | ++ RX_CMD_RECEIVE_INT | RX_CMD_NO_MORE_INT; + -+ return hros.b_rdy; ++ regmap_write(priv->map, ASPEED_MCTP_INT_EN, enable); +} + -+static u8 mmbi_get_bmc_rst(struct aspeed_mmbi_channel *chan) ++static void aspeed_mctp_irq_disable(struct aspeed_mctp *priv) +{ -+ struct host_ros hros; -+ -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); -+ -+ return hros.b_rst; ++ regmap_write(priv->map, ASPEED_MCTP_INT_EN, 0); +} + -+static u8 mmbi_get_host_rst(struct aspeed_mmbi_channel *chan) ++static void aspeed_mctp_pcie_setup(struct aspeed_mctp *priv) +{ -+ struct host_rws hrws; ++ int ret; ++ u8 tx_max_payload_size; ++ u8 rx_max_payload_size; ++ struct kobject *kobj = &priv->mctp_miscdev.this_device->kobj; + -+ memcpy_fromio(&hrws, chan->hrws_vmem, sizeof(hrws)); ++ ret = _get_bdf(priv); + -+ return hrws.h_rst; -+} ++ if (ret >= 0) { ++ cancel_delayed_work(&priv->pcie.rst_dwork); ++ if (priv->match_data->need_address_mapping) ++ regmap_update_bits(priv->map, ASPEED_MCTP_EID, ++ MEMORY_SPACE_MAPPING, BIT(31)); + -+static u8 mmbi_get_host_rdy(struct aspeed_mmbi_channel *chan) -+{ -+ struct host_rws hrws; ++ /* Only set TX MPS since HW will parse RX packet to decide how many bytes to receive ++ * based on the length field in PCIe VDM header. ++ */ ++ if (priv->match_data->dma_need_64bits_width) { ++ tx_max_payload_size = ++ FIELD_GET(TX_MAX_PAYLOAD_SIZE_MASK, ++ ilog2(ASPEED_MCTP_MTU >> 6)); ++ } else { ++ /* ++ * In ast2600, tx som and eom will not match expected result. ++ * e.g. When Maximum Transmit Unit (MTU) set to 64 byte, and then transfer ++ * size set between 61 ~ 124 (MTU-3 ~ 2*MTU-4), the engine will set all ++ * packet vdm header eom to 1, no matter what it setted. To fix that ++ * issue, the driver set MTU to next level(e.g. 64 to 128). ++ */ ++ tx_max_payload_size = ++ FIELD_GET(TX_MAX_PAYLOAD_SIZE_MASK, ++ fls(ASPEED_MCTP_MTU >> 6)); ++ } + -+ memcpy_fromio(&hrws, chan->hrws_vmem, sizeof(hrws)); ++ regmap_update_bits(priv->map, ASPEED_MCTP_ENGINE_CTRL, ++ TX_MAX_PAYLOAD_SIZE_MASK | RX_MAX_PAYLOAD_SIZE_MASK, ++ (rx_max_payload_size << RX_MAX_PAYLOAD_SIZE_SHIFT) | tx_max_payload_size); + -+ return hrws.h_rdy; ++ aspeed_mctp_flush_all_tx_queues(priv); ++ if (!priv->miss_mctp_int) { ++ aspeed_mctp_irq_enable(priv); ++ } else { ++ if (priv->rx_det_period_us) ++ schedule_delayed_work(&priv->rx_det_dwork, ++ usecs_to_jiffies(priv->rx_det_period_us)); ++ } ++ aspeed_mctp_rx_trigger(&priv->rx); ++ aspeed_mctp_send_pcie_uevent(kobj, true); ++ } else { ++ schedule_delayed_work(&priv->pcie.rst_dwork, ++ msecs_to_jiffies(1000)); ++ } +} + -+static u8 mmbi_get_host_up(struct aspeed_mmbi_channel *chan) ++static void aspeed_mctp_reset_work(struct work_struct *work) +{ -+ struct host_rws hrws; ++ struct aspeed_mctp *priv = container_of(work, typeof(*priv), ++ pcie.rst_dwork.work); ++ struct kobject *kobj = &priv->mctp_miscdev.this_device->kobj; + -+ memcpy_fromio(&hrws, chan->hrws_vmem, sizeof(hrws)); ++ if (priv->pcie.need_uevent) { ++ aspeed_mctp_send_pcie_uevent(kobj, false); ++ priv->pcie.need_uevent = false; ++ } + -+ return hrws.h_up; ++ aspeed_mctp_pcie_setup(priv); +} + -+static void mmbi_set_bmc_rst(struct aspeed_mmbi_channel *chan, bool set) ++static void aspeed_mctp_rx_detect_work(struct work_struct *work) +{ -+ struct host_ros hros; ++ struct aspeed_mctp *priv = ++ container_of(work, typeof(*priv), rx_det_dwork.work); + -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); -+ hros.b_rst = set; -+ memcpy_toio(chan->hros_vmem, &hros, sizeof(hros)); ++ tasklet_hi_schedule(&priv->rx.tasklet); ++ schedule_delayed_work(&priv->rx_det_dwork, ++ usecs_to_jiffies(priv->rx_det_period_us)); +} + -+static void mmbi_set_bmc_rdy(struct aspeed_mmbi_channel *chan, bool set) ++static void aspeed_mctp_channels_init(struct aspeed_mctp *priv) +{ -+ struct host_ros hros; -+ -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); -+ hros.b_rdy = set; -+ memcpy_toio(chan->hros_vmem, &hros, sizeof(hros)); ++ aspeed_mctp_rx_chan_init(&priv->rx); ++ aspeed_mctp_tx_chan_init(&priv->tx); +} + -+static void mmbi_set_bmc_up(struct aspeed_mmbi_channel *chan, bool set) ++static irqreturn_t aspeed_mctp_irq_handler(int irq, void *arg) +{ -+ struct host_ros hros; ++ struct aspeed_mctp *priv = arg; ++ u32 handled = 0; ++ u32 status; + -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); -+ hros.b_up = set; -+ memcpy_toio(chan->hros_vmem, &hros, sizeof(hros)); -+} ++ regmap_read(priv->map, ASPEED_MCTP_INT_STS, &status); ++ regmap_write(priv->map, ASPEED_MCTP_INT_STS, status); + -+static void raise_b2h_interrupt(struct aspeed_mmbi_channel *chan) -+{ -+ if (!chan->host_int_en) -+ return; ++ if (status & TX_CMD_SENT_INT) { ++ tasklet_hi_schedule(&priv->tx.tasklet); ++ if (!priv->match_data->fifo_auto_surround) ++ priv->tx.rd_ptr = (priv->tx.rd_ptr + 1) % TX_PACKET_COUNT; ++ handled |= TX_CMD_SENT_INT; ++ } + -+ regmap_write(chan->mmbi->e2m, ASPEED_E2M_EVENT_SET, BIT(chan->mmbi->e2m_h2b_int)); -+} ++ if (status & TX_CMD_WRONG_INT) { ++ /* TODO: print the actual command */ ++ dev_warn(priv->dev, "TX wrong"); + -+static void mmbi_clear_hros(struct aspeed_mmbi_channel *chan) -+{ -+ memset_io(chan->hros_vmem, 0, sizeof(struct host_ros)); -+} ++ handled |= TX_CMD_WRONG_INT; ++ } + -+static void mmbi_clear_hrws(struct aspeed_mmbi_channel *chan) -+{ -+ memset_io(chan->hrws_vmem, 0, sizeof(struct host_rws)); ++ if (status & RX_CMD_RECEIVE_INT) { ++ tasklet_hi_schedule(&priv->rx.tasklet); ++ ++ handled |= RX_CMD_RECEIVE_INT; ++ } ++ ++ if (status & RX_CMD_NO_MORE_INT) { ++ dev_dbg(priv->dev, "RX full"); ++ priv->rx.stopped = true; ++ tasklet_hi_schedule(&priv->rx.tasklet); ++ ++ handled |= RX_CMD_NO_MORE_INT; ++ } ++ ++ if (!handled) ++ return IRQ_NONE; ++ ++ return IRQ_HANDLED; +} + -+static void get_b2h_avail_buf_len(struct aspeed_mmbi_channel *chan, ssize_t *avail_buf_len) ++static irqreturn_t aspeed_mctp_pcie_rst_irq_handler(int irq, void *arg) +{ -+ struct device *dev = chan->dev; -+ u32 b2h_rp, b2h_wp; ++ struct aspeed_mctp *priv = arg; + -+ b2h_rp = GET_B2H_READ_POINTER(chan); -+ b2h_wp = GET_B2H_WRITE_POINTER(chan); -+ dev_dbg(dev, "MMBI B2H - b2h_rp: 0x%0x, b2h_wp: 0x%0x\n", b2h_rp, b2h_wp); ++ aspeed_mctp_channels_init(priv); + -+ if (b2h_wp >= b2h_rp) -+ *avail_buf_len = chan->b2h_cb_size - b2h_wp + b2h_rp; -+ else -+ *avail_buf_len = b2h_rp - b2h_wp; ++ priv->pcie.need_uevent = true; ++ priv->eid = 0; ++ ++ schedule_delayed_work(&priv->pcie.rst_dwork, 0); ++ ++ return IRQ_HANDLED; +} + -+static u8 mmbi_get_state(struct aspeed_mmbi_channel *chan) ++static void aspeed_mctp_drv_init(struct aspeed_mctp *priv) +{ -+ u8 state = 0; ++ INIT_LIST_HEAD(&priv->clients); ++ INIT_LIST_HEAD(&priv->mctp_type_handlers); ++ INIT_LIST_HEAD(&priv->endpoints); + -+ state = mmbi_get_bmc_up(chan) << 3; -+ state |= mmbi_get_bmc_rst(chan) << 2; -+ state |= mmbi_get_host_up(chan) << 1; -+ state |= mmbi_get_host_rst(chan); ++ spin_lock_init(&priv->clients_lock); ++ mutex_init(&priv->endpoints_lock); + -+ return state; ++ INIT_DELAYED_WORK(&priv->pcie.rst_dwork, aspeed_mctp_reset_work); ++ ++ tasklet_init(&priv->tx.tasklet, aspeed_mctp_tx_tasklet, ++ (unsigned long)&priv->tx); ++ tasklet_init(&priv->rx.tasklet, aspeed_mctp_rx_tasklet, ++ (unsigned long)&priv->rx); +} + -+static int get_mmbi_header(struct aspeed_mmbi_channel *chan, u32 *data_length, -+ u8 *type, u32 *unread_data_len, u8 *padding) ++static void aspeed_mctp_drv_fini(struct aspeed_mctp *priv) +{ -+ u32 h2b_wp, h2b_rp, b2h_wp, b2h_rp; -+ struct device *dev = chan->dev; -+ struct mmbi_header header; ++ aspeed_mctp_eid_info_list_remove(&priv->endpoints); ++ tasklet_disable(&priv->tx.tasklet); ++ tasklet_kill(&priv->tx.tasklet); ++ tasklet_disable(&priv->rx.tasklet); ++ tasklet_kill(&priv->rx.tasklet); + -+ h2b_wp = GET_H2B_WRITE_POINTER(chan); -+ h2b_rp = GET_H2B_READ_POINTER(chan); -+ b2h_wp = GET_B2H_WRITE_POINTER(chan); -+ b2h_rp = GET_B2H_READ_POINTER(chan); -+ dev_dbg(dev, "MMBI HRWS - h2b_wp: 0x%0x, b2h_rp: 0x%0x\n", h2b_wp, -+ b2h_rp); -+ dev_dbg(dev, "MMBI HROS - b2h_wp: 0x%0x, h2b_rp: 0x%0x\n", b2h_wp, -+ h2b_rp); ++ cancel_delayed_work_sync(&priv->pcie.rst_dwork); ++ if (priv->miss_mctp_int) ++ cancel_delayed_work_sync(&priv->rx_det_dwork); ++} + -+ if (h2b_wp >= h2b_rp) -+ *unread_data_len = h2b_wp - h2b_rp; -+ else -+ *unread_data_len = chan->h2b_cb_size - h2b_rp + h2b_wp; ++static int aspeed_mctp_resources_init(struct aspeed_mctp *priv) ++{ ++ struct platform_device *pdev = to_platform_device(priv->dev); ++ void __iomem *regs; + -+ if (*unread_data_len < sizeof(struct mmbi_header)) { -+ dev_dbg(dev, "No data to read(%d - %d)\n", h2b_wp, h2b_rp); -+ return -EAGAIN; ++ regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(regs)) { ++ dev_err(priv->dev, "Failed to get regmap!\n"); ++ return PTR_ERR(regs); + } + -+ dev_dbg(dev, "READ MMBI header from: 0x%lx\n", -+ (ssize_t)(chan->h2b_cb_vmem + h2b_rp)); ++ priv->map = devm_regmap_init_mmio(priv->dev, regs, ++ &aspeed_mctp_regmap_cfg); ++ if (IS_ERR(priv->map)) ++ return PTR_ERR(priv->map); + -+ /* Extract MMBI protocol - protocol type and length */ -+ if ((h2b_rp + sizeof(header)) <= chan->h2b_cb_size) { -+ memcpy_fromio(&header, chan->h2b_cb_vmem + h2b_rp, -+ sizeof(header)); -+ } else { -+ ssize_t chunk_len = chan->h2b_cb_size - h2b_rp; ++ priv->reset = ++ priv->rc_f ? ++ devm_reset_control_get_by_index(priv->dev, 0) : ++ devm_reset_control_get_shared_by_index(priv->dev, 0); ++ if (IS_ERR(priv->reset)) { ++ dev_err(priv->dev, "Failed to get reset!\n"); ++ return PTR_ERR(priv->reset); ++ } + -+ memcpy_fromio(&header, chan->h2b_cb_vmem + h2b_rp, chunk_len); -+ memcpy_fromio(((u8 *)&header) + chunk_len, chan->h2b_cb_vmem, -+ sizeof(header) - chunk_len); ++ if (priv->rc_f) { ++ priv->reset_dma = devm_reset_control_get_shared_by_index(priv->dev, 1); ++ if (IS_ERR(priv->reset_dma)) { ++ dev_err(priv->dev, "Failed to get ep reset!\n"); ++ return PTR_ERR(priv->reset_dma); ++ } ++ } ++ priv->pcie.map = ++ syscon_regmap_lookup_by_phandle(priv->dev->of_node, ++ "aspeed,pcieh"); ++ if (IS_ERR(priv->pcie.map)) { ++ dev_err(priv->dev, "Failed to find PCIe Host regmap!\n"); ++ return PTR_ERR(priv->pcie.map); + } + -+ *data_length = (header.pkt_len << 2) - sizeof(header) - header.pkt_pad; -+ *padding = header.pkt_pad; -+ *type = header.pkt_type; ++ platform_set_drvdata(pdev, priv); + + return 0; +} + -+static int mmbi_state_check(struct aspeed_mmbi_channel *chan) ++static void aspeed_release_rmem(void *d) +{ -+ enum mmbi_state current_state = mmbi_get_state(chan); -+ struct device *dev = chan->dev; -+ u32 req_data_len, unread_data_len; -+ u8 type, padding; -+ -+ switch (current_state) { -+ case INIT_MISMATCH: -+ dev_dbg(dev, "Get INIT_MISMATCH state from HOST"); -+ /* Reset MMBI data structure */ -+ mmbi_desc_init(chan); -+ /* Translat state to INIT_IN_PROGRESS */ -+ mmbi_clear_hros(chan); -+ mmbi_clear_hrws(chan); -+ /* Translat state to INIT_COMPLETED*/ -+ mmbi_set_bmc_up(chan, 1); -+ -+ dev_dbg(dev, "Change state to INIT_COMPLETED to HOST"); -+ raise_b2h_interrupt(chan); -+ return 1; -+ case NORMAL_RUNTIME: -+ if (mmbi_get_bmc_rdy(chan)) -+ return 0; -+ dev_dbg(dev, "Get NORMAL_RUNTIME state from HOST"); -+ mmbi_set_bmc_rdy(chan, 1); -+ return 1; -+ case RESET_REQ_BY_HOST: -+ dev_dbg(dev, "Get RESET_REQ_BY_HOST state from HOST"); -+ /* Stop operation */ -+ mmbi_set_bmc_rdy(chan, 0); -+ /* Change state to RESET_ACKED */ -+ mmbi_set_bmc_rst(chan, 1); -+ raise_b2h_interrupt(chan); -+ /* Change state to TRANS_TO_INIT */ -+ mmbi_set_bmc_up(chan, 0); -+ /* Reset MMBI data structure */ -+ mmbi_desc_init(chan); -+ /* Translat state to INIT_IN_PROGRESS */ -+ mmbi_clear_hros(chan); -+ mmbi_clear_hrws(chan); -+ /* Translat state to INIT_COMPLETED*/ -+ mmbi_set_bmc_up(chan, 1); -+ -+ dev_dbg(dev, "Change state to INIT_COMPLETED to HOST"); -+ raise_b2h_interrupt(chan); -+ return 1; -+ case RESET_ACKED: -+ /* Receive all packet from Host */ -+ while (get_mmbi_header(chan, &req_data_len, &type, &unread_data_len, &padding) == 0 && -+ mmbi_get_state(chan) == RESET_ACKED) -+ ; -+ /* Change state to TRANS_TO_INIT */ -+ mmbi_set_bmc_up(chan, 0); -+ /* Reset MMBI data structure */ -+ mmbi_desc_init(chan); -+ /* Translat state to INIT_IN_PROGRESS */ -+ mmbi_clear_hros(chan); -+ mmbi_clear_hrws(chan); -+ /* Translat state to INIT_COMPLETED*/ -+ mmbi_set_bmc_up(chan, 1); -+ -+ dev_dbg(dev, "Change state to INIT_COMPLETED to HOST"); -+ raise_b2h_interrupt(chan); -+ default: -+ break; -+ } -+ -+ return 0; ++ of_reserved_mem_device_release(d); +} + -+static void update_host_ros(struct aspeed_mmbi_channel *chan, unsigned int w_len, -+ unsigned int r_len) ++static int aspeed_mctp_dma_init(struct aspeed_mctp *priv) +{ -+ struct device *dev = chan->dev; -+ struct host_ros hros; -+ u32 h2b_rp, b2h_wp; ++ struct mctp_channel *tx = &priv->tx; ++ struct mctp_channel *rx = &priv->rx; ++ size_t alloc_size; ++ int ret = -ENOMEM; + -+ b2h_wp = GET_B2H_WRITE_POINTER(chan); -+ h2b_rp = GET_H2B_READ_POINTER(chan); ++ BUILD_BUG_ON(TX_PACKET_COUNT >= TX_MAX_PACKET_COUNT); ++ BUILD_BUG_ON(RX_PACKET_COUNT >= RX_MAX_PACKET_COUNT); + -+ /* Advance the B2H CB offset for next write */ -+ if ((b2h_wp + w_len) <= chan->b2h_cb_size) -+ b2h_wp += w_len; -+ else -+ b2h_wp = b2h_wp + w_len - chan->b2h_cb_size; ++ ret = dma_set_mask_and_coherent(priv->dev, DMA_BIT_MASK(64)); ++ if (ret) { ++ dev_err(priv->dev, "cannot set 64-bits DMA mask\n"); ++ return ret; ++ } + -+ /* Advance the H2B CB offset till where BMC read data */ -+ if ((h2b_rp + r_len) <= chan->h2b_cb_size) -+ h2b_rp += r_len; -+ else -+ h2b_rp = h2b_rp + r_len - chan->h2b_cb_size; ++ ret = of_reserved_mem_device_init(priv->dev); ++ if (ret) { ++ dev_err(priv->dev, "device does not have specific DMA pool: %d\n", ++ ret); ++ return ret; ++ } + -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); -+ hros.b2h_wp = FIELD_GET(B2H_WRITE_POINTER_MASK, b2h_wp); -+ hros.h2b_rp = FIELD_GET(H2B_READ_POINTER_MASK, h2b_rp); -+ memcpy_toio(chan->hros_vmem, &hros, sizeof(hros)); -+ dev_dbg(dev, "Updating HROS - h2b_rp: 0x%0x, b2h_wp: 0x%0x\n", h2b_rp, b2h_wp); ++ ret = devm_add_action_or_reset(priv->dev, aspeed_release_rmem, ++ priv->dev); ++ if (ret) ++ return ret; + -+ if (w_len != 0) -+ raise_b2h_interrupt(chan); -+} ++ alloc_size = PAGE_ALIGN(priv->rx_packet_count * priv->match_data->packet_unit_size); ++ rx->data.vaddr = ++ dma_alloc_coherent(priv->dev, alloc_size, &rx->data.dma_handle, GFP_KERNEL); + -+static int aspeed_mmbi_write(struct aspeed_mmbi_channel *chan, char *buffer, size_t len, -+ protocol_type type) -+{ -+ struct device *dev = chan->dev; -+ struct mmbi_header header = { 0 }; -+ ssize_t avail_buf_len; -+ ssize_t total_len; -+ ssize_t wt_offset; -+ ssize_t chunk_len; -+ ssize_t end_offset; -+ u8 padding = 0; ++ if (!rx->data.vaddr) ++ return -ENOMEM; + -+ /* If HOST READY bit is not set, Just discard the write. */ -+ if (!GET_HOST_READY_BIT(chan)) { -+ dev_dbg(dev, "Host not ready, discarding request...\n"); -+ return -EAGAIN; -+ } ++ alloc_size = PAGE_ALIGN(priv->rx_packet_count * priv->match_data->rx_cmd_size); ++ rx->cmd.vaddr = dma_alloc_coherent(priv->dev, alloc_size, &rx->cmd.dma_handle, GFP_KERNEL); + -+ get_b2h_avail_buf_len(chan, &avail_buf_len); ++ if (!rx->cmd.vaddr) ++ goto out_rx_cmd; + -+ dev_dbg(dev, "B2H buffer empty space: %ld\n", avail_buf_len); ++ alloc_size = PAGE_ALIGN(TX_PACKET_COUNT * priv->match_data->packet_unit_size); ++ tx->data.vaddr = ++ dma_alloc_coherent(priv->dev, alloc_size, &tx->data.dma_handle, GFP_KERNEL); + -+ /* Header size */ -+ total_len = len + 4; ++ if (!tx->data.vaddr) ++ goto out_tx_data; ++ alloc_size = PAGE_ALIGN(TX_PACKET_COUNT * priv->match_data->tx_cmd_size); ++ tx->cmd.vaddr = dma_alloc_coherent(priv->dev, alloc_size, &tx->cmd.dma_handle, GFP_KERNEL); + -+ padding = total_len & 0x3; -+ if (padding) -+ padding = 4 - padding; -+ total_len += padding; ++ if (!tx->cmd.vaddr) ++ goto out_tx_cmd; + -+ /* Empty space should be more than write request data size */ -+ if (avail_buf_len <= sizeof(header) || (total_len > (avail_buf_len - sizeof(header)))) -+ return -ENOSPC; ++ return 0; ++out_tx_cmd: ++ alloc_size = PAGE_ALIGN(TX_PACKET_COUNT * ++ priv->match_data->packet_unit_size); ++ dma_free_coherent(priv->dev, alloc_size, tx->data.vaddr, ++ tx->data.dma_handle); + -+ /* Fill multi-protocol header */ -+ header.pkt_type = type; -+ header.pkt_len = total_len >> 2; -+ header.pkt_pad = padding; ++out_tx_data: ++ alloc_size = PAGE_ALIGN(priv->rx_packet_count * ++ priv->match_data->rx_cmd_size); ++ dma_free_coherent(priv->dev, alloc_size, rx->cmd.vaddr, ++ rx->cmd.dma_handle); + -+ wt_offset = GET_B2H_WRITE_POINTER(chan); -+ end_offset = chan->b2h_cb_size; ++out_rx_cmd: ++ alloc_size = PAGE_ALIGN(priv->rx_packet_count * ++ priv->match_data->packet_unit_size); ++ dma_free_coherent(priv->dev, alloc_size, rx->data.vaddr, ++ rx->data.dma_handle); + -+ /* Copy Header */ -+ if ((end_offset - wt_offset) >= sizeof(header)) { -+ memcpy_toio(chan->b2h_cb_vmem + wt_offset, &header, sizeof(header)); -+ wt_offset += sizeof(header); -+ } else { -+ chunk_len = end_offset - wt_offset; -+ memcpy_toio(chan->b2h_cb_vmem + wt_offset, &header, chunk_len); -+ memcpy_toio(chan->b2h_cb_vmem, &header + chunk_len, (sizeof(header) - chunk_len)); -+ wt_offset = (sizeof(header) - chunk_len); -+ } ++ return -ENOMEM; ++} + -+ /* Write the data */ -+ if ((end_offset - wt_offset) >= len) { -+ memcpy_toio(&chan->b2h_cb_vmem[wt_offset], buffer, len); -+ wt_offset += len; -+ } else { -+ chunk_len = end_offset - wt_offset; -+ dev_dbg(dev, "Write data chunk_len: %ld\n", chunk_len); -+ memcpy_toio(&chan->b2h_cb_vmem[wt_offset], buffer, chunk_len); ++static void aspeed_mctp_dma_fini(struct aspeed_mctp *priv) ++{ ++ struct mctp_channel *tx = &priv->tx; ++ struct mctp_channel *rx = &priv->rx; ++ size_t free_size; ++ ++ free_size = PAGE_ALIGN(TX_PACKET_COUNT * priv->match_data->tx_cmd_size); ++ dma_free_coherent(priv->dev, free_size, tx->cmd.vaddr, ++ tx->cmd.dma_handle); + -+ wt_offset = 0; -+ memcpy_toio(&chan->b2h_cb_vmem[wt_offset], buffer + chunk_len, len - chunk_len); -+ wt_offset += len - chunk_len; -+ } ++ free_size = PAGE_ALIGN(priv->rx_packet_count * ++ priv->match_data->rx_cmd_size); ++ dma_free_coherent(priv->dev, free_size, rx->cmd.vaddr, ++ rx->cmd.dma_handle); + -+ update_host_ros(chan, total_len, 0); ++ free_size = PAGE_ALIGN(TX_PACKET_COUNT * ++ priv->match_data->packet_unit_size); ++ dma_free_coherent(priv->dev, free_size, tx->data.vaddr, ++ tx->data.dma_handle); + -+ return 0; ++ free_size = PAGE_ALIGN(priv->rx_packet_count * ++ priv->match_data->packet_unit_size); ++ dma_free_coherent(priv->dev, free_size, rx->data.vaddr, ++ rx->data.dma_handle); +} + -+static void aspeed_mmbi_read(struct aspeed_mmbi_channel *chan, char *buffer, size_t len, u8 padding) ++static int aspeed_mctp_irq_init(struct aspeed_mctp *priv) +{ -+ struct device *dev = chan->dev; -+ ssize_t rd_offset; -+ u32 h2b_rp; -+ -+ h2b_rp = GET_H2B_READ_POINTER(chan); -+ if ((h2b_rp + sizeof(struct mmbi_header)) <= chan->h2b_cb_size) -+ rd_offset = h2b_rp + sizeof(struct mmbi_header); -+ else -+ rd_offset = h2b_rp + sizeof(struct mmbi_header) - chan->h2b_cb_size; -+ -+ /* Extract data and copy to user space application */ -+ dev_dbg(dev, "READ MMBI Data from: 0x%0lx and length: %ld\n", -+ (ssize_t)(chan->h2b_cb_vmem + rd_offset), len); ++ struct platform_device *pdev = to_platform_device(priv->dev); ++ int irq, ret; + -+ if ((chan->h2b_cb_size - rd_offset) >= len) { -+ memcpy_fromio(buffer, chan->h2b_cb_vmem + rd_offset, len); -+ rd_offset += len; ++ irq = platform_get_irq_byname_optional(pdev, "mctp"); ++ if (irq < 0) { ++ /* mctp irq is option */ ++ priv->miss_mctp_int = 1; ++ INIT_DELAYED_WORK(&priv->rx_det_dwork, aspeed_mctp_rx_detect_work); + } else { -+ ssize_t chunk_len; -+ -+ chunk_len = chan->h2b_cb_size - rd_offset; -+ dev_dbg(dev, "Read data chunk_len: %ld\n", chunk_len); -+ memcpy_fromio(buffer, chan->h2b_cb_vmem + rd_offset, chunk_len); -+ -+ rd_offset = 0; -+ memcpy_fromio(buffer + chunk_len, chan->h2b_cb_vmem + rd_offset, len - chunk_len); ++ ret = devm_request_irq(priv->dev, irq, aspeed_mctp_irq_handler, ++ IRQF_SHARED, dev_name(&pdev->dev), priv); ++ if (ret) ++ return ret; ++ aspeed_mctp_irq_enable(priv); + } ++ irq = platform_get_irq_byname(pdev, "pcie"); ++ if (irq < 0) ++ return irq; + -+ update_host_ros(chan, 0, len + sizeof(struct mmbi_header) + padding); ++ ret = devm_request_irq(priv->dev, irq, aspeed_mctp_pcie_rst_irq_handler, ++ IRQF_SHARED, dev_name(&pdev->dev), priv); ++ if (ret) ++ return ret; ++ ++ return 0; +} + -+static void mctp_mmbi_rx(struct aspeed_mmbi_channel *chan) ++static int aspeed_mctp_hw_reset(struct aspeed_mctp *priv) +{ -+ struct net_device *ndev = chan->ndev; -+ struct sk_buff *skb; -+ struct mctp_skb_cb *cb; -+ u32 req_data_len, unread_data_len; -+ u8 type, padding; -+ int status; -+ -+ if (get_mmbi_header(chan, &req_data_len, &type, &unread_data_len, &padding) != 0) -+ return; -+ -+ dev_dbg(chan->dev, "%s: Length: 0x%0x, Protocol Type: %d, Unread data: %d\n", __func__, -+ req_data_len, type, unread_data_len); ++ int ret = 0; + -+ skb = netdev_alloc_skb(ndev, req_data_len); -+ if (!skb) { -+ ndev->stats.rx_dropped++; -+ update_host_ros(chan, 0, req_data_len + sizeof(struct mmbi_header)); -+ return; ++ ret = reset_control_deassert(priv->reset); ++ if (ret) { ++ dev_warn(priv->dev, "Failed to deassert reset\n"); ++ return ret; + } + -+ skb->protocol = htons(ETH_P_MCTP); -+ aspeed_mmbi_read(chan, skb_put(skb, req_data_len), req_data_len, padding); -+ skb_reset_network_header(skb); ++ if (priv->rc_f) { ++ ret = reset_control_deassert(priv->reset_dma); ++ if (ret) { ++ dev_warn(priv->dev, "Failed to deassert ep reset\n"); ++ return ret; ++ } ++ } + -+ cb = __mctp_cb(skb); -+ cb->halen = 0; ++ if (priv->match_data->dma_need_64bits_width) ++ ret = pcie_vdm_enable(priv->dev); + -+ status = netif_rx(skb); -+ if (status == NET_RX_SUCCESS) { -+ ndev->stats.rx_packets++; -+ ndev->stats.rx_bytes += req_data_len; -+ } else { -+ ndev->stats.rx_dropped++; -+ } ++ return ret; +} + -+static netdev_tx_t mctp_mmbi_tx(struct sk_buff *skb, struct net_device *ndev) ++static int aspeed_mctp_probe(struct platform_device *pdev) +{ -+ struct aspeed_mmbi_mctp *mctp = netdev_priv(ndev); -+ int ret; ++ struct aspeed_mctp *priv; ++ int ret, id; ++ const char *name; + -+ if (!mmbi_get_host_rdy(&mctp->mmbi->chan) || skb->len > MCTP_MMBI_MTU_MAX) { -+ ndev->stats.tx_dropped++; ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) { ++ ret = -ENOMEM; + goto out; + } ++ priv->dev = &pdev->dev; ++ priv->rc_f = ++ of_find_property(priv->dev->of_node, "pcie_rc", NULL) ? 1 : 0; ++ priv->match_data = of_device_get_match_data(priv->dev); + -+ ret = aspeed_mmbi_write(&mctp->mmbi->chan, skb->data, skb->len, MMBI_PROTOCOL_MCTP); ++ ret = device_property_read_u32(priv->dev, "aspeed,rx-packet-count", ++ &priv->rx_packet_count); + if (ret) { -+ netif_stop_queue(ndev); -+ return NETDEV_TX_BUSY; ++ priv->rx_packet_count = RX_PACKET_COUNT; ++ } else if (priv->rx_packet_count % 4 || ++ priv->rx_packet_count >= RX_MAX_PACKET_COUNT) { ++ dev_err(priv->dev, ++ "The aspeed,rx-packet-count:%d should be 4-aligned and less than %ld", ++ priv->rx_packet_count, RX_MAX_PACKET_COUNT); ++ ret = -EINVAL; ++ goto out; + } + -+ ndev->stats.tx_packets++; -+ ndev->stats.tx_bytes += skb->len; -+out: -+ kfree_skb(skb); -+ return NETDEV_TX_OK; -+} ++ ret = device_property_read_u32(priv->dev, "aspeed,rx-ring-count", ++ &priv->rx_ring_count); ++ if (ret) ++ priv->rx_ring_count = RX_RING_COUNT; + -+static const struct net_device_ops mctp_mmbi_netdev_ops = { -+ .ndo_start_xmit = mctp_mmbi_tx, -+}; ++ ret = device_property_read_u32(priv->dev, "aspeed,tx-ring-count", ++ &priv->tx_ring_count); ++ if (ret) ++ priv->tx_ring_count = TX_RING_COUNT; + -+static void aspeed_mctp_mmbi_setup(struct net_device *ndev) -+{ -+ ndev->type = ARPHRD_MCTP; ++ ret = device_property_read_u32(priv->dev, "aspeed,rx-det-period-us", ++ &priv->rx_det_period_us); ++ if (ret) ++ priv->rx_det_period_us = 1000; + -+ /* we limit at the fixed MTU, which is also the MCTP-standard -+ * baseline MTU, so is also our minimum -+ */ -+ ndev->mtu = MCTP_MMBI_MTU; -+ ndev->max_mtu = MCTP_MMBI_MTU_MAX; -+ ndev->min_mtu = MCTP_MMBI_MTU_MIN; ++ aspeed_mctp_drv_init(priv); + -+ ndev->hard_header_len = 0; -+ ndev->addr_len = 0; -+ ndev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; -+ ndev->flags = IFF_NOARP; -+ ndev->netdev_ops = &mctp_mmbi_netdev_ops; -+ ndev->needs_free_netdev = true; -+} ++ ret = aspeed_mctp_resources_init(priv); ++ if (ret) { ++ dev_err(priv->dev, "Failed to init resources\n"); ++ goto out_drv; ++ } + -+static int aspeed_mmbi_mctp_init(struct aspeed_mmbi_channel *chan) -+{ -+ struct aspeed_mmbi_mctp *mctp; ++#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM + struct net_device *ndev; -+ char name[32]; -+ int ret; ++ struct mctp_client *client; + -+ snprintf(name, sizeof(name), "mctpmmbi%d%d", chan->mmbi->id, chan->mmbi->e2m_index); -+ ndev = alloc_netdev(sizeof(*mctp), name, NET_NAME_ENUM, aspeed_mctp_mmbi_setup); -+ if (!ndev) -+ return -ENOMEM; ++ /** use priv's default client to send/receive mctp packets */ ++ client = aspeed_mctp_create_client(priv); ++ aspeed_mctp_register_default_handler(client); + -+ mctp = netdev_priv(ndev); -+ mctp->ndev = ndev; -+ mctp->mmbi = chan->mmbi; ++ ndev = mctp_pcie_vdm_add_dev(priv->dev, &aspeed_mctp_pcie_vdm_ops); ++ if (IS_ERR(ndev)) { ++ dev_err(priv->dev, "Failed to add mctp pcie vdm device Err %ld\n", PTR_ERR(ndev)); ++ goto out_drv; ++ } ++ priv->ndev = ndev; ++#endif + -+ chan->ndev = ndev; ++ ret = aspeed_mctp_dma_init(priv); ++ if (ret) { ++ dev_err(priv->dev, "Failed to init DMA\n"); ++ goto out_drv; ++ } + -+ ret = register_netdev(ndev); ++ ret = aspeed_mctp_hw_reset(priv); + if (ret) -+ goto free_netdev; ++ goto out_drv; + -+ return 0; ++ aspeed_mctp_channels_init(priv); + -+free_netdev: -+ free_netdev(ndev); ++ id = of_alias_get_id(priv->dev->of_node, "mctp"); ++ if (id < 0) ++ return id; ++ priv->mctp_miscdev.parent = priv->dev; ++ priv->mctp_miscdev.minor = MISC_DYNAMIC_MINOR; ++ priv->mctp_miscdev.name = devm_kasprintf(priv->dev, GFP_KERNEL, "aspeed-mctp%d", id); ++ priv->mctp_miscdev.fops = &aspeed_mctp_fops; ++ ret = misc_register(&priv->mctp_miscdev); ++ if (ret) { ++ dev_err(priv->dev, "Failed to register miscdev\n"); ++ goto out_dma; ++ } ++ priv->mctp_miscdev.this_device->type = &aspeed_mctp_type; ++ ++ ret = aspeed_mctp_irq_init(priv); ++ if (ret) { ++ dev_err(priv->dev, "Failed to init IRQ!\n"); ++ goto out_dma; ++ } ++ aspeed_mctp_pcie_setup(priv); ++ ++ name = devm_kasprintf(priv->dev, GFP_KERNEL, "peci-mctp%d", id); ++ priv->peci_mctp = ++ platform_device_register_data(priv->dev, name, PLATFORM_DEVID_NONE, NULL, 0); ++ if (IS_ERR(priv->peci_mctp)) ++ dev_err(priv->dev, "Failed to register peci-mctp device\n"); ++ ++ return 0; + ++out_dma: ++ aspeed_mctp_dma_fini(priv); ++out_drv: ++ aspeed_mctp_drv_fini(priv); ++out: ++ dev_err(&pdev->dev, "Failed to probe Aspeed MCTP: %d\n", ret); + return ret; +} + -+static void aspeed_mmbi_work_func(struct work_struct *workq) ++static void aspeed_mctp_remove(struct platform_device *pdev) +{ -+ struct aspeed_mmbi_channel *chan = container_of(workq, struct aspeed_mmbi_channel, work); -+ u32 weight = 256, req_data_len, unread_data_len; -+ u8 type, padding; -+ int i; ++ struct aspeed_mctp *priv = platform_get_drvdata(pdev); + -+ for (i = 0; i < weight; i++) { -+ if (get_mmbi_header(chan, &req_data_len, &type, &unread_data_len, &padding) != 0) -+ return; ++#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM ++ mctp_pcie_vdm_remove_dev(priv->ndev); ++#endif + -+ dev_dbg(chan->dev, "%s: Length: 0x%0x, Protocol Type: %d\n", -+ __func__, req_data_len, type); ++ platform_device_unregister(priv->peci_mctp); + -+ if (type == MMBI_PROTOCOL_MCTP) -+ mctp_mmbi_rx(chan); -+ else -+ /* Discard data and advance the hrws */ -+ update_host_ros(chan, 0, req_data_len + sizeof(struct mmbi_header) + padding); ++ misc_deregister(&priv->mctp_miscdev); + -+ raise_b2h_interrupt(chan); -+ } ++ aspeed_mctp_irq_disable(priv); + -+ if (get_mmbi_header(chan, &req_data_len, &type, &unread_data_len, &padding) != 0) -+ queue_work(system_unbound_wq, &chan->work); ++ aspeed_mctp_dma_fini(priv); ++ ++ aspeed_mctp_drv_fini(priv); +} + -+static irqreturn_t aspeed_pcie_mmbi_isr(int irq, void *dev_id) -+{ -+ struct aspeed_pcie_mmbi *mmbi = dev_id; -+ struct aspeed_mmbi_channel *chan = &mmbi->chan; -+ ssize_t avail_buf_len; ++static const struct aspeed_mctp_match_data ast2500_mctp_match_data = { ++ .rx_cmd_size = sizeof(struct aspeed_mctp_rx_cmd), ++ .tx_cmd_size = sizeof(struct aspeed_mctp_tx_cmd), ++ .packet_unit_size = 128, ++ .need_address_mapping = true, ++ .vdm_hdr_direct_xfer = false, ++ .fifo_auto_surround = false, ++}; + -+ get_b2h_avail_buf_len(chan, &avail_buf_len); -+ if (avail_buf_len > MCTP_MMBI_MTU_MAX) { -+ if (netif_queue_stopped(chan->ndev)) { -+ dev_dbg(chan->dev, "Wake up mctp net device\n"); -+ netif_wake_queue(chan->ndev); -+ } -+ } ++static const struct aspeed_mctp_match_data ast2600_mctp_match_data = { ++ .rx_cmd_size = sizeof(u32), ++ .tx_cmd_size = sizeof(struct aspeed_mctp_tx_cmd), ++ .packet_unit_size = sizeof(struct mctp_pcie_packet_data), ++ .need_address_mapping = false, ++ .vdm_hdr_direct_xfer = true, ++ .fifo_auto_surround = true, ++}; + -+ if (mmbi_state_check(chan)) -+ return IRQ_HANDLED; ++static const struct aspeed_mctp_match_data ast2700_mctp0_match_data = { ++ .rx_cmd_size = sizeof(struct aspeed_mctp_rx_cmd), ++ .tx_cmd_size = sizeof(struct aspeed_g7_mctp_tx_cmd), ++ .packet_unit_size = sizeof(struct mctp_pcie_packet_data), ++ .need_address_mapping = false, ++ .vdm_hdr_direct_xfer = true, ++ .fifo_auto_surround = true, ++ .dma_need_64bits_width = true, ++ .scu_pcie_ctrl_offset = ASPEED_G7_SCU_PCIE0_CTRL_OFFSET, ++}; + -+ queue_work(system_unbound_wq, &chan->work); ++static const struct aspeed_mctp_match_data ast2700_mctp1_match_data = { ++ .rx_cmd_size = sizeof(struct aspeed_mctp_rx_cmd), ++ .tx_cmd_size = sizeof(struct aspeed_g7_mctp_tx_cmd), ++ .packet_unit_size = sizeof(struct mctp_pcie_packet_data), ++ .need_address_mapping = false, ++ .vdm_hdr_direct_xfer = true, ++ .fifo_auto_surround = true, ++ .dma_need_64bits_width = true, ++ .scu_pcie_ctrl_offset = ASPEED_G7_SCU_PCIE1_CTRL_OFFSET, ++}; + -+ return IRQ_HANDLED; -+} ++static const struct of_device_id aspeed_mctp_match_table[] = { ++ { .compatible = "aspeed,ast2500-mctp", .data = &ast2500_mctp_match_data}, ++ { .compatible = "aspeed,ast2600-mctp", .data = &ast2600_mctp_match_data}, ++ { .compatible = "aspeed,ast2700-mctp0", .data = &ast2700_mctp0_match_data}, ++ { .compatible = "aspeed,ast2700-mctp1", .data = &ast2700_mctp1_match_data}, ++ { } ++}; + -+static void mmbi_desc_init(struct aspeed_mmbi_channel *chan) ++static struct platform_driver aspeed_mctp_driver = { ++ .driver = { ++ .name = "aspeed-mctp", ++ .of_match_table = of_match_ptr(aspeed_mctp_match_table), ++ }, ++ .probe = aspeed_mctp_probe, ++ .remove = aspeed_mctp_remove, ++}; ++ ++static int __init aspeed_mctp_init(void) +{ -+ struct mmbi_cap_desc desc; ++ packet_cache = ++ kmem_cache_create_usercopy("mctp-packet", ++ sizeof(struct mctp_pcie_packet), ++ 0, 0, 0, ++ sizeof(struct mctp_pcie_packet), ++ NULL); ++ if (!packet_cache) ++ return -ENOMEM; + -+ memset(&desc, 0, sizeof(struct mmbi_cap_desc)); ++ return platform_driver_register(&aspeed_mctp_driver); ++} + -+ desc.version = 1; -+ /* This MMBI interface is intended for OS use */ -+ desc.os_use = 1; -+ desc.b2h_ba = (chan->b2h_cb_vmem - chan->desc_vmem) >> 3; -+ desc.h2b_ba = (chan->h2b_cb_vmem - chan->desc_vmem) >> 3; -+ /* Make sure the buffer size is 4 byte aligmnent */ -+ desc.b2h_l = chan->b2h_cb_size & ~0x3; -+ desc.h2b_l = chan->h2b_cb_size & ~0x3; -+ /* Variable Packet Size Circular Buffers (VPSCB) v1 */ -+ desc.buffer_type = 0x01; -+ desc.bt_desc.h_ros_p = (chan->hros_vmem - chan->desc_vmem) >> 3; -+ desc.bt_desc.h_rws_p = (chan->hrws_vmem - chan->desc_vmem) >> 3; -+ /* PCIe Interrupt */ -+ desc.bt_desc.h_int_t = 0x01; -+ desc.bt_desc.h_int_l = chan->host_int_location; -+ desc.bt_desc.h_int_v = 0; /* Skip for PCIe Interrupt */ -+ desc.bt_desc.bmc_int_t = 0x01; /* relative memory space address */ -+ desc.bt_desc.bmc_int_l = chan->bmc_int_location; -+ desc.bt_desc.bmc_int_v = chan->bmc_int_value; ++static void __exit aspeed_mctp_exit(void) ++{ ++ platform_driver_unregister(&aspeed_mctp_driver); ++ kmem_cache_destroy(packet_cache); ++} + -+ /* Per MMBI protoco spec, Set it to "#MMBI$" */ -+ memcpy(desc.signature, MMBI_SIGNATURE, sizeof(desc.signature)); ++module_init(aspeed_mctp_init) ++module_exit(aspeed_mctp_exit) + -+ memcpy_toio(chan->desc_vmem, &desc, sizeof(desc)); -+} ++MODULE_DEVICE_TABLE(of, aspeed_mctp_match_table); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Iwona Winiarska "); ++MODULE_DESCRIPTION("Aspeed MCTP driver"); +diff --git a/drivers/soc/aspeed/aspeed-p2a-ctrl.c b/drivers/soc/aspeed/aspeed-p2a-ctrl.c +--- a/drivers/soc/aspeed/aspeed-p2a-ctrl.c 2025-08-01 08:48:47.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-p2a-ctrl.c 2026-04-08 18:03:48.311705302 +0000 +@@ -431,7 +431,7 @@ + .of_match_table = aspeed_p2a_ctrl_match, + }, + .probe = aspeed_p2a_ctrl_probe, +- .remove_new = aspeed_p2a_ctrl_remove, ++ .remove = aspeed_p2a_ctrl_remove, + }; + + module_platform_driver(aspeed_p2a_ctrl_driver); +diff --git a/drivers/soc/aspeed/aspeed-pcie-mmbi.c b/drivers/soc/aspeed/aspeed-pcie-mmbi.c +--- a/drivers/soc/aspeed/aspeed-pcie-mmbi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-pcie-mmbi.c 2026-04-08 18:03:48.311705302 +0000 +@@ -0,0 +1,963 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++// Copyright (C) ASPEED Technology Inc. + -+static int aspeed_pcie_mmbi_init(struct aspeed_pcie_mmbi *mmbi) -+{ -+ struct aspeed_mmbi_channel *chan = &mmbi->chan; -+ struct device *dev = chan->dev; -+ u32 b2h_size = mmbi->mem_size >> 1; -+ u32 h2b_size = mmbi->mem_size >> 1; -+ u8 *h2b_vaddr, *b2h_vaddr; -+ int ret; ++#include ++#include ++#include ++#include + -+ b2h_vaddr = mmbi->mem_virt; -+ h2b_vaddr = b2h_vaddr + b2h_size; ++#include ++#include ++#include ++#include ++#include ++#include + -+ chan->dev = dev; -+ chan->desc_vmem = b2h_vaddr; -+ chan->hros_vmem = b2h_vaddr + sizeof(struct mmbi_cap_desc); -+ chan->b2h_cb_vmem = b2h_vaddr + sizeof(struct mmbi_cap_desc) + sizeof(struct host_ros); -+ chan->b2h_cb_size = b2h_size - sizeof(struct mmbi_cap_desc) - sizeof(struct host_ros); ++#include ++#include + -+ chan->hrws_vmem = h2b_vaddr; -+ chan->h2b_cb_vmem = h2b_vaddr + sizeof(struct host_rws); -+ chan->h2b_cb_size = h2b_size - sizeof(struct host_rws); ++#include ++#include ++#include ++#include ++#include ++#include + -+ dev_dbg(dev, "B2H mapped addr - desc: 0x%0lx, hros: 0x%0lx, b2h_cb: 0x%0lx\n", -+ (size_t)chan->desc_vmem, (size_t)chan->hros_vmem, (size_t)chan->b2h_cb_vmem); -+ dev_dbg(dev, "H2B mapped addr - hrws: 0x%0lx, h2b_cb: 0x%0lx\n", (size_t)chan->hrws_vmem, -+ (size_t)chan->h2b_cb_vmem); ++#include ++#include ++#include ++#include ++#include + -+ dev_dbg(dev, "B2H buffer size: 0x%0lx\n", (size_t)chan->b2h_cb_size); -+ dev_dbg(dev, "H2B buffer size: 0x%0lx\n", (size_t)chan->h2b_cb_size); ++#include "aspeed-pcie-mmbi.h" + -+ /* Initialize the MMBI channel descriptor */ -+ mmbi_desc_init(chan); ++/* AST2700 E2M */ ++#define ASPEED_E2M_EVENT 0x0D0 ++#define ASPEED_E2M_EVENT_SET 0x0D4 ++#define ASPEED_E2M_EVENT_CLR 0x0D8 ++#define ASPEED_E2M_EVENT_EN 0x0DC ++#define ASPEED_E2M_ADRMAP00 0x100 ++#define ASPEED_E2M_WIRQA0 0x180 ++#define ASPEED_E2M_WIRQV0 0x1C0 ++#define ASPEED_E2M_SPROT_SIDG0 0x210 ++#define ASPEED_E2M_SPROT_CTL0 0x280 ++#define ASPEED_E2M_SPROT_ADR0 0x2C0 + -+ /* Clear HRWS & HROS */ -+ mmbi_clear_hros(chan); -+ mmbi_clear_hrws(chan); ++/* AST2700 SCU */ ++#define ASPEED_SCU_DECODE_DEV BIT(18) ++#define ASPEED_SCU_INT_EN BIT(23) ++struct aspeed_platform { ++ int (*mmbi_init)(struct platform_device *pdev); ++}; + -+ /* Initialize MTCP function */ -+ ret = aspeed_mmbi_mctp_init(chan); -+ if (ret) { -+ dev_err(dev, "Unable to init mctp\n"); -+ return ret; -+ } ++struct aspeed_pcie_mmbi { ++ struct device *dev; ++ struct regmap *device; ++ struct regmap *e2m; ++ int irq; ++ const struct aspeed_platform *platform; ++ /* E2M index */ ++ int id; ++ int pid; ++ int scu_bar_offset; ++ int e2m_index; ++ int e2m_h2b_int; ++ ++ /* Memory Mapping */ ++ void __iomem *mem_virt; ++ dma_addr_t mem_phy; ++ phys_addr_t mem_size; + -+ /* Set BMC UP bit */ -+ mmbi_set_bmc_up(chan, 1); ++ struct aspeed_mmbi_channel chan; ++}; + -+ return 0; -+} ++static void mmbi_desc_init(struct aspeed_mmbi_channel *chan); + -+/* -+ * AST2700 PCIe MMBI (SCU & E2M) -+ * SoC | 0 | 1 | -+ * PCI class | MFD (0xFF_00_00) | MMBI (0x0C_0C_00) | -+ * Node | 0 1 | 0 | -+ * PID | 3 4 5 6 11 12 13 14 | 2 3 4 5 6 7 | -+ * E2M index | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 | -+ * BAR index | 2 3 4 5 2 3 4 5 | 0 1 2 3 4 5 | -+ * SCU BAR | 3c 4c 5c 6c 3c 4c 5c 6c | 1c 50 3c 4c 5c 6c | -+ * E2M H2B Int | 0 1 2 3 0 1 2 3 | 0 1 2 3 4 5 | (bit) -+ */ -+static int aspeed_ast2700_pcie_mmbi_init(struct platform_device *pdev) ++static u8 mmbi_get_bmc_up(struct aspeed_mmbi_channel *chan) +{ -+ struct aspeed_pcie_mmbi *mmbi = platform_get_drvdata(pdev); -+ struct aspeed_mmbi_channel *chan = &mmbi->chan; -+ struct device *dev = &pdev->dev; -+ u32 value, sprot_size, e2m_index, pid; -+ struct resource res; -+ int ret, i; -+ -+ /* Get register map*/ -+ mmbi->e2m = syscon_node_to_regmap(dev->of_node->parent); -+ if (IS_ERR(mmbi->e2m)) { -+ dev_err(dev, "failed to find e2m regmap\n"); -+ return PTR_ERR(mmbi->e2m); -+ } -+ if (of_address_to_resource(dev->of_node->parent, 0, &res)) { -+ dev_err(dev, "Failed to get e2m resource\n"); -+ return -EINVAL; -+ } -+ if (res.start == 0x14c1d000) -+ mmbi->id = 2; -+ else if (res.start == 0x12c22000) -+ mmbi->id = 1; -+ else -+ mmbi->id = 0; /* 0x12c21000 */ ++ struct host_ros hros; + -+ mmbi->device = syscon_regmap_lookup_by_phandle(dev->of_node->parent, "aspeed,device"); -+ if (IS_ERR(mmbi->device)) { -+ dev_err(dev, "failed to find device regmap\n"); -+ return PTR_ERR(mmbi->device); -+ } ++ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); + -+ ret = of_property_read_u32(dev->of_node, "index", &mmbi->e2m_index); -+ if (ret < 0) { -+ dev_err(dev, "cannot get mmbi index value\n"); -+ return ret; -+ } ++ return hros.b_up; ++} + -+ ret = of_property_read_u32(dev->of_node, "pid", &mmbi->pid); -+ if (ret < 0) { -+ dev_err(dev, "cannot get mmbi pid value\n"); -+ return ret; -+ } ++static u8 mmbi_get_bmc_rdy(struct aspeed_mmbi_channel *chan) ++{ ++ struct host_ros hros; + -+ ret = of_property_read_u32(dev->of_node, "bar", &mmbi->scu_bar_offset); -+ if (ret < 0) { -+ dev_err(dev, "cannot get mmbi bar value\n"); -+ return ret; -+ } ++ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); + -+ e2m_index = mmbi->e2m_index; -+ pid = mmbi->pid; -+ mmbi->e2m_h2b_int += mmbi->e2m_index; -+ if (mmbi->id < 2) { -+ /* PCIe device class, sub-class, protocol and reversion */ -+ regmap_write(mmbi->device, 0x18, 0xFF000027); -+ } else { -+ regmap_write(mmbi->device, 0x18, 0x0C0C0027); -+ regmap_write(mmbi->device, 0x78, ASPEED_SCU_INT_EN | ASPEED_SCU_DECODE_DEV); -+ } ++ return hros.b_rdy; ++} + -+ /* MSI */ -+ regmap_update_bits(mmbi->device, 0x74, GENMASK(7, 4), BIT(7) | (5 << 4)); ++static u8 mmbi_get_bmc_rst(struct aspeed_mmbi_channel *chan) ++{ ++ struct host_ros hros; + -+ regmap_update_bits(mmbi->device, 0x70, BIT(25) | BIT(17) | BIT(9) | BIT(1), -+ BIT(25) | BIT(17) | BIT(9) | BIT(1)); ++ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); + -+ /* Calculate the BAR Size */ -+ for (i = 1; i < 16; i++) { -+ /* bar size check for 4k align */ -+ if ((mmbi->mem_size / 4096) == (1 << (i - 1))) -+ break; -+ } -+ if (i == 16) { -+ i = 0; -+ dev_warn(dev, "Bar size not align for 4K : %dK\n", (u32)mmbi->mem_size / 1024); -+ } -+ regmap_write(mmbi->device, mmbi->scu_bar_offset, (mmbi->mem_phy >> 4) | i); -+ regmap_write(mmbi->e2m, ASPEED_E2M_ADRMAP00 + (4 * pid), (mmbi->mem_phy >> 4) | i); ++ return hros.b_rst; ++} + -+ /* BMC Interrupt */ -+ if (chan->bmc_int_en) { -+ value = mmbi->mem_phy + chan->bmc_int_location; -+ regmap_write(mmbi->e2m, ASPEED_E2M_WIRQA0 + (4 * e2m_index), value); -+ value = (BIT(16) << pid) | chan->bmc_int_value; -+ regmap_write(mmbi->e2m, ASPEED_E2M_WIRQV0 + (4 * e2m_index), value); -+ } ++static u8 mmbi_get_host_rst(struct aspeed_mmbi_channel *chan) ++{ ++ struct host_rws hrws; + -+ /* HOST Interrupt: MSI */ -+ regmap_read(mmbi->e2m, ASPEED_E2M_EVENT_EN, &value); -+ value |= BIT(mmbi->e2m_h2b_int); -+ regmap_write(mmbi->e2m, ASPEED_E2M_EVENT_EN, value); ++ memcpy_fromio(&hrws, chan->hrws_vmem, sizeof(hrws)); + -+ /* B2H Write Protect */ -+ sprot_size = (mmbi->mem_size / 2) / SZ_1M; -+ value = (sprot_size << 16) | (mmbi->mem_phy >> 20); -+ regmap_write(mmbi->e2m, ASPEED_E2M_SPROT_ADR0 + (4 * e2m_index), value); -+ /* Enable read & disalbe write */ -+ value = 1 << (8 + e2m_index); -+ regmap_write(mmbi->e2m, ASPEED_E2M_SPROT_CTL0 + (4 * e2m_index), value); -+ /* Set PID */ -+ regmap_read(mmbi->e2m, ASPEED_E2M_SPROT_SIDG0 + (4 * (e2m_index / 4)), &value); -+ value |= pid << (8 * (e2m_index % 4)); -+ regmap_write(mmbi->e2m, ASPEED_E2M_SPROT_SIDG0 + (4 * (e2m_index / 4)), value); ++ return hrws.h_rst; ++} + -+ mmbi->chan.dev = dev; -+ mmbi->chan.mmbi = mmbi; -+ ret = aspeed_pcie_mmbi_init(mmbi); -+ if (ret < 0) { -+ dev_err(dev, "Initialize MMBI device failed.\n"); -+ return ret; -+ } ++static u8 mmbi_get_host_rdy(struct aspeed_mmbi_channel *chan) ++{ ++ struct host_rws hrws; + -+ INIT_WORK(&chan->work, aspeed_mmbi_work_func); ++ memcpy_fromio(&hrws, chan->hrws_vmem, sizeof(hrws)); + -+ return 0; ++ return hrws.h_rdy; +} + -+struct aspeed_platform ast2700_platform = { -+ .mmbi_init = aspeed_ast2700_pcie_mmbi_init, -+}; ++static u8 mmbi_get_host_up(struct aspeed_mmbi_channel *chan) ++{ ++ struct host_rws hrws; + -+static const struct of_device_id aspeed_pcie_mmbi_of_matches[] = { -+ { .compatible = "aspeed,ast2700-pcie-mmbi", .data = &ast2700_platform }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, aspeed_pcie_mmbi_of_matches); ++ memcpy_fromio(&hrws, chan->hrws_vmem, sizeof(hrws)); + -+static int aspeed_pcie_mmbi_probe(struct platform_device *pdev) ++ return hrws.h_up; ++} ++ ++static void mmbi_set_bmc_rst(struct aspeed_mmbi_channel *chan, bool set) +{ -+ struct aspeed_pcie_mmbi *mmbi; -+ struct aspeed_mmbi_channel *chan; -+ struct device *dev = &pdev->dev; -+ struct resource res; -+ struct device_node *np; -+ const void *md; -+ int ret = 0; ++ struct host_ros hros; + -+ md = of_device_get_match_data(dev); -+ if (!md) -+ return -ENODEV; ++ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); ++ hros.b_rst = set; ++ memcpy_toio(chan->hros_vmem, &hros, sizeof(hros)); ++} + -+ mmbi = devm_kzalloc(&pdev->dev, sizeof(struct aspeed_pcie_mmbi), GFP_KERNEL); -+ if (!mmbi) -+ return -ENOMEM; -+ dev_set_drvdata(dev, mmbi); ++static void mmbi_set_bmc_rdy(struct aspeed_mmbi_channel *chan, bool set) ++{ ++ struct host_ros hros; + -+ mmbi->dev = dev; -+ mmbi->platform = md; ++ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); ++ hros.b_rdy = set; ++ memcpy_toio(chan->hros_vmem, &hros, sizeof(hros)); ++} + -+ /* Get MMBI memory size */ -+ np = of_parse_phandle(dev->of_node, "memory-region", 0); -+ if (!np || of_address_to_resource(np, 0, &res)) { -+ dev_err(dev, "Failed to find memory-region.\n"); -+ ret = -ENOMEM; -+ goto out_region; -+ } ++static void mmbi_set_bmc_up(struct aspeed_mmbi_channel *chan, bool set) ++{ ++ struct host_ros hros; + -+ of_node_put(np); ++ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); ++ hros.b_up = set; ++ memcpy_toio(chan->hros_vmem, &hros, sizeof(hros)); ++} + -+ mmbi->mem_phy = res.start; -+ mmbi->mem_size = resource_size(&res); -+ mmbi->mem_virt = ioremap(mmbi->mem_phy, mmbi->mem_size); -+ if (!mmbi->mem_virt) { -+ dev_err(dev, "cannot map mmbi memory region\n"); -+ ret = -ENOMEM; -+ goto out_region; -+ } ++static void raise_b2h_interrupt(struct aspeed_mmbi_channel *chan) ++{ ++ if (!chan->host_int_en) ++ return; + -+ /* Get IRQ */ -+ mmbi->irq = platform_get_irq(pdev, 0); -+ if (mmbi->irq < 0) { -+ dev_err(&pdev->dev, "platform get of irq[=%d] failed!\n", mmbi->irq); -+ goto out_unmap; -+ } -+ ret = devm_request_irq(&pdev->dev, mmbi->irq, aspeed_pcie_mmbi_isr, 0, dev_name(&pdev->dev), -+ mmbi); -+ if (ret) { -+ dev_err(dev, "pcie mmbi unable to get IRQ"); -+ goto out_unmap; -+ } ++ regmap_write(chan->mmbi->e2m, ASPEED_E2M_EVENT_SET, BIT(chan->mmbi->e2m_h2b_int)); ++} + -+ chan = &mmbi->chan; -+ memset(chan, 0, sizeof(struct aspeed_mmbi_channel)); ++static void mmbi_clear_hros(struct aspeed_mmbi_channel *chan) ++{ ++ memset_io(chan->hros_vmem, 0, sizeof(struct host_ros)); ++} + -+ chan->bmc_int_en = true; -+ /* H2B Interrupt */ -+ ret = of_property_read_u8(dev->of_node, "bmc-int-value", &chan->bmc_int_value); -+ if (ret) { -+ dev_err(dev, "cannot get valid MMBI H2B interrupt value\n"); -+ chan->bmc_int_en = false; -+ } -+ ret = of_property_read_u32(dev->of_node, "bmc-int-location", &chan->bmc_int_location); -+ if (ret) { -+ dev_err(dev, "cannot get valid MMBI H2B interrupt location\n"); -+ chan->bmc_int_en = false; -+ } -+ /* B2H Interrupt */ -+ chan->host_int_en = true; -+ ret = of_property_read_u8(dev->of_node, "msi", &chan->host_int_value); -+ if (ret) { -+ dev_err(dev, "cannot get valid MMBI B2H interrupt location\n"); -+ chan->host_int_en = false; -+ } ++static void mmbi_clear_hrws(struct aspeed_mmbi_channel *chan) ++{ ++ memset_io(chan->hrws_vmem, 0, sizeof(struct host_rws)); ++} + -+ ret = mmbi->platform->mmbi_init(pdev); -+ if (ret) { -+ dev_err(dev, "Initialize pcie mmbi failed\n"); -+ goto out_irq; -+ } ++static void get_b2h_avail_buf_len(struct aspeed_mmbi_channel *chan, ssize_t *avail_buf_len) ++{ ++ struct device *dev = chan->dev; ++ u32 b2h_rp, b2h_wp; + -+ dev_info(dev, "ASPEED PCIe MMBI Dev %d: driver successfully loaded.\n", mmbi->id); ++ b2h_rp = GET_B2H_READ_POINTER(chan); ++ b2h_wp = GET_B2H_WRITE_POINTER(chan); ++ dev_dbg(dev, "MMBI B2H - b2h_rp: 0x%0x, b2h_wp: 0x%0x\n", b2h_rp, b2h_wp); + -+ return 0; -+out_irq: -+ devm_free_irq(dev, mmbi->irq, mmbi); -+out_unmap: -+ iounmap(mmbi->mem_virt); -+out_region: -+ devm_kfree(dev, mmbi); -+ dev_warn(dev, "aspeed bmc device: driver init failed (ret=%d)!\n", ret); -+ return ret; ++ if (b2h_wp >= b2h_rp) ++ *avail_buf_len = chan->b2h_cb_size - b2h_wp + b2h_rp; ++ else ++ *avail_buf_len = b2h_rp - b2h_wp; +} + -+static void aspeed_pcie_mmbi_remove(struct platform_device *pdev) ++static u8 mmbi_get_state(struct aspeed_mmbi_channel *chan) +{ -+ struct aspeed_pcie_mmbi *mmbi = platform_get_drvdata(pdev); ++ u8 state = 0; + -+ cancel_work_sync(&mmbi->chan.work); -+ unregister_netdev(mmbi->chan.ndev); -+ devm_free_irq(&pdev->dev, mmbi->irq, mmbi); -+ iounmap(mmbi->mem_virt); -+ devm_kfree(&pdev->dev, mmbi); ++ state = mmbi_get_bmc_up(chan) << 3; ++ state |= mmbi_get_bmc_rst(chan) << 2; ++ state |= mmbi_get_host_up(chan) << 1; ++ state |= mmbi_get_host_rst(chan); ++ ++ return state; +} + -+static struct platform_driver aspeed_pcie_mmbi_driver = { -+ .probe = aspeed_pcie_mmbi_probe, -+ .remove = aspeed_pcie_mmbi_remove, -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .of_match_table = aspeed_pcie_mmbi_of_matches, -+ }, -+}; ++static int get_mmbi_header(struct aspeed_mmbi_channel *chan, u32 *data_length, ++ u8 *type, u32 *unread_data_len, u8 *padding) ++{ ++ u32 h2b_wp, h2b_rp, b2h_wp, b2h_rp; ++ struct device *dev = chan->dev; ++ struct mmbi_header header; + -+module_platform_driver(aspeed_pcie_mmbi_driver); ++ h2b_wp = GET_H2B_WRITE_POINTER(chan); ++ h2b_rp = GET_H2B_READ_POINTER(chan); ++ b2h_wp = GET_B2H_WRITE_POINTER(chan); ++ b2h_rp = GET_B2H_READ_POINTER(chan); ++ dev_dbg(dev, "MMBI HRWS - h2b_wp: 0x%0x, b2h_rp: 0x%0x\n", h2b_wp, ++ b2h_rp); ++ dev_dbg(dev, "MMBI HROS - b2h_wp: 0x%0x, h2b_rp: 0x%0x\n", b2h_wp, ++ h2b_rp); + -+MODULE_AUTHOR("Jacky Chou "); -+MODULE_DESCRIPTION("ASPEED PCI-E MMBI Driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/soc/aspeed/aspeed-pcie-mmbi.h b/drivers/soc/aspeed/aspeed-pcie-mmbi.h ---- a/drivers/soc/aspeed/aspeed-pcie-mmbi.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-pcie-mmbi.h 2025-12-23 10:16:21.125032653 +0000 -@@ -0,0 +1,141 @@ -+/* SPDX-License-Identifier: GPL-2.0+ */ -+/* -+ * Copyright 2024 Aspeed Technology Inc. -+ */ -+#ifndef __ASPEED_PCIE_MMBI_H__ -+#define __ASPEED_PCIE_MMBI_H__ ++ if (h2b_wp >= h2b_rp) ++ *unread_data_len = h2b_wp - h2b_rp; ++ else ++ *unread_data_len = chan->h2b_cb_size - h2b_rp + h2b_wp; + -+#define MMBI_SIGNATURE "#MMBI$" ++ if (*unread_data_len < sizeof(struct mmbi_header)) { ++ dev_dbg(dev, "No data to read(%d - %d)\n", h2b_wp, h2b_rp); ++ return -EAGAIN; ++ } + -+//This definitions are as per MMBI specification. -+#define MMBI_PROTOCOL_IPMI 1 -+#define MMBI_PROTOCOL_SEAMLESS 2 -+#define MMBI_PROTOCOL_RAS_OFFLOAD 3 -+#define MMBI_PROTOCOL_MCTP 4 -+#define MMBI_PROTOCOL_NODE_MANAGER 5 ++ dev_dbg(dev, "READ MMBI header from: 0x%lx\n", ++ (ssize_t)(chan->h2b_cb_vmem + h2b_rp)); + -+#define MMBI_HRWS0(x) readl((x)->hrws_vmem) -+#define MMBI_HRWS1(x) readl((x)->hrws_vmem + 4) -+#define MMBI_HROS0(x) readl((x)->hros_vmem) -+#define MMBI_HROS1(x) readl((x)->hros_vmem + 4) ++ /* Extract MMBI protocol - protocol type and length */ ++ if ((h2b_rp + sizeof(header)) <= chan->h2b_cb_size) { ++ memcpy_fromio(&header, chan->h2b_cb_vmem + h2b_rp, ++ sizeof(header)); ++ } else { ++ ssize_t chunk_len = chan->h2b_cb_size - h2b_rp; + -+#define H2B_WRITE_POINTER_MASK GENMASK(31, 2) -+#define H2B_READ_POINTER_MASK GENMASK(31, 2) -+#define B2H_WRITE_POINTER_MASK GENMASK(31, 2) -+#define B2H_READ_POINTER_MASK GENMASK(31, 2) ++ memcpy_fromio(&header, chan->h2b_cb_vmem + h2b_rp, chunk_len); ++ memcpy_fromio(((u8 *)&header) + chunk_len, chan->h2b_cb_vmem, ++ sizeof(header) - chunk_len); ++ } + -+#define GET_H2B_WRITE_POINTER(x) (MMBI_HRWS0(x) & H2B_WRITE_POINTER_MASK) -+#define GET_H2B_READ_POINTER(x) (MMBI_HROS1(x) & H2B_READ_POINTER_MASK) -+#define GET_B2H_WRITE_POINTER(x) (MMBI_HROS0(x) & B2H_WRITE_POINTER_MASK) -+#define GET_B2H_READ_POINTER(x) (MMBI_HRWS1(x) & B2H_READ_POINTER_MASK) ++ *data_length = (header.pkt_len << 2) - sizeof(header) - header.pkt_pad; ++ *padding = header.pkt_pad; ++ *type = header.pkt_type; + -+#define GET_HOST_READY_BIT(x) (MMBI_HRWS1(x) & 0x01) -+#define GET_BMC_READY_BIT(x) (MMBI_HROS1(x) & 0x01) ++ return 0; ++} + -+typedef u8 protocol_type; ++static int mmbi_state_check(struct aspeed_mmbi_channel *chan) ++{ ++ enum mmbi_state current_state = mmbi_get_state(chan); ++ struct device *dev = chan->dev; ++ u32 req_data_len, unread_data_len; ++ u8 type, padding; + -+enum mmbi_state { /* B_U B_R H_U H_R */ -+ INIT_IN_PROGRESS = 0x00, /* 0 0 0 0 */ -+ INIT_COMPLETED = 0x08, /* 1 0 0 0 */ -+ NORMAL_RUNTIME = 0x0A, /* 1 0 1 0 */ -+ RESET_REQ_BY_BMC = 0x0E, /* 1 1 1 0 */ -+ RESET_REQ_BY_HOST = 0x0B, /* 1 0 1 1 */ -+ RESET_ACKED = 0x0F, /* 1 1 1 1 */ -+ TRANS_TO_INIT = 0x07, /* 0 1 1 1 */ -+ INIT_MISMATCH = 0x09, /* 1 0 0 1 */ -+ POWER_UP_OR_ERROR = 0x80000000, -+}; ++ switch (current_state) { ++ case INIT_MISMATCH: ++ dev_dbg(dev, "Get INIT_MISMATCH state from HOST"); ++ /* Reset MMBI data structure */ ++ mmbi_desc_init(chan); ++ /* Translat state to INIT_IN_PROGRESS */ ++ mmbi_clear_hros(chan); ++ mmbi_clear_hrws(chan); ++ /* Translat state to INIT_COMPLETED*/ ++ mmbi_set_bmc_up(chan, 1); + -+struct mmbi_header { -+ u32 pkt_pad : 2; -+ u32 pkt_len : 22; -+ u32 pkt_type : 4; -+ u32 reserved : 4; -+}; ++ dev_dbg(dev, "Change state to INIT_COMPLETED to HOST"); ++ raise_b2h_interrupt(chan); ++ return 1; ++ case NORMAL_RUNTIME: ++ if (mmbi_get_bmc_rdy(chan)) ++ return 0; ++ dev_dbg(dev, "Get NORMAL_RUNTIME state from HOST"); ++ mmbi_set_bmc_rdy(chan, 1); ++ return 1; ++ case RESET_REQ_BY_HOST: ++ dev_dbg(dev, "Get RESET_REQ_BY_HOST state from HOST"); ++ /* Stop operation */ ++ mmbi_set_bmc_rdy(chan, 0); ++ /* Change state to RESET_ACKED */ ++ mmbi_set_bmc_rst(chan, 1); ++ raise_b2h_interrupt(chan); ++ /* Change state to TRANS_TO_INIT */ ++ mmbi_set_bmc_up(chan, 0); ++ /* Reset MMBI data structure */ ++ mmbi_desc_init(chan); ++ /* Translat state to INIT_IN_PROGRESS */ ++ mmbi_clear_hros(chan); ++ mmbi_clear_hrws(chan); ++ /* Translat state to INIT_COMPLETED*/ ++ mmbi_set_bmc_up(chan, 1); + -+struct host_ros { -+ u32 b_rst : 1; /* BMC Reset Request */ -+ u32 b_up : 1; /* BMC Interface Up */ -+ u32 b2h_wp : 30; /* B2H Write Pointer */ -+ u32 b_rdy : 1; /* BMC Ready */ -+ u32 reserved1 : 1; -+ u32 h2b_rp : 30; /* H2B Read Pointer */ -+}; ++ dev_dbg(dev, "Change state to INIT_COMPLETED to HOST"); ++ raise_b2h_interrupt(chan); ++ return 1; ++ case RESET_ACKED: ++ /* Receive all packet from Host */ ++ while (get_mmbi_header(chan, &req_data_len, &type, &unread_data_len, &padding) == 0 && ++ mmbi_get_state(chan) == RESET_ACKED) ++ ; ++ /* Change state to TRANS_TO_INIT */ ++ mmbi_set_bmc_up(chan, 0); ++ /* Reset MMBI data structure */ ++ mmbi_desc_init(chan); ++ /* Translat state to INIT_IN_PROGRESS */ ++ mmbi_clear_hros(chan); ++ mmbi_clear_hrws(chan); ++ /* Translat state to INIT_COMPLETED*/ ++ mmbi_set_bmc_up(chan, 1); + -+struct host_rws { -+ u32 h_rst : 1; /* Host Reset Request */ -+ u32 h_up : 1; /* Host Interface Up */ -+ u32 h2b_wp : 30; /* H2B Write Pointer */ -+ u32 h_rdy : 1; /* Host Ready */ -+ u32 reserved1 : 1; -+ u32 b2h_rp : 30; /* B2H Read Pointer */ -+}; ++ dev_dbg(dev, "Change state to INIT_COMPLETED to HOST"); ++ raise_b2h_interrupt(chan); ++ default: ++ break; ++ } + -+struct buffer_type_desc { -+ u32 h_ros_p; /* Host Read-Only Structure Pointer */ -+ u32 h_rws_p; /* Host Read-Write Structure Pointer */ -+ u8 h_int_t; /* Host Interrupt Type */ -+ u8 h_int_l; /* Host Interrupt Location */ -+ u8 reserved1[3]; -+ u8 h_int_v; /* Host Interrupt Value */ -+ u8 bmc_int_t; /* BMC Interrupt Type */ -+ u32 bmc_int_l; /* BMC Interrupt Location */ -+ u8 reserved2[4]; -+ u8 bmc_int_v; /* BMC Interrupt Value */ -+} __packed; ++ return 0; ++} + -+struct mmbi_cap_desc { -+ u8 signature[6]; -+ u8 version; -+ u8 os_use; -+ u32 b2h_ba; /* B2H Buffer Base Address */ -+ u32 h2b_ba; /* H2B Buffer Base Address */ -+ u32 b2h_l; /* B2H Buffer Length */ -+ u32 h2b_l; /* H2B Buffer Length */ -+ u8 buffer_type; -+ u8 reserved1[7]; -+ struct buffer_type_desc bt_desc; -+ u8 reserved2[8]; -+} __packed; ++static void update_host_ros(struct aspeed_mmbi_channel *chan, unsigned int w_len, ++ unsigned int r_len) ++{ ++ struct device *dev = chan->dev; ++ struct host_ros hros; ++ u32 h2b_rp, b2h_wp; + -+struct aspeed_pcie_mmbi; ++ b2h_wp = GET_B2H_WRITE_POINTER(chan); ++ h2b_rp = GET_H2B_READ_POINTER(chan); + -+#define MCTP_MMBI_MTU 65536 -+#define MCTP_MMBI_MTU_MIN 68 /* base mtu (64) + mctp header */ -+#define MCTP_MMBI_MTU_MAX 65536 ++ /* Advance the B2H CB offset for next write */ ++ if ((b2h_wp + w_len) <= chan->b2h_cb_size) ++ b2h_wp += w_len; ++ else ++ b2h_wp = b2h_wp + w_len - chan->b2h_cb_size; + -+struct aspeed_mmbi_mctp { -+ struct aspeed_pcie_mmbi *mmbi; -+ struct net_device *ndev; -+}; ++ /* Advance the H2B CB offset till where BMC read data */ ++ if ((h2b_rp + r_len) <= chan->h2b_cb_size) ++ h2b_rp += r_len; ++ else ++ h2b_rp = h2b_rp + r_len - chan->h2b_cb_size; + -+struct aspeed_mmbi_channel { -+ struct aspeed_pcie_mmbi *mmbi; -+ struct device *dev; ++ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); ++ hros.b2h_wp = FIELD_GET(B2H_WRITE_POINTER_MASK, b2h_wp); ++ hros.h2b_rp = FIELD_GET(H2B_READ_POINTER_MASK, h2b_rp); ++ memcpy_toio(chan->hros_vmem, &hros, sizeof(hros)); ++ dev_dbg(dev, "Updating HROS - h2b_rp: 0x%0x, b2h_wp: 0x%0x\n", h2b_rp, b2h_wp); + -+ u32 b2h_cb_size; -+ u32 h2b_cb_size; -+ u8 __iomem *desc_vmem; -+ u8 __iomem *hros_vmem; -+ u8 __iomem *b2h_cb_vmem; -+ u8 __iomem *hrws_vmem; -+ u8 __iomem *h2b_cb_vmem; ++ if (w_len != 0) ++ raise_b2h_interrupt(chan); ++} + -+ bool bmc_int_en; -+ u8 bmc_int_value; -+ u32 bmc_int_location; -+ u8 __iomem *bmc_int_vmem; ++static int aspeed_mmbi_write(struct aspeed_mmbi_channel *chan, char *buffer, size_t len, ++ protocol_type type) ++{ ++ struct device *dev = chan->dev; ++ struct mmbi_header header = { 0 }; ++ ssize_t avail_buf_len; ++ ssize_t total_len; ++ ssize_t wt_offset; ++ ssize_t chunk_len; ++ ssize_t end_offset; ++ u8 padding = 0; + -+ bool host_int_en; -+ u8 host_int_location; -+ u8 host_int_value; ++ /* If HOST READY bit is not set, Just discard the write. */ ++ if (!GET_HOST_READY_BIT(chan)) { ++ dev_dbg(dev, "Host not ready, discarding request...\n"); ++ return -EAGAIN; ++ } + -+ enum mmbi_state state; ++ get_b2h_avail_buf_len(chan, &avail_buf_len); + -+ /* MCTP */ -+ struct net_device *ndev; ++ dev_dbg(dev, "B2H buffer empty space: %ld\n", avail_buf_len); + -+ struct work_struct work; -+}; ++ /* Header size */ ++ total_len = len + 4; + -+#endif -diff --git a/drivers/soc/aspeed/aspeed-socinfo.c b/drivers/soc/aspeed/aspeed-socinfo.c ---- a/drivers/soc/aspeed/aspeed-socinfo.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-socinfo.c 2025-12-23 10:16:21.125032653 +0000 -@@ -27,6 +27,10 @@ - { "AST2620", 0x05010203 }, - { "AST2605", 0x05030103 }, - { "AST2625", 0x05030403 }, -+ /* AST2700 */ -+ { "AST2750", 0x06000003 }, -+ { "AST2700", 0x06000103 }, -+ { "AST2720", 0x06000203 }, - }; - - static const char *siliconid_to_name(u32 siliconid) -diff --git a/drivers/soc/aspeed/aspeed-ssp.c b/drivers/soc/aspeed/aspeed-ssp.c ---- a/drivers/soc/aspeed/aspeed-ssp.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-ssp.c 2025-12-23 10:16:21.125032653 +0000 -@@ -0,0 +1,275 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+// Copyright (C) ASPEED Technology Inc. ++ padding = total_len & 0x3; ++ if (padding) ++ padding = 4 - padding; ++ total_len += padding; + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++ /* Empty space should be more than write request data size */ ++ if (avail_buf_len <= sizeof(header) || (total_len > (avail_buf_len - sizeof(header)))) ++ return -ENOSPC; + -+#define SSP_FILE_NAME "ast2600_ssp.bin" -+#define AST2600_CVIC_TRIGGER 0x28 -+#define AST2600_CVIC_PENDING_STATUS 0x18 -+#define AST2600_CVIC_PENDING_CLEAR 0x1C ++ /* Fill multi-protocol header */ ++ header.pkt_type = type; ++ header.pkt_len = total_len >> 2; ++ header.pkt_pad = padding; + -+#define SSP_CTRL_REG 0xa00 -+#define SSP_CTRL_RESET_ASSERT BIT(1) -+#define SSP_CTRL_EN BIT(0) ++ wt_offset = GET_B2H_WRITE_POINTER(chan); ++ end_offset = chan->b2h_cb_size; + -+#define SSP_MEM_BASE_REG 0xa04 -+#define SSP_IMEM_LIMIT_REG 0xa08 -+#define SSP_DMEM_LIMIT_REG 0xa0c -+#define SSP_CACHE_RANGE_REG 0xa40 -+#define SSP_CACHE_INVALID_REG 0xa44 -+#define SSP_CACHE_CTRL_REG 0xa48 -+#define SSP_CACHE_CLEAR_ICACHE BIT(2) -+#define SSP_CACHE_CLEAR_DCACHE BIT(1) -+#define SSP_CACHE_EN BIT(0) ++ /* Copy Header */ ++ if ((end_offset - wt_offset) >= sizeof(header)) { ++ memcpy_toio(chan->b2h_cb_vmem + wt_offset, &header, sizeof(header)); ++ wt_offset += sizeof(header); ++ } else { ++ chunk_len = end_offset - wt_offset; ++ memcpy_toio(chan->b2h_cb_vmem + wt_offset, &header, chunk_len); ++ memcpy_toio(chan->b2h_cb_vmem, &header + chunk_len, (sizeof(header) - chunk_len)); ++ wt_offset = (sizeof(header) - chunk_len); ++ } + -+#define SSP_TOTAL_MEM_SZ (32 * 1024 * 1024) -+#define SSP_CACHED_MEM_SZ (16 * 1024 * 1024) -+#define SSP_UNCACHED_MEM_SZ (SSP_TOTAL_MEM_SZ - SSP_CACHED_MEM_SZ) -+#define SSP_CACHE_1ST_16MB_ENABLE BIT(0) ++ /* Write the data */ ++ if ((end_offset - wt_offset) >= len) { ++ memcpy_toio(&chan->b2h_cb_vmem[wt_offset], buffer, len); ++ wt_offset += len; ++ } else { ++ chunk_len = end_offset - wt_offset; ++ dev_dbg(dev, "Write data chunk_len: %ld\n", chunk_len); ++ memcpy_toio(&chan->b2h_cb_vmem[wt_offset], buffer, chunk_len); + -+struct ast2600_ssp { -+ struct device *dev; -+ struct regmap *scu; -+ dma_addr_t ssp_mem_phy_addr; -+ void __iomem *ssp_mem_vir_addr; -+ dma_addr_t ssp_shared_mem_phy_addr; -+ void __iomem *ssp_shared_mem_vir_addr; -+ int ssp_shared_mem_size; -+ void __iomem *cvic; -+ int irq[16]; -+ int n_irq; -+}; ++ wt_offset = 0; ++ memcpy_toio(&chan->b2h_cb_vmem[wt_offset], buffer + chunk_len, len - chunk_len); ++ wt_offset += len - chunk_len; ++ } + -+static int ast_ssp_open(struct inode *inode, struct file *file) -+{ -+ return 0; -+} ++ update_host_ros(chan, total_len, 0); + -+static int ast_ssp_release(struct inode *inode, struct file *file) -+{ + return 0; +} + -+static const struct file_operations ast_ssp_fops = { -+ .owner = THIS_MODULE, -+ .open = ast_ssp_open, -+ .release = ast_ssp_release, -+ .llseek = noop_llseek, -+}; ++static void aspeed_mmbi_read(struct aspeed_mmbi_channel *chan, char *buffer, size_t len, u8 padding) ++{ ++ struct device *dev = chan->dev; ++ ssize_t rd_offset; ++ u32 h2b_rp; + -+struct miscdevice ast_ssp_misc = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = "ast-ssp", -+ .fops = &ast_ssp_fops, -+}; ++ h2b_rp = GET_H2B_READ_POINTER(chan); ++ if ((h2b_rp + sizeof(struct mmbi_header)) <= chan->h2b_cb_size) ++ rd_offset = h2b_rp + sizeof(struct mmbi_header); ++ else ++ rd_offset = h2b_rp + sizeof(struct mmbi_header) - chan->h2b_cb_size; + -+static irqreturn_t ast2600_ssp_interrupt(int irq, void *dev_id) -+{ -+ u32 i; -+ struct ast2600_ssp *priv = dev_id; -+ u32 isr = readl(priv->cvic + AST2600_CVIC_PENDING_STATUS); -+ u32 ssp_shared_rx_tx_size = priv->ssp_shared_mem_size / 2; -+ u32 *ssp_shared_mem_tx = priv->ssp_shared_mem_vir_addr; -+ u32 *ssp_shared_mem_rx = priv->ssp_shared_mem_vir_addr + ssp_shared_rx_tx_size; ++ /* Extract data and copy to user space application */ ++ dev_dbg(dev, "READ MMBI Data from: 0x%0lx and length: %ld\n", ++ (ssize_t)(chan->h2b_cb_vmem + rd_offset), len); + -+ dev_info(priv->dev, "isr %x\n", isr); -+ writel(isr, priv->cvic + AST2600_CVIC_PENDING_CLEAR); ++ if ((chan->h2b_cb_size - rd_offset) >= len) { ++ memcpy_fromio(buffer, chan->h2b_cb_vmem + rd_offset, len); ++ rd_offset += len; ++ } else { ++ ssize_t chunk_len; + -+ dev_info(priv->dev, "[CA7] rx addr:%08x, tx addr:%08x\n", -+ (u32)ssp_shared_mem_rx, (u32)ssp_shared_mem_tx); ++ chunk_len = chan->h2b_cb_size - rd_offset; ++ dev_dbg(dev, "Read data chunk_len: %ld\n", chunk_len); ++ memcpy_fromio(buffer, chan->h2b_cb_vmem + rd_offset, chunk_len); + -+ /* Check the CA7 RX data from CM3 TX data. */ -+ dev_info(priv->dev, "CA7 RX data from CM3 TX data: "); -+ for (i = 0; i < ssp_shared_rx_tx_size / 4; i++) { -+ if (readl(ssp_shared_mem_rx + i) != 0) { -+ dev_info(priv->dev, "[%08x] %08x ", -+ (u32)(ssp_shared_mem_rx + i), readl(ssp_shared_mem_rx + i)); -+ } else { -+ break; -+ } ++ rd_offset = 0; ++ memcpy_fromio(buffer + chunk_len, chan->h2b_cb_vmem + rd_offset, len - chunk_len); + } + -+ return IRQ_HANDLED; ++ update_host_ros(chan, 0, len + sizeof(struct mmbi_header) + padding); +} + -+static int ast_ssp_probe(struct platform_device *pdev) ++static void mctp_mmbi_rx(struct aspeed_mmbi_channel *chan) +{ -+ struct device_node *np, *mnode = dev_of_node(&pdev->dev); -+ const struct firmware *firmware; -+ struct ast2600_ssp *priv; -+ struct reserved_mem *rmem; -+ int i, ret; ++ struct net_device *ndev = chan->ndev; ++ struct sk_buff *skb; ++ struct mctp_skb_cb *cb; ++ u32 req_data_len, unread_data_len; ++ u8 type, padding; ++ int status; + -+ priv = kzalloc(sizeof(*priv), GFP_KERNEL); -+ if (!priv) { -+ ret = -ENOMEM; -+ goto finish; -+ } ++ if (get_mmbi_header(chan, &req_data_len, &type, &unread_data_len, &padding) != 0) ++ return; + -+ priv->dev = &pdev->dev; -+ priv->scu = syscon_regmap_lookup_by_phandle(priv->dev->of_node, "aspeed,scu"); -+ if (IS_ERR(priv->scu)) { -+ dev_err(priv->dev, "failed to find SCU regmap\n"); -+ ret = -EINVAL; -+ goto finish; -+ } -+ platform_set_drvdata(pdev, priv); ++ dev_dbg(chan->dev, "%s: Length: 0x%0x, Protocol Type: %d, Unread data: %d\n", __func__, ++ req_data_len, type, unread_data_len); + -+ ret = misc_register(&ast_ssp_misc); -+ if (ret) { -+ pr_err("can't misc_register :(\n"); -+ ret = -EIO; -+ goto finish; ++ skb = netdev_alloc_skb(ndev, req_data_len); ++ if (!skb) { ++ ndev->stats.rx_dropped++; ++ update_host_ros(chan, 0, req_data_len + sizeof(struct mmbi_header)); ++ return; + } -+ dev_set_drvdata(ast_ssp_misc.this_device, pdev); + -+ ret = of_reserved_mem_device_init(&pdev->dev); -+ if (ret) { -+ dev_err(priv->dev, -+ "failed to initialize reserved mem: %d\n", ret); -+ ret = -ENOMEM; -+ goto finish; -+ } ++ skb->protocol = htons(ETH_P_MCTP); ++ aspeed_mmbi_read(chan, skb_put(skb, req_data_len), req_data_len, padding); ++ skb_reset_network_header(skb); + -+ np = of_parse_phandle(priv->dev->of_node, "memory-region", 0); -+ if (!np) { -+ dev_err(priv->dev, "can't find memory-region node\n"); -+ ret = -ENOMEM; -+ goto finish; -+ } ++ cb = __mctp_cb(skb); ++ cb->halen = 0; + -+ rmem = of_reserved_mem_lookup(np); -+ of_node_put(np); -+ if (!rmem) { -+ dev_err(priv->dev, "can't find reserved memory.\n"); -+ ret = -ENOMEM; -+ goto finish; ++ status = netif_rx(skb); ++ if (status == NET_RX_SUCCESS) { ++ ndev->stats.rx_packets++; ++ ndev->stats.rx_bytes += req_data_len; + } else { -+ priv->ssp_mem_phy_addr = rmem->base; -+ priv->ssp_mem_vir_addr = devm_ioremap(priv->dev, priv->ssp_mem_phy_addr, SSP_TOTAL_MEM_SZ); -+ if (!priv->ssp_mem_vir_addr) { -+ dev_err(priv->dev, "can't create reserved memory.\n"); -+ ret = -ENOMEM; -+ goto finish; -+ } else { -+ dev_info(priv->dev, "\nSSP memory: virt(0x%08x), phys(0x%08x)\n", -+ (uint32_t)priv->ssp_mem_vir_addr, priv->ssp_mem_phy_addr); -+ } -+ } -+ -+ if (of_property_read_u32(np, "shm-size", &priv->ssp_shared_mem_size)) { -+ dev_err(priv->dev, "can't find shm-size property\n"); -+ ret = -ENOMEM; -+ goto finish; -+ } -+ -+ priv->ssp_shared_mem_vir_addr = priv->ssp_mem_vir_addr + SSP_TOTAL_MEM_SZ -+ - priv->ssp_shared_mem_size; -+ priv->ssp_shared_mem_phy_addr = priv->ssp_mem_phy_addr + SSP_TOTAL_MEM_SZ -+ - priv->ssp_shared_mem_size; -+ dev_info(priv->dev, "\nSSP shared memory: virt(0x%08x), phys(0x%08x), size(0x%08x)\n", -+ (uint32_t)priv->ssp_shared_mem_vir_addr, priv->ssp_shared_mem_phy_addr, -+ priv->ssp_shared_mem_size); -+ -+ if (request_firmware(&firmware, SSP_FILE_NAME, priv->dev) < 0) { -+ dev_err(priv->dev, "don't have %s\n", SSP_FILE_NAME); -+ release_firmware(firmware); -+ ret = -EINVAL; -+ goto finish; ++ ndev->stats.rx_dropped++; + } ++} + -+ memcpy(priv->ssp_mem_vir_addr, (void *)firmware->data, firmware->size); -+ release_firmware(firmware); ++static netdev_tx_t mctp_mmbi_tx(struct sk_buff *skb, struct net_device *ndev) ++{ ++ struct aspeed_mmbi_mctp *mctp = netdev_priv(ndev); ++ int ret; + -+ np = of_parse_phandle(mnode, "aspeed,cvic", 0); -+ if (!np) { -+ dev_err(priv->dev, "can't find CVIC\n"); -+ ret = -EINVAL; -+ goto finish; ++ if (!mmbi_get_host_rdy(&mctp->mmbi->chan) || skb->len > MCTP_MMBI_MTU_MAX) { ++ ndev->stats.tx_dropped++; ++ goto out; + } + -+ priv->cvic = devm_of_iomap(priv->dev, np, 0, NULL); -+ if (IS_ERR(priv->cvic)) { -+ dev_err(priv->dev, "can't map CVIC\n"); -+ ret = -EINVAL; -+ goto finish; ++ ret = aspeed_mmbi_write(&mctp->mmbi->chan, skb->data, skb->len, MMBI_PROTOCOL_MCTP); ++ if (ret) { ++ netif_stop_queue(ndev); ++ return NETDEV_TX_BUSY; + } + -+ i = 0; -+ while (0 != (priv->irq[i] = irq_of_parse_and_map(mnode, i))) { -+ ret = request_irq(priv->irq[i], ast2600_ssp_interrupt, 0, -+ "ssp-sw-irq", priv); -+ i++; -+ } -+ priv->n_irq = i; -+ dev_info(priv->dev, "%d ISRs registered\n", priv->n_irq); ++ ndev->stats.tx_packets++; ++ ndev->stats.tx_bytes += skb->len; ++out: ++ kfree_skb(skb); ++ return NETDEV_TX_OK; ++} + -+ regmap_write(priv->scu, SSP_CTRL_REG, 0); -+ mdelay(1); -+ regmap_write(priv->scu, SSP_MEM_BASE_REG, priv->ssp_mem_phy_addr); -+ regmap_write(priv->scu, SSP_IMEM_LIMIT_REG, priv->ssp_mem_phy_addr + SSP_CACHED_MEM_SZ); -+ regmap_write(priv->scu, SSP_DMEM_LIMIT_REG, priv->ssp_mem_phy_addr + SSP_TOTAL_MEM_SZ); ++static const struct net_device_ops mctp_mmbi_netdev_ops = { ++ .ndo_start_xmit = mctp_mmbi_tx, ++}; + -+ regmap_write(priv->scu, SSP_CACHE_RANGE_REG, SSP_CACHE_1ST_16MB_ENABLE); ++static void aspeed_mctp_mmbi_setup(struct net_device *ndev) ++{ ++ ndev->type = ARPHRD_MCTP; + -+ regmap_write(priv->scu, SSP_CTRL_REG, SSP_CTRL_RESET_ASSERT); -+ mdelay(1); -+ regmap_write(priv->scu, SSP_CTRL_REG, 0); -+ mdelay(1); -+ regmap_write(priv->scu, SSP_CTRL_REG, SSP_CTRL_EN); ++ /* we limit at the fixed MTU, which is also the MCTP-standard ++ * baseline MTU, so is also our minimum ++ */ ++ ndev->mtu = MCTP_MMBI_MTU; ++ ndev->max_mtu = MCTP_MMBI_MTU_MAX; ++ ndev->min_mtu = MCTP_MMBI_MTU_MIN; + -+ dev_info(priv->dev, "Init successful\n"); -+ ret = 0; -+finish: -+ return ret; ++ ndev->hard_header_len = 0; ++ ndev->addr_len = 0; ++ ndev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; ++ ndev->flags = IFF_NOARP; ++ ndev->netdev_ops = &mctp_mmbi_netdev_ops; ++ ndev->needs_free_netdev = true; +} + -+static void ast_ssp_remove(struct platform_device *pdev) ++static int aspeed_mmbi_mctp_init(struct aspeed_mmbi_channel *chan) +{ -+ struct ast2600_ssp *priv = platform_get_drvdata(pdev); -+ int i; -+ -+ dev_info(priv->dev, "SSP module removed\n"); -+ regmap_write(priv->scu, SSP_CTRL_REG, 0); -+ for (i = 0; i < priv->n_irq; i++) -+ free_irq(priv->irq[i], priv); -+ -+ kfree(priv); ++ struct aspeed_mmbi_mctp *mctp; ++ struct net_device *ndev; ++ char name[32]; ++ int ret; + -+ misc_deregister((struct miscdevice *)&ast_ssp_misc); -+} ++ snprintf(name, sizeof(name), "mctpmmbi%d%d", chan->mmbi->id, chan->mmbi->e2m_index); ++ ndev = alloc_netdev(sizeof(*mctp), name, NET_NAME_ENUM, aspeed_mctp_mmbi_setup); ++ if (!ndev) ++ return -ENOMEM; + -+static const struct of_device_id of_ast_ssp_match_table[] = { -+ { .compatible = "aspeed,ast2600-ssp", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, of_ast_ssp_match_table); ++ mctp = netdev_priv(ndev); ++ mctp->ndev = ndev; ++ mctp->mmbi = chan->mmbi; + -+static struct platform_driver ast_ssp_driver = { -+ .probe = ast_ssp_probe, -+ .remove = ast_ssp_remove, -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .of_match_table = of_ast_ssp_match_table, -+ }, -+}; ++ chan->ndev = ndev; + -+module_platform_driver(ast_ssp_driver); ++ ret = register_netdev(ndev); ++ if (ret) ++ goto free_netdev; + -+MODULE_LICENSE("Dual BSD/GPL"); -diff --git a/drivers/soc/aspeed/aspeed-uart-routing.c b/drivers/soc/aspeed/aspeed-uart-routing.c ---- a/drivers/soc/aspeed/aspeed-uart-routing.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-uart-routing.c 2025-12-23 10:16:21.125032653 +0000 -@@ -15,20 +15,30 @@ - #define HICRA 0x9c - - /* attributes options */ -+#define UART_ROUTING_IO0 "io0" - #define UART_ROUTING_IO1 "io1" - #define UART_ROUTING_IO2 "io2" - #define UART_ROUTING_IO3 "io3" - #define UART_ROUTING_IO4 "io4" - #define UART_ROUTING_IO5 "io5" - #define UART_ROUTING_IO6 "io6" -+#define UART_ROUTING_IO7 "io7" -+#define UART_ROUTING_IO8 "io8" -+#define UART_ROUTING_IO9 "io9" - #define UART_ROUTING_IO10 "io10" -+#define UART_ROUTING_IO12 "io12" -+#define UART_ROUTING_UART0 "uart0" - #define UART_ROUTING_UART1 "uart1" - #define UART_ROUTING_UART2 "uart2" - #define UART_ROUTING_UART3 "uart3" - #define UART_ROUTING_UART4 "uart4" - #define UART_ROUTING_UART5 "uart5" - #define UART_ROUTING_UART6 "uart6" -+#define UART_ROUTING_UART7 "uart7" -+#define UART_ROUTING_UART8 "uart8" -+#define UART_ROUTING_UART9 "uart9" - #define UART_ROUTING_UART10 "uart10" -+#define UART_ROUTING_UART12 "uart12" - #define UART_ROUTING_RES "reserved" - - struct aspeed_uart_routing { -@@ -488,6 +498,416 @@ - .attrs = ast2600_uart_routing_attrs, - }; - -+/* routing selector for AST27xx node 0 */ -+static struct aspeed_uart_routing_selector ast2700n0_uart9_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART9), -+ .reg = HICR9, -+ .shift = 12, -+ .mask = 0xf, -+ .options = { -+ UART_ROUTING_IO9, -+ UART_ROUTING_IO0, -+ UART_ROUTING_IO1, -+ UART_ROUTING_IO2, -+ UART_ROUTING_IO3, -+ UART_ROUTING_RES, -+ UART_ROUTING_UART0, -+ UART_ROUTING_UART1, -+ UART_ROUTING_UART2, -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART12, -+ NULL, -+ }, -+}; ++ return 0; + -+static struct aspeed_uart_routing_selector ast2700n0_io9_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO9), -+ .reg = HICR9, -+ .shift = 8, -+ .mask = 0xf, -+ .options = { -+ UART_ROUTING_UART0, -+ UART_ROUTING_UART1, -+ UART_ROUTING_UART2, -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART12, -+ UART_ROUTING_IO0, -+ UART_ROUTING_IO1, -+ UART_ROUTING_IO2, -+ UART_ROUTING_IO3, -+ UART_ROUTING_RES, -+ UART_ROUTING_UART9, -+ NULL, -+ }, -+}; ++free_netdev: ++ free_netdev(ndev); + -+static struct aspeed_uart_routing_selector ast2700n0_uart3_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART3), -+ .reg = HICRA, -+ .shift = 25, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO3, -+ UART_ROUTING_IO0, -+ UART_ROUTING_IO1, -+ UART_ROUTING_IO2, -+ UART_ROUTING_UART0, -+ UART_ROUTING_UART1, -+ UART_ROUTING_UART2, -+ UART_ROUTING_IO9, -+ NULL, -+ }, -+}; ++ return ret; ++} + -+static struct aspeed_uart_routing_selector ast2700n0_uart2_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART2), -+ .reg = HICRA, -+ .shift = 22, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO2, -+ UART_ROUTING_IO3, -+ UART_ROUTING_IO0, -+ UART_ROUTING_IO1, -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART0, -+ UART_ROUTING_UART1, -+ UART_ROUTING_IO9, -+ NULL, -+ }, -+}; ++static void aspeed_mmbi_work_func(struct work_struct *workq) ++{ ++ struct aspeed_mmbi_channel *chan = container_of(workq, struct aspeed_mmbi_channel, work); ++ u32 weight = 256, req_data_len, unread_data_len; ++ u8 type, padding; ++ int i; + -+static struct aspeed_uart_routing_selector ast2700n0_uart1_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART1), -+ .reg = HICRA, -+ .shift = 19, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO1, -+ UART_ROUTING_IO2, -+ UART_ROUTING_IO3, -+ UART_ROUTING_IO0, -+ UART_ROUTING_UART2, -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART0, -+ UART_ROUTING_IO9, -+ NULL, -+ }, -+}; ++ for (i = 0; i < weight; i++) { ++ if (get_mmbi_header(chan, &req_data_len, &type, &unread_data_len, &padding) != 0) ++ return; + -+static struct aspeed_uart_routing_selector ast2700n0_uart0_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART0), -+ .reg = HICRA, -+ .shift = 16, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO0, -+ UART_ROUTING_IO1, -+ UART_ROUTING_IO2, -+ UART_ROUTING_IO3, -+ UART_ROUTING_UART1, -+ UART_ROUTING_UART2, -+ UART_ROUTING_UART3, -+ UART_ROUTING_IO9, -+ NULL, -+ }, -+}; ++ dev_dbg(chan->dev, "%s: Length: 0x%0x, Protocol Type: %d\n", ++ __func__, req_data_len, type); + -+static struct aspeed_uart_routing_selector ast2700n0_io3_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO3), -+ .reg = HICRA, -+ .shift = 9, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART9, -+ UART_ROUTING_UART0, -+ UART_ROUTING_UART1, -+ UART_ROUTING_UART2, -+ UART_ROUTING_IO0, -+ UART_ROUTING_IO1, -+ UART_ROUTING_IO9, -+ NULL, -+ }, -+}; ++ if (type == MMBI_PROTOCOL_MCTP) ++ mctp_mmbi_rx(chan); ++ else ++ /* Discard data and advance the hrws */ ++ update_host_ros(chan, 0, req_data_len + sizeof(struct mmbi_header) + padding); + -+static struct aspeed_uart_routing_selector ast2700n0_io2_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO2), -+ .reg = HICRA, -+ .shift = 6, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART2, -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART9, -+ UART_ROUTING_UART0, -+ UART_ROUTING_UART1, -+ UART_ROUTING_IO0, -+ UART_ROUTING_IO1, -+ UART_ROUTING_IO9, -+ NULL, -+ }, -+}; ++ raise_b2h_interrupt(chan); ++ } + -+static struct aspeed_uart_routing_selector ast2700n0_io1_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO1), -+ .reg = HICRA, -+ .shift = 3, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART1, -+ UART_ROUTING_UART2, -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART9, -+ UART_ROUTING_UART0, -+ UART_ROUTING_IO2, -+ UART_ROUTING_IO3, -+ UART_ROUTING_IO9, -+ NULL, -+ }, -+}; ++ if (get_mmbi_header(chan, &req_data_len, &type, &unread_data_len, &padding) != 0) ++ queue_work(system_unbound_wq, &chan->work); ++} + -+static struct aspeed_uart_routing_selector ast2700n0_io0_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO0), -+ .reg = HICRA, -+ .shift = 0, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART0, -+ UART_ROUTING_UART1, -+ UART_ROUTING_UART2, -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART9, -+ UART_ROUTING_IO2, -+ UART_ROUTING_IO3, -+ UART_ROUTING_IO9, -+ NULL, -+ }, -+}; ++static irqreturn_t aspeed_pcie_mmbi_isr(int irq, void *dev_id) ++{ ++ struct aspeed_pcie_mmbi *mmbi = dev_id; ++ struct aspeed_mmbi_channel *chan = &mmbi->chan; ++ ssize_t avail_buf_len; + -+static struct attribute *ast2700n0_uart_routing_attrs[] = { -+ &ast2700n0_uart9_sel.dev_attr.attr, -+ &ast2700n0_io9_sel.dev_attr.attr, -+ &ast2700n0_uart3_sel.dev_attr.attr, -+ &ast2700n0_uart2_sel.dev_attr.attr, -+ &ast2700n0_uart1_sel.dev_attr.attr, -+ &ast2700n0_uart0_sel.dev_attr.attr, -+ &ast2700n0_io3_sel.dev_attr.attr, -+ &ast2700n0_io2_sel.dev_attr.attr, -+ &ast2700n0_io1_sel.dev_attr.attr, -+ &ast2700n0_io0_sel.dev_attr.attr, -+ NULL, -+}; ++ get_b2h_avail_buf_len(chan, &avail_buf_len); ++ if (avail_buf_len > MCTP_MMBI_MTU_MAX) { ++ if (netif_queue_stopped(chan->ndev)) { ++ dev_dbg(chan->dev, "Wake up mctp net device\n"); ++ netif_wake_queue(chan->ndev); ++ } ++ } + -+static const struct attribute_group ast2700n0_uart_routing_attr_group = { -+ .attrs = ast2700n0_uart_routing_attrs, -+}; ++ if (mmbi_state_check(chan)) ++ return IRQ_HANDLED; + -+/* routing selector for AST27xx node 1 */ -+static struct aspeed_uart_routing_selector ast2700n1_uart10_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART10), -+ .reg = HICR9, -+ .shift = 12, -+ .mask = 0xf, -+ .options = { -+ UART_ROUTING_IO10, -+ UART_ROUTING_IO5, -+ UART_ROUTING_IO6, -+ UART_ROUTING_IO7, -+ UART_ROUTING_IO8, -+ UART_ROUTING_RES, -+ UART_ROUTING_UART5, -+ UART_ROUTING_UART6, -+ UART_ROUTING_UART7, -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART12, -+ NULL, -+ }, -+}; ++ queue_work(system_unbound_wq, &chan->work); + -+static struct aspeed_uart_routing_selector ast2700n1_io10_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO10), -+ .reg = HICR9, -+ .shift = 8, -+ .mask = 0xf, -+ .options = { -+ UART_ROUTING_UART5, -+ UART_ROUTING_UART6, -+ UART_ROUTING_UART7, -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART12, -+ UART_ROUTING_IO5, -+ UART_ROUTING_IO6, -+ UART_ROUTING_IO7, -+ UART_ROUTING_IO8, -+ UART_ROUTING_RES, -+ UART_ROUTING_UART10, -+ NULL, -+ }, -+}; ++ return IRQ_HANDLED; ++} + -+static struct aspeed_uart_routing_selector ast2700n1_uart8_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART8), -+ .reg = HICRA, -+ .shift = 25, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO8, -+ UART_ROUTING_IO5, -+ UART_ROUTING_IO6, -+ UART_ROUTING_IO7, -+ UART_ROUTING_UART5, -+ UART_ROUTING_UART6, -+ UART_ROUTING_UART7, -+ UART_ROUTING_IO10, -+ NULL, -+ }, -+}; ++static void mmbi_desc_init(struct aspeed_mmbi_channel *chan) ++{ ++ struct mmbi_cap_desc desc; + -+static struct aspeed_uart_routing_selector ast2700n1_uart7_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART7), -+ .reg = HICRA, -+ .shift = 22, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO7, -+ UART_ROUTING_IO8, -+ UART_ROUTING_IO5, -+ UART_ROUTING_IO6, -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART5, -+ UART_ROUTING_UART6, -+ UART_ROUTING_IO10, -+ NULL, -+ }, -+}; ++ memset(&desc, 0, sizeof(struct mmbi_cap_desc)); + -+static struct aspeed_uart_routing_selector ast2700n1_uart6_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART6), -+ .reg = HICRA, -+ .shift = 19, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO6, -+ UART_ROUTING_IO7, -+ UART_ROUTING_IO8, -+ UART_ROUTING_IO5, -+ UART_ROUTING_UART7, -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART5, -+ UART_ROUTING_IO10, -+ NULL, -+ }, -+}; ++ desc.version = 1; ++ /* This MMBI interface is intended for OS use */ ++ desc.os_use = 1; ++ desc.b2h_ba = (chan->b2h_cb_vmem - chan->desc_vmem) >> 3; ++ desc.h2b_ba = (chan->h2b_cb_vmem - chan->desc_vmem) >> 3; ++ /* Make sure the buffer size is 4 byte aligmnent */ ++ desc.b2h_l = chan->b2h_cb_size & ~0x3; ++ desc.h2b_l = chan->h2b_cb_size & ~0x3; ++ /* Variable Packet Size Circular Buffers (VPSCB) v1 */ ++ desc.buffer_type = 0x01; ++ desc.bt_desc.h_ros_p = (chan->hros_vmem - chan->desc_vmem) >> 3; ++ desc.bt_desc.h_rws_p = (chan->hrws_vmem - chan->desc_vmem) >> 3; ++ /* PCIe Interrupt */ ++ desc.bt_desc.h_int_t = 0x01; ++ desc.bt_desc.h_int_l = chan->host_int_location; ++ desc.bt_desc.h_int_v = 0; /* Skip for PCIe Interrupt */ ++ desc.bt_desc.bmc_int_t = 0x01; /* relative memory space address */ ++ desc.bt_desc.bmc_int_l = chan->bmc_int_location; ++ desc.bt_desc.bmc_int_v = chan->bmc_int_value; + -+static struct aspeed_uart_routing_selector ast2700n1_uart5_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART5), -+ .reg = HICRA, -+ .shift = 16, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO5, -+ UART_ROUTING_IO6, -+ UART_ROUTING_IO7, -+ UART_ROUTING_IO8, -+ UART_ROUTING_UART6, -+ UART_ROUTING_UART7, -+ UART_ROUTING_UART8, -+ UART_ROUTING_IO10, -+ NULL, -+ }, -+}; ++ /* Per MMBI protoco spec, Set it to "#MMBI$" */ ++ memcpy(desc.signature, MMBI_SIGNATURE, sizeof(desc.signature)); + -+static struct aspeed_uart_routing_selector ast2700n1_io8_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO8), -+ .reg = HICRA, -+ .shift = 9, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART10, -+ UART_ROUTING_UART5, -+ UART_ROUTING_UART6, -+ UART_ROUTING_UART7, -+ UART_ROUTING_IO5, -+ UART_ROUTING_IO6, -+ UART_ROUTING_IO10, -+ NULL, -+ }, -+}; ++ memcpy_toio(chan->desc_vmem, &desc, sizeof(desc)); ++} + -+static struct aspeed_uart_routing_selector ast2700n1_io7_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO7), -+ .reg = HICRA, -+ .shift = 6, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART7, -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART10, -+ UART_ROUTING_UART5, -+ UART_ROUTING_UART6, -+ UART_ROUTING_IO5, -+ UART_ROUTING_IO6, -+ UART_ROUTING_IO10, -+ NULL, -+ }, -+}; ++static int aspeed_pcie_mmbi_init(struct aspeed_pcie_mmbi *mmbi) ++{ ++ struct aspeed_mmbi_channel *chan = &mmbi->chan; ++ struct device *dev = chan->dev; ++ u32 b2h_size = mmbi->mem_size >> 1; ++ u32 h2b_size = mmbi->mem_size >> 1; ++ u8 *h2b_vaddr, *b2h_vaddr; ++ int ret; + -+static struct aspeed_uart_routing_selector ast2700n1_io6_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO6), -+ .reg = HICRA, -+ .shift = 3, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART6, -+ UART_ROUTING_UART7, -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART10, -+ UART_ROUTING_UART5, -+ UART_ROUTING_IO7, -+ UART_ROUTING_IO8, -+ UART_ROUTING_IO10, -+ NULL, -+ }, -+}; ++ b2h_vaddr = mmbi->mem_virt; ++ h2b_vaddr = b2h_vaddr + b2h_size; + -+static struct aspeed_uart_routing_selector ast2700n1_io5_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO5), -+ .reg = HICRA, -+ .shift = 0, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART5, -+ UART_ROUTING_UART6, -+ UART_ROUTING_UART7, -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART10, -+ UART_ROUTING_IO7, -+ UART_ROUTING_IO8, -+ UART_ROUTING_IO10, -+ NULL, -+ }, -+}; ++ chan->dev = dev; ++ chan->desc_vmem = b2h_vaddr; ++ chan->hros_vmem = b2h_vaddr + sizeof(struct mmbi_cap_desc); ++ chan->b2h_cb_vmem = b2h_vaddr + sizeof(struct mmbi_cap_desc) + sizeof(struct host_ros); ++ chan->b2h_cb_size = b2h_size - sizeof(struct mmbi_cap_desc) - sizeof(struct host_ros); + -+static struct attribute *ast2700n1_uart_routing_attrs[] = { -+ &ast2700n1_uart10_sel.dev_attr.attr, -+ &ast2700n1_io10_sel.dev_attr.attr, -+ &ast2700n1_uart8_sel.dev_attr.attr, -+ &ast2700n1_uart7_sel.dev_attr.attr, -+ &ast2700n1_uart6_sel.dev_attr.attr, -+ &ast2700n1_uart5_sel.dev_attr.attr, -+ &ast2700n1_io8_sel.dev_attr.attr, -+ &ast2700n1_io7_sel.dev_attr.attr, -+ &ast2700n1_io6_sel.dev_attr.attr, -+ &ast2700n1_io5_sel.dev_attr.attr, -+ NULL, -+}; ++ chan->hrws_vmem = h2b_vaddr; ++ chan->h2b_cb_vmem = h2b_vaddr + sizeof(struct host_rws); ++ chan->h2b_cb_size = h2b_size - sizeof(struct host_rws); + -+static const struct attribute_group ast2700n1_uart_routing_attr_group = { -+ .attrs = ast2700n1_uart_routing_attrs, -+}; ++ dev_dbg(dev, "B2H mapped addr - desc: 0x%0lx, hros: 0x%0lx, b2h_cb: 0x%0lx\n", ++ (size_t)chan->desc_vmem, (size_t)chan->hros_vmem, (size_t)chan->b2h_cb_vmem); ++ dev_dbg(dev, "H2B mapped addr - hrws: 0x%0lx, h2b_cb: 0x%0lx\n", (size_t)chan->hrws_vmem, ++ (size_t)chan->h2b_cb_vmem); ++ ++ dev_dbg(dev, "B2H buffer size: 0x%0lx\n", (size_t)chan->b2h_cb_size); ++ dev_dbg(dev, "H2B buffer size: 0x%0lx\n", (size_t)chan->h2b_cb_size); ++ ++ /* Initialize the MMBI channel descriptor */ ++ mmbi_desc_init(chan); ++ ++ /* Clear HRWS & HROS */ ++ mmbi_clear_hros(chan); ++ mmbi_clear_hrws(chan); ++ ++ /* Initialize MTCP function */ ++ ret = aspeed_mmbi_mctp_init(chan); ++ if (ret) { ++ dev_err(dev, "Unable to init mctp\n"); ++ return ret; ++ } ++ ++ /* Set BMC UP bit */ ++ mmbi_set_bmc_up(chan, 1); ++ ++ return 0; ++} + - static ssize_t aspeed_uart_routing_show(struct device *dev, - struct device_attribute *attr, - char *buf) -@@ -580,6 +1000,10 @@ - .data = &ast2500_uart_routing_attr_group }, - { .compatible = "aspeed,ast2600-uart-routing", - .data = &ast2600_uart_routing_attr_group }, -+ { .compatible = "aspeed,ast2700n0-uart-routing", -+ .data = &ast2700n0_uart_routing_attr_group }, -+ { .compatible = "aspeed,ast2700n1-uart-routing", -+ .data = &ast2700n1_uart_routing_attr_group }, - { }, - }; - -@@ -589,7 +1013,7 @@ - .of_match_table = aspeed_uart_routing_table, - }, - .probe = aspeed_uart_routing_probe, -- .remove_new = aspeed_uart_routing_remove, -+ .remove = aspeed_uart_routing_remove, - }; - - module_platform_driver(aspeed_uart_routing_driver); -diff --git a/drivers/soc/aspeed/aspeed-udma.c b/drivers/soc/aspeed/aspeed-udma.c ---- a/drivers/soc/aspeed/aspeed-udma.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-udma.c 2025-12-23 10:16:21.125032653 +0000 -@@ -0,0 +1,434 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later +/* -+ * Copyright 2020 Aspeed Technology Inc. ++ * AST2700 PCIe MMBI (SCU & E2M) ++ * SoC | 0 | 1 | ++ * PCI class | MFD (0xFF_00_00) | MMBI (0x0C_0C_00) | ++ * Node | 0 1 | 0 | ++ * PID | 3 4 5 6 11 12 13 14 | 2 3 4 5 6 7 | ++ * E2M index | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 | ++ * BAR index | 2 3 4 5 2 3 4 5 | 0 1 2 3 4 5 | ++ * SCU BAR | 3c 4c 5c 6c 3c 4c 5c 6c | 1c 50 3c 4c 5c 6c | ++ * E2M H2B Int | 0 1 2 3 0 1 2 3 | 0 1 2 3 4 5 | (bit) + */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++static int aspeed_ast2700_pcie_mmbi_init(struct platform_device *pdev) ++{ ++ struct aspeed_pcie_mmbi *mmbi = platform_get_drvdata(pdev); ++ struct aspeed_mmbi_channel *chan = &mmbi->chan; ++ struct device *dev = &pdev->dev; ++ u32 value, sprot_size, e2m_index, pid; ++ struct resource res; ++ int ret, i; + -+#define DEVICE_NAME "aspeed-udma" ++ /* Get register map*/ ++ mmbi->e2m = syscon_node_to_regmap(dev->of_node->parent); ++ if (IS_ERR(mmbi->e2m)) { ++ dev_err(dev, "failed to find e2m regmap\n"); ++ return PTR_ERR(mmbi->e2m); ++ } ++ if (of_address_to_resource(dev->of_node->parent, 0, &res)) { ++ dev_err(dev, "Failed to get e2m resource\n"); ++ return -EINVAL; ++ } ++ if (res.start == 0x14c1d000) ++ mmbi->id = 2; ++ else if (res.start == 0x12c22000) ++ mmbi->id = 1; ++ else ++ mmbi->id = 0; /* 0x12c21000 */ + -+/* UART DMA registers offset */ -+#define UDMA_TX_DMA_EN 0x000 -+#define UDMA_RX_DMA_EN 0x004 -+#define UDMA_MISC 0x008 -+#define UDMA_MISC_RX_BUFSZ GENMASK(3, 2) -+#define UDMA_MISC_TX_BUFSZ GENMASK(1, 0) -+#define UDMA_TMOUT_TIMER 0x00c -+#define UDMA_TX_DMA_RST 0x020 -+#define UDMA_RX_DMA_RST 0x024 -+#define UDMA_TX_DMA_INT_EN 0x030 -+#define UDMA_TX_DMA_INT_STS 0x034 -+#define UDMA_RX_DMA_INT_EN 0x038 -+#define UDMA_RX_DMA_INT_STS 0x03c ++ mmbi->device = syscon_regmap_lookup_by_phandle(dev->of_node->parent, "aspeed,device"); ++ if (IS_ERR(mmbi->device)) { ++ dev_err(dev, "failed to find device regmap\n"); ++ return PTR_ERR(mmbi->device); ++ } + -+#define UDMA_CHX_OFF(x) ((x) * 0x20) -+#define UDMA_CHX_TX_RD_PTR(x) (0x040 + UDMA_CHX_OFF(x)) -+#define UDMA_CHX_TX_WR_PTR(x) (0x044 + UDMA_CHX_OFF(x)) -+#define UDMA_CHX_TX_BUF_ADDR(x) (0x048 + UDMA_CHX_OFF(x)) -+#define UDMA_CHX_TX_CTRL(x) (0x04c + UDMA_CHX_OFF(x)) -+#define UDMA_TX_CTRL_BUF_ADDRH GENMASK(10, 8) -+#define UDMA_TX_CTRL_TMOUT_DIS BIT(4) -+#define UDMA_TX_CTRL_BUFSZ GENMASK(3, 0) -+#define UDMA_CHX_RX_RD_PTR(x) (0x050 + UDMA_CHX_OFF(x)) -+#define UDMA_CHX_RX_WR_PTR(x) (0x054 + UDMA_CHX_OFF(x)) -+#define UDMA_CHX_RX_BUF_ADDR(x) (0x058 + UDMA_CHX_OFF(x)) -+#define UDMA_CHX_RX_CTRL(x) (0x05c + UDMA_CHX_OFF(x)) -+#define UDMA_RX_CTRL_BUF_ADDRH GENMASK(10, 8) -+#define UDMA_RX_CTRL_TMOUT_DIS BIT(4) -+#define UDMA_RX_CTRL_BUFSZ GENMASK(1, 0) ++ ret = of_property_read_u32(dev->of_node, "index", &mmbi->e2m_index); ++ if (ret < 0) { ++ dev_err(dev, "cannot get mmbi index value\n"); ++ return ret; ++ } + -+#define UDMA_MAX_CHANNEL 16 -+#define UDMA_TMOUT 0x200 ++ ret = of_property_read_u32(dev->of_node, "pid", &mmbi->pid); ++ if (ret < 0) { ++ dev_err(dev, "cannot get mmbi pid value\n"); ++ return ret; ++ } + -+enum aspeed_udma_bufsz_code { -+ UDMA_BUFSZ_CODE_1KB, -+ UDMA_BUFSZ_CODE_4KB, -+ UDMA_BUFSZ_CODE_16KB, -+ UDMA_BUFSZ_CODE_64KB, -+}; ++ ret = of_property_read_u32(dev->of_node, "bar", &mmbi->scu_bar_offset); ++ if (ret < 0) { ++ dev_err(dev, "cannot get mmbi bar value\n"); ++ return ret; ++ } + -+struct aspeed_udma_chan { -+ dma_addr_t dma_addr; ++ e2m_index = mmbi->e2m_index; ++ pid = mmbi->pid; ++ mmbi->e2m_h2b_int += mmbi->e2m_index; ++ if (mmbi->id < 2) { ++ /* PCIe device class, sub-class, protocol and reversion */ ++ regmap_write(mmbi->device, 0x18, 0xFF000027); ++ } else { ++ regmap_write(mmbi->device, 0x18, 0x0C0C0027); ++ regmap_write(mmbi->device, 0x78, ASPEED_SCU_INT_EN | ASPEED_SCU_DECODE_DEV); ++ } + -+ struct kfifo *fifo; -+ u32 fifo_sz; ++ /* MSI */ ++ regmap_update_bits(mmbi->device, 0x74, GENMASK(7, 4), BIT(7) | (5 << 4)); + -+ aspeed_udma_cb_t cb; -+ void *cb_arg; ++ regmap_update_bits(mmbi->device, 0x70, BIT(25) | BIT(17) | BIT(9) | BIT(1), ++ BIT(25) | BIT(17) | BIT(9) | BIT(1)); + -+ bool dis_tmout; -+}; ++ /* Calculate the BAR Size */ ++ for (i = 1; i < 16; i++) { ++ /* bar size check for 4k align */ ++ if ((mmbi->mem_size / 4096) == (1 << (i - 1))) ++ break; ++ } ++ if (i == 16) { ++ i = 0; ++ dev_warn(dev, "Bar size not align for 4K : %dK\n", (u32)mmbi->mem_size / 1024); ++ } ++ regmap_write(mmbi->device, mmbi->scu_bar_offset, (mmbi->mem_phy >> 4) | i); ++ regmap_write(mmbi->e2m, ASPEED_E2M_ADRMAP00 + (4 * pid), (mmbi->mem_phy >> 4) | i); + -+struct aspeed_udma { -+ struct device *dev; -+ u8 __iomem *regs; -+ int irq; -+ struct aspeed_udma_chan tx_chs[UDMA_MAX_CHANNEL]; -+ struct aspeed_udma_chan rx_chs[UDMA_MAX_CHANNEL]; -+ spinlock_t lock; -+}; ++ /* BMC Interrupt */ ++ if (chan->bmc_int_en) { ++ value = mmbi->mem_phy + chan->bmc_int_location; ++ regmap_write(mmbi->e2m, ASPEED_E2M_WIRQA0 + (4 * e2m_index), value); ++ value = (BIT(16) << pid) | chan->bmc_int_value; ++ regmap_write(mmbi->e2m, ASPEED_E2M_WIRQV0 + (4 * e2m_index), value); ++ } + -+struct aspeed_udma udma[1]; ++ /* HOST Interrupt: MSI */ ++ regmap_read(mmbi->e2m, ASPEED_E2M_EVENT_EN, &value); ++ value |= BIT(mmbi->e2m_h2b_int); ++ regmap_write(mmbi->e2m, ASPEED_E2M_EVENT_EN, value); + -+static int aspeed_udma_get_bufsz_code(u32 buf_sz) -+{ -+ switch (buf_sz) { -+ case SZ_1K: -+ return UDMA_BUFSZ_CODE_1KB; -+ case SZ_4K: -+ return UDMA_BUFSZ_CODE_4KB; -+ case SZ_16K: -+ return UDMA_BUFSZ_CODE_16KB; -+ case SZ_64K: -+ return UDMA_BUFSZ_CODE_64KB; -+ default: -+ break; ++ /* B2H Write Protect */ ++ sprot_size = (mmbi->mem_size / 2) / SZ_1M; ++ value = (sprot_size << 16) | (mmbi->mem_phy >> 20); ++ regmap_write(mmbi->e2m, ASPEED_E2M_SPROT_ADR0 + (4 * e2m_index), value); ++ /* Enable read & disalbe write */ ++ value = 1 << (8 + e2m_index); ++ regmap_write(mmbi->e2m, ASPEED_E2M_SPROT_CTL0 + (4 * e2m_index), value); ++ /* Set PID */ ++ regmap_read(mmbi->e2m, ASPEED_E2M_SPROT_SIDG0 + (4 * (e2m_index / 4)), &value); ++ value |= pid << (8 * (e2m_index % 4)); ++ regmap_write(mmbi->e2m, ASPEED_E2M_SPROT_SIDG0 + (4 * (e2m_index / 4)), value); ++ ++ mmbi->chan.dev = dev; ++ mmbi->chan.mmbi = mmbi; ++ ret = aspeed_pcie_mmbi_init(mmbi); ++ if (ret < 0) { ++ dev_err(dev, "Initialize MMBI device failed.\n"); ++ return ret; + } + -+ return -1; -+} ++ INIT_WORK(&chan->work, aspeed_mmbi_work_func); + -+static u32 aspeed_udma_get_tx_rptr(u32 ch_no) -+{ -+ return readl(udma->regs + UDMA_CHX_TX_RD_PTR(ch_no)); ++ return 0; +} + -+static u32 aspeed_udma_get_rx_wptr(u32 ch_no) -+{ -+ return readl(udma->regs + UDMA_CHX_RX_WR_PTR(ch_no)); -+} ++struct aspeed_platform ast2700_platform = { ++ .mmbi_init = aspeed_ast2700_pcie_mmbi_init, ++}; + -+static void aspeed_udma_set_ptr(u32 ch_no, u32 ptr, bool is_tx) -+{ -+ writel(ptr, udma->regs + -+ ((is_tx) ? UDMA_CHX_TX_WR_PTR(ch_no) : UDMA_CHX_RX_RD_PTR(ch_no))); -+} ++static const struct of_device_id aspeed_pcie_mmbi_of_matches[] = { ++ { .compatible = "aspeed,ast2700-pcie-mmbi", .data = &ast2700_platform }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, aspeed_pcie_mmbi_of_matches); + -+void aspeed_udma_set_tx_wptr(u32 ch_no, u32 wptr) ++static int aspeed_pcie_mmbi_probe(struct platform_device *pdev) +{ -+ aspeed_udma_set_ptr(ch_no, wptr, true); -+} -+EXPORT_SYMBOL(aspeed_udma_set_tx_wptr); ++ struct aspeed_pcie_mmbi *mmbi; ++ struct aspeed_mmbi_channel *chan; ++ struct device *dev = &pdev->dev; ++ struct resource res; ++ struct device_node *np; ++ const void *md; ++ int ret = 0; + -+void aspeed_udma_set_rx_rptr(u32 ch_no, u32 rptr) -+{ -+ aspeed_udma_set_ptr(ch_no, rptr, false); -+} -+EXPORT_SYMBOL(aspeed_udma_set_rx_rptr); ++ md = of_device_get_match_data(dev); ++ if (!md) ++ return -ENODEV; + -+static int aspeed_udma_free_chan(u32 ch_no, bool is_tx) -+{ -+ u32 reg; -+ unsigned long flags; ++ mmbi = devm_kzalloc(&pdev->dev, sizeof(struct aspeed_pcie_mmbi), GFP_KERNEL); ++ if (!mmbi) ++ return -ENOMEM; ++ dev_set_drvdata(dev, mmbi); + -+ if (ch_no > UDMA_MAX_CHANNEL) -+ return -EINVAL; ++ mmbi->dev = dev; ++ mmbi->platform = md; + -+ spin_lock_irqsave(&udma->lock, flags); ++ /* Get MMBI memory size */ ++ np = of_parse_phandle(dev->of_node, "memory-region", 0); ++ if (!np || of_address_to_resource(np, 0, &res)) { ++ dev_err(dev, "Failed to find memory-region.\n"); ++ ret = -ENOMEM; ++ goto out_region; ++ } + -+ reg = readl(udma->regs + -+ ((is_tx) ? UDMA_TX_DMA_INT_EN : UDMA_RX_DMA_INT_EN)); -+ reg &= ~(0x1 << ch_no); ++ of_node_put(np); + -+ writel(reg, udma->regs + -+ ((is_tx) ? UDMA_TX_DMA_INT_EN : UDMA_RX_DMA_INT_EN)); ++ mmbi->mem_phy = res.start; ++ mmbi->mem_size = resource_size(&res); ++ mmbi->mem_virt = ioremap(mmbi->mem_phy, mmbi->mem_size); ++ if (!mmbi->mem_virt) { ++ dev_err(dev, "cannot map mmbi memory region\n"); ++ ret = -ENOMEM; ++ goto out_region; ++ } + -+ spin_unlock_irqrestore(&udma->lock, flags); ++ /* Get IRQ */ ++ mmbi->irq = platform_get_irq(pdev, 0); ++ if (mmbi->irq < 0) { ++ dev_err(&pdev->dev, "platform get of irq[=%d] failed!\n", mmbi->irq); ++ ret = mmbi->irq; ++ goto out_unmap; ++ } ++ ret = devm_request_irq(&pdev->dev, mmbi->irq, aspeed_pcie_mmbi_isr, 0, dev_name(&pdev->dev), ++ mmbi); ++ if (ret) { ++ dev_err(dev, "pcie mmbi unable to get IRQ"); ++ goto out_unmap; ++ } ++ ++ chan = &mmbi->chan; ++ memset(chan, 0, sizeof(struct aspeed_mmbi_channel)); ++ ++ chan->bmc_int_en = true; ++ /* H2B Interrupt */ ++ ret = of_property_read_u8(dev->of_node, "bmc-int-value", &chan->bmc_int_value); ++ if (ret) { ++ dev_err(dev, "cannot get valid MMBI H2B interrupt value\n"); ++ chan->bmc_int_en = false; ++ } ++ ret = of_property_read_u32(dev->of_node, "bmc-int-location", &chan->bmc_int_location); ++ if (ret) { ++ dev_err(dev, "cannot get valid MMBI H2B interrupt location\n"); ++ chan->bmc_int_en = false; ++ } ++ /* B2H Interrupt */ ++ chan->host_int_en = true; ++ ret = of_property_read_u8(dev->of_node, "msi", &chan->host_int_value); ++ if (ret) { ++ dev_err(dev, "cannot get valid MMBI B2H interrupt location\n"); ++ chan->host_int_en = false; ++ } ++ ++ ret = mmbi->platform->mmbi_init(pdev); ++ if (ret) { ++ dev_err(dev, "Initialize pcie mmbi failed\n"); ++ goto out_irq; ++ } ++ ++ dev_info(dev, "ASPEED PCIe MMBI Dev %d: driver successfully loaded.\n", mmbi->id); + + return 0; ++out_irq: ++ devm_free_irq(dev, mmbi->irq, mmbi); ++out_unmap: ++ iounmap(mmbi->mem_virt); ++out_region: ++ devm_kfree(dev, mmbi); ++ dev_warn(dev, "aspeed pcie mmbi: driver init failed (ret=%d)!\n", ret); ++ return ret; +} + -+int aspeed_udma_free_tx_chan(u32 ch_no) ++static void aspeed_pcie_mmbi_remove(struct platform_device *pdev) +{ -+ return aspeed_udma_free_chan(ch_no, true); -+} -+EXPORT_SYMBOL(aspeed_udma_free_tx_chan); ++ struct aspeed_pcie_mmbi *mmbi = platform_get_drvdata(pdev); + -+int aspeed_udma_free_rx_chan(u32 ch_no) -+{ -+ return aspeed_udma_free_chan(ch_no, false); ++ cancel_work_sync(&mmbi->chan.work); ++ unregister_netdev(mmbi->chan.ndev); ++ devm_free_irq(&pdev->dev, mmbi->irq, mmbi); ++ iounmap(mmbi->mem_virt); ++ devm_kfree(&pdev->dev, mmbi); +} -+EXPORT_SYMBOL(aspeed_udma_free_rx_chan); + -+static int aspeed_udma_request_chan(u32 ch_no, dma_addr_t addr, -+ struct kfifo *fifo, u32 fifo_sz, -+ aspeed_udma_cb_t cb, void *id, bool dis_tmout, bool is_tx) -+{ -+ int retval = 0; -+ int fifosz_code; ++static struct platform_driver aspeed_pcie_mmbi_driver = { ++ .probe = aspeed_pcie_mmbi_probe, ++ .remove = aspeed_pcie_mmbi_remove, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = aspeed_pcie_mmbi_of_matches, ++ }, ++}; + -+ u32 reg; -+ unsigned long flags; -+ struct aspeed_udma_chan *ch; ++module_platform_driver(aspeed_pcie_mmbi_driver); + -+ if (ch_no > UDMA_MAX_CHANNEL) { -+ retval = -EINVAL; -+ goto out; -+ } ++MODULE_AUTHOR("Jacky Chou "); ++MODULE_DESCRIPTION("ASPEED PCI-E MMBI Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/soc/aspeed/aspeed-pcie-mmbi.h b/drivers/soc/aspeed/aspeed-pcie-mmbi.h +--- a/drivers/soc/aspeed/aspeed-pcie-mmbi.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-pcie-mmbi.h 2026-04-08 18:03:48.311705302 +0000 +@@ -0,0 +1,141 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright 2024 Aspeed Technology Inc. ++ */ ++#ifndef __ASPEED_PCIE_MMBI_H__ ++#define __ASPEED_PCIE_MMBI_H__ + -+ if (IS_ERR_OR_NULL(fifo) || IS_ERR_OR_NULL(fifo->kfifo.data)) { -+ retval = -EINVAL; -+ goto out; -+ } ++#define MMBI_SIGNATURE "#MMBI$" + -+ fifosz_code = aspeed_udma_get_bufsz_code(fifo_sz); -+ if (fifosz_code < 0) { -+ retval = -EINVAL; -+ goto out; -+ } ++//This definitions are as per MMBI specification. ++#define MMBI_PROTOCOL_IPMI 1 ++#define MMBI_PROTOCOL_SEAMLESS 2 ++#define MMBI_PROTOCOL_RAS_OFFLOAD 3 ++#define MMBI_PROTOCOL_MCTP 4 ++#define MMBI_PROTOCOL_NODE_MANAGER 5 + -+ spin_lock_irqsave(&udma->lock, flags); ++#define MMBI_HRWS0(x) readl((x)->hrws_vmem) ++#define MMBI_HRWS1(x) readl((x)->hrws_vmem + 4) ++#define MMBI_HROS0(x) readl((x)->hros_vmem) ++#define MMBI_HROS1(x) readl((x)->hros_vmem + 4) + -+ if (is_tx) { -+ reg = readl(udma->regs + UDMA_TX_DMA_INT_EN); -+ if (reg & (0x1 << ch_no)) { -+ retval = -EBUSY; -+ goto unlock_n_out; -+ } ++#define H2B_WRITE_POINTER_MASK GENMASK(31, 2) ++#define H2B_READ_POINTER_MASK GENMASK(31, 2) ++#define B2H_WRITE_POINTER_MASK GENMASK(31, 2) ++#define B2H_READ_POINTER_MASK GENMASK(31, 2) + -+ reg |= (0x1 << ch_no); -+ writel(reg, udma->regs + UDMA_TX_DMA_INT_EN); ++#define GET_H2B_WRITE_POINTER(x) (MMBI_HRWS0(x) & H2B_WRITE_POINTER_MASK) ++#define GET_H2B_READ_POINTER(x) (MMBI_HROS1(x) & H2B_READ_POINTER_MASK) ++#define GET_B2H_WRITE_POINTER(x) (MMBI_HROS0(x) & B2H_WRITE_POINTER_MASK) ++#define GET_B2H_READ_POINTER(x) (MMBI_HRWS1(x) & B2H_READ_POINTER_MASK) + -+ reg = FIELD_PREP(UDMA_TX_CTRL_BUF_ADDRH, (u64)addr >> 32) | -+ ((dis_tmout) ? UDMA_TX_CTRL_TMOUT_DIS : 0) | -+ FIELD_PREP(UDMA_TX_CTRL_BUFSZ, fifosz_code); -+ writel(reg, udma->regs + UDMA_CHX_TX_CTRL(ch_no)); ++#define GET_HOST_READY_BIT(x) (MMBI_HRWS1(x) & 0x01) ++#define GET_BMC_READY_BIT(x) (MMBI_HROS1(x) & 0x01) + -+ writel(addr, udma->regs + UDMA_CHX_TX_BUF_ADDR(ch_no)); -+ } else { -+ reg = readl(udma->regs + UDMA_RX_DMA_INT_EN); -+ if (reg & (0x1 << ch_no)) { -+ retval = -EBUSY; -+ goto unlock_n_out; -+ } ++typedef u8 protocol_type; + -+ reg |= (0x1 << ch_no); -+ writel(reg, udma->regs + UDMA_RX_DMA_INT_EN); ++enum mmbi_state { /* B_U B_R H_U H_R */ ++ INIT_IN_PROGRESS = 0x00, /* 0 0 0 0 */ ++ INIT_COMPLETED = 0x08, /* 1 0 0 0 */ ++ NORMAL_RUNTIME = 0x0A, /* 1 0 1 0 */ ++ RESET_REQ_BY_BMC = 0x0E, /* 1 1 1 0 */ ++ RESET_REQ_BY_HOST = 0x0B, /* 1 0 1 1 */ ++ RESET_ACKED = 0x0F, /* 1 1 1 1 */ ++ TRANS_TO_INIT = 0x07, /* 0 1 1 1 */ ++ INIT_MISMATCH = 0x09, /* 1 0 0 1 */ ++ POWER_UP_OR_ERROR = 0x80000000, ++}; + -+ reg = FIELD_PREP(UDMA_RX_CTRL_BUF_ADDRH, (u64)addr >> 32) | -+ ((dis_tmout) ? UDMA_RX_CTRL_TMOUT_DIS : 0) | -+ FIELD_PREP(UDMA_RX_CTRL_BUFSZ, fifosz_code); -+ writel(reg, udma->regs + UDMA_CHX_RX_CTRL(ch_no)); ++struct mmbi_header { ++ u32 pkt_pad : 2; ++ u32 pkt_len : 22; ++ u32 pkt_type : 4; ++ u32 reserved : 4; ++}; + -+ writel(addr, udma->regs + UDMA_CHX_RX_BUF_ADDR(ch_no)); -+ } ++struct host_ros { ++ u32 b_rst : 1; /* BMC Reset Request */ ++ u32 b_up : 1; /* BMC Interface Up */ ++ u32 b2h_wp : 30; /* B2H Write Pointer */ ++ u32 b_rdy : 1; /* BMC Ready */ ++ u32 reserved1 : 1; ++ u32 h2b_rp : 30; /* H2B Read Pointer */ ++}; + -+ ch = (is_tx) ? &udma->tx_chs[ch_no] : &udma->rx_chs[ch_no]; -+ ch->fifo = fifo; -+ ch->fifo_sz = fifo_sz; -+ ch->cb = cb; -+ ch->cb_arg = id; -+ ch->dma_addr = addr; -+ ch->dis_tmout = dis_tmout; ++struct host_rws { ++ u32 h_rst : 1; /* Host Reset Request */ ++ u32 h_up : 1; /* Host Interface Up */ ++ u32 h2b_wp : 30; /* H2B Write Pointer */ ++ u32 h_rdy : 1; /* Host Ready */ ++ u32 reserved1 : 1; ++ u32 b2h_rp : 30; /* B2H Read Pointer */ ++}; + -+unlock_n_out: -+ spin_unlock_irqrestore(&udma->lock, flags); -+out: -+ return 0; -+} ++struct buffer_type_desc { ++ u32 h_ros_p; /* Host Read-Only Structure Pointer */ ++ u32 h_rws_p; /* Host Read-Write Structure Pointer */ ++ u8 h_int_t; /* Host Interrupt Type */ ++ u8 h_int_l; /* Host Interrupt Location */ ++ u8 reserved1[3]; ++ u8 h_int_v; /* Host Interrupt Value */ ++ u8 bmc_int_t; /* BMC Interrupt Type */ ++ u32 bmc_int_l; /* BMC Interrupt Location */ ++ u8 reserved2[4]; ++ u8 bmc_int_v; /* BMC Interrupt Value */ ++} __packed; + -+int aspeed_udma_request_tx_chan(u32 ch_no, dma_addr_t addr, -+ struct kfifo *fifo, u32 fifo_sz, -+ aspeed_udma_cb_t cb, void *id, bool dis_tmout) -+{ -+ return aspeed_udma_request_chan(ch_no, addr, fifo, fifo_sz, cb, id, -+ dis_tmout, true); -+} -+EXPORT_SYMBOL(aspeed_udma_request_tx_chan); ++struct mmbi_cap_desc { ++ u8 signature[6]; ++ u8 version; ++ u8 os_use; ++ u32 b2h_ba; /* B2H Buffer Base Address */ ++ u32 h2b_ba; /* H2B Buffer Base Address */ ++ u32 b2h_l; /* B2H Buffer Length */ ++ u32 h2b_l; /* H2B Buffer Length */ ++ u8 buffer_type; ++ u8 reserved1[7]; ++ struct buffer_type_desc bt_desc; ++ u8 reserved2[8]; ++} __packed; ++ ++struct aspeed_pcie_mmbi; ++ ++#define MCTP_MMBI_MTU 65536 ++#define MCTP_MMBI_MTU_MIN 68 /* base mtu (64) + mctp header */ ++#define MCTP_MMBI_MTU_MAX 65536 ++ ++struct aspeed_mmbi_mctp { ++ struct aspeed_pcie_mmbi *mmbi; ++ struct net_device *ndev; ++}; ++ ++struct aspeed_mmbi_channel { ++ struct aspeed_pcie_mmbi *mmbi; ++ struct device *dev; ++ ++ u32 b2h_cb_size; ++ u32 h2b_cb_size; ++ u8 __iomem *desc_vmem; ++ u8 __iomem *hros_vmem; ++ u8 __iomem *b2h_cb_vmem; ++ u8 __iomem *hrws_vmem; ++ u8 __iomem *h2b_cb_vmem; ++ ++ bool bmc_int_en; ++ u8 bmc_int_value; ++ u32 bmc_int_location; ++ u8 __iomem *bmc_int_vmem; ++ ++ bool host_int_en; ++ u8 host_int_location; ++ u8 host_int_value; + -+int aspeed_udma_request_rx_chan(u32 ch_no, dma_addr_t addr, -+ struct kfifo *fifo, u32 fifo_sz, -+ aspeed_udma_cb_t cb, void *id, bool dis_tmout) -+{ -+ return aspeed_udma_request_chan(ch_no, addr, fifo, fifo_sz, cb, id, -+ dis_tmout, false); -+} -+EXPORT_SYMBOL(aspeed_udma_request_rx_chan); ++ enum mmbi_state state; + -+static void aspeed_udma_chan_ctrl(u32 ch_no, u32 op, bool is_tx) -+{ -+ unsigned long flags; -+ u32 reg_en, reg_rst; -+ u32 reg_en_off = (is_tx) ? UDMA_TX_DMA_EN : UDMA_RX_DMA_EN; -+ u32 reg_rst_off = (is_tx) ? UDMA_TX_DMA_RST : UDMA_TX_DMA_RST; ++ /* MCTP */ ++ struct net_device *ndev; + -+ if (ch_no > UDMA_MAX_CHANNEL) -+ return; ++ struct work_struct work; ++}; + -+ spin_lock_irqsave(&udma->lock, flags); ++#endif +diff --git a/drivers/soc/aspeed/aspeed-socinfo.c b/drivers/soc/aspeed/aspeed-socinfo.c +--- a/drivers/soc/aspeed/aspeed-socinfo.c 2025-08-01 08:48:47.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-socinfo.c 2026-04-08 18:03:48.311705302 +0000 +@@ -27,6 +27,10 @@ + { "AST2620", 0x05010203 }, + { "AST2605", 0x05030103 }, + { "AST2625", 0x05030403 }, ++ /* AST2700 */ ++ { "AST2750", 0x06000003 }, ++ { "AST2700", 0x06000103 }, ++ { "AST2720", 0x06000203 }, + }; + + static const char *siliconid_to_name(u32 siliconid) +diff --git a/drivers/soc/aspeed/aspeed-ssp.c b/drivers/soc/aspeed/aspeed-ssp.c +--- a/drivers/soc/aspeed/aspeed-ssp.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-ssp.c 2026-04-08 18:03:48.311705302 +0000 +@@ -0,0 +1,275 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++// Copyright (C) ASPEED Technology Inc. + -+ reg_en = readl(udma->regs + reg_en_off); -+ reg_rst = readl(udma->regs + reg_rst_off); ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ switch (op) { -+ case ASPEED_UDMA_OP_ENABLE: -+ reg_en |= (0x1 << ch_no); -+ writel(reg_en, udma->regs + reg_en_off); -+ break; -+ case ASPEED_UDMA_OP_DISABLE: -+ reg_en &= ~(0x1 << ch_no); -+ writel(reg_en, udma->regs + reg_en_off); -+ break; -+ case ASPEED_UDMA_OP_RESET: -+ reg_en &= ~(0x1 << ch_no); -+ writel(reg_en, udma->regs + reg_en_off); ++#define SSP_FILE_NAME "ast2600_ssp.bin" ++#define AST2600_CVIC_TRIGGER 0x28 ++#define AST2600_CVIC_PENDING_STATUS 0x18 ++#define AST2600_CVIC_PENDING_CLEAR 0x1C + -+ reg_rst |= (0x1 << ch_no); -+ writel(reg_rst, udma->regs + reg_rst_off); ++#define SSP_CTRL_REG 0xa00 ++#define SSP_CTRL_RESET_ASSERT BIT(1) ++#define SSP_CTRL_EN BIT(0) + -+ udelay(100); ++#define SSP_MEM_BASE_REG 0xa04 ++#define SSP_IMEM_LIMIT_REG 0xa08 ++#define SSP_DMEM_LIMIT_REG 0xa0c ++#define SSP_CACHE_RANGE_REG 0xa40 ++#define SSP_CACHE_INVALID_REG 0xa44 ++#define SSP_CACHE_CTRL_REG 0xa48 ++#define SSP_CACHE_CLEAR_ICACHE BIT(2) ++#define SSP_CACHE_CLEAR_DCACHE BIT(1) ++#define SSP_CACHE_EN BIT(0) + -+ reg_rst &= ~(0x1 << ch_no); -+ writel(reg_rst, udma->regs + reg_rst_off); -+ break; -+ default: -+ break; -+ } ++#define SSP_TOTAL_MEM_SZ (32 * 1024 * 1024) ++#define SSP_CACHED_MEM_SZ (16 * 1024 * 1024) ++#define SSP_UNCACHED_MEM_SZ (SSP_TOTAL_MEM_SZ - SSP_CACHED_MEM_SZ) ++#define SSP_CACHE_1ST_16MB_ENABLE BIT(0) + -+ spin_unlock_irqrestore(&udma->lock, flags); -+} ++struct ast2600_ssp { ++ struct device *dev; ++ struct regmap *scu; ++ dma_addr_t ssp_mem_phy_addr; ++ void __iomem *ssp_mem_vir_addr; ++ dma_addr_t ssp_shared_mem_phy_addr; ++ void __iomem *ssp_shared_mem_vir_addr; ++ int ssp_shared_mem_size; ++ void __iomem *cvic; ++ int irq[16]; ++ int n_irq; ++}; + -+void aspeed_udma_tx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op) ++static int ast_ssp_open(struct inode *inode, struct file *file) +{ -+ aspeed_udma_chan_ctrl(ch_no, op, true); ++ return 0; +} -+EXPORT_SYMBOL(aspeed_udma_tx_chan_ctrl); + -+void aspeed_udma_rx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op) ++static int ast_ssp_release(struct inode *inode, struct file *file) +{ -+ aspeed_udma_chan_ctrl(ch_no, op, false); ++ return 0; +} -+EXPORT_SYMBOL(aspeed_udma_rx_chan_ctrl); + -+static irqreturn_t aspeed_udma_isr(int irq, void *arg) -+{ -+ u32 bit; -+ unsigned long tx_sts = readl(udma->regs + UDMA_TX_DMA_INT_STS); -+ unsigned long rx_sts = readl(udma->regs + UDMA_RX_DMA_INT_STS); ++static const struct file_operations ast_ssp_fops = { ++ .owner = THIS_MODULE, ++ .open = ast_ssp_open, ++ .release = ast_ssp_release, ++ .llseek = noop_llseek, ++}; + -+ if (udma != (struct aspeed_udma *)arg) -+ return IRQ_NONE; ++struct miscdevice ast_ssp_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "ast-ssp", ++ .fops = &ast_ssp_fops, ++}; + -+ if (tx_sts == 0 && rx_sts == 0) -+ return IRQ_NONE; ++static irqreturn_t ast2600_ssp_interrupt(int irq, void *dev_id) ++{ ++ u32 i; ++ struct ast2600_ssp *priv = dev_id; ++ u32 isr = readl(priv->cvic + AST2600_CVIC_PENDING_STATUS); ++ u32 ssp_shared_rx_tx_size = priv->ssp_shared_mem_size / 2; ++ u32 *ssp_shared_mem_tx = priv->ssp_shared_mem_vir_addr; ++ u32 *ssp_shared_mem_rx = priv->ssp_shared_mem_vir_addr + ssp_shared_rx_tx_size; + -+ for_each_set_bit(bit, &tx_sts, UDMA_MAX_CHANNEL) { -+ writel((0x1 << bit), udma->regs + UDMA_TX_DMA_INT_STS); -+ if (udma->tx_chs[bit].cb) -+ udma->tx_chs[bit].cb(aspeed_udma_get_tx_rptr(bit), -+ udma->tx_chs[bit].cb_arg); -+ } ++ dev_info(priv->dev, "isr %x\n", isr); ++ writel(isr, priv->cvic + AST2600_CVIC_PENDING_CLEAR); + -+ for_each_set_bit(bit, &rx_sts, UDMA_MAX_CHANNEL) { -+ writel((0x1 << bit), udma->regs + UDMA_RX_DMA_INT_STS); -+ if (udma->rx_chs[bit].cb) -+ udma->rx_chs[bit].cb(aspeed_udma_get_rx_wptr(bit), -+ udma->rx_chs[bit].cb_arg); ++ dev_info(priv->dev, "[CA7] rx addr:%08x, tx addr:%08x\n", ++ (u32)ssp_shared_mem_rx, (u32)ssp_shared_mem_tx); ++ ++ /* Check the CA7 RX data from CM3 TX data. */ ++ dev_info(priv->dev, "CA7 RX data from CM3 TX data: "); ++ for (i = 0; i < ssp_shared_rx_tx_size / 4; i++) { ++ if (readl(ssp_shared_mem_rx + i) != 0) { ++ dev_info(priv->dev, "[%08x] %08x ", ++ (u32)(ssp_shared_mem_rx + i), readl(ssp_shared_mem_rx + i)); ++ } else { ++ break; ++ } + } + + return IRQ_HANDLED; +} + -+static int aspeed_udma_probe(struct platform_device *pdev) ++static int ast_ssp_probe(struct platform_device *pdev) +{ -+ int i, rc; -+ uint32_t reg; -+ struct resource *res; -+ struct device *dev = &pdev->dev; ++ struct device_node *np, *mnode = dev_of_node(&pdev->dev); ++ const struct firmware *firmware; ++ struct ast2600_ssp *priv; ++ struct reserved_mem *rmem; ++ int i, ret; + -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (IS_ERR_OR_NULL(res)) { -+ dev_err(dev, "failed to get register base\n"); -+ return -ENODEV; ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) { ++ ret = -ENOMEM; ++ goto finish; + } + -+ udma->regs = devm_ioremap_resource(dev, res); -+ if (IS_ERR_OR_NULL(udma->regs)) { -+ dev_err(dev, "failed to map registers\n"); -+ return PTR_ERR(udma->regs); ++ priv->dev = &pdev->dev; ++ priv->scu = syscon_regmap_lookup_by_phandle(priv->dev->of_node, "aspeed,scu"); ++ if (IS_ERR(priv->scu)) { ++ dev_err(priv->dev, "failed to find SCU regmap\n"); ++ ret = -EINVAL; ++ goto finish; + } ++ platform_set_drvdata(pdev, priv); + -+ /* disable for safety */ -+ writel(0x0, udma->regs + UDMA_TX_DMA_EN); -+ writel(0x0, udma->regs + UDMA_RX_DMA_EN); ++ ret = misc_register(&ast_ssp_misc); ++ if (ret) { ++ pr_err("can't misc_register :(\n"); ++ ret = -EIO; ++ goto finish; ++ } ++ dev_set_drvdata(ast_ssp_misc.this_device, pdev); + -+ udma->irq = platform_get_irq(pdev, 0); -+ if (udma->irq < 0) { -+ dev_err(dev, "failed to get IRQ number\n"); -+ return -ENODEV; ++ ret = of_reserved_mem_device_init(&pdev->dev); ++ if (ret) { ++ dev_err(priv->dev, ++ "failed to initialize reserved mem: %d\n", ret); ++ ret = -ENOMEM; ++ goto finish; + } + -+ rc = devm_request_irq(dev, udma->irq, aspeed_udma_isr, -+ IRQF_SHARED, DEVICE_NAME, udma); -+ if (rc) { -+ dev_err(dev, "failed to request IRQ handler\n"); -+ return rc; ++ np = of_parse_phandle(priv->dev->of_node, "memory-region", 0); ++ if (!np) { ++ dev_err(priv->dev, "can't find memory-region node\n"); ++ ret = -ENOMEM; ++ goto finish; + } + -+ /* -+ * For legacy design. -+ * - TX ringbuffer size: 4KB -+ * - RX ringbuffer size: 64KB -+ * - Timeout timer disabled -+ */ -+ reg = FIELD_PREP(UDMA_MISC_TX_BUFSZ, UDMA_BUFSZ_CODE_4KB) | -+ FIELD_PREP(UDMA_MISC_RX_BUFSZ, UDMA_BUFSZ_CODE_64KB); -+ writel(reg, udma->regs + UDMA_MISC); ++ rmem = of_reserved_mem_lookup(np); ++ of_node_put(np); ++ if (!rmem) { ++ dev_err(priv->dev, "can't find reserved memory.\n"); ++ ret = -ENOMEM; ++ goto finish; ++ } else { ++ priv->ssp_mem_phy_addr = rmem->base; ++ priv->ssp_mem_vir_addr = devm_ioremap(priv->dev, priv->ssp_mem_phy_addr, SSP_TOTAL_MEM_SZ); ++ if (!priv->ssp_mem_vir_addr) { ++ dev_err(priv->dev, "can't create reserved memory.\n"); ++ ret = -ENOMEM; ++ goto finish; ++ } else { ++ dev_info(priv->dev, "\nSSP memory: virt(0x%08x), phys(0x%08x)\n", ++ (uint32_t)priv->ssp_mem_vir_addr, priv->ssp_mem_phy_addr); ++ } ++ } + -+ for (i = 0; i < UDMA_MAX_CHANNEL; ++i) { -+ writel(0, udma->regs + UDMA_CHX_TX_WR_PTR(i)); -+ writel(0, udma->regs + UDMA_CHX_RX_RD_PTR(i)); ++ if (of_property_read_u32(np, "shm-size", &priv->ssp_shared_mem_size)) { ++ dev_err(priv->dev, "can't find shm-size property\n"); ++ ret = -ENOMEM; ++ goto finish; + } + -+ writel(0xffffffff, udma->regs + UDMA_TX_DMA_RST); -+ writel(0x0, udma->regs + UDMA_TX_DMA_RST); ++ priv->ssp_shared_mem_vir_addr = priv->ssp_mem_vir_addr + SSP_TOTAL_MEM_SZ ++ - priv->ssp_shared_mem_size; ++ priv->ssp_shared_mem_phy_addr = priv->ssp_mem_phy_addr + SSP_TOTAL_MEM_SZ ++ - priv->ssp_shared_mem_size; ++ dev_info(priv->dev, "\nSSP shared memory: virt(0x%08x), phys(0x%08x), size(0x%08x)\n", ++ (uint32_t)priv->ssp_shared_mem_vir_addr, priv->ssp_shared_mem_phy_addr, ++ priv->ssp_shared_mem_size); + -+ writel(0xffffffff, udma->regs + UDMA_RX_DMA_RST); -+ writel(0x0, udma->regs + UDMA_RX_DMA_RST); ++ if (request_firmware(&firmware, SSP_FILE_NAME, priv->dev) < 0) { ++ dev_err(priv->dev, "don't have %s\n", SSP_FILE_NAME); ++ release_firmware(firmware); ++ ret = -EINVAL; ++ goto finish; ++ } + -+ writel(0x0, udma->regs + UDMA_TX_DMA_INT_EN); -+ writel(0xffffffff, udma->regs + UDMA_TX_DMA_INT_STS); -+ writel(0x0, udma->regs + UDMA_RX_DMA_INT_EN); -+ writel(0xffffffff, udma->regs + UDMA_RX_DMA_INT_STS); ++ memcpy(priv->ssp_mem_vir_addr, (void *)firmware->data, firmware->size); ++ release_firmware(firmware); + -+ writel(UDMA_TMOUT, udma->regs + UDMA_TMOUT_TIMER); ++ np = of_parse_phandle(mnode, "aspeed,cvic", 0); ++ if (!np) { ++ dev_err(priv->dev, "can't find CVIC\n"); ++ ret = -EINVAL; ++ goto finish; ++ } + -+ spin_lock_init(&udma->lock); ++ priv->cvic = devm_of_iomap(priv->dev, np, 0, NULL); ++ if (IS_ERR(priv->cvic)) { ++ dev_err(priv->dev, "can't map CVIC\n"); ++ ret = -EINVAL; ++ goto finish; ++ } + -+ dev_set_drvdata(dev, udma); ++ i = 0; ++ while (0 != (priv->irq[i] = irq_of_parse_and_map(mnode, i))) { ++ ret = request_irq(priv->irq[i], ast2600_ssp_interrupt, 0, ++ "ssp-sw-irq", priv); ++ i++; ++ } ++ priv->n_irq = i; ++ dev_info(priv->dev, "%d ISRs registered\n", priv->n_irq); + -+ return 0; -+} ++ regmap_write(priv->scu, SSP_CTRL_REG, 0); ++ mdelay(1); ++ regmap_write(priv->scu, SSP_MEM_BASE_REG, priv->ssp_mem_phy_addr); ++ regmap_write(priv->scu, SSP_IMEM_LIMIT_REG, priv->ssp_mem_phy_addr + SSP_CACHED_MEM_SZ); ++ regmap_write(priv->scu, SSP_DMEM_LIMIT_REG, priv->ssp_mem_phy_addr + SSP_TOTAL_MEM_SZ); + -+static const struct of_device_id aspeed_udma_match[] = { -+ { .compatible = "aspeed,ast2500-udma" }, -+ { .compatible = "aspeed,ast2600-udma" }, -+ { .compatible = "aspeed,ast2700-udma" }, -+ { }, -+}; ++ regmap_write(priv->scu, SSP_CACHE_RANGE_REG, SSP_CACHE_1ST_16MB_ENABLE); + -+static struct platform_driver aspeed_udma_driver = { -+ .driver = { -+ .name = DEVICE_NAME, -+ .of_match_table = aspeed_udma_match, ++ regmap_write(priv->scu, SSP_CTRL_REG, SSP_CTRL_RESET_ASSERT); ++ mdelay(1); ++ regmap_write(priv->scu, SSP_CTRL_REG, 0); ++ mdelay(1); ++ regmap_write(priv->scu, SSP_CTRL_REG, SSP_CTRL_EN); + -+ }, -+ .probe = aspeed_udma_probe, -+}; ++ dev_info(priv->dev, "Init successful\n"); ++ ret = 0; ++finish: ++ return ret; ++} + -+module_platform_driver(aspeed_udma_driver); ++static void ast_ssp_remove(struct platform_device *pdev) ++{ ++ struct ast2600_ssp *priv = platform_get_drvdata(pdev); ++ int i; + -+MODULE_AUTHOR("Chia-Wei Wang "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Aspeed UDMA Engine Driver"); -diff --git a/drivers/soc/aspeed/aspeed-usb-hp.c b/drivers/soc/aspeed/aspeed-usb-hp.c ---- a/drivers/soc/aspeed/aspeed-usb-hp.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-usb-hp.c 2025-12-23 10:16:21.125032653 +0000 -@@ -0,0 +1,152 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Copyright 2021 Aspeed Technology Inc. -+ */ ++ dev_info(priv->dev, "SSP module removed\n"); ++ regmap_write(priv->scu, SSP_CTRL_REG, 0); ++ for (i = 0; i < priv->n_irq; i++) ++ free_irq(priv->irq[i], priv); + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++ kfree(priv); + -+#define USB_HP_BEHCI84 0x84 /* Controller Fine-tune Register */ ++ misc_deregister((struct miscdevice *)&ast_ssp_misc); ++} + -+static const struct of_device_id aspeed_usb_hp_dt_ids[] = { -+ { -+ .compatible = "aspeed,ast2600-usb2ahp", -+ }, -+ { -+ .compatible = "aspeed,ast2700-usb3ahp", -+ }, -+ { -+ .compatible = "aspeed,ast2700-usb3bhp", -+ }, -+ { -+ .compatible = "aspeed,ast2700-usb2ahp", -+ }, -+ { -+ .compatible = "aspeed,ast2700-usb2bhp", -+ }, -+ {} ++static const struct of_device_id of_ast_ssp_match_table[] = { ++ { .compatible = "aspeed,ast2600-ssp", }, ++ {}, +}; -+MODULE_DEVICE_TABLE(of, aspeed_usb_hp_dt_ids); -+ -+static int aspeed_usb_hp_probe(struct platform_device *pdev) -+{ -+ struct device_node *node = pdev->dev.of_node; -+ void __iomem *regs; -+ bool ehci_32bits_quirk; -+ u32 val; -+ struct clk *clk; -+ struct reset_control *rst; -+ struct regmap *device; -+ struct phy *usb3_phy; -+ bool is_pcie_xhci; -+ int rc = 0; -+ -+ if (of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2600-usb2ahp")) { -+ dev_info(&pdev->dev, "Initialized AST2600 USB2AHP\n"); -+ return 0; -+ } -+ -+ if (of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2700-usb3ahp") || -+ of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2700-usb3bhp")) { -+ is_pcie_xhci = true; -+ } else if (of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2700-usb2ahp") || -+ of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2700-usb2bhp")) { -+ is_pcie_xhci = false; -+ } -+ clk = devm_clk_get(&pdev->dev, NULL); -+ if (IS_ERR(clk)) -+ return PTR_ERR(clk); ++MODULE_DEVICE_TABLE(of, of_ast_ssp_match_table); + -+ rc = clk_prepare_enable(clk); -+ if (rc) { -+ dev_err(&pdev->dev, "Unable to enable clock (%d)\n", rc); -+ return rc; -+ } ++static struct platform_driver ast_ssp_driver = { ++ .probe = ast_ssp_probe, ++ .remove = ast_ssp_remove, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = of_ast_ssp_match_table, ++ }, ++}; + -+ rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); -+ if (IS_ERR(rst)) { -+ rc = PTR_ERR(rst); -+ goto err; -+ } -+ rc = reset_control_deassert(rst); -+ if (rc) -+ goto err; ++module_platform_driver(ast_ssp_driver); + -+ device = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "aspeed,device"); -+ if (IS_ERR(device)) { -+ dev_err(&pdev->dev, "failed to find device regmap\n"); -+ goto err; -+ } ++MODULE_LICENSE("Dual BSD/GPL"); +diff --git a/drivers/soc/aspeed/aspeed-uart-routing.c b/drivers/soc/aspeed/aspeed-uart-routing.c +--- a/drivers/soc/aspeed/aspeed-uart-routing.c 2025-08-01 08:48:47.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-uart-routing.c 2026-04-08 18:03:48.311705302 +0000 +@@ -15,20 +15,30 @@ + #define HICRA 0x9c + + /* attributes options */ ++#define UART_ROUTING_IO0 "io0" + #define UART_ROUTING_IO1 "io1" + #define UART_ROUTING_IO2 "io2" + #define UART_ROUTING_IO3 "io3" + #define UART_ROUTING_IO4 "io4" + #define UART_ROUTING_IO5 "io5" + #define UART_ROUTING_IO6 "io6" ++#define UART_ROUTING_IO7 "io7" ++#define UART_ROUTING_IO8 "io8" ++#define UART_ROUTING_IO9 "io9" + #define UART_ROUTING_IO10 "io10" ++#define UART_ROUTING_IO12 "io12" ++#define UART_ROUTING_UART0 "uart0" + #define UART_ROUTING_UART1 "uart1" + #define UART_ROUTING_UART2 "uart2" + #define UART_ROUTING_UART3 "uart3" + #define UART_ROUTING_UART4 "uart4" + #define UART_ROUTING_UART5 "uart5" + #define UART_ROUTING_UART6 "uart6" ++#define UART_ROUTING_UART7 "uart7" ++#define UART_ROUTING_UART8 "uart8" ++#define UART_ROUTING_UART9 "uart9" + #define UART_ROUTING_UART10 "uart10" ++#define UART_ROUTING_UART12 "uart12" + #define UART_ROUTING_RES "reserved" + + struct aspeed_uart_routing { +@@ -488,6 +498,416 @@ + .attrs = ast2600_uart_routing_attrs, + }; + ++/* routing selector for AST27xx node 0 */ ++static struct aspeed_uart_routing_selector ast2700n0_uart9_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART9), ++ .reg = HICR9, ++ .shift = 12, ++ .mask = 0xf, ++ .options = { ++ UART_ROUTING_IO9, ++ UART_ROUTING_IO0, ++ UART_ROUTING_IO1, ++ UART_ROUTING_IO2, ++ UART_ROUTING_IO3, ++ UART_ROUTING_RES, ++ UART_ROUTING_UART0, ++ UART_ROUTING_UART1, ++ UART_ROUTING_UART2, ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART12, ++ NULL, ++ }, ++}; + -+ if (is_pcie_xhci) { -+ usb3_phy = devm_phy_get(&pdev->dev, "usb3-phy"); -+ if (IS_ERR(usb3_phy)) { -+ rc = dev_err_probe(&pdev->dev, PTR_ERR(usb3_phy), -+ "failed to get usb3 phy\n"); -+ goto err; -+ } -+ rc = phy_init(usb3_phy); -+ if (rc < 0) { -+ dev_err(&pdev->dev, "failed to init usb3 phy\n"); -+ goto err; -+ } -+ //EnPCIaMSI_EnPCIaIntA_EnPCIaMst_EnPCIaDev -+ /* Turn on PCIe xHCI without MSI */ -+ regmap_update_bits(device, 0x70, -+ BIT(19) | BIT(11) | BIT(3), -+ BIT(19) | BIT(11) | BIT(3)); -+ } else { -+ ehci_32bits_quirk = -+ device_property_read_bool(&pdev->dev, "aspeed,ehci_32bits_quirk"); ++static struct aspeed_uart_routing_selector ast2700n0_io9_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO9), ++ .reg = HICR9, ++ .shift = 8, ++ .mask = 0xf, ++ .options = { ++ UART_ROUTING_UART0, ++ UART_ROUTING_UART1, ++ UART_ROUTING_UART2, ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART12, ++ UART_ROUTING_IO0, ++ UART_ROUTING_IO1, ++ UART_ROUTING_IO2, ++ UART_ROUTING_IO3, ++ UART_ROUTING_RES, ++ UART_ROUTING_UART9, ++ NULL, ++ }, ++}; + -+ if (ehci_32bits_quirk) { -+ regs = of_iomap(node, 0); -+ val = readl(regs + USB_HP_BEHCI84) & ~BIT(11); -+ writel(val, regs + USB_HP_BEHCI84); -+ } ++static struct aspeed_uart_routing_selector ast2700n0_uart3_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART3), ++ .reg = HICRA, ++ .shift = 25, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO3, ++ UART_ROUTING_IO0, ++ UART_ROUTING_IO1, ++ UART_ROUTING_IO2, ++ UART_ROUTING_UART0, ++ UART_ROUTING_UART1, ++ UART_ROUTING_UART2, ++ UART_ROUTING_IO9, ++ NULL, ++ }, ++}; + -+ //EnPCIaMSI_EnPCIaIntA_EnPCIaMst_EnPCIaDev -+ /* Turn on PCIe EHCI without MSI */ -+ regmap_update_bits(device, 0x70, -+ BIT(18) | BIT(10) | BIT(2), -+ BIT(18) | BIT(10) | BIT(2)); -+ } -+ dev_info(&pdev->dev, "Initialized AST2700 USB Host PCIe\n"); -+ return 0; -+err: -+ if (clk) -+ clk_disable_unprepare(clk); -+ return rc; -+} ++static struct aspeed_uart_routing_selector ast2700n0_uart2_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART2), ++ .reg = HICRA, ++ .shift = 22, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO2, ++ UART_ROUTING_IO3, ++ UART_ROUTING_IO0, ++ UART_ROUTING_IO1, ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART0, ++ UART_ROUTING_UART1, ++ UART_ROUTING_IO9, ++ NULL, ++ }, ++}; + -+static void aspeed_usb_hp_remove(struct platform_device *pdev) -+{ -+ dev_info(&pdev->dev, "Remove USB Host PCIe\n"); -+} ++static struct aspeed_uart_routing_selector ast2700n0_uart1_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART1), ++ .reg = HICRA, ++ .shift = 19, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO1, ++ UART_ROUTING_IO2, ++ UART_ROUTING_IO3, ++ UART_ROUTING_IO0, ++ UART_ROUTING_UART2, ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART0, ++ UART_ROUTING_IO9, ++ NULL, ++ }, ++}; + -+static struct platform_driver aspeed_usb_hp_driver = { -+ .probe = aspeed_usb_hp_probe, -+ .remove = aspeed_usb_hp_remove, -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .of_match_table = aspeed_usb_hp_dt_ids, -+ }, ++static struct aspeed_uart_routing_selector ast2700n0_uart0_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART0), ++ .reg = HICRA, ++ .shift = 16, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO0, ++ UART_ROUTING_IO1, ++ UART_ROUTING_IO2, ++ UART_ROUTING_IO3, ++ UART_ROUTING_UART1, ++ UART_ROUTING_UART2, ++ UART_ROUTING_UART3, ++ UART_ROUTING_IO9, ++ NULL, ++ }, +}; -+module_platform_driver(aspeed_usb_hp_driver); + -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Neal Liu "); -diff --git a/drivers/soc/aspeed/aspeed-usb-phy.c b/drivers/soc/aspeed/aspeed-usb-phy.c ---- a/drivers/soc/aspeed/aspeed-usb-phy.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-usb-phy.c 2025-12-23 10:16:21.125032653 +0000 -@@ -0,0 +1,112 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Copyright 2021 Aspeed Technology Inc. -+ */ ++static struct aspeed_uart_routing_selector ast2700n0_io3_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO3), ++ .reg = HICRA, ++ .shift = 9, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART9, ++ UART_ROUTING_UART0, ++ UART_ROUTING_UART1, ++ UART_ROUTING_UART2, ++ UART_ROUTING_IO0, ++ UART_ROUTING_IO1, ++ UART_ROUTING_IO9, ++ NULL, ++ }, ++}; + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++static struct aspeed_uart_routing_selector ast2700n0_io2_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO2), ++ .reg = HICRA, ++ .shift = 6, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART2, ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART9, ++ UART_ROUTING_UART0, ++ UART_ROUTING_UART1, ++ UART_ROUTING_IO0, ++ UART_ROUTING_IO1, ++ UART_ROUTING_IO9, ++ NULL, ++ }, ++}; + -+struct usb_phy_ctrl { -+ u32 offset; -+ u32 value; ++static struct aspeed_uart_routing_selector ast2700n0_io1_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO1), ++ .reg = HICRA, ++ .shift = 3, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART1, ++ UART_ROUTING_UART2, ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART9, ++ UART_ROUTING_UART0, ++ UART_ROUTING_IO2, ++ UART_ROUTING_IO3, ++ UART_ROUTING_IO9, ++ NULL, ++ }, +}; + -+static const struct of_device_id aspeed_usb_phy_dt_ids[] = { -+ { -+ .compatible = "aspeed,ast2600-uphyb", -+ }, -+ { -+ .compatible = "aspeed,ast2700-uphy2a", -+ }, -+ { -+ .compatible = "aspeed,ast2700-uphy2b", -+ }, -+ { } ++static struct aspeed_uart_routing_selector ast2700n0_io0_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO0), ++ .reg = HICRA, ++ .shift = 0, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART0, ++ UART_ROUTING_UART1, ++ UART_ROUTING_UART2, ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART9, ++ UART_ROUTING_IO2, ++ UART_ROUTING_IO3, ++ UART_ROUTING_IO9, ++ NULL, ++ }, +}; -+MODULE_DEVICE_TABLE(of, aspeed_usb_phy_dt_ids); + -+static int aspeed_usb_phy_probe(struct platform_device *pdev) -+{ -+ struct device_node *node = pdev->dev.of_node; -+ struct usb_phy_ctrl *ctrl_data; -+ void __iomem *base; -+ struct regmap *scu; -+ int ctrl_num = 1; -+ int ret, i; -+ u32 val; ++static struct attribute *ast2700n0_uart_routing_attrs[] = { ++ &ast2700n0_uart9_sel.dev_attr.attr, ++ &ast2700n0_io9_sel.dev_attr.attr, ++ &ast2700n0_uart3_sel.dev_attr.attr, ++ &ast2700n0_uart2_sel.dev_attr.attr, ++ &ast2700n0_uart1_sel.dev_attr.attr, ++ &ast2700n0_uart0_sel.dev_attr.attr, ++ &ast2700n0_io3_sel.dev_attr.attr, ++ &ast2700n0_io2_sel.dev_attr.attr, ++ &ast2700n0_io1_sel.dev_attr.attr, ++ &ast2700n0_io0_sel.dev_attr.attr, ++ NULL, ++}; + -+ scu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "aspeed,scu"); -+ if (IS_ERR(scu)) { -+ dev_err(&pdev->dev, "cannot to find SCU regmap\n"); -+ return -ENODEV; -+ } ++static const struct attribute_group ast2700n0_uart_routing_attr_group = { ++ .attrs = ast2700n0_uart_routing_attrs, ++}; + -+ if (of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2600-uphyb")) { -+ /* Check SCU040[3] USB port B controller reset is deassert */ -+ regmap_read(scu, 0x40, &val); -+ if ((val & BIT(3))) -+ return -EPROBE_DEFER; -+ } ++/* routing selector for AST27xx node 1 */ ++static struct aspeed_uart_routing_selector ast2700n1_uart10_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART10), ++ .reg = HICR9, ++ .shift = 12, ++ .mask = 0xf, ++ .options = { ++ UART_ROUTING_IO10, ++ UART_ROUTING_IO5, ++ UART_ROUTING_IO6, ++ UART_ROUTING_IO7, ++ UART_ROUTING_IO8, ++ UART_ROUTING_RES, ++ UART_ROUTING_UART5, ++ UART_ROUTING_UART6, ++ UART_ROUTING_UART7, ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART12, ++ NULL, ++ }, ++}; + -+ if (of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2700-uphy2a")) { -+ /* Check SCU220[0] USB vHubA1 controller reset is deassert */ -+ regmap_read(scu, 0x220, &val); -+ if ((val & BIT(0))) -+ return -EPROBE_DEFER; -+ } ++static struct aspeed_uart_routing_selector ast2700n1_io10_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO10), ++ .reg = HICR9, ++ .shift = 8, ++ .mask = 0xf, ++ .options = { ++ UART_ROUTING_UART5, ++ UART_ROUTING_UART6, ++ UART_ROUTING_UART7, ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART12, ++ UART_ROUTING_IO5, ++ UART_ROUTING_IO6, ++ UART_ROUTING_IO7, ++ UART_ROUTING_IO8, ++ UART_ROUTING_RES, ++ UART_ROUTING_UART10, ++ NULL, ++ }, ++}; + -+ if (of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2700-uphy2b")) { -+ /* Check SCU220[3] USB vHubB1 controller reset is deassert */ -+ regmap_read(scu, 0x220, &val); -+ if ((val & BIT(3))) -+ return -EPROBE_DEFER; -+ } ++static struct aspeed_uart_routing_selector ast2700n1_uart8_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART8), ++ .reg = HICRA, ++ .shift = 25, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO8, ++ UART_ROUTING_IO5, ++ UART_ROUTING_IO6, ++ UART_ROUTING_IO7, ++ UART_ROUTING_UART5, ++ UART_ROUTING_UART6, ++ UART_ROUTING_UART7, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+ ctrl_data = devm_kzalloc(&pdev->dev, -+ sizeof(struct usb_phy_ctrl) * ctrl_num, -+ GFP_KERNEL); -+ if (!ctrl_data) -+ return -ENOMEM; ++static struct aspeed_uart_routing_selector ast2700n1_uart7_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART7), ++ .reg = HICRA, ++ .shift = 22, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO7, ++ UART_ROUTING_IO8, ++ UART_ROUTING_IO5, ++ UART_ROUTING_IO6, ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART5, ++ UART_ROUTING_UART6, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+ base = of_iomap(node, 0); ++static struct aspeed_uart_routing_selector ast2700n1_uart6_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART6), ++ .reg = HICRA, ++ .shift = 19, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO6, ++ UART_ROUTING_IO7, ++ UART_ROUTING_IO8, ++ UART_ROUTING_IO5, ++ UART_ROUTING_UART7, ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART5, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+ ret = of_property_read_u32_array(node, "ctrl", (u32 *)ctrl_data, -+ ctrl_num * 2); -+ if (ret < 0) { -+ dev_err(&pdev->dev, "Could not read ctrl property\n"); -+ return -EINVAL; -+ } ++static struct aspeed_uart_routing_selector ast2700n1_uart5_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART5), ++ .reg = HICRA, ++ .shift = 16, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO5, ++ UART_ROUTING_IO6, ++ UART_ROUTING_IO7, ++ UART_ROUTING_IO8, ++ UART_ROUTING_UART6, ++ UART_ROUTING_UART7, ++ UART_ROUTING_UART8, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+ for (i = 0; i < ctrl_num; i++) -+ writel(ctrl_data[i].value, base + ctrl_data[i].offset); ++static struct aspeed_uart_routing_selector ast2700n1_io8_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO8), ++ .reg = HICRA, ++ .shift = 9, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART10, ++ UART_ROUTING_UART5, ++ UART_ROUTING_UART6, ++ UART_ROUTING_UART7, ++ UART_ROUTING_IO5, ++ UART_ROUTING_IO6, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+ dev_info(&pdev->dev, "Initialized USB PHY\n"); ++static struct aspeed_uart_routing_selector ast2700n1_io7_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO7), ++ .reg = HICRA, ++ .shift = 6, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART7, ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART10, ++ UART_ROUTING_UART5, ++ UART_ROUTING_UART6, ++ UART_ROUTING_IO5, ++ UART_ROUTING_IO6, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+ return 0; -+} ++static struct aspeed_uart_routing_selector ast2700n1_io6_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO6), ++ .reg = HICRA, ++ .shift = 3, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART6, ++ UART_ROUTING_UART7, ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART10, ++ UART_ROUTING_UART5, ++ UART_ROUTING_IO7, ++ UART_ROUTING_IO8, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+static void aspeed_usb_phy_remove(struct platform_device *pdev) -+{ -+} ++static struct aspeed_uart_routing_selector ast2700n1_io5_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO5), ++ .reg = HICRA, ++ .shift = 0, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART5, ++ UART_ROUTING_UART6, ++ UART_ROUTING_UART7, ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART10, ++ UART_ROUTING_IO7, ++ UART_ROUTING_IO8, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+static struct platform_driver aspeed_usb_phy_driver = { -+ .probe = aspeed_usb_phy_probe, -+ .remove = aspeed_usb_phy_remove, -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .of_match_table = aspeed_usb_phy_dt_ids, -+ }, ++static struct attribute *ast2700n1_uart_routing_attrs[] = { ++ &ast2700n1_uart10_sel.dev_attr.attr, ++ &ast2700n1_io10_sel.dev_attr.attr, ++ &ast2700n1_uart8_sel.dev_attr.attr, ++ &ast2700n1_uart7_sel.dev_attr.attr, ++ &ast2700n1_uart6_sel.dev_attr.attr, ++ &ast2700n1_uart5_sel.dev_attr.attr, ++ &ast2700n1_io8_sel.dev_attr.attr, ++ &ast2700n1_io7_sel.dev_attr.attr, ++ &ast2700n1_io6_sel.dev_attr.attr, ++ &ast2700n1_io5_sel.dev_attr.attr, ++ NULL, +}; -+module_platform_driver(aspeed_usb_phy_driver); + -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Neal Liu "); -diff --git a/drivers/soc/aspeed/aspeed-xdma.c b/drivers/soc/aspeed/aspeed-xdma.c ---- a/drivers/soc/aspeed/aspeed-xdma.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-xdma.c 2025-12-23 10:16:21.125032653 +0000 -@@ -0,0 +1,1438 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+// Copyright IBM Corp 2019 ++static const struct attribute_group ast2700n1_uart_routing_attr_group = { ++ .attrs = ast2700n1_uart_routing_attrs, ++}; + -+#include + static ssize_t aspeed_uart_routing_show(struct device *dev, + struct device_attribute *attr, + char *buf) +@@ -580,6 +1000,10 @@ + .data = &ast2500_uart_routing_attr_group }, + { .compatible = "aspeed,ast2600-uart-routing", + .data = &ast2600_uart_routing_attr_group }, ++ { .compatible = "aspeed,ast2700n0-uart-routing", ++ .data = &ast2700n0_uart_routing_attr_group }, ++ { .compatible = "aspeed,ast2700n1-uart-routing", ++ .data = &ast2700n1_uart_routing_attr_group }, + { }, + }; + +@@ -589,7 +1013,7 @@ + .of_match_table = aspeed_uart_routing_table, + }, + .probe = aspeed_uart_routing_probe, +- .remove_new = aspeed_uart_routing_remove, ++ .remove = aspeed_uart_routing_remove, + }; + + module_platform_driver(aspeed_uart_routing_driver); +diff --git a/drivers/soc/aspeed/aspeed-udma.c b/drivers/soc/aspeed/aspeed-udma.c +--- a/drivers/soc/aspeed/aspeed-udma.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-udma.c 2026-04-08 18:03:48.311705302 +0000 +@@ -0,0 +1,424 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright 2020 Aspeed Technology Inc. ++ */ +#include -+#include +#include -+#include +#include -+#include -+#include +#include +#include -+#include -+#include -+#include +#include -+#include ++#include +#include -+#include +#include -+#include -+#include -+#include -+#include ++#include ++#include +#include -+#include -+#include -+#include -+#include -+ -+#define DEVICE_NAME "aspeed-xdma" -+ -+#define SCU_AST2600_MISC_CTRL 0x0c0 -+#define SCU_AST2600_MISC_CTRL_XDMA_BMC BIT(8) -+#define SCU_AST2700_MISC_CTRL_XDMA_CLIENT BIT(4) -+ -+#define SCU_AST2600_DEBUG_CTRL 0x0c8 -+#define DEBUG_CTRL_AST2600_XDMA_DISABLE BIT(2) -+#define DEBUG_CTRL_AST2700_XDMA_DISABLE BIT(8) -+ -+#define SCU_AST2500_PCIE_CONF 0x180 -+#define SCU_AST2600_PCIE_CONF 0xc20 -+#define SCU_AST2700_PCIE0_CONF 0x970 -+#define SCU_AST2700_PCIE1_CONF 0x9B0 -+#define SCU_PCIE_CONF_VGA_EN BIT(0) -+#define SCU_PCIE_CONF_VGA_EN_MMIO BIT(1) -+#define SCU_PCIE_CONF_VGA_EN_LPC BIT(2) -+#define SCU_PCIE_CONF_VGA_EN_MSI BIT(3) -+#define SCU_PCIE_CONF_VGA_EN_MCTP BIT(4) -+#define SCU_PCIE_CONF_VGA_EN_IRQ BIT(5) -+#define SCU_PCIE_CONF_VGA_EN_DMA BIT(6) -+#define SCU_PCIE_CONF_BMC_EN BIT(8) -+#define SCU_PCIE_CONF_BMC_EN_MMIO BIT(9) -+#define SCU_PCIE_CONF_BMC_EN_MSI BIT(11) -+#define SCU_PCIE_CONF_BMC_EN_MCTP BIT(12) -+#define SCU_PCIE_CONF_BMC_EN_IRQ BIT(13) -+#define SCU_PCIE_CONF_BMC_EN_DMA BIT(14) -+ -+#define SCU_AST2700_PCIE0_CTRL 0xa60 -+#define SCU_AST2700_PCIE1_CTRL 0xae0 -+#define SCU_AST2700_PCIE_CTRL_DMA_EN BIT(2) -+ -+#define SCU_AST2500_BMC_CLASS_REV 0x19c -+#define SCU_AST2600_BMC_CLASS_REV 0xc68 -+#define SCU_AST2700_PCIE0_BMC_CLASS_REV 0xa18 -+#define SCU_AST2700_PCIE1_BMC_CLASS_REV 0xa98 -+#define SCU_BMC_CLASS_REV_XDMA 0xff000001 -+#define SCU_BMC_CLASS_REV_MASK 0xffffff00 + -+#define XDMA_CMDQ_SIZE PAGE_SIZE -+#define XDMA_NUM_CMDS \ -+ (XDMA_CMDQ_SIZE / sizeof(struct aspeed_xdma_cmd)) ++#define DEVICE_NAME "aspeed-udma" + -+/* Aspeed specification requires 100us after disabling the reset */ -+#define XDMA_ENGINE_SETUP_TIME_MAX_US 1000 -+#define XDMA_ENGINE_SETUP_TIME_MIN_US 100 ++/* UART DMA registers offset */ ++#define UDMA_TX_DMA_EN 0x000 ++#define UDMA_RX_DMA_EN 0x004 ++#define UDMA_MISC 0x008 ++#define UDMA_MISC_RX_BUFSZ GENMASK(3, 2) ++#define UDMA_MISC_TX_BUFSZ GENMASK(1, 0) ++#define UDMA_TMOUT_TIMER 0x00c ++#define UDMA_TX_DMA_RST 0x020 ++#define UDMA_RX_DMA_RST 0x024 ++#define UDMA_TX_DMA_INT_EN 0x030 ++#define UDMA_TX_DMA_INT_STS 0x034 ++#define UDMA_RX_DMA_INT_EN 0x038 ++#define UDMA_RX_DMA_INT_STS 0x03c + -+#define XDMA_CMD_AST2500_PITCH_SHIFT 3 -+#define XDMA_CMD_AST2500_PITCH_BMC GENMASK_ULL(62, 51) -+#define XDMA_CMD_AST2500_PITCH_HOST GENMASK_ULL(46, 35) -+#define XDMA_CMD_AST2500_PITCH_UPSTREAM BIT_ULL(31) -+#define XDMA_CMD_AST2500_PITCH_ADDR GENMASK_ULL(29, 4) -+#define XDMA_CMD_AST2500_PITCH_ID BIT_ULL(0) -+#define XDMA_CMD_AST2500_CMD_IRQ_EN BIT_ULL(31) -+#define XDMA_CMD_AST2500_CMD_LINE_NO GENMASK_ULL(27, 16) -+#define XDMA_CMD_AST2500_CMD_IRQ_BMC BIT_ULL(15) -+#define XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT 4 -+#define XDMA_CMD_AST2500_CMD_LINE_SIZE \ -+ GENMASK_ULL(14, XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT) -+#define XDMA_CMD_AST2500_CMD_ID BIT_ULL(1) ++#define UDMA_CHX_OFF(x) ((x) * 0x20) ++#define UDMA_CHX_TX_RD_PTR(x) (0x040 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_TX_WR_PTR(x) (0x044 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_TX_BUF_ADDR(x) (0x048 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_TX_CTRL(x) (0x04c + UDMA_CHX_OFF(x)) ++#define UDMA_TX_CTRL_BUF_ADDRH GENMASK(10, 8) ++#define UDMA_TX_CTRL_TMOUT_DIS BIT(4) ++#define UDMA_TX_CTRL_BUFSZ GENMASK(3, 0) ++#define UDMA_CHX_RX_RD_PTR(x) (0x050 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_RX_WR_PTR(x) (0x054 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_RX_BUF_ADDR(x) (0x058 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_RX_CTRL(x) (0x05c + UDMA_CHX_OFF(x)) ++#define UDMA_RX_CTRL_BUF_ADDRH GENMASK(10, 8) ++#define UDMA_RX_CTRL_TMOUT_DIS BIT(4) ++#define UDMA_RX_CTRL_BUFSZ GENMASK(1, 0) + -+#define XDMA_CMD_AST2600_PITCH_BMC GENMASK_ULL(62, 48) -+#define XDMA_CMD_AST2600_PITCH_HOST GENMASK_ULL(46, 32) -+#define XDMA_CMD_AST2600_PITCH_ADDR GENMASK_ULL(30, 0) -+#define XDMA_CMD_AST2600_CMD_64_EN BIT_ULL(40) -+#define XDMA_CMD_AST2600_CMD_IRQ_BMC BIT_ULL(37) -+#define XDMA_CMD_AST2600_CMD_IRQ_HOST BIT_ULL(36) -+#define XDMA_CMD_AST2600_CMD_UPSTREAM BIT_ULL(32) -+#define XDMA_CMD_AST2600_CMD_LINE_NO GENMASK_ULL(27, 16) -+#define XDMA_CMD_AST2600_CMD_LINE_SIZE GENMASK_ULL(14, 0) -+#define XDMA_CMD_AST2600_CMD_MULTILINE_SIZE GENMASK_ULL(14, 12) ++#define UDMA_MAX_CHANNEL 16 ++#define UDMA_TMOUT 0x200 + -+#define XDMA_CMD_AST2700_PITCH_BMC GENMASK_ULL(62, 48) -+#define XDMA_CMD_AST2700_PITCH_HOST GENMASK_ULL(46, 32) -+#define XDMA_CMD_AST2700_CMD_64_EN BIT_ULL(40) -+#define XDMA_CMD_AST2700_CMD_IRQ_BMC BIT_ULL(37) -+#define XDMA_CMD_AST2700_CMD_UPSTREAM BIT_ULL(32) -+#define XDMA_CMD_AST2700_CMD_LINE_NO GENMASK_ULL(27, 16) -+#define XDMA_CMD_AST2700_CMD_LINE_SIZE GENMASK_ULL(14, 0) -+#define XDMA_CMD_AST2700_CMD_MULTILINE_SIZE GENMASK_ULL(14, 12) -+#define XDMA_CMD_AST2700_BMC_ADDR GENMASK_ULL(34, 0) ++enum aspeed_udma_bufsz_code { ++ UDMA_BUFSZ_CODE_1KB, ++ UDMA_BUFSZ_CODE_4KB, ++ UDMA_BUFSZ_CODE_16KB, ++ UDMA_BUFSZ_CODE_64KB, ++}; + -+#define XDMA_AST2500_QUEUE_ENTRY_SIZE 4 -+#define XDMA_AST2500_HOST_CMDQ_ADDR0 0x00 -+#define XDMA_AST2500_HOST_CMDQ_ENDP 0x04 -+#define XDMA_AST2500_HOST_CMDQ_WRITEP 0x08 -+#define XDMA_AST2500_HOST_CMDQ_READP 0x0c -+#define XDMA_AST2500_BMC_CMDQ_ADDR 0x10 -+#define XDMA_AST2500_BMC_CMDQ_ENDP 0x14 -+#define XDMA_AST2500_BMC_CMDQ_WRITEP 0x18 -+#define XDMA_AST2500_BMC_CMDQ_READP 0x1c -+#define XDMA_BMC_CMDQ_READP_RESET 0xee882266 -+#define XDMA_AST2500_CTRL 0x20 -+#define XDMA_AST2500_CTRL_US_COMP BIT(4) -+#define XDMA_AST2500_CTRL_DS_COMP BIT(5) -+#define XDMA_AST2500_CTRL_DS_DIRTY BIT(6) -+#define XDMA_AST2500_CTRL_DS_SIZE_256 BIT(17) -+#define XDMA_AST2500_CTRL_DS_TIMEOUT BIT(28) -+#define XDMA_AST2500_CTRL_DS_CHECK_ID BIT(29) -+#define XDMA_AST2500_STATUS 0x24 -+#define XDMA_AST2500_STATUS_US_COMP BIT(4) -+#define XDMA_AST2500_STATUS_DS_COMP BIT(5) -+#define XDMA_AST2500_STATUS_DS_DIRTY BIT(6) -+#define XDMA_AST2500_INPRG_DS_CMD1 0x38 -+#define XDMA_AST2500_INPRG_DS_CMD2 0x3c -+#define XDMA_AST2500_INPRG_US_CMD00 0x40 -+#define XDMA_AST2500_INPRG_US_CMD01 0x44 -+#define XDMA_AST2500_INPRG_US_CMD10 0x48 -+#define XDMA_AST2500_INPRG_US_CMD11 0x4c -+#define XDMA_AST2500_INPRG_US_CMD20 0x50 -+#define XDMA_AST2500_INPRG_US_CMD21 0x54 -+#define XDMA_AST2500_HOST_CMDQ_ADDR1 0x60 -+#define XDMA_AST2500_VGA_CMDQ_ADDR0 0x64 -+#define XDMA_AST2500_VGA_CMDQ_ENDP 0x68 -+#define XDMA_AST2500_VGA_CMDQ_WRITEP 0x6c -+#define XDMA_AST2500_VGA_CMDQ_READP 0x70 -+#define XDMA_AST2500_VGA_CMD_STATUS 0x74 -+#define XDMA_AST2500_VGA_CMDQ_ADDR1 0x78 ++struct aspeed_udma_chan { ++ dma_addr_t dma_addr; + -+#define XDMA_AST2600_QUEUE_ENTRY_SIZE 2 -+#define XDMA_AST2600_HOST_CMDQ_ADDR0 0x00 -+#define XDMA_AST2600_HOST_CMDQ_ADDR1 0x04 -+#define XDMA_AST2600_HOST_CMDQ_ENDP 0x08 -+#define XDMA_AST2600_HOST_CMDQ_WRITEP 0x0c -+#define XDMA_AST2600_HOST_CMDQ_READP 0x10 -+#define XDMA_AST2600_BMC_CMDQ_ADDR 0x14 -+#define XDMA_AST2600_BMC_CMDQ_ENDP 0x18 -+#define XDMA_AST2600_BMC_CMDQ_WRITEP 0x1c -+#define XDMA_AST2600_BMC_CMDQ_READP 0x20 -+#define XDMA_AST2600_VGA_CMDQ_ADDR0 0x24 -+#define XDMA_AST2600_VGA_CMDQ_ADDR1 0x28 -+#define XDMA_AST2600_VGA_CMDQ_ENDP 0x2c -+#define XDMA_AST2600_VGA_CMDQ_WRITEP 0x30 -+#define XDMA_AST2600_VGA_CMDQ_READP 0x34 -+#define XDMA_AST2600_CTRL 0x38 -+#define XDMA_AST2600_CTRL_US_COMP BIT(16) -+#define XDMA_AST2600_CTRL_DS_COMP BIT(17) -+#define XDMA_AST2600_CTRL_DS_DIRTY BIT(18) -+#define XDMA_AST2600_CTRL_DS_SIZE_256 BIT(20) -+#define XDMA_AST2600_STATUS 0x3c -+#define XDMA_AST2600_STATUS_US_COMP BIT(16) -+#define XDMA_AST2600_STATUS_DS_COMP BIT(17) -+#define XDMA_AST2600_STATUS_DS_DIRTY BIT(18) -+#define XDMA_AST2600_INPRG_DS_CMD00 0x40 -+#define XDMA_AST2600_INPRG_DS_CMD01 0x44 -+#define XDMA_AST2600_INPRG_DS_CMD10 0x48 -+#define XDMA_AST2600_INPRG_DS_CMD11 0x4c -+#define XDMA_AST2600_INPRG_DS_CMD20 0x50 -+#define XDMA_AST2600_INPRG_DS_CMD21 0x54 -+#define XDMA_AST2600_INPRG_US_CMD00 0x60 -+#define XDMA_AST2600_INPRG_US_CMD01 0x64 -+#define XDMA_AST2600_INPRG_US_CMD10 0x68 -+#define XDMA_AST2600_INPRG_US_CMD11 0x6c -+#define XDMA_AST2600_INPRG_US_CMD20 0x70 -+#define XDMA_AST2600_INPRG_US_CMD21 0x74 ++ u32 rb_sz; + -+#define XDMA_AST2700_QUEUE_ENTRY_SIZE 2 -+#define XDMA_AST2700_BMC_CMDQ_ADDR0 0x10 -+#define XDMA_AST2700_BMC_CMDQ_ADDR1 0x14 -+#define XDMA_AST2700_BMC_CMDQ_ENDP 0x18 -+#define XDMA_AST2700_BMC_CMDQ_WRITEP 0x1c -+#define XDMA_AST2700_BMC_CMDQ_READP 0x20 -+#define XDMA_AST2700_CTRL 0x38 -+#define XDMA_AST2700_CTRL_US_COMP BIT(16) -+#define XDMA_AST2700_CTRL_DS_COMP BIT(17) -+#define XDMA_AST2700_CTRL_DS_DIRTY BIT(18) -+#define XDMA_AST2700_CTRL_IDLE BIT(19) -+#define XDMA_AST2700_CTRL_DS_SIZE_256 BIT(20) -+#define XDMA_AST2700_STATUS 0x3c -+#define XDMA_AST2700_STATUS_US_COMP BIT(16) -+#define XDMA_AST2700_STATUS_DS_COMP BIT(17) -+#define XDMA_AST2700_STATUS_DS_DIRTY BIT(18) -+#define XDMA_AST2700_STATUS_IDLE BIT(19) -+#define XDMA_AST2700_INPRG_DS_CMD00 0x40 -+#define XDMA_AST2700_INPRG_DS_CMD01 0x44 -+#define XDMA_AST2700_INPRG_DS_CMD10 0x48 -+#define XDMA_AST2700_INPRG_DS_CMD11 0x4c -+#define XDMA_AST2700_INPRG_DS_CMD20 0x50 -+#define XDMA_AST2700_INPRG_DS_CMD21 0x54 -+#define XDMA_AST2700_INPRG_US_CMD00 0x60 -+#define XDMA_AST2700_INPRG_US_CMD01 0x64 -+#define XDMA_AST2700_INPRG_US_CMD10 0x68 -+#define XDMA_AST2700_INPRG_US_CMD11 0x6c -+#define XDMA_AST2700_INPRG_US_CMD20 0x70 -+#define XDMA_AST2700_INPRG_US_CMD21 0x74 ++ aspeed_udma_cb_t cb; ++ void *cb_arg; + -+struct aspeed_xdma_cmd { -+ u64 host_addr; -+ u64 pitch; -+ u64 cmd; -+ u64 reserved; ++ bool dis_tmout; +}; + -+struct aspeed_xdma_regs { -+ u8 bmc_cmdq_addr; -+ u8 bmc_cmdq_addr_ext; -+ u8 bmc_cmdq_endp; -+ u8 bmc_cmdq_writep; -+ u8 bmc_cmdq_readp; -+ u8 control; -+ u8 status; ++struct aspeed_udma { ++ struct device *dev; ++ u8 __iomem *regs; ++ int irq; ++ struct aspeed_udma_chan tx_chs[UDMA_MAX_CHANNEL]; ++ struct aspeed_udma_chan rx_chs[UDMA_MAX_CHANNEL]; ++ spinlock_t lock; +}; + -+struct aspeed_xdma_status_bits { -+ u32 us_comp; -+ u32 ds_comp; -+ u32 ds_dirty; -+}; ++struct aspeed_udma udma[1]; + -+struct aspeed_xdma; ++static int aspeed_udma_get_bufsz_code(u32 buf_sz) ++{ ++ switch (buf_sz) { ++ case SZ_1K: ++ return UDMA_BUFSZ_CODE_1KB; ++ case SZ_4K: ++ return UDMA_BUFSZ_CODE_4KB; ++ case SZ_16K: ++ return UDMA_BUFSZ_CODE_16KB; ++ case SZ_64K: ++ return UDMA_BUFSZ_CODE_64KB; ++ default: ++ break; ++ } + -+struct aspeed_xdma_chip { -+ u32 control; -+ u32 scu_bmc_class; -+ u32 scu_misc_ctrl; -+ u32 scu_misc_mask; -+ u32 scu_disable_mask; -+ u32 scu_pcie_conf; -+ u32 scu_pcie_ctrl; -+ unsigned int queue_entry_size; -+ struct aspeed_xdma_regs regs; -+ struct aspeed_xdma_status_bits status_bits; -+ unsigned int (*set_cmd)(struct aspeed_xdma *ctx, -+ struct aspeed_xdma_cmd cmds[2], -+ struct aspeed_xdma_op *op, u64 bmc_addr); -+}; ++ return -1; ++} + -+struct aspeed_xdma_client; ++static u32 aspeed_udma_get_tx_rptr(u32 ch_no) ++{ ++ return readl(udma->regs + UDMA_CHX_TX_RD_PTR(ch_no)); ++} + -+struct aspeed_xdma { -+ struct kobject kobj; -+ const struct aspeed_xdma_chip *chip; ++static u32 aspeed_udma_get_rx_wptr(u32 ch_no) ++{ ++ return readl(udma->regs + UDMA_CHX_RX_WR_PTR(ch_no)); ++} + -+ int irq; -+ int pcie_irq; -+ struct clk *clock; -+ struct device *dev; -+ void __iomem *base; -+ resource_size_t res_size; -+ resource_size_t res_start; -+ struct reset_control *reset; -+ struct reset_control *reset_rc; ++static void aspeed_udma_set_ptr(u32 ch_no, u32 ptr, bool is_tx) ++{ ++ writel(ptr, udma->regs + ++ ((is_tx) ? UDMA_CHX_TX_WR_PTR(ch_no) : UDMA_CHX_RX_RD_PTR(ch_no))); ++} + -+ /* Protects current_client */ -+ spinlock_t client_lock; -+ struct aspeed_xdma_client *current_client; ++void aspeed_udma_set_tx_wptr(u32 ch_no, u32 wptr) ++{ ++ aspeed_udma_set_ptr(ch_no, wptr, true); ++} ++EXPORT_SYMBOL(aspeed_udma_set_tx_wptr); + -+ /* Protects engine configuration */ -+ spinlock_t engine_lock; -+ struct aspeed_xdma_cmd *cmdq; -+ unsigned int cmd_idx; -+ bool in_reset; -+ bool upstream; ++void aspeed_udma_set_rx_rptr(u32 ch_no, u32 rptr) ++{ ++ aspeed_udma_set_ptr(ch_no, rptr, false); ++} ++EXPORT_SYMBOL(aspeed_udma_set_rx_rptr); + -+ /* Queue waiters for idle engine */ -+ wait_queue_head_t wait; ++static int aspeed_udma_free_chan(u32 ch_no, bool is_tx) ++{ ++ u32 reg; ++ unsigned long flags; + -+ struct work_struct reset_work; ++ if (ch_no > UDMA_MAX_CHANNEL) ++ return -EINVAL; + -+ phys_addr_t mem_phys; -+ phys_addr_t mem_size; -+ void *mem_virt; -+ dma_addr_t mem_coherent; -+ dma_addr_t cmdq_phys; -+ struct gen_pool *pool; ++ spin_lock_irqsave(&udma->lock, flags); + -+ struct miscdevice misc; -+}; ++ reg = readl(udma->regs + ++ ((is_tx) ? UDMA_TX_DMA_INT_EN : UDMA_RX_DMA_INT_EN)); ++ reg &= ~(0x1 << ch_no); + -+struct aspeed_xdma_client { -+ struct aspeed_xdma *ctx; ++ writel(reg, udma->regs + ++ ((is_tx) ? UDMA_TX_DMA_INT_EN : UDMA_RX_DMA_INT_EN)); + -+ bool error; -+ bool in_progress; -+ void *virt; -+ dma_addr_t phys; -+ u32 size; -+}; ++ spin_unlock_irqrestore(&udma->lock, flags); + -+#define CREATE_TRACE_POINTS -+#include ++ return 0; ++} + -+static u32 aspeed_xdma_readl(struct aspeed_xdma *ctx, u8 reg) ++int aspeed_udma_free_tx_chan(u32 ch_no) +{ -+ u32 v = readl(ctx->base + reg); -+ -+ dev_dbg(ctx->dev, "read %02x[%08x]\n", reg, v); -+ return v; ++ return aspeed_udma_free_chan(ch_no, true); +} ++EXPORT_SYMBOL(aspeed_udma_free_tx_chan); + -+static void aspeed_xdma_writel(struct aspeed_xdma *ctx, u8 reg, u32 val) ++int aspeed_udma_free_rx_chan(u32 ch_no) +{ -+ writel(val, ctx->base + reg); -+ dev_dbg(ctx->dev, "write %02x[%08x]\n", reg, val); ++ return aspeed_udma_free_chan(ch_no, false); +} ++EXPORT_SYMBOL(aspeed_udma_free_rx_chan); + -+static void aspeed_xdma_init_eng(struct aspeed_xdma *ctx) ++static int aspeed_udma_request_chan(u32 ch_no, dma_addr_t addr, u32 rb_sz, ++ aspeed_udma_cb_t cb, void *id, ++ bool dis_tmout, bool is_tx) +{ ++ int retval = 0; ++ int rbsz_code; ++ ++ u32 reg; + unsigned long flags; ++ struct aspeed_udma_chan *ch; + -+ spin_lock_irqsave(&ctx->engine_lock, flags); -+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_endp, -+ ctx->chip->queue_entry_size * XDMA_NUM_CMDS); -+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_readp, -+ XDMA_BMC_CMDQ_READP_RESET); -+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_writep, 0); -+ aspeed_xdma_writel(ctx, ctx->chip->regs.control, ctx->chip->control); -+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_addr, ctx->cmdq_phys); -+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT -+ if (ctx->chip->regs.bmc_cmdq_addr_ext) -+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_addr_ext, ctx->cmdq_phys >> 32); -+#endif ++ if (ch_no > UDMA_MAX_CHANNEL) { ++ retval = -EINVAL; ++ goto out; ++ } + -+ ctx->cmd_idx = 0; -+ spin_unlock_irqrestore(&ctx->engine_lock, flags); ++ rbsz_code = aspeed_udma_get_bufsz_code(rb_sz); ++ if (rbsz_code < 0) { ++ retval = -EINVAL; ++ goto out; ++ } ++ ++ spin_lock_irqsave(&udma->lock, flags); ++ ++ if (is_tx) { ++ reg = readl(udma->regs + UDMA_TX_DMA_INT_EN); ++ if (reg & (0x1 << ch_no)) { ++ retval = -EBUSY; ++ goto unlock_n_out; ++ } ++ ++ reg |= (0x1 << ch_no); ++ writel(reg, udma->regs + UDMA_TX_DMA_INT_EN); ++ ++ reg = FIELD_PREP(UDMA_TX_CTRL_BUF_ADDRH, (u64)addr >> 32) | ++ ((dis_tmout) ? UDMA_TX_CTRL_TMOUT_DIS : 0) | ++ FIELD_PREP(UDMA_TX_CTRL_BUFSZ, rbsz_code); ++ writel(reg, udma->regs + UDMA_CHX_TX_CTRL(ch_no)); ++ ++ writel(addr, udma->regs + UDMA_CHX_TX_BUF_ADDR(ch_no)); ++ } else { ++ reg = readl(udma->regs + UDMA_RX_DMA_INT_EN); ++ if (reg & (0x1 << ch_no)) { ++ retval = -EBUSY; ++ goto unlock_n_out; ++ } ++ ++ reg |= (0x1 << ch_no); ++ writel(reg, udma->regs + UDMA_RX_DMA_INT_EN); ++ ++ reg = FIELD_PREP(UDMA_RX_CTRL_BUF_ADDRH, (u64)addr >> 32) | ++ ((dis_tmout) ? UDMA_RX_CTRL_TMOUT_DIS : 0) | ++ FIELD_PREP(UDMA_RX_CTRL_BUFSZ, rbsz_code); ++ writel(reg, udma->regs + UDMA_CHX_RX_CTRL(ch_no)); ++ ++ writel(addr, udma->regs + UDMA_CHX_RX_BUF_ADDR(ch_no)); ++ } ++ ++ ch = (is_tx) ? &udma->tx_chs[ch_no] : &udma->rx_chs[ch_no]; ++ ch->rb_sz = rb_sz; ++ ch->cb = cb; ++ ch->cb_arg = id; ++ ch->dma_addr = addr; ++ ch->dis_tmout = dis_tmout; ++ ++unlock_n_out: ++ spin_unlock_irqrestore(&udma->lock, flags); ++out: ++ return 0; ++} ++ ++int aspeed_udma_request_tx_chan(u32 ch_no, dma_addr_t addr, u32 rb_sz, ++ aspeed_udma_cb_t cb, void *id, bool dis_tmout) ++{ ++ return aspeed_udma_request_chan(ch_no, addr, rb_sz, cb, id, ++ dis_tmout, true); +} ++EXPORT_SYMBOL(aspeed_udma_request_tx_chan); + -+static unsigned int aspeed_xdma_ast2500_set_cmd(struct aspeed_xdma *ctx, -+ struct aspeed_xdma_cmd cmds[2], -+ struct aspeed_xdma_op *op, -+ u64 bmc_addr) ++int aspeed_udma_request_rx_chan(u32 ch_no, dma_addr_t addr, u32 rb_sz, ++ aspeed_udma_cb_t cb, void *id, bool dis_tmout) +{ -+ unsigned int rc = 1; -+ unsigned int pitch = 1; -+ unsigned int line_no = 1; -+ unsigned int line_size = op->len >> -+ XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT; -+ u64 cmd = XDMA_CMD_AST2500_CMD_IRQ_EN | XDMA_CMD_AST2500_CMD_IRQ_BMC | -+ XDMA_CMD_AST2500_CMD_ID; -+ u64 cmd_pitch = (op->direction ? XDMA_CMD_AST2500_PITCH_UPSTREAM : 0) | -+ XDMA_CMD_AST2500_PITCH_ID; ++ return aspeed_udma_request_chan(ch_no, addr, rb_sz, cb, id, ++ dis_tmout, false); ++} ++EXPORT_SYMBOL(aspeed_udma_request_rx_chan); + -+ dev_dbg(ctx->dev, "xdma %s ast2500: bmc[%08llx] len[%08x] host[%08x]\n", -+ op->direction ? "upstream" : "downstream", bmc_addr, op->len, -+ (u32)op->host_addr); ++static void aspeed_udma_chan_ctrl(u32 ch_no, u32 op, bool is_tx) ++{ ++ unsigned long flags; ++ u32 reg_en, reg_rst; ++ u32 reg_en_off = (is_tx) ? UDMA_TX_DMA_EN : UDMA_RX_DMA_EN; ++ u32 reg_rst_off = (is_tx) ? UDMA_TX_DMA_RST : UDMA_TX_DMA_RST; + -+ if (op->len > XDMA_CMD_AST2500_CMD_LINE_SIZE) { -+ unsigned int rem; -+ unsigned int total; ++ if (ch_no > UDMA_MAX_CHANNEL) ++ return; + -+ line_no = op->len / XDMA_CMD_AST2500_CMD_LINE_SIZE; -+ total = XDMA_CMD_AST2500_CMD_LINE_SIZE * line_no; -+ rem = (op->len - total) >> -+ XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT; -+ line_size = XDMA_CMD_AST2500_CMD_LINE_SIZE; -+ pitch = line_size >> XDMA_CMD_AST2500_PITCH_SHIFT; -+ line_size >>= XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT; ++ spin_lock_irqsave(&udma->lock, flags); + -+ if (rem) { -+ u32 rbmc = bmc_addr + total; ++ reg_en = readl(udma->regs + reg_en_off); ++ reg_rst = readl(udma->regs + reg_rst_off); + -+ cmds[1].host_addr = op->host_addr + (u64)total; -+ cmds[1].pitch = cmd_pitch | -+ ((u64)rbmc & XDMA_CMD_AST2500_PITCH_ADDR) | -+ FIELD_PREP(XDMA_CMD_AST2500_PITCH_HOST, 1) | -+ FIELD_PREP(XDMA_CMD_AST2500_PITCH_BMC, 1); -+ cmds[1].cmd = cmd | -+ FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_NO, 1) | -+ FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_SIZE, -+ rem); -+ cmds[1].reserved = 0ULL; ++ switch (op) { ++ case ASPEED_UDMA_OP_ENABLE: ++ reg_en |= (0x1 << ch_no); ++ writel(reg_en, udma->regs + reg_en_off); ++ break; ++ case ASPEED_UDMA_OP_DISABLE: ++ reg_en &= ~(0x1 << ch_no); ++ writel(reg_en, udma->regs + reg_en_off); ++ break; ++ case ASPEED_UDMA_OP_RESET: ++ reg_en &= ~(0x1 << ch_no); ++ writel(reg_en, udma->regs + reg_en_off); + -+ print_hex_dump_debug("xdma rem ", DUMP_PREFIX_OFFSET, -+ 16, 1, &cmds[1], sizeof(*cmds), -+ true); ++ reg_rst |= (0x1 << ch_no); ++ writel(reg_rst, udma->regs + reg_rst_off); + -+ cmd &= ~(XDMA_CMD_AST2500_CMD_IRQ_EN | -+ XDMA_CMD_AST2500_CMD_IRQ_BMC); ++ udelay(100); + -+ rc++; -+ } ++ reg_rst &= ~(0x1 << ch_no); ++ writel(reg_rst, udma->regs + reg_rst_off); ++ break; ++ default: ++ break; + } + -+ cmds[0].host_addr = op->host_addr; -+ cmds[0].pitch = cmd_pitch | -+ (bmc_addr & XDMA_CMD_AST2500_PITCH_ADDR) | -+ FIELD_PREP(XDMA_CMD_AST2500_PITCH_HOST, pitch) | -+ FIELD_PREP(XDMA_CMD_AST2500_PITCH_BMC, pitch); -+ cmds[0].cmd = cmd | FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_NO, line_no) | -+ FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_SIZE, line_size); -+ cmds[0].reserved = 0ULL; -+ -+ print_hex_dump_debug("xdma cmd ", DUMP_PREFIX_OFFSET, 16, 1, cmds, -+ sizeof(*cmds), true); -+ -+ return rc; ++ spin_unlock_irqrestore(&udma->lock, flags); +} + -+static unsigned int aspeed_xdma_ast2600_set_cmd(struct aspeed_xdma *ctx, -+ struct aspeed_xdma_cmd cmds[2], -+ struct aspeed_xdma_op *op, -+ u64 bmc_addr) ++void aspeed_udma_tx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op) +{ -+ unsigned int rc = 1; -+ unsigned int pitch = 1; -+ unsigned int line_no = 1; -+ unsigned int line_size = op->len; -+ u64 cmd = XDMA_CMD_AST2600_CMD_IRQ_BMC | -+ (op->direction ? XDMA_CMD_AST2600_CMD_UPSTREAM : 0); ++ aspeed_udma_chan_ctrl(ch_no, op, true); ++} ++EXPORT_SYMBOL(aspeed_udma_tx_chan_ctrl); + -+ if (op->host_addr & 0xffffffff00000000ULL || -+ (op->host_addr + (u64)op->len) & 0xffffffff00000000ULL) -+ cmd |= XDMA_CMD_AST2600_CMD_64_EN; ++void aspeed_udma_rx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op) ++{ ++ aspeed_udma_chan_ctrl(ch_no, op, false); ++} ++EXPORT_SYMBOL(aspeed_udma_rx_chan_ctrl); + -+ dev_dbg(ctx->dev, "xdma %s ast2600: bmc[%08llx] len[%08x] host[%016llx]\n", -+ op->direction ? "upstream" : "downstream", -+ bmc_addr, op->len, op->host_addr); ++static irqreturn_t aspeed_udma_isr(int irq, void *arg) ++{ ++ u32 bit; ++ unsigned long tx_sts = readl(udma->regs + UDMA_TX_DMA_INT_STS); ++ unsigned long rx_sts = readl(udma->regs + UDMA_RX_DMA_INT_STS); + -+ if ((op->host_addr & 0xff) + op->len > XDMA_CMD_AST2600_CMD_LINE_SIZE) { -+ unsigned int rem; -+ unsigned int total; ++ if (udma != (struct aspeed_udma *)arg) ++ return IRQ_NONE; + -+ line_no = op->len / XDMA_CMD_AST2600_CMD_MULTILINE_SIZE; -+ total = XDMA_CMD_AST2600_CMD_MULTILINE_SIZE * line_no; -+ rem = op->len - total; -+ line_size = XDMA_CMD_AST2600_CMD_MULTILINE_SIZE; -+ pitch = line_size; ++ if (tx_sts == 0 && rx_sts == 0) ++ return IRQ_NONE; + -+ if (rem) { -+ u32 rbmc = bmc_addr + total; ++ for_each_set_bit(bit, &tx_sts, UDMA_MAX_CHANNEL) { ++ writel((0x1 << bit), udma->regs + UDMA_TX_DMA_INT_STS); ++ if (udma->tx_chs[bit].cb) ++ udma->tx_chs[bit].cb(aspeed_udma_get_tx_rptr(bit), ++ udma->tx_chs[bit].cb_arg); ++ } + -+ cmds[1].host_addr = op->host_addr + (u64)total; -+ cmds[1].pitch = -+ ((u64)rbmc & XDMA_CMD_AST2600_PITCH_ADDR) | -+ FIELD_PREP(XDMA_CMD_AST2600_PITCH_HOST, 1) | -+ FIELD_PREP(XDMA_CMD_AST2600_PITCH_BMC, 1); -+ cmds[1].cmd = cmd | -+ FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_NO, 1) | -+ FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_SIZE, -+ rem); -+ cmds[1].reserved = 0ULL; ++ for_each_set_bit(bit, &rx_sts, UDMA_MAX_CHANNEL) { ++ writel((0x1 << bit), udma->regs + UDMA_RX_DMA_INT_STS); ++ if (udma->rx_chs[bit].cb) ++ udma->rx_chs[bit].cb(aspeed_udma_get_rx_wptr(bit), ++ udma->rx_chs[bit].cb_arg); ++ } + -+ print_hex_dump_debug("xdma rem ", DUMP_PREFIX_OFFSET, -+ 16, 1, &cmds[1], sizeof(*cmds), -+ true); ++ return IRQ_HANDLED; ++} + -+ cmd &= ~XDMA_CMD_AST2600_CMD_IRQ_BMC; ++static int aspeed_udma_probe(struct platform_device *pdev) ++{ ++ int i, rc; ++ uint32_t reg; ++ struct resource *res; ++ struct device *dev = &pdev->dev; + -+ rc++; -+ } ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (IS_ERR_OR_NULL(res)) { ++ dev_err(dev, "failed to get register base\n"); ++ return -ENODEV; + } + -+ cmds[0].host_addr = op->host_addr; -+ cmds[0].pitch = (bmc_addr & XDMA_CMD_AST2600_PITCH_ADDR) | -+ FIELD_PREP(XDMA_CMD_AST2600_PITCH_HOST, pitch) | -+ FIELD_PREP(XDMA_CMD_AST2600_PITCH_BMC, pitch); -+ cmds[0].cmd = cmd | FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_NO, line_no) | -+ FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_SIZE, line_size); -+ cmds[0].reserved = 0ULL; -+ -+ print_hex_dump_debug("xdma cmd ", DUMP_PREFIX_OFFSET, 16, 1, cmds, -+ sizeof(*cmds), true); -+ -+ return rc; -+} ++ udma->regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR_OR_NULL(udma->regs)) { ++ dev_err(dev, "failed to map registers\n"); ++ return PTR_ERR(udma->regs); ++ } + -+static unsigned int aspeed_xdma_ast2700_set_cmd(struct aspeed_xdma *ctx, -+ struct aspeed_xdma_cmd cmds[2], -+ struct aspeed_xdma_op *op, -+ u64 bmc_addr) -+{ -+ unsigned int rc = 1; -+ unsigned int pitch = 1; -+ unsigned int line_no = 1; -+ unsigned int line_size = op->len; -+ u64 cmd = XDMA_CMD_AST2700_CMD_IRQ_BMC | -+ (op->direction ? XDMA_CMD_AST2700_CMD_UPSTREAM : 0); ++ /* disable for safety */ ++ writel(0x0, udma->regs + UDMA_TX_DMA_EN); ++ writel(0x0, udma->regs + UDMA_RX_DMA_EN); + -+ if (op->host_addr & 0xffffffff00000000ULL || -+ (op->host_addr + (u64)op->len) & 0xffffffff00000000ULL) -+ cmd |= XDMA_CMD_AST2700_CMD_64_EN; ++ udma->irq = platform_get_irq(pdev, 0); ++ if (udma->irq < 0) { ++ dev_err(dev, "failed to get IRQ number\n"); ++ return -ENODEV; ++ } + -+ dev_dbg(ctx->dev, "xdma %s ast2700: bmc[%016llx] len[%08x] host[%016llx]\n", -+ op->direction ? "upstream" : "downstream", -+ bmc_addr, op->len, op->host_addr); ++ rc = devm_request_irq(dev, udma->irq, aspeed_udma_isr, ++ IRQF_SHARED, DEVICE_NAME, udma); ++ if (rc) { ++ dev_err(dev, "failed to request IRQ handler\n"); ++ return rc; ++ } + -+ if (op->len > XDMA_CMD_AST2700_CMD_LINE_SIZE) { -+ unsigned int rem; -+ unsigned int total; ++ /* ++ * For legacy design. ++ * - TX ringbuffer size: 4KB ++ * - RX ringbuffer size: 64KB ++ * - Timeout timer disabled ++ */ ++ reg = FIELD_PREP(UDMA_MISC_TX_BUFSZ, UDMA_BUFSZ_CODE_4KB) | ++ FIELD_PREP(UDMA_MISC_RX_BUFSZ, UDMA_BUFSZ_CODE_64KB); ++ writel(reg, udma->regs + UDMA_MISC); + -+ line_no = op->len / XDMA_CMD_AST2700_CMD_MULTILINE_SIZE; -+ total = XDMA_CMD_AST2700_CMD_MULTILINE_SIZE * line_no; -+ rem = op->len - total; -+ line_size = XDMA_CMD_AST2700_CMD_MULTILINE_SIZE; -+ pitch = line_size; ++ for (i = 0; i < UDMA_MAX_CHANNEL; ++i) { ++ writel(0, udma->regs + UDMA_CHX_TX_WR_PTR(i)); ++ writel(0, udma->regs + UDMA_CHX_RX_RD_PTR(i)); ++ } + -+ if (rem) { -+ u64 rbmc = bmc_addr + total; ++ writel(0xffffffff, udma->regs + UDMA_TX_DMA_RST); ++ writel(0x0, udma->regs + UDMA_TX_DMA_RST); + -+ cmds[1].host_addr = op->host_addr + (u64)total; -+ cmds[1].pitch = -+ FIELD_PREP(XDMA_CMD_AST2700_PITCH_HOST, 1) | -+ FIELD_PREP(XDMA_CMD_AST2700_PITCH_BMC, 1); -+ cmds[1].cmd = cmd | -+ FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_NO, 1) | -+ FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_SIZE, -+ rem); -+ cmds[1].reserved = rbmc & XDMA_CMD_AST2700_BMC_ADDR; ++ writel(0xffffffff, udma->regs + UDMA_RX_DMA_RST); ++ writel(0x0, udma->regs + UDMA_RX_DMA_RST); + -+ print_hex_dump_debug("xdma rem ", DUMP_PREFIX_OFFSET, -+ 16, 1, &cmds[1], sizeof(*cmds), -+ true); ++ writel(0x0, udma->regs + UDMA_TX_DMA_INT_EN); ++ writel(0xffffffff, udma->regs + UDMA_TX_DMA_INT_STS); ++ writel(0x0, udma->regs + UDMA_RX_DMA_INT_EN); ++ writel(0xffffffff, udma->regs + UDMA_RX_DMA_INT_STS); + -+ cmd &= ~XDMA_CMD_AST2700_CMD_IRQ_BMC; ++ writel(UDMA_TMOUT, udma->regs + UDMA_TMOUT_TIMER); + -+ rc++; -+ } -+ } -+ cmds[0].host_addr = op->host_addr; -+ cmds[0].pitch = -+ FIELD_PREP(XDMA_CMD_AST2700_PITCH_HOST, pitch) | -+ FIELD_PREP(XDMA_CMD_AST2700_PITCH_BMC, pitch); -+ cmds[0].cmd = cmd | FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_NO, line_no) | -+ FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_SIZE, line_size); -+ cmds[0].reserved = bmc_addr & XDMA_CMD_AST2700_BMC_ADDR; ++ spin_lock_init(&udma->lock); + -+ print_hex_dump_debug("xdma cmd ", DUMP_PREFIX_OFFSET, 16, 1, cmds, -+ sizeof(*cmds), true); ++ dev_set_drvdata(dev, udma); + -+ return rc; ++ return 0; +} + -+static int aspeed_xdma_start(struct aspeed_xdma *ctx, unsigned int num_cmds, -+ struct aspeed_xdma_cmd cmds[2], bool upstream, -+ struct aspeed_xdma_client *client) -+{ -+ unsigned int i; -+ int rc = -EBUSY; -+ unsigned long flags; ++static const struct of_device_id aspeed_udma_match[] = { ++ { .compatible = "aspeed,ast2500-udma" }, ++ { .compatible = "aspeed,ast2600-udma" }, ++ { .compatible = "aspeed,ast2700-udma" }, ++ { }, ++}; + -+ spin_lock_irqsave(&ctx->engine_lock, flags); -+ if (ctx->in_reset) -+ goto unlock; ++static struct platform_driver aspeed_udma_driver = { ++ .driver = { ++ .name = DEVICE_NAME, ++ .of_match_table = aspeed_udma_match, + -+ spin_lock(&ctx->client_lock); -+ if (ctx->current_client) { -+ spin_unlock(&ctx->client_lock); -+ goto unlock; -+ } ++ }, ++ .probe = aspeed_udma_probe, ++}; + -+ client->error = false; -+ client->in_progress = true; -+ ctx->current_client = client; -+ spin_unlock(&ctx->client_lock); ++module_platform_driver(aspeed_udma_driver); + -+ ctx->upstream = upstream; -+ for (i = 0; i < num_cmds; ++i) { -+ trace_xdma_start(ctx, &cmds[i]); -+ /* -+ * Use memcpy_toio here to get some barriers before starting -+ * the operation. The command(s) need to be in physical memory -+ * before the XDMA engine starts. -+ */ -+ memcpy_toio(&ctx->cmdq[ctx->cmd_idx], &cmds[i], -+ sizeof(struct aspeed_xdma_cmd)); -+ ctx->cmd_idx = (ctx->cmd_idx + 1) % XDMA_NUM_CMDS; -+ } ++MODULE_AUTHOR("Chia-Wei Wang "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Aspeed UDMA Engine Driver"); +diff --git a/drivers/soc/aspeed/aspeed-usb-hp.c b/drivers/soc/aspeed/aspeed-usb-hp.c +--- a/drivers/soc/aspeed/aspeed-usb-hp.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-usb-hp.c 2026-04-08 18:03:48.311705302 +0000 +@@ -0,0 +1,152 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2021 Aspeed Technology Inc. ++ */ + -+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_writep, -+ ctx->cmd_idx * ctx->chip->queue_entry_size); -+ rc = 0; ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+unlock: -+ spin_unlock_irqrestore(&ctx->engine_lock, flags); -+ return rc; -+} ++#define USB_HP_BEHCI84 0x84 /* Controller Fine-tune Register */ + -+static void aspeed_xdma_done(struct aspeed_xdma *ctx, bool error) ++static const struct of_device_id aspeed_usb_hp_dt_ids[] = { ++ { ++ .compatible = "aspeed,ast2600-usb2ahp", ++ }, ++ { ++ .compatible = "aspeed,ast2700-usb3ahp", ++ }, ++ { ++ .compatible = "aspeed,ast2700-usb3bhp", ++ }, ++ { ++ .compatible = "aspeed,ast2700-usb2ahp", ++ }, ++ { ++ .compatible = "aspeed,ast2700-usb2bhp", ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, aspeed_usb_hp_dt_ids); ++ ++static int aspeed_usb_hp_probe(struct platform_device *pdev) +{ -+ unsigned long flags; ++ struct device_node *node = pdev->dev.of_node; ++ void __iomem *regs; ++ bool ehci_32bits_quirk; ++ u32 val; ++ struct clk *clk; ++ struct reset_control *rst; ++ struct regmap *device; ++ struct phy *usb3_phy; ++ bool is_pcie_xhci; ++ int rc = 0; + -+ spin_lock_irqsave(&ctx->client_lock, flags); -+ if (ctx->current_client) { -+ ctx->current_client->error = error; -+ ctx->current_client->in_progress = false; -+ ctx->current_client = NULL; ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2600-usb2ahp")) { ++ dev_info(&pdev->dev, "Initialized AST2600 USB2AHP\n"); ++ return 0; + } -+ spin_unlock_irqrestore(&ctx->client_lock, flags); + -+ wake_up_interruptible_all(&ctx->wait); -+} ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2700-usb3ahp") || ++ of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2700-usb3bhp")) { ++ is_pcie_xhci = true; ++ } else if (of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2700-usb2ahp") || ++ of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2700-usb2bhp")) { ++ is_pcie_xhci = false; ++ } ++ clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); + -+static irqreturn_t aspeed_xdma_irq(int irq, void *arg) -+{ -+ struct aspeed_xdma *ctx = arg; -+ u32 status; ++ rc = clk_prepare_enable(clk); ++ if (rc) { ++ dev_err(&pdev->dev, "Unable to enable clock (%d)\n", rc); ++ return rc; ++ } + -+ spin_lock(&ctx->engine_lock); -+ status = aspeed_xdma_readl(ctx, ctx->chip->regs.status); ++ rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); ++ if (IS_ERR(rst)) { ++ rc = PTR_ERR(rst); ++ goto err; ++ } ++ rc = reset_control_deassert(rst); ++ if (rc) ++ goto err; + -+ trace_xdma_irq(status); ++ device = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "aspeed,device"); ++ if (IS_ERR(device)) { ++ dev_err(&pdev->dev, "failed to find device regmap\n"); ++ goto err; ++ } + -+ if (status & ctx->chip->status_bits.ds_dirty) { -+ aspeed_xdma_done(ctx, true); -+ } else { -+ if (status & ctx->chip->status_bits.us_comp) { -+ if (ctx->upstream) -+ aspeed_xdma_done(ctx, false); ++ if (is_pcie_xhci) { ++ usb3_phy = devm_phy_get(&pdev->dev, "usb3-phy"); ++ if (IS_ERR(usb3_phy)) { ++ rc = dev_err_probe(&pdev->dev, PTR_ERR(usb3_phy), ++ "failed to get usb3 phy\n"); ++ goto err; + } -+ -+ if (status & ctx->chip->status_bits.ds_comp) { -+ if (!ctx->upstream) -+ aspeed_xdma_done(ctx, false); ++ rc = phy_init(usb3_phy); ++ if (rc < 0) { ++ dev_err(&pdev->dev, "failed to init usb3 phy\n"); ++ goto err; + } -+ } ++ //EnPCIaMSI_EnPCIaIntA_EnPCIaMst_EnPCIaDev ++ /* Turn on PCIe xHCI without MSI */ ++ regmap_update_bits(device, 0x70, ++ BIT(19) | BIT(11) | BIT(3), ++ BIT(19) | BIT(11) | BIT(3)); ++ } else { ++ ehci_32bits_quirk = ++ device_property_read_bool(&pdev->dev, "aspeed,ehci_32bits_quirk"); + -+ aspeed_xdma_writel(ctx, ctx->chip->regs.status, status); -+ spin_unlock(&ctx->engine_lock); ++ if (ehci_32bits_quirk) { ++ regs = of_iomap(node, 0); ++ val = readl(regs + USB_HP_BEHCI84) & ~BIT(11); ++ writel(val, regs + USB_HP_BEHCI84); ++ } + -+ return IRQ_HANDLED; ++ //EnPCIaMSI_EnPCIaIntA_EnPCIaMst_EnPCIaDev ++ /* Turn on PCIe EHCI without MSI */ ++ regmap_update_bits(device, 0x70, ++ BIT(18) | BIT(10) | BIT(2), ++ BIT(18) | BIT(10) | BIT(2)); ++ } ++ dev_info(&pdev->dev, "Initialized AST2700 USB Host PCIe\n"); ++ return 0; ++err: ++ if (clk) ++ clk_disable_unprepare(clk); ++ return rc; +} + -+static void aspeed_xdma_reset(struct aspeed_xdma *ctx) ++static void aspeed_usb_hp_remove(struct platform_device *pdev) +{ -+ unsigned long flags; -+ -+ trace_xdma_reset(ctx); ++ dev_info(&pdev->dev, "Remove USB Host PCIe\n"); ++} + -+ reset_control_assert(ctx->reset); -+ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, -+ XDMA_ENGINE_SETUP_TIME_MAX_US); -+ reset_control_deassert(ctx->reset); -+ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, -+ XDMA_ENGINE_SETUP_TIME_MAX_US); ++static struct platform_driver aspeed_usb_hp_driver = { ++ .probe = aspeed_usb_hp_probe, ++ .remove = aspeed_usb_hp_remove, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = aspeed_usb_hp_dt_ids, ++ }, ++}; ++module_platform_driver(aspeed_usb_hp_driver); + -+ aspeed_xdma_init_eng(ctx); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Neal Liu "); +diff --git a/drivers/soc/aspeed/aspeed-usb-phy.c b/drivers/soc/aspeed/aspeed-usb-phy.c +--- a/drivers/soc/aspeed/aspeed-usb-phy.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-usb-phy.c 2026-04-08 18:03:48.311705302 +0000 +@@ -0,0 +1,112 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2021 Aspeed Technology Inc. ++ */ + -+ aspeed_xdma_done(ctx, true); ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ spin_lock_irqsave(&ctx->engine_lock, flags); -+ ctx->in_reset = false; -+ spin_unlock_irqrestore(&ctx->engine_lock, flags); ++struct usb_phy_ctrl { ++ u32 offset; ++ u32 value; ++}; + -+ wake_up_interruptible(&ctx->wait); -+} ++static const struct of_device_id aspeed_usb_phy_dt_ids[] = { ++ { ++ .compatible = "aspeed,ast2600-uphyb", ++ }, ++ { ++ .compatible = "aspeed,ast2700-uphy2a", ++ }, ++ { ++ .compatible = "aspeed,ast2700-uphy2b", ++ }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, aspeed_usb_phy_dt_ids); + -+static void aspeed_xdma_reset_work(struct work_struct *work) ++static int aspeed_usb_phy_probe(struct platform_device *pdev) +{ -+ struct aspeed_xdma *ctx = container_of(work, struct aspeed_xdma, -+ reset_work); -+ -+ aspeed_xdma_reset(ctx); -+} ++ struct device_node *node = pdev->dev.of_node; ++ struct usb_phy_ctrl *ctrl_data; ++ void __iomem *base; ++ struct regmap *scu; ++ int ctrl_num = 1; ++ int ret, i; ++ u32 val; + -+static irqreturn_t aspeed_xdma_pcie_irq(int irq, void *arg) -+{ -+ struct aspeed_xdma *ctx = arg; ++ scu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "aspeed,scu"); ++ if (IS_ERR(scu)) { ++ dev_err(&pdev->dev, "cannot to find SCU regmap\n"); ++ return -ENODEV; ++ } + -+ trace_xdma_perst(ctx); ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2600-uphyb")) { ++ /* Check SCU040[3] USB port B controller reset is deassert */ ++ regmap_read(scu, 0x40, &val); ++ if ((val & BIT(3))) ++ return -EPROBE_DEFER; ++ } + -+ spin_lock(&ctx->engine_lock); -+ if (ctx->in_reset) { -+ spin_unlock(&ctx->engine_lock); -+ return IRQ_HANDLED; ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2700-uphy2a")) { ++ /* Check SCU220[0] USB vHubA1 controller reset is deassert */ ++ regmap_read(scu, 0x220, &val); ++ if ((val & BIT(0))) ++ return -EPROBE_DEFER; + } + -+ ctx->in_reset = true; -+ spin_unlock(&ctx->engine_lock); ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2700-uphy2b")) { ++ /* Check SCU220[3] USB vHubB1 controller reset is deassert */ ++ regmap_read(scu, 0x220, &val); ++ if ((val & BIT(3))) ++ return -EPROBE_DEFER; ++ } + -+ schedule_work(&ctx->reset_work); -+ return IRQ_HANDLED; -+} ++ ctrl_data = devm_kzalloc(&pdev->dev, ++ sizeof(struct usb_phy_ctrl) * ctrl_num, ++ GFP_KERNEL); ++ if (!ctrl_data) ++ return -ENOMEM; + -+static ssize_t aspeed_xdma_write(struct file *file, const char __user *buf, -+ size_t len, loff_t *offset) -+{ -+ int rc; -+ unsigned int num_cmds; -+ struct aspeed_xdma_op op; -+ struct aspeed_xdma_cmd cmds[2]; -+ struct aspeed_xdma_client *client = file->private_data; -+ struct aspeed_xdma *ctx = client->ctx; ++ base = of_iomap(node, 0); + -+ if (len != sizeof(op)) ++ ret = of_property_read_u32_array(node, "ctrl", (u32 *)ctrl_data, ++ ctrl_num * 2); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Could not read ctrl property\n"); + return -EINVAL; ++ } + -+ if (READ_ONCE(client->in_progress)) -+ return -EBUSY; ++ for (i = 0; i < ctrl_num; i++) ++ writel(ctrl_data[i].value, base + ctrl_data[i].offset); + -+ if (copy_from_user(&op, buf, len)) -+ return -EFAULT; ++ dev_info(&pdev->dev, "Initialized USB PHY\n"); + -+ if (!op.len || op.len > client->size || -+ op.direction > ASPEED_XDMA_DIRECTION_UPSTREAM) -+ return -EINVAL; ++ return 0; ++} + -+ num_cmds = ctx->chip->set_cmd(ctx, cmds, &op, client->phys); -+ do { -+ rc = aspeed_xdma_start(ctx, num_cmds, cmds, !!op.direction, -+ client); -+ if (!rc) -+ break; ++static void aspeed_usb_phy_remove(struct platform_device *pdev) ++{ ++} + -+ if ((file->f_flags & O_NONBLOCK) || rc != -EBUSY) -+ return rc; ++static struct platform_driver aspeed_usb_phy_driver = { ++ .probe = aspeed_usb_phy_probe, ++ .remove = aspeed_usb_phy_remove, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = aspeed_usb_phy_dt_ids, ++ }, ++}; ++module_platform_driver(aspeed_usb_phy_driver); + -+ rc = wait_event_interruptible(ctx->wait, -+ !(ctx->current_client || -+ ctx->in_reset)); -+ } while (!rc); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Neal Liu "); +diff --git a/drivers/soc/aspeed/aspeed-xdma.c b/drivers/soc/aspeed/aspeed-xdma.c +--- a/drivers/soc/aspeed/aspeed-xdma.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-xdma.c 2026-04-08 18:03:48.311705302 +0000 +@@ -0,0 +1,1438 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++// Copyright IBM Corp 2019 + -+ if (rc) -+ return -EINTR; ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ if (!(file->f_flags & O_NONBLOCK)) { -+ rc = wait_event_interruptible(ctx->wait, !client->in_progress); -+ if (rc) -+ return -EINTR; ++#define DEVICE_NAME "aspeed-xdma" + -+ if (client->error) -+ return -EIO; -+ } ++#define SCU_AST2600_MISC_CTRL 0x0c0 ++#define SCU_AST2600_MISC_CTRL_XDMA_BMC BIT(8) ++#define SCU_AST2700_MISC_CTRL_XDMA_CLIENT BIT(4) + -+ return len; -+} ++#define SCU_AST2600_DEBUG_CTRL 0x0c8 ++#define DEBUG_CTRL_AST2600_XDMA_DISABLE BIT(2) ++#define DEBUG_CTRL_AST2700_XDMA_DISABLE BIT(8) + -+static __poll_t aspeed_xdma_poll(struct file *file, -+ struct poll_table_struct *wait) -+{ -+ __poll_t mask = 0; -+ __poll_t req = poll_requested_events(wait); -+ struct aspeed_xdma_client *client = file->private_data; -+ struct aspeed_xdma *ctx = client->ctx; ++#define SCU_AST2500_PCIE_CONF 0x180 ++#define SCU_AST2600_PCIE_CONF 0xc20 ++#define SCU_AST2700_PCIE0_CONF 0x970 ++#define SCU_AST2700_PCIE1_CONF 0x9B0 ++#define SCU_PCIE_CONF_VGA_EN BIT(0) ++#define SCU_PCIE_CONF_VGA_EN_MMIO BIT(1) ++#define SCU_PCIE_CONF_VGA_EN_LPC BIT(2) ++#define SCU_PCIE_CONF_VGA_EN_MSI BIT(3) ++#define SCU_PCIE_CONF_VGA_EN_MCTP BIT(4) ++#define SCU_PCIE_CONF_VGA_EN_IRQ BIT(5) ++#define SCU_PCIE_CONF_VGA_EN_DMA BIT(6) ++#define SCU_PCIE_CONF_BMC_EN BIT(8) ++#define SCU_PCIE_CONF_BMC_EN_MMIO BIT(9) ++#define SCU_PCIE_CONF_BMC_EN_MSI BIT(11) ++#define SCU_PCIE_CONF_BMC_EN_MCTP BIT(12) ++#define SCU_PCIE_CONF_BMC_EN_IRQ BIT(13) ++#define SCU_PCIE_CONF_BMC_EN_DMA BIT(14) + -+ if (req & (EPOLLIN | EPOLLRDNORM)) { -+ if (READ_ONCE(client->in_progress)) -+ poll_wait(file, &ctx->wait, wait); ++#define SCU_AST2700_PCIE0_CTRL 0xa60 ++#define SCU_AST2700_PCIE1_CTRL 0xae0 ++#define SCU_AST2700_PCIE_CTRL_DMA_EN BIT(2) + -+ if (!READ_ONCE(client->in_progress)) { -+ if (READ_ONCE(client->error)) -+ mask |= EPOLLERR; -+ else -+ mask |= EPOLLIN | EPOLLRDNORM; -+ } -+ } ++#define SCU_AST2500_BMC_CLASS_REV 0x19c ++#define SCU_AST2600_BMC_CLASS_REV 0xc68 ++#define SCU_AST2700_PCIE0_BMC_CLASS_REV 0xa18 ++#define SCU_AST2700_PCIE1_BMC_CLASS_REV 0xa98 ++#define SCU_BMC_CLASS_REV_XDMA 0xff000001 ++#define SCU_BMC_CLASS_REV_MASK 0xffffff00 + -+ if (req & (EPOLLOUT | EPOLLWRNORM)) { -+ if (READ_ONCE(ctx->current_client)) -+ poll_wait(file, &ctx->wait, wait); ++#define XDMA_CMDQ_SIZE PAGE_SIZE ++#define XDMA_NUM_CMDS \ ++ (XDMA_CMDQ_SIZE / sizeof(struct aspeed_xdma_cmd)) + -+ if (!READ_ONCE(ctx->current_client)) -+ mask |= EPOLLOUT | EPOLLWRNORM; -+ } ++/* Aspeed specification requires 100us after disabling the reset */ ++#define XDMA_ENGINE_SETUP_TIME_MAX_US 1000 ++#define XDMA_ENGINE_SETUP_TIME_MIN_US 100 + -+ if (mask) -+ aspeed_xdma_reset(ctx); ++#define XDMA_CMD_AST2500_PITCH_SHIFT 3 ++#define XDMA_CMD_AST2500_PITCH_BMC GENMASK_ULL(62, 51) ++#define XDMA_CMD_AST2500_PITCH_HOST GENMASK_ULL(46, 35) ++#define XDMA_CMD_AST2500_PITCH_UPSTREAM BIT_ULL(31) ++#define XDMA_CMD_AST2500_PITCH_ADDR GENMASK_ULL(29, 4) ++#define XDMA_CMD_AST2500_PITCH_ID BIT_ULL(0) ++#define XDMA_CMD_AST2500_CMD_IRQ_EN BIT_ULL(31) ++#define XDMA_CMD_AST2500_CMD_LINE_NO GENMASK_ULL(27, 16) ++#define XDMA_CMD_AST2500_CMD_IRQ_BMC BIT_ULL(15) ++#define XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT 4 ++#define XDMA_CMD_AST2500_CMD_LINE_SIZE \ ++ GENMASK_ULL(14, XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT) ++#define XDMA_CMD_AST2500_CMD_ID BIT_ULL(1) + -+ return mask; -+} ++#define XDMA_CMD_AST2600_PITCH_BMC GENMASK_ULL(62, 48) ++#define XDMA_CMD_AST2600_PITCH_HOST GENMASK_ULL(46, 32) ++#define XDMA_CMD_AST2600_PITCH_ADDR GENMASK_ULL(30, 0) ++#define XDMA_CMD_AST2600_CMD_64_EN BIT_ULL(40) ++#define XDMA_CMD_AST2600_CMD_IRQ_BMC BIT_ULL(37) ++#define XDMA_CMD_AST2600_CMD_IRQ_HOST BIT_ULL(36) ++#define XDMA_CMD_AST2600_CMD_UPSTREAM BIT_ULL(32) ++#define XDMA_CMD_AST2600_CMD_LINE_NO GENMASK_ULL(27, 16) ++#define XDMA_CMD_AST2600_CMD_LINE_SIZE GENMASK_ULL(14, 0) ++#define XDMA_CMD_AST2600_CMD_MULTILINE_SIZE GENMASK_ULL(14, 12) + -+static long aspeed_xdma_ioctl(struct file *file, unsigned int cmd, -+ unsigned long param) -+{ -+ unsigned long flags; -+ struct aspeed_xdma_client *client = file->private_data; -+ struct aspeed_xdma *ctx = client->ctx; ++#define XDMA_CMD_AST2700_PITCH_BMC GENMASK_ULL(62, 48) ++#define XDMA_CMD_AST2700_PITCH_HOST GENMASK_ULL(46, 32) ++#define XDMA_CMD_AST2700_CMD_64_EN BIT_ULL(40) ++#define XDMA_CMD_AST2700_CMD_IRQ_BMC BIT_ULL(37) ++#define XDMA_CMD_AST2700_CMD_UPSTREAM BIT_ULL(32) ++#define XDMA_CMD_AST2700_CMD_LINE_NO GENMASK_ULL(27, 16) ++#define XDMA_CMD_AST2700_CMD_LINE_SIZE GENMASK_ULL(14, 0) ++#define XDMA_CMD_AST2700_CMD_MULTILINE_SIZE GENMASK_ULL(14, 12) ++#define XDMA_CMD_AST2700_BMC_ADDR GENMASK_ULL(34, 0) + -+ switch (cmd) { -+ case ASPEED_XDMA_IOCTL_RESET: -+ spin_lock_irqsave(&ctx->engine_lock, flags); -+ if (ctx->in_reset) { -+ spin_unlock_irqrestore(&ctx->engine_lock, flags); -+ return 0; -+ } ++#define XDMA_AST2500_QUEUE_ENTRY_SIZE 4 ++#define XDMA_AST2500_HOST_CMDQ_ADDR0 0x00 ++#define XDMA_AST2500_HOST_CMDQ_ENDP 0x04 ++#define XDMA_AST2500_HOST_CMDQ_WRITEP 0x08 ++#define XDMA_AST2500_HOST_CMDQ_READP 0x0c ++#define XDMA_AST2500_BMC_CMDQ_ADDR 0x10 ++#define XDMA_AST2500_BMC_CMDQ_ENDP 0x14 ++#define XDMA_AST2500_BMC_CMDQ_WRITEP 0x18 ++#define XDMA_AST2500_BMC_CMDQ_READP 0x1c ++#define XDMA_BMC_CMDQ_READP_RESET 0xee882266 ++#define XDMA_AST2500_CTRL 0x20 ++#define XDMA_AST2500_CTRL_US_COMP BIT(4) ++#define XDMA_AST2500_CTRL_DS_COMP BIT(5) ++#define XDMA_AST2500_CTRL_DS_DIRTY BIT(6) ++#define XDMA_AST2500_CTRL_DS_SIZE_256 BIT(17) ++#define XDMA_AST2500_CTRL_DS_TIMEOUT BIT(28) ++#define XDMA_AST2500_CTRL_DS_CHECK_ID BIT(29) ++#define XDMA_AST2500_STATUS 0x24 ++#define XDMA_AST2500_STATUS_US_COMP BIT(4) ++#define XDMA_AST2500_STATUS_DS_COMP BIT(5) ++#define XDMA_AST2500_STATUS_DS_DIRTY BIT(6) ++#define XDMA_AST2500_INPRG_DS_CMD1 0x38 ++#define XDMA_AST2500_INPRG_DS_CMD2 0x3c ++#define XDMA_AST2500_INPRG_US_CMD00 0x40 ++#define XDMA_AST2500_INPRG_US_CMD01 0x44 ++#define XDMA_AST2500_INPRG_US_CMD10 0x48 ++#define XDMA_AST2500_INPRG_US_CMD11 0x4c ++#define XDMA_AST2500_INPRG_US_CMD20 0x50 ++#define XDMA_AST2500_INPRG_US_CMD21 0x54 ++#define XDMA_AST2500_HOST_CMDQ_ADDR1 0x60 ++#define XDMA_AST2500_VGA_CMDQ_ADDR0 0x64 ++#define XDMA_AST2500_VGA_CMDQ_ENDP 0x68 ++#define XDMA_AST2500_VGA_CMDQ_WRITEP 0x6c ++#define XDMA_AST2500_VGA_CMDQ_READP 0x70 ++#define XDMA_AST2500_VGA_CMD_STATUS 0x74 ++#define XDMA_AST2500_VGA_CMDQ_ADDR1 0x78 + -+ ctx->in_reset = true; -+ spin_unlock_irqrestore(&ctx->engine_lock, flags); ++#define XDMA_AST2600_QUEUE_ENTRY_SIZE 2 ++#define XDMA_AST2600_HOST_CMDQ_ADDR0 0x00 ++#define XDMA_AST2600_HOST_CMDQ_ADDR1 0x04 ++#define XDMA_AST2600_HOST_CMDQ_ENDP 0x08 ++#define XDMA_AST2600_HOST_CMDQ_WRITEP 0x0c ++#define XDMA_AST2600_HOST_CMDQ_READP 0x10 ++#define XDMA_AST2600_BMC_CMDQ_ADDR 0x14 ++#define XDMA_AST2600_BMC_CMDQ_ENDP 0x18 ++#define XDMA_AST2600_BMC_CMDQ_WRITEP 0x1c ++#define XDMA_AST2600_BMC_CMDQ_READP 0x20 ++#define XDMA_AST2600_VGA_CMDQ_ADDR0 0x24 ++#define XDMA_AST2600_VGA_CMDQ_ADDR1 0x28 ++#define XDMA_AST2600_VGA_CMDQ_ENDP 0x2c ++#define XDMA_AST2600_VGA_CMDQ_WRITEP 0x30 ++#define XDMA_AST2600_VGA_CMDQ_READP 0x34 ++#define XDMA_AST2600_CTRL 0x38 ++#define XDMA_AST2600_CTRL_US_COMP BIT(16) ++#define XDMA_AST2600_CTRL_DS_COMP BIT(17) ++#define XDMA_AST2600_CTRL_DS_DIRTY BIT(18) ++#define XDMA_AST2600_CTRL_DS_SIZE_256 BIT(20) ++#define XDMA_AST2600_STATUS 0x3c ++#define XDMA_AST2600_STATUS_US_COMP BIT(16) ++#define XDMA_AST2600_STATUS_DS_COMP BIT(17) ++#define XDMA_AST2600_STATUS_DS_DIRTY BIT(18) ++#define XDMA_AST2600_INPRG_DS_CMD00 0x40 ++#define XDMA_AST2600_INPRG_DS_CMD01 0x44 ++#define XDMA_AST2600_INPRG_DS_CMD10 0x48 ++#define XDMA_AST2600_INPRG_DS_CMD11 0x4c ++#define XDMA_AST2600_INPRG_DS_CMD20 0x50 ++#define XDMA_AST2600_INPRG_DS_CMD21 0x54 ++#define XDMA_AST2600_INPRG_US_CMD00 0x60 ++#define XDMA_AST2600_INPRG_US_CMD01 0x64 ++#define XDMA_AST2600_INPRG_US_CMD10 0x68 ++#define XDMA_AST2600_INPRG_US_CMD11 0x6c ++#define XDMA_AST2600_INPRG_US_CMD20 0x70 ++#define XDMA_AST2600_INPRG_US_CMD21 0x74 + -+ if (READ_ONCE(ctx->current_client)) -+ dev_warn(ctx->dev, -+ "User reset with transfer in progress.\n"); ++#define XDMA_AST2700_QUEUE_ENTRY_SIZE 2 ++#define XDMA_AST2700_BMC_CMDQ_ADDR0 0x10 ++#define XDMA_AST2700_BMC_CMDQ_ADDR1 0x14 ++#define XDMA_AST2700_BMC_CMDQ_ENDP 0x18 ++#define XDMA_AST2700_BMC_CMDQ_WRITEP 0x1c ++#define XDMA_AST2700_BMC_CMDQ_READP 0x20 ++#define XDMA_AST2700_CTRL 0x38 ++#define XDMA_AST2700_CTRL_US_COMP BIT(16) ++#define XDMA_AST2700_CTRL_DS_COMP BIT(17) ++#define XDMA_AST2700_CTRL_DS_DIRTY BIT(18) ++#define XDMA_AST2700_CTRL_IDLE BIT(19) ++#define XDMA_AST2700_CTRL_DS_SIZE_256 BIT(20) ++#define XDMA_AST2700_STATUS 0x3c ++#define XDMA_AST2700_STATUS_US_COMP BIT(16) ++#define XDMA_AST2700_STATUS_DS_COMP BIT(17) ++#define XDMA_AST2700_STATUS_DS_DIRTY BIT(18) ++#define XDMA_AST2700_STATUS_IDLE BIT(19) ++#define XDMA_AST2700_INPRG_DS_CMD00 0x40 ++#define XDMA_AST2700_INPRG_DS_CMD01 0x44 ++#define XDMA_AST2700_INPRG_DS_CMD10 0x48 ++#define XDMA_AST2700_INPRG_DS_CMD11 0x4c ++#define XDMA_AST2700_INPRG_DS_CMD20 0x50 ++#define XDMA_AST2700_INPRG_DS_CMD21 0x54 ++#define XDMA_AST2700_INPRG_US_CMD00 0x60 ++#define XDMA_AST2700_INPRG_US_CMD01 0x64 ++#define XDMA_AST2700_INPRG_US_CMD10 0x68 ++#define XDMA_AST2700_INPRG_US_CMD11 0x6c ++#define XDMA_AST2700_INPRG_US_CMD20 0x70 ++#define XDMA_AST2700_INPRG_US_CMD21 0x74 + -+ aspeed_xdma_reset(ctx); -+ break; -+ default: -+ return -EINVAL; -+ } ++struct aspeed_xdma_cmd { ++ u64 host_addr; ++ u64 pitch; ++ u64 cmd; ++ u64 reserved; ++}; + -+ return 0; -+} ++struct aspeed_xdma_regs { ++ u8 bmc_cmdq_addr; ++ u8 bmc_cmdq_addr_ext; ++ u8 bmc_cmdq_endp; ++ u8 bmc_cmdq_writep; ++ u8 bmc_cmdq_readp; ++ u8 control; ++ u8 status; ++}; + -+static void aspeed_xdma_vma_close(struct vm_area_struct *vma) -+{ -+ int rc; -+ struct aspeed_xdma_client *client = vma->vm_private_data; ++struct aspeed_xdma_status_bits { ++ u32 us_comp; ++ u32 ds_comp; ++ u32 ds_dirty; ++}; + -+ rc = wait_event_interruptible(client->ctx->wait, !client->in_progress); -+ if (rc) -+ return; ++struct aspeed_xdma; + -+ gen_pool_free(client->ctx->pool, (unsigned long)client->virt, -+ client->size); -+ trace_xdma_unmap(client); ++struct aspeed_xdma_chip { ++ u32 control; ++ u32 scu_bmc_class; ++ u32 scu_misc_ctrl; ++ u32 scu_misc_mask; ++ u32 scu_disable_mask; ++ u32 scu_pcie_conf; ++ u32 scu_pcie_ctrl; ++ unsigned int queue_entry_size; ++ struct aspeed_xdma_regs regs; ++ struct aspeed_xdma_status_bits status_bits; ++ unsigned int (*set_cmd)(struct aspeed_xdma *ctx, ++ struct aspeed_xdma_cmd cmds[2], ++ struct aspeed_xdma_op *op, u64 bmc_addr); ++}; + -+ client->virt = NULL; -+ client->phys = 0; -+ client->size = 0; -+} ++struct aspeed_xdma_client; + -+static const struct vm_operations_struct aspeed_xdma_vm_ops = { -+ .close = aspeed_xdma_vma_close, -+}; ++struct aspeed_xdma { ++ struct kobject kobj; ++ const struct aspeed_xdma_chip *chip; + -+static int aspeed_xdma_mmap(struct file *file, struct vm_area_struct *vma) -+{ -+ int rc; -+ struct aspeed_xdma_client *client = file->private_data; -+ struct aspeed_xdma *ctx = client->ctx; ++ int irq; ++ int pcie_irq; ++ struct clk *clock; ++ struct device *dev; ++ void __iomem *base; ++ resource_size_t res_size; ++ resource_size_t res_start; ++ struct reset_control *reset; ++ struct reset_control *reset_rc; + -+ /* restrict file to one mapping */ -+ if (client->size) -+ return -EBUSY; ++ /* Protects current_client */ ++ spinlock_t client_lock; ++ struct aspeed_xdma_client *current_client; + -+ client->size = vma->vm_end - vma->vm_start; -+ client->virt = gen_pool_dma_alloc(ctx->pool, client->size, -+ &client->phys); -+ if (!client->virt) { -+ trace_xdma_mmap_error(client, 0UL); -+ client->phys = 0; -+ client->size = 0; -+ return -ENOMEM; -+ } ++ /* Protects engine configuration */ ++ spinlock_t engine_lock; ++ struct aspeed_xdma_cmd *cmdq; ++ unsigned int cmd_idx; ++ bool in_reset; ++ bool upstream; + -+ vma->vm_pgoff = (client->phys - ctx->mem_phys) >> PAGE_SHIFT; -+ vma->vm_ops = &aspeed_xdma_vm_ops; -+ vma->vm_private_data = client; -+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ /* Queue waiters for idle engine */ ++ wait_queue_head_t wait; + -+ rc = io_remap_pfn_range(vma, vma->vm_start, client->phys >> PAGE_SHIFT, -+ client->size, vma->vm_page_prot); -+ if (rc) { -+ dev_warn(ctx->dev, "mmap err: v[%08lx] to p[%pa], s[%08x]\n", -+ vma->vm_start, &client->phys, client->size); ++ struct work_struct reset_work; + -+ gen_pool_free(ctx->pool, (unsigned long)client->virt, -+ client->size); ++ phys_addr_t mem_phys; ++ phys_addr_t mem_size; ++ void *mem_virt; ++ dma_addr_t mem_coherent; ++ dma_addr_t cmdq_phys; ++ struct gen_pool *pool; + -+ trace_xdma_mmap_error(client, vma->vm_start); -+ client->virt = NULL; -+ client->phys = 0; -+ client->size = 0; -+ return rc; -+ } ++ struct miscdevice misc; ++}; + -+ trace_xdma_mmap(client); -+ dev_dbg(ctx->dev, "mmap: v[%08lx] to p[%pa], s[%08x]\n", -+ vma->vm_start, &client->phys, client->size); ++struct aspeed_xdma_client { ++ struct aspeed_xdma *ctx; + -+ return 0; -+} ++ bool error; ++ bool in_progress; ++ void *virt; ++ dma_addr_t phys; ++ u32 size; ++}; + -+static int aspeed_xdma_open(struct inode *inode, struct file *file) ++#define CREATE_TRACE_POINTS ++#include ++ ++static u32 aspeed_xdma_readl(struct aspeed_xdma *ctx, u8 reg) +{ -+ struct miscdevice *misc = file->private_data; -+ struct aspeed_xdma *ctx = container_of(misc, struct aspeed_xdma, misc); -+ struct aspeed_xdma_client *client = kzalloc(sizeof(*client), -+ GFP_KERNEL); ++ u32 v = readl(ctx->base + reg); + -+ if (!client) -+ return -ENOMEM; ++ dev_dbg(ctx->dev, "read %02x[%08x]\n", reg, v); ++ return v; ++} + -+ kobject_get(&ctx->kobj); -+ client->ctx = ctx; -+ file->private_data = client; -+ return 0; ++static void aspeed_xdma_writel(struct aspeed_xdma *ctx, u8 reg, u32 val) ++{ ++ writel(val, ctx->base + reg); ++ dev_dbg(ctx->dev, "write %02x[%08x]\n", reg, val); +} + -+static int aspeed_xdma_release(struct inode *inode, struct file *file) ++static void aspeed_xdma_init_eng(struct aspeed_xdma *ctx) +{ -+ bool reset = false; + unsigned long flags; -+ struct aspeed_xdma_client *client = file->private_data; -+ struct aspeed_xdma *ctx = client->ctx; -+ -+ spin_lock_irqsave(&ctx->client_lock, flags); -+ if (client == ctx->current_client) { -+ spin_lock(&ctx->engine_lock); -+ if (ctx->in_reset) { -+ ctx->current_client = NULL; -+ } else { -+ ctx->in_reset = true; -+ reset = true; -+ } -+ spin_unlock(&ctx->engine_lock); -+ } -+ spin_unlock_irqrestore(&ctx->client_lock, flags); -+ -+ if (reset) -+ aspeed_xdma_reset(ctx); + -+ if (client->virt) { -+ gen_pool_free(ctx->pool, (unsigned long)client->virt, -+ client->size); -+ trace_xdma_unmap(client); -+ } ++ spin_lock_irqsave(&ctx->engine_lock, flags); ++ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_endp, ++ ctx->chip->queue_entry_size * XDMA_NUM_CMDS); ++ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_readp, ++ XDMA_BMC_CMDQ_READP_RESET); ++ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_writep, 0); ++ aspeed_xdma_writel(ctx, ctx->chip->regs.control, ctx->chip->control); ++ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_addr, ctx->cmdq_phys); ++#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT ++ if (ctx->chip->regs.bmc_cmdq_addr_ext) ++ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_addr_ext, ctx->cmdq_phys >> 32); ++#endif + -+ kfree(client); -+ kobject_put(&ctx->kobj); -+ return 0; ++ ctx->cmd_idx = 0; ++ spin_unlock_irqrestore(&ctx->engine_lock, flags); +} + -+static const struct file_operations aspeed_xdma_fops = { -+ .owner = THIS_MODULE, -+ .write = aspeed_xdma_write, -+ .poll = aspeed_xdma_poll, -+ .unlocked_ioctl = aspeed_xdma_ioctl, -+ .mmap = aspeed_xdma_mmap, -+ .open = aspeed_xdma_open, -+ .release = aspeed_xdma_release, -+}; -+ -+static int aspeed_xdma_init_scu(struct aspeed_xdma *ctx, struct device *dev) ++static unsigned int aspeed_xdma_ast2500_set_cmd(struct aspeed_xdma *ctx, ++ struct aspeed_xdma_cmd cmds[2], ++ struct aspeed_xdma_op *op, ++ u64 bmc_addr) +{ -+ struct regmap *scu = syscon_regmap_lookup_by_phandle(dev->of_node, -+ "aspeed,scu"); ++ unsigned int rc = 1; ++ unsigned int pitch = 1; ++ unsigned int line_no = 1; ++ unsigned int line_size = op->len >> ++ XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT; ++ u64 cmd = XDMA_CMD_AST2500_CMD_IRQ_EN | XDMA_CMD_AST2500_CMD_IRQ_BMC | ++ XDMA_CMD_AST2500_CMD_ID; ++ u64 cmd_pitch = (op->direction ? XDMA_CMD_AST2500_PITCH_UPSTREAM : 0) | ++ XDMA_CMD_AST2500_PITCH_ID; + -+ if (!IS_ERR(scu)) { -+ u32 selection; -+ bool pcie_device_bmc = true; -+ const u32 bmc = SCU_PCIE_CONF_BMC_EN | -+ SCU_PCIE_CONF_BMC_EN_MSI | SCU_PCIE_CONF_BMC_EN_IRQ | -+ SCU_PCIE_CONF_BMC_EN_DMA; -+ const u32 vga = SCU_PCIE_CONF_VGA_EN | -+ SCU_PCIE_CONF_VGA_EN_MSI | SCU_PCIE_CONF_VGA_EN_IRQ | -+ SCU_PCIE_CONF_VGA_EN_DMA; -+ const char *pcie = NULL; ++ dev_dbg(ctx->dev, "xdma %s ast2500: bmc[%08llx] len[%08x] host[%08x]\n", ++ op->direction ? "upstream" : "downstream", bmc_addr, op->len, ++ (u32)op->host_addr); + -+ if (!of_property_read_string(dev->of_node, -+ "aspeed,pcie-device", &pcie)) { -+ if (!strcmp(pcie, "vga")) { -+ pcie_device_bmc = false; -+ } else if (strcmp(pcie, "bmc")) { -+ dev_err(dev, -+ "Invalid pcie-device property %s.\n", -+ pcie); -+ return -EINVAL; -+ } -+ } ++ if (op->len > XDMA_CMD_AST2500_CMD_LINE_SIZE) { ++ unsigned int rem; ++ unsigned int total; + -+ if (pcie_device_bmc) { -+ selection = bmc; -+ regmap_update_bits(scu, ctx->chip->scu_bmc_class, -+ SCU_BMC_CLASS_REV_MASK, -+ SCU_BMC_CLASS_REV_XDMA); -+ } else { -+ selection = vga; -+ } ++ line_no = op->len / XDMA_CMD_AST2500_CMD_LINE_SIZE; ++ total = XDMA_CMD_AST2500_CMD_LINE_SIZE * line_no; ++ rem = (op->len - total) >> ++ XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT; ++ line_size = XDMA_CMD_AST2500_CMD_LINE_SIZE; ++ pitch = line_size >> XDMA_CMD_AST2500_PITCH_SHIFT; ++ line_size >>= XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT; + -+ regmap_update_bits(scu, ctx->chip->scu_pcie_conf, bmc | vga, -+ selection); ++ if (rem) { ++ u32 rbmc = bmc_addr + total; + -+ if (ctx->chip->scu_misc_ctrl) { -+ regmap_update_bits(scu, ctx->chip->scu_misc_ctrl, -+ ctx->chip->scu_misc_mask, -+ ctx->chip->scu_misc_mask); ++ cmds[1].host_addr = op->host_addr + (u64)total; ++ cmds[1].pitch = cmd_pitch | ++ ((u64)rbmc & XDMA_CMD_AST2500_PITCH_ADDR) | ++ FIELD_PREP(XDMA_CMD_AST2500_PITCH_HOST, 1) | ++ FIELD_PREP(XDMA_CMD_AST2500_PITCH_BMC, 1); ++ cmds[1].cmd = cmd | ++ FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_NO, 1) | ++ FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_SIZE, ++ rem); ++ cmds[1].reserved = 0ULL; + -+ regmap_update_bits(scu, SCU_AST2600_DEBUG_CTRL, -+ ctx->chip->scu_disable_mask, 0); -+ } ++ print_hex_dump_debug("xdma rem ", DUMP_PREFIX_OFFSET, ++ 16, 1, &cmds[1], sizeof(*cmds), ++ true); + -+ if (ctx->chip->scu_pcie_ctrl) { -+ regmap_update_bits(scu, ctx->chip->scu_pcie_ctrl, -+ SCU_AST2700_PCIE_CTRL_DMA_EN, -+ SCU_AST2700_PCIE_CTRL_DMA_EN); ++ cmd &= ~(XDMA_CMD_AST2500_CMD_IRQ_EN | ++ XDMA_CMD_AST2500_CMD_IRQ_BMC); ++ ++ rc++; + } -+ } else { -+ dev_warn(dev, "Unable to configure PCIe: %ld; continuing.\n", -+ PTR_ERR(scu)); + } + -+ return 0; -+} -+ -+static void aspeed_xdma_kobject_release(struct kobject *kobj) -+{ -+ struct aspeed_xdma *ctx = container_of(kobj, struct aspeed_xdma, kobj); -+ -+ if (ctx->pcie_irq >= 0) -+ free_irq(ctx->pcie_irq, ctx); -+ -+ gen_pool_free(ctx->pool, (unsigned long)ctx->cmdq, XDMA_CMDQ_SIZE); ++ cmds[0].host_addr = op->host_addr; ++ cmds[0].pitch = cmd_pitch | ++ (bmc_addr & XDMA_CMD_AST2500_PITCH_ADDR) | ++ FIELD_PREP(XDMA_CMD_AST2500_PITCH_HOST, pitch) | ++ FIELD_PREP(XDMA_CMD_AST2500_PITCH_BMC, pitch); ++ cmds[0].cmd = cmd | FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_NO, line_no) | ++ FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_SIZE, line_size); ++ cmds[0].reserved = 0ULL; + -+ gen_pool_destroy(ctx->pool); ++ print_hex_dump_debug("xdma cmd ", DUMP_PREFIX_OFFSET, 16, 1, cmds, ++ sizeof(*cmds), true); + -+ dma_free_coherent(ctx->dev, ctx->mem_size, ctx->mem_virt, -+ ctx->mem_coherent); ++ return rc; ++} + -+ if (ctx->reset_rc) -+ reset_control_put(ctx->reset_rc); -+ reset_control_put(ctx->reset); ++static unsigned int aspeed_xdma_ast2600_set_cmd(struct aspeed_xdma *ctx, ++ struct aspeed_xdma_cmd cmds[2], ++ struct aspeed_xdma_op *op, ++ u64 bmc_addr) ++{ ++ unsigned int rc = 1; ++ unsigned int pitch = 1; ++ unsigned int line_no = 1; ++ unsigned int line_size = op->len; ++ u64 cmd = XDMA_CMD_AST2600_CMD_IRQ_BMC | ++ (op->direction ? XDMA_CMD_AST2600_CMD_UPSTREAM : 0); + -+ clk_put(ctx->clock); ++ if (op->host_addr & 0xffffffff00000000ULL || ++ (op->host_addr + (u64)op->len) & 0xffffffff00000000ULL) ++ cmd |= XDMA_CMD_AST2600_CMD_64_EN; + -+ free_irq(ctx->irq, ctx); ++ dev_dbg(ctx->dev, "xdma %s ast2600: bmc[%08llx] len[%08x] host[%016llx]\n", ++ op->direction ? "upstream" : "downstream", ++ bmc_addr, op->len, op->host_addr); + -+ iounmap(ctx->base); -+ release_mem_region(ctx->res_start, ctx->res_size); ++ if ((op->host_addr & 0xff) + op->len > XDMA_CMD_AST2600_CMD_LINE_SIZE) { ++ unsigned int rem; ++ unsigned int total; + -+ kfree(ctx); -+} ++ line_no = op->len / XDMA_CMD_AST2600_CMD_MULTILINE_SIZE; ++ total = XDMA_CMD_AST2600_CMD_MULTILINE_SIZE * line_no; ++ rem = op->len - total; ++ line_size = XDMA_CMD_AST2600_CMD_MULTILINE_SIZE; ++ pitch = line_size; + -+static const struct kobj_type aspeed_xdma_kobject_type = { -+ .release = aspeed_xdma_kobject_release, -+}; ++ if (rem) { ++ u32 rbmc = bmc_addr + total; + -+static int aspeed_xdma_iomap(struct aspeed_xdma *ctx, -+ struct platform_device *pdev) -+{ -+ resource_size_t size; -+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ cmds[1].host_addr = op->host_addr + (u64)total; ++ cmds[1].pitch = ++ ((u64)rbmc & XDMA_CMD_AST2600_PITCH_ADDR) | ++ FIELD_PREP(XDMA_CMD_AST2600_PITCH_HOST, 1) | ++ FIELD_PREP(XDMA_CMD_AST2600_PITCH_BMC, 1); ++ cmds[1].cmd = cmd | ++ FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_NO, 1) | ++ FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_SIZE, ++ rem); ++ cmds[1].reserved = 0ULL; + -+ if (!res) -+ return -ENOMEM; ++ print_hex_dump_debug("xdma rem ", DUMP_PREFIX_OFFSET, ++ 16, 1, &cmds[1], sizeof(*cmds), ++ true); + -+ size = resource_size(res); -+ if (!request_mem_region(res->start, size, dev_name(ctx->dev))) -+ return -ENOMEM; ++ cmd &= ~XDMA_CMD_AST2600_CMD_IRQ_BMC; + -+ ctx->base = ioremap(res->start, size); -+ if (!ctx->base) { -+ release_mem_region(res->start, size); -+ return -ENOMEM; ++ rc++; ++ } + } + -+ ctx->res_start = res->start; -+ ctx->res_size = size; ++ cmds[0].host_addr = op->host_addr; ++ cmds[0].pitch = (bmc_addr & XDMA_CMD_AST2600_PITCH_ADDR) | ++ FIELD_PREP(XDMA_CMD_AST2600_PITCH_HOST, pitch) | ++ FIELD_PREP(XDMA_CMD_AST2600_PITCH_BMC, pitch); ++ cmds[0].cmd = cmd | FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_NO, line_no) | ++ FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_SIZE, line_size); ++ cmds[0].reserved = 0ULL; + -+ return 0; ++ print_hex_dump_debug("xdma cmd ", DUMP_PREFIX_OFFSET, 16, 1, cmds, ++ sizeof(*cmds), true); ++ ++ return rc; +} + -+static int aspeed_xdma_probe(struct platform_device *pdev) ++static unsigned int aspeed_xdma_ast2700_set_cmd(struct aspeed_xdma *ctx, ++ struct aspeed_xdma_cmd cmds[2], ++ struct aspeed_xdma_op *op, ++ u64 bmc_addr) +{ -+ int rc, id; -+ struct aspeed_xdma *ctx; -+ struct reserved_mem *mem; -+ struct device *dev = &pdev->dev; -+ struct device_node *memory_region; -+ const void *md = of_device_get_match_data(dev); -+ bool rc_f; -+ -+ if (!md) -+ return -ENODEV; ++ unsigned int rc = 1; ++ unsigned int pitch = 1; ++ unsigned int line_no = 1; ++ unsigned int line_size = op->len; ++ u64 cmd = XDMA_CMD_AST2700_CMD_IRQ_BMC | ++ (op->direction ? XDMA_CMD_AST2700_CMD_UPSTREAM : 0); + -+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); -+ if (!ctx) -+ return -ENOMEM; ++ if (op->host_addr & 0xffffffff00000000ULL || ++ (op->host_addr + (u64)op->len) & 0xffffffff00000000ULL) ++ cmd |= XDMA_CMD_AST2700_CMD_64_EN; + -+ ctx->chip = md; -+ ctx->dev = dev; -+ platform_set_drvdata(pdev, ctx); -+ spin_lock_init(&ctx->client_lock); -+ spin_lock_init(&ctx->engine_lock); -+ INIT_WORK(&ctx->reset_work, aspeed_xdma_reset_work); -+ init_waitqueue_head(&ctx->wait); ++ dev_dbg(ctx->dev, "xdma %s ast2700: bmc[%016llx] len[%08x] host[%016llx]\n", ++ op->direction ? "upstream" : "downstream", ++ bmc_addr, op->len, op->host_addr); + -+ rc_f = of_find_property(dev->of_node, "pcie_rc", NULL) ? 1 : 0; ++ if (op->len > XDMA_CMD_AST2700_CMD_LINE_SIZE) { ++ unsigned int rem; ++ unsigned int total; + -+ rc = aspeed_xdma_iomap(ctx, pdev); -+ if (rc) { -+ dev_err(dev, "Failed to map registers.\n"); -+ goto err_nomap; -+ } ++ line_no = op->len / XDMA_CMD_AST2700_CMD_MULTILINE_SIZE; ++ total = XDMA_CMD_AST2700_CMD_MULTILINE_SIZE * line_no; ++ rem = op->len - total; ++ line_size = XDMA_CMD_AST2700_CMD_MULTILINE_SIZE; ++ pitch = line_size; + -+ ctx->irq = platform_get_irq(pdev, 0); -+ if (ctx->irq < 0) { -+ dev_err(dev, "Failed to find IRQ.\n"); -+ rc = ctx->irq; -+ goto err_noirq; -+ } ++ if (rem) { ++ u64 rbmc = bmc_addr + total; + -+ rc = request_irq(ctx->irq, aspeed_xdma_irq, 0, dev_name(dev), ctx); -+ if (rc < 0) { -+ dev_err(dev, "Failed to request IRQ %d.\n", ctx->irq); -+ goto err_noirq; -+ } ++ cmds[1].host_addr = op->host_addr + (u64)total; ++ cmds[1].pitch = ++ FIELD_PREP(XDMA_CMD_AST2700_PITCH_HOST, 1) | ++ FIELD_PREP(XDMA_CMD_AST2700_PITCH_BMC, 1); ++ cmds[1].cmd = cmd | ++ FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_NO, 1) | ++ FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_SIZE, ++ rem); ++ cmds[1].reserved = rbmc & XDMA_CMD_AST2700_BMC_ADDR; + -+ ctx->clock = clk_get(dev, NULL); -+ if (IS_ERR(ctx->clock)) { -+ dev_err(dev, "Failed to request clock.\n"); -+ rc = PTR_ERR(ctx->clock); -+ goto err_noclk; -+ } ++ print_hex_dump_debug("xdma rem ", DUMP_PREFIX_OFFSET, ++ 16, 1, &cmds[1], sizeof(*cmds), ++ true); + -+ ctx->reset = reset_control_get_exclusive(dev, NULL); -+ if (IS_ERR(ctx->reset)) { -+ dev_err(dev, "Failed to request reset control.\n"); -+ rc = PTR_ERR(ctx->reset); -+ goto err_noreset; -+ } ++ cmd &= ~XDMA_CMD_AST2700_CMD_IRQ_BMC; + -+ if (rc_f) { -+ ctx->reset_rc = reset_control_get_exclusive(dev, "root-complex"); -+ if (IS_ERR(ctx->reset_rc)) { -+ dev_dbg(dev, "Failed to request reset RC control.\n"); -+ ctx->reset_rc = NULL; ++ rc++; + } + } ++ cmds[0].host_addr = op->host_addr; ++ cmds[0].pitch = ++ FIELD_PREP(XDMA_CMD_AST2700_PITCH_HOST, pitch) | ++ FIELD_PREP(XDMA_CMD_AST2700_PITCH_BMC, pitch); ++ cmds[0].cmd = cmd | FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_NO, line_no) | ++ FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_SIZE, line_size); ++ cmds[0].reserved = bmc_addr & XDMA_CMD_AST2700_BMC_ADDR; + -+ memory_region = of_parse_phandle(dev->of_node, "memory-region", 0); -+ if (!memory_region) { -+ dev_err(dev, "Failed to find memory-region.\n"); -+ rc = -ENOMEM; -+ goto err_nomem; -+ } -+ -+ mem = of_reserved_mem_lookup(memory_region); -+ of_node_put(memory_region); -+ if (!mem) { -+ dev_err(dev, "Failed to find reserved memory.\n"); -+ rc = -ENOMEM; -+ goto err_nomem; -+ } -+ -+ ctx->mem_phys = mem->base; -+ ctx->mem_size = mem->size; -+ -+ rc = of_reserved_mem_device_init(dev); -+ if (rc) { -+ dev_err(dev, "Failed to init reserved memory.\n"); -+ goto err_nomem; -+ } ++ print_hex_dump_debug("xdma cmd ", DUMP_PREFIX_OFFSET, 16, 1, cmds, ++ sizeof(*cmds), true); + -+ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); -+ if (rc) { -+ dev_err(dev, "Failed to mask DMA.\n"); -+ goto err_nomem; -+ } ++ return rc; ++} + -+ ctx->mem_virt = dma_alloc_coherent(dev, ctx->mem_size, -+ &ctx->mem_coherent, __GFP_NOWARN); -+ if (!ctx->mem_virt) { -+ dev_err(dev, "Failed to allocate reserved memory.\n"); -+ rc = -ENOMEM; -+ goto err_nomem; -+ } ++static int aspeed_xdma_start(struct aspeed_xdma *ctx, unsigned int num_cmds, ++ struct aspeed_xdma_cmd cmds[2], bool upstream, ++ struct aspeed_xdma_client *client) ++{ ++ unsigned int i; ++ int rc = -EBUSY; ++ unsigned long flags; + -+ ctx->pool = gen_pool_create(ilog2(PAGE_SIZE), -1); -+ if (!ctx->pool) { -+ dev_err(dev, "Failed to setup genalloc pool.\n"); -+ rc = -ENOMEM; -+ goto err_nopool; -+ } ++ spin_lock_irqsave(&ctx->engine_lock, flags); ++ if (ctx->in_reset) ++ goto unlock; + -+ rc = gen_pool_add_virt(ctx->pool, (unsigned long)ctx->mem_virt, -+ ctx->mem_phys, ctx->mem_size, -1); -+ if (rc) { -+ dev_err(ctx->dev, "Failed to add memory to genalloc pool.\n"); -+ goto err_pool_scu_clk; ++ spin_lock(&ctx->client_lock); ++ if (ctx->current_client) { ++ spin_unlock(&ctx->client_lock); ++ goto unlock; + } + -+ rc = aspeed_xdma_init_scu(ctx, dev); -+ if (rc) -+ goto err_pool_scu_clk; ++ client->error = false; ++ client->in_progress = true; ++ ctx->current_client = client; ++ spin_unlock(&ctx->client_lock); + -+ rc = clk_prepare_enable(ctx->clock); -+ if (rc) { -+ dev_err(dev, "Failed to enable the clock.\n"); -+ goto err_pool_scu_clk; ++ ctx->upstream = upstream; ++ for (i = 0; i < num_cmds; ++i) { ++ trace_xdma_start(ctx, &cmds[i]); ++ /* ++ * Use memcpy_toio here to get some barriers before starting ++ * the operation. The command(s) need to be in physical memory ++ * before the XDMA engine starts. ++ */ ++ memcpy_toio(&ctx->cmdq[ctx->cmd_idx], &cmds[i], ++ sizeof(struct aspeed_xdma_cmd)); ++ ctx->cmd_idx = (ctx->cmd_idx + 1) % XDMA_NUM_CMDS; + } + -+ if (ctx->reset_rc) { -+ rc = reset_control_deassert(ctx->reset_rc); -+ if (rc) { -+ dev_err(dev, "Failed to clear the RC reset.\n"); -+ goto err_reset_rc; -+ } -+ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, -+ XDMA_ENGINE_SETUP_TIME_MAX_US); -+ } ++ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_writep, ++ ctx->cmd_idx * ctx->chip->queue_entry_size); ++ rc = 0; + -+ rc = reset_control_deassert(ctx->reset); -+ if (rc) { -+ dev_err(dev, "Failed to clear the reset.\n"); -+ goto err_reset; -+ } -+ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, -+ XDMA_ENGINE_SETUP_TIME_MAX_US); ++unlock: ++ spin_unlock_irqrestore(&ctx->engine_lock, flags); ++ return rc; ++} + -+ ctx->cmdq = gen_pool_dma_alloc(ctx->pool, XDMA_CMDQ_SIZE, -+ &ctx->cmdq_phys); -+ if (!ctx->cmdq) { -+ dev_err(ctx->dev, "Failed to genalloc cmdq.\n"); -+ rc = -ENOMEM; -+ goto err_pool; ++static void aspeed_xdma_done(struct aspeed_xdma *ctx, bool error) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ctx->client_lock, flags); ++ if (ctx->current_client) { ++ ctx->current_client->error = error; ++ ctx->current_client->in_progress = false; ++ ctx->current_client = NULL; + } ++ spin_unlock_irqrestore(&ctx->client_lock, flags); + -+ aspeed_xdma_init_eng(ctx); ++ wake_up_interruptible_all(&ctx->wait); ++} + -+ id = of_alias_get_id(dev->of_node, "xdma"); -+ if (id < 0) -+ id = 0; ++static irqreturn_t aspeed_xdma_irq(int irq, void *arg) ++{ ++ struct aspeed_xdma *ctx = arg; ++ u32 status; + -+ ctx->misc.minor = MISC_DYNAMIC_MINOR; -+ ctx->misc.fops = &aspeed_xdma_fops; -+ ctx->misc.name = kasprintf(GFP_KERNEL, "%s%d", DEVICE_NAME, id); -+ ctx->misc.parent = dev; -+ rc = misc_register(&ctx->misc); -+ if (rc) { -+ dev_err(dev, "Failed to register xdma miscdevice.\n"); -+ goto err_misc; -+ } ++ spin_lock(&ctx->engine_lock); ++ status = aspeed_xdma_readl(ctx, ctx->chip->regs.status); + -+ /* -+ * This interrupt could fire immediately so only request it once the -+ * engine and driver are initialized. -+ */ -+ ctx->pcie_irq = platform_get_irq(pdev, 1); -+ if (ctx->pcie_irq < 0) { -+ dev_warn(dev, "Failed to find PCI-E IRQ.\n"); ++ trace_xdma_irq(status); ++ ++ if (status & ctx->chip->status_bits.ds_dirty) { ++ aspeed_xdma_done(ctx, true); + } else { -+ rc = request_irq(ctx->pcie_irq, aspeed_xdma_pcie_irq, -+ IRQF_SHARED, dev_name(dev), ctx); -+ if (rc < 0) { -+ dev_warn(dev, "Failed to request PCI-E IRQ %d.\n", rc); -+ ctx->pcie_irq = -1; ++ if (status & ctx->chip->status_bits.us_comp) { ++ if (ctx->upstream) ++ aspeed_xdma_done(ctx, false); ++ } ++ ++ if (status & ctx->chip->status_bits.ds_comp) { ++ if (!ctx->upstream) ++ aspeed_xdma_done(ctx, false); + } + } + -+ kobject_init(&ctx->kobj, &aspeed_xdma_kobject_type); -+ return 0; ++ aspeed_xdma_writel(ctx, ctx->chip->regs.status, status); ++ spin_unlock(&ctx->engine_lock); + -+err_misc: -+ gen_pool_free(ctx->pool, (unsigned long)ctx->cmdq, XDMA_CMDQ_SIZE); -+err_pool: -+ reset_control_assert(ctx->reset); -+err_reset: -+ if (ctx->reset_rc) -+ reset_control_assert(ctx->reset_rc); -+err_reset_rc: -+ clk_disable_unprepare(ctx->clock); -+err_pool_scu_clk: -+ gen_pool_destroy(ctx->pool); -+err_nopool: -+ dma_free_coherent(ctx->dev, ctx->mem_size, ctx->mem_virt, -+ ctx->mem_coherent); -+err_nomem: -+ if (ctx->reset_rc) -+ reset_control_put(ctx->reset_rc); -+ reset_control_put(ctx->reset); -+err_noreset: -+ clk_put(ctx->clock); -+err_noclk: -+ free_irq(ctx->irq, ctx); -+err_noirq: -+ iounmap(ctx->base); -+ release_mem_region(ctx->res_start, ctx->res_size); -+err_nomap: -+ kfree(ctx); -+ return rc; ++ return IRQ_HANDLED; +} + -+static void aspeed_xdma_remove(struct platform_device *pdev) ++static void aspeed_xdma_reset(struct aspeed_xdma *ctx) +{ -+ struct aspeed_xdma *ctx = platform_get_drvdata(pdev); -+ -+ reset_control_assert(ctx->reset); -+ if (ctx->reset_rc) -+ reset_control_assert(ctx->reset_rc); -+ clk_disable_unprepare(ctx->clock); ++ unsigned long flags; + -+ aspeed_xdma_done(ctx, true); ++ trace_xdma_reset(ctx); + -+ misc_deregister(&ctx->misc); -+ kobject_put(&ctx->kobj); -+} ++ reset_control_assert(ctx->reset); ++ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, ++ XDMA_ENGINE_SETUP_TIME_MAX_US); ++ reset_control_deassert(ctx->reset); ++ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, ++ XDMA_ENGINE_SETUP_TIME_MAX_US); + -+static const struct aspeed_xdma_chip aspeed_ast2500_xdma_chip = { -+ .control = XDMA_AST2500_CTRL_US_COMP | XDMA_AST2500_CTRL_DS_COMP | -+ XDMA_AST2500_CTRL_DS_DIRTY | XDMA_AST2500_CTRL_DS_SIZE_256 | -+ XDMA_AST2500_CTRL_DS_TIMEOUT | XDMA_AST2500_CTRL_DS_CHECK_ID, -+ .scu_bmc_class = SCU_AST2500_BMC_CLASS_REV, -+ .scu_misc_ctrl = 0, -+ .scu_pcie_conf = SCU_AST2500_PCIE_CONF, -+ .scu_pcie_ctrl = 0, -+ .queue_entry_size = XDMA_AST2500_QUEUE_ENTRY_SIZE, -+ .regs = { -+ .bmc_cmdq_addr = XDMA_AST2500_BMC_CMDQ_ADDR, -+ .bmc_cmdq_addr_ext = 0, -+ .bmc_cmdq_endp = XDMA_AST2500_BMC_CMDQ_ENDP, -+ .bmc_cmdq_writep = XDMA_AST2500_BMC_CMDQ_WRITEP, -+ .bmc_cmdq_readp = XDMA_AST2500_BMC_CMDQ_READP, -+ .control = XDMA_AST2500_CTRL, -+ .status = XDMA_AST2500_STATUS, -+ }, -+ .status_bits = { -+ .us_comp = XDMA_AST2500_STATUS_US_COMP, -+ .ds_comp = XDMA_AST2500_STATUS_DS_COMP, -+ .ds_dirty = XDMA_AST2500_STATUS_DS_DIRTY, -+ }, -+ .set_cmd = aspeed_xdma_ast2500_set_cmd, -+}; ++ aspeed_xdma_init_eng(ctx); + -+static const struct aspeed_xdma_chip aspeed_ast2600_xdma_chip = { -+ .control = XDMA_AST2600_CTRL_US_COMP | XDMA_AST2600_CTRL_DS_COMP | -+ XDMA_AST2600_CTRL_DS_DIRTY | XDMA_AST2600_CTRL_DS_SIZE_256, -+ .scu_bmc_class = SCU_AST2600_BMC_CLASS_REV, -+ .scu_misc_ctrl = SCU_AST2600_MISC_CTRL, -+ .scu_misc_mask = SCU_AST2600_MISC_CTRL_XDMA_BMC, -+ .scu_disable_mask = DEBUG_CTRL_AST2600_XDMA_DISABLE, -+ .scu_pcie_conf = SCU_AST2600_PCIE_CONF, -+ .scu_pcie_ctrl = 0, -+ .queue_entry_size = XDMA_AST2600_QUEUE_ENTRY_SIZE, -+ .regs = { -+ .bmc_cmdq_addr = XDMA_AST2600_BMC_CMDQ_ADDR, -+ .bmc_cmdq_addr_ext = 0, -+ .bmc_cmdq_endp = XDMA_AST2600_BMC_CMDQ_ENDP, -+ .bmc_cmdq_writep = XDMA_AST2600_BMC_CMDQ_WRITEP, -+ .bmc_cmdq_readp = XDMA_AST2600_BMC_CMDQ_READP, -+ .control = XDMA_AST2600_CTRL, -+ .status = XDMA_AST2600_STATUS, -+ }, -+ .status_bits = { -+ .us_comp = XDMA_AST2600_STATUS_US_COMP, -+ .ds_comp = XDMA_AST2600_STATUS_DS_COMP, -+ .ds_dirty = XDMA_AST2600_STATUS_DS_DIRTY, -+ }, -+ .set_cmd = aspeed_xdma_ast2600_set_cmd, -+}; ++ aspeed_xdma_done(ctx, true); + -+static const struct aspeed_xdma_chip aspeed_ast2700_xdma0_chip = { -+ .control = XDMA_AST2700_CTRL_US_COMP | XDMA_AST2700_CTRL_DS_COMP | -+ XDMA_AST2700_CTRL_DS_DIRTY, -+ .scu_bmc_class = SCU_AST2700_PCIE0_BMC_CLASS_REV, -+ .scu_misc_ctrl = SCU_AST2600_MISC_CTRL, -+ .scu_misc_mask = SCU_AST2700_MISC_CTRL_XDMA_CLIENT, -+ .scu_disable_mask = DEBUG_CTRL_AST2600_XDMA_DISABLE | DEBUG_CTRL_AST2700_XDMA_DISABLE, -+ .scu_pcie_conf = SCU_AST2700_PCIE0_CONF, -+ .scu_pcie_ctrl = SCU_AST2700_PCIE0_CTRL, -+ .queue_entry_size = XDMA_AST2700_QUEUE_ENTRY_SIZE, -+ .regs = { -+ .bmc_cmdq_addr = XDMA_AST2700_BMC_CMDQ_ADDR0, -+ .bmc_cmdq_addr_ext = XDMA_AST2700_BMC_CMDQ_ADDR1, -+ .bmc_cmdq_endp = XDMA_AST2700_BMC_CMDQ_ENDP, -+ .bmc_cmdq_writep = XDMA_AST2700_BMC_CMDQ_WRITEP, -+ .bmc_cmdq_readp = XDMA_AST2700_BMC_CMDQ_READP, -+ .control = XDMA_AST2700_CTRL, -+ .status = XDMA_AST2700_STATUS, -+ }, -+ .status_bits = { -+ .us_comp = XDMA_AST2700_STATUS_US_COMP, -+ .ds_comp = XDMA_AST2700_STATUS_DS_COMP, -+ .ds_dirty = XDMA_AST2700_STATUS_DS_DIRTY, -+ }, -+ .set_cmd = aspeed_xdma_ast2700_set_cmd, -+}; ++ spin_lock_irqsave(&ctx->engine_lock, flags); ++ ctx->in_reset = false; ++ spin_unlock_irqrestore(&ctx->engine_lock, flags); + -+static const struct aspeed_xdma_chip aspeed_ast2700_xdma1_chip = { -+ .control = XDMA_AST2700_CTRL_US_COMP | XDMA_AST2700_CTRL_DS_COMP | -+ XDMA_AST2700_CTRL_DS_DIRTY, -+ .scu_bmc_class = SCU_AST2700_PCIE1_BMC_CLASS_REV, -+ .scu_misc_ctrl = SCU_AST2600_MISC_CTRL, -+ .scu_misc_mask = SCU_AST2700_MISC_CTRL_XDMA_CLIENT, -+ .scu_disable_mask = DEBUG_CTRL_AST2600_XDMA_DISABLE | DEBUG_CTRL_AST2700_XDMA_DISABLE, -+ .scu_pcie_conf = SCU_AST2700_PCIE1_CONF, -+ .scu_pcie_ctrl = SCU_AST2700_PCIE1_CTRL, -+ .queue_entry_size = XDMA_AST2700_QUEUE_ENTRY_SIZE, -+ .regs = { -+ .bmc_cmdq_addr = XDMA_AST2700_BMC_CMDQ_ADDR0, -+ .bmc_cmdq_addr_ext = XDMA_AST2700_BMC_CMDQ_ADDR1, -+ .bmc_cmdq_endp = XDMA_AST2700_BMC_CMDQ_ENDP, -+ .bmc_cmdq_writep = XDMA_AST2700_BMC_CMDQ_WRITEP, -+ .bmc_cmdq_readp = XDMA_AST2700_BMC_CMDQ_READP, -+ .control = XDMA_AST2700_CTRL, -+ .status = XDMA_AST2700_STATUS, -+ }, -+ .status_bits = { -+ .us_comp = XDMA_AST2700_STATUS_US_COMP, -+ .ds_comp = XDMA_AST2700_STATUS_DS_COMP, -+ .ds_dirty = XDMA_AST2700_STATUS_DS_DIRTY, -+ }, -+ .set_cmd = aspeed_xdma_ast2700_set_cmd, -+}; ++ wake_up_interruptible(&ctx->wait); ++} + -+static const struct of_device_id aspeed_xdma_match[] = { -+ { -+ .compatible = "aspeed,ast2500-xdma", -+ .data = &aspeed_ast2500_xdma_chip, -+ }, -+ { -+ .compatible = "aspeed,ast2600-xdma", -+ .data = &aspeed_ast2600_xdma_chip, -+ }, -+ { -+ .compatible = "aspeed,ast2700-xdma0", -+ .data = &aspeed_ast2700_xdma0_chip, -+ }, -+ { -+ .compatible = "aspeed,ast2700-xdma1", -+ .data = &aspeed_ast2700_xdma1_chip, -+ }, -+ { }, -+}; ++static void aspeed_xdma_reset_work(struct work_struct *work) ++{ ++ struct aspeed_xdma *ctx = container_of(work, struct aspeed_xdma, ++ reset_work); + -+static struct platform_driver aspeed_xdma_driver = { -+ .probe = aspeed_xdma_probe, -+ .remove = aspeed_xdma_remove, -+ .driver = { -+ .name = DEVICE_NAME, -+ .of_match_table = aspeed_xdma_match, -+ }, -+}; ++ aspeed_xdma_reset(ctx); ++} + -+module_platform_driver(aspeed_xdma_driver); ++static irqreturn_t aspeed_xdma_pcie_irq(int irq, void *arg) ++{ ++ struct aspeed_xdma *ctx = arg; + -+MODULE_AUTHOR("Eddie James"); -+MODULE_DESCRIPTION("ASPEED XDMA Engine Driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/soc/aspeed/ast2500-espi.c b/drivers/soc/aspeed/ast2500-espi.c ---- a/drivers/soc/aspeed/ast2500-espi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/ast2500-espi.c 2025-12-23 10:16:21.125032653 +0000 -@@ -0,0 +1,1739 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Copyright 2023 Aspeed Technology Inc. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++ trace_xdma_perst(ctx); + -+#include "ast2500-espi.h" ++ spin_lock(&ctx->engine_lock); ++ if (ctx->in_reset) { ++ spin_unlock(&ctx->engine_lock); ++ return IRQ_HANDLED; ++ } + -+#define DEVICE_NAME "aspeed-espi" ++ ctx->in_reset = true; ++ spin_unlock(&ctx->engine_lock); + -+#define PERIF_MCYC_UNLOCK 0xfedc756e -+#define PERIF_MCYC_ALIGN SZ_64K ++ schedule_work(&ctx->reset_work); ++ return IRQ_HANDLED; ++} + -+#define FLASH_SAFS_ALIGN SZ_16M ++static ssize_t aspeed_xdma_write(struct file *file, const char __user *buf, ++ size_t len, loff_t *offset) ++{ ++ int rc; ++ unsigned int num_cmds; ++ struct aspeed_xdma_op op; ++ struct aspeed_xdma_cmd cmds[2]; ++ struct aspeed_xdma_client *client = file->private_data; ++ struct aspeed_xdma *ctx = client->ctx; + -+struct ast2500_espi_perif { -+ struct { -+ bool enable; -+ void *virt; -+ dma_addr_t taddr; -+ uint32_t saddr; -+ uint32_t size; -+ } mcyc; ++ if (len != sizeof(op)) ++ return -EINVAL; + -+ struct { -+ bool enable; -+ void *np_tx_virt; -+ dma_addr_t np_tx_addr; -+ void *pc_tx_virt; -+ dma_addr_t pc_tx_addr; -+ void *pc_rx_virt; -+ dma_addr_t pc_rx_addr; -+ } dma; ++ if (READ_ONCE(client->in_progress)) ++ return -EBUSY; + -+ bool rx_ready; -+ wait_queue_head_t wq; ++ if (copy_from_user(&op, buf, len)) ++ return -EFAULT; + -+ spinlock_t lock; -+ struct mutex np_tx_mtx; -+ struct mutex pc_tx_mtx; -+ struct mutex pc_rx_mtx; ++ if (!op.len || op.len > client->size || ++ op.direction > ASPEED_XDMA_DIRECTION_UPSTREAM) ++ return -EINVAL; + -+ struct miscdevice mdev; -+}; ++ num_cmds = ctx->chip->set_cmd(ctx, cmds, &op, client->phys); ++ do { ++ rc = aspeed_xdma_start(ctx, num_cmds, cmds, !!op.direction, ++ client); ++ if (!rc) ++ break; + -+struct ast2500_espi_vw { -+ struct { -+ bool hw_mode; -+ uint32_t val; -+ } gpio; ++ if ((file->f_flags & O_NONBLOCK) || rc != -EBUSY) ++ return rc; + -+ struct miscdevice mdev; -+}; ++ rc = wait_event_interruptible(ctx->wait, ++ !(ctx->current_client || ++ ctx->in_reset)); ++ } while (!rc); + -+struct ast2500_espi_oob { -+ struct { -+ bool enable; -+ void *tx_virt; -+ dma_addr_t tx_addr; -+ void *rx_virt; -+ dma_addr_t rx_addr; -+ } dma; ++ if (rc) ++ return -EINTR; + -+ bool rx_ready; -+ wait_queue_head_t wq; ++ if (!(file->f_flags & O_NONBLOCK)) { ++ rc = wait_event_interruptible(ctx->wait, !client->in_progress); ++ if (rc) ++ return -EINTR; + -+ spinlock_t lock; -+ struct mutex tx_mtx; -+ struct mutex rx_mtx; ++ if (client->error) ++ return -EIO; ++ } + -+ struct miscdevice mdev; -+}; ++ return len; ++} + -+struct ast2500_espi_flash { -+ struct { -+ uint32_t mode; -+ phys_addr_t taddr; -+ uint32_t size; -+ } safs; ++static __poll_t aspeed_xdma_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ __poll_t mask = 0; ++ __poll_t req = poll_requested_events(wait); ++ struct aspeed_xdma_client *client = file->private_data; ++ struct aspeed_xdma *ctx = client->ctx; + -+ struct { -+ bool enable; -+ void *tx_virt; -+ dma_addr_t tx_addr; -+ void *rx_virt; -+ dma_addr_t rx_addr; -+ } dma; ++ if (req & (EPOLLIN | EPOLLRDNORM)) { ++ if (READ_ONCE(client->in_progress)) ++ poll_wait(file, &ctx->wait, wait); + -+ bool rx_ready; -+ wait_queue_head_t wq; ++ if (!READ_ONCE(client->in_progress)) { ++ if (READ_ONCE(client->error)) ++ mask |= EPOLLERR; ++ else ++ mask |= EPOLLIN | EPOLLRDNORM; ++ } ++ } + -+ spinlock_t lock; -+ struct mutex rx_mtx; -+ struct mutex tx_mtx; ++ if (req & (EPOLLOUT | EPOLLWRNORM)) { ++ if (READ_ONCE(ctx->current_client)) ++ poll_wait(file, &ctx->wait, wait); + -+ struct miscdevice mdev; -+}; ++ if (!READ_ONCE(ctx->current_client)) ++ mask |= EPOLLOUT | EPOLLWRNORM; ++ } + -+struct ast2500_espi { -+ struct device *dev; -+ void __iomem *regs; -+ struct clk *clk; -+ int irq; ++ if (mask) ++ aspeed_xdma_reset(ctx); + -+ struct ast2500_espi_perif perif; -+ struct ast2500_espi_vw vw; -+ struct ast2500_espi_oob oob; -+ struct ast2500_espi_flash flash; -+}; ++ return mask; ++} + -+/* peripheral channel (CH0) */ -+static long ast2500_espi_perif_pc_get_rx(struct file *fp, -+ struct ast2500_espi_perif *perif, -+ struct aspeed_espi_ioc *ioc) ++static long aspeed_xdma_ioctl(struct file *file, unsigned int cmd, ++ unsigned long param) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2500_espi *espi; -+ struct espi_comm_hdr *hdr; + unsigned long flags; -+ uint32_t pkt_len; -+ uint8_t *pkt; -+ int i, rc; -+ -+ espi = container_of(perif, struct ast2500_espi, perif); -+ -+ if (fp->f_flags & O_NONBLOCK) { -+ if (!mutex_trylock(&perif->pc_rx_mtx)) -+ return -EAGAIN; -+ -+ if (!perif->rx_ready) { -+ rc = -ENODATA; -+ goto unlock_mtx_n_out; -+ } -+ } else { -+ mutex_lock(&perif->pc_rx_mtx); ++ struct aspeed_xdma_client *client = file->private_data; ++ struct aspeed_xdma *ctx = client->ctx; + -+ if (!perif->rx_ready) { -+ rc = wait_event_interruptible(perif->wq, perif->rx_ready); -+ if (rc == -ERESTARTSYS) { -+ rc = -EINTR; -+ goto unlock_mtx_n_out; -+ } ++ switch (cmd) { ++ case ASPEED_XDMA_IOCTL_RESET: ++ spin_lock_irqsave(&ctx->engine_lock, flags); ++ if (ctx->in_reset) { ++ spin_unlock_irqrestore(&ctx->engine_lock, flags); ++ return 0; + } -+ } -+ -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ reg = readl(espi->regs + ESPI_PERIF_PC_RX_CTRL); -+ cyc = FIELD_GET(ESPI_PERIF_PC_RX_CTRL_CYC, reg); -+ tag = FIELD_GET(ESPI_PERIF_PC_RX_CTRL_TAG, reg); -+ len = FIELD_GET(ESPI_PERIF_PC_RX_CTRL_LEN, reg); -+ -+ /* -+ * calculate the length of the rest part of the -+ * eSPI packet to be read from HW and copied to -+ * user space. -+ */ -+ switch (cyc) { -+ case ESPI_PERIF_MSG: -+ pkt_len = sizeof(struct espi_perif_msg); -+ break; -+ case ESPI_PERIF_MSG_D: -+ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + -+ sizeof(struct espi_perif_msg); -+ break; -+ case ESPI_PERIF_SUC_CMPLT_D_MIDDLE: -+ case ESPI_PERIF_SUC_CMPLT_D_FIRST: -+ case ESPI_PERIF_SUC_CMPLT_D_LAST: -+ case ESPI_PERIF_SUC_CMPLT_D_ONLY: -+ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + -+ sizeof(struct espi_perif_cmplt); -+ break; -+ case ESPI_PERIF_SUC_CMPLT: -+ case ESPI_PERIF_UNSUC_CMPLT: -+ pkt_len = sizeof(struct espi_perif_cmplt); ++ ++ ctx->in_reset = true; ++ spin_unlock_irqrestore(&ctx->engine_lock, flags); ++ ++ if (READ_ONCE(ctx->current_client)) ++ dev_warn(ctx->dev, ++ "User reset with transfer in progress.\n"); ++ ++ aspeed_xdma_reset(ctx); + break; + default: -+ rc = -EFAULT; -+ goto unlock_mtx_n_out; ++ return -EINVAL; + } + -+ if (ioc->pkt_len < pkt_len) { -+ rc = -EINVAL; -+ goto unlock_mtx_n_out; -+ } ++ return 0; ++} + -+ pkt = vmalloc(pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_mtx_n_out; -+ } ++static void aspeed_xdma_vma_close(struct vm_area_struct *vma) ++{ ++ int rc; ++ struct aspeed_xdma_client *client = vma->vm_private_data; + -+ hdr = (struct espi_comm_hdr *)pkt; -+ hdr->cyc = cyc; -+ hdr->tag = tag; -+ hdr->len_h = len >> 8; -+ hdr->len_l = len & 0xff; ++ rc = wait_event_interruptible(client->ctx->wait, !client->in_progress); ++ if (rc) ++ return; + -+ if (perif->dma.enable) { -+ memcpy(hdr + 1, perif->dma.pc_rx_virt, pkt_len - sizeof(*hdr)); -+ } else { -+ for (i = sizeof(*hdr); i < pkt_len; ++i) -+ reg = readl(espi->regs + ESPI_PERIF_PC_RX_DATA) & 0xff; -+ } ++ gen_pool_free(client->ctx->pool, (unsigned long)client->virt, ++ client->size); ++ trace_xdma_unmap(client); + -+ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } ++ client->virt = NULL; ++ client->phys = 0; ++ client->size = 0; ++} + -+ spin_lock_irqsave(&perif->lock, flags); ++static const struct vm_operations_struct aspeed_xdma_vm_ops = { ++ .close = aspeed_xdma_vma_close, ++}; + -+ writel(ESPI_PERIF_PC_RX_CTRL_SERV_PEND, espi->regs + ESPI_PERIF_PC_RX_CTRL); -+ perif->rx_ready = 0; ++static int aspeed_xdma_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int rc; ++ struct aspeed_xdma_client *client = file->private_data; ++ struct aspeed_xdma *ctx = client->ctx; + -+ spin_unlock_irqrestore(&perif->lock, flags); ++ /* restrict file to one mapping */ ++ if (client->size) ++ return -EBUSY; + -+ rc = 0; ++ client->size = vma->vm_end - vma->vm_start; ++ client->virt = gen_pool_dma_alloc(ctx->pool, client->size, ++ &client->phys); ++ if (!client->virt) { ++ trace_xdma_mmap_error(client, 0UL); ++ client->phys = 0; ++ client->size = 0; ++ return -ENOMEM; ++ } + -+free_n_out: -+ vfree(pkt); ++ vma->vm_pgoff = (client->phys - ctx->mem_phys) >> PAGE_SHIFT; ++ vma->vm_ops = &aspeed_xdma_vm_ops; ++ vma->vm_private_data = client; ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + -+unlock_mtx_n_out: -+ mutex_unlock(&perif->pc_rx_mtx); ++ rc = io_remap_pfn_range(vma, vma->vm_start, client->phys >> PAGE_SHIFT, ++ client->size, vma->vm_page_prot); ++ if (rc) { ++ dev_warn(ctx->dev, "mmap err: v[%08lx] to p[%pa], s[%08x]\n", ++ vma->vm_start, &client->phys, client->size); + -+ return rc; ++ gen_pool_free(ctx->pool, (unsigned long)client->virt, ++ client->size); ++ ++ trace_xdma_mmap_error(client, vma->vm_start); ++ client->virt = NULL; ++ client->phys = 0; ++ client->size = 0; ++ return rc; ++ } ++ ++ trace_xdma_mmap(client); ++ dev_dbg(ctx->dev, "mmap: v[%08lx] to p[%pa], s[%08x]\n", ++ vma->vm_start, &client->phys, client->size); ++ ++ return 0; +} + -+static long ast2500_espi_perif_pc_put_tx(struct file *fp, -+ struct ast2500_espi_perif *perif, -+ struct aspeed_espi_ioc *ioc) ++static int aspeed_xdma_open(struct inode *inode, struct file *file) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2500_espi *espi; -+ struct espi_comm_hdr *hdr; -+ uint8_t *pkt; -+ int i, rc; ++ struct miscdevice *misc = file->private_data; ++ struct aspeed_xdma *ctx = container_of(misc, struct aspeed_xdma, misc); ++ struct aspeed_xdma_client *client = kzalloc(sizeof(*client), ++ GFP_KERNEL); + -+ espi = container_of(perif, struct ast2500_espi, perif); ++ if (!client) ++ return -ENOMEM; + -+ if (!mutex_trylock(&perif->pc_tx_mtx)) -+ return -EAGAIN; ++ kobject_get(&ctx->kobj); ++ client->ctx = ctx; ++ file->private_data = client; ++ return 0; ++} + -+ reg = readl(espi->regs + ESPI_PERIF_PC_TX_CTRL); -+ if (reg & ESPI_PERIF_PC_TX_CTRL_TRIG_PEND) { -+ rc = -EBUSY; -+ goto unlock_n_out; -+ } ++static int aspeed_xdma_release(struct inode *inode, struct file *file) ++{ ++ bool reset = false; ++ unsigned long flags; ++ struct aspeed_xdma_client *client = file->private_data; ++ struct aspeed_xdma *ctx = client->ctx; + -+ pkt = vmalloc(ioc->pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_n_out; ++ spin_lock_irqsave(&ctx->client_lock, flags); ++ if (client == ctx->current_client) { ++ spin_lock(&ctx->engine_lock); ++ if (ctx->in_reset) { ++ ctx->current_client = NULL; ++ } else { ++ ctx->in_reset = true; ++ reset = true; ++ } ++ spin_unlock(&ctx->engine_lock); + } ++ spin_unlock_irqrestore(&ctx->client_lock, flags); + -+ hdr = (struct espi_comm_hdr *)pkt; -+ -+ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } ++ if (reset) ++ aspeed_xdma_reset(ctx); + -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ if (perif->dma.enable) { -+ memcpy(perif->dma.pc_tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); -+ dma_wmb(); -+ } else { -+ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_PERIF_PC_TX_DATA); ++ if (client->virt) { ++ gen_pool_free(ctx->pool, (unsigned long)client->virt, ++ client->size); ++ trace_xdma_unmap(client); + } + -+ cyc = hdr->cyc; -+ tag = hdr->tag; -+ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); ++ kfree(client); ++ kobject_put(&ctx->kobj); ++ return 0; ++} + -+ reg = FIELD_PREP(ESPI_PERIF_PC_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_PERIF_PC_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_PERIF_PC_TX_CTRL_LEN, len) -+ | ESPI_PERIF_PC_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_PERIF_PC_TX_CTRL); ++static const struct file_operations aspeed_xdma_fops = { ++ .owner = THIS_MODULE, ++ .write = aspeed_xdma_write, ++ .poll = aspeed_xdma_poll, ++ .unlocked_ioctl = aspeed_xdma_ioctl, ++ .mmap = aspeed_xdma_mmap, ++ .open = aspeed_xdma_open, ++ .release = aspeed_xdma_release, ++}; + -+ rc = 0; ++static int aspeed_xdma_init_scu(struct aspeed_xdma *ctx, struct device *dev) ++{ ++ struct regmap *scu = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "aspeed,scu"); + -+free_n_out: -+ vfree(pkt); ++ if (!IS_ERR(scu)) { ++ u32 selection; ++ bool pcie_device_bmc = true; ++ const u32 bmc = SCU_PCIE_CONF_BMC_EN | ++ SCU_PCIE_CONF_BMC_EN_MSI | SCU_PCIE_CONF_BMC_EN_IRQ | ++ SCU_PCIE_CONF_BMC_EN_DMA; ++ const u32 vga = SCU_PCIE_CONF_VGA_EN | ++ SCU_PCIE_CONF_VGA_EN_MSI | SCU_PCIE_CONF_VGA_EN_IRQ | ++ SCU_PCIE_CONF_VGA_EN_DMA; ++ const char *pcie = NULL; + -+unlock_n_out: -+ mutex_unlock(&perif->pc_tx_mtx); ++ if (!of_property_read_string(dev->of_node, ++ "aspeed,pcie-device", &pcie)) { ++ if (!strcmp(pcie, "vga")) { ++ pcie_device_bmc = false; ++ } else if (strcmp(pcie, "bmc")) { ++ dev_err(dev, ++ "Invalid pcie-device property %s.\n", ++ pcie); ++ return -EINVAL; ++ } ++ } + -+ return rc; -+} ++ if (pcie_device_bmc) { ++ selection = bmc; ++ regmap_update_bits(scu, ctx->chip->scu_bmc_class, ++ SCU_BMC_CLASS_REV_MASK, ++ SCU_BMC_CLASS_REV_XDMA); ++ } else { ++ selection = vga; ++ } + -+static long ast2500_espi_perif_np_put_tx(struct file *fp, -+ struct ast2500_espi_perif *perif, -+ struct aspeed_espi_ioc *ioc) -+{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2500_espi *espi; -+ struct espi_comm_hdr *hdr; -+ uint8_t *pkt; -+ int i, rc; ++ regmap_update_bits(scu, ctx->chip->scu_pcie_conf, bmc | vga, ++ selection); + -+ espi = container_of(perif, struct ast2500_espi, perif); ++ if (ctx->chip->scu_misc_ctrl) { ++ regmap_update_bits(scu, ctx->chip->scu_misc_ctrl, ++ ctx->chip->scu_misc_mask, ++ ctx->chip->scu_misc_mask); + -+ if (!mutex_trylock(&perif->np_tx_mtx)) -+ return -EAGAIN; ++ regmap_update_bits(scu, SCU_AST2600_DEBUG_CTRL, ++ ctx->chip->scu_disable_mask, 0); ++ } + -+ reg = readl(espi->regs + ESPI_PERIF_NP_TX_CTRL); -+ if (reg & ESPI_PERIF_NP_TX_CTRL_TRIG_PEND) { -+ rc = -EBUSY; -+ goto unlock_n_out; ++ if (ctx->chip->scu_pcie_ctrl) { ++ regmap_update_bits(scu, ctx->chip->scu_pcie_ctrl, ++ SCU_AST2700_PCIE_CTRL_DMA_EN, ++ SCU_AST2700_PCIE_CTRL_DMA_EN); ++ } ++ } else { ++ dev_warn(dev, "Unable to configure PCIe: %ld; continuing.\n", ++ PTR_ERR(scu)); + } + -+ pkt = vmalloc(ioc->pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_n_out; -+ } ++ return 0; ++} + -+ hdr = (struct espi_comm_hdr *)pkt; ++static void aspeed_xdma_kobject_release(struct kobject *kobj) ++{ ++ struct aspeed_xdma *ctx = container_of(kobj, struct aspeed_xdma, kobj); + -+ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } ++ if (ctx->pcie_irq >= 0) ++ free_irq(ctx->pcie_irq, ctx); + -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ if (perif->dma.enable) { -+ memcpy(perif->dma.np_tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); -+ dma_wmb(); -+ } else { -+ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_PERIF_NP_TX_DATA); -+ } ++ gen_pool_free(ctx->pool, (unsigned long)ctx->cmdq, XDMA_CMDQ_SIZE); + -+ cyc = hdr->cyc; -+ tag = hdr->tag; -+ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); ++ gen_pool_destroy(ctx->pool); + -+ reg = FIELD_PREP(ESPI_PERIF_NP_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_PERIF_NP_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_PERIF_NP_TX_CTRL_LEN, len) -+ | ESPI_PERIF_NP_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_PERIF_NP_TX_CTRL); ++ dma_free_coherent(ctx->dev, ctx->mem_size, ctx->mem_virt, ++ ctx->mem_coherent); + -+ rc = 0; ++ if (ctx->reset_rc) ++ reset_control_put(ctx->reset_rc); ++ reset_control_put(ctx->reset); + -+free_n_out: -+ vfree(pkt); ++ clk_put(ctx->clock); + -+unlock_n_out: -+ mutex_unlock(&perif->np_tx_mtx); ++ free_irq(ctx->irq, ctx); + -+ return rc; ++ iounmap(ctx->base); ++ release_mem_region(ctx->res_start, ctx->res_size); ++ ++ kfree(ctx); +} + -+static long ast2500_espi_perif_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static const struct kobj_type aspeed_xdma_kobject_type = { ++ .release = aspeed_xdma_kobject_release, ++}; ++ ++static int aspeed_xdma_iomap(struct aspeed_xdma *ctx, ++ struct platform_device *pdev) +{ -+ struct ast2500_espi_perif *perif; -+ struct aspeed_espi_ioc ioc; ++ resource_size_t size; ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + -+ perif = container_of(fp->private_data, struct ast2500_espi_perif, mdev); ++ if (!res) ++ return -ENOMEM; + -+ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) -+ return -EFAULT; ++ size = resource_size(res); ++ if (!request_mem_region(res->start, size, dev_name(ctx->dev))) ++ return -ENOMEM; + -+ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) -+ return -EINVAL; ++ ctx->base = ioremap(res->start, size); ++ if (!ctx->base) { ++ release_mem_region(res->start, size); ++ return -ENOMEM; ++ } + -+ switch (cmd) { -+ case ASPEED_ESPI_PERIF_PC_GET_RX: -+ return ast2500_espi_perif_pc_get_rx(fp, perif, &ioc); -+ case ASPEED_ESPI_PERIF_PC_PUT_TX: -+ return ast2500_espi_perif_pc_put_tx(fp, perif, &ioc); -+ case ASPEED_ESPI_PERIF_NP_PUT_TX: -+ return ast2500_espi_perif_np_put_tx(fp, perif, &ioc); -+ default: -+ break; -+ }; ++ ctx->res_start = res->start; ++ ctx->res_size = size; + -+ return -EINVAL; ++ return 0; +} + -+static int ast2500_espi_perif_mmap(struct file *fp, struct vm_area_struct *vma) ++static int aspeed_xdma_probe(struct platform_device *pdev) +{ -+ struct ast2500_espi_perif *perif; -+ unsigned long vm_size; -+ pgprot_t vm_prot; ++ int rc, id; ++ struct aspeed_xdma *ctx; ++ struct reserved_mem *mem; ++ struct device *dev = &pdev->dev; ++ struct device_node *memory_region; ++ const void *md = of_device_get_match_data(dev); ++ bool rc_f; + -+ perif = container_of(fp->private_data, struct ast2500_espi_perif, mdev); -+ if (!perif->mcyc.enable) -+ return -EPERM; ++ if (!md) ++ return -ENODEV; + -+ vm_size = vma->vm_end - vma->vm_start; -+ vm_prot = vma->vm_page_prot; ++ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return -ENOMEM; + -+ if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > perif->mcyc.size) -+ return -EINVAL; ++ ctx->chip = md; ++ ctx->dev = dev; ++ platform_set_drvdata(pdev, ctx); ++ spin_lock_init(&ctx->client_lock); ++ spin_lock_init(&ctx->engine_lock); ++ INIT_WORK(&ctx->reset_work, aspeed_xdma_reset_work); ++ init_waitqueue_head(&ctx->wait); + -+ vm_prot = pgprot_noncached(vm_prot); ++ rc_f = of_find_property(dev->of_node, "pcie_rc", NULL) ? 1 : 0; + -+ if (remap_pfn_range(vma, vma->vm_start, -+ (perif->mcyc.taddr >> PAGE_SHIFT) + vma->vm_pgoff, -+ vm_size, vm_prot)) -+ return -EAGAIN; ++ rc = aspeed_xdma_iomap(ctx, pdev); ++ if (rc) { ++ dev_err(dev, "Failed to map registers.\n"); ++ goto err_nomap; ++ } + -+ return 0; -+} ++ ctx->irq = platform_get_irq(pdev, 0); ++ if (ctx->irq < 0) { ++ dev_err(dev, "Failed to find IRQ.\n"); ++ rc = ctx->irq; ++ goto err_noirq; ++ } + -+static const struct file_operations ast2500_espi_perif_fops = { -+ .owner = THIS_MODULE, -+ .mmap = ast2500_espi_perif_mmap, -+ .unlocked_ioctl = ast2500_espi_perif_ioctl, -+}; ++ rc = request_irq(ctx->irq, aspeed_xdma_irq, 0, dev_name(dev), ctx); ++ if (rc < 0) { ++ dev_err(dev, "Failed to request IRQ %d.\n", ctx->irq); ++ goto err_noirq; ++ } + -+static void ast2500_espi_perif_isr(struct ast2500_espi *espi) -+{ -+ struct ast2500_espi_perif *perif; -+ unsigned long flags; -+ uint32_t sts; ++ ctx->clock = clk_get(dev, NULL); ++ if (IS_ERR(ctx->clock)) { ++ dev_err(dev, "Failed to request clock.\n"); ++ rc = PTR_ERR(ctx->clock); ++ goto err_noclk; ++ } + -+ perif = &espi->perif; ++ ctx->reset = reset_control_get_exclusive(dev, NULL); ++ if (IS_ERR(ctx->reset)) { ++ dev_err(dev, "Failed to request reset control.\n"); ++ rc = PTR_ERR(ctx->reset); ++ goto err_noreset; ++ } + -+ sts = readl(espi->regs + ESPI_INT_STS); ++ if (rc_f) { ++ ctx->reset_rc = reset_control_get_exclusive(dev, "root-complex"); ++ if (IS_ERR(ctx->reset_rc)) { ++ dev_dbg(dev, "Failed to request reset RC control.\n"); ++ ctx->reset_rc = NULL; ++ } ++ } + -+ if (sts & ESPI_INT_STS_PERIF_PC_RX_CMPLT) { -+ writel(ESPI_INT_STS_PERIF_PC_RX_CMPLT, espi->regs + ESPI_INT_STS); ++ memory_region = of_parse_phandle(dev->of_node, "memory-region", 0); ++ if (!memory_region) { ++ dev_err(dev, "Failed to find memory-region.\n"); ++ rc = -ENOMEM; ++ goto err_nomem; ++ } + -+ spin_lock_irqsave(&perif->lock, flags); -+ perif->rx_ready = true; -+ spin_unlock_irqrestore(&perif->lock, flags); ++ mem = of_reserved_mem_lookup(memory_region); ++ of_node_put(memory_region); ++ if (!mem) { ++ dev_err(dev, "Failed to find reserved memory.\n"); ++ rc = -ENOMEM; ++ goto err_nomem; ++ } + -+ wake_up_interruptible(&perif->wq); ++ ctx->mem_phys = mem->base; ++ ctx->mem_size = mem->size; ++ ++ rc = of_reserved_mem_device_init(dev); ++ if (rc) { ++ dev_err(dev, "Failed to init reserved memory.\n"); ++ goto err_nomem; + } -+} + -+static void ast2500_espi_perif_reset(struct ast2500_espi *espi) -+{ -+ struct ast2500_espi_perif *perif; -+ struct device *dev; -+ uint32_t reg, mask; ++ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); ++ if (rc) { ++ dev_err(dev, "Failed to mask DMA.\n"); ++ goto err_nomem; ++ } + -+ dev = espi->dev; ++ ctx->mem_virt = dma_alloc_coherent(dev, ctx->mem_size, ++ &ctx->mem_coherent, __GFP_NOWARN); ++ if (!ctx->mem_virt) { ++ dev_err(dev, "Failed to allocate reserved memory.\n"); ++ rc = -ENOMEM; ++ goto err_nomem; ++ } + -+ perif = &espi->perif; ++ ctx->pool = gen_pool_create(ilog2(PAGE_SIZE), -1); ++ if (!ctx->pool) { ++ dev_err(dev, "Failed to setup genalloc pool.\n"); ++ rc = -ENOMEM; ++ goto err_nopool; ++ } + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_PERIF); -+ writel(reg, espi->regs + ESPI_INT_EN); -+ writel(ESPI_INT_STS_PERIF, espi->regs + ESPI_INT_STS); ++ rc = gen_pool_add_virt(ctx->pool, (unsigned long)ctx->mem_virt, ++ ctx->mem_phys, ctx->mem_size, -1); ++ if (rc) { ++ dev_err(ctx->dev, "Failed to add memory to genalloc pool.\n"); ++ goto err_pool_scu_clk; ++ } ++ ++ rc = aspeed_xdma_init_scu(ctx, dev); ++ if (rc) ++ goto err_pool_scu_clk; ++ ++ rc = clk_prepare_enable(ctx->clock); ++ if (rc) { ++ dev_err(dev, "Failed to enable the clock.\n"); ++ goto err_pool_scu_clk; ++ } ++ ++ if (ctx->reset_rc) { ++ rc = reset_control_deassert(ctx->reset_rc); ++ if (rc) { ++ dev_err(dev, "Failed to clear the RC reset.\n"); ++ goto err_reset_rc; ++ } ++ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, ++ XDMA_ENGINE_SETUP_TIME_MAX_US); ++ } + -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_PERIF_NP_TX_SW_RST -+ | ESPI_CTRL_PERIF_NP_RX_SW_RST -+ | ESPI_CTRL_PERIF_PC_TX_SW_RST -+ | ESPI_CTRL_PERIF_PC_RX_SW_RST -+ | ESPI_CTRL_PERIF_NP_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_RX_DMA_EN -+ | ESPI_CTRL_PERIF_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); ++ rc = reset_control_deassert(ctx->reset); ++ if (rc) { ++ dev_err(dev, "Failed to clear the reset.\n"); ++ goto err_reset; ++ } ++ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, ++ XDMA_ENGINE_SETUP_TIME_MAX_US); + -+ udelay(1); ++ ctx->cmdq = gen_pool_dma_alloc(ctx->pool, XDMA_CMDQ_SIZE, ++ &ctx->cmdq_phys); ++ if (!ctx->cmdq) { ++ dev_err(ctx->dev, "Failed to genalloc cmdq.\n"); ++ rc = -ENOMEM; ++ goto err_pool; ++ } + -+ reg |= (ESPI_CTRL_PERIF_NP_TX_SW_RST -+ | ESPI_CTRL_PERIF_NP_RX_SW_RST -+ | ESPI_CTRL_PERIF_PC_TX_SW_RST -+ | ESPI_CTRL_PERIF_PC_RX_SW_RST); -+ writel(reg, espi->regs + ESPI_CTRL); ++ aspeed_xdma_init_eng(ctx); + -+ if (perif->mcyc.enable) { -+ mask = ~(perif->mcyc.size - 1); -+ writel(PERIF_MCYC_UNLOCK, espi->regs + ESPI_PERIF_MCYC_MASK); -+ writel(mask, espi->regs + ESPI_PERIF_MCYC_MASK); ++ id = of_alias_get_id(dev->of_node, "xdma"); ++ if (id < 0) ++ id = 0; + -+ writel(perif->mcyc.saddr, espi->regs + ESPI_PERIF_MCYC_SADDR); -+ writel(perif->mcyc.taddr, espi->regs + ESPI_PERIF_MCYC_TADDR); ++ ctx->misc.minor = MISC_DYNAMIC_MINOR; ++ ctx->misc.fops = &aspeed_xdma_fops; ++ ctx->misc.name = kasprintf(GFP_KERNEL, "%s%d", DEVICE_NAME, id); ++ ctx->misc.parent = dev; ++ rc = misc_register(&ctx->misc); ++ if (rc) { ++ dev_err(dev, "Failed to register xdma miscdevice.\n"); ++ goto err_misc; + } + -+ if (perif->dma.enable) { -+ writel(perif->dma.np_tx_addr, espi->regs + ESPI_PERIF_NP_TX_DMA); -+ writel(perif->dma.pc_tx_addr, espi->regs + ESPI_PERIF_PC_TX_DMA); -+ writel(perif->dma.pc_rx_addr, espi->regs + ESPI_PERIF_PC_RX_DMA); -+ -+ reg = readl(espi->regs + ESPI_CTRL) -+ | ESPI_CTRL_PERIF_NP_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_RX_DMA_EN; -+ writel(reg, espi->regs + ESPI_CTRL); ++ /* ++ * This interrupt could fire immediately so only request it once the ++ * engine and driver are initialized. ++ */ ++ ctx->pcie_irq = platform_get_irq(pdev, 1); ++ if (ctx->pcie_irq < 0) { ++ dev_warn(dev, "Failed to find PCI-E IRQ.\n"); ++ } else { ++ rc = request_irq(ctx->pcie_irq, aspeed_xdma_pcie_irq, ++ IRQF_SHARED, dev_name(dev), ctx); ++ if (rc < 0) { ++ dev_warn(dev, "Failed to request PCI-E IRQ %d.\n", rc); ++ ctx->pcie_irq = -1; ++ } + } + -+ reg = readl(espi->regs + ESPI_INT_EN) | ESPI_INT_EN_PERIF_PC_RX_CMPLT; -+ writel(reg, espi->regs + ESPI_INT_EN); ++ kobject_init(&ctx->kobj, &aspeed_xdma_kobject_type); ++ return 0; + -+ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_PERIF_SW_RDY; -+ writel(reg, espi->regs + ESPI_CTRL); ++err_misc: ++ gen_pool_free(ctx->pool, (unsigned long)ctx->cmdq, XDMA_CMDQ_SIZE); ++err_pool: ++ reset_control_assert(ctx->reset); ++err_reset: ++ if (ctx->reset_rc) ++ reset_control_assert(ctx->reset_rc); ++err_reset_rc: ++ clk_disable_unprepare(ctx->clock); ++err_pool_scu_clk: ++ gen_pool_destroy(ctx->pool); ++err_nopool: ++ dma_free_coherent(ctx->dev, ctx->mem_size, ctx->mem_virt, ++ ctx->mem_coherent); ++err_nomem: ++ if (ctx->reset_rc) ++ reset_control_put(ctx->reset_rc); ++ reset_control_put(ctx->reset); ++err_noreset: ++ clk_put(ctx->clock); ++err_noclk: ++ free_irq(ctx->irq, ctx); ++err_noirq: ++ iounmap(ctx->base); ++ release_mem_region(ctx->res_start, ctx->res_size); ++err_nomap: ++ kfree(ctx); ++ return rc; +} + -+static int ast2500_espi_perif_probe(struct ast2500_espi *espi) ++static void aspeed_xdma_remove(struct platform_device *pdev) +{ -+ struct ast2500_espi_perif *perif; -+ struct device *dev; -+ int rc; -+ -+ dev = espi->dev; ++ struct aspeed_xdma *ctx = platform_get_drvdata(pdev); + -+ perif = &espi->perif; ++ reset_control_assert(ctx->reset); ++ if (ctx->reset_rc) ++ reset_control_assert(ctx->reset_rc); ++ clk_disable_unprepare(ctx->clock); + -+ init_waitqueue_head(&perif->wq); ++ aspeed_xdma_done(ctx, true); + -+ spin_lock_init(&perif->lock); ++ misc_deregister(&ctx->misc); ++ kobject_put(&ctx->kobj); ++} + -+ mutex_init(&perif->np_tx_mtx); -+ mutex_init(&perif->pc_tx_mtx); -+ mutex_init(&perif->pc_rx_mtx); ++static const struct aspeed_xdma_chip aspeed_ast2500_xdma_chip = { ++ .control = XDMA_AST2500_CTRL_US_COMP | XDMA_AST2500_CTRL_DS_COMP | ++ XDMA_AST2500_CTRL_DS_DIRTY | XDMA_AST2500_CTRL_DS_SIZE_256 | ++ XDMA_AST2500_CTRL_DS_TIMEOUT | XDMA_AST2500_CTRL_DS_CHECK_ID, ++ .scu_bmc_class = SCU_AST2500_BMC_CLASS_REV, ++ .scu_misc_ctrl = 0, ++ .scu_pcie_conf = SCU_AST2500_PCIE_CONF, ++ .scu_pcie_ctrl = 0, ++ .queue_entry_size = XDMA_AST2500_QUEUE_ENTRY_SIZE, ++ .regs = { ++ .bmc_cmdq_addr = XDMA_AST2500_BMC_CMDQ_ADDR, ++ .bmc_cmdq_addr_ext = 0, ++ .bmc_cmdq_endp = XDMA_AST2500_BMC_CMDQ_ENDP, ++ .bmc_cmdq_writep = XDMA_AST2500_BMC_CMDQ_WRITEP, ++ .bmc_cmdq_readp = XDMA_AST2500_BMC_CMDQ_READP, ++ .control = XDMA_AST2500_CTRL, ++ .status = XDMA_AST2500_STATUS, ++ }, ++ .status_bits = { ++ .us_comp = XDMA_AST2500_STATUS_US_COMP, ++ .ds_comp = XDMA_AST2500_STATUS_DS_COMP, ++ .ds_dirty = XDMA_AST2500_STATUS_DS_DIRTY, ++ }, ++ .set_cmd = aspeed_xdma_ast2500_set_cmd, ++}; + -+ perif->mcyc.enable = of_property_read_bool(dev->of_node, "perif-mcyc-enable"); -+ if (perif->mcyc.enable) { -+ rc = of_property_read_u32(dev->of_node, "perif-mcyc-src-addr", &perif->mcyc.saddr); -+ if (rc || !IS_ALIGNED(perif->mcyc.saddr, PERIF_MCYC_ALIGN)) { -+ dev_err(dev, "cannot get 64KB-aligned memory cycle host address\n"); -+ return -ENODEV; -+ } ++static const struct aspeed_xdma_chip aspeed_ast2600_xdma_chip = { ++ .control = XDMA_AST2600_CTRL_US_COMP | XDMA_AST2600_CTRL_DS_COMP | ++ XDMA_AST2600_CTRL_DS_DIRTY | XDMA_AST2600_CTRL_DS_SIZE_256, ++ .scu_bmc_class = SCU_AST2600_BMC_CLASS_REV, ++ .scu_misc_ctrl = SCU_AST2600_MISC_CTRL, ++ .scu_misc_mask = SCU_AST2600_MISC_CTRL_XDMA_BMC, ++ .scu_disable_mask = DEBUG_CTRL_AST2600_XDMA_DISABLE, ++ .scu_pcie_conf = SCU_AST2600_PCIE_CONF, ++ .scu_pcie_ctrl = 0, ++ .queue_entry_size = XDMA_AST2600_QUEUE_ENTRY_SIZE, ++ .regs = { ++ .bmc_cmdq_addr = XDMA_AST2600_BMC_CMDQ_ADDR, ++ .bmc_cmdq_addr_ext = 0, ++ .bmc_cmdq_endp = XDMA_AST2600_BMC_CMDQ_ENDP, ++ .bmc_cmdq_writep = XDMA_AST2600_BMC_CMDQ_WRITEP, ++ .bmc_cmdq_readp = XDMA_AST2600_BMC_CMDQ_READP, ++ .control = XDMA_AST2600_CTRL, ++ .status = XDMA_AST2600_STATUS, ++ }, ++ .status_bits = { ++ .us_comp = XDMA_AST2600_STATUS_US_COMP, ++ .ds_comp = XDMA_AST2600_STATUS_DS_COMP, ++ .ds_dirty = XDMA_AST2600_STATUS_DS_DIRTY, ++ }, ++ .set_cmd = aspeed_xdma_ast2600_set_cmd, ++}; + -+ rc = of_property_read_u32(dev->of_node, "perif-mcyc-size", &perif->mcyc.size); -+ if (rc || !IS_ALIGNED(perif->mcyc.size, PERIF_MCYC_ALIGN)) { -+ dev_err(dev, "cannot get 64KB-aligned memory cycle size\n"); -+ return -EINVAL; -+ } ++static const struct aspeed_xdma_chip aspeed_ast2700_xdma0_chip = { ++ .control = XDMA_AST2700_CTRL_US_COMP | XDMA_AST2700_CTRL_DS_COMP | ++ XDMA_AST2700_CTRL_DS_DIRTY, ++ .scu_bmc_class = SCU_AST2700_PCIE0_BMC_CLASS_REV, ++ .scu_misc_ctrl = SCU_AST2600_MISC_CTRL, ++ .scu_misc_mask = SCU_AST2700_MISC_CTRL_XDMA_CLIENT, ++ .scu_disable_mask = DEBUG_CTRL_AST2600_XDMA_DISABLE | DEBUG_CTRL_AST2700_XDMA_DISABLE, ++ .scu_pcie_conf = SCU_AST2700_PCIE0_CONF, ++ .scu_pcie_ctrl = SCU_AST2700_PCIE0_CTRL, ++ .queue_entry_size = XDMA_AST2700_QUEUE_ENTRY_SIZE, ++ .regs = { ++ .bmc_cmdq_addr = XDMA_AST2700_BMC_CMDQ_ADDR0, ++ .bmc_cmdq_addr_ext = XDMA_AST2700_BMC_CMDQ_ADDR1, ++ .bmc_cmdq_endp = XDMA_AST2700_BMC_CMDQ_ENDP, ++ .bmc_cmdq_writep = XDMA_AST2700_BMC_CMDQ_WRITEP, ++ .bmc_cmdq_readp = XDMA_AST2700_BMC_CMDQ_READP, ++ .control = XDMA_AST2700_CTRL, ++ .status = XDMA_AST2700_STATUS, ++ }, ++ .status_bits = { ++ .us_comp = XDMA_AST2700_STATUS_US_COMP, ++ .ds_comp = XDMA_AST2700_STATUS_DS_COMP, ++ .ds_dirty = XDMA_AST2700_STATUS_DS_DIRTY, ++ }, ++ .set_cmd = aspeed_xdma_ast2700_set_cmd, ++}; + -+ perif->mcyc.virt = dmam_alloc_coherent(dev, perif->mcyc.size, -+ &perif->mcyc.taddr, GFP_KERNEL); -+ if (!perif->mcyc.virt) { -+ dev_err(dev, "cannot allocate memory cycle\n"); -+ return -ENOMEM; -+ } -+ } ++static const struct aspeed_xdma_chip aspeed_ast2700_xdma1_chip = { ++ .control = XDMA_AST2700_CTRL_US_COMP | XDMA_AST2700_CTRL_DS_COMP | ++ XDMA_AST2700_CTRL_DS_DIRTY, ++ .scu_bmc_class = SCU_AST2700_PCIE1_BMC_CLASS_REV, ++ .scu_misc_ctrl = SCU_AST2600_MISC_CTRL, ++ .scu_misc_mask = SCU_AST2700_MISC_CTRL_XDMA_CLIENT, ++ .scu_disable_mask = DEBUG_CTRL_AST2600_XDMA_DISABLE | DEBUG_CTRL_AST2700_XDMA_DISABLE, ++ .scu_pcie_conf = SCU_AST2700_PCIE1_CONF, ++ .scu_pcie_ctrl = SCU_AST2700_PCIE1_CTRL, ++ .queue_entry_size = XDMA_AST2700_QUEUE_ENTRY_SIZE, ++ .regs = { ++ .bmc_cmdq_addr = XDMA_AST2700_BMC_CMDQ_ADDR0, ++ .bmc_cmdq_addr_ext = XDMA_AST2700_BMC_CMDQ_ADDR1, ++ .bmc_cmdq_endp = XDMA_AST2700_BMC_CMDQ_ENDP, ++ .bmc_cmdq_writep = XDMA_AST2700_BMC_CMDQ_WRITEP, ++ .bmc_cmdq_readp = XDMA_AST2700_BMC_CMDQ_READP, ++ .control = XDMA_AST2700_CTRL, ++ .status = XDMA_AST2700_STATUS, ++ }, ++ .status_bits = { ++ .us_comp = XDMA_AST2700_STATUS_US_COMP, ++ .ds_comp = XDMA_AST2700_STATUS_DS_COMP, ++ .ds_dirty = XDMA_AST2700_STATUS_DS_DIRTY, ++ }, ++ .set_cmd = aspeed_xdma_ast2700_set_cmd, ++}; + -+ perif->dma.enable = of_property_read_bool(dev->of_node, "perif-dma-mode"); -+ if (perif->dma.enable) { -+ perif->dma.pc_tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, -+ &perif->dma.pc_tx_addr, GFP_KERNEL); -+ if (!perif->dma.pc_tx_virt) { -+ dev_err(dev, "cannot allocate posted TX DMA buffer\n"); -+ return -ENOMEM; -+ } ++static const struct of_device_id aspeed_xdma_match[] = { ++ { ++ .compatible = "aspeed,ast2500-xdma", ++ .data = &aspeed_ast2500_xdma_chip, ++ }, ++ { ++ .compatible = "aspeed,ast2600-xdma", ++ .data = &aspeed_ast2600_xdma_chip, ++ }, ++ { ++ .compatible = "aspeed,ast2700-xdma0", ++ .data = &aspeed_ast2700_xdma0_chip, ++ }, ++ { ++ .compatible = "aspeed,ast2700-xdma1", ++ .data = &aspeed_ast2700_xdma1_chip, ++ }, ++ { }, ++}; + -+ perif->dma.pc_rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, -+ &perif->dma.pc_rx_addr, GFP_KERNEL); -+ if (!perif->dma.pc_rx_virt) { -+ dev_err(dev, "cannot allocate posted RX DMA buffer\n"); -+ return -ENOMEM; -+ } ++static struct platform_driver aspeed_xdma_driver = { ++ .probe = aspeed_xdma_probe, ++ .remove = aspeed_xdma_remove, ++ .driver = { ++ .name = DEVICE_NAME, ++ .of_match_table = aspeed_xdma_match, ++ }, ++}; + -+ perif->dma.np_tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, -+ &perif->dma.np_tx_addr, GFP_KERNEL); -+ if (!perif->dma.np_tx_virt) { -+ dev_err(dev, "cannot allocate non-posted TX DMA buffer\n"); -+ return -ENOMEM; -+ } -+ } ++module_platform_driver(aspeed_xdma_driver); + -+ perif->mdev.parent = dev; -+ perif->mdev.minor = MISC_DYNAMIC_MINOR; -+ perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-peripheral", DEVICE_NAME); -+ perif->mdev.fops = &ast2500_espi_perif_fops; -+ rc = misc_register(&perif->mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", perif->mdev.name); -+ return rc; -+ } ++MODULE_AUTHOR("Eddie James"); ++MODULE_DESCRIPTION("ASPEED XDMA Engine Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/soc/aspeed/ast2600-otp.c b/drivers/soc/aspeed/ast2600-otp.c +--- a/drivers/soc/aspeed/ast2600-otp.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/ast2600-otp.c 2026-04-08 18:03:48.312705284 +0000 +@@ -0,0 +1,638 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) ASPEED Technology Inc. ++ */ + -+ ast2500_espi_perif_reset(espi); ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ return 0; -+} ++#define ASPEED_REVISION_ID0 0x04 ++#define ASPEED_REVISION_ID1 0x14 ++#define ID0_AST2600A0 0x05000303 ++#define ID1_AST2600A0 0x05000303 ++#define ID0_AST2600A1 0x05010303 ++#define ID1_AST2600A1 0x05010303 ++#define ID0_AST2600A2 0x05010303 ++#define ID1_AST2600A2 0x05020303 ++#define ID0_AST2600A3 0x05030303 ++#define ID1_AST2600A3 0x05030303 ++#define ID0_AST2620A1 0x05010203 ++#define ID1_AST2620A1 0x05010203 ++#define ID0_AST2620A2 0x05010203 ++#define ID1_AST2620A2 0x05020203 ++#define ID0_AST2620A3 0x05030203 ++#define ID1_AST2620A3 0x05030203 ++#define ID0_AST2605A2 0x05010103 ++#define ID1_AST2605A2 0x05020103 ++#define ID0_AST2605A3 0x05030103 ++#define ID1_AST2605A3 0x05030103 ++#define ID0_AST2625A3 0x05030403 ++#define ID1_AST2625A3 0x05030403 + -+static int ast2500_espi_perif_remove(struct ast2500_espi *espi) -+{ -+ struct ast2500_espi_perif *perif; -+ struct device *dev; -+ uint32_t reg; ++#define OTP_PROTECT_KEY 0x0 ++#define OTP_PASSWD 0x349fe38a ++#define OTP_COMMAND 0x4 ++#define OTP_TIMING 0x8 ++#define OTP_ADDR 0x10 ++#define OTP_STATUS 0x14 ++#define OTP_COMPARE_1 0x20 ++#define OTP_COMPARE_2 0x24 ++#define OTP_COMPARE_3 0x28 ++#define OTP_COMPARE_4 0x2c ++#define SW_REV_ID0 0x68 ++#define SW_REV_ID1 0x6c ++#define SEC_KEY_NUM 0x78 ++#define RETRY 20 + -+ dev = espi->dev; ++struct aspeed_otp { ++ struct miscdevice miscdev; ++ void __iomem *reg_base; ++ bool is_open; ++ u32 otp_ver; ++ u32 *data; ++}; + -+ perif = &espi->perif; ++static DEFINE_SPINLOCK(otp_state_lock); + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_PERIF); -+ writel(reg, espi->regs + ESPI_INT_EN); ++static inline u32 aspeed_otp_read(struct aspeed_otp *ctx, u32 reg) ++{ ++ int val; + -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_PERIF_NP_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_RX_DMA_EN -+ | ESPI_CTRL_PERIF_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); ++ val = readl(ctx->reg_base + reg); ++ // printk("read:reg = 0x%08x, val = 0x%08x\n", reg, val); ++ return val; ++} + -+ if (perif->mcyc.enable) -+ dmam_free_coherent(dev, perif->mcyc.size, perif->mcyc.virt, -+ perif->mcyc.taddr); ++static inline void aspeed_otp_write(struct aspeed_otp *ctx, u32 val, u32 reg) ++{ ++ // printk("write:reg = 0x%08x, val = 0x%08x\n", reg, val); ++ writel(val, ctx->reg_base + reg); ++} + -+ if (perif->dma.enable) { -+ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.np_tx_virt, -+ perif->dma.np_tx_addr); -+ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.pc_tx_virt, -+ perif->dma.pc_tx_addr); -+ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.pc_rx_virt, -+ perif->dma.pc_rx_addr); ++static uint32_t chip_version(u32 revid0, u32 revid1) ++{ ++ if (revid0 == ID0_AST2600A0 && revid1 == ID1_AST2600A0) { ++ /* AST2600-A0 */ ++ return OTP_A0; ++ } else if (revid0 == ID0_AST2600A1 && revid1 == ID1_AST2600A1) { ++ /* AST2600-A1 */ ++ return OTP_A1; ++ } else if (revid0 == ID0_AST2600A2 && revid1 == ID1_AST2600A2) { ++ /* AST2600-A2 */ ++ return OTP_A2; ++ } else if (revid0 == ID0_AST2600A3 && revid1 == ID1_AST2600A3) { ++ /* AST2600-A3 */ ++ return OTP_A3; ++ } else if (revid0 == ID0_AST2620A1 && revid1 == ID1_AST2620A1) { ++ /* AST2620-A1 */ ++ return OTP_A1; ++ } else if (revid0 == ID0_AST2620A2 && revid1 == ID1_AST2620A2) { ++ /* AST2620-A2 */ ++ return OTP_A2; ++ } else if (revid0 == ID0_AST2620A3 && revid1 == ID1_AST2620A3) { ++ /* AST2620-A3 */ ++ return OTP_A3; ++ } else if (revid0 == ID0_AST2605A2 && revid1 == ID1_AST2605A2) { ++ /* AST2605-A2 */ ++ return OTP_A2; ++ } else if (revid0 == ID0_AST2605A3 && revid1 == ID1_AST2605A3) { ++ /* AST2605-A3 */ ++ return OTP_A3; ++ } else if (revid0 == ID0_AST2625A3 && revid1 == ID1_AST2625A3) { ++ /* AST2605-A3 */ ++ return OTP_A3; + } ++ return -1; ++} + -+ mutex_destroy(&perif->np_tx_mtx); -+ mutex_destroy(&perif->pc_tx_mtx); -+ mutex_destroy(&perif->pc_rx_mtx); ++static void wait_complete(struct aspeed_otp *ctx) ++{ ++ int reg; ++ int i = 0; + -+ misc_deregister(&perif->mdev); ++ do { ++ reg = aspeed_otp_read(ctx, OTP_STATUS); ++ if ((reg & 0x6) == 0x6) ++ i++; ++ } while (i != 2); ++} ++ ++static void otp_write(struct aspeed_otp *ctx, u32 otp_addr, u32 val) ++{ ++ aspeed_otp_write(ctx, otp_addr, OTP_ADDR); //write address ++ aspeed_otp_write(ctx, val, OTP_COMPARE_1); //write val ++ aspeed_otp_write(ctx, 0x23b1e362, OTP_COMMAND); //write command ++ wait_complete(ctx); ++} ++ ++static void otp_soak(struct aspeed_otp *ctx, int soak) ++{ ++ if (ctx->otp_ver == OTP_A2 || ctx->otp_ver == OTP_A3) { ++ switch (soak) { ++ case 0: //default ++ otp_write(ctx, 0x3000, 0x0); // Write MRA ++ otp_write(ctx, 0x5000, 0x0); // Write MRB ++ otp_write(ctx, 0x1000, 0x0); // Write MR ++ break; ++ case 1: //normal program ++ otp_write(ctx, 0x3000, 0x1320); // Write MRA ++ otp_write(ctx, 0x5000, 0x1008); // Write MRB ++ otp_write(ctx, 0x1000, 0x0024); // Write MR ++ aspeed_otp_write(ctx, 0x04191388, OTP_TIMING); // 200us ++ break; ++ case 2: //soak program ++ otp_write(ctx, 0x3000, 0x1320); // Write MRA ++ otp_write(ctx, 0x5000, 0x0007); // Write MRB ++ otp_write(ctx, 0x1000, 0x0100); // Write MR ++ aspeed_otp_write(ctx, 0x04193a98, OTP_TIMING); // 600us ++ break; ++ } ++ } else { ++ switch (soak) { ++ case 0: //default ++ otp_write(ctx, 0x3000, 0x0); // Write MRA ++ otp_write(ctx, 0x5000, 0x0); // Write MRB ++ otp_write(ctx, 0x1000, 0x0); // Write MR ++ break; ++ case 1: //normal program ++ otp_write(ctx, 0x3000, 0x4021); // Write MRA ++ otp_write(ctx, 0x5000, 0x302f); // Write MRB ++ otp_write(ctx, 0x1000, 0x4020); // Write MR ++ aspeed_otp_write(ctx, 0x04190760, OTP_TIMING); // 75us ++ break; ++ case 2: //soak program ++ otp_write(ctx, 0x3000, 0x4021); // Write MRA ++ otp_write(ctx, 0x5000, 0x1027); // Write MRB ++ otp_write(ctx, 0x1000, 0x4820); // Write MR ++ aspeed_otp_write(ctx, 0x041930d4, OTP_TIMING); // 500us ++ break; ++ } ++ } + -+ return 0; ++ wait_complete(ctx); +} + -+/* virtual wire channel (CH1) */ -+static long ast2500_espi_vw_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static int verify_bit(struct aspeed_otp *ctx, u32 otp_addr, int bit_offset, int value) +{ -+ struct ast2500_espi_vw *vw; -+ struct ast2500_espi *espi; -+ uint32_t gpio; -+ -+ vw = container_of(fp->private_data, struct ast2500_espi_vw, mdev); -+ espi = container_of(vw, struct ast2500_espi, vw); -+ gpio = vw->gpio.val; ++ u32 ret[2]; + -+ switch (cmd) { -+ case ASPEED_ESPI_VW_GET_GPIO_VAL: -+ if (put_user(gpio, (uint32_t __user *)arg)) -+ return -EFAULT; -+ break; -+ case ASPEED_ESPI_VW_PUT_GPIO_VAL: -+ if (get_user(gpio, (uint32_t __user *)arg)) -+ return -EFAULT; ++ if (otp_addr % 2 == 0) ++ aspeed_otp_write(ctx, otp_addr, OTP_ADDR); //Read address ++ else ++ aspeed_otp_write(ctx, otp_addr - 1, OTP_ADDR); //Read address + -+ writel(gpio, espi->regs + ESPI_VW_GPIO_VAL); -+ break; -+ default: -+ return -EINVAL; -+ }; ++ aspeed_otp_write(ctx, 0x23b1e361, OTP_COMMAND); //trigger read ++ wait_complete(ctx); ++ ret[0] = aspeed_otp_read(ctx, OTP_COMPARE_1); ++ ret[1] = aspeed_otp_read(ctx, OTP_COMPARE_2); + -+ return 0; ++ if (otp_addr % 2 == 0) { ++ if (((ret[0] >> bit_offset) & 1) == value) ++ return 0; ++ else ++ return -1; ++ } else { ++ if (((ret[1] >> bit_offset) & 1) == value) ++ return 0; ++ else ++ return -1; ++ } +} + -+static const struct file_operations ast2500_espi_vw_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2500_espi_vw_ioctl, -+}; -+ -+static void ast2500_espi_vw_isr(struct ast2500_espi *espi) ++static void otp_prog(struct aspeed_otp *ctx, u32 otp_addr, u32 prog_bit) +{ -+ struct ast2500_espi_vw *vw; -+ uint32_t reg, sts, sts_sysevt; -+ -+ vw = &espi->vw; -+ -+ sts = readl(espi->regs + ESPI_INT_STS); -+ -+ if (sts & ESPI_INT_STS_VW_SYSEVT) { -+ sts_sysevt = readl(espi->regs + ESPI_VW_SYSEVT_INT_STS); -+ -+ if (sts_sysevt & ESPI_VW_SYSEVT_INT_STS_HOST_RST_WARN) { -+ reg = readl(espi->regs + ESPI_VW_SYSEVT) | ESPI_VW_SYSEVT_HOST_RST_ACK; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT); -+ writel(ESPI_VW_SYSEVT_INT_STS_HOST_RST_WARN, espi->regs + ESPI_VW_SYSEVT_INT_STS); -+ } ++ otp_write(ctx, 0x0, prog_bit); ++ aspeed_otp_write(ctx, otp_addr, OTP_ADDR); //write address ++ aspeed_otp_write(ctx, prog_bit, OTP_COMPARE_1); //write data ++ aspeed_otp_write(ctx, 0x23b1e364, OTP_COMMAND); //write command ++ wait_complete(ctx); ++} + -+ if (sts_sysevt & ESPI_VW_SYSEVT_INT_STS_OOB_RST_WARN) { -+ reg = readl(espi->regs + ESPI_VW_SYSEVT) | ESPI_VW_SYSEVT_OOB_RST_ACK; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT); -+ writel(ESPI_VW_SYSEVT_INT_STS_OOB_RST_WARN, espi->regs + ESPI_VW_SYSEVT_INT_STS); -+ } ++static void _otp_prog_bit(struct aspeed_otp *ctx, u32 value, u32 prog_address, u32 bit_offset) ++{ ++ int prog_bit; + -+ writel(ESPI_INT_STS_VW_SYSEVT, espi->regs + ESPI_INT_STS); ++ if (prog_address % 2 == 0) { ++ if (value) ++ prog_bit = ~(0x1 << bit_offset); ++ else ++ return; ++ } else { ++ if (ctx->otp_ver != OTP_A3) ++ prog_address |= 1 << 15; ++ if (!value) ++ prog_bit = 0x1 << bit_offset; ++ else ++ return; + } ++ otp_prog(ctx, prog_address, prog_bit); ++} + -+ if (sts & ESPI_INT_STS_VW_SYSEVT1) { -+ sts_sysevt = readl(espi->regs + ESPI_VW_SYSEVT1_INT_STS); -+ -+ if (sts_sysevt & ESPI_VW_SYSEVT1_INT_STS_SUSPEND_WARN) { -+ reg = readl(espi->regs + ESPI_VW_SYSEVT1) | ESPI_VW_SYSEVT1_SUSPEND_ACK; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT1); -+ writel(ESPI_VW_SYSEVT1_INT_STS_SUSPEND_WARN, espi->regs + ESPI_VW_SYSEVT1_INT_STS); -+ } ++static int otp_prog_bit(struct aspeed_otp *ctx, u32 value, u32 prog_address, u32 bit_offset) ++{ ++ int pass; ++ int i; + -+ writel(ESPI_INT_STS_VW_SYSEVT1, espi->regs + ESPI_INT_STS); -+ } ++ otp_soak(ctx, 1); ++ _otp_prog_bit(ctx, value, prog_address, bit_offset); ++ pass = 0; + -+ if (sts & ESPI_INT_STS_VW_GPIO) { -+ vw->gpio.val = readl(espi->regs + ESPI_VW_GPIO_VAL); -+ writel(ESPI_INT_STS_VW_GPIO, espi->regs + ESPI_INT_STS); ++ for (i = 0; i < RETRY; i++) { ++ if (verify_bit(ctx, prog_address, bit_offset, value) != 0) { ++ otp_soak(ctx, 2); ++ _otp_prog_bit(ctx, value, prog_address, bit_offset); ++ if (verify_bit(ctx, prog_address, bit_offset, value) != 0) { ++ otp_soak(ctx, 1); ++ } else { ++ pass = 1; ++ break; ++ } ++ } else { ++ pass = 1; ++ break; ++ } + } ++ otp_soak(ctx, 0); ++ return pass; +} + -+static void ast2500_espi_vw_reset(struct ast2500_espi *espi) ++static void otp_read_conf_dw(struct aspeed_otp *ctx, u32 offset, u32 *buf) +{ -+ uint32_t reg; -+ struct ast2500_espi_vw *vw = &espi->vw; -+ -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_VW); -+ writel(reg, espi->regs + ESPI_INT_EN); -+ writel(ESPI_INT_STS_VW, espi->regs + ESPI_INT_STS); -+ -+ vw->gpio.val = readl(espi->regs + ESPI_VW_GPIO_VAL); -+ -+ /* Host Reset Warn and OOB Reset Warn system events */ -+ reg = readl(espi->regs + ESPI_VW_SYSEVT_INT_T2) -+ | ESPI_VW_SYSEVT_INT_T2_HOST_RST_WARN -+ | ESPI_VW_SYSEVT_INT_T2_OOB_RST_WARN; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT_INT_T2); -+ -+ reg = readl(espi->regs + ESPI_VW_SYSEVT_INT_EN) -+ | ESPI_VW_SYSEVT_INT_EN_HOST_RST_WARN -+ | ESPI_VW_SYSEVT_INT_EN_OOB_RST_WARN; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT_INT_EN); -+ -+ /* Suspend Warn system event */ -+ reg = readl(espi->regs + ESPI_VW_SYSEVT1_INT_T0) | ESPI_VW_SYSEVT1_INT_T0_SUSPEND_WARN; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT1_INT_T0); -+ -+ reg = readl(espi->regs + ESPI_VW_SYSEVT1_INT_EN) | ESPI_VW_SYSEVT1_INT_EN_SUSPEND_WARN; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT1_INT_EN); -+ -+ reg = readl(espi->regs + ESPI_INT_EN) -+ | ESPI_INT_EN_VW_GPIO -+ | ESPI_INT_EN_VW_SYSEVT -+ | ESPI_INT_EN_VW_SYSEVT1; -+ writel(reg, espi->regs + ESPI_INT_EN); ++ u32 config_offset; + -+ reg = readl(espi->regs + ESPI_VW_SYSEVT) -+ | ESPI_VW_SYSEVT_SLV_BOOT_STS -+ | ESPI_VW_SYSEVT_SLV_BOOT_DONE; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT); ++ config_offset = 0x800; ++ config_offset |= (offset / 8) * 0x200; ++ config_offset |= (offset % 8) * 0x2; + -+ reg = readl(espi->regs + ESPI_CTRL) -+ | ((vw->gpio.hw_mode) ? 0 : ESPI_CTRL_VW_GPIO_SW) -+ | ESPI_CTRL_VW_SW_RDY; -+ writel(reg, espi->regs + ESPI_CTRL); ++ aspeed_otp_write(ctx, config_offset, OTP_ADDR); //Read address ++ aspeed_otp_write(ctx, 0x23b1e361, OTP_COMMAND); //trigger read ++ wait_complete(ctx); ++ buf[0] = aspeed_otp_read(ctx, OTP_COMPARE_1); +} + -+static int ast2500_espi_vw_probe(struct ast2500_espi *espi) ++static void otp_read_conf(struct aspeed_otp *ctx, u32 offset, u32 len) +{ -+ int rc; -+ struct device *dev = espi->dev; -+ struct ast2500_espi_vw *vw = &espi->vw; -+ -+ writel(0x0, espi->regs + ESPI_VW_SYSEVT_INT_EN); -+ writel(0xffffffff, espi->regs + ESPI_VW_SYSEVT_INT_STS); -+ -+ writel(0x0, espi->regs + ESPI_VW_SYSEVT1_INT_EN); -+ writel(0xffffffff, espi->regs + ESPI_VW_SYSEVT1_INT_STS); -+ -+ vw->gpio.hw_mode = of_property_read_bool(dev->of_node, "vw-gpio-hw-mode"); -+ -+ vw->mdev.parent = dev; -+ vw->mdev.minor = MISC_DYNAMIC_MINOR; -+ vw->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-vw", DEVICE_NAME); -+ vw->mdev.fops = &ast2500_espi_vw_fops; -+ rc = misc_register(&vw->mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", vw->mdev.name); -+ return rc; -+ } -+ -+ ast2500_espi_vw_reset(espi); ++ int i, j; + -+ return 0; ++ otp_soak(ctx, 0); ++ for (i = offset, j = 0; j < len; i++, j++) ++ otp_read_conf_dw(ctx, i, &ctx->data[j]); +} + -+static int ast2500_espi_vw_remove(struct ast2500_espi *espi) ++static void otp_read_data_2dw(struct aspeed_otp *ctx, u32 offset, u32 *buf) +{ -+ struct ast2500_espi_vw *vw; -+ uint32_t reg; -+ -+ vw = &espi->vw; ++ aspeed_otp_write(ctx, offset, OTP_ADDR); //Read address ++ aspeed_otp_write(ctx, 0x23b1e361, OTP_COMMAND); //trigger read ++ wait_complete(ctx); ++ buf[0] = aspeed_otp_read(ctx, OTP_COMPARE_1); ++ buf[1] = aspeed_otp_read(ctx, OTP_COMPARE_2); ++} + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_VW); -+ writel(reg, espi->regs + ESPI_INT_EN); ++static void otp_read_data(struct aspeed_otp *ctx, u32 offset, u32 len) ++{ ++ int i, j; ++ u32 ret[2]; + -+ misc_deregister(&vw->mdev); ++ otp_soak(ctx, 0); + -+ return 0; ++ i = offset; ++ j = 0; ++ if (offset % 2) { ++ otp_read_data_2dw(ctx, i - 1, ret); ++ ctx->data[0] = ret[1]; ++ i++; ++ j++; ++ } ++ for (; j < len; i += 2, j += 2) ++ otp_read_data_2dw(ctx, i, &ctx->data[j]); +} + -+/* out-of-band channel (CH2) */ -+static long ast2500_espi_oob_get_rx(struct file *fp, -+ struct ast2500_espi_oob *oob, -+ struct aspeed_espi_ioc *ioc) ++static int otp_prog_data(struct aspeed_otp *ctx, u32 value, u32 dw_offset, u32 bit_offset) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2500_espi *espi; -+ struct espi_comm_hdr *hdr; -+ unsigned long flags; -+ uint32_t pkt_len; -+ uint8_t *pkt; -+ int i, rc; -+ -+ espi = container_of(oob, struct ast2500_espi, oob); ++ u32 read[2]; ++ int otp_bit; + -+ if (fp->f_flags & O_NONBLOCK) { -+ if (!mutex_trylock(&oob->rx_mtx)) -+ return -EAGAIN; ++ if (dw_offset % 2 == 0) { ++ otp_read_data_2dw(ctx, dw_offset, read); ++ otp_bit = (read[0] >> bit_offset) & 0x1; + -+ if (!oob->rx_ready) { -+ rc = -ENODATA; -+ goto unlock_mtx_n_out; ++ if (otp_bit == 1 && value == 0) { ++ pr_err("OTPDATA%X[%X] = 1\n", dw_offset, bit_offset); ++ pr_err("OTP is programed, which can't be cleaned\n"); ++ return -EINVAL; + } + } else { -+ mutex_lock(&oob->rx_mtx); ++ otp_read_data_2dw(ctx, dw_offset - 1, read); ++ otp_bit = (read[1] >> bit_offset) & 0x1; + -+ if (!oob->rx_ready) { -+ rc = wait_event_interruptible(oob->wq, oob->rx_ready); -+ if (rc == -ERESTARTSYS) { -+ rc = -EINTR; -+ goto unlock_mtx_n_out; -+ } ++ if (otp_bit == 0 && value == 1) { ++ pr_err("OTPDATA%X[%X] = 1\n", dw_offset, bit_offset); ++ pr_err("OTP is programed, which can't be writen\n"); ++ return -EINVAL; + } + } ++ if (otp_bit == value) { ++ pr_err("OTPDATA%X[%X] = %d\n", dw_offset, bit_offset, value); ++ pr_err("No need to program\n"); ++ return 0; ++ } + -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ reg = readl(espi->regs + ESPI_OOB_RX_CTRL); -+ cyc = FIELD_GET(ESPI_OOB_RX_CTRL_CYC, reg); -+ tag = FIELD_GET(ESPI_OOB_RX_CTRL_TAG, reg); -+ len = FIELD_GET(ESPI_OOB_RX_CTRL_LEN, reg); ++ return otp_prog_bit(ctx, value, dw_offset, bit_offset); ++} + -+ /* -+ * calculate the length of the rest part of the -+ * eSPI packet to be read from HW and copied to -+ * user space. -+ */ -+ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + sizeof(struct espi_comm_hdr); ++static int otp_prog_conf(struct aspeed_otp *ctx, u32 value, u32 dw_offset, u32 bit_offset) ++{ ++ u32 read; ++ u32 prog_address = 0; ++ int otp_bit; + -+ if (ioc->pkt_len < pkt_len) { -+ rc = -EINVAL; -+ goto unlock_mtx_n_out; -+ } ++ otp_read_conf_dw(ctx, dw_offset, &read); + -+ pkt = vmalloc(pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_mtx_n_out; ++ prog_address = 0x800; ++ prog_address |= (dw_offset / 8) * 0x200; ++ prog_address |= (dw_offset % 8) * 0x2; ++ otp_bit = (read >> bit_offset) & 0x1; ++ if (otp_bit == value) { ++ pr_err("OTPCFG%X[%X] = %d\n", dw_offset, bit_offset, value); ++ pr_err("No need to program\n"); ++ return 0; + } -+ -+ hdr = (struct espi_comm_hdr *)pkt; -+ hdr->cyc = cyc; -+ hdr->tag = tag; -+ hdr->len_h = len >> 8; -+ hdr->len_l = len & 0xff; -+ -+ if (oob->dma.enable) { -+ memcpy(hdr + 1, oob->dma.rx_virt, pkt_len - sizeof(*hdr)); -+ } else { -+ for (i = sizeof(*hdr); i < pkt_len; ++i) -+ pkt[i] = readl(espi->regs + ESPI_OOB_RX_DATA) & 0xff; ++ if (otp_bit == 1 && value == 0) { ++ pr_err("OTPCFG%X[%X] = 1\n", dw_offset, bit_offset); ++ pr_err("OTP is programed, which can't be clean\n"); ++ return -EINVAL; + } + -+ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } ++ return otp_prog_bit(ctx, value, prog_address, bit_offset); ++} + -+ spin_lock_irqsave(&oob->lock, flags); ++struct aspeed_otp *glob_ctx; + -+ writel(ESPI_OOB_RX_CTRL_SERV_PEND, espi->regs + ESPI_OOB_RX_CTRL); -+ oob->rx_ready = 0; ++void otp_read_data_buf(u32 offset, u32 *buf, u32 len) ++{ ++ int i, j; ++ u32 ret[2]; + -+ spin_unlock_irqrestore(&oob->lock, flags); ++ aspeed_otp_write(glob_ctx, OTP_PASSWD, OTP_PROTECT_KEY); + -+ rc = 0; ++ otp_soak(glob_ctx, 0); + -+free_n_out: -+ vfree(pkt); ++ i = offset; ++ j = 0; ++ if (offset % 2) { ++ otp_read_data_2dw(glob_ctx, i - 1, ret); ++ buf[0] = ret[1]; ++ i++; ++ j++; ++ } ++ for (; j < len; i += 2, j += 2) ++ otp_read_data_2dw(glob_ctx, i, &buf[j]); ++ aspeed_otp_write(glob_ctx, 0, OTP_PROTECT_KEY); ++} ++EXPORT_SYMBOL(otp_read_data_buf); + -+unlock_mtx_n_out: -+ mutex_unlock(&oob->rx_mtx); ++void otp_read_conf_buf(u32 offset, u32 *buf, u32 len) ++{ ++ int i, j; + -+ return rc; ++ aspeed_otp_write(glob_ctx, OTP_PASSWD, OTP_PROTECT_KEY); ++ otp_soak(glob_ctx, 0); ++ for (i = offset, j = 0; j < len; i++, j++) ++ otp_read_conf_dw(glob_ctx, i, &buf[j]); ++ aspeed_otp_write(glob_ctx, 0, OTP_PROTECT_KEY); +} ++EXPORT_SYMBOL(otp_read_conf_buf); + -+static long ast2500_espi_oob_put_tx(struct file *fp, -+ struct ast2500_espi_oob *oob, -+ struct aspeed_espi_ioc *ioc) ++static long otp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2500_espi *espi; -+ struct espi_comm_hdr *hdr; -+ uint8_t *pkt; -+ int i, rc; ++ struct miscdevice *c = file->private_data; ++ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); ++ void __user *argp = (void __user *)arg; ++ struct otp_read xfer; ++ struct otp_prog prog; ++ u32 reg_read[2]; ++ int ret = 0; + -+ espi = container_of(oob, struct ast2500_espi, oob); ++ switch (cmd) { ++ case ASPEED_OTP_READ_DATA: ++ if (copy_from_user(&xfer, argp, sizeof(struct otp_read))) ++ return -EFAULT; ++ if ((xfer.offset + xfer.len) > 0x800) { ++ pr_err("out of range"); ++ return -EINVAL; ++ } + -+ if (!mutex_trylock(&oob->tx_mtx)) -+ return -EAGAIN; ++ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); ++ otp_read_data(ctx, xfer.offset, xfer.len); ++ aspeed_otp_write(ctx, 0, OTP_PROTECT_KEY); + -+ reg = readl(espi->regs + ESPI_OOB_TX_CTRL); -+ if (reg & ESPI_OOB_TX_CTRL_TRIG_PEND) { -+ rc = -EBUSY; -+ goto unlock_mtx_n_out; -+ } ++ if (copy_to_user(xfer.data, ctx->data, xfer.len * 4)) ++ return -EFAULT; ++ if (copy_to_user(argp, &xfer, sizeof(struct otp_read))) ++ return -EFAULT; ++ break; ++ case ASPEED_OTP_READ_CONF: ++ if (copy_from_user(&xfer, argp, sizeof(struct otp_read))) ++ return -EFAULT; ++ if ((xfer.offset + xfer.len) > 0x800) { ++ pr_err("out of range"); ++ return -EINVAL; ++ } + -+ if (ioc->pkt_len > ESPI_MAX_PKT_LEN) { -+ rc = -EINVAL; -+ goto unlock_mtx_n_out; -+ } ++ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); ++ otp_read_conf(ctx, xfer.offset, xfer.len); ++ aspeed_otp_write(ctx, 0, OTP_PROTECT_KEY); + -+ pkt = vmalloc(ioc->pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_mtx_n_out; ++ if (copy_to_user(xfer.data, ctx->data, xfer.len * 4)) ++ return -EFAULT; ++ if (copy_to_user(argp, &xfer, sizeof(struct otp_read))) ++ return -EFAULT; ++ break; ++ case ASPEED_OTP_PROG_DATA: ++ if (copy_from_user(&prog, argp, sizeof(struct otp_prog))) ++ return -EFAULT; ++ if (prog.bit_offset >= 32 || (prog.value != 0 && prog.value != 1)) { ++ pr_err("out of range"); ++ return -EINVAL; ++ } ++ if (prog.dw_offset >= 0x800) { ++ pr_err("out of range"); ++ return -EINVAL; ++ } ++ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); ++ ret = otp_prog_data(ctx, prog.value, prog.dw_offset, prog.bit_offset); ++ break; ++ case ASPEED_OTP_PROG_CONF: ++ if (copy_from_user(&prog, argp, sizeof(struct otp_prog))) ++ return -EFAULT; ++ if (prog.bit_offset >= 32 || (prog.value != 0 && prog.value != 1)) { ++ pr_err("out of range"); ++ return -EINVAL; ++ } ++ if (prog.dw_offset >= 0x20) { ++ pr_err("out of range"); ++ return -EINVAL; ++ } ++ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); ++ ret = otp_prog_conf(ctx, prog.value, prog.dw_offset, prog.bit_offset); ++ break; ++ case ASPEED_OTP_VER: ++ if (copy_to_user(argp, &ctx->otp_ver, sizeof(u32))) ++ return -EFAULT; ++ break; ++ case ASPEED_OTP_SW_RID: ++ reg_read[0] = aspeed_otp_read(ctx, SW_REV_ID0); ++ reg_read[1] = aspeed_otp_read(ctx, SW_REV_ID1); ++ if (copy_to_user(argp, reg_read, sizeof(u32) * 2)) ++ return -EFAULT; ++ break; ++ case ASPEED_SEC_KEY_NUM: ++ reg_read[0] = aspeed_otp_read(ctx, SEC_KEY_NUM) & 7; ++ if (copy_to_user(argp, reg_read, sizeof(u32))) ++ return -EFAULT; ++ break; + } ++ return ret; ++} + -+ hdr = (struct espi_comm_hdr *)pkt; ++static int otp_open(struct inode *inode, struct file *file) ++{ ++ struct miscdevice *c = file->private_data; ++ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); + -+ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } ++ spin_lock(&otp_state_lock); + -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ if (oob->dma.enable) { -+ memcpy(oob->dma.tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); -+ dma_wmb(); -+ } else { -+ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_OOB_TX_DATA); ++ if (ctx->is_open) { ++ spin_unlock(&otp_state_lock); ++ return -EBUSY; + } + -+ cyc = hdr->cyc; -+ tag = hdr->tag; -+ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); -+ -+ reg = FIELD_PREP(ESPI_OOB_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_OOB_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_OOB_TX_CTRL_LEN, len) -+ | ESPI_OOB_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_OOB_TX_CTRL); -+ -+ rc = 0; -+ -+free_n_out: -+ vfree(pkt); ++ ctx->is_open = true; + -+unlock_mtx_n_out: -+ mutex_unlock(&oob->tx_mtx); ++ spin_unlock(&otp_state_lock); + -+ return rc; ++ return 0; +} + -+static long ast2500_espi_oob_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static int otp_release(struct inode *inode, struct file *file) +{ -+ struct ast2500_espi_oob *oob; -+ struct aspeed_espi_ioc ioc; -+ -+ oob = container_of(fp->private_data, struct ast2500_espi_oob, mdev); ++ struct miscdevice *c = file->private_data; ++ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); + -+ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) -+ return -EFAULT; ++ spin_lock(&otp_state_lock); + -+ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) -+ return -EINVAL; ++ ctx->is_open = false; + -+ switch (cmd) { -+ case ASPEED_ESPI_OOB_GET_RX: -+ return ast2500_espi_oob_get_rx(fp, oob, &ioc); -+ case ASPEED_ESPI_OOB_PUT_TX: -+ return ast2500_espi_oob_put_tx(fp, oob, &ioc); -+ default: -+ break; -+ }; ++ spin_unlock(&otp_state_lock); + -+ return -EINVAL; ++ return 0; +} + -+static const struct file_operations ast2500_espi_oob_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2500_espi_oob_ioctl, ++static const struct file_operations otp_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = otp_ioctl, ++ .open = otp_open, ++ .release = otp_release, +}; + -+static void ast2500_espi_oob_isr(struct ast2500_espi *espi) -+{ -+ struct ast2500_espi_oob *oob; -+ unsigned long flags; -+ uint32_t sts; ++static const struct of_device_id aspeed_otp_of_matches[] = { ++ { .compatible = "aspeed,ast2600-sbc" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, aspeed_otp_of_matches); + -+ oob = &espi->oob; ++static int aspeed_otp_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct regmap *scu; ++ struct aspeed_otp *priv; ++ struct resource *res; ++ u32 revid0, revid1; ++ int rc; + -+ sts = readl(espi->regs + ESPI_INT_STS); ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ glob_ctx = priv; ++ if (!priv) ++ return -ENOMEM; + -+ if (sts & ESPI_INT_STS_OOB_RX_CMPLT) { -+ writel(ESPI_INT_STS_OOB_RX_CMPLT, espi->regs + ESPI_INT_STS); ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM\n"); ++ return -ENOENT; ++ } + -+ spin_lock_irqsave(&oob->lock, flags); -+ oob->rx_ready = true; -+ spin_unlock_irqrestore(&oob->lock, flags); ++ priv->reg_base = devm_ioremap_resource(&pdev->dev, res); ++ if (!priv->reg_base) ++ return -EIO; + -+ wake_up_interruptible(&oob->wq); ++ scu = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu"); ++ if (IS_ERR(scu)) { ++ dev_err(dev, "failed to find 2600 SCU regmap\n"); ++ return PTR_ERR(scu); + } -+} -+ -+static void ast2500_espi_oob_reset(struct ast2500_espi *espi) -+{ -+ struct ast2500_espi_oob *oob; -+ uint32_t reg; + -+ oob = &espi->oob; ++ regmap_read(scu, ASPEED_REVISION_ID0, &revid0); ++ regmap_read(scu, ASPEED_REVISION_ID1, &revid1); + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_OOB); -+ writel(reg, espi->regs + ESPI_INT_EN); -+ writel(ESPI_INT_STS_OOB, espi->regs + ESPI_INT_STS); ++ priv->otp_ver = chip_version(revid0, revid1); + -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_OOB_TX_SW_RST -+ | ESPI_CTRL_OOB_RX_SW_RST -+ | ESPI_CTRL_OOB_TX_DMA_EN -+ | ESPI_CTRL_OOB_RX_DMA_EN -+ | ESPI_CTRL_OOB_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); ++ if (priv->otp_ver == -1) { ++ dev_err(dev, "invalid SCU\n"); ++ return -EINVAL; ++ } + -+ udelay(1); ++ priv->data = kmalloc(8192, GFP_KERNEL); ++ if (!priv->data) ++ return -ENOMEM; + -+ reg |= (ESPI_CTRL_OOB_TX_SW_RST | ESPI_CTRL_OOB_RX_SW_RST); -+ writel(reg, espi->regs + ESPI_CTRL); ++ dev_set_drvdata(dev, priv); + -+ if (oob->dma.enable) { -+ writel(oob->dma.tx_addr, espi->regs + ESPI_OOB_TX_DMA); -+ writel(oob->dma.rx_addr, espi->regs + ESPI_OOB_RX_DMA); ++ /* Set up the miscdevice */ ++ priv->miscdev.minor = MISC_DYNAMIC_MINOR; ++ priv->miscdev.name = "aspeed-otp"; ++ priv->miscdev.fops = &otp_fops; + -+ reg = readl(espi->regs + ESPI_CTRL) -+ | ESPI_CTRL_OOB_TX_DMA_EN -+ | ESPI_CTRL_OOB_RX_DMA_EN; -+ writel(reg, espi->regs + ESPI_CTRL); ++ /* Register the device */ ++ rc = misc_register(&priv->miscdev); ++ if (rc) { ++ dev_err(dev, "Unable to register device\n"); ++ return rc; + } + -+ reg = readl(espi->regs + ESPI_INT_EN) | ESPI_INT_EN_OOB_RX_CMPLT; -+ writel(reg, espi->regs + ESPI_INT_EN); -+ -+ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_OOB_SW_RDY; -+ writel(reg, espi->regs + ESPI_CTRL); ++ return 0; +} + -+static int ast2500_espi_oob_probe(struct ast2500_espi *espi) ++static void aspeed_otp_remove(struct platform_device *pdev) +{ -+ struct ast2500_espi_oob *oob; -+ struct device *dev; -+ int rc; ++ struct aspeed_otp *ctx = dev_get_drvdata(&pdev->dev); + -+ dev = espi->dev; ++ kfree(ctx->data); ++ misc_deregister(&ctx->miscdev); ++} + -+ oob = &espi->oob; ++static struct platform_driver aspeed_otp_driver = { ++ .probe = aspeed_otp_probe, ++ .remove = aspeed_otp_remove, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = aspeed_otp_of_matches, ++ }, ++}; + -+ init_waitqueue_head(&oob->wq); ++module_platform_driver(aspeed_otp_driver); + -+ spin_lock_init(&oob->lock); ++MODULE_AUTHOR("Neal Liu "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("ASPEED OTP Driver"); +diff --git a/drivers/soc/aspeed/ast2700-otp.c b/drivers/soc/aspeed/ast2700-otp.c +--- a/drivers/soc/aspeed/ast2700-otp.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/ast2700-otp.c 2026-04-08 18:03:48.312705284 +0000 +@@ -0,0 +1,565 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2024 Aspeed Technology Inc. ++ */ + -+ mutex_init(&oob->tx_mtx); -+ mutex_init(&oob->rx_mtx); ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ oob->dma.enable = of_property_read_bool(dev->of_node, "oob-dma-mode"); -+ if (oob->dma.enable) { -+ oob->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &oob->dma.tx_addr, GFP_KERNEL); -+ if (!oob->dma.tx_virt) { -+ dev_err(dev, "cannot allocate DMA TX buffer\n"); -+ return -ENOMEM; -+ } ++static DEFINE_SPINLOCK(otp_state_lock); + -+ oob->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &oob->dma.rx_addr, GFP_KERNEL); -+ if (!oob->dma.rx_virt) { -+ dev_err(dev, "cannot allocate DMA TX buffer\n"); -+ return -ENOMEM; -+ } -+ } ++/*********************** ++ * * ++ * OTP regs definition * ++ * * ++ ***********************/ ++#define OTP_REG_SIZE 0x200 + -+ oob->mdev.parent = dev; -+ oob->mdev.minor = MISC_DYNAMIC_MINOR; -+ oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-oob", DEVICE_NAME); -+ oob->mdev.fops = &ast2500_espi_oob_fops; -+ rc = misc_register(&oob->mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", oob->mdev.name); -+ return rc; -+ } ++#define OTP_PASSWD 0x349fe38a ++#define OTP_CMD_READ 0x23b1e361 ++#define OTP_CMD_PROG 0x23b1e364 ++#define OTP_CMD_PROG_MULTI 0x23b1e365 ++#define OTP_CMD_CMP 0x23b1e363 ++#define OTP_CMD_BIST 0x23b1e368 + -+ ast2500_espi_oob_reset(espi); ++#define OTP_CMD_OFFSET 0x20 ++#define OTP_MASTER OTP_M0 + -+ return 0; -+} ++#define OTP_KEY 0x0 ++#define OTP_CMD (OTP_MASTER * OTP_CMD_OFFSET + 0x4) ++#define OTP_WDATA_0 (OTP_MASTER * OTP_CMD_OFFSET + 0x8) ++#define OTP_WDATA_1 (OTP_MASTER * OTP_CMD_OFFSET + 0xc) ++#define OTP_WDATA_2 (OTP_MASTER * OTP_CMD_OFFSET + 0x10) ++#define OTP_WDATA_3 (OTP_MASTER * OTP_CMD_OFFSET + 0x14) ++#define OTP_STATUS (OTP_MASTER * OTP_CMD_OFFSET + 0x18) ++#define OTP_ADDR (OTP_MASTER * OTP_CMD_OFFSET + 0x1c) ++#define OTP_RDATA (OTP_MASTER * OTP_CMD_OFFSET + 0x20) + -+static int ast2500_espi_oob_remove(struct ast2500_espi *espi) -+{ -+ struct ast2500_espi_oob *oob; -+ struct device *dev; -+ uint32_t reg; ++#define OTP_DBG00 0x0C4 ++#define OTP_DBG01 0x0C8 ++#define OTP_MASTER_PID 0x0D0 ++#define OTP_ECC_EN 0x0D4 ++#define OTP_CMD_LOCK 0x0D8 ++#define OTP_SW_RST 0x0DC ++#define OTP_SLV_ID 0x0E0 ++#define OTP_PMC_CQ 0x0E4 ++#define OTP_FPGA 0x0EC ++#define OTP_CLR_FPGA 0x0F0 ++#define OTP_REGION_ROM_PATCH 0x100 ++#define OTP_REGION_OTPCFG 0x104 ++#define OTP_REGION_OTPSTRAP 0x108 ++#define OTP_REGION_OTPSTRAP_EXT 0x10C ++#define OTP_REGION_SECURE0 0x120 ++#define OTP_REGION_SECURE0_RANGE 0x124 ++#define OTP_REGION_SECURE1 0x128 ++#define OTP_REGION_SECURE1_RANGE 0x12C ++#define OTP_REGION_SECURE2 0x130 ++#define OTP_REGION_SECURE2_RANGE 0x134 ++#define OTP_REGION_SECURE3 0x138 ++#define OTP_REGION_SECURE3_RANGE 0x13C ++#define OTP_REGION_USR0 0x140 ++#define OTP_REGION_USR0_RANGE 0x144 ++#define OTP_REGION_USR1 0x148 ++#define OTP_REGION_USR1_RANGE 0x14C ++#define OTP_REGION_USR2 0x150 ++#define OTP_REGION_USR2_RANGE 0x154 ++#define OTP_REGION_USR3 0x158 ++#define OTP_REGION_USR3_RANGE 0x15C ++#define OTP_REGION_CALIPTRA_0 0x160 ++#define OTP_REGION_CALIPTRA_0_RANGE 0x164 ++#define OTP_REGION_CALIPTRA_1 0x168 ++#define OTP_REGION_CALIPTRA_1_RANGE 0x16C ++#define OTP_REGION_CALIPTRA_2 0x170 ++#define OTP_REGION_CALIPTRA_2_RANGE 0x174 ++#define OTP_REGION_CALIPTRA_3 0x178 ++#define OTP_REGION_CALIPTRA_3_RANGE 0x17C ++#define OTP_RBP_SOC_SVN 0x180 ++#define OTP_RBP_SOC_KEYRETIRE 0x184 ++#define OTP_RBP_CALIP_SVN 0x188 ++#define OTP_RBP_CALIP_KEYRETIRE 0x18C ++#define OTP_PUF 0x1A0 ++#define OTP_MASTER_ID 0x1B0 ++#define OTP_MASTER_ID_EXT 0x1B4 ++#define OTP_R_MASTER_ID 0x1B8 ++#define OTP_R_MASTER_ID_EXT 0x1BC ++#define OTP_SOC_ECCKEY 0x1C0 ++#define OTP_SEC_BOOT_EN 0x1C4 ++#define OTP_SOC_KEY 0x1C8 ++#define OTP_CALPITRA_MANU_KEY 0x1CC ++#define OTP_CALPITRA_OWNER_KEY 0x1D0 ++#define OTP_FW_ID_LSB 0x1D4 ++#define OTP_FW_ID_MSB 0x1D8 ++#define OTP_CALIP_FMC_SVN 0x1DC ++#define OTP_CALIP_RUNTIME_SVN0 0x1E0 ++#define OTP_CALIP_RUNTIME_SVN1 0x1E4 ++#define OTP_CALIP_RUNTIME_SVN2 0x1E8 ++#define OTP_CALIP_RUNTIME_SVN3 0x1EC ++#define OTP_SVN_WLOCK 0x1F0 ++#define OTP_INTR_EN 0x200 ++#define OTP_INTR_STS 0x204 ++#define OTP_INTR_MID 0x208 ++#define OTP_INTR_FUNC_INFO 0x20C ++#define OTP_INTR_M_INFO 0x210 ++#define OTP_INTR_R_INFO 0x214 + -+ dev = espi->dev; ++#define OTP_PMC 0x400 ++#define OTP_DAP 0x500 + -+ oob = &espi->oob; ++/* OTP status: [0] */ ++#define OTP_STS_IDLE 0x0 ++#define OTP_STS_BUSY 0x1 + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_OOB); -+ writel(reg, espi->regs + ESPI_INT_EN); ++/* OTP cmd status: [7:4] */ ++#define OTP_GET_CMD_STS(x) (((x) & 0xF0) >> 4) ++#define OTP_STS_PASS 0x0 ++#define OTP_STS_FAIL 0x1 ++#define OTP_STS_CMP_FAIL 0x2 ++#define OTP_STS_REGION_FAIL 0x3 ++#define OTP_STS_MASTER_FAIL 0x4 + -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_OOB_TX_DMA_EN -+ | ESPI_CTRL_OOB_RX_DMA_EN -+ | ESPI_CTRL_OOB_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); ++/* OTP ECC EN */ ++#define ECC_ENABLE 0x1 ++#define ECC_DISABLE 0x0 ++#define ECCBRP_EN BIT(0) + -+ if (oob->dma.enable) { -+ dmam_free_coherent(dev, PAGE_SIZE, oob->dma.tx_virt, oob->dma.tx_addr); -+ dmam_free_coherent(dev, PAGE_SIZE, oob->dma.rx_virt, oob->dma.rx_addr); -+ } ++#define ROM_REGION_START_ADDR 0x0 ++#define ROM_REGION_END_ADDR 0x3e0 ++#define RBP_REGION_START_ADDR ROM_REGION_END_ADDR ++#define RBP_REGION_END_ADDR 0x400 ++#define CONF_REGION_START_ADDR RBP_REGION_END_ADDR ++#define CONF_REGION_END_ADDR 0x420 ++#define STRAP_REGION_START_ADDR CONF_REGION_END_ADDR ++#define STRAP_REGION_END_ADDR 0x430 ++#define STRAPEXT_REGION_START_ADDR STRAP_REGION_END_ADDR ++#define STRAPEXT_REGION_END_ADDR 0x440 ++#define USER_REGION_START_ADDR STRAPEXT_REGION_END_ADDR ++#define USER_REGION_END_ADDR 0x1000 ++#define SEC_REGION_START_ADDR USER_REGION_END_ADDR ++#define SEC_REGION_END_ADDR 0x1c00 ++#define CAL_REGION_START_ADDR SEC_REGION_END_ADDR ++#define CAL_REGION_END_ADDR 0x1f80 ++#define SW_PUF_REGION_START_ADDR CAL_REGION_END_ADDR ++#define SW_PUF_REGION_END_ADDR 0x1fc0 ++#define HW_PUF_REGION_START_ADDR SW_PUF_REGION_END_ADDR ++#define HW_PUF_REGION_END_ADDR 0x2000 + -+ mutex_destroy(&oob->tx_mtx); -+ mutex_destroy(&oob->rx_mtx); ++#define OTP_MEMORY_SIZE (HW_PUF_REGION_END_ADDR * 2) + -+ misc_deregister(&oob->mdev); ++#define OTP_TIMEOUT_US 10000 + -+ return 0; -+} ++/* OTPSTRAP */ ++#define OTPSTRAP0_ADDR STRAP_REGION_START_ADDR ++#define OTPSTRAP14_ADDR (OTPSTRAP0_ADDR + 0xe) + -+/* flash channel (CH3) */ -+static long ast2500_espi_flash_get_rx(struct file *fp, -+ struct ast2500_espi_flash *flash, -+ struct aspeed_espi_ioc *ioc) -+{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2500_espi *espi; -+ struct espi_comm_hdr *hdr; -+ unsigned long flags; -+ uint32_t pkt_len; -+ uint8_t *pkt; -+ int i, rc; ++#define OTPTOOL_VERSION(a, b, c) (((a) << 24) + ((b) << 12) + (c)) ++#define OTPTOOL_VERSION_MAJOR(x) (((x) >> 24) & 0xff) ++#define OTPTOOL_VERSION_PATCHLEVEL(x) (((x) >> 12) & 0xfff) ++#define OTPTOOL_VERSION_SUBLEVEL(x) ((x) & 0xfff) ++#define OTPTOOL_COMPT_VERSION 2 + -+ rc = 0; ++enum otp_error_code { ++ OTP_SUCCESS, ++ OTP_READ_FAIL, ++ OTP_PROG_FAIL, ++ OTP_CMP_FAIL, ++}; + -+ espi = container_of(flash, struct ast2500_espi, flash); ++enum aspeed_otp_master_id { ++ OTP_M0 = 0, ++ OTP_M1, ++ OTP_M2, ++ OTP_M3, ++ OTP_M4, ++ OTP_M5, ++ OTP_MID_MAX, ++}; + -+ if (fp->f_flags & O_NONBLOCK) { -+ if (!mutex_trylock(&flash->rx_mtx)) -+ return -EAGAIN; ++struct aspeed_otp { ++ struct miscdevice miscdev; ++ struct device *dev; ++ void __iomem *base; ++ u32 chip_revid0; ++ u32 chip_revid1; ++ bool is_open; ++ int gbl_ecc_en; ++ u8 *data; ++}; + -+ if (!flash->rx_ready) { -+ rc = -ENODATA; -+ goto unlock_mtx_n_out; -+ } -+ } else { -+ mutex_lock(&flash->rx_mtx); ++enum otp_ioctl_cmds { ++ GET_ECC_STATUS = 1, ++ SET_ECC_ENABLE, ++}; + -+ if (!flash->rx_ready) { -+ rc = wait_event_interruptible(flash->wq, flash->rx_ready); -+ if (rc == -ERESTARTSYS) { -+ rc = -EINTR; -+ goto unlock_mtx_n_out; -+ } -+ } -+ } ++enum otp_ecc_codes { ++ OTP_ECC_MISMATCH = -1, ++ OTP_ECC_DISABLE = 0, ++ OTP_ECC_ENABLE = 1, ++}; + -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ reg = readl(espi->regs + ESPI_FLASH_RX_CTRL); -+ cyc = FIELD_GET(ESPI_FLASH_RX_CTRL_CYC, reg); -+ tag = FIELD_GET(ESPI_FLASH_RX_CTRL_TAG, reg); -+ len = FIELD_GET(ESPI_FLASH_RX_CTRL_LEN, reg); ++static void otp_unlock(struct device *dev) ++{ ++ struct aspeed_otp *ctx = dev_get_drvdata(dev); + -+ /* -+ * calculate the length of the rest part of the -+ * eSPI packet to be read from HW and copied to -+ * user space. -+ */ -+ switch (cyc) { -+ case ESPI_FLASH_WRITE: -+ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + -+ sizeof(struct espi_flash_rwe); -+ break; -+ case ESPI_FLASH_READ: -+ case ESPI_FLASH_ERASE: -+ pkt_len = sizeof(struct espi_flash_rwe); -+ break; -+ case ESPI_FLASH_SUC_CMPLT_D_MIDDLE: -+ case ESPI_FLASH_SUC_CMPLT_D_FIRST: -+ case ESPI_FLASH_SUC_CMPLT_D_LAST: -+ case ESPI_FLASH_SUC_CMPLT_D_ONLY: -+ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + -+ sizeof(struct espi_flash_cmplt); -+ break; -+ case ESPI_FLASH_SUC_CMPLT: -+ case ESPI_FLASH_UNSUC_CMPLT: -+ pkt_len = sizeof(struct espi_flash_cmplt); -+ break; -+ default: -+ rc = -EFAULT; -+ goto unlock_mtx_n_out; -+ } ++ writel(OTP_PASSWD, ctx->base + OTP_KEY); ++} + -+ if (ioc->pkt_len < pkt_len) { -+ rc = -EINVAL; -+ goto unlock_mtx_n_out; -+ } ++static void otp_lock(struct device *dev) ++{ ++ struct aspeed_otp *ctx = dev_get_drvdata(dev); + -+ pkt = vmalloc(pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_mtx_n_out; -+ } ++ writel(0x1, ctx->base + OTP_KEY); ++} + -+ hdr = (struct espi_comm_hdr *)pkt; -+ hdr->cyc = cyc; -+ hdr->tag = tag; -+ hdr->len_h = len >> 8; -+ hdr->len_l = len & 0xff; ++static int wait_complete(struct device *dev) ++{ ++ struct aspeed_otp *ctx = dev_get_drvdata(dev); ++ int ret; ++ u32 val; + -+ if (flash->dma.enable) { -+ memcpy(hdr + 1, flash->dma.rx_virt, pkt_len - sizeof(*hdr)); -+ } else { -+ for (i = sizeof(*hdr); i < pkt_len; ++i) -+ pkt[i] = readl(espi->regs + ESPI_FLASH_RX_DATA) & 0xff; -+ } ++ ret = readl_poll_timeout(ctx->base + OTP_STATUS, val, (val == 0x0), ++ 1, OTP_TIMEOUT_US); ++ if (ret) ++ dev_warn(dev, "timeout. sts:0x%x\n", val); + -+ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } ++ return ret; ++} ++ ++static int otp_read_data(struct aspeed_otp *ctx, u32 offset, u16 *data) ++{ ++ struct device *dev = ctx->dev; ++ int ret; ++ ++ writel(ctx->gbl_ecc_en, ctx->base + OTP_ECC_EN); ++ writel(offset, ctx->base + OTP_ADDR); ++ writel(OTP_CMD_READ, ctx->base + OTP_CMD); ++ ret = wait_complete(dev); ++ if (ret) ++ return OTP_READ_FAIL; ++ ++ data[0] = readl(ctx->base + OTP_RDATA); ++ ++ return 0; ++} + -+ spin_lock_irqsave(&flash->lock, flags); ++static int otp_prog_data(struct aspeed_otp *ctx, u32 offset, u16 data) ++{ ++ struct device *dev = ctx->dev; ++ int ret; + -+ writel(ESPI_FLASH_RX_CTRL_SERV_PEND, espi->regs + ESPI_FLASH_RX_CTRL); -+ flash->rx_ready = 0; ++ writel(ctx->gbl_ecc_en, ctx->base + OTP_ECC_EN); ++ writel(offset, ctx->base + OTP_ADDR); ++ writel(data, ctx->base + OTP_WDATA_0); ++ writel(OTP_CMD_PROG, ctx->base + OTP_CMD); ++ ret = wait_complete(dev); ++ if (ret) ++ return OTP_PROG_FAIL; + -+ spin_unlock_irqrestore(&flash->lock, flags); ++ return 0; ++} + -+ rc = 0; ++static int otp_prog_multi_data(struct aspeed_otp *ctx, u32 offset, u32 *data, int count) ++{ ++ struct device *dev = ctx->dev; ++ int ret; + -+free_n_out: -+ vfree(pkt); ++ writel(ctx->gbl_ecc_en, ctx->base + OTP_ECC_EN); ++ writel(offset, ctx->base + OTP_ADDR); ++ for (int i = 0; i < count; i++) ++ writel(data[i], ctx->base + OTP_WDATA_0 + 4 * i); + -+unlock_mtx_n_out: -+ mutex_unlock(&flash->rx_mtx); ++ writel(OTP_CMD_PROG_MULTI, ctx->base + OTP_CMD); ++ ret = wait_complete(dev); ++ if (ret) ++ return OTP_PROG_FAIL; + -+ return rc; ++ return 0; +} + -+static long ast2500_espi_flash_put_tx(struct file *fp, -+ struct ast2500_espi_flash *flash, -+ struct aspeed_espi_ioc *ioc) ++static int aspeed_otp_read(struct aspeed_otp *ctx, int offset, ++ void *buf, int size) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2500_espi *espi; -+ struct espi_comm_hdr *hdr; -+ uint8_t *pkt; -+ int i, rc; ++ struct device *dev = ctx->dev; ++ u16 *data = buf; ++ int ret; + -+ espi = container_of(flash, struct ast2500_espi, flash); ++ otp_unlock(dev); ++ for (int i = 0; i < size; i++) { ++ ret = otp_read_data(ctx, offset + i, data + i); ++ if (ret) { ++ dev_warn(ctx->dev, "read failed\n"); ++ break; ++ } ++ } + -+ if (!mutex_trylock(&flash->tx_mtx)) -+ return -EAGAIN; ++ otp_lock(dev); ++ return ret; ++} + -+ reg = readl(espi->regs + ESPI_FLASH_TX_CTRL); -+ if (reg & ESPI_FLASH_TX_CTRL_TRIG_PEND) { -+ rc = -EBUSY; -+ goto unlock_mtx_n_out; -+ } ++static int aspeed_otp_write(struct aspeed_otp *ctx, int offset, ++ const void *buf, int size) ++{ ++ struct device *dev = ctx->dev; ++ u32 *data32 = (u32 *)buf; ++ u16 *data = (u16 *)buf; ++ int ret; + -+ pkt = vmalloc(ioc->pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_mtx_n_out; -+ } ++ otp_unlock(dev); + -+ hdr = (struct espi_comm_hdr *)pkt; ++ if (size == 1) ++ ret = otp_prog_data(ctx, offset, data[0]); ++ else ++ ret = otp_prog_multi_data(ctx, offset, data32, size / 2); + -+ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } ++ if (ret) ++ dev_warn(ctx->dev, "prog failed\n"); + -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ if (flash->dma.enable) { -+ memcpy(flash->dma.tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); -+ dma_wmb(); -+ } else { -+ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_FLASH_TX_DATA); -+ } ++ otp_lock(dev); ++ return ret; ++} + -+ cyc = hdr->cyc; -+ tag = hdr->tag; -+ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); ++static int aspeed_otp_ecc_en(struct aspeed_otp *ctx) ++{ ++ struct device *dev = ctx->dev; ++ int ret = 0; + -+ reg = FIELD_PREP(ESPI_FLASH_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_FLASH_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_FLASH_TX_CTRL_LEN, len) -+ | ESPI_FLASH_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_FLASH_TX_CTRL); ++ /* Check ecc is already enabled */ ++ if (ctx->gbl_ecc_en == 1) ++ return 0; + -+ rc = 0; ++ otp_unlock(dev); + -+free_n_out: -+ vfree(pkt); ++ /* enable cfg ecc */ ++ ret = otp_prog_data(ctx, OTPSTRAP14_ADDR, 0x1); ++ if (ret) { ++ dev_warn(dev, "%s: prog failed\n", __func__); ++ goto end; ++ } + -+unlock_mtx_n_out: -+ mutex_unlock(&flash->tx_mtx); ++ ctx->gbl_ecc_en = 1; ++end: ++ otp_lock(dev); + -+ return rc; ++ return ret; +} + -+static long ast2500_espi_flash_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static long aspeed_otp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ -+ struct ast2500_espi_flash *flash; -+ struct aspeed_espi_ioc ioc; ++ struct miscdevice *c = file->private_data; ++ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); ++ void __user *argp = (void __user *)arg; ++ struct otp_revid revid; ++ struct otp_read rdata; ++ struct otp_prog pdata; ++ int ret = 0; + -+ flash = container_of(fp->private_data, struct ast2500_espi_flash, mdev); ++ switch (cmd) { ++ case ASPEED_OTP_READ_DATA: ++ if (copy_from_user(&rdata, argp, sizeof(struct otp_read))) ++ return -EFAULT; + -+ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) -+ return -EFAULT; ++ ret = aspeed_otp_read(ctx, rdata.offset, ctx->data, rdata.len); ++ if (ret) ++ return -EFAULT; + -+ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) -+ return -EINVAL; ++ if (copy_to_user(rdata.data, ctx->data, rdata.len * 2)) ++ return -EFAULT; + -+ switch (cmd) { -+ case ASPEED_ESPI_FLASH_GET_RX: -+ return ast2500_espi_flash_get_rx(fp, flash, &ioc); -+ case ASPEED_ESPI_FLASH_PUT_TX: -+ return ast2500_espi_flash_put_tx(fp, flash, &ioc); ++ break; ++ ++ case ASPEED_OTP_PROG_DATA: ++ if (copy_from_user(&pdata, argp, sizeof(struct otp_prog))) ++ return -EFAULT; ++ ++ ret = aspeed_otp_write(ctx, pdata.w_offset, pdata.data, pdata.len); ++ break; ++ ++ case ASPEED_OTP_GET_ECC: ++ if (copy_to_user(argp, &ctx->gbl_ecc_en, sizeof(ctx->gbl_ecc_en))) ++ return -EFAULT; ++ break; ++ ++ case ASPEED_OTP_SET_ECC: ++ ret = aspeed_otp_ecc_en(ctx); ++ break; ++ ++ case ASPEED_OTP_GET_REVID: ++ revid.revid0 = ctx->chip_revid0; ++ revid.revid1 = ctx->chip_revid1; ++ if (copy_to_user(argp, &revid, sizeof(struct otp_revid))) ++ return -EFAULT; ++ break; + default: ++ dev_warn(ctx->dev, "cmd 0x%x is not supported\n", cmd); + break; -+ }; ++ } + -+ return -EINVAL; ++ return ret; +} + -+static const struct file_operations ast2500_espi_flash_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2500_espi_flash_ioctl, -+}; -+ -+static void ast2500_espi_flash_isr(struct ast2500_espi *espi) ++static int aspeed_otp_ecc_init(struct device *dev) +{ -+ struct ast2500_espi_flash *flash; -+ unsigned long flags; -+ uint32_t sts; ++ struct aspeed_otp *ctx = dev_get_drvdata(dev); ++ int ret; ++ u32 val; + -+ flash = &espi->flash; ++ otp_unlock(dev); + -+ sts = readl(espi->regs + ESPI_INT_STS); ++ /* Check cfg_ecc_en */ ++ writel(0, ctx->base + OTP_ECC_EN); ++ writel(OTPSTRAP14_ADDR, ctx->base + OTP_ADDR); ++ writel(OTP_CMD_READ, ctx->base + OTP_CMD); ++ ret = wait_complete(dev); ++ if (ret) ++ return OTP_READ_FAIL; + -+ if (sts & ESPI_INT_STS_FLASH_RX_CMPLT) { -+ spin_lock_irqsave(&flash->lock, flags); -+ flash->rx_ready = true; -+ spin_unlock_irqrestore(&flash->lock, flags); ++ val = readl(ctx->base + OTP_RDATA); ++ if (val & 0x1) ++ ctx->gbl_ecc_en = 0x1; ++ else ++ ctx->gbl_ecc_en = 0x0; + -+ wake_up_interruptible(&flash->wq); ++ otp_lock(dev); + -+ writel(ESPI_INT_STS_FLASH_RX_CMPLT, espi->regs + ESPI_INT_STS); -+ } ++ return 0; +} + -+static void ast2500_espi_flash_reset(struct ast2500_espi *espi) ++static int aspeed_otp_open(struct inode *inode, struct file *file) +{ -+ struct ast2500_espi_flash *flash = &espi->flash; -+ uint32_t reg; ++ struct miscdevice *c = file->private_data; ++ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_FLASH); -+ writel(reg, espi->regs + ESPI_INT_EN); -+ writel(ESPI_INT_STS_FLASH, espi->regs + ESPI_INT_STS); ++ spin_lock(&otp_state_lock); + -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_FLASH_TX_SW_RST -+ | ESPI_CTRL_FLASH_RX_SW_RST -+ | ESPI_CTRL_FLASH_TX_DMA_EN -+ | ESPI_CTRL_FLASH_RX_DMA_EN -+ | ESPI_CTRL_FLASH_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); ++ if (ctx->is_open) { ++ spin_unlock(&otp_state_lock); ++ return -EBUSY; ++ } + -+ udelay(1); ++ ctx->is_open = true; + -+ reg |= (ESPI_CTRL_FLASH_TX_SW_RST | ESPI_CTRL_FLASH_RX_SW_RST); -+ writel(reg, espi->regs + ESPI_CTRL); ++ spin_unlock(&otp_state_lock); + -+ if (flash->safs.mode == SAFS_MODE_MIX) { -+ reg = FIELD_PREP(ESPI_FLASH_SAFS_TADDR_BASE, flash->safs.taddr >> 24) -+ | FIELD_PREP(ESPI_FLASH_SAFS_TADDR_MASK, (~(flash->safs.size - 1)) >> 24); -+ writel(reg, espi->regs + ESPI_FLASH_SAFS_TADDR); -+ } else { -+ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_FLASH_SAFS_SW_MODE; -+ writel(reg, espi->regs + ESPI_CTRL); -+ } ++ return 0; ++} + -+ if (flash->dma.enable) { -+ writel(flash->dma.tx_addr, espi->regs + ESPI_FLASH_TX_DMA); -+ writel(flash->dma.rx_addr, espi->regs + ESPI_FLASH_RX_DMA); ++static int aspeed_otp_release(struct inode *inode, struct file *file) ++{ ++ struct miscdevice *c = file->private_data; ++ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); + -+ reg = readl(espi->regs + ESPI_CTRL) -+ | ESPI_CTRL_FLASH_TX_DMA_EN -+ | ESPI_CTRL_FLASH_RX_DMA_EN; -+ writel(reg, espi->regs + ESPI_CTRL); -+ } ++ spin_lock(&otp_state_lock); + -+ reg = readl(espi->regs + ESPI_INT_EN) | ESPI_INT_EN_FLASH_RX_CMPLT; -+ writel(reg, espi->regs + ESPI_INT_EN); ++ ctx->is_open = false; + -+ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_FLASH_SW_RDY; -+ writel(reg, espi->regs + ESPI_CTRL); ++ spin_unlock(&otp_state_lock); ++ ++ return 0; +} + -+static int ast2500_espi_flash_probe(struct ast2500_espi *espi) ++static const struct file_operations otp_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = aspeed_otp_ioctl, ++ .open = aspeed_otp_open, ++ .release = aspeed_otp_release, ++}; ++ ++static const struct of_device_id aspeed_otp_of_matches[] = { ++ { .compatible = "aspeed,ast2700-otp" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, aspeed_otp_of_matches); ++ ++static int aspeed_otp_probe(struct platform_device *pdev) +{ -+ struct ast2500_espi_flash *flash; -+ struct device *dev; ++ struct device *dev = &pdev->dev; ++ struct regmap *scu0, *scu1; ++ struct aspeed_otp *priv; ++ struct resource *res; + int rc; + -+ dev = espi->dev; -+ -+ flash = &espi->flash; ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; + -+ init_waitqueue_head(&flash->wq); ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM\n"); ++ return -ENOENT; ++ } + -+ spin_lock_init(&flash->lock); ++ priv->base = devm_ioremap_resource(&pdev->dev, res); ++ if (!priv->base) ++ return -EIO; + -+ mutex_init(&flash->tx_mtx); -+ mutex_init(&flash->rx_mtx); ++ scu0 = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu0"); ++ scu1 = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu1"); ++ if (IS_ERR(scu0) || IS_ERR(scu1)) { ++ dev_err(dev, "failed to find SCU regmap\n"); ++ return PTR_ERR(scu0) || PTR_ERR(scu1); ++ } + -+ flash->safs.mode = SAFS_MODE_MIX; ++ regmap_read(scu0, 0x0, &priv->chip_revid0); ++ regmap_read(scu1, 0x0, &priv->chip_revid1); + -+ of_property_read_u32(dev->of_node, "flash-safs-mode", &flash->safs.mode); -+ if (flash->safs.mode == SAFS_MODE_MIX) { -+ rc = of_property_read_u32(dev->of_node, "flash-safs-tgt-addr", &flash->safs.taddr); -+ if (rc || !IS_ALIGNED(flash->safs.taddr, FLASH_SAFS_ALIGN)) { -+ dev_err(dev, "cannot get 16MB-aligned SAFS target address\n"); -+ return -ENODEV; -+ } ++ priv->dev = dev; ++ dev_set_drvdata(dev, priv); + -+ rc = of_property_read_u32(dev->of_node, "flash-safs-size", &flash->safs.size); -+ if (rc || !IS_ALIGNED(flash->safs.size, FLASH_SAFS_ALIGN)) { -+ dev_err(dev, "cannot get 16MB-aligned SAFS size\n"); -+ return -ENODEV; -+ } -+ } ++ /* OTP ECC init */ ++ rc = aspeed_otp_ecc_init(dev); ++ if (rc) ++ return -EIO; + -+ flash->dma.enable = of_property_read_bool(dev->of_node, "flash-dma-mode"); -+ if (flash->dma.enable) { -+ flash->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.tx_addr, GFP_KERNEL); -+ if (!flash->dma.tx_virt) { -+ dev_err(dev, "cannot allocate DMA TX buffer\n"); -+ return -ENOMEM; -+ } ++ priv->data = kmalloc(OTP_MEMORY_SIZE, GFP_KERNEL); ++ if (!priv->data) ++ return -ENOMEM; + -+ flash->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.rx_addr, GFP_KERNEL); -+ if (!flash->dma.rx_virt) { -+ dev_err(dev, "cannot allocate DMA RX buffer\n"); -+ return -ENOMEM; -+ } -+ } ++ /* Set up the miscdevice */ ++ priv->miscdev.minor = MISC_DYNAMIC_MINOR; ++ priv->miscdev.name = "aspeed-otp"; ++ priv->miscdev.fops = &otp_fops; + -+ flash->mdev.parent = dev; -+ flash->mdev.minor = MISC_DYNAMIC_MINOR; -+ flash->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-flash", DEVICE_NAME); -+ flash->mdev.fops = &ast2500_espi_flash_fops; -+ rc = misc_register(&flash->mdev); ++ /* Register the device */ ++ rc = misc_register(&priv->miscdev); + if (rc) { -+ dev_err(dev, "cannot register device %s\n", flash->mdev.name); ++ dev_err(dev, "Unable to register device\n"); + return rc; + } + -+ ast2500_espi_flash_reset(espi); ++ dev_info(dev, "Aspeed OTP driver successfully registered\n"); + + return 0; +} + -+static int ast2500_espi_flash_remove(struct ast2500_espi *espi) ++static void aspeed_otp_remove(struct platform_device *pdev) +{ -+ struct ast2500_espi_flash *flash; -+ struct device *dev; -+ uint32_t reg; ++ struct aspeed_otp *ctx = dev_get_drvdata(&pdev->dev); + -+ dev = espi->dev; ++ kfree(ctx->data); ++ misc_deregister(&ctx->miscdev); ++} + -+ flash = &espi->flash; ++static struct platform_driver aspeed_otp_driver = { ++ .probe = aspeed_otp_probe, ++ .remove = aspeed_otp_remove, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = aspeed_otp_of_matches, ++ }, ++}; + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_FLASH); -+ writel(reg, espi->regs + ESPI_INT_EN); ++module_platform_driver(aspeed_otp_driver); + -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_FLASH_TX_DMA_EN -+ | ESPI_CTRL_FLASH_RX_DMA_EN -+ | ESPI_CTRL_FLASH_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); ++MODULE_AUTHOR("Neal Liu "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("ASPEED OTP Driver"); +diff --git a/drivers/soc/aspeed/espi/Kconfig b/drivers/soc/aspeed/espi/Kconfig +--- a/drivers/soc/aspeed/espi/Kconfig 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/Kconfig 2026-04-08 18:03:48.312705284 +0000 +@@ -0,0 +1,18 @@ ++menu "ASPEED eSPI drivers" + -+ if (flash->dma.enable) { -+ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.tx_virt, flash->dma.tx_addr); -+ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.rx_virt, flash->dma.rx_addr); -+ } ++config ASPEED_ESPI ++ tristate "ASPEED eSPI slave driver" ++ help ++ Enable driver support for Aspeed AST2700 eSPI engine. The eSPI engine ++ plays as a slave device in BMC to communicate with the Host over ++ the eSPI interface. The four eSPI channels, namely peripheral, ++ virtual wire, out-of-band, and flash are supported. + -+ mutex_destroy(&flash->tx_mtx); -+ mutex_destroy(&flash->rx_mtx); ++config AST2700_RTC_OVER_ESPI ++ tristate "ASPEED AST2700 RTC over eSPI drvier" ++ depends on HAS_IOMEM ++ help ++ Enable driver support for Aspeed AST2700 RTC over eSPI function. ++ Periodically copies RAM content from a RTC tm to a memory-mapped eSPI region. + -+ misc_deregister(&flash->mdev); ++endmenu +diff --git a/drivers/soc/aspeed/espi/Makefile b/drivers/soc/aspeed/espi/Makefile +--- a/drivers/soc/aspeed/espi/Makefile 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/Makefile 2026-04-08 18:03:48.312705284 +0000 +@@ -0,0 +1,6 @@ ++obj-$(CONFIG_ASPEED_ESPI) += \ ++ ast2700-espi.o \ ++ ast2600-espi.o \ ++ ast2500-espi.o \ ++ aspeed-espi.o ++obj-$(CONFIG_AST2700_RTC_OVER_ESPI) += ast2700-rtc-over-espi.o +diff --git a/drivers/soc/aspeed/espi/aspeed-espi-comm.h b/drivers/soc/aspeed/espi/aspeed-espi-comm.h +--- a/drivers/soc/aspeed/espi/aspeed-espi-comm.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/aspeed-espi-comm.h 2026-04-08 18:03:48.312705284 +0000 +@@ -0,0 +1,207 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Aspeed eSPI protocol definitions and IOCTL methods ++ * Copyright 2023 Aspeed Technology Inc. ++ */ ++#ifndef __ASPEED_ESPI_COMM_H__ ++#define __ASPEED_ESPI_COMM_H__ + -+ return 0; -+} ++#include ++#include + -+/* global control */ -+static irqreturn_t ast2500_espi_isr(int irq, void *arg) -+{ -+ struct ast2500_espi *espi; -+ uint32_t sts; ++/* ++ * eSPI cycle type encoding ++ * ++ * Section 5.1 Cycle Types and Packet Format, ++ * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. ++ */ ++#define ESPI_PERIF_MEMRD32 0x00 ++#define ESPI_PERIF_MEMRD64 0x02 ++#define ESPI_PERIF_MEMWR32 0x01 ++#define ESPI_PERIF_MEMWR64 0x03 ++#define ESPI_PERIF_MSG 0x10 ++#define ESPI_PERIF_MSG_D 0x11 ++#define ESPI_PERIF_SUC_CMPLT 0x06 ++#define ESPI_PERIF_SUC_CMPLT_D_MIDDLE 0x09 ++#define ESPI_PERIF_SUC_CMPLT_D_FIRST 0x0b ++#define ESPI_PERIF_SUC_CMPLT_D_LAST 0x0d ++#define ESPI_PERIF_SUC_CMPLT_D_ONLY 0x0f ++#define ESPI_PERIF_UNSUC_CMPLT 0x0c ++#define ESPI_OOB_MSG 0x21 ++#define ESPI_FLASH_READ 0x00 ++#define ESPI_FLASH_WRITE 0x01 ++#define ESPI_FLASH_ERASE 0x02 ++#define ESPI_FLASH_SUC_CMPLT 0x06 ++#define ESPI_FLASH_SUC_CMPLT_D_MIDDLE 0x09 ++#define ESPI_FLASH_SUC_CMPLT_D_FIRST 0x0b ++#define ESPI_FLASH_SUC_CMPLT_D_LAST 0x0d ++#define ESPI_FLASH_SUC_CMPLT_D_ONLY 0x0f ++#define ESPI_FLASH_UNSUC_CMPLT 0x0c + -+ espi = (struct ast2500_espi *)arg; ++/* ++ * eSPI packet format structure ++ * ++ * Section 5.1 Cycle Types and Packet Format, ++ * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. ++ */ ++struct espi_comm_hdr { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++}; + -+ sts = readl(espi->regs + ESPI_INT_STS); -+ if (!sts) -+ return IRQ_NONE; ++struct espi_perif_mem32 { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++ u32 addr_be; ++ u8 data[]; ++} __packed; + -+ if (sts & ESPI_INT_STS_PERIF) -+ ast2500_espi_perif_isr(espi); ++struct espi_perif_mem64 { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++ u32 addr_be; ++ u8 data[]; ++} __packed; ++ ++struct espi_perif_msg { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++ u8 msg_code; ++ u8 msg_byte[4]; ++ u8 data[]; ++} __packed; ++ ++struct espi_perif_cmplt { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++ u8 data[]; ++} __packed; ++ ++struct espi_oob_msg { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++ u8 data[]; ++}; ++ ++struct espi_flash_rwe { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++ u32 addr_be; ++ u8 data[]; ++} __packed; ++ ++struct espi_flash_cmplt { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++ u8 data[]; ++} __packed; ++ ++#define ESPI_MAX_PLD_LEN BIT(12) ++ ++/* ++ * Aspeed IOCTL for eSPI raw packet send/receive ++ * ++ * This IOCTL interface works in the eSPI packet in/out paradigm. ++ * ++ * Only the virtual wire IOCTL is a special case which does not send ++ * or receive an eSPI packet. However, to keep a more consisten use from ++ * userspace, we make all of the four channel drivers serve through the ++ * IOCTL interface. ++ * ++ * For the eSPI packet format, refer to ++ * Section 5.1 Cycle Types and Packet Format, ++ * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. ++ * ++ * For the example user apps using these IOCTL, refer to ++ * https://github.com/AspeedTech-BMC/aspeed_app/tree/master/espi_test ++ */ ++#define __ASPEED_ESPI_IOCTL_MAGIC 0xb8 ++ ++/* ++ * we choose the longest header and the max payload size ++ * based on the Intel specification to define the maximum ++ * eSPI packet length ++ */ ++#define ESPI_MAX_PKT_LEN (sizeof(struct espi_perif_msg) + ESPI_MAX_PLD_LEN) ++ ++struct aspeed_espi_ioc { ++ u32 pkt_len; ++ u8 *pkt; ++}; + -+ if (sts & ESPI_INT_STS_VW) -+ ast2500_espi_vw_isr(espi); ++/* ++ * Peripheral Channel (CH0) ++ * - ASPEED_ESPI_PERIF_PC_GET_RX ++ * Receive an eSPI Posted/Completion packet ++ * - ASPEED_ESPI_PERIF_PC_PUT_TX ++ * Transmit an eSPI Posted/Completion packet ++ * - ASPEED_ESPI_PERIF_NP_PUT_TX ++ * Transmit an eSPI Non-Posted packet ++ */ ++#define ASPEED_ESPI_PERIF_PC_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x00, struct aspeed_espi_ioc) ++#define ASPEED_ESPI_PERIF_PC_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x01, struct aspeed_espi_ioc) ++#define ASPEED_ESPI_PERIF_NP_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x02, struct aspeed_espi_ioc) ++/* ++ * Virtual Wire Channel (CH1) ++ * - ASPEED_ESPI_VW_GET_GPIO_VAL ++ * Read the input value of GPIO over the VW channel ++ * - ASPEED_ESPI_VW_PUT_GPIO_VAL ++ * Write the output value of GPIO over the VW channel ++ * - ASPEED_ESPI_VW_GET_GPIO_VAL1 (new feature in AST2700) ++ * Read the input value1 of GPIO over the VW channel ++ * - ASPEED_ESPI_VW_PUT_GPIO_VAL1 (new feature in AST2700) ++ * Write the output value1 of GPIO over the VW channel ++ */ ++#define ASPEED_ESPI_VW_GET_GPIO_VAL _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x10, u32) ++#define ASPEED_ESPI_VW_PUT_GPIO_VAL _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x11, u32) ++#ifdef CONFIG_ARM64 ++#define ASPEED_ESPI_VW_GET_GPIO_VAL1 _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x12, u32) ++#define ASPEED_ESPI_VW_PUT_GPIO_VAL1 _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x13, u32) ++#endif ++/* ++ * Out-of-band Channel (CH2) ++ * - ASPEED_ESPI_OOB_GET_RX ++ * Receive an eSPI OOB packet ++ * - ASPEED_ESPI_OOB_PUT_TX ++ * Transmit an eSPI OOB packet ++ */ ++#define ASPEED_ESPI_OOB_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x20, struct aspeed_espi_ioc) ++#define ASPEED_ESPI_OOB_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x21, struct aspeed_espi_ioc) ++/* ++ * Flash Channel (CH3) ++ * - ASPEED_ESPI_FLASH_GET_RX ++ * Receive an eSPI flash packet ++ * - ASPEED_ESPI_FLASH_PUT_TX ++ * Transmit an eSPI flash packet ++ */ ++#define ASPEED_ESPI_FLASH_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x30, struct aspeed_espi_ioc) ++#define ASPEED_ESPI_FLASH_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x31, struct aspeed_espi_ioc) + -+ if (sts & ESPI_INT_STS_OOB) -+ ast2500_espi_oob_isr(espi); ++#endif +diff --git a/drivers/soc/aspeed/espi/aspeed-espi.c b/drivers/soc/aspeed/espi/aspeed-espi.c +--- a/drivers/soc/aspeed/espi/aspeed-espi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/aspeed-espi.c 2026-04-08 18:03:48.312705284 +0000 +@@ -0,0 +1,256 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Unified Aspeed eSPI driver (AST2500/AST2600/AST2700) ++ * ++ * This wraps the existing SoC-specific implementations behind a ++ * single driver name and compatible strings. For now we keep the ++ * full implementations in their original files and dispatch through ++ * small per-SoC glue ops. ++ */ + -+ if (sts & ESPI_INT_STS_FLASH) -+ ast2500_espi_flash_isr(espi); ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ if (sts & ESPI_INT_STS_RST_DEASSERT) { -+ ast2500_espi_perif_reset(espi); -+ ast2500_espi_vw_reset(espi); -+ ast2500_espi_oob_reset(espi); -+ ast2500_espi_flash_reset(espi); -+ writel(ESPI_INT_STS_RST_DEASSERT, espi->regs + ESPI_INT_STS); -+ } ++#include "aspeed-espi.h" ++#include "ast2500-espi.h" ++#include "ast2600-espi.h" ++#include "ast2700-espi.h" + -+ return IRQ_HANDLED; -+} ++struct aspeed_espi_ops { ++ enum aspeed_espi_platform_id platform_id; ++ void (*espi_pre_init)(struct aspeed_espi *espi); ++ void (*espi_post_init)(struct aspeed_espi *espi); ++ void (*espi_deinit)(struct aspeed_espi *espi); ++ int (*espi_perif_probe)(struct aspeed_espi *espi); ++ int (*espi_perif_remove)(struct aspeed_espi *espi); ++ int (*espi_vw_probe)(struct aspeed_espi *espi); ++ int (*espi_vw_remove)(struct aspeed_espi *espi); ++ int (*espi_oob_probe)(struct aspeed_espi *espi); ++ int (*espi_oob_remove)(struct aspeed_espi *espi); ++ int (*espi_flash_probe)(struct aspeed_espi *espi); ++ int (*espi_flash_remove)(struct aspeed_espi *espi); ++ irqreturn_t (*espi_isr)(int irq, void *espi); ++}; ++ ++static const struct aspeed_espi_ops aspeed_espi_ast2500_ops = { ++ .platform_id = ASPEED_ESPI_PLATFORM_ID_AST2500, ++ .espi_pre_init = ast2500_espi_pre_init, ++ .espi_post_init = ast2500_espi_post_init, ++ .espi_deinit = ast2500_espi_deinit, ++ .espi_perif_probe = ast2500_espi_perif_probe, ++ .espi_perif_remove = ast2500_espi_perif_remove, ++ .espi_vw_probe = ast2500_espi_vw_probe, ++ .espi_vw_remove = ast2500_espi_vw_remove, ++ .espi_oob_probe = ast2500_espi_oob_probe, ++ .espi_oob_remove = ast2500_espi_oob_remove, ++ .espi_flash_probe = ast2500_espi_flash_probe, ++ .espi_flash_remove = ast2500_espi_flash_remove, ++ .espi_isr = ast2500_espi_isr, ++}; ++ ++static const struct aspeed_espi_ops aspeed_espi_ast2600_ops = { ++ .platform_id = ASPEED_ESPI_PLATFORM_ID_AST2600, ++ .espi_pre_init = ast2600_espi_pre_init, ++ .espi_post_init = ast2600_espi_post_init, ++ .espi_deinit = ast2600_espi_deinit, ++ .espi_perif_probe = ast2600_espi_perif_probe, ++ .espi_perif_remove = ast2600_espi_perif_remove, ++ .espi_vw_probe = ast2600_espi_vw_probe, ++ .espi_vw_remove = ast2600_espi_vw_remove, ++ .espi_oob_probe = ast2600_espi_oob_probe, ++ .espi_oob_remove = ast2600_espi_oob_remove, ++ .espi_flash_probe = ast2600_espi_flash_probe, ++ .espi_flash_remove = ast2600_espi_flash_remove, ++ .espi_isr = ast2600_espi_isr, ++}; ++ ++static const struct aspeed_espi_ops aspeed_espi_ast2700_ops = { ++ .platform_id = ASPEED_ESPI_PLATFORM_ID_AST2700, ++ .espi_pre_init = ast2700_espi_pre_init, ++ .espi_post_init = ast2700_espi_post_init, ++ .espi_deinit = ast2700_espi_deinit, ++ .espi_perif_probe = ast2700_espi_perif_probe, ++ .espi_perif_remove = ast2700_espi_perif_remove, ++ .espi_vw_probe = ast2700_espi_vw_probe, ++ .espi_vw_remove = ast2700_espi_vw_remove, ++ .espi_oob_probe = ast2700_espi_oob_probe, ++ .espi_oob_remove = ast2700_espi_oob_remove, ++ .espi_flash_probe = ast2700_espi_flash_probe, ++ .espi_flash_remove = ast2700_espi_flash_remove, ++ .espi_isr = ast2700_espi_isr, ++}; ++ ++static const struct of_device_id aspeed_espi_of_matches[] = { ++ { .compatible = "aspeed,ast2500-espi", .data = &aspeed_espi_ast2500_ops }, ++ { .compatible = "aspeed,ast2600-espi", .data = &aspeed_espi_ast2600_ops }, ++ { .compatible = "aspeed,ast2700-espi", .data = &aspeed_espi_ast2700_ops }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, aspeed_espi_of_matches); + -+static int ast2500_espi_probe(struct platform_device *pdev) ++static int aspeed_espi_probe(struct platform_device *pdev) +{ -+ struct ast2500_espi *espi; ++ const struct of_device_id *match; ++ struct device *dev = &pdev->dev; ++ struct aspeed_espi *espi; + struct resource *res; -+ struct device *dev; -+ uint32_t reg; + int rc; + -+ dev = &pdev->dev; -+ + espi = devm_kzalloc(dev, sizeof(*espi), GFP_KERNEL); + if (!espi) + return -ENOMEM; + + espi->dev = dev; ++ match = of_match_device(aspeed_espi_of_matches, dev); ++ if (!match) ++ return -ENODEV; ++ espi->ops = match->data; ++ ++ espi->pdev = pdev; + + rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (rc) { @@ -68551,6 +75479,12 @@ diff --git a/drivers/soc/aspeed/ast2500-espi.c b/drivers/soc/aspeed/ast2500-espi + return -ENODEV; + } + ++ espi->rst = devm_reset_control_get_optional(&pdev->dev, NULL); ++ if (IS_ERR(espi->rst)) { ++ dev_err(dev, "cannot get reset control\n"); ++ return PTR_ERR(espi->rst); ++ } ++ + espi->clk = devm_clk_get(dev, NULL); + if (IS_ERR(espi->clk)) { + dev_err(dev, "cannot get clock control\n"); @@ -68563,410 +75497,141 @@ diff --git a/drivers/soc/aspeed/ast2500-espi.c b/drivers/soc/aspeed/ast2500-espi + return rc; + } + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~ESPI_INT_EN_RST_DEASSERT; -+ writel(reg, espi->regs + ESPI_INT_EN); ++ espi->ops->espi_pre_init(espi); + -+ rc = ast2500_espi_perif_probe(espi); ++ rc = espi->ops->espi_perif_probe(espi); + if (rc) { + dev_err(dev, "cannot init peripheral channel, rc=%d\n", rc); + return rc; + } + -+ rc = ast2500_espi_vw_probe(espi); ++ rc = espi->ops->espi_vw_probe(espi); + if (rc) { + dev_err(dev, "cannot init vw channel, rc=%d\n", rc); + goto err_remove_perif; + } + -+ rc = ast2500_espi_oob_probe(espi); ++ rc = espi->ops->espi_oob_probe(espi); + if (rc) { + dev_err(dev, "cannot init oob channel, rc=%d\n", rc); + goto err_remove_vw; + } + -+ rc = ast2500_espi_flash_probe(espi); ++ rc = espi->ops->espi_flash_probe(espi); + if (rc) { + dev_err(dev, "cannot init flash channel, rc=%d\n", rc); + goto err_remove_oob; + } + -+ rc = devm_request_irq(dev, espi->irq, ast2500_espi_isr, 0, dev_name(dev), espi); ++ rc = devm_request_irq(dev, espi->irq, espi->ops->espi_isr, 0, ++ dev_name(dev), espi); + if (rc) { + dev_err(dev, "cannot request IRQ\n"); + goto err_remove_flash; + } + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg |= ESPI_INT_EN_RST_DEASSERT; -+ writel(reg, espi->regs + ESPI_INT_EN); ++ if (espi->perif.mmbi.enable) { ++ rc = devm_request_irq(dev, espi->perif.mmbi.irq, ++ espi->perif.mmbi.mmbi_isr, 0, dev_name(dev), ++ espi); ++ if (rc) { ++ dev_err(dev, "cannot request MMBI IRQ\n"); ++ goto err_remove_flash; ++ } ++ } + -+ dev_set_drvdata(dev, espi); ++ espi->ops->espi_post_init(espi); ++ ++ platform_set_drvdata(pdev, espi); + + dev_info(dev, "module loaded\n"); + + return 0; + +err_remove_flash: -+ ast2500_espi_flash_remove(espi); ++ espi->ops->espi_flash_remove(espi); +err_remove_oob: -+ ast2500_espi_oob_remove(espi); ++ espi->ops->espi_oob_remove(espi); +err_remove_vw: -+ ast2500_espi_vw_remove(espi); ++ espi->ops->espi_vw_remove(espi); +err_remove_perif: -+ ast2500_espi_perif_remove(espi); ++ espi->ops->espi_perif_remove(espi); + + return rc; +} + -+static int ast2500_espi_remove(struct platform_device *pdev) ++static void aspeed_espi_remove(struct platform_device *pdev) +{ -+ struct ast2500_espi *espi; ++ struct aspeed_espi *espi; + struct device *dev; -+ uint32_t reg; -+ int rc; + + dev = &pdev->dev; + -+ espi = (struct ast2500_espi *)dev_get_drvdata(dev); -+ -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_RST_DEASSERT); -+ writel(reg, espi->regs + ESPI_INT_EN); -+ -+ rc = ast2500_espi_perif_remove(espi); -+ if (rc) -+ dev_warn(dev, "cannot remove peripheral channel, rc=%d\n", rc); -+ -+ rc = ast2500_espi_vw_remove(espi); -+ if (rc) -+ dev_warn(dev, "cannot remove peripheral channel, rc=%d\n", rc); -+ -+ rc = ast2500_espi_oob_remove(espi); -+ if (rc) -+ dev_warn(dev, "cannot remove peripheral channel, rc=%d\n", rc); ++ espi = dev_get_drvdata(dev); + -+ rc = ast2500_espi_flash_remove(espi); -+ if (rc) -+ dev_warn(dev, "cannot remove peripheral channel, rc=%d\n", rc); ++ espi->ops->espi_deinit(espi); + -+ return 0; ++ if (espi->ops->espi_perif_remove(espi)) ++ dev_warn(dev, "cannot remove peripheral channel\n"); ++ if (espi->ops->espi_vw_remove(espi)) ++ dev_warn(dev, "cannot remove vw channel\n"); ++ if (espi->ops->espi_oob_remove(espi)) ++ dev_warn(dev, "cannot remove oob channel\n"); ++ if (espi->ops->espi_flash_remove(espi)) ++ dev_warn(dev, "cannot remove flash channel\n"); +} + -+static const struct of_device_id ast2500_espi_of_matches[] = { -+ { .compatible = "aspeed,ast2500-espi" }, -+ { }, -+}; -+ -+static struct platform_driver ast2500_espi_driver = { ++static struct platform_driver aspeed_espi_driver = { + .driver = { -+ .name = "ast2500-espi", -+ .of_match_table = ast2500_espi_of_matches, ++ .name = "aspeed-espi", ++ .of_match_table = aspeed_espi_of_matches, + }, -+ .probe = ast2500_espi_probe, -+ .remove = ast2500_espi_remove, ++ .probe = aspeed_espi_probe, ++ .remove = aspeed_espi_remove, +}; + -+module_platform_driver(ast2500_espi_driver); ++module_platform_driver(aspeed_espi_driver); + -+MODULE_AUTHOR("Chia-Wei Wang "); -+MODULE_DESCRIPTION("Control of AST2500 eSPI Device"); ++MODULE_AUTHOR("Aspeed Technology Inc."); ++MODULE_DESCRIPTION("Aspeed eSPI controller (AST2500/2600/2700)"); +MODULE_LICENSE("GPL"); -diff --git a/drivers/soc/aspeed/ast2500-espi.h b/drivers/soc/aspeed/ast2500-espi.h ---- a/drivers/soc/aspeed/ast2500-espi.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/ast2500-espi.h 2025-12-23 10:16:21.126032636 +0000 -@@ -0,0 +1,250 @@ +diff --git a/drivers/soc/aspeed/espi/aspeed-espi.h b/drivers/soc/aspeed/espi/aspeed-espi.h +--- a/drivers/soc/aspeed/espi/aspeed-espi.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/aspeed-espi.h 2026-04-08 18:03:48.312705284 +0000 +@@ -0,0 +1,227 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* -+ * Copyright 2023 Aspeed Technology Inc. ++ * Unified eSPI driver header file and data structures ++ * Copyright 2026 Aspeed Technology Inc. + */ -+#ifndef _AST2500_ESPI_H_ -+#define _AST2500_ESPI_H_ -+ -+#include -+#include "aspeed-espi-comm.h" -+ -+/* registers */ -+#define ESPI_CTRL 0x000 -+#define ESPI_CTRL_FLASH_TX_SW_RST BIT(31) -+#define ESPI_CTRL_FLASH_RX_SW_RST BIT(30) -+#define ESPI_CTRL_OOB_TX_SW_RST BIT(29) -+#define ESPI_CTRL_OOB_RX_SW_RST BIT(28) -+#define ESPI_CTRL_PERIF_NP_TX_SW_RST BIT(27) -+#define ESPI_CTRL_PERIF_NP_RX_SW_RST BIT(26) -+#define ESPI_CTRL_PERIF_PC_TX_SW_RST BIT(25) -+#define ESPI_CTRL_PERIF_PC_RX_SW_RST BIT(24) -+#define ESPI_CTRL_FLASH_TX_DMA_EN BIT(23) -+#define ESPI_CTRL_FLASH_RX_DMA_EN BIT(22) -+#define ESPI_CTRL_OOB_TX_DMA_EN BIT(21) -+#define ESPI_CTRL_OOB_RX_DMA_EN BIT(20) -+#define ESPI_CTRL_PERIF_NP_TX_DMA_EN BIT(19) -+#define ESPI_CTRL_PERIF_PC_TX_DMA_EN BIT(17) -+#define ESPI_CTRL_PERIF_PC_RX_DMA_EN BIT(16) -+#define ESPI_CTRL_FLASH_SAFS_SW_MODE BIT(10) -+#define ESPI_CTRL_VW_GPIO_SW BIT(9) -+#define ESPI_CTRL_FLASH_SW_RDY BIT(7) -+#define ESPI_CTRL_OOB_SW_RDY BIT(4) -+#define ESPI_CTRL_VW_SW_RDY BIT(3) -+#define ESPI_CTRL_PERIF_SW_RDY BIT(1) -+#define ESPI_STS 0x004 -+#define ESPI_INT_STS 0x008 -+#define ESPI_INT_STS_RST_DEASSERT BIT(31) -+#define ESPI_INT_STS_OOB_RX_TMOUT BIT(23) -+#define ESPI_INT_STS_VW_SYSEVT1 BIT(22) -+#define ESPI_INT_STS_FLASH_TX_ERR BIT(21) -+#define ESPI_INT_STS_OOB_TX_ERR BIT(20) -+#define ESPI_INT_STS_FLASH_TX_ABT BIT(19) -+#define ESPI_INT_STS_OOB_TX_ABT BIT(18) -+#define ESPI_INT_STS_PERIF_NP_TX_ABT BIT(17) -+#define ESPI_INT_STS_PERIF_PC_TX_ABT BIT(16) -+#define ESPI_INT_STS_FLASH_RX_ABT BIT(15) -+#define ESPI_INT_STS_OOB_RX_ABT BIT(14) -+#define ESPI_INT_STS_PERIF_NP_RX_ABT BIT(13) -+#define ESPI_INT_STS_PERIF_PC_RX_ABT BIT(12) -+#define ESPI_INT_STS_PERIF_NP_TX_ERR BIT(11) -+#define ESPI_INT_STS_PERIF_PC_TX_ERR BIT(10) -+#define ESPI_INT_STS_VW_GPIO BIT(9) -+#define ESPI_INT_STS_VW_SYSEVT BIT(8) -+#define ESPI_INT_STS_FLASH_TX_CMPLT BIT(7) -+#define ESPI_INT_STS_FLASH_RX_CMPLT BIT(6) -+#define ESPI_INT_STS_OOB_TX_CMPLT BIT(5) -+#define ESPI_INT_STS_OOB_RX_CMPLT BIT(4) -+#define ESPI_INT_STS_PERIF_NP_TX_CMPLT BIT(3) -+#define ESPI_INT_STS_PERIF_PC_TX_CMPLT BIT(1) -+#define ESPI_INT_STS_PERIF_PC_RX_CMPLT BIT(0) -+#define ESPI_INT_EN 0x00c -+#define ESPI_INT_EN_RST_DEASSERT BIT(31) -+#define ESPI_INT_EN_OOB_RX_TMOUT BIT(23) -+#define ESPI_INT_EN_VW_SYSEVT1 BIT(22) -+#define ESPI_INT_EN_FLASH_TX_ERR BIT(21) -+#define ESPI_INT_EN_OOB_TX_ERR BIT(20) -+#define ESPI_INT_EN_FLASH_TX_ABT BIT(19) -+#define ESPI_INT_EN_OOB_TX_ABT BIT(18) -+#define ESPI_INT_EN_PERIF_NP_TX_ABT BIT(17) -+#define ESPI_INT_EN_PERIF_PC_TX_ABT BIT(16) -+#define ESPI_INT_EN_FLASH_RX_ABT BIT(15) -+#define ESPI_INT_EN_OOB_RX_ABT BIT(14) -+#define ESPI_INT_EN_PERIF_NP_RX_ABT BIT(13) -+#define ESPI_INT_EN_PERIF_PC_RX_ABT BIT(12) -+#define ESPI_INT_EN_PERIF_NP_TX_ERR BIT(11) -+#define ESPI_INT_EN_PERIF_PC_TX_ERR BIT(10) -+#define ESPI_INT_EN_VW_GPIO BIT(9) -+#define ESPI_INT_EN_VW_SYSEVT BIT(8) -+#define ESPI_INT_EN_FLASH_TX_CMPLT BIT(7) -+#define ESPI_INT_EN_FLASH_RX_CMPLT BIT(6) -+#define ESPI_INT_EN_OOB_TX_CMPLT BIT(5) -+#define ESPI_INT_EN_OOB_RX_CMPLT BIT(4) -+#define ESPI_INT_EN_PERIF_NP_TX_CMPLT BIT(3) -+#define ESPI_INT_EN_PERIF_PC_TX_CMPLT BIT(1) -+#define ESPI_INT_EN_PERIF_PC_RX_CMPLT BIT(0) -+#define ESPI_PERIF_PC_RX_DMA 0x010 -+#define ESPI_PERIF_PC_RX_CTRL 0x014 -+#define ESPI_PERIF_PC_RX_CTRL_SERV_PEND BIT(31) -+#define ESPI_PERIF_PC_RX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_PERIF_PC_RX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_PERIF_PC_RX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_PERIF_PC_RX_DATA 0x018 -+#define ESPI_PERIF_PC_TX_DMA 0x020 -+#define ESPI_PERIF_PC_TX_CTRL 0x024 -+#define ESPI_PERIF_PC_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_PERIF_PC_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_PERIF_PC_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_PERIF_PC_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_PERIF_PC_TX_DATA 0x028 -+#define ESPI_PERIF_NP_TX_DMA 0x030 -+#define ESPI_PERIF_NP_TX_CTRL 0x034 -+#define ESPI_PERIF_NP_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_PERIF_NP_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_PERIF_NP_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_PERIF_NP_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_PERIF_NP_TX_DATA 0x038 -+#define ESPI_OOB_RX_DMA 0x040 -+#define ESPI_OOB_RX_CTRL 0x044 -+#define ESPI_OOB_RX_CTRL_SERV_PEND BIT(31) -+#define ESPI_OOB_RX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_OOB_RX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_OOB_RX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_OOB_RX_DATA 0x048 -+#define ESPI_OOB_TX_DMA 0x050 -+#define ESPI_OOB_TX_CTRL 0x054 -+#define ESPI_OOB_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_OOB_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_OOB_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_OOB_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_OOB_TX_DATA 0x058 -+#define ESPI_FLASH_RX_DMA 0x060 -+#define ESPI_FLASH_RX_CTRL 0x064 -+#define ESPI_FLASH_RX_CTRL_SERV_PEND BIT(31) -+#define ESPI_FLASH_RX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_FLASH_RX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_FLASH_RX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_FLASH_RX_DATA 0x068 -+#define ESPI_FLASH_TX_DMA 0x070 -+#define ESPI_FLASH_TX_CTRL 0x074 -+#define ESPI_FLASH_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_FLASH_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_FLASH_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_FLASH_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_FLASH_TX_DATA 0x078 -+#define ESPI_PERIF_MCYC_SADDR 0x084 -+#define ESPI_PERIF_MCYC_TADDR 0x088 -+#define ESPI_PERIF_MCYC_MASK 0x08c -+#define ESPI_FLASH_SAFS_TADDR 0x090 -+#define ESPI_FLASH_SAFS_TADDR_BASE GENMASK(31, 24) -+#define ESPI_FLASH_SAFS_TADDR_MASK GENMASK(15, 8) -+#define ESPI_VW_SYSEVT_INT_EN 0x094 -+#define ESPI_VW_SYSEVT_INT_EN_HOST_RST_WARN BIT(8) -+#define ESPI_VW_SYSEVT_INT_EN_OOB_RST_WARN BIT(6) -+#define ESPI_VW_SYSEVT 0x098 -+#define ESPI_VW_SYSEVT_HOST_RST_ACK BIT(27) -+#define ESPI_VW_SYSEVT_SLV_BOOT_STS BIT(23) -+#define ESPI_VW_SYSEVT_SLV_BOOT_DONE BIT(20) -+#define ESPI_VW_SYSEVT_OOB_RST_ACK BIT(16) -+#define ESPI_VW_SYSEVT_HOST_RST_WARN BIT(8) -+#define ESPI_VW_SYSEVT_OOB_RST_WARN BIT(6) -+#define ESPI_VW_GPIO_VAL 0x09c -+#define ESPI_GEN_CAP_N_CONF 0x0a0 -+#define ESPI_CH0_CAP_N_CONF 0x0a4 -+#define ESPI_CH1_CAP_N_CONF 0x0a8 -+#define ESPI_CH2_CAP_N_CONF 0x0ac -+#define ESPI_CH3_CAP_N_CONF 0x0b0 -+#define ESPI_CH3_CAP_N_CONF2 0x0b4 -+#define ESPI_VW_GPIO_DIR 0x0c0 -+#define ESPI_VW_GPIO_GRP 0x0c4 -+#define ESPI_VW_SYSEVT1_INT_EN 0x100 -+#define ESPI_VW_SYSEVT1_INT_EN_SUSPEND_WARN BIT(0) -+#define ESPI_VW_SYSEVT1 0x104 -+#define ESPI_VW_SYSEVT1_SUSPEND_ACK BIT(20) -+#define ESPI_VW_SYSEVT1_SUSPEND_WARN BIT(0) -+#define ESPI_VW_SYSEVT_INT_T0 0x110 -+#define ESPI_VW_SYSEVT_INT_T1 0x114 -+#define ESPI_VW_SYSEVT_INT_T2 0x118 -+#define ESPI_VW_SYSEVT_INT_T2_HOST_RST_WARN BIT(8) -+#define ESPI_VW_SYSEVT_INT_T2_OOB_RST_WARN BIT(6) -+#define ESPI_VW_SYSEVT_INT_STS 0x11c -+#define ESPI_VW_SYSEVT_INT_STS_HOST_RST_WARN BIT(8) -+#define ESPI_VW_SYSEVT_INT_STS_OOB_RST_WARN BIT(6) -+#define ESPI_VW_SYSEVT1_INT_T0 0x120 -+#define ESPI_VW_SYSEVT1_INT_T0_SUSPEND_WARN BIT(0) -+#define ESPI_VW_SYSEVT1_INT_T1 0x124 -+#define ESPI_VW_SYSEVT1_INT_T2 0x128 -+#define ESPI_VW_SYSEVT1_INT_STS 0x12c -+#define ESPI_VW_SYSEVT1_INT_STS_SUSPEND_WARN BIT(0) -+ -+/* collect ESPI_INT_EN bits for convenience */ -+#define ESPI_INT_EN_PERIF \ -+ (ESPI_INT_EN_PERIF_NP_TX_ABT | \ -+ ESPI_INT_EN_PERIF_PC_TX_ABT | \ -+ ESPI_INT_EN_PERIF_NP_RX_ABT | \ -+ ESPI_INT_EN_PERIF_PC_RX_ABT | \ -+ ESPI_INT_EN_PERIF_NP_TX_ERR | \ -+ ESPI_INT_EN_PERIF_PC_TX_ERR | \ -+ ESPI_INT_EN_PERIF_NP_TX_CMPLT | \ -+ ESPI_INT_EN_PERIF_PC_TX_CMPLT | \ -+ ESPI_INT_EN_PERIF_PC_RX_CMPLT) -+ -+#define ESPI_INT_EN_VW \ -+ (ESPI_INT_EN_VW_SYSEVT1 | \ -+ ESPI_INT_EN_VW_GPIO | \ -+ ESPI_INT_EN_VW_SYSEVT) -+ -+#define ESPI_INT_EN_OOB \ -+ (ESPI_INT_EN_OOB_RX_TMOUT | \ -+ ESPI_INT_EN_OOB_TX_ERR | \ -+ ESPI_INT_EN_OOB_TX_ABT | \ -+ ESPI_INT_EN_OOB_RX_ABT | \ -+ ESPI_INT_EN_OOB_TX_CMPLT | \ -+ ESPI_INT_EN_OOB_RX_CMPLT) -+ -+#define ESPI_INT_EN_FLASH \ -+ (ESPI_INT_EN_FLASH_TX_ERR | \ -+ ESPI_INT_EN_FLASH_TX_ABT | \ -+ ESPI_INT_EN_FLASH_RX_ABT | \ -+ ESPI_INT_EN_FLASH_TX_CMPLT | \ -+ ESPI_INT_EN_FLASH_RX_CMPLT) -+ -+/* collect ESPI_INT_STS bits for convenience */ -+#define ESPI_INT_STS_PERIF \ -+ (ESPI_INT_STS_PERIF_NP_TX_ABT | \ -+ ESPI_INT_STS_PERIF_PC_TX_ABT | \ -+ ESPI_INT_STS_PERIF_NP_RX_ABT | \ -+ ESPI_INT_STS_PERIF_PC_RX_ABT | \ -+ ESPI_INT_STS_PERIF_NP_TX_ERR | \ -+ ESPI_INT_STS_PERIF_PC_TX_ERR | \ -+ ESPI_INT_STS_PERIF_NP_TX_CMPLT | \ -+ ESPI_INT_STS_PERIF_PC_TX_CMPLT | \ -+ ESPI_INT_STS_PERIF_PC_RX_CMPLT) -+ -+#define ESPI_INT_STS_VW \ -+ (ESPI_INT_STS_VW_SYSEVT1 | \ -+ ESPI_INT_STS_VW_GPIO | \ -+ ESPI_INT_STS_VW_SYSEVT) -+ -+#define ESPI_INT_STS_OOB \ -+ (ESPI_INT_STS_OOB_RX_TMOUT | \ -+ ESPI_INT_STS_OOB_TX_ERR | \ -+ ESPI_INT_STS_OOB_TX_ABT | \ -+ ESPI_INT_STS_OOB_RX_ABT | \ -+ ESPI_INT_STS_OOB_TX_CMPLT | \ -+ ESPI_INT_STS_OOB_RX_CMPLT) -+ -+#define ESPI_INT_STS_FLASH \ -+ (ESPI_INT_STS_FLASH_TX_ERR | \ -+ ESPI_INT_STS_FLASH_TX_ABT | \ -+ ESPI_INT_STS_FLASH_RX_ABT | \ -+ ESPI_INT_STS_FLASH_TX_CMPLT | \ -+ ESPI_INT_STS_FLASH_RX_CMPLT) -+ -+/* consistent with DTS property "flash-safs-mode" */ -+enum ast2500_safs_mode { -+ SAFS_MODE_MIX = 0x0, -+ SAFS_MODE_SW, -+ SAFS_MODES, -+}; ++#ifndef __ASPEED_ESPI_H__ ++#define __ASPEED_ESPI_H__ + -+#endif -diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi.c ---- a/drivers/soc/aspeed/ast2600-espi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/ast2600-espi.c 2025-12-23 10:16:21.126032636 +0000 -@@ -0,0 +1,2188 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Copyright 2023 Aspeed Technology Inc. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++#include "linux/platform_device.h" ++#include +#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ast2600-espi.h" ++#include + ++#define PERIF_MMBI_INST_NUM 8 +#define DEVICE_NAME "aspeed-espi" + -+#define PERIF_MCYC_ALIGN SZ_64K -+#define PERIF_MMBI_ALIGN SZ_64K -+#define PERIF_MMBI_INST_NUM 8 ++enum aspeed_espi_platform_id { ++ ASPEED_ESPI_PLATFORM_ID_AST2500 = 0x2500, ++ ASPEED_ESPI_PLATFORM_ID_AST2600 = 0x2600, ++ ASPEED_ESPI_PLATFORM_ID_AST2700 = 0x2700, ++}; + -+#define OOB_DMA_RPTR_KEY 0x45538073 -+#define OOB_DMA_DESC_NUM 8 -+#define OOB_DMA_DESC_CUSTOM 0x4 ++/* consistent with DTS property "flash-safs-mode" */ ++enum aspeed_edaf_mode { ++ EDAF_MODE_MIX = 0x0, ++ EDAF_MODE_SW, ++ EDAF_MODE_HW, ++ EDAF_MODES, ++}; + -+#define FLASH_SAFS_ALIGN SZ_16M ++struct aspeed_espi_perif; + -+struct ast2600_espi_perif_mmbi { ++struct aspeed_espi_perif_mmbi { + void *b2h_virt; + void *h2b_virt; + dma_addr_t b2h_addr; @@ -68975,27 +75640,29 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + struct miscdevice h2b_mdev; + bool host_rwp_update; + wait_queue_head_t wq; -+ struct ast2600_espi_perif *perif; ++ struct aspeed_espi_perif *perif; +}; + -+struct ast2600_espi_perif { ++struct aspeed_espi_perif { + struct { + bool enable; + int irq; + void *virt; + dma_addr_t taddr; -+ uint32_t saddr; -+ uint32_t size; -+ uint32_t inst_size; -+ struct ast2600_espi_perif_mmbi inst[PERIF_MMBI_INST_NUM]; ++ dma_addr_t saddr; ++ resource_size_t size; ++ u32 inst_num; ++ u32 inst_size; ++ irqreturn_t (*mmbi_isr)(int irq, void *espi); ++ struct aspeed_espi_perif_mmbi inst[PERIF_MMBI_INST_NUM]; + } mmbi; + + struct { + bool enable; + void *virt; + dma_addr_t taddr; -+ uint32_t saddr; -+ uint32_t size; ++ dma_addr_t saddr; ++ resource_size_t size; + } mcyc; + + struct { @@ -69008,57 +75675,98 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + dma_addr_t pc_rx_addr; + } dma; + ++ bool rtc_enable; + bool rx_ready; + wait_queue_head_t wq; + -+ spinlock_t lock; -+ struct mutex np_tx_mtx; -+ struct mutex pc_tx_mtx; -+ struct mutex pc_rx_mtx; ++ spinlock_t lock; // protects rx_ready ++ struct mutex np_tx_mtx; // protects np_tx_virt/addr ++ struct mutex pc_tx_mtx; // protects pc_tx_virt/addr ++ struct mutex pc_rx_mtx; // protects pc_rx_virt/addr + + struct miscdevice mdev; +}; + -+struct ast2600_espi_vw { ++struct aspeed_espi_vw { + struct { + bool hw_mode; -+ uint32_t dir; -+ uint32_t val; ++ u32 grp; ++ u32 dir0; ++ u32 dir1; ++ u32 val0; ++ u32 val1; + } gpio; + ++ struct { ++ bool enabled; ++ spinlock_t pltrst_lock; // protects pltrst_status ++ wait_queue_head_t pltrst_wq; ++ char pltrst_status; ++ bool pltrst_avail; ++ } pltrst; ++ ++ struct miscdevice pltrst_mdev; + struct miscdevice mdev; +}; + ++struct ast2700_espi_oob_dma_tx_desc { ++ u32 data_addrl; ++ u32 data_addrh; ++ u8 cyc; ++ u16 tag : 4; ++ u16 len : 12; ++ u8 msg_type : 3; ++ u8 raz0 : 1; ++ u8 pec : 1; ++ u8 int_en : 1; ++ u8 pause : 1; ++ u8 raz1 : 1; ++ u32 raz2; ++ u32 raz3; ++ u32 pad[3]; ++} __packed; ++ ++struct ast2700_espi_oob_dma_rx_desc { ++ u32 data_addrl; ++ u32 data_addrh; ++ u8 cyc; ++ u16 tag : 4; ++ u16 len : 12; ++ u8 raz : 7; ++ u8 dirty : 1; ++ u32 pad; ++} __packed; ++ +struct ast2600_espi_oob_dma_tx_desc { -+ uint32_t data_addr; -+ uint8_t cyc; -+ uint16_t tag : 4; -+ uint16_t len : 12; -+ uint8_t msg_type : 3; -+ uint8_t raz0 : 1; -+ uint8_t pec : 1; -+ uint8_t int_en : 1; -+ uint8_t pause : 1; -+ uint8_t raz1 : 1; -+ uint32_t raz2; -+ uint32_t raz3; ++ u32 data_addr; ++ u8 cyc; ++ u16 tag : 4; ++ u16 len : 12; ++ u8 msg_type : 3; ++ u8 raz0 : 1; ++ u8 pec : 1; ++ u8 int_en : 1; ++ u8 pause : 1; ++ u8 raz1 : 1; ++ u32 raz2; ++ u32 raz3; +} __packed; + +struct ast2600_espi_oob_dma_rx_desc { -+ uint32_t data_addr; -+ uint8_t cyc; -+ uint16_t tag : 4; -+ uint16_t len : 12; -+ uint8_t raz : 7; -+ uint8_t dirty : 1; ++ u32 data_addr; ++ u8 cyc; ++ u16 tag : 4; ++ u16 len : 12; ++ u8 raz : 7; ++ u8 dirty : 1; +} __packed; + -+struct ast2600_espi_oob { ++struct aspeed_espi_oob { + struct { + bool enable; -+ struct ast2600_espi_oob_dma_tx_desc *txd_virt; ++ void *txd_virt; + dma_addr_t txd_addr; -+ struct ast2600_espi_oob_dma_rx_desc *rxd_virt; ++ void *rxd_virt; + dma_addr_t rxd_addr; + void *tx_virt; + dma_addr_t tx_addr; @@ -69069,19 +75777,19 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + bool rx_ready; + wait_queue_head_t wq; + -+ spinlock_t lock; -+ struct mutex tx_mtx; -+ struct mutex rx_mtx; ++ spinlock_t lock; // protects rx_ready ++ struct mutex tx_mtx; // protects tx_virt/addr ++ struct mutex rx_mtx; // protects rx_virt/addr + + struct miscdevice mdev; +}; + -+struct ast2600_espi_flash { ++struct aspeed_espi_flash { + struct { -+ uint32_t mode; ++ u32 mode; + phys_addr_t taddr; -+ uint32_t size; -+ } safs; ++ resource_size_t size; ++ } edaf; + + struct { + bool enable; @@ -69094,116 +75802,78 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + bool rx_ready; + wait_queue_head_t wq; + -+ spinlock_t lock; -+ struct mutex rx_mtx; -+ struct mutex tx_mtx; ++ spinlock_t lock; // protects rx_ready ++ struct mutex rx_mtx; // protects rx_virt/addr ++ struct mutex tx_mtx; // protects tx_virt/addr + + struct miscdevice mdev; +}; + -+struct ast2600_espi { ++struct aspeed_espi { ++ struct platform_device *pdev; + struct device *dev; + void __iomem *regs; + struct reset_control *rst; + struct clk *clk; ++ int dev_id; + int irq; + -+ struct ast2600_espi_perif perif; -+ struct ast2600_espi_vw vw; -+ struct ast2600_espi_oob oob; -+ struct ast2600_espi_flash flash; ++ struct aspeed_espi_perif perif; ++ struct aspeed_espi_vw vw; ++ struct aspeed_espi_oob oob; ++ struct aspeed_espi_flash flash; ++ const struct aspeed_espi_ops *ops; +}; + -+/* peripheral channel (CH0) */ -+static int ast2600_espi_mmbi_b2h_mmap(struct file *fp, struct vm_area_struct *vma) -+{ -+ struct ast2600_espi_perif_mmbi *mmbi; -+ struct ast2600_espi_perif *perif; -+ struct ast2600_espi *espi; -+ unsigned long vm_size; -+ pgprot_t prot; -+ -+ mmbi = container_of(fp->private_data, struct ast2600_espi_perif_mmbi, b2h_mdev); -+ -+ perif = mmbi->perif; -+ -+ espi = container_of(perif, struct ast2600_espi, perif); -+ -+ vm_size = vma->vm_end - vma->vm_start; -+ prot = vma->vm_page_prot; -+ -+ if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > (SZ_4K << perif->mmbi.inst_size)) -+ return -EINVAL; -+ -+ prot = pgprot_noncached(prot); -+ -+ if (remap_pfn_range(vma, vma->vm_start, -+ (mmbi->b2h_addr >> PAGE_SHIFT) + vma->vm_pgoff, -+ vm_size, prot)) -+ return -EAGAIN; -+ -+ return 0; -+} -+ -+static int ast2600_espi_mmbi_h2b_mmap(struct file *fp, struct vm_area_struct *vma) -+{ -+ struct ast2600_espi_perif_mmbi *mmbi; -+ struct ast2600_espi_perif *perif; -+ struct ast2600_espi *espi; -+ unsigned long vm_size; -+ pgprot_t prot; -+ -+ mmbi = container_of(fp->private_data, struct ast2600_espi_perif_mmbi, h2b_mdev); -+ -+ perif = mmbi->perif; -+ -+ espi = container_of(perif, struct ast2600_espi, perif); -+ -+ vm_size = vma->vm_end - vma->vm_start; -+ prot = vma->vm_page_prot; -+ -+ if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > (SZ_4K << perif->mmbi.inst_size)) -+ return -EINVAL; -+ -+ prot = pgprot_noncached(prot); -+ -+ if (remap_pfn_range(vma, vma->vm_start, -+ (mmbi->h2b_addr >> PAGE_SHIFT) + vma->vm_pgoff, -+ vm_size, prot)) -+ return -EAGAIN; -+ -+ return 0; -+} -+ -+static __poll_t ast2600_espi_mmbi_h2b_poll(struct file *fp, struct poll_table_struct *pt) -+{ -+ struct ast2600_espi_perif_mmbi *mmbi; -+ -+ mmbi = container_of(fp->private_data, struct ast2600_espi_perif_mmbi, h2b_mdev); -+ -+ poll_wait(fp, &mmbi->wq, pt); ++#endif // __ASPEED_ESPI_H__ +diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/ast2500-espi.c +--- a/drivers/soc/aspeed/espi/ast2500-espi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2500-espi.c 2026-04-08 18:03:48.312705284 +0000 +@@ -0,0 +1,1510 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2023 Aspeed Technology Inc. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ if (!mmbi->host_rwp_update) -+ return 0; ++#include "ast2500-espi.h" ++#include "aspeed-espi-comm.h" ++#include "aspeed-espi.h" + -+ mmbi->host_rwp_update = false; ++#define PERIF_MCYC_UNLOCK 0xfedc756e ++#define PERIF_MCYC_ALIGN SZ_64K + -+ return EPOLLIN; -+} ++#define FLASH_SAFS_ALIGN SZ_16M + -+static long ast2600_espi_perif_pc_get_rx(struct file *fp, -+ struct ast2600_espi_perif *perif, ++/* peripheral channel (CH0) */ ++static long ast2500_espi_perif_pc_get_rx(struct file *fp, ++ struct aspeed_espi_perif *perif, + struct aspeed_espi_ioc *ioc) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2600_espi *espi; ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; + struct espi_comm_hdr *hdr; + unsigned long flags; -+ uint32_t pkt_len; -+ uint8_t *pkt; ++ u32 pkt_len; ++ u8 *pkt; + int i, rc; + -+ espi = container_of(perif, struct ast2600_espi, perif); ++ espi = container_of(perif, struct aspeed_espi, perif); + + if (fp->f_flags & O_NONBLOCK) { + if (!mutex_trylock(&perif->pc_rx_mtx)) @@ -69310,17 +75980,17 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + return rc; +} + -+static long ast2600_espi_perif_pc_put_tx(struct file *fp, -+ struct ast2600_espi_perif *perif, ++static long ast2500_espi_perif_pc_put_tx(struct file *fp, ++ struct aspeed_espi_perif *perif, + struct aspeed_espi_ioc *ioc) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2600_espi *espi; ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; + struct espi_comm_hdr *hdr; -+ uint8_t *pkt; ++ u8 *pkt; + int i, rc; + -+ espi = container_of(perif, struct ast2600_espi, perif); ++ espi = container_of(perif, struct aspeed_espi, perif); + + if (!mutex_trylock(&perif->pc_tx_mtx)) + return -EAGAIN; @@ -69377,17 +76047,17 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + return rc; +} + -+static long ast2600_espi_perif_np_put_tx(struct file *fp, -+ struct ast2600_espi_perif *perif, ++static long ast2500_espi_perif_np_put_tx(struct file *fp, ++ struct aspeed_espi_perif *perif, + struct aspeed_espi_ioc *ioc) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2600_espi *espi; ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; + struct espi_comm_hdr *hdr; -+ uint8_t *pkt; ++ u8 *pkt; + int i, rc; + -+ espi = container_of(perif, struct ast2600_espi, perif); ++ espi = container_of(perif, struct aspeed_espi, perif); + + if (!mutex_trylock(&perif->np_tx_mtx)) + return -EAGAIN; @@ -69444,12 +76114,12 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + return rc; +} + -+static long ast2600_espi_perif_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static long ast2500_espi_perif_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ -+ struct ast2600_espi_perif *perif; ++ struct aspeed_espi_perif *perif; + struct aspeed_espi_ioc ioc; + -+ perif = container_of(fp->private_data, struct ast2600_espi_perif, mdev); ++ perif = container_of(fp->private_data, struct aspeed_espi_perif, mdev); + + if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) + return -EFAULT; @@ -69459,11 +76129,11 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + + switch (cmd) { + case ASPEED_ESPI_PERIF_PC_GET_RX: -+ return ast2600_espi_perif_pc_get_rx(fp, perif, &ioc); ++ return ast2500_espi_perif_pc_get_rx(fp, perif, &ioc); + case ASPEED_ESPI_PERIF_PC_PUT_TX: -+ return ast2600_espi_perif_pc_put_tx(fp, perif, &ioc); ++ return ast2500_espi_perif_pc_put_tx(fp, perif, &ioc); + case ASPEED_ESPI_PERIF_NP_PUT_TX: -+ return ast2600_espi_perif_np_put_tx(fp, perif, &ioc); ++ return ast2500_espi_perif_np_put_tx(fp, perif, &ioc); + default: + break; + }; @@ -69471,13 +76141,13 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + return -EINVAL; +} + -+static int ast2600_espi_perif_mmap(struct file *fp, struct vm_area_struct *vma) ++static int ast2500_espi_perif_mmap(struct file *fp, struct vm_area_struct *vma) +{ -+ struct ast2600_espi_perif *perif; ++ struct aspeed_espi_perif *perif; + unsigned long vm_size; + pgprot_t vm_prot; + -+ perif = container_of(fp->private_data, struct ast2600_espi_perif, mdev); ++ perif = container_of(fp->private_data, struct aspeed_espi_perif, mdev); + if (!perif->mcyc.enable) + return -EPERM; + @@ -69497,1073 +76167,1585 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + return 0; +} + -+static const struct file_operations ast2600_espi_mmbi_b2h_fops = { ++static const struct file_operations ast2500_espi_perif_fops = { + .owner = THIS_MODULE, -+ .mmap = ast2600_espi_mmbi_b2h_mmap, ++ .mmap = ast2500_espi_perif_mmap, ++ .unlocked_ioctl = ast2500_espi_perif_ioctl, +}; + -+static const struct file_operations ast2600_espi_mmbi_h2b_fops = { -+ .owner = THIS_MODULE, -+ .mmap = ast2600_espi_mmbi_h2b_mmap, -+ .poll = ast2600_espi_mmbi_h2b_poll, -+}; ++static void ast2500_espi_perif_isr(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_perif *perif; ++ unsigned long flags; ++ u32 sts; + -+static const struct file_operations ast2600_espi_perif_fops = { ++ perif = &espi->perif; ++ ++ sts = readl(espi->regs + ESPI_INT_STS); ++ ++ if (sts & ESPI_INT_STS_PERIF_PC_RX_CMPLT) { ++ writel(ESPI_INT_STS_PERIF_PC_RX_CMPLT, espi->regs + ESPI_INT_STS); ++ ++ spin_lock_irqsave(&perif->lock, flags); ++ perif->rx_ready = true; ++ spin_unlock_irqrestore(&perif->lock, flags); ++ ++ wake_up_interruptible(&perif->wq); ++ } ++} ++ ++static void ast2500_espi_perif_reset(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_perif *perif; ++ struct device *dev; ++ u32 reg, mask; ++ ++ dev = espi->dev; ++ ++ perif = &espi->perif; ++ ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_PERIF); ++ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_STS_PERIF, espi->regs + ESPI_INT_STS); ++ ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_PERIF_NP_TX_SW_RST ++ | ESPI_CTRL_PERIF_NP_RX_SW_RST ++ | ESPI_CTRL_PERIF_PC_TX_SW_RST ++ | ESPI_CTRL_PERIF_PC_RX_SW_RST ++ | ESPI_CTRL_PERIF_NP_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_RX_DMA_EN ++ | ESPI_CTRL_PERIF_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); ++ ++ udelay(1); ++ ++ reg |= (ESPI_CTRL_PERIF_NP_TX_SW_RST ++ | ESPI_CTRL_PERIF_NP_RX_SW_RST ++ | ESPI_CTRL_PERIF_PC_TX_SW_RST ++ | ESPI_CTRL_PERIF_PC_RX_SW_RST); ++ writel(reg, espi->regs + ESPI_CTRL); ++ ++ if (perif->mcyc.enable) { ++ mask = ~(perif->mcyc.size - 1); ++ writel(PERIF_MCYC_UNLOCK, espi->regs + ESPI_PERIF_MCYC_MASK); ++ writel(mask, espi->regs + ESPI_PERIF_MCYC_MASK); ++ ++ writel(perif->mcyc.saddr, espi->regs + ESPI_PERIF_MCYC_SADDR); ++ writel(perif->mcyc.taddr, espi->regs + ESPI_PERIF_MCYC_TADDR); ++ } ++ ++ if (perif->dma.enable) { ++ writel(perif->dma.np_tx_addr, espi->regs + ESPI_PERIF_NP_TX_DMA); ++ writel(perif->dma.pc_tx_addr, espi->regs + ESPI_PERIF_PC_TX_DMA); ++ writel(perif->dma.pc_rx_addr, espi->regs + ESPI_PERIF_PC_RX_DMA); ++ ++ reg = readl(espi->regs + ESPI_CTRL) ++ | ESPI_CTRL_PERIF_NP_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_RX_DMA_EN; ++ writel(reg, espi->regs + ESPI_CTRL); ++ } ++ ++ reg = readl(espi->regs + ESPI_INT_EN) | ESPI_INT_EN_PERIF_PC_RX_CMPLT; ++ writel(reg, espi->regs + ESPI_INT_EN); ++ ++ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_PERIF_SW_RDY; ++ writel(reg, espi->regs + ESPI_CTRL); ++} ++ ++int ast2500_espi_perif_probe(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_perif *perif; ++ struct device *dev; ++ int rc; ++ ++ dev = espi->dev; ++ ++ perif = &espi->perif; ++ ++ init_waitqueue_head(&perif->wq); ++ ++ spin_lock_init(&perif->lock); ++ ++ mutex_init(&perif->np_tx_mtx); ++ mutex_init(&perif->pc_tx_mtx); ++ mutex_init(&perif->pc_rx_mtx); ++ ++ perif->mcyc.enable = of_property_read_bool(dev->of_node, "perif-mcyc-enable"); ++ if (perif->mcyc.enable) { ++ rc = of_property_read_u32(dev->of_node, "perif-mcyc-src-addr", (u32 *)&perif->mcyc.saddr); ++ if (rc || !IS_ALIGNED(perif->mcyc.saddr, PERIF_MCYC_ALIGN)) { ++ dev_err(dev, "cannot get 64KB-aligned memory cycle host address\n"); ++ return -ENODEV; ++ } ++ ++ rc = of_property_read_u32(dev->of_node, "perif-mcyc-size", (u32 *)&perif->mcyc.size); ++ if (rc || !IS_ALIGNED(perif->mcyc.size, PERIF_MCYC_ALIGN)) { ++ dev_err(dev, "cannot get 64KB-aligned memory cycle size\n"); ++ return -EINVAL; ++ } ++ ++ perif->mcyc.virt = dmam_alloc_coherent(dev, perif->mcyc.size, ++ &perif->mcyc.taddr, GFP_KERNEL); ++ if (!perif->mcyc.virt) { ++ dev_err(dev, "cannot allocate memory cycle\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ perif->dma.enable = of_property_read_bool(dev->of_node, "perif-dma-mode"); ++ if (perif->dma.enable) { ++ perif->dma.pc_tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, ++ &perif->dma.pc_tx_addr, GFP_KERNEL); ++ if (!perif->dma.pc_tx_virt) { ++ dev_err(dev, "cannot allocate posted TX DMA buffer\n"); ++ return -ENOMEM; ++ } ++ ++ perif->dma.pc_rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, ++ &perif->dma.pc_rx_addr, GFP_KERNEL); ++ if (!perif->dma.pc_rx_virt) { ++ dev_err(dev, "cannot allocate posted RX DMA buffer\n"); ++ return -ENOMEM; ++ } ++ ++ perif->dma.np_tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, ++ &perif->dma.np_tx_addr, GFP_KERNEL); ++ if (!perif->dma.np_tx_virt) { ++ dev_err(dev, "cannot allocate non-posted TX DMA buffer\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ perif->mdev.parent = dev; ++ perif->mdev.minor = MISC_DYNAMIC_MINOR; ++ perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-peripheral", DEVICE_NAME); ++ perif->mdev.fops = &ast2500_espi_perif_fops; ++ rc = misc_register(&perif->mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", perif->mdev.name); ++ return rc; ++ } ++ ++ ast2500_espi_perif_reset(espi); ++ ++ return 0; ++} ++ ++int ast2500_espi_perif_remove(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_perif *perif; ++ struct device *dev; ++ u32 reg; ++ ++ dev = espi->dev; ++ ++ perif = &espi->perif; ++ ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_PERIF); ++ writel(reg, espi->regs + ESPI_INT_EN); ++ ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_PERIF_NP_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_RX_DMA_EN ++ | ESPI_CTRL_PERIF_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); ++ ++ if (perif->mcyc.enable) ++ dmam_free_coherent(dev, perif->mcyc.size, perif->mcyc.virt, ++ perif->mcyc.taddr); ++ ++ if (perif->dma.enable) { ++ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.np_tx_virt, ++ perif->dma.np_tx_addr); ++ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.pc_tx_virt, ++ perif->dma.pc_tx_addr); ++ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.pc_rx_virt, ++ perif->dma.pc_rx_addr); ++ } ++ ++ mutex_destroy(&perif->np_tx_mtx); ++ mutex_destroy(&perif->pc_tx_mtx); ++ mutex_destroy(&perif->pc_rx_mtx); ++ ++ misc_deregister(&perif->mdev); ++ ++ return 0; ++} ++ ++/* virtual wire channel (CH1) */ ++static long ast2500_espi_vw_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++{ ++ struct aspeed_espi_vw *vw; ++ struct aspeed_espi *espi; ++ u32 gpio; ++ ++ vw = container_of(fp->private_data, struct aspeed_espi_vw, mdev); ++ espi = container_of(vw, struct aspeed_espi, vw); ++ gpio = vw->gpio.val0; ++ ++ switch (cmd) { ++ case ASPEED_ESPI_VW_GET_GPIO_VAL: ++ if (put_user(gpio, (u32 __user *)arg)) ++ return -EFAULT; ++ break; ++ case ASPEED_ESPI_VW_PUT_GPIO_VAL: ++ if (get_user(gpio, (u32 __user *)arg)) ++ return -EFAULT; ++ ++ writel(gpio, espi->regs + ESPI_VW_GPIO_VAL); ++ break; ++ default: ++ return -EINVAL; ++ }; ++ ++ return 0; ++} ++ ++static const struct file_operations ast2500_espi_vw_fops = { + .owner = THIS_MODULE, -+ .mmap = ast2600_espi_perif_mmap, -+ .unlocked_ioctl = ast2600_espi_perif_ioctl, ++ .unlocked_ioctl = ast2500_espi_vw_ioctl, +}; + -+static irqreturn_t ast2600_espi_perif_mmbi_isr(int irq, void *arg) ++static void ast2500_espi_vw_isr(struct aspeed_espi *espi) +{ -+ struct ast2600_espi_perif_mmbi *mmbi; -+ struct ast2600_espi_perif *perif; -+ struct ast2600_espi *espi; -+ uint32_t sts, tmp; -+ uint32_t *p; -+ int i; ++ struct aspeed_espi_vw *vw; ++ u32 reg, sts, sts_sysevt; ++ ++ vw = &espi->vw; ++ ++ sts = readl(espi->regs + ESPI_INT_STS); ++ ++ if (sts & ESPI_INT_STS_VW_SYSEVT) { ++ sts_sysevt = readl(espi->regs + ESPI_VW_SYSEVT_INT_STS); ++ ++ if (sts_sysevt & ESPI_VW_SYSEVT_INT_STS_HOST_RST_WARN) { ++ reg = readl(espi->regs + ESPI_VW_SYSEVT) | ESPI_VW_SYSEVT_HOST_RST_ACK; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT); ++ writel(ESPI_VW_SYSEVT_INT_STS_HOST_RST_WARN, espi->regs + ESPI_VW_SYSEVT_INT_STS); ++ } ++ ++ if (sts_sysevt & ESPI_VW_SYSEVT_INT_STS_OOB_RST_WARN) { ++ reg = readl(espi->regs + ESPI_VW_SYSEVT) | ESPI_VW_SYSEVT_OOB_RST_ACK; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT); ++ writel(ESPI_VW_SYSEVT_INT_STS_OOB_RST_WARN, espi->regs + ESPI_VW_SYSEVT_INT_STS); ++ } ++ ++ writel(ESPI_INT_STS_VW_SYSEVT, espi->regs + ESPI_INT_STS); ++ } ++ ++ if (sts & ESPI_INT_STS_VW_SYSEVT1) { ++ sts_sysevt = readl(espi->regs + ESPI_VW_SYSEVT1_INT_STS); ++ ++ if (sts_sysevt & ESPI_VW_SYSEVT1_INT_STS_SUSPEND_WARN) { ++ reg = readl(espi->regs + ESPI_VW_SYSEVT1) | ESPI_VW_SYSEVT1_SUSPEND_ACK; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT1); ++ writel(ESPI_VW_SYSEVT1_INT_STS_SUSPEND_WARN, espi->regs + ESPI_VW_SYSEVT1_INT_STS); ++ } ++ ++ writel(ESPI_INT_STS_VW_SYSEVT1, espi->regs + ESPI_INT_STS); ++ } ++ ++ if (sts & ESPI_INT_STS_VW_GPIO) { ++ vw->gpio.val0 = readl(espi->regs + ESPI_VW_GPIO_VAL); ++ writel(ESPI_INT_STS_VW_GPIO, espi->regs + ESPI_INT_STS); ++ } ++} ++ ++static void ast2500_espi_vw_reset(struct aspeed_espi *espi) ++{ ++ u32 reg; ++ struct aspeed_espi_vw *vw = &espi->vw; ++ ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_VW); ++ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_STS_VW, espi->regs + ESPI_INT_STS); ++ ++ vw->gpio.val0 = readl(espi->regs + ESPI_VW_GPIO_VAL); ++ ++ /* Host Reset Warn and OOB Reset Warn system events */ ++ reg = readl(espi->regs + ESPI_VW_SYSEVT_INT_T2) ++ | ESPI_VW_SYSEVT_INT_T2_HOST_RST_WARN ++ | ESPI_VW_SYSEVT_INT_T2_OOB_RST_WARN; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT_INT_T2); ++ ++ reg = readl(espi->regs + ESPI_VW_SYSEVT_INT_EN) ++ | ESPI_VW_SYSEVT_INT_EN_HOST_RST_WARN ++ | ESPI_VW_SYSEVT_INT_EN_OOB_RST_WARN; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT_INT_EN); ++ ++ /* Suspend Warn system event */ ++ reg = readl(espi->regs + ESPI_VW_SYSEVT1_INT_T0) | ESPI_VW_SYSEVT1_INT_T0_SUSPEND_WARN; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT1_INT_T0); ++ ++ reg = readl(espi->regs + ESPI_VW_SYSEVT1_INT_EN) | ESPI_VW_SYSEVT1_INT_EN_SUSPEND_WARN; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT1_INT_EN); ++ ++ reg = readl(espi->regs + ESPI_INT_EN) ++ | ESPI_INT_EN_VW_GPIO ++ | ESPI_INT_EN_VW_SYSEVT ++ | ESPI_INT_EN_VW_SYSEVT1; ++ writel(reg, espi->regs + ESPI_INT_EN); ++ ++ reg = readl(espi->regs + ESPI_VW_SYSEVT) ++ | ESPI_VW_SYSEVT_SLV_BOOT_STS ++ | ESPI_VW_SYSEVT_SLV_BOOT_DONE; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT); ++ ++ reg = readl(espi->regs + ESPI_CTRL) ++ | ((vw->gpio.hw_mode) ? 0 : ESPI_CTRL_VW_GPIO_SW) ++ | ESPI_CTRL_VW_SW_RDY; ++ writel(reg, espi->regs + ESPI_CTRL); ++} ++ ++int ast2500_espi_vw_probe(struct aspeed_espi *espi) ++{ ++ int rc; ++ struct device *dev = espi->dev; ++ struct aspeed_espi_vw *vw = &espi->vw; ++ ++ writel(0x0, espi->regs + ESPI_VW_SYSEVT_INT_EN); ++ writel(0xffffffff, espi->regs + ESPI_VW_SYSEVT_INT_STS); ++ ++ writel(0x0, espi->regs + ESPI_VW_SYSEVT1_INT_EN); ++ writel(0xffffffff, espi->regs + ESPI_VW_SYSEVT1_INT_STS); ++ ++ vw->gpio.hw_mode = of_property_read_bool(dev->of_node, "vw-gpio-hw-mode"); ++ ++ vw->mdev.parent = dev; ++ vw->mdev.minor = MISC_DYNAMIC_MINOR; ++ vw->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-vw", DEVICE_NAME); ++ vw->mdev.fops = &ast2500_espi_vw_fops; ++ rc = misc_register(&vw->mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", vw->mdev.name); ++ return rc; ++ } ++ ++ ast2500_espi_vw_reset(espi); ++ ++ return 0; ++} ++ ++int ast2500_espi_vw_remove(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_vw *vw; ++ u32 reg; ++ ++ vw = &espi->vw; ++ ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_VW); ++ writel(reg, espi->regs + ESPI_INT_EN); ++ ++ misc_deregister(&vw->mdev); ++ ++ return 0; ++} ++ ++/* out-of-band channel (CH2) */ ++static long ast2500_espi_oob_get_rx(struct file *fp, ++ struct aspeed_espi_oob *oob, ++ struct aspeed_espi_ioc *ioc) ++{ ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ unsigned long flags; ++ u32 pkt_len; ++ u8 *pkt; ++ int i, rc; ++ ++ espi = container_of(oob, struct aspeed_espi, oob); ++ ++ if (fp->f_flags & O_NONBLOCK) { ++ if (!mutex_trylock(&oob->rx_mtx)) ++ return -EAGAIN; ++ ++ if (!oob->rx_ready) { ++ rc = -ENODATA; ++ goto unlock_mtx_n_out; ++ } ++ } else { ++ mutex_lock(&oob->rx_mtx); ++ ++ if (!oob->rx_ready) { ++ rc = wait_event_interruptible(oob->wq, oob->rx_ready); ++ if (rc == -ERESTARTSYS) { ++ rc = -EINTR; ++ goto unlock_mtx_n_out; ++ } ++ } ++ } ++ ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ reg = readl(espi->regs + ESPI_OOB_RX_CTRL); ++ cyc = FIELD_GET(ESPI_OOB_RX_CTRL_CYC, reg); ++ tag = FIELD_GET(ESPI_OOB_RX_CTRL_TAG, reg); ++ len = FIELD_GET(ESPI_OOB_RX_CTRL_LEN, reg); ++ ++ /* ++ * calculate the length of the rest part of the ++ * eSPI packet to be read from HW and copied to ++ * user space. ++ */ ++ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + sizeof(struct espi_comm_hdr); ++ ++ if (ioc->pkt_len < pkt_len) { ++ rc = -EINVAL; ++ goto unlock_mtx_n_out; ++ } ++ ++ pkt = vmalloc(pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_mtx_n_out; ++ } ++ ++ hdr = (struct espi_comm_hdr *)pkt; ++ hdr->cyc = cyc; ++ hdr->tag = tag; ++ hdr->len_h = len >> 8; ++ hdr->len_l = len & 0xff; ++ ++ if (oob->dma.enable) { ++ memcpy(hdr + 1, oob->dma.rx_virt, pkt_len - sizeof(*hdr)); ++ } else { ++ for (i = sizeof(*hdr); i < pkt_len; ++i) ++ pkt[i] = readl(espi->regs + ESPI_OOB_RX_DATA) & 0xff; ++ } ++ ++ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } ++ ++ spin_lock_irqsave(&oob->lock, flags); ++ ++ writel(ESPI_OOB_RX_CTRL_SERV_PEND, espi->regs + ESPI_OOB_RX_CTRL); ++ oob->rx_ready = 0; ++ ++ spin_unlock_irqrestore(&oob->lock, flags); ++ ++ rc = 0; ++ ++free_n_out: ++ vfree(pkt); ++ ++unlock_mtx_n_out: ++ mutex_unlock(&oob->rx_mtx); ++ ++ return rc; ++} ++ ++static long ast2500_espi_oob_put_tx(struct file *fp, ++ struct aspeed_espi_oob *oob, ++ struct aspeed_espi_ioc *ioc) ++{ ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ u8 *pkt; ++ int i, rc; ++ ++ espi = container_of(oob, struct aspeed_espi, oob); ++ ++ if (!mutex_trylock(&oob->tx_mtx)) ++ return -EAGAIN; ++ ++ reg = readl(espi->regs + ESPI_OOB_TX_CTRL); ++ if (reg & ESPI_OOB_TX_CTRL_TRIG_PEND) { ++ rc = -EBUSY; ++ goto unlock_mtx_n_out; ++ } ++ ++ if (ioc->pkt_len > ESPI_MAX_PKT_LEN) { ++ rc = -EINVAL; ++ goto unlock_mtx_n_out; ++ } ++ ++ pkt = vmalloc(ioc->pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_mtx_n_out; ++ } ++ ++ hdr = (struct espi_comm_hdr *)pkt; ++ ++ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } ++ ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ if (oob->dma.enable) { ++ memcpy(oob->dma.tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); ++ dma_wmb(); ++ } else { ++ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) ++ writel(pkt[i], espi->regs + ESPI_OOB_TX_DATA); ++ } ++ ++ cyc = hdr->cyc; ++ tag = hdr->tag; ++ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + -+ espi = (struct ast2600_espi *)arg; ++ reg = FIELD_PREP(ESPI_OOB_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_OOB_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_OOB_TX_CTRL_LEN, len) ++ | ESPI_OOB_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_OOB_TX_CTRL); + -+ perif = &espi->perif; ++ rc = 0; + -+ sts = readl(espi->regs + ESPI_MMBI_INT_STS); -+ if (!sts) -+ return IRQ_NONE; ++free_n_out: ++ vfree(pkt); + -+ for (i = 0, tmp = sts; i < PERIF_MMBI_INST_NUM; ++i, tmp >>= 2) { -+ if (!(tmp & 0x3)) -+ continue; ++unlock_mtx_n_out: ++ mutex_unlock(&oob->tx_mtx); + -+ mmbi = &perif->mmbi.inst[i]; ++ return rc; ++} + -+ p = (uint32_t *)mmbi->h2b_virt; -+ p[0] = readl(espi->regs + ESPI_MMBI_HOST_RWP(i)); -+ p[1] = readl(espi->regs + ESPI_MMBI_HOST_RWP(i) + 4); ++static long ast2500_espi_oob_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++{ ++ struct aspeed_espi_oob *oob; ++ struct aspeed_espi_ioc ioc; + -+ mmbi->host_rwp_update = true; ++ oob = container_of(fp->private_data, struct aspeed_espi_oob, mdev); + -+ wake_up_interruptible(&mmbi->wq); -+ } ++ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) ++ return -EFAULT; + -+ writel(sts, espi->regs + ESPI_MMBI_INT_STS); ++ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) ++ return -EINVAL; + -+ return IRQ_HANDLED; ++ switch (cmd) { ++ case ASPEED_ESPI_OOB_GET_RX: ++ return ast2500_espi_oob_get_rx(fp, oob, &ioc); ++ case ASPEED_ESPI_OOB_PUT_TX: ++ return ast2500_espi_oob_put_tx(fp, oob, &ioc); ++ default: ++ break; ++ }; ++ ++ return -EINVAL; +} + -+static void ast2600_espi_perif_isr(struct ast2600_espi *espi) ++static const struct file_operations ast2500_espi_oob_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = ast2500_espi_oob_ioctl, ++}; ++ ++static void ast2500_espi_oob_isr(struct aspeed_espi *espi) +{ -+ struct ast2600_espi_perif *perif; ++ struct aspeed_espi_oob *oob; + unsigned long flags; -+ uint32_t sts; ++ u32 sts; + -+ perif = &espi->perif; ++ oob = &espi->oob; + + sts = readl(espi->regs + ESPI_INT_STS); + -+ if (sts & ESPI_INT_STS_PERIF_PC_RX_CMPLT) { -+ writel(ESPI_INT_STS_PERIF_PC_RX_CMPLT, espi->regs + ESPI_INT_STS); ++ if (sts & ESPI_INT_STS_OOB_RX_CMPLT) { ++ writel(ESPI_INT_STS_OOB_RX_CMPLT, espi->regs + ESPI_INT_STS); + -+ spin_lock_irqsave(&perif->lock, flags); -+ perif->rx_ready = true; -+ spin_unlock_irqrestore(&perif->lock, flags); ++ spin_lock_irqsave(&oob->lock, flags); ++ oob->rx_ready = true; ++ spin_unlock_irqrestore(&oob->lock, flags); + -+ wake_up_interruptible(&perif->wq); ++ wake_up_interruptible(&oob->wq); + } +} + -+static void ast2600_espi_perif_sw_reset(struct ast2600_espi *espi) ++static void ast2500_espi_oob_reset(struct aspeed_espi *espi) +{ -+ struct device *dev; -+ uint32_t reg; ++ struct aspeed_espi_oob *oob; ++ u32 reg; + -+ dev = espi->dev; ++ oob = &espi->oob; ++ ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_OOB); ++ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_STS_OOB, espi->regs + ESPI_INT_STS); + + reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_PERIF_NP_TX_SW_RST -+ | ESPI_CTRL_PERIF_NP_RX_SW_RST -+ | ESPI_CTRL_PERIF_PC_TX_SW_RST -+ | ESPI_CTRL_PERIF_PC_RX_SW_RST -+ | ESPI_CTRL_PERIF_NP_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_RX_DMA_EN -+ | ESPI_CTRL_PERIF_SW_RDY); ++ reg &= ~(ESPI_CTRL_OOB_TX_SW_RST ++ | ESPI_CTRL_OOB_RX_SW_RST ++ | ESPI_CTRL_OOB_TX_DMA_EN ++ | ESPI_CTRL_OOB_RX_DMA_EN ++ | ESPI_CTRL_OOB_SW_RDY); + writel(reg, espi->regs + ESPI_CTRL); + + udelay(1); + -+ reg |= (ESPI_CTRL_PERIF_NP_TX_SW_RST -+ | ESPI_CTRL_PERIF_NP_RX_SW_RST -+ | ESPI_CTRL_PERIF_PC_TX_SW_RST -+ | ESPI_CTRL_PERIF_PC_RX_SW_RST); ++ reg |= (ESPI_CTRL_OOB_TX_SW_RST | ESPI_CTRL_OOB_RX_SW_RST); + writel(reg, espi->regs + ESPI_CTRL); -+} -+ -+static void ast2600_espi_perif_reset(struct ast2600_espi *espi) -+{ -+ struct ast2600_espi_perif *perif; -+ struct device *dev; -+ uint32_t reg, mask; -+ -+ dev = espi->dev; + -+ perif = &espi->perif; -+ -+ writel(ESPI_INT_EN_PERIF, espi->regs + ESPI_INT_EN_CLR); -+ writel(ESPI_INT_STS_PERIF, espi->regs + ESPI_INT_STS); ++ if (oob->dma.enable) { ++ writel(oob->dma.tx_addr, espi->regs + ESPI_OOB_TX_DMA); ++ writel(oob->dma.rx_addr, espi->regs + ESPI_OOB_RX_DMA); + -+ writel(0x0, espi->regs + ESPI_MMBI_INT_EN); -+ writel(0xffffffff, espi->regs + ESPI_MMBI_INT_STS); ++ reg = readl(espi->regs + ESPI_CTRL) ++ | ESPI_CTRL_OOB_TX_DMA_EN ++ | ESPI_CTRL_OOB_RX_DMA_EN; ++ writel(reg, espi->regs + ESPI_CTRL); ++ } + -+ reg = readl(espi->regs + ESPI_CTRL2); -+ reg &= ~(ESPI_CTRL2_MCYC_RD_DIS_WDT | ESPI_CTRL2_MCYC_WR_DIS_WDT); -+ writel(reg, espi->regs + ESPI_CTRL2); ++ reg = readl(espi->regs + ESPI_INT_EN) | ESPI_INT_EN_OOB_RX_CMPLT; ++ writel(reg, espi->regs + ESPI_INT_EN); + -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_PERIF_NP_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_RX_DMA_EN -+ | ESPI_CTRL_PERIF_SW_RDY); ++ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_OOB_SW_RDY; + writel(reg, espi->regs + ESPI_CTRL); ++} + -+ if (perif->mmbi.enable) { -+ reg = readl(espi->regs + ESPI_MMBI_CTRL); -+ reg &= ~(ESPI_MMBI_CTRL_EN); -+ writel(reg, espi->regs + ESPI_MMBI_CTRL); ++int ast2500_espi_oob_probe(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_oob *oob; ++ struct device *dev; ++ int rc; + -+ mask = ~(perif->mmbi.size - 1); -+ writel(mask, espi->regs + ESPI_PERIF_MMBI_MASK); -+ writel(perif->mmbi.saddr, espi->regs + ESPI_PERIF_MMBI_SADDR); -+ writel(perif->mmbi.taddr, espi->regs + ESPI_PERIF_MMBI_TADDR); ++ dev = espi->dev; + -+ writel(0xffffffff, espi->regs + ESPI_MMBI_INT_EN); ++ oob = &espi->oob; + -+ reg = FIELD_PREP(ESPI_MMBI_CTRL_INST_SZ, perif->mmbi.inst_size) -+ | FIELD_PREP(ESPI_MMBI_CTRL_TOTAL_SZ, perif->mmbi.inst_size) -+ | ESPI_MMBI_CTRL_EN; -+ writel(reg, espi->regs + ESPI_MMBI_CTRL); ++ init_waitqueue_head(&oob->wq); + -+ reg = readl(espi->regs + ESPI_CTRL2) & ~(ESPI_CTRL2_MMBI_RD_DIS | ESPI_CTRL2_MMBI_WR_DIS); -+ writel(reg, espi->regs + ESPI_CTRL2); -+ } ++ spin_lock_init(&oob->lock); + -+ if (perif->mcyc.enable) { -+ mask = ~(perif->mcyc.size - 1); -+ writel(mask, espi->regs + ESPI_PERIF_MCYC_MASK); -+ writel(perif->mcyc.saddr, espi->regs + ESPI_PERIF_MCYC_SADDR); -+ writel(perif->mcyc.taddr, espi->regs + ESPI_PERIF_MCYC_TADDR); ++ mutex_init(&oob->tx_mtx); ++ mutex_init(&oob->rx_mtx); + -+ reg = readl(espi->regs + ESPI_CTRL2) & ~(ESPI_CTRL2_MCYC_RD_DIS | ESPI_CTRL2_MCYC_WR_DIS); -+ writel(reg, espi->regs + ESPI_CTRL2); -+ } ++ oob->dma.enable = of_property_read_bool(dev->of_node, "oob-dma-mode"); ++ if (oob->dma.enable) { ++ oob->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &oob->dma.tx_addr, GFP_KERNEL); ++ if (!oob->dma.tx_virt) { ++ dev_err(dev, "cannot allocate DMA TX buffer\n"); ++ return -ENOMEM; ++ } + -+ if (perif->dma.enable) { -+ writel(perif->dma.np_tx_addr, espi->regs + ESPI_PERIF_NP_TX_DMA); -+ writel(perif->dma.pc_tx_addr, espi->regs + ESPI_PERIF_PC_TX_DMA); -+ writel(perif->dma.pc_rx_addr, espi->regs + ESPI_PERIF_PC_RX_DMA); ++ oob->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &oob->dma.rx_addr, GFP_KERNEL); ++ if (!oob->dma.rx_virt) { ++ dev_err(dev, "cannot allocate DMA TX buffer\n"); ++ return -ENOMEM; ++ } ++ } + -+ reg = readl(espi->regs + ESPI_CTRL) -+ | ESPI_CTRL_PERIF_NP_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_RX_DMA_EN; -+ writel(reg, espi->regs + ESPI_CTRL); ++ oob->mdev.parent = dev; ++ oob->mdev.minor = MISC_DYNAMIC_MINOR; ++ oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-oob", DEVICE_NAME); ++ oob->mdev.fops = &ast2500_espi_oob_fops; ++ rc = misc_register(&oob->mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", oob->mdev.name); ++ return rc; + } + -+ writel(ESPI_INT_EN_PERIF_PC_RX_CMPLT, espi->regs + ESPI_INT_EN); ++ ast2500_espi_oob_reset(espi); + -+ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_PERIF_SW_RDY; -+ writel(reg, espi->regs + ESPI_CTRL); ++ return 0; +} + -+static int ast2600_espi_perif_probe(struct ast2600_espi *espi) ++int ast2500_espi_oob_remove(struct aspeed_espi *espi) +{ -+ struct ast2600_espi_perif_mmbi *mmbi; -+ struct ast2600_espi_perif *perif; -+ struct platform_device *pdev; ++ struct aspeed_espi_oob *oob; + struct device *dev; -+ int i, rc; ++ u32 reg; + + dev = espi->dev; + -+ perif = &espi->perif; -+ -+ init_waitqueue_head(&perif->wq); ++ oob = &espi->oob; + -+ spin_lock_init(&perif->lock); ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_OOB); ++ writel(reg, espi->regs + ESPI_INT_EN); + -+ mutex_init(&perif->np_tx_mtx); -+ mutex_init(&perif->pc_tx_mtx); -+ mutex_init(&perif->pc_rx_mtx); ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_OOB_TX_DMA_EN ++ | ESPI_CTRL_OOB_RX_DMA_EN ++ | ESPI_CTRL_OOB_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); + -+ perif->mmbi.enable = of_property_read_bool(dev->of_node, "perif-mmbi-enable"); -+ if (perif->mmbi.enable) { -+ pdev = container_of(dev, struct platform_device, dev); ++ if (oob->dma.enable) { ++ dmam_free_coherent(dev, PAGE_SIZE, oob->dma.tx_virt, oob->dma.tx_addr); ++ dmam_free_coherent(dev, PAGE_SIZE, oob->dma.rx_virt, oob->dma.rx_addr); ++ } + -+ perif->mmbi.irq = platform_get_irq(pdev, 1); -+ if (perif->mmbi.irq < 0) { -+ dev_err(dev, "cannot get MMBI IRQ number\n"); -+ return -ENODEV; -+ } ++ mutex_destroy(&oob->tx_mtx); ++ mutex_destroy(&oob->rx_mtx); + -+ rc = of_property_read_u32(dev->of_node, "perif-mmbi-src-addr", &perif->mmbi.saddr); -+ if (rc || !IS_ALIGNED(perif->mmbi.saddr, PERIF_MMBI_ALIGN)) { -+ dev_err(dev, "cannot get 64KB-aligned MMBI host address\n"); -+ return -ENODEV; -+ } ++ misc_deregister(&oob->mdev); + -+ rc = of_property_read_u32(dev->of_node, "perif-mmbi-instance-size", &perif->mmbi.inst_size); -+ if (rc || perif->mmbi.inst_size >= MMBI_INST_SIZE_TYPES) { -+ dev_err(dev, "cannot get valid MMBI instance size\n"); -+ return -EINVAL; -+ } ++ return 0; ++} + -+ perif->mmbi.size = (SZ_8K << perif->mmbi.inst_size) * PERIF_MMBI_INST_NUM; -+ perif->mmbi.virt = dmam_alloc_coherent(dev, perif->mmbi.size, -+ &perif->mmbi.taddr, GFP_KERNEL); -+ if (!perif->mmbi.virt) { -+ dev_err(dev, "cannot allocate MMBI\n"); -+ return -ENOMEM; -+ } ++/* flash channel (CH3) */ ++static long ast2500_espi_flash_get_rx(struct file *fp, ++ struct aspeed_espi_flash *flash, ++ struct aspeed_espi_ioc *ioc) ++{ ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ unsigned long flags; ++ u32 pkt_len; ++ u8 *pkt; ++ int i, rc; + -+ for (i = 0; i < PERIF_MMBI_INST_NUM; ++i) { -+ mmbi = &perif->mmbi.inst[i]; ++ rc = 0; + -+ init_waitqueue_head(&mmbi->wq); ++ espi = container_of(flash, struct aspeed_espi, flash); + -+ mmbi->perif = perif; -+ mmbi->host_rwp_update = false; ++ if (fp->f_flags & O_NONBLOCK) { ++ if (!mutex_trylock(&flash->rx_mtx)) ++ return -EAGAIN; + -+ mmbi->b2h_virt = perif->mmbi.virt + ((SZ_4K << perif->mmbi.inst_size) * i); -+ mmbi->b2h_addr = perif->mmbi.taddr + ((SZ_4K << perif->mmbi.inst_size) * i); -+ mmbi->b2h_mdev.parent = dev; -+ mmbi->b2h_mdev.minor = MISC_DYNAMIC_MINOR; -+ mmbi->b2h_mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-mmbi-b2h%d", DEVICE_NAME, i); -+ mmbi->b2h_mdev.fops = &ast2600_espi_mmbi_b2h_fops; -+ rc = misc_register(&mmbi->b2h_mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", mmbi->b2h_mdev.name); -+ return rc; -+ } ++ if (!flash->rx_ready) { ++ rc = -ENODATA; ++ goto unlock_mtx_n_out; ++ } ++ } else { ++ mutex_lock(&flash->rx_mtx); + -+ mmbi->h2b_virt = perif->mmbi.virt + ((SZ_4K << perif->mmbi.inst_size) * (i + PERIF_MMBI_INST_NUM)); -+ mmbi->h2b_addr = perif->mmbi.taddr + ((SZ_4K << perif->mmbi.inst_size) * (i + PERIF_MMBI_INST_NUM)); -+ mmbi->h2b_mdev.parent = dev; -+ mmbi->h2b_mdev.minor = MISC_DYNAMIC_MINOR; -+ mmbi->h2b_mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-mmbi-h2b%d", DEVICE_NAME, i); -+ mmbi->h2b_mdev.fops = &ast2600_espi_mmbi_h2b_fops; -+ rc = misc_register(&mmbi->h2b_mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", mmbi->h2b_mdev.name); -+ return rc; ++ if (!flash->rx_ready) { ++ rc = wait_event_interruptible(flash->wq, flash->rx_ready); ++ if (rc == -ERESTARTSYS) { ++ rc = -EINTR; ++ goto unlock_mtx_n_out; + } + } + } + -+ perif->mcyc.enable = of_property_read_bool(dev->of_node, "perif-mcyc-enable"); -+ if (perif->mcyc.enable) { -+ if (perif->mmbi.enable) { -+ dev_err(dev, "cannot enable memory cycle, occupied by MMBI\n"); -+ return -EPERM; -+ } -+ -+ rc = of_property_read_u32(dev->of_node, "perif-mcyc-src-addr", &perif->mcyc.saddr); -+ if (rc || !IS_ALIGNED(perif->mcyc.saddr, PERIF_MCYC_ALIGN)) { -+ dev_err(dev, "cannot get 64KB-aligned memory cycle host address\n"); -+ return -ENODEV; -+ } ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ reg = readl(espi->regs + ESPI_FLASH_RX_CTRL); ++ cyc = FIELD_GET(ESPI_FLASH_RX_CTRL_CYC, reg); ++ tag = FIELD_GET(ESPI_FLASH_RX_CTRL_TAG, reg); ++ len = FIELD_GET(ESPI_FLASH_RX_CTRL_LEN, reg); + -+ rc = of_property_read_u32(dev->of_node, "perif-mcyc-size", &perif->mcyc.size); -+ if (rc || !IS_ALIGNED(perif->mcyc.size, PERIF_MCYC_ALIGN)) { -+ dev_err(dev, "cannot get 64KB-aligned memory cycle size\n"); -+ return -EINVAL; -+ } ++ /* ++ * calculate the length of the rest part of the ++ * eSPI packet to be read from HW and copied to ++ * user space. ++ */ ++ switch (cyc) { ++ case ESPI_FLASH_WRITE: ++ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + ++ sizeof(struct espi_flash_rwe); ++ break; ++ case ESPI_FLASH_READ: ++ case ESPI_FLASH_ERASE: ++ pkt_len = sizeof(struct espi_flash_rwe); ++ break; ++ case ESPI_FLASH_SUC_CMPLT_D_MIDDLE: ++ case ESPI_FLASH_SUC_CMPLT_D_FIRST: ++ case ESPI_FLASH_SUC_CMPLT_D_LAST: ++ case ESPI_FLASH_SUC_CMPLT_D_ONLY: ++ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + ++ sizeof(struct espi_flash_cmplt); ++ break; ++ case ESPI_FLASH_SUC_CMPLT: ++ case ESPI_FLASH_UNSUC_CMPLT: ++ pkt_len = sizeof(struct espi_flash_cmplt); ++ break; ++ default: ++ rc = -EFAULT; ++ goto unlock_mtx_n_out; ++ } + -+ perif->mcyc.virt = dmam_alloc_coherent(dev, perif->mcyc.size, -+ &perif->mcyc.taddr, GFP_KERNEL); -+ if (!perif->mcyc.virt) { -+ dev_err(dev, "cannot allocate memory cycle\n"); -+ return -ENOMEM; -+ } ++ if (ioc->pkt_len < pkt_len) { ++ rc = -EINVAL; ++ goto unlock_mtx_n_out; + } + -+ perif->dma.enable = of_property_read_bool(dev->of_node, "perif-dma-mode"); -+ if (perif->dma.enable) { -+ perif->dma.pc_tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, -+ &perif->dma.pc_tx_addr, GFP_KERNEL); -+ if (!perif->dma.pc_tx_virt) { -+ dev_err(dev, "cannot allocate posted TX DMA buffer\n"); -+ return -ENOMEM; -+ } ++ pkt = vmalloc(pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_mtx_n_out; ++ } + -+ perif->dma.pc_rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, -+ &perif->dma.pc_rx_addr, GFP_KERNEL); -+ if (!perif->dma.pc_rx_virt) { -+ dev_err(dev, "cannot allocate posted RX DMA buffer\n"); -+ return -ENOMEM; -+ } ++ hdr = (struct espi_comm_hdr *)pkt; ++ hdr->cyc = cyc; ++ hdr->tag = tag; ++ hdr->len_h = len >> 8; ++ hdr->len_l = len & 0xff; + -+ perif->dma.np_tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, -+ &perif->dma.np_tx_addr, GFP_KERNEL); -+ if (!perif->dma.np_tx_virt) { -+ dev_err(dev, "cannot allocate non-posted TX DMA buffer\n"); -+ return -ENOMEM; -+ } ++ if (flash->dma.enable) { ++ memcpy(hdr + 1, flash->dma.rx_virt, pkt_len - sizeof(*hdr)); ++ } else { ++ for (i = sizeof(*hdr); i < pkt_len; ++i) ++ pkt[i] = readl(espi->regs + ESPI_FLASH_RX_DATA) & 0xff; + } + -+ perif->mdev.parent = dev; -+ perif->mdev.minor = MISC_DYNAMIC_MINOR; -+ perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-peripheral", DEVICE_NAME); -+ perif->mdev.fops = &ast2600_espi_perif_fops; -+ rc = misc_register(&perif->mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", perif->mdev.name); -+ return rc; ++ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; + } + -+ ast2600_espi_perif_reset(espi); ++ spin_lock_irqsave(&flash->lock, flags); + -+ if (perif->mmbi.enable) { -+ rc = devm_request_irq(dev, espi->perif.mmbi.irq, -+ ast2600_espi_perif_mmbi_isr, 0, dev_name(dev), espi); -+ if (rc) { -+ dev_err(dev, "cannot request MMBI IRQ\n"); -+ return rc; -+ } -+ } ++ writel(ESPI_FLASH_RX_CTRL_SERV_PEND, espi->regs + ESPI_FLASH_RX_CTRL); ++ flash->rx_ready = 0; + -+ return 0; -+} ++ spin_unlock_irqrestore(&flash->lock, flags); + -+static int ast2600_espi_perif_remove(struct ast2600_espi *espi) -+{ -+ struct ast2600_espi_perif_mmbi *mmbi; -+ struct ast2600_espi_perif *perif; -+ struct device *dev; -+ uint32_t reg; -+ int i; ++ rc = 0; + -+ dev = espi->dev; ++free_n_out: ++ vfree(pkt); + -+ perif = &espi->perif; ++unlock_mtx_n_out: ++ mutex_unlock(&flash->rx_mtx); + -+ writel(ESPI_INT_EN_PERIF, espi->regs + ESPI_INT_EN_CLR); ++ return rc; ++} + -+ reg = readl(espi->regs + ESPI_CTRL2); -+ reg |= (ESPI_CTRL2_MCYC_RD_DIS | ESPI_CTRL2_MCYC_WR_DIS); -+ writel(reg, espi->regs + ESPI_CTRL2); ++static long ast2500_espi_flash_put_tx(struct file *fp, ++ struct aspeed_espi_flash *flash, ++ struct aspeed_espi_ioc *ioc) ++{ ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ u8 *pkt; ++ int i, rc; + -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_PERIF_NP_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_RX_DMA_EN -+ | ESPI_CTRL_PERIF_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); ++ espi = container_of(flash, struct aspeed_espi, flash); + -+ if (perif->mmbi.enable) { -+ reg = readl(espi->regs + ESPI_MMBI_CTRL); -+ reg &= ~ESPI_MMBI_CTRL_EN; -+ writel(reg, espi->regs + ESPI_MMBI_CTRL); ++ if (!mutex_trylock(&flash->tx_mtx)) ++ return -EAGAIN; + -+ for (i = 0; i < PERIF_MMBI_INST_NUM; ++i) { -+ mmbi = &perif->mmbi.inst[i]; -+ misc_deregister(&mmbi->b2h_mdev); -+ misc_deregister(&mmbi->h2b_mdev); -+ } ++ reg = readl(espi->regs + ESPI_FLASH_TX_CTRL); ++ if (reg & ESPI_FLASH_TX_CTRL_TRIG_PEND) { ++ rc = -EBUSY; ++ goto unlock_mtx_n_out; ++ } + -+ dmam_free_coherent(dev, perif->mmbi.size, perif->mmbi.virt, -+ perif->mmbi.taddr); ++ pkt = vmalloc(ioc->pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_mtx_n_out; + } + -+ if (perif->mcyc.enable) -+ dmam_free_coherent(dev, perif->mcyc.size, perif->mcyc.virt, -+ perif->mcyc.taddr); ++ hdr = (struct espi_comm_hdr *)pkt; + -+ if (perif->dma.enable) { -+ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.np_tx_virt, -+ perif->dma.np_tx_addr); -+ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.pc_tx_virt, -+ perif->dma.pc_tx_addr); -+ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.pc_rx_virt, -+ perif->dma.pc_rx_addr); ++ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; + } + -+ mutex_destroy(&perif->np_tx_mtx); -+ mutex_destroy(&perif->pc_tx_mtx); -+ mutex_destroy(&perif->pc_rx_mtx); ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ if (flash->dma.enable) { ++ memcpy(flash->dma.tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); ++ dma_wmb(); ++ } else { ++ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) ++ writel(pkt[i], espi->regs + ESPI_FLASH_TX_DATA); ++ } + -+ misc_deregister(&perif->mdev); ++ cyc = hdr->cyc; ++ tag = hdr->tag; ++ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + -+ return 0; ++ reg = FIELD_PREP(ESPI_FLASH_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_FLASH_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_FLASH_TX_CTRL_LEN, len) ++ | ESPI_FLASH_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_FLASH_TX_CTRL); ++ ++ rc = 0; ++ ++free_n_out: ++ vfree(pkt); ++ ++unlock_mtx_n_out: ++ mutex_unlock(&flash->tx_mtx); ++ ++ return rc; +} + -+/* virtual wire channel (CH1) */ -+static long ast2600_espi_vw_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static long ast2500_espi_flash_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ -+ struct ast2600_espi_vw *vw; -+ struct ast2600_espi *espi; -+ uint32_t gpio, hw_mode; ++ struct aspeed_espi_flash *flash; ++ struct aspeed_espi_ioc ioc; + -+ vw = container_of(fp->private_data, struct ast2600_espi_vw, mdev); -+ espi = container_of(vw, struct ast2600_espi, vw); -+ gpio = vw->gpio.val; -+ hw_mode = vw->gpio.hw_mode; ++ flash = container_of(fp->private_data, struct aspeed_espi_flash, mdev); + -+ if (hw_mode) { -+ dev_err(espi->dev, "HW mode: vGPIO reflect on physical GPIO. Get state from GPIO driver.\n"); ++ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) + return -EFAULT; -+ } -+ -+ switch (cmd) { -+ case ASPEED_ESPI_VW_GET_GPIO_VAL: -+ if (put_user(gpio, (uint32_t __user *)arg)) { -+ dev_err(espi->dev, "failed to get vGPIO value\n"); -+ return -EFAULT; -+ } -+ -+ dev_info(espi->dev, "Get vGPIO value: 0x%x\n", gpio); -+ break; -+ -+ case ASPEED_ESPI_VW_PUT_GPIO_VAL: -+ if (get_user(gpio, (uint32_t __user *)arg)) { -+ dev_err(espi->dev, "failed to put vGPIO value\n"); -+ return -EFAULT; -+ } + -+ dev_info(espi->dev, "Put vGPIO value: 0x%x\n", gpio); -+ writel(gpio, espi->regs + ESPI_VW_GPIO_VAL); -+ break; ++ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) ++ return -EINVAL; + ++ switch (cmd) { ++ case ASPEED_ESPI_FLASH_GET_RX: ++ return ast2500_espi_flash_get_rx(fp, flash, &ioc); ++ case ASPEED_ESPI_FLASH_PUT_TX: ++ return ast2500_espi_flash_put_tx(fp, flash, &ioc); + default: -+ return -EINVAL; ++ break; + }; + -+ return 0; ++ return -EINVAL; +} + -+static const struct file_operations ast2600_espi_vw_fops = { ++static const struct file_operations ast2500_espi_flash_fops = { + .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2600_espi_vw_ioctl, ++ .unlocked_ioctl = ast2500_espi_flash_ioctl, +}; + -+static void ast2600_espi_vw_isr(struct ast2600_espi *espi) ++static void ast2500_espi_flash_isr(struct aspeed_espi *espi) +{ -+ struct ast2600_espi_vw *vw; -+ uint32_t sts; ++ struct aspeed_espi_flash *flash; ++ unsigned long flags; ++ u32 sts; + -+ vw = &espi->vw; ++ flash = &espi->flash; + + sts = readl(espi->regs + ESPI_INT_STS); + -+ if (sts & ESPI_INT_STS_VW_GPIO) { -+ vw->gpio.val = readl(espi->regs + ESPI_VW_GPIO_VAL); -+ writel(ESPI_INT_STS_VW_GPIO, espi->regs + ESPI_INT_STS); -+ } else if (sts & ESPI_INT_STS_VW_SYSEVT) { -+ /* Handle system event */ -+ writel(ESPI_INT_STS_VW_SYSEVT, espi->regs + ESPI_INT_STS); -+ } else if (sts & (ESPI_INT_STS_VW_SYSEVT1)) { -+ /* Handle system event1 */ -+ writel(ESPI_INT_STS_VW_SYSEVT1, espi->regs + ESPI_INT_STS); ++ if (sts & ESPI_INT_STS_FLASH_RX_CMPLT) { ++ spin_lock_irqsave(&flash->lock, flags); ++ flash->rx_ready = true; ++ spin_unlock_irqrestore(&flash->lock, flags); ++ ++ wake_up_interruptible(&flash->wq); ++ ++ writel(ESPI_INT_STS_FLASH_RX_CMPLT, espi->regs + ESPI_INT_STS); + } +} + -+static void ast2600_espi_vw_reset(struct ast2600_espi *espi) ++static void ast2500_espi_flash_reset(struct aspeed_espi *espi) +{ -+ uint32_t reg; -+ struct ast2600_espi_vw *vw = &espi->vw; ++ struct aspeed_espi_flash *flash = &espi->flash; ++ u32 reg; + -+ writel(ESPI_INT_EN_VW, espi->regs + ESPI_INT_EN_CLR); -+ writel(ESPI_INT_STS_VW, espi->regs + ESPI_INT_STS); ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_FLASH); ++ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_STS_FLASH, espi->regs + ESPI_INT_STS); + -+ writel(vw->gpio.dir, espi->regs + ESPI_VW_GPIO_DIR); ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_FLASH_TX_SW_RST ++ | ESPI_CTRL_FLASH_RX_SW_RST ++ | ESPI_CTRL_FLASH_TX_DMA_EN ++ | ESPI_CTRL_FLASH_RX_DMA_EN ++ | ESPI_CTRL_FLASH_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); + -+ vw->gpio.val = readl(espi->regs + ESPI_VW_GPIO_VAL); ++ udelay(1); + -+ reg = readl(espi->regs + ESPI_CTRL2) & ~(ESPI_CTRL2_VW_TX_SORT); -+ writel(reg, espi->regs + ESPI_CTRL2); ++ reg |= (ESPI_CTRL_FLASH_TX_SW_RST | ESPI_CTRL_FLASH_RX_SW_RST); ++ writel(reg, espi->regs + ESPI_CTRL); + -+ writel(ESPI_INT_EN_VW_GPIO, espi->regs + ESPI_INT_EN); ++ if (flash->edaf.mode == EDAF_MODE_MIX) { ++ reg = FIELD_PREP(ESPI_FLASH_SAFS_TADDR_BASE, flash->edaf.taddr >> 24) ++ | FIELD_PREP(ESPI_FLASH_SAFS_TADDR_MASK, (~(flash->edaf.size - 1)) >> 24); ++ writel(reg, espi->regs + ESPI_FLASH_SAFS_TADDR); ++ } else { ++ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_FLASH_SAFS_SW_MODE; ++ writel(reg, espi->regs + ESPI_CTRL); ++ } + -+ reg = readl(espi->regs + ESPI_CTRL) -+ | ((vw->gpio.hw_mode) ? 0 : ESPI_CTRL_VW_GPIO_SW) -+ | ESPI_CTRL_VW_SW_RDY; -+ writel(reg, espi->regs + ESPI_CTRL); ++ if (flash->dma.enable) { ++ writel(flash->dma.tx_addr, espi->regs + ESPI_FLASH_TX_DMA); ++ writel(flash->dma.rx_addr, espi->regs + ESPI_FLASH_RX_DMA); + -+ writel(0x0, espi->regs + ESPI_VW_SYSEVT_INT_T0); -+ writel(0x0, espi->regs + ESPI_VW_SYSEVT_INT_T1); ++ reg = readl(espi->regs + ESPI_CTRL) ++ | ESPI_CTRL_FLASH_TX_DMA_EN ++ | ESPI_CTRL_FLASH_RX_DMA_EN; ++ writel(reg, espi->regs + ESPI_CTRL); ++ } + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg |= ESPI_INT_EN_RST_DEASSERT; ++ reg = readl(espi->regs + ESPI_INT_EN) | ESPI_INT_EN_FLASH_RX_CMPLT; + writel(reg, espi->regs + ESPI_INT_EN); + -+ writel(0xffffffff, espi->regs + ESPI_VW_SYSEVT_INT_EN); -+ writel(0x1, espi->regs + ESPI_VW_SYSEVT1_INT_EN); -+ writel(0x1, espi->regs + ESPI_VW_SYSEVT1_INT_T0); ++ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_FLASH_SW_RDY; ++ writel(reg, espi->regs + ESPI_CTRL); +} + -+static int ast2600_espi_vw_probe(struct ast2600_espi *espi) ++int ast2500_espi_flash_probe(struct aspeed_espi *espi) +{ ++ struct aspeed_espi_flash *flash; ++ struct device *dev; + int rc; -+ struct device *dev = espi->dev; -+ struct ast2600_espi_vw *vw = &espi->vw; + -+ vw->gpio.hw_mode = of_property_read_bool(dev->of_node, "vw-gpio-hw-mode"); -+ of_property_read_u32(dev->of_node, "vw-gpio-direction", &vw->gpio.dir); ++ dev = espi->dev; + -+ vw->mdev.parent = dev; -+ vw->mdev.minor = MISC_DYNAMIC_MINOR; -+ vw->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-vw", DEVICE_NAME); -+ vw->mdev.fops = &ast2600_espi_vw_fops; -+ rc = misc_register(&vw->mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", vw->mdev.name); -+ return rc; -+ } ++ flash = &espi->flash; + -+ ast2600_espi_vw_reset(espi); ++ init_waitqueue_head(&flash->wq); + -+ return 0; -+} ++ spin_lock_init(&flash->lock); + -+static int ast2600_espi_vw_remove(struct ast2600_espi *espi) -+{ -+ struct ast2600_espi_vw *vw; ++ mutex_init(&flash->tx_mtx); ++ mutex_init(&flash->rx_mtx); + -+ vw = &espi->vw; ++ flash->edaf.mode = EDAF_MODE_MIX; + -+ writel(ESPI_INT_EN_VW, espi->regs + ESPI_INT_EN_CLR); ++ of_property_read_u32(dev->of_node, "flash-safs-mode", &flash->edaf.mode); ++ if (flash->edaf.mode == EDAF_MODE_MIX) { ++ rc = of_property_read_u32(dev->of_node, "flash-safs-tgt-addr", (u32 *)&flash->edaf.taddr); ++ if (rc || !IS_ALIGNED(flash->edaf.taddr, FLASH_SAFS_ALIGN)) { ++ dev_err(dev, "cannot get 16MB-aligned SAFS target address\n"); ++ return -ENODEV; ++ } + -+ misc_deregister(&vw->mdev); ++ rc = of_property_read_u32(dev->of_node, "flash-safs-size", (u32 *)&flash->edaf.size); ++ if (rc || !IS_ALIGNED(flash->edaf.size, FLASH_SAFS_ALIGN)) { ++ dev_err(dev, "cannot get 16MB-aligned SAFS size\n"); ++ return -ENODEV; ++ } ++ } + -+ return 0; -+} ++ flash->dma.enable = of_property_read_bool(dev->of_node, "flash-dma-mode"); ++ if (flash->dma.enable) { ++ flash->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.tx_addr, GFP_KERNEL); ++ if (!flash->dma.tx_virt) { ++ dev_err(dev, "cannot allocate DMA TX buffer\n"); ++ return -ENOMEM; ++ } + -+/* out-of-band channel (CH2) */ -+static long ast2600_espi_oob_dma_get_rx(struct file *fp, -+ struct ast2600_espi_oob *oob, -+ struct aspeed_espi_ioc *ioc) -+{ -+ struct ast2600_espi_oob_dma_rx_desc *d; -+ struct ast2600_espi *espi; -+ struct espi_comm_hdr *hdr; -+ uint32_t wptr, pkt_len; -+ unsigned long flags; -+ uint8_t *pkt; -+ int rc; ++ flash->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.rx_addr, GFP_KERNEL); ++ if (!flash->dma.rx_virt) { ++ dev_err(dev, "cannot allocate DMA RX buffer\n"); ++ return -ENOMEM; ++ } ++ } + -+ espi = container_of(oob, struct ast2600_espi, oob); ++ flash->mdev.parent = dev; ++ flash->mdev.minor = MISC_DYNAMIC_MINOR; ++ flash->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-flash", DEVICE_NAME); ++ flash->mdev.fops = &ast2500_espi_flash_fops; ++ rc = misc_register(&flash->mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", flash->mdev.name); ++ return rc; ++ } + -+ wptr = FIELD_PREP(ESPI_OOB_RX_DESC_WPTR_WP, readl(espi->regs + ESPI_OOB_RX_DESC_WPTR)); ++ ast2500_espi_flash_reset(espi); + -+ d = &oob->dma.rxd_virt[wptr]; ++ return 0; ++} + -+ if (!d->dirty) -+ return -EFAULT; ++int ast2500_espi_flash_remove(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_flash *flash; ++ struct device *dev; ++ u32 reg; + -+ pkt_len = ((d->len) ? d->len : ESPI_MAX_PLD_LEN) + sizeof(struct espi_comm_hdr); ++ dev = espi->dev; + -+ if (ioc->pkt_len < pkt_len) -+ return -EINVAL; ++ flash = &espi->flash; + -+ pkt = vmalloc(pkt_len); -+ if (!pkt) -+ return -ENOMEM; ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_FLASH); ++ writel(reg, espi->regs + ESPI_INT_EN); + -+ hdr = (struct espi_comm_hdr *)pkt; -+ hdr->cyc = d->cyc; -+ hdr->tag = d->tag; -+ hdr->len_h = d->len >> 8; -+ hdr->len_l = d->len & 0xff; -+ memcpy(hdr + 1, oob->dma.rx_virt + (PAGE_SIZE * wptr), pkt_len - sizeof(*hdr)); ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_FLASH_TX_DMA_EN ++ | ESPI_CTRL_FLASH_RX_DMA_EN ++ | ESPI_CTRL_FLASH_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); + -+ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; ++ if (flash->dma.enable) { ++ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.tx_virt, flash->dma.tx_addr); ++ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.rx_virt, flash->dma.rx_addr); + } + -+ spin_lock_irqsave(&oob->lock, flags); -+ -+ /* make current descriptor available again */ -+ d->dirty = 0; -+ -+ wptr = (wptr + 1) % OOB_DMA_DESC_NUM; -+ writel(wptr | ESPI_OOB_RX_DESC_WPTR_RECV_EN, espi->regs + ESPI_OOB_RX_DESC_WPTR); -+ -+ /* set ready flag base on the next RX descriptor */ -+ oob->rx_ready = oob->dma.rxd_virt[wptr].dirty; -+ -+ spin_unlock_irqrestore(&oob->lock, flags); -+ -+ rc = 0; ++ mutex_destroy(&flash->tx_mtx); ++ mutex_destroy(&flash->rx_mtx); + -+free_n_out: -+ vfree(pkt); ++ misc_deregister(&flash->mdev); + -+ return rc; ++ return 0; +} + -+static long ast2600_espi_oob_get_rx(struct file *fp, -+ struct ast2600_espi_oob *oob, -+ struct aspeed_espi_ioc *ioc) ++/* global control */ ++irqreturn_t ast2500_espi_isr(int irq, void *arg) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2600_espi *espi; -+ struct espi_comm_hdr *hdr; -+ unsigned long flags; -+ uint32_t pkt_len; -+ uint8_t *pkt; -+ int i, rc; -+ -+ espi = container_of(oob, struct ast2600_espi, oob); -+ -+ if (fp->f_flags & O_NONBLOCK) { -+ if (!mutex_trylock(&oob->rx_mtx)) -+ return -EAGAIN; -+ -+ if (!oob->rx_ready) { -+ rc = -ENODATA; -+ goto unlock_mtx_n_out; -+ } -+ } else { -+ mutex_lock(&oob->rx_mtx); -+ -+ if (!oob->rx_ready) { -+ rc = wait_event_interruptible(oob->wq, oob->rx_ready); -+ if (rc == -ERESTARTSYS) { -+ rc = -EINTR; -+ goto unlock_mtx_n_out; -+ } -+ } -+ } -+ -+ if (oob->dma.enable) { -+ rc = ast2600_espi_oob_dma_get_rx(fp, oob, ioc); -+ goto unlock_mtx_n_out; -+ } ++ struct aspeed_espi *espi; ++ u32 sts; + -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ reg = readl(espi->regs + ESPI_OOB_RX_CTRL); -+ cyc = FIELD_GET(ESPI_OOB_RX_CTRL_CYC, reg); -+ tag = FIELD_GET(ESPI_OOB_RX_CTRL_TAG, reg); -+ len = FIELD_GET(ESPI_OOB_RX_CTRL_LEN, reg); ++ espi = (struct aspeed_espi *)arg; + -+ /* -+ * calculate the length of the rest part of the -+ * eSPI packet to be read from HW and copied to -+ * user space. -+ */ -+ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + sizeof(struct espi_comm_hdr); ++ sts = readl(espi->regs + ESPI_INT_STS); ++ if (!sts) ++ return IRQ_NONE; + -+ if (ioc->pkt_len < pkt_len) { -+ rc = -EINVAL; -+ goto unlock_mtx_n_out; -+ } ++ if (sts & ESPI_INT_STS_PERIF) ++ ast2500_espi_perif_isr(espi); + -+ pkt = vmalloc(pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_mtx_n_out; -+ } ++ if (sts & ESPI_INT_STS_VW) ++ ast2500_espi_vw_isr(espi); + -+ hdr = (struct espi_comm_hdr *)pkt; -+ hdr->cyc = cyc; -+ hdr->tag = tag; -+ hdr->len_h = len >> 8; -+ hdr->len_l = len & 0xff; ++ if (sts & ESPI_INT_STS_OOB) ++ ast2500_espi_oob_isr(espi); + -+ for (i = sizeof(*hdr); i < pkt_len; ++i) -+ pkt[i] = readl(espi->regs + ESPI_OOB_RX_DATA) & 0xff; ++ if (sts & ESPI_INT_STS_FLASH) ++ ast2500_espi_flash_isr(espi); + -+ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; ++ if (sts & ESPI_INT_STS_RST_DEASSERT) { ++ ast2500_espi_perif_reset(espi); ++ ast2500_espi_vw_reset(espi); ++ ast2500_espi_oob_reset(espi); ++ ast2500_espi_flash_reset(espi); ++ writel(ESPI_INT_STS_RST_DEASSERT, espi->regs + ESPI_INT_STS); + } + -+ spin_lock_irqsave(&oob->lock, flags); ++ return IRQ_HANDLED; ++} + -+ writel(ESPI_OOB_RX_CTRL_SERV_PEND, espi->regs + ESPI_OOB_RX_CTRL); -+ oob->rx_ready = 0; ++void ast2500_espi_pre_init(struct aspeed_espi *espi) ++{ ++ u32 reg; + -+ spin_unlock_irqrestore(&oob->lock, flags); ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~ESPI_INT_EN_RST_DEASSERT; ++ writel(reg, espi->regs + ESPI_INT_EN); ++} + -+ rc = 0; ++void ast2500_espi_post_init(struct aspeed_espi *espi) ++{ ++ u32 reg; + -+free_n_out: -+ vfree(pkt); ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg |= ESPI_INT_EN_RST_DEASSERT; ++ writel(reg, espi->regs + ESPI_INT_EN); ++} + -+unlock_mtx_n_out: -+ mutex_unlock(&oob->rx_mtx); ++void ast2500_espi_deinit(struct aspeed_espi *espi) ++{ ++ u32 reg; + -+ return rc; ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_RST_DEASSERT); ++ writel(reg, espi->regs + ESPI_INT_EN); +} +diff --git a/drivers/soc/aspeed/espi/ast2500-espi.h b/drivers/soc/aspeed/espi/ast2500-espi.h +--- a/drivers/soc/aspeed/espi/ast2500-espi.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2500-espi.h 2026-04-08 18:03:48.312705284 +0000 +@@ -0,0 +1,258 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Register definitions for Aspeed AST2500 eSPI controller ++ * Copyright 2023 Aspeed Technology Inc. ++ */ ++#ifndef _AST2500_ESPI_H_ ++#define _AST2500_ESPI_H_ + -+static long ast2600_espi_oob_dma_put_tx(struct file *fp, -+ struct ast2600_espi_oob *oob, -+ struct aspeed_espi_ioc *ioc) -+{ -+ struct ast2600_espi_oob_dma_tx_desc *d; -+ struct ast2600_espi *espi; -+ struct espi_comm_hdr *hdr; -+ uint32_t rptr, wptr; -+ uint8_t *pkt; -+ int rc; ++#include ++#include ++#include "aspeed-espi.h" + -+ espi = container_of(oob, struct ast2600_espi, oob); ++/* registers */ ++#define ESPI_CTRL 0x000 ++#define ESPI_CTRL_FLASH_TX_SW_RST BIT(31) ++#define ESPI_CTRL_FLASH_RX_SW_RST BIT(30) ++#define ESPI_CTRL_OOB_TX_SW_RST BIT(29) ++#define ESPI_CTRL_OOB_RX_SW_RST BIT(28) ++#define ESPI_CTRL_PERIF_NP_TX_SW_RST BIT(27) ++#define ESPI_CTRL_PERIF_NP_RX_SW_RST BIT(26) ++#define ESPI_CTRL_PERIF_PC_TX_SW_RST BIT(25) ++#define ESPI_CTRL_PERIF_PC_RX_SW_RST BIT(24) ++#define ESPI_CTRL_FLASH_TX_DMA_EN BIT(23) ++#define ESPI_CTRL_FLASH_RX_DMA_EN BIT(22) ++#define ESPI_CTRL_OOB_TX_DMA_EN BIT(21) ++#define ESPI_CTRL_OOB_RX_DMA_EN BIT(20) ++#define ESPI_CTRL_PERIF_NP_TX_DMA_EN BIT(19) ++#define ESPI_CTRL_PERIF_PC_TX_DMA_EN BIT(17) ++#define ESPI_CTRL_PERIF_PC_RX_DMA_EN BIT(16) ++#define ESPI_CTRL_FLASH_SAFS_SW_MODE BIT(10) ++#define ESPI_CTRL_VW_GPIO_SW BIT(9) ++#define ESPI_CTRL_FLASH_SW_RDY BIT(7) ++#define ESPI_CTRL_OOB_SW_RDY BIT(4) ++#define ESPI_CTRL_VW_SW_RDY BIT(3) ++#define ESPI_CTRL_PERIF_SW_RDY BIT(1) ++#define ESPI_STS 0x004 ++#define ESPI_INT_STS 0x008 ++#define ESPI_INT_STS_RST_DEASSERT BIT(31) ++#define ESPI_INT_STS_OOB_RX_TMOUT BIT(23) ++#define ESPI_INT_STS_VW_SYSEVT1 BIT(22) ++#define ESPI_INT_STS_FLASH_TX_ERR BIT(21) ++#define ESPI_INT_STS_OOB_TX_ERR BIT(20) ++#define ESPI_INT_STS_FLASH_TX_ABT BIT(19) ++#define ESPI_INT_STS_OOB_TX_ABT BIT(18) ++#define ESPI_INT_STS_PERIF_NP_TX_ABT BIT(17) ++#define ESPI_INT_STS_PERIF_PC_TX_ABT BIT(16) ++#define ESPI_INT_STS_FLASH_RX_ABT BIT(15) ++#define ESPI_INT_STS_OOB_RX_ABT BIT(14) ++#define ESPI_INT_STS_PERIF_NP_RX_ABT BIT(13) ++#define ESPI_INT_STS_PERIF_PC_RX_ABT BIT(12) ++#define ESPI_INT_STS_PERIF_NP_TX_ERR BIT(11) ++#define ESPI_INT_STS_PERIF_PC_TX_ERR BIT(10) ++#define ESPI_INT_STS_VW_GPIO BIT(9) ++#define ESPI_INT_STS_VW_SYSEVT BIT(8) ++#define ESPI_INT_STS_FLASH_TX_CMPLT BIT(7) ++#define ESPI_INT_STS_FLASH_RX_CMPLT BIT(6) ++#define ESPI_INT_STS_OOB_TX_CMPLT BIT(5) ++#define ESPI_INT_STS_OOB_RX_CMPLT BIT(4) ++#define ESPI_INT_STS_PERIF_NP_TX_CMPLT BIT(3) ++#define ESPI_INT_STS_PERIF_PC_TX_CMPLT BIT(1) ++#define ESPI_INT_STS_PERIF_PC_RX_CMPLT BIT(0) ++#define ESPI_INT_EN 0x00c ++#define ESPI_INT_EN_RST_DEASSERT BIT(31) ++#define ESPI_INT_EN_OOB_RX_TMOUT BIT(23) ++#define ESPI_INT_EN_VW_SYSEVT1 BIT(22) ++#define ESPI_INT_EN_FLASH_TX_ERR BIT(21) ++#define ESPI_INT_EN_OOB_TX_ERR BIT(20) ++#define ESPI_INT_EN_FLASH_TX_ABT BIT(19) ++#define ESPI_INT_EN_OOB_TX_ABT BIT(18) ++#define ESPI_INT_EN_PERIF_NP_TX_ABT BIT(17) ++#define ESPI_INT_EN_PERIF_PC_TX_ABT BIT(16) ++#define ESPI_INT_EN_FLASH_RX_ABT BIT(15) ++#define ESPI_INT_EN_OOB_RX_ABT BIT(14) ++#define ESPI_INT_EN_PERIF_NP_RX_ABT BIT(13) ++#define ESPI_INT_EN_PERIF_PC_RX_ABT BIT(12) ++#define ESPI_INT_EN_PERIF_NP_TX_ERR BIT(11) ++#define ESPI_INT_EN_PERIF_PC_TX_ERR BIT(10) ++#define ESPI_INT_EN_VW_GPIO BIT(9) ++#define ESPI_INT_EN_VW_SYSEVT BIT(8) ++#define ESPI_INT_EN_FLASH_TX_CMPLT BIT(7) ++#define ESPI_INT_EN_FLASH_RX_CMPLT BIT(6) ++#define ESPI_INT_EN_OOB_TX_CMPLT BIT(5) ++#define ESPI_INT_EN_OOB_RX_CMPLT BIT(4) ++#define ESPI_INT_EN_PERIF_NP_TX_CMPLT BIT(3) ++#define ESPI_INT_EN_PERIF_PC_TX_CMPLT BIT(1) ++#define ESPI_INT_EN_PERIF_PC_RX_CMPLT BIT(0) ++#define ESPI_PERIF_PC_RX_DMA 0x010 ++#define ESPI_PERIF_PC_RX_CTRL 0x014 ++#define ESPI_PERIF_PC_RX_CTRL_SERV_PEND BIT(31) ++#define ESPI_PERIF_PC_RX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_PERIF_PC_RX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_PERIF_PC_RX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_PERIF_PC_RX_DATA 0x018 ++#define ESPI_PERIF_PC_TX_DMA 0x020 ++#define ESPI_PERIF_PC_TX_CTRL 0x024 ++#define ESPI_PERIF_PC_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_PERIF_PC_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_PERIF_PC_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_PERIF_PC_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_PERIF_PC_TX_DATA 0x028 ++#define ESPI_PERIF_NP_TX_DMA 0x030 ++#define ESPI_PERIF_NP_TX_CTRL 0x034 ++#define ESPI_PERIF_NP_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_PERIF_NP_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_PERIF_NP_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_PERIF_NP_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_PERIF_NP_TX_DATA 0x038 ++#define ESPI_OOB_RX_DMA 0x040 ++#define ESPI_OOB_RX_CTRL 0x044 ++#define ESPI_OOB_RX_CTRL_SERV_PEND BIT(31) ++#define ESPI_OOB_RX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_OOB_RX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_OOB_RX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_OOB_RX_DATA 0x048 ++#define ESPI_OOB_TX_DMA 0x050 ++#define ESPI_OOB_TX_CTRL 0x054 ++#define ESPI_OOB_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_OOB_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_OOB_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_OOB_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_OOB_TX_DATA 0x058 ++#define ESPI_FLASH_RX_DMA 0x060 ++#define ESPI_FLASH_RX_CTRL 0x064 ++#define ESPI_FLASH_RX_CTRL_SERV_PEND BIT(31) ++#define ESPI_FLASH_RX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_FLASH_RX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_FLASH_RX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_FLASH_RX_DATA 0x068 ++#define ESPI_FLASH_TX_DMA 0x070 ++#define ESPI_FLASH_TX_CTRL 0x074 ++#define ESPI_FLASH_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_FLASH_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_FLASH_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_FLASH_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_FLASH_TX_DATA 0x078 ++#define ESPI_PERIF_MCYC_SADDR 0x084 ++#define ESPI_PERIF_MCYC_TADDR 0x088 ++#define ESPI_PERIF_MCYC_MASK 0x08c ++#define ESPI_FLASH_SAFS_TADDR 0x090 ++#define ESPI_FLASH_SAFS_TADDR_BASE GENMASK(31, 24) ++#define ESPI_FLASH_SAFS_TADDR_MASK GENMASK(15, 8) ++#define ESPI_VW_SYSEVT_INT_EN 0x094 ++#define ESPI_VW_SYSEVT_INT_EN_HOST_RST_WARN BIT(8) ++#define ESPI_VW_SYSEVT_INT_EN_OOB_RST_WARN BIT(6) ++#define ESPI_VW_SYSEVT 0x098 ++#define ESPI_VW_SYSEVT_HOST_RST_ACK BIT(27) ++#define ESPI_VW_SYSEVT_SLV_BOOT_STS BIT(23) ++#define ESPI_VW_SYSEVT_SLV_BOOT_DONE BIT(20) ++#define ESPI_VW_SYSEVT_OOB_RST_ACK BIT(16) ++#define ESPI_VW_SYSEVT_HOST_RST_WARN BIT(8) ++#define ESPI_VW_SYSEVT_OOB_RST_WARN BIT(6) ++#define ESPI_VW_GPIO_VAL 0x09c ++#define ESPI_GEN_CAP_N_CONF 0x0a0 ++#define ESPI_CH0_CAP_N_CONF 0x0a4 ++#define ESPI_CH1_CAP_N_CONF 0x0a8 ++#define ESPI_CH2_CAP_N_CONF 0x0ac ++#define ESPI_CH3_CAP_N_CONF 0x0b0 ++#define ESPI_CH3_CAP_N_CONF2 0x0b4 ++#define ESPI_VW_GPIO_DIR 0x0c0 ++#define ESPI_VW_GPIO_GRP 0x0c4 ++#define ESPI_VW_SYSEVT1_INT_EN 0x100 ++#define ESPI_VW_SYSEVT1_INT_EN_SUSPEND_WARN BIT(0) ++#define ESPI_VW_SYSEVT1 0x104 ++#define ESPI_VW_SYSEVT1_SUSPEND_ACK BIT(20) ++#define ESPI_VW_SYSEVT1_SUSPEND_WARN BIT(0) ++#define ESPI_VW_SYSEVT_INT_T0 0x110 ++#define ESPI_VW_SYSEVT_INT_T1 0x114 ++#define ESPI_VW_SYSEVT_INT_T2 0x118 ++#define ESPI_VW_SYSEVT_INT_T2_HOST_RST_WARN BIT(8) ++#define ESPI_VW_SYSEVT_INT_T2_OOB_RST_WARN BIT(6) ++#define ESPI_VW_SYSEVT_INT_STS 0x11c ++#define ESPI_VW_SYSEVT_INT_STS_HOST_RST_WARN BIT(8) ++#define ESPI_VW_SYSEVT_INT_STS_OOB_RST_WARN BIT(6) ++#define ESPI_VW_SYSEVT1_INT_T0 0x120 ++#define ESPI_VW_SYSEVT1_INT_T0_SUSPEND_WARN BIT(0) ++#define ESPI_VW_SYSEVT1_INT_T1 0x124 ++#define ESPI_VW_SYSEVT1_INT_T2 0x128 ++#define ESPI_VW_SYSEVT1_INT_STS 0x12c ++#define ESPI_VW_SYSEVT1_INT_STS_SUSPEND_WARN BIT(0) + -+ pkt = vzalloc(ioc->pkt_len); -+ if (!pkt) -+ return -ENOMEM; ++/* collect ESPI_INT_EN bits for convenience */ ++#define ESPI_INT_EN_PERIF \ ++ (ESPI_INT_EN_PERIF_NP_TX_ABT | \ ++ ESPI_INT_EN_PERIF_PC_TX_ABT | \ ++ ESPI_INT_EN_PERIF_NP_RX_ABT | \ ++ ESPI_INT_EN_PERIF_PC_RX_ABT | \ ++ ESPI_INT_EN_PERIF_NP_TX_ERR | \ ++ ESPI_INT_EN_PERIF_PC_TX_ERR | \ ++ ESPI_INT_EN_PERIF_NP_TX_CMPLT | \ ++ ESPI_INT_EN_PERIF_PC_TX_CMPLT | \ ++ ESPI_INT_EN_PERIF_PC_RX_CMPLT) + -+ hdr = (struct espi_comm_hdr *)pkt; ++#define ESPI_INT_EN_VW \ ++ (ESPI_INT_EN_VW_SYSEVT1 | \ ++ ESPI_INT_EN_VW_GPIO | \ ++ ESPI_INT_EN_VW_SYSEVT) + -+ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } ++#define ESPI_INT_EN_OOB \ ++ (ESPI_INT_EN_OOB_RX_TMOUT | \ ++ ESPI_INT_EN_OOB_TX_ERR | \ ++ ESPI_INT_EN_OOB_TX_ABT | \ ++ ESPI_INT_EN_OOB_RX_ABT | \ ++ ESPI_INT_EN_OOB_TX_CMPLT | \ ++ ESPI_INT_EN_OOB_RX_CMPLT) + -+ /* kick HW to update descriptor read/write pointer */ -+ writel(ESPI_OOB_TX_DESC_RPTR_UPDATE, espi->regs + ESPI_OOB_TX_DESC_RPTR); ++#define ESPI_INT_EN_FLASH \ ++ (ESPI_INT_EN_FLASH_TX_ERR | \ ++ ESPI_INT_EN_FLASH_TX_ABT | \ ++ ESPI_INT_EN_FLASH_RX_ABT | \ ++ ESPI_INT_EN_FLASH_TX_CMPLT | \ ++ ESPI_INT_EN_FLASH_RX_CMPLT) + -+ rptr = readl(espi->regs + ESPI_OOB_TX_DESC_RPTR); -+ wptr = readl(espi->regs + ESPI_OOB_TX_DESC_WPTR); ++/* collect ESPI_INT_STS bits for convenience */ ++#define ESPI_INT_STS_PERIF \ ++ (ESPI_INT_STS_PERIF_NP_TX_ABT | \ ++ ESPI_INT_STS_PERIF_PC_TX_ABT | \ ++ ESPI_INT_STS_PERIF_NP_RX_ABT | \ ++ ESPI_INT_STS_PERIF_PC_RX_ABT | \ ++ ESPI_INT_STS_PERIF_NP_TX_ERR | \ ++ ESPI_INT_STS_PERIF_PC_TX_ERR | \ ++ ESPI_INT_STS_PERIF_NP_TX_CMPLT | \ ++ ESPI_INT_STS_PERIF_PC_TX_CMPLT | \ ++ ESPI_INT_STS_PERIF_PC_RX_CMPLT) + -+ if (((wptr + 1) % OOB_DMA_DESC_NUM) == rptr) { -+ rc = -EBUSY; -+ goto free_n_out; -+ } ++#define ESPI_INT_STS_VW \ ++ (ESPI_INT_STS_VW_SYSEVT1 | \ ++ ESPI_INT_STS_VW_GPIO | \ ++ ESPI_INT_STS_VW_SYSEVT) + -+ d = &oob->dma.txd_virt[wptr]; -+ d->cyc = hdr->cyc; -+ d->tag = hdr->tag; -+ d->len = (hdr->len_h << 8) | (hdr->len_l & 0xff); -+ d->msg_type = OOB_DMA_DESC_CUSTOM; ++#define ESPI_INT_STS_OOB \ ++ (ESPI_INT_STS_OOB_RX_TMOUT | \ ++ ESPI_INT_STS_OOB_TX_ERR | \ ++ ESPI_INT_STS_OOB_TX_ABT | \ ++ ESPI_INT_STS_OOB_RX_ABT | \ ++ ESPI_INT_STS_OOB_TX_CMPLT | \ ++ ESPI_INT_STS_OOB_RX_CMPLT) + -+ memcpy(oob->dma.tx_virt + (PAGE_SIZE * wptr), hdr + 1, ioc->pkt_len - sizeof(*hdr)); ++#define ESPI_INT_STS_FLASH \ ++ (ESPI_INT_STS_FLASH_TX_ERR | \ ++ ESPI_INT_STS_FLASH_TX_ABT | \ ++ ESPI_INT_STS_FLASH_RX_ABT | \ ++ ESPI_INT_STS_FLASH_TX_CMPLT | \ ++ ESPI_INT_STS_FLASH_RX_CMPLT) + -+ dma_wmb(); ++/* operators for framework initialization */ ++void ast2500_espi_pre_init(struct aspeed_espi *espi); ++void ast2500_espi_post_init(struct aspeed_espi *espi); ++void ast2500_espi_deinit(struct aspeed_espi *espi); ++int ast2500_espi_perif_probe(struct aspeed_espi *espi); ++int ast2500_espi_perif_remove(struct aspeed_espi *espi); ++int ast2500_espi_vw_probe(struct aspeed_espi *espi); ++int ast2500_espi_vw_remove(struct aspeed_espi *espi); ++int ast2500_espi_oob_probe(struct aspeed_espi *espi); ++int ast2500_espi_oob_remove(struct aspeed_espi *espi); ++int ast2500_espi_flash_probe(struct aspeed_espi *espi); ++int ast2500_espi_flash_remove(struct aspeed_espi *espi); ++irqreturn_t ast2500_espi_isr(int irq, void *arg); ++#endif +diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/ast2600-espi.c +--- a/drivers/soc/aspeed/espi/ast2600-espi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2600-espi.c 2026-04-08 18:03:48.313705265 +0000 +@@ -0,0 +1,1910 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2023 Aspeed Technology Inc. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ wptr = (wptr + 1) % OOB_DMA_DESC_NUM; -+ writel(wptr | ESPI_OOB_TX_DESC_WPTR_SEND_EN, espi->regs + ESPI_OOB_TX_DESC_WPTR); ++#include "ast2600-espi.h" ++#include "aspeed-espi-comm.h" ++#include "aspeed-espi.h" + -+ rc = 0; ++#define PERIF_MCYC_ALIGN SZ_64K ++#define PERIF_MMBI_ALIGN SZ_64K ++#define PERIF_MMBI_INST_NUM 8 + -+free_n_out: -+ vfree(pkt); ++#define OOB_DMA_RPTR_KEY 0x45538073 ++#define OOB_DMA_DESC_NUM 8 ++#define OOB_DMA_DESC_CUSTOM 0x4 + -+ return rc; -+} ++#define FLASH_EDAF_ALIGN SZ_16M + -+static long ast2600_espi_oob_put_tx(struct file *fp, -+ struct ast2600_espi_oob *oob, -+ struct aspeed_espi_ioc *ioc) ++/* peripheral channel (CH0) */ ++static int ast2600_espi_mmbi_b2h_mmap(struct file *fp, struct vm_area_struct *vma) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2600_espi *espi; -+ struct espi_comm_hdr *hdr; -+ uint8_t *pkt; -+ int i, rc; -+ -+ espi = container_of(oob, struct ast2600_espi, oob); -+ -+ if (!mutex_trylock(&oob->tx_mtx)) -+ return -EAGAIN; -+ -+ if (oob->dma.enable) { -+ rc = ast2600_espi_oob_dma_put_tx(fp, oob, ioc); -+ goto unlock_mtx_n_out; -+ } -+ -+ reg = readl(espi->regs + ESPI_OOB_TX_CTRL); -+ if (reg & ESPI_OOB_TX_CTRL_TRIG_PEND) { -+ rc = -EBUSY; -+ goto unlock_mtx_n_out; -+ } -+ -+ if (ioc->pkt_len > ESPI_MAX_PKT_LEN) { -+ rc = -EINVAL; -+ goto unlock_mtx_n_out; -+ } -+ -+ pkt = vmalloc(ioc->pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_mtx_n_out; -+ } -+ -+ hdr = (struct espi_comm_hdr *)pkt; -+ -+ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } -+ -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_OOB_TX_DATA); -+ -+ cyc = hdr->cyc; -+ tag = hdr->tag; -+ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); -+ -+ reg = FIELD_PREP(ESPI_OOB_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_OOB_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_OOB_TX_CTRL_LEN, len) -+ | ESPI_OOB_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_OOB_TX_CTRL); -+ -+ rc = 0; -+ -+free_n_out: -+ vfree(pkt); -+ -+unlock_mtx_n_out: -+ mutex_unlock(&oob->tx_mtx); ++ struct aspeed_espi_perif_mmbi *mmbi; ++ struct aspeed_espi_perif *perif; ++ struct aspeed_espi *espi; ++ unsigned long vm_size; ++ pgprot_t prot; + -+ return rc; -+} ++ mmbi = container_of(fp->private_data, struct aspeed_espi_perif_mmbi, b2h_mdev); + -+static long ast2600_espi_oob_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) -+{ -+ struct ast2600_espi_oob *oob; -+ struct aspeed_espi_ioc ioc; ++ perif = mmbi->perif; + -+ oob = container_of(fp->private_data, struct ast2600_espi_oob, mdev); ++ espi = container_of(perif, struct aspeed_espi, perif); + -+ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) -+ return -EFAULT; ++ vm_size = vma->vm_end - vma->vm_start; ++ prot = vma->vm_page_prot; + -+ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) ++ if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > (SZ_4K << perif->mmbi.inst_size)) + return -EINVAL; + -+ switch (cmd) { -+ case ASPEED_ESPI_OOB_GET_RX: -+ return ast2600_espi_oob_get_rx(fp, oob, &ioc); -+ case ASPEED_ESPI_OOB_PUT_TX: -+ return ast2600_espi_oob_put_tx(fp, oob, &ioc); -+ }; -+ -+ return -EINVAL; -+} -+ -+static const struct file_operations ast2600_espi_oob_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2600_espi_oob_ioctl, -+}; -+ -+static void ast2600_espi_oob_isr(struct ast2600_espi *espi) -+{ -+ struct ast2600_espi_oob *oob; -+ unsigned long flags; -+ uint32_t sts; -+ -+ oob = &espi->oob; -+ -+ sts = readl(espi->regs + ESPI_INT_STS); -+ -+ if (sts & ESPI_INT_STS_OOB_RX_CMPLT) { -+ writel(ESPI_INT_STS_OOB_RX_CMPLT, espi->regs + ESPI_INT_STS); -+ -+ spin_lock_irqsave(&oob->lock, flags); -+ oob->rx_ready = true; -+ spin_unlock_irqrestore(&oob->lock, flags); -+ -+ wake_up_interruptible(&oob->wq); -+ } -+} -+ -+static void ast2600_espi_oob_reset(struct ast2600_espi *espi) -+{ -+ struct ast2600_espi_oob *oob; -+ dma_addr_t tx_addr, rx_addr; -+ uint32_t reg; -+ int i; -+ -+ writel(ESPI_INT_EN_OOB, espi->regs + ESPI_INT_EN_CLR); -+ writel(ESPI_INT_STS_OOB, espi->regs + ESPI_INT_STS); -+ -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_OOB_TX_SW_RST -+ | ESPI_CTRL_OOB_RX_SW_RST -+ | ESPI_CTRL_OOB_TX_DMA_EN -+ | ESPI_CTRL_OOB_RX_DMA_EN -+ | ESPI_CTRL_OOB_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); -+ -+ udelay(1); -+ -+ reg |= (ESPI_CTRL_OOB_TX_SW_RST | ESPI_CTRL_OOB_RX_SW_RST); -+ writel(reg, espi->regs + ESPI_CTRL); -+ -+ oob = &espi->oob; -+ -+ if (oob->dma.enable) { -+ tx_addr = oob->dma.tx_addr; -+ rx_addr = oob->dma.rx_addr; -+ -+ for (i = 0; i < OOB_DMA_DESC_NUM; ++i) { -+ oob->dma.txd_virt[i].data_addr = tx_addr; -+ tx_addr += PAGE_SIZE; -+ -+ oob->dma.rxd_virt[i].data_addr = rx_addr; -+ oob->dma.rxd_virt[i].dirty = 0; -+ rx_addr += PAGE_SIZE; -+ } -+ -+ writel(oob->dma.txd_addr, espi->regs + ESPI_OOB_TX_DMA); -+ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_OOB_TX_DESC_RPTR); -+ writel(0x0, espi->regs + ESPI_OOB_TX_DESC_WPTR); -+ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_OOB_TX_DESC_NUM); -+ -+ writel(oob->dma.rxd_addr, espi->regs + ESPI_OOB_RX_DMA); -+ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_OOB_RX_DESC_RPTR); -+ writel(0x0, espi->regs + ESPI_OOB_RX_DESC_WPTR); -+ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_OOB_RX_DESC_NUM); -+ -+ reg = readl(espi->regs + ESPI_CTRL) -+ | ESPI_CTRL_OOB_TX_DMA_EN -+ | ESPI_CTRL_OOB_RX_DMA_EN; -+ writel(reg, espi->regs + ESPI_CTRL); -+ -+ /* activate RX DMA to make OOB_FREE */ -+ writel(ESPI_OOB_RX_DESC_WPTR_RECV_EN, espi->regs + ESPI_OOB_RX_DESC_WPTR); -+ } ++ prot = pgprot_noncached(prot); + -+ writel(ESPI_INT_EN_OOB_RX_CMPLT, espi->regs + ESPI_INT_EN); ++ if (remap_pfn_range(vma, vma->vm_start, ++ (mmbi->b2h_addr >> PAGE_SHIFT) + vma->vm_pgoff, ++ vm_size, prot)) ++ return -EAGAIN; + -+ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_OOB_SW_RDY; -+ writel(reg, espi->regs + ESPI_CTRL); ++ return 0; +} + -+static int ast2600_espi_oob_probe(struct ast2600_espi *espi) ++static int ast2600_espi_mmbi_h2b_mmap(struct file *fp, struct vm_area_struct *vma) +{ -+ struct ast2600_espi_oob *oob; -+ struct device *dev; -+ int rc; -+ -+ dev = espi->dev; -+ -+ oob = &espi->oob; -+ -+ init_waitqueue_head(&oob->wq); ++ struct aspeed_espi_perif_mmbi *mmbi; ++ struct aspeed_espi_perif *perif; ++ struct aspeed_espi *espi; ++ unsigned long vm_size; ++ pgprot_t prot; + -+ spin_lock_init(&oob->lock); ++ mmbi = container_of(fp->private_data, struct aspeed_espi_perif_mmbi, h2b_mdev); + -+ mutex_init(&oob->tx_mtx); -+ mutex_init(&oob->rx_mtx); ++ perif = mmbi->perif; + -+ oob->dma.enable = of_property_read_bool(dev->of_node, "oob-dma-mode"); -+ if (oob->dma.enable) { -+ oob->dma.txd_virt = dmam_alloc_coherent(dev, sizeof(*oob->dma.txd_virt) * OOB_DMA_DESC_NUM, &oob->dma.txd_addr, GFP_KERNEL); -+ if (!oob->dma.txd_virt) { -+ dev_err(dev, "cannot allocate DMA TX descriptor\n"); -+ return -ENOMEM; -+ } -+ oob->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, &oob->dma.tx_addr, GFP_KERNEL); -+ if (!oob->dma.tx_virt) { -+ dev_err(dev, "cannot allocate DMA TX buffer\n"); -+ return -ENOMEM; -+ } ++ espi = container_of(perif, struct aspeed_espi, perif); + -+ oob->dma.rxd_virt = dmam_alloc_coherent(dev, sizeof(*oob->dma.rxd_virt) * OOB_DMA_DESC_NUM, &oob->dma.rxd_addr, GFP_KERNEL); -+ if (!oob->dma.rxd_virt) { -+ dev_err(dev, "cannot allocate DMA RX descriptor\n"); -+ return -ENOMEM; -+ } ++ vm_size = vma->vm_end - vma->vm_start; ++ prot = vma->vm_page_prot; + -+ oob->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, &oob->dma.rx_addr, GFP_KERNEL); -+ if (!oob->dma.rx_virt) { -+ dev_err(dev, "cannot allocate DMA TX buffer\n"); -+ return -ENOMEM; -+ } -+ } ++ if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > (SZ_4K << perif->mmbi.inst_size)) ++ return -EINVAL; + -+ oob->mdev.parent = dev; -+ oob->mdev.minor = MISC_DYNAMIC_MINOR; -+ oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-oob", DEVICE_NAME); -+ oob->mdev.fops = &ast2600_espi_oob_fops; -+ rc = misc_register(&oob->mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", oob->mdev.name); -+ return rc; -+ } ++ prot = pgprot_noncached(prot); + -+ ast2600_espi_oob_reset(espi); ++ if (remap_pfn_range(vma, vma->vm_start, ++ (mmbi->h2b_addr >> PAGE_SHIFT) + vma->vm_pgoff, ++ vm_size, prot)) ++ return -EAGAIN; + + return 0; +} + -+static int ast2600_espi_oob_remove(struct ast2600_espi *espi) ++static __poll_t ast2600_espi_mmbi_h2b_poll(struct file *fp, struct poll_table_struct *pt) +{ -+ struct ast2600_espi_oob *oob; -+ struct device *dev; -+ uint32_t reg; -+ -+ dev = espi->dev; -+ -+ oob = &espi->oob; -+ -+ writel(ESPI_INT_EN_OOB, espi->regs + ESPI_INT_EN_CLR); ++ struct aspeed_espi_perif_mmbi *mmbi; + -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_OOB_TX_DMA_EN -+ | ESPI_CTRL_OOB_RX_DMA_EN -+ | ESPI_CTRL_OOB_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); ++ mmbi = container_of(fp->private_data, struct aspeed_espi_perif_mmbi, h2b_mdev); + -+ if (oob->dma.enable) { -+ dmam_free_coherent(dev, sizeof(*oob->dma.txd_virt) * OOB_DMA_DESC_NUM, -+ oob->dma.txd_virt, oob->dma.txd_addr); -+ dmam_free_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, -+ oob->dma.tx_virt, oob->dma.tx_addr); -+ dmam_free_coherent(dev, sizeof(*oob->dma.rxd_virt) * OOB_DMA_DESC_NUM, -+ oob->dma.rxd_virt, oob->dma.rxd_addr); -+ dmam_free_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, -+ oob->dma.rx_virt, oob->dma.rx_addr); -+ } ++ poll_wait(fp, &mmbi->wq, pt); + -+ mutex_destroy(&oob->tx_mtx); -+ mutex_destroy(&oob->rx_mtx); ++ if (!mmbi->host_rwp_update) ++ return 0; + -+ misc_deregister(&oob->mdev); ++ mmbi->host_rwp_update = false; + -+ return 0; ++ return EPOLLIN; +} + -+/* flash channel (CH3) */ -+static long ast2600_espi_flash_get_rx(struct file *fp, -+ struct ast2600_espi_flash *flash, -+ struct aspeed_espi_ioc *ioc) ++static long ast2600_espi_perif_pc_get_rx(struct file *fp, ++ struct aspeed_espi_perif *perif, ++ struct aspeed_espi_ioc *ioc) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2600_espi *espi; ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; + struct espi_comm_hdr *hdr; + unsigned long flags; -+ uint32_t pkt_len; -+ uint8_t *pkt; ++ u32 pkt_len; ++ u8 *pkt; + int i, rc; + -+ rc = 0; -+ -+ espi = container_of(flash, struct ast2600_espi, flash); ++ espi = container_of(perif, struct aspeed_espi, perif); + + if (fp->f_flags & O_NONBLOCK) { -+ if (!mutex_trylock(&flash->rx_mtx)) ++ if (!mutex_trylock(&perif->pc_rx_mtx)) + return -EAGAIN; + -+ if (!flash->rx_ready) { ++ if (!perif->rx_ready) { + rc = -ENODATA; + goto unlock_mtx_n_out; + } + } else { -+ mutex_lock(&flash->rx_mtx); ++ mutex_lock(&perif->pc_rx_mtx); + -+ if (!flash->rx_ready) { -+ rc = wait_event_interruptible(flash->wq, flash->rx_ready); ++ if (!perif->rx_ready) { ++ rc = wait_event_interruptible(perif->wq, perif->rx_ready); + if (rc == -ERESTARTSYS) { + rc = -EINTR; + goto unlock_mtx_n_out; @@ -70575,10 +77757,10 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ -+ reg = readl(espi->regs + ESPI_FLASH_RX_CTRL); -+ cyc = FIELD_GET(ESPI_FLASH_RX_CTRL_CYC, reg); -+ tag = FIELD_GET(ESPI_FLASH_RX_CTRL_TAG, reg); -+ len = FIELD_GET(ESPI_FLASH_RX_CTRL_LEN, reg); ++ reg = readl(espi->regs + ESPI_PERIF_PC_RX_CTRL); ++ cyc = FIELD_GET(ESPI_PERIF_PC_RX_CTRL_CYC, reg); ++ tag = FIELD_GET(ESPI_PERIF_PC_RX_CTRL_TAG, reg); ++ len = FIELD_GET(ESPI_PERIF_PC_RX_CTRL_LEN, reg); + + /* + * calculate the length of the rest part of the @@ -70586,24 +77768,23 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + * user space. + */ + switch (cyc) { -+ case ESPI_FLASH_WRITE: -+ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + -+ sizeof(struct espi_flash_rwe); ++ case ESPI_PERIF_MSG: ++ pkt_len = sizeof(struct espi_perif_msg); + break; -+ case ESPI_FLASH_READ: -+ case ESPI_FLASH_ERASE: -+ pkt_len = sizeof(struct espi_flash_rwe); ++ case ESPI_PERIF_MSG_D: ++ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + ++ sizeof(struct espi_perif_msg); + break; -+ case ESPI_FLASH_SUC_CMPLT_D_MIDDLE: -+ case ESPI_FLASH_SUC_CMPLT_D_FIRST: -+ case ESPI_FLASH_SUC_CMPLT_D_LAST: -+ case ESPI_FLASH_SUC_CMPLT_D_ONLY: ++ case ESPI_PERIF_SUC_CMPLT_D_MIDDLE: ++ case ESPI_PERIF_SUC_CMPLT_D_FIRST: ++ case ESPI_PERIF_SUC_CMPLT_D_LAST: ++ case ESPI_PERIF_SUC_CMPLT_D_ONLY: + pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + -+ sizeof(struct espi_flash_cmplt); ++ sizeof(struct espi_perif_cmplt); + break; -+ case ESPI_FLASH_SUC_CMPLT: -+ case ESPI_FLASH_UNSUC_CMPLT: -+ pkt_len = sizeof(struct espi_flash_cmplt); ++ case ESPI_PERIF_SUC_CMPLT: ++ case ESPI_PERIF_UNSUC_CMPLT: ++ pkt_len = sizeof(struct espi_perif_cmplt); + break; + default: + rc = -EFAULT; @@ -70627,11 +77808,11 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + hdr->len_h = len >> 8; + hdr->len_l = len & 0xff; + -+ if (flash->dma.enable) { -+ memcpy(hdr + 1, flash->dma.rx_virt, pkt_len - sizeof(*hdr)); ++ if (perif->dma.enable) { ++ memcpy(hdr + 1, perif->dma.pc_rx_virt, pkt_len - sizeof(*hdr)); + } else { + for (i = sizeof(*hdr); i < pkt_len; ++i) -+ pkt[i] = readl(espi->regs + ESPI_FLASH_RX_DATA) & 0xff; ++ reg = readl(espi->regs + ESPI_PERIF_PC_RX_DATA) & 0xff; + } + + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { @@ -70639,49 +77820,116 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + goto free_n_out; + } + -+ spin_lock_irqsave(&flash->lock, flags); ++ spin_lock_irqsave(&perif->lock, flags); + -+ writel(ESPI_FLASH_RX_CTRL_SERV_PEND, espi->regs + ESPI_FLASH_RX_CTRL); -+ flash->rx_ready = 0; ++ writel(ESPI_PERIF_PC_RX_CTRL_SERV_PEND, espi->regs + ESPI_PERIF_PC_RX_CTRL); ++ perif->rx_ready = 0; + -+ spin_unlock_irqrestore(&flash->lock, flags); ++ spin_unlock_irqrestore(&perif->lock, flags); ++ ++ rc = 0; ++ ++free_n_out: ++ vfree(pkt); ++ ++unlock_mtx_n_out: ++ mutex_unlock(&perif->pc_rx_mtx); ++ ++ return rc; ++} ++ ++static long ast2600_espi_perif_pc_put_tx(struct file *fp, ++ struct aspeed_espi_perif *perif, ++ struct aspeed_espi_ioc *ioc) ++{ ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ u8 *pkt; ++ int i, rc; ++ ++ espi = container_of(perif, struct aspeed_espi, perif); ++ ++ if (!mutex_trylock(&perif->pc_tx_mtx)) ++ return -EAGAIN; ++ ++ reg = readl(espi->regs + ESPI_PERIF_PC_TX_CTRL); ++ if (reg & ESPI_PERIF_PC_TX_CTRL_TRIG_PEND) { ++ rc = -EBUSY; ++ goto unlock_n_out; ++ } ++ ++ pkt = vmalloc(ioc->pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_n_out; ++ } ++ ++ hdr = (struct espi_comm_hdr *)pkt; ++ ++ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } ++ ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ if (perif->dma.enable) { ++ memcpy(perif->dma.pc_tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); ++ dma_wmb(); ++ } else { ++ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) ++ writel(pkt[i], espi->regs + ESPI_PERIF_PC_TX_DATA); ++ } ++ ++ cyc = hdr->cyc; ++ tag = hdr->tag; ++ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); ++ ++ reg = FIELD_PREP(ESPI_PERIF_PC_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_PERIF_PC_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_PERIF_PC_TX_CTRL_LEN, len) ++ | ESPI_PERIF_PC_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_PERIF_PC_TX_CTRL); + + rc = 0; + +free_n_out: + vfree(pkt); + -+unlock_mtx_n_out: -+ mutex_unlock(&flash->rx_mtx); ++unlock_n_out: ++ mutex_unlock(&perif->pc_tx_mtx); + + return rc; +} + -+static long ast2600_espi_flash_put_tx(struct file *fp, -+ struct ast2600_espi_flash *flash, -+ struct aspeed_espi_ioc *ioc) ++static long ast2600_espi_perif_np_put_tx(struct file *fp, ++ struct aspeed_espi_perif *perif, ++ struct aspeed_espi_ioc *ioc) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2600_espi *espi; ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; + struct espi_comm_hdr *hdr; -+ uint8_t *pkt; ++ u8 *pkt; + int i, rc; + -+ espi = container_of(flash, struct ast2600_espi, flash); ++ espi = container_of(perif, struct aspeed_espi, perif); + -+ if (!mutex_trylock(&flash->tx_mtx)) ++ if (!mutex_trylock(&perif->np_tx_mtx)) + return -EAGAIN; + -+ reg = readl(espi->regs + ESPI_FLASH_TX_CTRL); -+ if (reg & ESPI_FLASH_TX_CTRL_TRIG_PEND) { ++ reg = readl(espi->regs + ESPI_PERIF_NP_TX_CTRL); ++ if (reg & ESPI_PERIF_NP_TX_CTRL_TRIG_PEND) { + rc = -EBUSY; -+ goto unlock_mtx_n_out; ++ goto unlock_n_out; + } + + pkt = vmalloc(ioc->pkt_len); + if (!pkt) { + rc = -ENOMEM; -+ goto unlock_mtx_n_out; ++ goto unlock_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; @@ -70695,41 +77943,41 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ -+ if (flash->dma.enable) { -+ memcpy(flash->dma.tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); ++ if (perif->dma.enable) { ++ memcpy(perif->dma.np_tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); + dma_wmb(); + } else { + for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_FLASH_TX_DATA); ++ writel(pkt[i], espi->regs + ESPI_PERIF_NP_TX_DATA); + } + + cyc = hdr->cyc; + tag = hdr->tag; + len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + -+ reg = FIELD_PREP(ESPI_FLASH_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_FLASH_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_FLASH_TX_CTRL_LEN, len) -+ | ESPI_FLASH_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_FLASH_TX_CTRL); ++ reg = FIELD_PREP(ESPI_PERIF_NP_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_PERIF_NP_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_PERIF_NP_TX_CTRL_LEN, len) ++ | ESPI_PERIF_NP_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_PERIF_NP_TX_CTRL); + + rc = 0; + +free_n_out: + vfree(pkt); + -+unlock_mtx_n_out: -+ mutex_unlock(&flash->tx_mtx); ++unlock_n_out: ++ mutex_unlock(&perif->np_tx_mtx); + + return rc; +} + -+static long ast2600_espi_flash_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static long ast2600_espi_perif_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ -+ struct ast2600_espi_flash *flash; ++ struct aspeed_espi_perif *perif; + struct aspeed_espi_ioc ioc; + -+ flash = container_of(fp->private_data, struct ast2600_espi_flash, mdev); ++ perif = container_of(fp->private_data, struct aspeed_espi_perif, mdev); + + if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) + return -EFAULT; @@ -70738,10 +77986,12 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + return -EINVAL; + + switch (cmd) { -+ case ASPEED_ESPI_FLASH_GET_RX: -+ return ast2600_espi_flash_get_rx(fp, flash, &ioc); -+ case ASPEED_ESPI_FLASH_PUT_TX: -+ return ast2600_espi_flash_put_tx(fp, flash, &ioc); ++ case ASPEED_ESPI_PERIF_PC_GET_RX: ++ return ast2600_espi_perif_pc_get_rx(fp, perif, &ioc); ++ case ASPEED_ESPI_PERIF_PC_PUT_TX: ++ return ast2600_espi_perif_pc_put_tx(fp, perif, &ioc); ++ case ASPEED_ESPI_PERIF_NP_PUT_TX: ++ return ast2600_espi_perif_np_put_tx(fp, perif, &ioc); + default: + break; + }; @@ -70749,1326 +77999,1848 @@ diff --git a/drivers/soc/aspeed/ast2600-espi.c b/drivers/soc/aspeed/ast2600-espi + return -EINVAL; +} + -+static const struct file_operations ast2600_espi_flash_fops = { ++static int ast2600_espi_perif_mmap(struct file *fp, struct vm_area_struct *vma) ++{ ++ struct aspeed_espi_perif *perif; ++ unsigned long vm_size; ++ pgprot_t vm_prot; ++ ++ perif = container_of(fp->private_data, struct aspeed_espi_perif, mdev); ++ if (!perif->mcyc.enable) ++ return -EPERM; ++ ++ vm_size = vma->vm_end - vma->vm_start; ++ vm_prot = vma->vm_page_prot; ++ ++ if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > perif->mcyc.size) ++ return -EINVAL; ++ ++ vm_prot = pgprot_noncached(vm_prot); ++ ++ if (remap_pfn_range(vma, vma->vm_start, ++ (perif->mcyc.taddr >> PAGE_SHIFT) + vma->vm_pgoff, ++ vm_size, vm_prot)) ++ return -EAGAIN; ++ ++ return 0; ++} ++ ++static const struct file_operations ast2600_espi_mmbi_b2h_fops = { + .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2600_espi_flash_ioctl, ++ .mmap = ast2600_espi_mmbi_b2h_mmap, ++}; ++ ++static const struct file_operations ast2600_espi_mmbi_h2b_fops = { ++ .owner = THIS_MODULE, ++ .mmap = ast2600_espi_mmbi_h2b_mmap, ++ .poll = ast2600_espi_mmbi_h2b_poll, ++}; ++ ++static const struct file_operations ast2600_espi_perif_fops = { ++ .owner = THIS_MODULE, ++ .mmap = ast2600_espi_perif_mmap, ++ .unlocked_ioctl = ast2600_espi_perif_ioctl, +}; + -+static void ast2600_espi_flash_isr(struct ast2600_espi *espi) ++static irqreturn_t ast2600_espi_perif_mmbi_isr(int irq, void *arg) ++{ ++ struct aspeed_espi_perif_mmbi *mmbi; ++ struct aspeed_espi_perif *perif; ++ struct aspeed_espi *espi; ++ u32 sts, tmp; ++ u32 *p; ++ int i; ++ ++ espi = (struct aspeed_espi *)arg; ++ ++ perif = &espi->perif; ++ ++ sts = readl(espi->regs + ESPI_MMBI_INT_STS); ++ if (!sts) ++ return IRQ_NONE; ++ ++ for (i = 0, tmp = sts; i < PERIF_MMBI_INST_NUM; ++i, tmp >>= 2) { ++ if (!(tmp & 0x3)) ++ continue; ++ ++ mmbi = &perif->mmbi.inst[i]; ++ ++ p = (u32 *)mmbi->h2b_virt; ++ p[0] = readl(espi->regs + ESPI_MMBI_HOST_RWP(i)); ++ p[1] = readl(espi->regs + ESPI_MMBI_HOST_RWP(i) + 4); ++ ++ mmbi->host_rwp_update = true; ++ ++ wake_up_interruptible(&mmbi->wq); ++ } ++ ++ writel(sts, espi->regs + ESPI_MMBI_INT_STS); ++ ++ return IRQ_HANDLED; ++} ++ ++static void ast2600_espi_perif_isr(struct aspeed_espi *espi) +{ -+ struct ast2600_espi_flash *flash; ++ struct aspeed_espi_perif *perif; + unsigned long flags; -+ uint32_t sts; ++ u32 sts; + -+ flash = &espi->flash; ++ perif = &espi->perif; + + sts = readl(espi->regs + ESPI_INT_STS); + -+ if (sts & ESPI_INT_STS_FLASH_RX_CMPLT) { -+ writel(ESPI_INT_STS_FLASH_RX_CMPLT, espi->regs + ESPI_INT_STS); ++ if (sts & ESPI_INT_STS_PERIF_PC_RX_CMPLT) { ++ writel(ESPI_INT_STS_PERIF_PC_RX_CMPLT, espi->regs + ESPI_INT_STS); + -+ spin_lock_irqsave(&flash->lock, flags); -+ flash->rx_ready = true; -+ spin_unlock_irqrestore(&flash->lock, flags); ++ spin_lock_irqsave(&perif->lock, flags); ++ perif->rx_ready = true; ++ spin_unlock_irqrestore(&perif->lock, flags); + -+ wake_up_interruptible(&flash->wq); ++ wake_up_interruptible(&perif->wq); + } +} + -+static void ast2600_espi_flash_reset(struct ast2600_espi *espi) ++static void ast2600_espi_perif_sw_reset(struct aspeed_espi *espi) +{ -+ struct ast2600_espi_flash *flash; -+ uint32_t reg; -+ -+ flash = &espi->flash; ++ struct device *dev; ++ u32 reg; + -+ writel(ESPI_INT_EN_FLASH, espi->regs + ESPI_INT_EN_CLR); -+ writel(ESPI_INT_STS_FLASH, espi->regs + ESPI_INT_STS); ++ dev = espi->dev; + + reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_FLASH_TX_SW_RST -+ | ESPI_CTRL_FLASH_RX_SW_RST -+ | ESPI_CTRL_FLASH_TX_DMA_EN -+ | ESPI_CTRL_FLASH_RX_DMA_EN -+ | ESPI_CTRL_FLASH_SW_RDY); ++ reg &= ~(ESPI_CTRL_PERIF_NP_TX_SW_RST ++ | ESPI_CTRL_PERIF_NP_RX_SW_RST ++ | ESPI_CTRL_PERIF_PC_TX_SW_RST ++ | ESPI_CTRL_PERIF_PC_RX_SW_RST ++ | ESPI_CTRL_PERIF_NP_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_RX_DMA_EN ++ | ESPI_CTRL_PERIF_SW_RDY); + writel(reg, espi->regs + ESPI_CTRL); + + udelay(1); + -+ reg |= (ESPI_CTRL_FLASH_TX_SW_RST | ESPI_CTRL_FLASH_RX_SW_RST); ++ reg |= (ESPI_CTRL_PERIF_NP_TX_SW_RST ++ | ESPI_CTRL_PERIF_NP_RX_SW_RST ++ | ESPI_CTRL_PERIF_PC_TX_SW_RST ++ | ESPI_CTRL_PERIF_PC_RX_SW_RST); + writel(reg, espi->regs + ESPI_CTRL); ++} ++ ++static void ast2600_espi_perif_reset(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_perif *perif; ++ struct device *dev; ++ u32 reg, mask; ++ ++ dev = espi->dev; ++ ++ perif = &espi->perif; ++ ++ writel(ESPI_INT_EN_PERIF, espi->regs + ESPI_INT_EN_CLR); ++ writel(ESPI_INT_STS_PERIF, espi->regs + ESPI_INT_STS); + -+ reg = readl(espi->regs + ESPI_CTRL) & ~ESPI_CTRL_FLASH_SAFS_MODE; -+ reg |= FIELD_PREP(ESPI_CTRL_FLASH_SAFS_MODE, flash->safs.mode); ++ writel(0x0, espi->regs + ESPI_MMBI_INT_EN); ++ writel(0xffffffff, espi->regs + ESPI_MMBI_INT_STS); ++ ++ reg = readl(espi->regs + ESPI_CTRL2); ++ reg &= ~(ESPI_CTRL2_MCYC_RD_DIS_WDT | ESPI_CTRL2_MCYC_WR_DIS_WDT); ++ writel(reg, espi->regs + ESPI_CTRL2); ++ ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_PERIF_NP_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_RX_DMA_EN ++ | ESPI_CTRL_PERIF_SW_RDY); + writel(reg, espi->regs + ESPI_CTRL); + -+ if (flash->safs.mode == SAFS_MODE_MIX) { -+ reg = FIELD_PREP(ESPI_FLASH_SAFS_TADDR_BASE, flash->safs.taddr >> 24) -+ | FIELD_PREP(ESPI_FLASH_SAFS_TADDR_MASK, (~(flash->safs.size - 1)) >> 24); -+ writel(reg, espi->regs + ESPI_FLASH_SAFS_TADDR); ++ if (perif->mmbi.enable) { ++ reg = readl(espi->regs + ESPI_MMBI_CTRL); ++ reg &= ~(ESPI_MMBI_CTRL_EN); ++ writel(reg, espi->regs + ESPI_MMBI_CTRL); ++ ++ mask = ~(perif->mmbi.size - 1); ++ writel(mask, espi->regs + ESPI_PERIF_MMBI_MASK); ++ writel(perif->mmbi.saddr, espi->regs + ESPI_PERIF_MMBI_SADDR); ++ writel(perif->mmbi.taddr, espi->regs + ESPI_PERIF_MMBI_TADDR); ++ ++ writel(0xffffffff, espi->regs + ESPI_MMBI_INT_EN); ++ ++ reg = FIELD_PREP(ESPI_MMBI_CTRL_INST_SZ, perif->mmbi.inst_size) ++ | FIELD_PREP(ESPI_MMBI_CTRL_TOTAL_SZ, perif->mmbi.inst_size) ++ | ESPI_MMBI_CTRL_EN; ++ writel(reg, espi->regs + ESPI_MMBI_CTRL); ++ ++ reg = readl(espi->regs + ESPI_CTRL2) & ~(ESPI_CTRL2_MMBI_RD_DIS | ESPI_CTRL2_MMBI_WR_DIS); ++ writel(reg, espi->regs + ESPI_CTRL2); + } + -+ if (flash->dma.enable) { -+ writel(flash->dma.tx_addr, espi->regs + ESPI_FLASH_TX_DMA); -+ writel(flash->dma.rx_addr, espi->regs + ESPI_FLASH_RX_DMA); ++ if (perif->mcyc.enable) { ++ mask = ~(perif->mcyc.size - 1); ++ writel(mask, espi->regs + ESPI_PERIF_MCYC_MASK); ++ writel(perif->mcyc.saddr, espi->regs + ESPI_PERIF_MCYC_SADDR); ++ writel(perif->mcyc.taddr, espi->regs + ESPI_PERIF_MCYC_TADDR); ++ ++ reg = readl(espi->regs + ESPI_CTRL2) & ~(ESPI_CTRL2_MCYC_RD_DIS | ESPI_CTRL2_MCYC_WR_DIS); ++ writel(reg, espi->regs + ESPI_CTRL2); ++ } ++ ++ if (perif->dma.enable) { ++ writel(perif->dma.np_tx_addr, espi->regs + ESPI_PERIF_NP_TX_DMA); ++ writel(perif->dma.pc_tx_addr, espi->regs + ESPI_PERIF_PC_TX_DMA); ++ writel(perif->dma.pc_rx_addr, espi->regs + ESPI_PERIF_PC_RX_DMA); + + reg = readl(espi->regs + ESPI_CTRL) -+ | ESPI_CTRL_FLASH_TX_DMA_EN -+ | ESPI_CTRL_FLASH_RX_DMA_EN; ++ | ESPI_CTRL_PERIF_NP_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_RX_DMA_EN; + writel(reg, espi->regs + ESPI_CTRL); + } + -+ writel(ESPI_INT_EN_FLASH_RX_CMPLT, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_EN_PERIF_PC_RX_CMPLT, espi->regs + ESPI_INT_EN); + -+ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_FLASH_SW_RDY; ++ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_PERIF_SW_RDY; + writel(reg, espi->regs + ESPI_CTRL); +} + -+static int ast2600_espi_flash_probe(struct ast2600_espi *espi) ++int ast2600_espi_perif_probe(struct aspeed_espi *espi) +{ -+ struct ast2600_espi_flash *flash; ++ struct aspeed_espi_perif_mmbi *mmbi; ++ struct aspeed_espi_perif *perif; ++ struct platform_device *pdev; + struct device *dev; -+ int rc; ++ int i, rc; + + dev = espi->dev; + -+ flash = &espi->flash; ++ perif = &espi->perif; + -+ init_waitqueue_head(&flash->wq); ++ init_waitqueue_head(&perif->wq); + -+ spin_lock_init(&flash->lock); ++ spin_lock_init(&perif->lock); + -+ mutex_init(&flash->tx_mtx); -+ mutex_init(&flash->rx_mtx); ++ mutex_init(&perif->np_tx_mtx); ++ mutex_init(&perif->pc_tx_mtx); ++ mutex_init(&perif->pc_rx_mtx); + -+ flash->safs.mode = SAFS_MODE_HW; ++ perif->mmbi.enable = of_property_read_bool(dev->of_node, "perif-mmbi-enable"); ++ if (perif->mmbi.enable) { ++ pdev = espi->pdev; + -+ of_property_read_u32(dev->of_node, "flash-safs-mode", &flash->safs.mode); -+ if (flash->safs.mode == SAFS_MODE_MIX) { -+ rc = of_property_read_u32(dev->of_node, "flash-safs-tgt-addr", &flash->safs.taddr); -+ if (rc || !IS_ALIGNED(flash->safs.taddr, FLASH_SAFS_ALIGN)) { -+ dev_err(dev, "cannot get 16MB-aligned SAFS target address\n"); ++ perif->mmbi.irq = platform_get_irq(pdev, 1); ++ if (perif->mmbi.irq < 0) { ++ dev_err(dev, "cannot get MMBI IRQ number\n"); ++ return -ENODEV; ++ } ++ ++ rc = of_property_read_u32(dev->of_node, "perif-mmbi-src-addr", (u32 *)&perif->mmbi.saddr); ++ if (rc || !IS_ALIGNED(perif->mmbi.saddr, PERIF_MMBI_ALIGN)) { ++ dev_err(dev, "cannot get 64KB-aligned MMBI host address\n"); ++ return -ENODEV; ++ } ++ ++ rc = of_property_read_u32(dev->of_node, "perif-mmbi-instance-size", (u32 *)&perif->mmbi.inst_size); ++ if (rc || perif->mmbi.inst_size >= MMBI_INST_SIZE_TYPES) { ++ dev_err(dev, "cannot get valid MMBI instance size\n"); ++ return -EINVAL; ++ } ++ ++ perif->mmbi.size = (SZ_8K << perif->mmbi.inst_size) * PERIF_MMBI_INST_NUM; ++ perif->mmbi.virt = dmam_alloc_coherent(dev, perif->mmbi.size, ++ &perif->mmbi.taddr, GFP_KERNEL); ++ if (!perif->mmbi.virt) { ++ dev_err(dev, "cannot allocate MMBI\n"); ++ return -ENOMEM; ++ } ++ ++ perif->mmbi.mmbi_isr = ast2600_espi_perif_mmbi_isr; ++ ++ for (i = 0; i < PERIF_MMBI_INST_NUM; ++i) { ++ mmbi = &perif->mmbi.inst[i]; ++ ++ init_waitqueue_head(&mmbi->wq); ++ ++ mmbi->perif = perif; ++ mmbi->host_rwp_update = false; ++ ++ mmbi->b2h_virt = perif->mmbi.virt + ((SZ_4K << perif->mmbi.inst_size) * i); ++ mmbi->b2h_addr = perif->mmbi.taddr + ((SZ_4K << perif->mmbi.inst_size) * i); ++ mmbi->b2h_mdev.parent = dev; ++ mmbi->b2h_mdev.minor = MISC_DYNAMIC_MINOR; ++ mmbi->b2h_mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-mmbi-b2h%d", DEVICE_NAME, i); ++ mmbi->b2h_mdev.fops = &ast2600_espi_mmbi_b2h_fops; ++ rc = misc_register(&mmbi->b2h_mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", mmbi->b2h_mdev.name); ++ return rc; ++ } ++ ++ mmbi->h2b_virt = perif->mmbi.virt + ((SZ_4K << perif->mmbi.inst_size) * (i + PERIF_MMBI_INST_NUM)); ++ mmbi->h2b_addr = perif->mmbi.taddr + ((SZ_4K << perif->mmbi.inst_size) * (i + PERIF_MMBI_INST_NUM)); ++ mmbi->h2b_mdev.parent = dev; ++ mmbi->h2b_mdev.minor = MISC_DYNAMIC_MINOR; ++ mmbi->h2b_mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-mmbi-h2b%d", DEVICE_NAME, i); ++ mmbi->h2b_mdev.fops = &ast2600_espi_mmbi_h2b_fops; ++ rc = misc_register(&mmbi->h2b_mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", mmbi->h2b_mdev.name); ++ return rc; ++ } ++ } ++ } ++ ++ perif->mcyc.enable = of_property_read_bool(dev->of_node, "perif-mcyc-enable"); ++ if (perif->mcyc.enable) { ++ if (perif->mmbi.enable) { ++ dev_err(dev, "cannot enable memory cycle, occupied by MMBI\n"); ++ return -EPERM; ++ } ++ ++ rc = of_property_read_u32(dev->of_node, "perif-mcyc-src-addr", (u32 *)&perif->mcyc.saddr); ++ if (rc || !IS_ALIGNED(perif->mcyc.saddr, PERIF_MCYC_ALIGN)) { ++ dev_err(dev, "cannot get 64KB-aligned memory cycle host address\n"); + return -ENODEV; + } + -+ rc = of_property_read_u32(dev->of_node, "flash-safs-size", &flash->safs.size); -+ if (rc || !IS_ALIGNED(flash->safs.size, FLASH_SAFS_ALIGN)) { -+ dev_err(dev, "cannot get 16MB-aligned SAFS size\n"); -+ return -ENODEV; -+ } -+ } ++ rc = of_property_read_u32(dev->of_node, "perif-mcyc-size", (u32 *)&perif->mcyc.size); ++ if (rc || !IS_ALIGNED(perif->mcyc.size, PERIF_MCYC_ALIGN)) { ++ dev_err(dev, "cannot get 64KB-aligned memory cycle size\n"); ++ return -EINVAL; ++ } ++ ++ perif->mcyc.virt = dmam_alloc_coherent(dev, perif->mcyc.size, ++ &perif->mcyc.taddr, GFP_KERNEL); ++ if (!perif->mcyc.virt) { ++ dev_err(dev, "cannot allocate memory cycle\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ perif->dma.enable = of_property_read_bool(dev->of_node, "perif-dma-mode"); ++ if (perif->dma.enable) { ++ perif->dma.pc_tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, ++ &perif->dma.pc_tx_addr, GFP_KERNEL); ++ if (!perif->dma.pc_tx_virt) { ++ dev_err(dev, "cannot allocate posted TX DMA buffer\n"); ++ return -ENOMEM; ++ } ++ ++ perif->dma.pc_rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, ++ &perif->dma.pc_rx_addr, GFP_KERNEL); ++ if (!perif->dma.pc_rx_virt) { ++ dev_err(dev, "cannot allocate posted RX DMA buffer\n"); ++ return -ENOMEM; ++ } ++ ++ perif->dma.np_tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, ++ &perif->dma.np_tx_addr, GFP_KERNEL); ++ if (!perif->dma.np_tx_virt) { ++ dev_err(dev, "cannot allocate non-posted TX DMA buffer\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ perif->mdev.parent = dev; ++ perif->mdev.minor = MISC_DYNAMIC_MINOR; ++ perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-peripheral", DEVICE_NAME); ++ perif->mdev.fops = &ast2600_espi_perif_fops; ++ rc = misc_register(&perif->mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", perif->mdev.name); ++ return rc; ++ } ++ ++ ast2600_espi_perif_reset(espi); ++ ++ return 0; ++} ++ ++int ast2600_espi_perif_remove(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_perif_mmbi *mmbi; ++ struct aspeed_espi_perif *perif; ++ struct device *dev; ++ u32 reg; ++ int i; ++ ++ dev = espi->dev; ++ ++ perif = &espi->perif; ++ ++ writel(ESPI_INT_EN_PERIF, espi->regs + ESPI_INT_EN_CLR); ++ ++ reg = readl(espi->regs + ESPI_CTRL2); ++ reg |= (ESPI_CTRL2_MCYC_RD_DIS | ESPI_CTRL2_MCYC_WR_DIS); ++ writel(reg, espi->regs + ESPI_CTRL2); ++ ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_PERIF_NP_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_RX_DMA_EN ++ | ESPI_CTRL_PERIF_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); ++ ++ if (perif->mmbi.enable) { ++ reg = readl(espi->regs + ESPI_MMBI_CTRL); ++ reg &= ~ESPI_MMBI_CTRL_EN; ++ writel(reg, espi->regs + ESPI_MMBI_CTRL); ++ ++ for (i = 0; i < PERIF_MMBI_INST_NUM; ++i) { ++ mmbi = &perif->mmbi.inst[i]; ++ misc_deregister(&mmbi->b2h_mdev); ++ misc_deregister(&mmbi->h2b_mdev); ++ } ++ ++ dmam_free_coherent(dev, perif->mmbi.size, perif->mmbi.virt, ++ perif->mmbi.taddr); ++ } ++ ++ if (perif->mcyc.enable) ++ dmam_free_coherent(dev, perif->mcyc.size, perif->mcyc.virt, ++ perif->mcyc.taddr); ++ ++ if (perif->dma.enable) { ++ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.np_tx_virt, ++ perif->dma.np_tx_addr); ++ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.pc_tx_virt, ++ perif->dma.pc_tx_addr); ++ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.pc_rx_virt, ++ perif->dma.pc_rx_addr); ++ } ++ ++ mutex_destroy(&perif->np_tx_mtx); ++ mutex_destroy(&perif->pc_tx_mtx); ++ mutex_destroy(&perif->pc_rx_mtx); ++ ++ misc_deregister(&perif->mdev); ++ ++ return 0; ++} ++ ++/* virtual wire channel (CH1) */ ++static long ast2600_espi_vw_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++{ ++ struct aspeed_espi_vw *vw; ++ struct aspeed_espi *espi; ++ u32 gpio, hw_mode; ++ ++ vw = container_of(fp->private_data, struct aspeed_espi_vw, mdev); ++ espi = container_of(vw, struct aspeed_espi, vw); ++ gpio = vw->gpio.val0; ++ hw_mode = vw->gpio.hw_mode; ++ ++ if (hw_mode) { ++ dev_err(espi->dev, "HW mode: vGPIO reflect on physical GPIO. Get state from GPIO driver.\n"); ++ return -EFAULT; ++ } ++ ++ switch (cmd) { ++ case ASPEED_ESPI_VW_GET_GPIO_VAL: ++ if (put_user(gpio, (u32 __user *)arg)) { ++ dev_err(espi->dev, "failed to get vGPIO value\n"); ++ return -EFAULT; ++ } ++ ++ dev_info(espi->dev, "Get vGPIO value: 0x%x\n", gpio); ++ break; ++ ++ case ASPEED_ESPI_VW_PUT_GPIO_VAL: ++ if (get_user(gpio, (u32 __user *)arg)) { ++ dev_err(espi->dev, "failed to put vGPIO value\n"); ++ return -EFAULT; ++ } ++ ++ dev_info(espi->dev, "Put vGPIO value: 0x%x\n", gpio); ++ writel(gpio, espi->regs + ESPI_VW_GPIO_VAL); ++ break; ++ ++ default: ++ return -EINVAL; ++ }; ++ ++ return 0; ++} ++ ++static const struct file_operations ast2600_espi_vw_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = ast2600_espi_vw_ioctl, ++}; ++ ++static void ast2600_espi_vw_isr(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_vw *vw; ++ u32 sts; ++ ++ vw = &espi->vw; ++ ++ sts = readl(espi->regs + ESPI_INT_STS); ++ ++ if (sts & ESPI_INT_STS_VW_GPIO) { ++ vw->gpio.val0 = readl(espi->regs + ESPI_VW_GPIO_VAL); ++ writel(ESPI_INT_STS_VW_GPIO, espi->regs + ESPI_INT_STS); ++ } else if (sts & ESPI_INT_STS_VW_SYSEVT) { ++ /* Handle system event */ ++ writel(ESPI_INT_STS_VW_SYSEVT, espi->regs + ESPI_INT_STS); ++ } else if (sts & (ESPI_INT_STS_VW_SYSEVT1)) { ++ /* Handle system event1 */ ++ writel(ESPI_INT_STS_VW_SYSEVT1, espi->regs + ESPI_INT_STS); ++ } ++} ++ ++static void ast2600_espi_vw_reset(struct aspeed_espi *espi) ++{ ++ u32 reg; ++ struct aspeed_espi_vw *vw = &espi->vw; ++ ++ writel(ESPI_INT_EN_VW, espi->regs + ESPI_INT_EN_CLR); ++ writel(ESPI_INT_STS_VW, espi->regs + ESPI_INT_STS); ++ ++ writel(vw->gpio.dir0, espi->regs + ESPI_VW_GPIO_DIR); ++ ++ vw->gpio.val0 = readl(espi->regs + ESPI_VW_GPIO_VAL); ++ ++ reg = readl(espi->regs + ESPI_CTRL2) & ~(ESPI_CTRL2_VW_TX_SORT); ++ writel(reg, espi->regs + ESPI_CTRL2); ++ ++ writel(ESPI_INT_EN_VW_GPIO, espi->regs + ESPI_INT_EN); ++ ++ reg = readl(espi->regs + ESPI_CTRL) ++ | ((vw->gpio.hw_mode) ? 0 : ESPI_CTRL_VW_GPIO_SW) ++ | ESPI_CTRL_VW_SW_RDY; ++ writel(reg, espi->regs + ESPI_CTRL); ++ ++ writel(0x0, espi->regs + ESPI_VW_SYSEVT_INT_T0); ++ writel(0x0, espi->regs + ESPI_VW_SYSEVT_INT_T1); ++ ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg |= ESPI_INT_EN_RST_DEASSERT; ++ writel(reg, espi->regs + ESPI_INT_EN); ++ ++ writel(0xffffffff, espi->regs + ESPI_VW_SYSEVT_INT_EN); ++ writel(0x1, espi->regs + ESPI_VW_SYSEVT1_INT_EN); ++ writel(0x1, espi->regs + ESPI_VW_SYSEVT1_INT_T0); ++} + -+ flash->dma.enable = of_property_read_bool(dev->of_node, "flash-dma-mode"); -+ if (flash->dma.enable) { -+ flash->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.tx_addr, GFP_KERNEL); -+ if (!flash->dma.tx_virt) { -+ dev_err(dev, "cannot allocate DMA TX buffer\n"); -+ return -ENOMEM; -+ } ++int ast2600_espi_vw_probe(struct aspeed_espi *espi) ++{ ++ int rc; ++ struct device *dev = espi->dev; ++ struct aspeed_espi_vw *vw = &espi->vw; + -+ flash->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.rx_addr, GFP_KERNEL); -+ if (!flash->dma.rx_virt) { -+ dev_err(dev, "cannot allocate DMA RX buffer\n"); -+ return -ENOMEM; -+ } -+ } ++ vw->gpio.hw_mode = of_property_read_bool(dev->of_node, "vw-gpio-hw-mode"); ++ of_property_read_u32(dev->of_node, "vw-gpio-direction", &vw->gpio.dir0); + -+ flash->mdev.parent = dev; -+ flash->mdev.minor = MISC_DYNAMIC_MINOR; -+ flash->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-flash", DEVICE_NAME); -+ flash->mdev.fops = &ast2600_espi_flash_fops; -+ rc = misc_register(&flash->mdev); ++ vw->mdev.parent = dev; ++ vw->mdev.minor = MISC_DYNAMIC_MINOR; ++ vw->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-vw", DEVICE_NAME); ++ vw->mdev.fops = &ast2600_espi_vw_fops; ++ rc = misc_register(&vw->mdev); + if (rc) { -+ dev_err(dev, "cannot register device %s\n", flash->mdev.name); ++ dev_err(dev, "cannot register device %s\n", vw->mdev.name); + return rc; + } + -+ ast2600_espi_flash_reset(espi); ++ ast2600_espi_vw_reset(espi); + + return 0; +} + -+static int ast2600_espi_flash_remove(struct ast2600_espi *espi) ++int ast2600_espi_vw_remove(struct aspeed_espi *espi) +{ -+ struct ast2600_espi_flash *flash; -+ struct device *dev; -+ uint32_t reg; -+ -+ dev = espi->dev; -+ -+ flash = &espi->flash; -+ -+ writel(ESPI_INT_EN_FLASH, espi->regs + ESPI_INT_EN_CLR); -+ -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_FLASH_TX_DMA_EN -+ | ESPI_CTRL_FLASH_RX_DMA_EN -+ | ESPI_CTRL_FLASH_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); ++ struct aspeed_espi_vw *vw; + -+ if (flash->dma.enable) { -+ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.tx_virt, flash->dma.tx_addr); -+ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.rx_virt, flash->dma.rx_addr); -+ } ++ vw = &espi->vw; + -+ mutex_destroy(&flash->tx_mtx); -+ mutex_destroy(&flash->rx_mtx); ++ writel(ESPI_INT_EN_VW, espi->regs + ESPI_INT_EN_CLR); + -+ misc_deregister(&flash->mdev); ++ misc_deregister(&vw->mdev); + + return 0; +} + -+/* global control */ -+static irqreturn_t ast2600_espi_isr(int irq, void *arg) ++/* out-of-band channel (CH2) */ ++static long ast2600_espi_oob_dma_get_rx(struct file *fp, ++ struct aspeed_espi_oob *oob, ++ struct aspeed_espi_ioc *ioc) +{ -+ struct ast2600_espi *espi; -+ uint32_t sts; ++ struct ast2600_espi_oob_dma_rx_desc *d; ++ struct ast2600_espi_oob_dma_rx_desc *rx_descs; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ u32 wptr, pkt_len; ++ unsigned long flags; ++ u8 *pkt; ++ int rc; + -+ espi = (struct ast2600_espi *)arg; ++ espi = container_of(oob, struct aspeed_espi, oob); + -+ sts = readl(espi->regs + ESPI_INT_STS); -+ if (!sts) -+ return IRQ_NONE; ++ wptr = FIELD_PREP(ESPI_OOB_RX_DESC_WPTR_WP, readl(espi->regs + ESPI_OOB_RX_DESC_WPTR)); + -+ if (sts & ESPI_INT_STS_PERIF) -+ ast2600_espi_perif_isr(espi); ++ rx_descs = (struct ast2600_espi_oob_dma_rx_desc *)oob->dma.rxd_virt; ++ d = &rx_descs[wptr]; + -+ if (sts & ESPI_INT_STS_VW) -+ ast2600_espi_vw_isr(espi); ++ if (!d->dirty) ++ return -EFAULT; + -+ if (sts & ESPI_INT_STS_OOB) -+ ast2600_espi_oob_isr(espi); ++ pkt_len = ((d->len) ? d->len : ESPI_MAX_PLD_LEN) + sizeof(struct espi_comm_hdr); + -+ if (sts & ESPI_INT_STS_FLASH) -+ ast2600_espi_flash_isr(espi); ++ if (ioc->pkt_len < pkt_len) ++ return -EINVAL; + -+ if (sts & ESPI_INT_STS_RST_DEASSERT) { -+ /* this will clear all interrupt enable and status */ -+ reset_control_assert(espi->rst); -+ reset_control_deassert(espi->rst); ++ pkt = vmalloc(pkt_len); ++ if (!pkt) ++ return -ENOMEM; + -+ ast2600_espi_perif_sw_reset(espi); -+ ast2600_espi_perif_reset(espi); -+ ast2600_espi_vw_reset(espi); -+ ast2600_espi_oob_reset(espi); -+ ast2600_espi_flash_reset(espi); ++ hdr = (struct espi_comm_hdr *)pkt; ++ hdr->cyc = d->cyc; ++ hdr->tag = d->tag; ++ hdr->len_h = d->len >> 8; ++ hdr->len_l = d->len & 0xff; ++ memcpy(hdr + 1, oob->dma.rx_virt + (PAGE_SIZE * wptr), pkt_len - sizeof(*hdr)); + -+ /* re-enable eSPI_RESET# interrupt */ -+ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN); ++ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; + } + -+ return IRQ_HANDLED; -+} ++ spin_lock_irqsave(&oob->lock, flags); + -+static int ast2600_espi_probe(struct platform_device *pdev) -+{ -+ struct ast2600_espi *espi; -+ struct resource *res; -+ struct device *dev; -+ int rc; ++ /* make current descriptor available again */ ++ d->dirty = 0; + -+ dev = &pdev->dev; ++ wptr = (wptr + 1) % OOB_DMA_DESC_NUM; ++ writel(wptr | ESPI_OOB_RX_DESC_WPTR_RECV_EN, espi->regs + ESPI_OOB_RX_DESC_WPTR); + -+ espi = devm_kzalloc(dev, sizeof(*espi), GFP_KERNEL); -+ if (!espi) -+ return -ENOMEM; ++ /* set ready flag base on the next RX descriptor */ ++ oob->rx_ready = rx_descs[wptr].dirty; + -+ espi->dev = dev; ++ spin_unlock_irqrestore(&oob->lock, flags); + -+ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); -+ if (rc) { -+ dev_err(dev, "cannot set 64-bits DMA mask\n"); -+ return rc; -+ } ++ rc = 0; + -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) { -+ dev_err(dev, "cannot get resource\n"); -+ return -ENODEV; -+ } ++free_n_out: ++ vfree(pkt); + -+ espi->regs = devm_ioremap_resource(dev, res); -+ if (IS_ERR(espi->regs)) { -+ dev_err(dev, "cannot map registers\n"); -+ return PTR_ERR(espi->regs); -+ } ++ return rc; ++} + -+ espi->irq = platform_get_irq(pdev, 0); -+ if (espi->irq < 0) { -+ dev_err(dev, "cannot get IRQ number\n"); -+ return -ENODEV; -+ } ++static long ast2600_espi_oob_get_rx(struct file *fp, ++ struct aspeed_espi_oob *oob, ++ struct aspeed_espi_ioc *ioc) ++{ ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ unsigned long flags; ++ u32 pkt_len; ++ u8 *pkt; ++ int i, rc; + -+ espi->rst = devm_reset_control_get_exclusive_by_index(dev, 0); -+ if (IS_ERR(espi->rst)) { -+ dev_err(dev, "cannot get reset control\n"); -+ return PTR_ERR(espi->rst); -+ } ++ espi = container_of(oob, struct aspeed_espi, oob); + -+ espi->clk = devm_clk_get(dev, NULL); -+ if (IS_ERR(espi->clk)) { -+ dev_err(dev, "cannot get clock control\n"); -+ return PTR_ERR(espi->clk); ++ if (fp->f_flags & O_NONBLOCK) { ++ if (!mutex_trylock(&oob->rx_mtx)) ++ return -EAGAIN; ++ ++ if (!oob->rx_ready) { ++ rc = -ENODATA; ++ goto unlock_mtx_n_out; ++ } ++ } else { ++ mutex_lock(&oob->rx_mtx); ++ ++ if (!oob->rx_ready) { ++ rc = wait_event_interruptible(oob->wq, oob->rx_ready); ++ if (rc == -ERESTARTSYS) { ++ rc = -EINTR; ++ goto unlock_mtx_n_out; ++ } ++ } + } + -+ rc = clk_prepare_enable(espi->clk); -+ if (rc) { -+ dev_err(dev, "cannot enable clocks\n"); -+ return rc; ++ if (oob->dma.enable) { ++ rc = ast2600_espi_oob_dma_get_rx(fp, oob, ioc); ++ goto unlock_mtx_n_out; + } + -+ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN_CLR); ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ reg = readl(espi->regs + ESPI_OOB_RX_CTRL); ++ cyc = FIELD_GET(ESPI_OOB_RX_CTRL_CYC, reg); ++ tag = FIELD_GET(ESPI_OOB_RX_CTRL_TAG, reg); ++ len = FIELD_GET(ESPI_OOB_RX_CTRL_LEN, reg); + -+ rc = ast2600_espi_perif_probe(espi); -+ if (rc) { -+ dev_err(dev, "cannot init peripheral channel, rc=%d\n", rc); -+ return rc; -+ } ++ /* ++ * calculate the length of the rest part of the ++ * eSPI packet to be read from HW and copied to ++ * user space. ++ */ ++ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + sizeof(struct espi_comm_hdr); + -+ rc = ast2600_espi_vw_probe(espi); -+ if (rc) { -+ dev_err(dev, "cannot init vw channel, rc=%d\n", rc); -+ goto err_remove_perif; ++ if (ioc->pkt_len < pkt_len) { ++ rc = -EINVAL; ++ goto unlock_mtx_n_out; + } + -+ rc = ast2600_espi_oob_probe(espi); -+ if (rc) { -+ dev_err(dev, "cannot init oob channel, rc=%d\n", rc); -+ goto err_remove_vw; ++ pkt = vmalloc(pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_mtx_n_out; + } + -+ rc = ast2600_espi_flash_probe(espi); -+ if (rc) { -+ dev_err(dev, "cannot init flash channel, rc=%d\n", rc); -+ goto err_remove_oob; -+ } ++ hdr = (struct espi_comm_hdr *)pkt; ++ hdr->cyc = cyc; ++ hdr->tag = tag; ++ hdr->len_h = len >> 8; ++ hdr->len_l = len & 0xff; + -+ rc = devm_request_irq(dev, espi->irq, ast2600_espi_isr, 0, dev_name(dev), espi); -+ if (rc) { -+ dev_err(dev, "cannot request IRQ\n"); -+ goto err_remove_flash; ++ for (i = sizeof(*hdr); i < pkt_len; ++i) ++ pkt[i] = readl(espi->regs + ESPI_OOB_RX_DATA) & 0xff; ++ ++ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; + } + -+ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN); ++ spin_lock_irqsave(&oob->lock, flags); + -+ platform_set_drvdata(pdev, espi); ++ writel(ESPI_OOB_RX_CTRL_SERV_PEND, espi->regs + ESPI_OOB_RX_CTRL); ++ oob->rx_ready = 0; + -+ dev_info(dev, "module loaded\n"); ++ spin_unlock_irqrestore(&oob->lock, flags); + -+ return 0; ++ rc = 0; + -+err_remove_flash: -+ ast2600_espi_flash_remove(espi); -+err_remove_oob: -+ ast2600_espi_oob_remove(espi); -+err_remove_vw: -+ ast2600_espi_vw_remove(espi); -+err_remove_perif: -+ ast2600_espi_perif_remove(espi); ++free_n_out: ++ vfree(pkt); ++ ++unlock_mtx_n_out: ++ mutex_unlock(&oob->rx_mtx); + + return rc; +} + -+static void ast2600_espi_remove(struct platform_device *pdev) ++static long ast2600_espi_oob_dma_put_tx(struct file *fp, ++ struct aspeed_espi_oob *oob, ++ struct aspeed_espi_ioc *ioc) +{ -+ struct ast2600_espi *espi; -+ struct device *dev; ++ struct ast2600_espi_oob_dma_tx_desc *d; ++ struct ast2600_espi_oob_dma_tx_desc *tx_descs; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ u32 rptr, wptr; ++ u8 *pkt; + int rc; + -+ dev = &pdev->dev; -+ -+ espi = platform_get_drvdata(pdev); -+ -+ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN_CLR); -+ -+ rc = ast2600_espi_perif_remove(espi); -+ if (rc) -+ dev_warn(dev, "cannot remove peripheral channel, rc=%d\n", rc); -+ -+ rc = ast2600_espi_vw_remove(espi); -+ if (rc) -+ dev_warn(dev, "cannot remove peripheral channel, rc=%d\n", rc); -+ -+ rc = ast2600_espi_oob_remove(espi); -+ if (rc) -+ dev_warn(dev, "cannot remove peripheral channel, rc=%d\n", rc); ++ espi = container_of(oob, struct aspeed_espi, oob); + -+ rc = ast2600_espi_flash_remove(espi); -+ if (rc) -+ dev_warn(dev, "cannot remove peripheral channel, rc=%d\n", rc); -+} ++ pkt = vzalloc(ioc->pkt_len); ++ if (!pkt) ++ return -ENOMEM; + -+static const struct of_device_id ast2600_espi_of_matches[] = { -+ { .compatible = "aspeed,ast2600-espi" }, -+ { }, -+}; ++ hdr = (struct espi_comm_hdr *)pkt; + -+static struct platform_driver ast2600_espi_driver = { -+ .driver = { -+ .name = "ast2600-espi", -+ .of_match_table = ast2600_espi_of_matches, -+ }, -+ .probe = ast2600_espi_probe, -+ .remove = ast2600_espi_remove, -+}; ++ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } + -+module_platform_driver(ast2600_espi_driver); ++ /* kick HW to update descriptor read/write pointer */ ++ writel(ESPI_OOB_TX_DESC_RPTR_UPDATE, espi->regs + ESPI_OOB_TX_DESC_RPTR); + -+MODULE_AUTHOR("Chia-Wei Wang "); -+MODULE_DESCRIPTION("Control of AST2600 eSPI Device"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/soc/aspeed/ast2600-espi.h b/drivers/soc/aspeed/ast2600-espi.h ---- a/drivers/soc/aspeed/ast2600-espi.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/ast2600-espi.h 2025-12-23 10:16:21.126032636 +0000 -@@ -0,0 +1,297 @@ -+/* SPDX-License-Identifier: GPL-2.0+ */ -+/* -+ * Copyright 2023 Aspeed Technology Inc. -+ */ -+#ifndef _AST2600_ESPI_H_ -+#define _AST2600_ESPI_H_ ++ rptr = readl(espi->regs + ESPI_OOB_TX_DESC_RPTR); ++ wptr = readl(espi->regs + ESPI_OOB_TX_DESC_WPTR); + -+#include -+#include "aspeed-espi-comm.h" ++ if (((wptr + 1) % OOB_DMA_DESC_NUM) == rptr) { ++ rc = -EBUSY; ++ goto free_n_out; ++ } + -+/* registers */ -+#define ESPI_CTRL 0x000 -+#define ESPI_CTRL_FLASH_TX_SW_RST BIT(31) -+#define ESPI_CTRL_FLASH_RX_SW_RST BIT(30) -+#define ESPI_CTRL_OOB_TX_SW_RST BIT(29) -+#define ESPI_CTRL_OOB_RX_SW_RST BIT(28) -+#define ESPI_CTRL_PERIF_NP_TX_SW_RST BIT(27) -+#define ESPI_CTRL_PERIF_NP_RX_SW_RST BIT(26) -+#define ESPI_CTRL_PERIF_PC_TX_SW_RST BIT(25) -+#define ESPI_CTRL_PERIF_PC_RX_SW_RST BIT(24) -+#define ESPI_CTRL_FLASH_TX_DMA_EN BIT(23) -+#define ESPI_CTRL_FLASH_RX_DMA_EN BIT(22) -+#define ESPI_CTRL_OOB_TX_DMA_EN BIT(21) -+#define ESPI_CTRL_OOB_RX_DMA_EN BIT(20) -+#define ESPI_CTRL_PERIF_NP_TX_DMA_EN BIT(19) -+#define ESPI_CTRL_PERIF_PC_TX_DMA_EN BIT(17) -+#define ESPI_CTRL_PERIF_PC_RX_DMA_EN BIT(16) -+#define ESPI_CTRL_FLASH_SAFS_MODE GENMASK(11, 10) -+#define ESPI_CTRL_VW_GPIO_SW BIT(9) -+#define ESPI_CTRL_FLASH_SW_RDY BIT(7) -+#define ESPI_CTRL_OOB_SW_RDY BIT(4) -+#define ESPI_CTRL_VW_SW_RDY BIT(3) -+#define ESPI_CTRL_PERIF_SW_RDY BIT(1) -+#define ESPI_STS 0x004 -+#define ESPI_INT_STS 0x008 -+#define ESPI_INT_STS_RST_DEASSERT BIT(31) -+#define ESPI_INT_STS_OOB_RX_TMOUT BIT(23) -+#define ESPI_INT_STS_VW_SYSEVT1 BIT(22) -+#define ESPI_INT_STS_FLASH_TX_ERR BIT(21) -+#define ESPI_INT_STS_OOB_TX_ERR BIT(20) -+#define ESPI_INT_STS_FLASH_TX_ABT BIT(19) -+#define ESPI_INT_STS_OOB_TX_ABT BIT(18) -+#define ESPI_INT_STS_PERIF_NP_TX_ABT BIT(17) -+#define ESPI_INT_STS_PERIF_PC_TX_ABT BIT(16) -+#define ESPI_INT_STS_FLASH_RX_ABT BIT(15) -+#define ESPI_INT_STS_OOB_RX_ABT BIT(14) -+#define ESPI_INT_STS_PERIF_NP_RX_ABT BIT(13) -+#define ESPI_INT_STS_PERIF_PC_RX_ABT BIT(12) -+#define ESPI_INT_STS_PERIF_NP_TX_ERR BIT(11) -+#define ESPI_INT_STS_PERIF_PC_TX_ERR BIT(10) -+#define ESPI_INT_STS_VW_GPIO BIT(9) -+#define ESPI_INT_STS_VW_SYSEVT BIT(8) -+#define ESPI_INT_STS_FLASH_TX_CMPLT BIT(7) -+#define ESPI_INT_STS_FLASH_RX_CMPLT BIT(6) -+#define ESPI_INT_STS_OOB_TX_CMPLT BIT(5) -+#define ESPI_INT_STS_OOB_RX_CMPLT BIT(4) -+#define ESPI_INT_STS_PERIF_NP_TX_CMPLT BIT(3) -+#define ESPI_INT_STS_PERIF_PC_TX_CMPLT BIT(1) -+#define ESPI_INT_STS_PERIF_PC_RX_CMPLT BIT(0) -+#define ESPI_INT_EN 0x00c -+#define ESPI_INT_EN_RST_DEASSERT BIT(31) -+#define ESPI_INT_EN_OOB_RX_TMOUT BIT(23) -+#define ESPI_INT_EN_VW_SYSEVT1 BIT(22) -+#define ESPI_INT_EN_FLASH_TX_ERR BIT(21) -+#define ESPI_INT_EN_OOB_TX_ERR BIT(20) -+#define ESPI_INT_EN_FLASH_TX_ABT BIT(19) -+#define ESPI_INT_EN_OOB_TX_ABT BIT(18) -+#define ESPI_INT_EN_PERIF_NP_TX_ABT BIT(17) -+#define ESPI_INT_EN_PERIF_PC_TX_ABT BIT(16) -+#define ESPI_INT_EN_FLASH_RX_ABT BIT(15) -+#define ESPI_INT_EN_OOB_RX_ABT BIT(14) -+#define ESPI_INT_EN_PERIF_NP_RX_ABT BIT(13) -+#define ESPI_INT_EN_PERIF_PC_RX_ABT BIT(12) -+#define ESPI_INT_EN_PERIF_NP_TX_ERR BIT(11) -+#define ESPI_INT_EN_PERIF_PC_TX_ERR BIT(10) -+#define ESPI_INT_EN_VW_GPIO BIT(9) -+#define ESPI_INT_EN_VW_SYSEVT BIT(8) -+#define ESPI_INT_EN_FLASH_TX_CMPLT BIT(7) -+#define ESPI_INT_EN_FLASH_RX_CMPLT BIT(6) -+#define ESPI_INT_EN_OOB_TX_CMPLT BIT(5) -+#define ESPI_INT_EN_OOB_RX_CMPLT BIT(4) -+#define ESPI_INT_EN_PERIF_NP_TX_CMPLT BIT(3) -+#define ESPI_INT_EN_PERIF_PC_TX_CMPLT BIT(1) -+#define ESPI_INT_EN_PERIF_PC_RX_CMPLT BIT(0) -+#define ESPI_PERIF_PC_RX_DMA 0x010 -+#define ESPI_PERIF_PC_RX_CTRL 0x014 -+#define ESPI_PERIF_PC_RX_CTRL_SERV_PEND BIT(31) -+#define ESPI_PERIF_PC_RX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_PERIF_PC_RX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_PERIF_PC_RX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_PERIF_PC_RX_DATA 0x018 -+#define ESPI_PERIF_PC_TX_DMA 0x020 -+#define ESPI_PERIF_PC_TX_CTRL 0x024 -+#define ESPI_PERIF_PC_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_PERIF_PC_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_PERIF_PC_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_PERIF_PC_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_PERIF_PC_TX_DATA 0x028 -+#define ESPI_PERIF_NP_TX_DMA 0x030 -+#define ESPI_PERIF_NP_TX_CTRL 0x034 -+#define ESPI_PERIF_NP_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_PERIF_NP_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_PERIF_NP_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_PERIF_NP_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_PERIF_NP_TX_DATA 0x038 -+#define ESPI_OOB_RX_DMA 0x040 -+#define ESPI_OOB_RX_CTRL 0x044 -+#define ESPI_OOB_RX_CTRL_SERV_PEND BIT(31) -+#define ESPI_OOB_RX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_OOB_RX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_OOB_RX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_OOB_RX_DATA 0x048 -+#define ESPI_OOB_TX_DMA 0x050 -+#define ESPI_OOB_TX_CTRL 0x054 -+#define ESPI_OOB_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_OOB_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_OOB_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_OOB_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_OOB_TX_DATA 0x058 -+#define ESPI_FLASH_RX_DMA 0x060 -+#define ESPI_FLASH_RX_CTRL 0x064 -+#define ESPI_FLASH_RX_CTRL_SERV_PEND BIT(31) -+#define ESPI_FLASH_RX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_FLASH_RX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_FLASH_RX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_FLASH_RX_DATA 0x068 -+#define ESPI_FLASH_TX_DMA 0x070 -+#define ESPI_FLASH_TX_CTRL 0x074 -+#define ESPI_FLASH_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_FLASH_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_FLASH_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_FLASH_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_FLASH_TX_DATA 0x078 -+#define ESPI_CTRL2 0x080 -+#define ESPI_CTRL2_VW_TX_SORT BIT(30) -+#define ESPI_CTRL2_MCYC_RD_DIS_WDT BIT(11) -+#define ESPI_CTRL2_MCYC_WR_DIS_WDT BIT(10) -+#define ESPI_CTRL2_MCYC_RD_DIS BIT(6) -+#define ESPI_CTRL2_MMBI_RD_DIS ESPI_CTRL2_MCYC_RD_DIS -+#define ESPI_CTRL2_MCYC_WR_DIS BIT(4) -+#define ESPI_CTRL2_MMBI_WR_DIS ESPI_CTRL2_MCYC_WR_DIS -+#define ESPI_PERIF_MCYC_SADDR 0x084 -+#define ESPI_PERIF_MMBI_SADDR ESPI_PERIF_MCYC_SADDR -+#define ESPI_PERIF_MCYC_TADDR 0x088 -+#define ESPI_PERIF_MMBI_TADDR ESPI_PERIF_MCYC_TADDR -+#define ESPI_PERIF_MCYC_MASK 0x08c -+#define ESPI_PERIF_MMBI_MASK ESPI_PERIF_MCYC_MASK -+#define ESPI_FLASH_SAFS_TADDR 0x090 -+#define ESPI_FLASH_SAFS_TADDR_BASE GENMASK(31, 24) -+#define ESPI_FLASH_SAFS_TADDR_MASK GENMASK(15, 8) -+#define ESPI_VW_SYSEVT_INT_EN 0x094 -+#define ESPI_VW_SYSEVT 0x098 -+#define ESPI_VW_SYSEVT_HOST_RST_ACK BIT(27) -+#define ESPI_VW_SYSEVT_RST_CPU_INIT BIT(26) -+#define ESPI_VW_SYSEVT_SLV_BOOT_STS BIT(23) -+#define ESPI_VW_SYSEVT_NON_FATAL_ERR BIT(22) -+#define ESPI_VW_SYSEVT_FATAL_ERR BIT(21) -+#define ESPI_VW_SYSEVT_SLV_BOOT_DONE BIT(20) -+#define ESPI_VW_SYSEVT_OOB_RST_ACK BIT(16) -+#define ESPI_VW_SYSEVT_NMI_OUT BIT(10) -+#define ESPI_VW_SYSEVT_SMI_OUT BIT(9) -+#define ESPI_VW_SYSEVT_HOST_RST_WARN BIT(8) -+#define ESPI_VW_SYSEVT_OOB_RST_WARN BIT(6) -+#define ESPI_VW_SYSEVT_PLTRSTN BIT(5) -+#define ESPI_VW_SYSEVT_SUSPEND BIT(4) -+#define ESPI_VW_SYSEVT_S5_SLEEP BIT(2) -+#define ESPI_VW_SYSEVT_S4_SLEEP BIT(1) -+#define ESPI_VW_SYSEVT_S3_SLEEP BIT(0) -+#define ESPI_VW_GPIO_VAL 0x09c -+#define ESPI_GEN_CAP_N_CONF 0x0a0 -+#define ESPI_CH0_CAP_N_CONF 0x0a4 -+#define ESPI_CH1_CAP_N_CONF 0x0a8 -+#define ESPI_CH2_CAP_N_CONF 0x0ac -+#define ESPI_CH3_CAP_N_CONF 0x0b0 -+#define ESPI_CH3_CAP_N_CONF2 0x0b4 -+#define ESPI_VW_GPIO_DIR 0x0c0 -+#define ESPI_VW_GPIO_GRP 0x0c4 -+#define ESPI_INT_EN_CLR 0x0fc -+#define ESPI_VW_SYSEVT1_INT_EN 0x100 -+#define ESPI_VW_SYSEVT1 0x104 -+#define ESPI_VW_SYSEVT1_SUSPEND_ACK BIT(20) -+#define ESPI_VW_SYSEVT1_SUSPEND_WARN BIT(0) -+#define ESPI_VW_SYSEVT_INT_T0 0x110 -+#define ESPI_VW_SYSEVT_INT_T1 0x114 -+#define ESPI_VW_SYSEVT_INT_T2 0x118 -+#define ESPI_VW_SYSEVT_INT_STS 0x11c -+#define ESPI_VW_SYSEVT1_INT_T0 0x120 -+#define ESPI_VW_SYSEVT1_INT_T1 0x124 -+#define ESPI_VW_SYSEVT1_INT_T2 0x128 -+#define ESPI_VW_SYSEVT1_INT_STS 0x12c -+#define ESPI_OOB_RX_DESC_NUM 0x130 -+#define ESPI_OOB_RX_DESC_RPTR 0x134 -+#define ESPI_OOB_RX_DESC_RPTR_UPDATE BIT(31) -+#define ESPI_OOB_RX_DESC_RPTR_RP GENMASK(11, 0) -+#define ESPI_OOB_RX_DESC_WPTR 0x138 -+#define ESPI_OOB_RX_DESC_WPTR_RECV_EN BIT(31) -+#define ESPI_OOB_RX_DESC_WPTR_SP GENMASK(27, 16) -+#define ESPI_OOB_RX_DESC_WPTR_WP GENMASK(11, 0) -+#define ESPI_OOB_TX_DESC_NUM 0x140 -+#define ESPI_OOB_TX_DESC_RPTR 0x144 -+#define ESPI_OOB_TX_DESC_RPTR_UPDATE BIT(31) -+#define ESPI_OOB_TX_DESC_WPTR 0x148 -+#define ESPI_OOB_TX_DESC_WPTR_SEND_EN BIT(31) -+#define ESPI_MMBI_CTRL 0x800 -+#define ESPI_MMBI_CTRL_INST_SZ GENMASK(10, 8) -+#define ESPI_MMBI_CTRL_TOTAL_SZ GENMASK(6, 4) -+#define ESPI_MMBI_CTRL_EN BIT(0) -+#define ESPI_MMBI_INT_STS 0x808 -+#define ESPI_MMBI_INT_EN 0x80c -+#define ESPI_MMBI_HOST_RWP(x) (0x810 + ((x) << 3)) ++ tx_descs = (struct ast2600_espi_oob_dma_tx_desc *)oob->dma.txd_virt; ++ d = &tx_descs[wptr]; ++ d->cyc = hdr->cyc; ++ d->tag = hdr->tag; ++ d->len = (hdr->len_h << 8) | (hdr->len_l & 0xff); ++ d->msg_type = OOB_DMA_DESC_CUSTOM; + -+/* collect ESPI_INT_EN bits for convenience */ -+#define ESPI_INT_EN_PERIF \ -+ (ESPI_INT_EN_PERIF_NP_TX_ABT | \ -+ ESPI_INT_EN_PERIF_PC_TX_ABT | \ -+ ESPI_INT_EN_PERIF_NP_RX_ABT | \ -+ ESPI_INT_EN_PERIF_PC_RX_ABT | \ -+ ESPI_INT_EN_PERIF_NP_TX_ERR | \ -+ ESPI_INT_EN_PERIF_PC_TX_ERR | \ -+ ESPI_INT_EN_PERIF_NP_TX_CMPLT | \ -+ ESPI_INT_EN_PERIF_PC_TX_CMPLT | \ -+ ESPI_INT_EN_PERIF_PC_RX_CMPLT) ++ memcpy(oob->dma.tx_virt + (PAGE_SIZE * wptr), hdr + 1, ioc->pkt_len - sizeof(*hdr)); + -+#define ESPI_INT_EN_VW \ -+ (ESPI_INT_EN_VW_SYSEVT1 | \ -+ ESPI_INT_EN_VW_GPIO | \ -+ ESPI_INT_EN_VW_SYSEVT) ++ dma_wmb(); + -+#define ESPI_INT_EN_OOB \ -+ (ESPI_INT_EN_OOB_RX_TMOUT | \ -+ ESPI_INT_EN_OOB_TX_ERR | \ -+ ESPI_INT_EN_OOB_TX_ABT | \ -+ ESPI_INT_EN_OOB_RX_ABT | \ -+ ESPI_INT_EN_OOB_TX_CMPLT | \ -+ ESPI_INT_EN_OOB_RX_CMPLT) ++ wptr = (wptr + 1) % OOB_DMA_DESC_NUM; ++ writel(wptr | ESPI_OOB_TX_DESC_WPTR_SEND_EN, espi->regs + ESPI_OOB_TX_DESC_WPTR); + -+#define ESPI_INT_EN_FLASH \ -+ (ESPI_INT_EN_FLASH_TX_ERR | \ -+ ESPI_INT_EN_FLASH_TX_ABT | \ -+ ESPI_INT_EN_FLASH_RX_ABT | \ -+ ESPI_INT_EN_FLASH_TX_CMPLT | \ -+ ESPI_INT_EN_FLASH_RX_CMPLT) ++ rc = 0; + -+/* collect ESPI_INT_STS bits for convenience */ -+#define ESPI_INT_STS_PERIF \ -+ (ESPI_INT_STS_PERIF_NP_TX_ABT | \ -+ ESPI_INT_STS_PERIF_PC_TX_ABT | \ -+ ESPI_INT_STS_PERIF_NP_RX_ABT | \ -+ ESPI_INT_STS_PERIF_PC_RX_ABT | \ -+ ESPI_INT_STS_PERIF_NP_TX_ERR | \ -+ ESPI_INT_STS_PERIF_PC_TX_ERR | \ -+ ESPI_INT_STS_PERIF_NP_TX_CMPLT | \ -+ ESPI_INT_STS_PERIF_PC_TX_CMPLT | \ -+ ESPI_INT_STS_PERIF_PC_RX_CMPLT) ++free_n_out: ++ vfree(pkt); + -+#define ESPI_INT_STS_VW \ -+ (ESPI_INT_STS_VW_SYSEVT1 | \ -+ ESPI_INT_STS_VW_GPIO | \ -+ ESPI_INT_STS_VW_SYSEVT) ++ return rc; ++} + -+#define ESPI_INT_STS_OOB \ -+ (ESPI_INT_STS_OOB_RX_TMOUT | \ -+ ESPI_INT_STS_OOB_TX_ERR | \ -+ ESPI_INT_STS_OOB_TX_ABT | \ -+ ESPI_INT_STS_OOB_RX_ABT | \ -+ ESPI_INT_STS_OOB_TX_CMPLT | \ -+ ESPI_INT_STS_OOB_RX_CMPLT) ++static long ast2600_espi_oob_put_tx(struct file *fp, ++ struct aspeed_espi_oob *oob, ++ struct aspeed_espi_ioc *ioc) ++{ ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ u8 *pkt; ++ int i, rc; + -+#define ESPI_INT_STS_FLASH \ -+ (ESPI_INT_STS_FLASH_TX_ERR | \ -+ ESPI_INT_STS_FLASH_TX_ABT | \ -+ ESPI_INT_STS_FLASH_RX_ABT | \ -+ ESPI_INT_STS_FLASH_TX_CMPLT | \ -+ ESPI_INT_STS_FLASH_RX_CMPLT) ++ espi = container_of(oob, struct aspeed_espi, oob); + -+/* consistent with DTS property "flash-safs-mode" */ -+enum ast2600_safs_mode { -+ SAFS_MODE_MIX = 0x0, -+ SAFS_MODE_SW, -+ SAFS_MODE_HW, -+ SAFS_MODES, -+}; ++ if (!mutex_trylock(&oob->tx_mtx)) ++ return -EAGAIN; + -+/* consistent with DTS property "perif-mmbi-instance-size" */ -+enum ast2600_mmbi_instance_size { -+ MMBI_INST_SIZE_8KB = 0x0, -+ MMBI_INST_SIZE_16KB, -+ MMBI_INST_SIZE_32KB, -+ MMBI_INST_SIZE_64KB, -+ MMBI_INST_SIZE_128KB, -+ MMBI_INST_SIZE_256KB, -+ MMBI_INST_SIZE_512KB, -+ MMBI_INST_SIZE_1024KB, -+ MMBI_INST_SIZE_TYPES, -+}; ++ if (oob->dma.enable) { ++ rc = ast2600_espi_oob_dma_put_tx(fp, oob, ioc); ++ goto unlock_mtx_n_out; ++ } + -+#endif -diff --git a/drivers/soc/aspeed/ast2600-otp.c b/drivers/soc/aspeed/ast2600-otp.c ---- a/drivers/soc/aspeed/ast2600-otp.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/ast2600-otp.c 2025-12-23 10:16:21.126032636 +0000 -@@ -0,0 +1,638 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (C) ASPEED Technology Inc. -+ */ ++ reg = readl(espi->regs + ESPI_OOB_TX_CTRL); ++ if (reg & ESPI_OOB_TX_CTRL_TRIG_PEND) { ++ rc = -EBUSY; ++ goto unlock_mtx_n_out; ++ } + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++ if (ioc->pkt_len > ESPI_MAX_PKT_LEN) { ++ rc = -EINVAL; ++ goto unlock_mtx_n_out; ++ } + -+#define ASPEED_REVISION_ID0 0x04 -+#define ASPEED_REVISION_ID1 0x14 -+#define ID0_AST2600A0 0x05000303 -+#define ID1_AST2600A0 0x05000303 -+#define ID0_AST2600A1 0x05010303 -+#define ID1_AST2600A1 0x05010303 -+#define ID0_AST2600A2 0x05010303 -+#define ID1_AST2600A2 0x05020303 -+#define ID0_AST2600A3 0x05030303 -+#define ID1_AST2600A3 0x05030303 -+#define ID0_AST2620A1 0x05010203 -+#define ID1_AST2620A1 0x05010203 -+#define ID0_AST2620A2 0x05010203 -+#define ID1_AST2620A2 0x05020203 -+#define ID0_AST2620A3 0x05030203 -+#define ID1_AST2620A3 0x05030203 -+#define ID0_AST2605A2 0x05010103 -+#define ID1_AST2605A2 0x05020103 -+#define ID0_AST2605A3 0x05030103 -+#define ID1_AST2605A3 0x05030103 -+#define ID0_AST2625A3 0x05030403 -+#define ID1_AST2625A3 0x05030403 ++ pkt = vmalloc(ioc->pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_mtx_n_out; ++ } + -+#define OTP_PROTECT_KEY 0x0 -+#define OTP_PASSWD 0x349fe38a -+#define OTP_COMMAND 0x4 -+#define OTP_TIMING 0x8 -+#define OTP_ADDR 0x10 -+#define OTP_STATUS 0x14 -+#define OTP_COMPARE_1 0x20 -+#define OTP_COMPARE_2 0x24 -+#define OTP_COMPARE_3 0x28 -+#define OTP_COMPARE_4 0x2c -+#define SW_REV_ID0 0x68 -+#define SW_REV_ID1 0x6c -+#define SEC_KEY_NUM 0x78 -+#define RETRY 20 ++ hdr = (struct espi_comm_hdr *)pkt; + -+struct aspeed_otp { -+ struct miscdevice miscdev; -+ void __iomem *reg_base; -+ bool is_open; -+ u32 otp_ver; -+ u32 *data; ++ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } ++ ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) ++ writel(pkt[i], espi->regs + ESPI_OOB_TX_DATA); ++ ++ cyc = hdr->cyc; ++ tag = hdr->tag; ++ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); ++ ++ reg = FIELD_PREP(ESPI_OOB_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_OOB_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_OOB_TX_CTRL_LEN, len) ++ | ESPI_OOB_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_OOB_TX_CTRL); ++ ++ rc = 0; ++ ++free_n_out: ++ vfree(pkt); ++ ++unlock_mtx_n_out: ++ mutex_unlock(&oob->tx_mtx); ++ ++ return rc; ++} ++ ++static long ast2600_espi_oob_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++{ ++ struct aspeed_espi_oob *oob; ++ struct aspeed_espi_ioc ioc; ++ ++ oob = container_of(fp->private_data, struct aspeed_espi_oob, mdev); ++ ++ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) ++ return -EFAULT; ++ ++ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) ++ return -EINVAL; ++ ++ switch (cmd) { ++ case ASPEED_ESPI_OOB_GET_RX: ++ return ast2600_espi_oob_get_rx(fp, oob, &ioc); ++ case ASPEED_ESPI_OOB_PUT_TX: ++ return ast2600_espi_oob_put_tx(fp, oob, &ioc); ++ }; ++ ++ return -EINVAL; ++} ++ ++static const struct file_operations ast2600_espi_oob_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = ast2600_espi_oob_ioctl, +}; + -+static DEFINE_SPINLOCK(otp_state_lock); ++static void ast2600_espi_oob_isr(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_oob *oob; ++ unsigned long flags; ++ u32 sts; + -+static inline u32 aspeed_otp_read(struct aspeed_otp *ctx, u32 reg) ++ oob = &espi->oob; ++ ++ sts = readl(espi->regs + ESPI_INT_STS); ++ ++ if (sts & ESPI_INT_STS_OOB_RX_CMPLT) { ++ writel(ESPI_INT_STS_OOB_RX_CMPLT, espi->regs + ESPI_INT_STS); ++ ++ spin_lock_irqsave(&oob->lock, flags); ++ oob->rx_ready = true; ++ spin_unlock_irqrestore(&oob->lock, flags); ++ ++ wake_up_interruptible(&oob->wq); ++ } ++} ++ ++static void ast2600_espi_oob_reset(struct aspeed_espi *espi) +{ -+ int val; ++ struct aspeed_espi_oob *oob; ++ struct ast2600_espi_oob_dma_tx_desc *tx_desc; ++ struct ast2600_espi_oob_dma_rx_desc *rx_desc; ++ dma_addr_t tx_addr, rx_addr; ++ u32 reg; ++ int i; + -+ val = readl(ctx->reg_base + reg); -+ // printk("read:reg = 0x%08x, val = 0x%08x\n", reg, val); -+ return val; ++ writel(ESPI_INT_EN_OOB, espi->regs + ESPI_INT_EN_CLR); ++ writel(ESPI_INT_STS_OOB, espi->regs + ESPI_INT_STS); ++ ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_OOB_TX_SW_RST ++ | ESPI_CTRL_OOB_RX_SW_RST ++ | ESPI_CTRL_OOB_TX_DMA_EN ++ | ESPI_CTRL_OOB_RX_DMA_EN ++ | ESPI_CTRL_OOB_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); ++ ++ udelay(1); ++ ++ reg |= (ESPI_CTRL_OOB_TX_SW_RST | ESPI_CTRL_OOB_RX_SW_RST); ++ writel(reg, espi->regs + ESPI_CTRL); ++ ++ oob = &espi->oob; ++ ++ if (oob->dma.enable) { ++ tx_addr = oob->dma.tx_addr; ++ rx_addr = oob->dma.rx_addr; ++ ++ tx_desc = (struct ast2600_espi_oob_dma_tx_desc *)oob->dma.txd_virt; ++ rx_desc = (struct ast2600_espi_oob_dma_rx_desc *)oob->dma.rxd_virt; ++ ++ for (i = 0; i < OOB_DMA_DESC_NUM; ++i) { ++ tx_desc[i].data_addr = tx_addr; ++ tx_addr += PAGE_SIZE; ++ ++ rx_desc[i].data_addr = rx_addr; ++ rx_desc[i].dirty = 0; ++ rx_addr += PAGE_SIZE; ++ } ++ ++ writel(oob->dma.txd_addr, espi->regs + ESPI_OOB_TX_DMA); ++ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_OOB_TX_DESC_RPTR); ++ writel(0x0, espi->regs + ESPI_OOB_TX_DESC_WPTR); ++ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_OOB_TX_DESC_NUM); ++ ++ writel(oob->dma.rxd_addr, espi->regs + ESPI_OOB_RX_DMA); ++ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_OOB_RX_DESC_RPTR); ++ writel(0x0, espi->regs + ESPI_OOB_RX_DESC_WPTR); ++ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_OOB_RX_DESC_NUM); ++ ++ reg = readl(espi->regs + ESPI_CTRL) ++ | ESPI_CTRL_OOB_TX_DMA_EN ++ | ESPI_CTRL_OOB_RX_DMA_EN; ++ writel(reg, espi->regs + ESPI_CTRL); ++ ++ /* activate RX DMA to make OOB_FREE */ ++ writel(ESPI_OOB_RX_DESC_WPTR_RECV_EN, espi->regs + ESPI_OOB_RX_DESC_WPTR); ++ } ++ ++ writel(ESPI_INT_EN_OOB_RX_CMPLT, espi->regs + ESPI_INT_EN); ++ ++ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_OOB_SW_RDY; ++ writel(reg, espi->regs + ESPI_CTRL); +} + -+static inline void aspeed_otp_write(struct aspeed_otp *ctx, u32 val, u32 reg) ++int ast2600_espi_oob_probe(struct aspeed_espi *espi) +{ -+ // printk("write:reg = 0x%08x, val = 0x%08x\n", reg, val); -+ writel(val, ctx->reg_base + reg); ++ struct aspeed_espi_oob *oob; ++ struct device *dev; ++ int rc; ++ ++ dev = espi->dev; ++ ++ oob = &espi->oob; ++ ++ init_waitqueue_head(&oob->wq); ++ ++ spin_lock_init(&oob->lock); ++ ++ mutex_init(&oob->tx_mtx); ++ mutex_init(&oob->rx_mtx); ++ ++ oob->dma.enable = of_property_read_bool(dev->of_node, "oob-dma-mode"); ++ if (oob->dma.enable) { ++ oob->dma.txd_virt = dmam_alloc_coherent(dev, ++ sizeof(struct ast2600_espi_oob_dma_tx_desc) * ++ OOB_DMA_DESC_NUM, ++ &oob->dma.txd_addr, GFP_KERNEL); ++ if (!oob->dma.txd_virt) { ++ dev_err(dev, "cannot allocate DMA TX descriptor\n"); ++ return -ENOMEM; ++ } ++ oob->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, &oob->dma.tx_addr, GFP_KERNEL); ++ if (!oob->dma.tx_virt) { ++ dev_err(dev, "cannot allocate DMA TX buffer\n"); ++ return -ENOMEM; ++ } ++ ++ oob->dma.rxd_virt = dmam_alloc_coherent(dev, ++ sizeof(struct ast2600_espi_oob_dma_rx_desc) * ++ OOB_DMA_DESC_NUM, ++ &oob->dma.rxd_addr, GFP_KERNEL); ++ if (!oob->dma.rxd_virt) { ++ dev_err(dev, "cannot allocate DMA RX descriptor\n"); ++ return -ENOMEM; ++ } ++ ++ oob->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, &oob->dma.rx_addr, GFP_KERNEL); ++ if (!oob->dma.rx_virt) { ++ dev_err(dev, "cannot allocate DMA TX buffer\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ oob->mdev.parent = dev; ++ oob->mdev.minor = MISC_DYNAMIC_MINOR; ++ oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-oob", DEVICE_NAME); ++ oob->mdev.fops = &ast2600_espi_oob_fops; ++ rc = misc_register(&oob->mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", oob->mdev.name); ++ return rc; ++ } ++ ++ ast2600_espi_oob_reset(espi); ++ ++ return 0; +} + -+static uint32_t chip_version(u32 revid0, u32 revid1) ++int ast2600_espi_oob_remove(struct aspeed_espi *espi) +{ -+ if (revid0 == ID0_AST2600A0 && revid1 == ID1_AST2600A0) { -+ /* AST2600-A0 */ -+ return OTP_A0; -+ } else if (revid0 == ID0_AST2600A1 && revid1 == ID1_AST2600A1) { -+ /* AST2600-A1 */ -+ return OTP_A1; -+ } else if (revid0 == ID0_AST2600A2 && revid1 == ID1_AST2600A2) { -+ /* AST2600-A2 */ -+ return OTP_A2; -+ } else if (revid0 == ID0_AST2600A3 && revid1 == ID1_AST2600A3) { -+ /* AST2600-A3 */ -+ return OTP_A3; -+ } else if (revid0 == ID0_AST2620A1 && revid1 == ID1_AST2620A1) { -+ /* AST2620-A1 */ -+ return OTP_A1; -+ } else if (revid0 == ID0_AST2620A2 && revid1 == ID1_AST2620A2) { -+ /* AST2620-A2 */ -+ return OTP_A2; -+ } else if (revid0 == ID0_AST2620A3 && revid1 == ID1_AST2620A3) { -+ /* AST2620-A3 */ -+ return OTP_A3; -+ } else if (revid0 == ID0_AST2605A2 && revid1 == ID1_AST2605A2) { -+ /* AST2605-A2 */ -+ return OTP_A2; -+ } else if (revid0 == ID0_AST2605A3 && revid1 == ID1_AST2605A3) { -+ /* AST2605-A3 */ -+ return OTP_A3; -+ } else if (revid0 == ID0_AST2625A3 && revid1 == ID1_AST2625A3) { -+ /* AST2605-A3 */ -+ return OTP_A3; ++ struct aspeed_espi_oob *oob; ++ struct device *dev; ++ u32 reg; ++ ++ dev = espi->dev; ++ ++ oob = &espi->oob; ++ ++ writel(ESPI_INT_EN_OOB, espi->regs + ESPI_INT_EN_CLR); ++ ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_OOB_TX_DMA_EN ++ | ESPI_CTRL_OOB_RX_DMA_EN ++ | ESPI_CTRL_OOB_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); ++ ++ if (oob->dma.enable) { ++ dmam_free_coherent(dev, ++ sizeof(struct ast2600_espi_oob_dma_tx_desc) * ++ OOB_DMA_DESC_NUM, ++ oob->dma.txd_virt, oob->dma.txd_addr); ++ dmam_free_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, ++ oob->dma.tx_virt, oob->dma.tx_addr); ++ ++ dmam_free_coherent(dev, ++ sizeof(struct ast2600_espi_oob_dma_rx_desc) * ++ OOB_DMA_DESC_NUM, ++ oob->dma.rxd_virt, oob->dma.rxd_addr); ++ dmam_free_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, ++ oob->dma.rx_virt, oob->dma.rx_addr); ++ } ++ ++ mutex_destroy(&oob->tx_mtx); ++ mutex_destroy(&oob->rx_mtx); ++ ++ misc_deregister(&oob->mdev); ++ ++ return 0; ++} ++ ++/* flash channel (CH3) */ ++static long ast2600_espi_flash_get_rx(struct file *fp, ++ struct aspeed_espi_flash *flash, ++ struct aspeed_espi_ioc *ioc) ++{ ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ unsigned long flags; ++ u32 pkt_len; ++ u8 *pkt; ++ int i, rc; ++ ++ rc = 0; ++ ++ espi = container_of(flash, struct aspeed_espi, flash); ++ ++ if (fp->f_flags & O_NONBLOCK) { ++ if (!mutex_trylock(&flash->rx_mtx)) ++ return -EAGAIN; ++ ++ if (!flash->rx_ready) { ++ rc = -ENODATA; ++ goto unlock_mtx_n_out; ++ } ++ } else { ++ mutex_lock(&flash->rx_mtx); ++ ++ if (!flash->rx_ready) { ++ rc = wait_event_interruptible(flash->wq, flash->rx_ready); ++ if (rc == -ERESTARTSYS) { ++ rc = -EINTR; ++ goto unlock_mtx_n_out; ++ } ++ } ++ } ++ ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ reg = readl(espi->regs + ESPI_FLASH_RX_CTRL); ++ cyc = FIELD_GET(ESPI_FLASH_RX_CTRL_CYC, reg); ++ tag = FIELD_GET(ESPI_FLASH_RX_CTRL_TAG, reg); ++ len = FIELD_GET(ESPI_FLASH_RX_CTRL_LEN, reg); ++ ++ /* ++ * calculate the length of the rest part of the ++ * eSPI packet to be read from HW and copied to ++ * user space. ++ */ ++ switch (cyc) { ++ case ESPI_FLASH_WRITE: ++ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + ++ sizeof(struct espi_flash_rwe); ++ break; ++ case ESPI_FLASH_READ: ++ case ESPI_FLASH_ERASE: ++ pkt_len = sizeof(struct espi_flash_rwe); ++ break; ++ case ESPI_FLASH_SUC_CMPLT_D_MIDDLE: ++ case ESPI_FLASH_SUC_CMPLT_D_FIRST: ++ case ESPI_FLASH_SUC_CMPLT_D_LAST: ++ case ESPI_FLASH_SUC_CMPLT_D_ONLY: ++ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + ++ sizeof(struct espi_flash_cmplt); ++ break; ++ case ESPI_FLASH_SUC_CMPLT: ++ case ESPI_FLASH_UNSUC_CMPLT: ++ pkt_len = sizeof(struct espi_flash_cmplt); ++ break; ++ default: ++ rc = -EFAULT; ++ goto unlock_mtx_n_out; ++ } ++ ++ if (ioc->pkt_len < pkt_len) { ++ rc = -EINVAL; ++ goto unlock_mtx_n_out; ++ } ++ ++ pkt = vmalloc(pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_mtx_n_out; ++ } ++ ++ hdr = (struct espi_comm_hdr *)pkt; ++ hdr->cyc = cyc; ++ hdr->tag = tag; ++ hdr->len_h = len >> 8; ++ hdr->len_l = len & 0xff; ++ ++ if (flash->dma.enable) { ++ memcpy(hdr + 1, flash->dma.rx_virt, pkt_len - sizeof(*hdr)); ++ } else { ++ for (i = sizeof(*hdr); i < pkt_len; ++i) ++ pkt[i] = readl(espi->regs + ESPI_FLASH_RX_DATA) & 0xff; + } -+ return -1; -+} + -+static void wait_complete(struct aspeed_otp *ctx) -+{ -+ int reg; -+ int i = 0; ++ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } + -+ do { -+ reg = aspeed_otp_read(ctx, OTP_STATUS); -+ if ((reg & 0x6) == 0x6) -+ i++; -+ } while (i != 2); -+} ++ spin_lock_irqsave(&flash->lock, flags); + -+static void otp_write(struct aspeed_otp *ctx, u32 otp_addr, u32 val) -+{ -+ aspeed_otp_write(ctx, otp_addr, OTP_ADDR); //write address -+ aspeed_otp_write(ctx, val, OTP_COMPARE_1); //write val -+ aspeed_otp_write(ctx, 0x23b1e362, OTP_COMMAND); //write command -+ wait_complete(ctx); -+} ++ writel(ESPI_FLASH_RX_CTRL_SERV_PEND, espi->regs + ESPI_FLASH_RX_CTRL); ++ flash->rx_ready = 0; + -+static void otp_soak(struct aspeed_otp *ctx, int soak) -+{ -+ if (ctx->otp_ver == OTP_A2 || ctx->otp_ver == OTP_A3) { -+ switch (soak) { -+ case 0: //default -+ otp_write(ctx, 0x3000, 0x0); // Write MRA -+ otp_write(ctx, 0x5000, 0x0); // Write MRB -+ otp_write(ctx, 0x1000, 0x0); // Write MR -+ break; -+ case 1: //normal program -+ otp_write(ctx, 0x3000, 0x1320); // Write MRA -+ otp_write(ctx, 0x5000, 0x1008); // Write MRB -+ otp_write(ctx, 0x1000, 0x0024); // Write MR -+ aspeed_otp_write(ctx, 0x04191388, OTP_TIMING); // 200us -+ break; -+ case 2: //soak program -+ otp_write(ctx, 0x3000, 0x1320); // Write MRA -+ otp_write(ctx, 0x5000, 0x0007); // Write MRB -+ otp_write(ctx, 0x1000, 0x0100); // Write MR -+ aspeed_otp_write(ctx, 0x04193a98, OTP_TIMING); // 600us -+ break; -+ } -+ } else { -+ switch (soak) { -+ case 0: //default -+ otp_write(ctx, 0x3000, 0x0); // Write MRA -+ otp_write(ctx, 0x5000, 0x0); // Write MRB -+ otp_write(ctx, 0x1000, 0x0); // Write MR -+ break; -+ case 1: //normal program -+ otp_write(ctx, 0x3000, 0x4021); // Write MRA -+ otp_write(ctx, 0x5000, 0x302f); // Write MRB -+ otp_write(ctx, 0x1000, 0x4020); // Write MR -+ aspeed_otp_write(ctx, 0x04190760, OTP_TIMING); // 75us -+ break; -+ case 2: //soak program -+ otp_write(ctx, 0x3000, 0x4021); // Write MRA -+ otp_write(ctx, 0x5000, 0x1027); // Write MRB -+ otp_write(ctx, 0x1000, 0x4820); // Write MR -+ aspeed_otp_write(ctx, 0x041930d4, OTP_TIMING); // 500us -+ break; -+ } -+ } ++ spin_unlock_irqrestore(&flash->lock, flags); + -+ wait_complete(ctx); ++ rc = 0; ++ ++free_n_out: ++ vfree(pkt); ++ ++unlock_mtx_n_out: ++ mutex_unlock(&flash->rx_mtx); ++ ++ return rc; +} + -+static int verify_bit(struct aspeed_otp *ctx, u32 otp_addr, int bit_offset, int value) ++static long ast2600_espi_flash_put_tx(struct file *fp, ++ struct aspeed_espi_flash *flash, ++ struct aspeed_espi_ioc *ioc) +{ -+ u32 ret[2]; ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ u8 *pkt; ++ int i, rc; + -+ if (otp_addr % 2 == 0) -+ aspeed_otp_write(ctx, otp_addr, OTP_ADDR); //Read address -+ else -+ aspeed_otp_write(ctx, otp_addr - 1, OTP_ADDR); //Read address ++ espi = container_of(flash, struct aspeed_espi, flash); + -+ aspeed_otp_write(ctx, 0x23b1e361, OTP_COMMAND); //trigger read -+ wait_complete(ctx); -+ ret[0] = aspeed_otp_read(ctx, OTP_COMPARE_1); -+ ret[1] = aspeed_otp_read(ctx, OTP_COMPARE_2); ++ if (!mutex_trylock(&flash->tx_mtx)) ++ return -EAGAIN; + -+ if (otp_addr % 2 == 0) { -+ if (((ret[0] >> bit_offset) & 1) == value) -+ return 0; -+ else -+ return -1; -+ } else { -+ if (((ret[1] >> bit_offset) & 1) == value) -+ return 0; -+ else -+ return -1; ++ reg = readl(espi->regs + ESPI_FLASH_TX_CTRL); ++ if (reg & ESPI_FLASH_TX_CTRL_TRIG_PEND) { ++ rc = -EBUSY; ++ goto unlock_mtx_n_out; + } -+} + -+static void otp_prog(struct aspeed_otp *ctx, u32 otp_addr, u32 prog_bit) -+{ -+ otp_write(ctx, 0x0, prog_bit); -+ aspeed_otp_write(ctx, otp_addr, OTP_ADDR); //write address -+ aspeed_otp_write(ctx, prog_bit, OTP_COMPARE_1); //write data -+ aspeed_otp_write(ctx, 0x23b1e364, OTP_COMMAND); //write command -+ wait_complete(ctx); -+} ++ pkt = vmalloc(ioc->pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_mtx_n_out; ++ } + -+static void _otp_prog_bit(struct aspeed_otp *ctx, u32 value, u32 prog_address, u32 bit_offset) -+{ -+ int prog_bit; ++ hdr = (struct espi_comm_hdr *)pkt; + -+ if (prog_address % 2 == 0) { -+ if (value) -+ prog_bit = ~(0x1 << bit_offset); -+ else -+ return; ++ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } ++ ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ if (flash->dma.enable) { ++ memcpy(flash->dma.tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); ++ dma_wmb(); + } else { -+ if (ctx->otp_ver != OTP_A3) -+ prog_address |= 1 << 15; -+ if (!value) -+ prog_bit = 0x1 << bit_offset; -+ else -+ return; ++ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) ++ writel(pkt[i], espi->regs + ESPI_FLASH_TX_DATA); + } -+ otp_prog(ctx, prog_address, prog_bit); -+} + -+static int otp_prog_bit(struct aspeed_otp *ctx, u32 value, u32 prog_address, u32 bit_offset) -+{ -+ int pass; -+ int i; ++ cyc = hdr->cyc; ++ tag = hdr->tag; ++ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + -+ otp_soak(ctx, 1); -+ _otp_prog_bit(ctx, value, prog_address, bit_offset); -+ pass = 0; ++ reg = FIELD_PREP(ESPI_FLASH_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_FLASH_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_FLASH_TX_CTRL_LEN, len) ++ | ESPI_FLASH_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_FLASH_TX_CTRL); + -+ for (i = 0; i < RETRY; i++) { -+ if (verify_bit(ctx, prog_address, bit_offset, value) != 0) { -+ otp_soak(ctx, 2); -+ _otp_prog_bit(ctx, value, prog_address, bit_offset); -+ if (verify_bit(ctx, prog_address, bit_offset, value) != 0) { -+ otp_soak(ctx, 1); -+ } else { -+ pass = 1; -+ break; -+ } -+ } else { -+ pass = 1; -+ break; -+ } -+ } -+ otp_soak(ctx, 0); -+ return pass; -+} ++ rc = 0; + -+static void otp_read_conf_dw(struct aspeed_otp *ctx, u32 offset, u32 *buf) -+{ -+ u32 config_offset; ++free_n_out: ++ vfree(pkt); + -+ config_offset = 0x800; -+ config_offset |= (offset / 8) * 0x200; -+ config_offset |= (offset % 8) * 0x2; ++unlock_mtx_n_out: ++ mutex_unlock(&flash->tx_mtx); + -+ aspeed_otp_write(ctx, config_offset, OTP_ADDR); //Read address -+ aspeed_otp_write(ctx, 0x23b1e361, OTP_COMMAND); //trigger read -+ wait_complete(ctx); -+ buf[0] = aspeed_otp_read(ctx, OTP_COMPARE_1); ++ return rc; +} + -+static void otp_read_conf(struct aspeed_otp *ctx, u32 offset, u32 len) ++static long ast2600_espi_flash_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ -+ int i, j; ++ struct aspeed_espi_flash *flash; ++ struct aspeed_espi_ioc ioc; + -+ otp_soak(ctx, 0); -+ for (i = offset, j = 0; j < len; i++, j++) -+ otp_read_conf_dw(ctx, i, &ctx->data[j]); -+} ++ flash = container_of(fp->private_data, struct aspeed_espi_flash, mdev); + -+static void otp_read_data_2dw(struct aspeed_otp *ctx, u32 offset, u32 *buf) -+{ -+ aspeed_otp_write(ctx, offset, OTP_ADDR); //Read address -+ aspeed_otp_write(ctx, 0x23b1e361, OTP_COMMAND); //trigger read -+ wait_complete(ctx); -+ buf[0] = aspeed_otp_read(ctx, OTP_COMPARE_1); -+ buf[1] = aspeed_otp_read(ctx, OTP_COMPARE_2); -+} ++ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) ++ return -EFAULT; + -+static void otp_read_data(struct aspeed_otp *ctx, u32 offset, u32 len) -+{ -+ int i, j; -+ u32 ret[2]; ++ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) ++ return -EINVAL; + -+ otp_soak(ctx, 0); ++ switch (cmd) { ++ case ASPEED_ESPI_FLASH_GET_RX: ++ return ast2600_espi_flash_get_rx(fp, flash, &ioc); ++ case ASPEED_ESPI_FLASH_PUT_TX: ++ return ast2600_espi_flash_put_tx(fp, flash, &ioc); ++ default: ++ break; ++ }; + -+ i = offset; -+ j = 0; -+ if (offset % 2) { -+ otp_read_data_2dw(ctx, i - 1, ret); -+ ctx->data[0] = ret[1]; -+ i++; -+ j++; -+ } -+ for (; j < len; i += 2, j += 2) -+ otp_read_data_2dw(ctx, i, &ctx->data[j]); ++ return -EINVAL; +} + -+static int otp_prog_data(struct aspeed_otp *ctx, u32 value, u32 dw_offset, u32 bit_offset) ++static const struct file_operations ast2600_espi_flash_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = ast2600_espi_flash_ioctl, ++}; ++ ++static void ast2600_espi_flash_isr(struct aspeed_espi *espi) +{ -+ u32 read[2]; -+ int otp_bit; ++ struct aspeed_espi_flash *flash; ++ unsigned long flags; ++ u32 sts; + -+ if (dw_offset % 2 == 0) { -+ otp_read_data_2dw(ctx, dw_offset, read); -+ otp_bit = (read[0] >> bit_offset) & 0x1; ++ flash = &espi->flash; + -+ if (otp_bit == 1 && value == 0) { -+ pr_err("OTPDATA%X[%X] = 1\n", dw_offset, bit_offset); -+ pr_err("OTP is programed, which can't be cleaned\n"); -+ return -EINVAL; -+ } -+ } else { -+ otp_read_data_2dw(ctx, dw_offset - 1, read); -+ otp_bit = (read[1] >> bit_offset) & 0x1; ++ sts = readl(espi->regs + ESPI_INT_STS); + -+ if (otp_bit == 0 && value == 1) { -+ pr_err("OTPDATA%X[%X] = 1\n", dw_offset, bit_offset); -+ pr_err("OTP is programed, which can't be writen\n"); -+ return -EINVAL; -+ } -+ } -+ if (otp_bit == value) { -+ pr_err("OTPDATA%X[%X] = %d\n", dw_offset, bit_offset, value); -+ pr_err("No need to program\n"); -+ return 0; -+ } ++ if (sts & ESPI_INT_STS_FLASH_RX_CMPLT) { ++ writel(ESPI_INT_STS_FLASH_RX_CMPLT, espi->regs + ESPI_INT_STS); + -+ return otp_prog_bit(ctx, value, dw_offset, bit_offset); ++ spin_lock_irqsave(&flash->lock, flags); ++ flash->rx_ready = true; ++ spin_unlock_irqrestore(&flash->lock, flags); ++ ++ wake_up_interruptible(&flash->wq); ++ } +} + -+static int otp_prog_conf(struct aspeed_otp *ctx, u32 value, u32 dw_offset, u32 bit_offset) ++static void ast2600_espi_flash_reset(struct aspeed_espi *espi) +{ -+ u32 read; -+ u32 prog_address = 0; -+ int otp_bit; ++ struct aspeed_espi_flash *flash; ++ u32 reg; + -+ otp_read_conf_dw(ctx, dw_offset, &read); ++ flash = &espi->flash; + -+ prog_address = 0x800; -+ prog_address |= (dw_offset / 8) * 0x200; -+ prog_address |= (dw_offset % 8) * 0x2; -+ otp_bit = (read >> bit_offset) & 0x1; -+ if (otp_bit == value) { -+ pr_err("OTPCFG%X[%X] = %d\n", dw_offset, bit_offset, value); -+ pr_err("No need to program\n"); -+ return 0; -+ } -+ if (otp_bit == 1 && value == 0) { -+ pr_err("OTPCFG%X[%X] = 1\n", dw_offset, bit_offset); -+ pr_err("OTP is programed, which can't be clean\n"); -+ return -EINVAL; -+ } ++ writel(ESPI_INT_EN_FLASH, espi->regs + ESPI_INT_EN_CLR); ++ writel(ESPI_INT_STS_FLASH, espi->regs + ESPI_INT_STS); + -+ return otp_prog_bit(ctx, value, prog_address, bit_offset); -+} ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_FLASH_TX_SW_RST ++ | ESPI_CTRL_FLASH_RX_SW_RST ++ | ESPI_CTRL_FLASH_TX_DMA_EN ++ | ESPI_CTRL_FLASH_RX_DMA_EN ++ | ESPI_CTRL_FLASH_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); + -+struct aspeed_otp *glob_ctx; ++ udelay(1); + -+void otp_read_data_buf(u32 offset, u32 *buf, u32 len) -+{ -+ int i, j; -+ u32 ret[2]; ++ reg |= (ESPI_CTRL_FLASH_TX_SW_RST | ESPI_CTRL_FLASH_RX_SW_RST); ++ writel(reg, espi->regs + ESPI_CTRL); + -+ aspeed_otp_write(glob_ctx, OTP_PASSWD, OTP_PROTECT_KEY); ++ reg = readl(espi->regs + ESPI_CTRL) & ~ESPI_CTRL_FLASH_EDAF_MODE; ++ reg |= FIELD_PREP(ESPI_CTRL_FLASH_EDAF_MODE, flash->edaf.mode); ++ writel(reg, espi->regs + ESPI_CTRL); + -+ otp_soak(glob_ctx, 0); ++ if (flash->edaf.mode == EDAF_MODE_MIX) { ++ reg = FIELD_PREP(ESPI_FLASH_EDAF_TADDR_BASE, flash->edaf.taddr >> 24) ++ | FIELD_PREP(ESPI_FLASH_EDAF_TADDR_MASK, (~(flash->edaf.size - 1)) >> 24); ++ writel(reg, espi->regs + ESPI_FLASH_EDAF_TADDR); ++ } + -+ i = offset; -+ j = 0; -+ if (offset % 2) { -+ otp_read_data_2dw(glob_ctx, i - 1, ret); -+ buf[0] = ret[1]; -+ i++; -+ j++; ++ if (flash->dma.enable) { ++ writel(flash->dma.tx_addr, espi->regs + ESPI_FLASH_TX_DMA); ++ writel(flash->dma.rx_addr, espi->regs + ESPI_FLASH_RX_DMA); ++ ++ reg = readl(espi->regs + ESPI_CTRL) ++ | ESPI_CTRL_FLASH_TX_DMA_EN ++ | ESPI_CTRL_FLASH_RX_DMA_EN; ++ writel(reg, espi->regs + ESPI_CTRL); + } -+ for (; j < len; i += 2, j += 2) -+ otp_read_data_2dw(glob_ctx, i, &buf[j]); -+ aspeed_otp_write(glob_ctx, 0, OTP_PROTECT_KEY); -+} -+EXPORT_SYMBOL(otp_read_data_buf); + -+void otp_read_conf_buf(u32 offset, u32 *buf, u32 len) -+{ -+ int i, j; ++ writel(ESPI_INT_EN_FLASH_RX_CMPLT, espi->regs + ESPI_INT_EN); + -+ aspeed_otp_write(glob_ctx, OTP_PASSWD, OTP_PROTECT_KEY); -+ otp_soak(glob_ctx, 0); -+ for (i = offset, j = 0; j < len; i++, j++) -+ otp_read_conf_dw(glob_ctx, i, &buf[j]); -+ aspeed_otp_write(glob_ctx, 0, OTP_PROTECT_KEY); ++ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_FLASH_SW_RDY; ++ writel(reg, espi->regs + ESPI_CTRL); +} -+EXPORT_SYMBOL(otp_read_conf_buf); + -+static long otp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++int ast2600_espi_flash_probe(struct aspeed_espi *espi) +{ -+ struct miscdevice *c = file->private_data; -+ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); -+ void __user *argp = (void __user *)arg; -+ struct otp_read xfer; -+ struct otp_prog prog; -+ u32 reg_read[2]; -+ int ret = 0; ++ struct aspeed_espi_flash *flash; ++ struct device *dev; ++ int rc; + -+ switch (cmd) { -+ case ASPEED_OTP_READ_DATA: -+ if (copy_from_user(&xfer, argp, sizeof(struct otp_read))) -+ return -EFAULT; -+ if ((xfer.offset + xfer.len) > 0x800) { -+ pr_err("out of range"); -+ return -EINVAL; -+ } ++ dev = espi->dev; + -+ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); -+ otp_read_data(ctx, xfer.offset, xfer.len); -+ aspeed_otp_write(ctx, 0, OTP_PROTECT_KEY); ++ flash = &espi->flash; + -+ if (copy_to_user(xfer.data, ctx->data, xfer.len * 4)) -+ return -EFAULT; -+ if (copy_to_user(argp, &xfer, sizeof(struct otp_read))) -+ return -EFAULT; -+ break; -+ case ASPEED_OTP_READ_CONF: -+ if (copy_from_user(&xfer, argp, sizeof(struct otp_read))) -+ return -EFAULT; -+ if ((xfer.offset + xfer.len) > 0x800) { -+ pr_err("out of range"); -+ return -EINVAL; -+ } ++ init_waitqueue_head(&flash->wq); + -+ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); -+ otp_read_conf(ctx, xfer.offset, xfer.len); -+ aspeed_otp_write(ctx, 0, OTP_PROTECT_KEY); ++ spin_lock_init(&flash->lock); + -+ if (copy_to_user(xfer.data, ctx->data, xfer.len * 4)) -+ return -EFAULT; -+ if (copy_to_user(argp, &xfer, sizeof(struct otp_read))) -+ return -EFAULT; -+ break; -+ case ASPEED_OTP_PROG_DATA: -+ if (copy_from_user(&prog, argp, sizeof(struct otp_prog))) -+ return -EFAULT; -+ if (prog.bit_offset >= 32 || (prog.value != 0 && prog.value != 1)) { -+ pr_err("out of range"); -+ return -EINVAL; ++ mutex_init(&flash->tx_mtx); ++ mutex_init(&flash->rx_mtx); ++ ++ flash->edaf.mode = EDAF_MODE_HW; ++ ++ of_property_read_u32(dev->of_node, "flash-safs-mode", &flash->edaf.mode); ++ if (flash->edaf.mode == EDAF_MODE_MIX) { ++ rc = of_property_read_u32(dev->of_node, "flash-safs-tgt-addr", (u32 *)&flash->edaf.taddr); ++ if (rc || !IS_ALIGNED(flash->edaf.taddr, FLASH_EDAF_ALIGN)) { ++ dev_err(dev, "cannot get 16MB-aligned SAFS target address\n"); ++ return -ENODEV; + } -+ if (prog.dw_offset >= 0x800) { -+ pr_err("out of range"); -+ return -EINVAL; ++ ++ rc = of_property_read_u32(dev->of_node, "flash-safs-size", (u32 *)&flash->edaf.size); ++ if (rc || !IS_ALIGNED(flash->edaf.size, FLASH_EDAF_ALIGN)) { ++ dev_err(dev, "cannot get 16MB-aligned SAFS size\n"); ++ return -ENODEV; + } -+ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); -+ ret = otp_prog_data(ctx, prog.value, prog.dw_offset, prog.bit_offset); -+ break; -+ case ASPEED_OTP_PROG_CONF: -+ if (copy_from_user(&prog, argp, sizeof(struct otp_prog))) -+ return -EFAULT; -+ if (prog.bit_offset >= 32 || (prog.value != 0 && prog.value != 1)) { -+ pr_err("out of range"); -+ return -EINVAL; ++ } ++ ++ flash->dma.enable = of_property_read_bool(dev->of_node, "flash-dma-mode"); ++ if (flash->dma.enable) { ++ flash->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.tx_addr, GFP_KERNEL); ++ if (!flash->dma.tx_virt) { ++ dev_err(dev, "cannot allocate DMA TX buffer\n"); ++ return -ENOMEM; + } -+ if (prog.dw_offset >= 0x20) { -+ pr_err("out of range"); -+ return -EINVAL; ++ ++ flash->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.rx_addr, GFP_KERNEL); ++ if (!flash->dma.rx_virt) { ++ dev_err(dev, "cannot allocate DMA RX buffer\n"); ++ return -ENOMEM; + } -+ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); -+ ret = otp_prog_conf(ctx, prog.value, prog.dw_offset, prog.bit_offset); -+ break; -+ case ASPEED_OTP_VER: -+ if (copy_to_user(argp, &ctx->otp_ver, sizeof(u32))) -+ return -EFAULT; -+ break; -+ case ASPEED_OTP_SW_RID: -+ reg_read[0] = aspeed_otp_read(ctx, SW_REV_ID0); -+ reg_read[1] = aspeed_otp_read(ctx, SW_REV_ID1); -+ if (copy_to_user(argp, reg_read, sizeof(u32) * 2)) -+ return -EFAULT; -+ break; -+ case ASPEED_SEC_KEY_NUM: -+ reg_read[0] = aspeed_otp_read(ctx, SEC_KEY_NUM) & 7; -+ if (copy_to_user(argp, reg_read, sizeof(u32))) -+ return -EFAULT; -+ break; + } -+ return ret; ++ ++ flash->mdev.parent = dev; ++ flash->mdev.minor = MISC_DYNAMIC_MINOR; ++ flash->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-flash", DEVICE_NAME); ++ flash->mdev.fops = &ast2600_espi_flash_fops; ++ rc = misc_register(&flash->mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", flash->mdev.name); ++ return rc; ++ } ++ ++ ast2600_espi_flash_reset(espi); ++ ++ return 0; +} + -+static int otp_open(struct inode *inode, struct file *file) ++int ast2600_espi_flash_remove(struct aspeed_espi *espi) +{ -+ struct miscdevice *c = file->private_data; -+ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); ++ struct aspeed_espi_flash *flash; ++ struct device *dev; ++ u32 reg; + -+ spin_lock(&otp_state_lock); ++ dev = espi->dev; ++ ++ flash = &espi->flash; ++ ++ writel(ESPI_INT_EN_FLASH, espi->regs + ESPI_INT_EN_CLR); ++ ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_FLASH_TX_DMA_EN ++ | ESPI_CTRL_FLASH_RX_DMA_EN ++ | ESPI_CTRL_FLASH_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); + -+ if (ctx->is_open) { -+ spin_unlock(&otp_state_lock); -+ return -EBUSY; ++ if (flash->dma.enable) { ++ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.tx_virt, flash->dma.tx_addr); ++ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.rx_virt, flash->dma.rx_addr); + } + -+ ctx->is_open = true; ++ mutex_destroy(&flash->tx_mtx); ++ mutex_destroy(&flash->rx_mtx); + -+ spin_unlock(&otp_state_lock); ++ misc_deregister(&flash->mdev); + + return 0; +} + -+static int otp_release(struct inode *inode, struct file *file) ++/* global control */ ++irqreturn_t ast2600_espi_isr(int irq, void *arg) +{ -+ struct miscdevice *c = file->private_data; -+ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); ++ struct aspeed_espi *espi; ++ u32 sts; + -+ spin_lock(&otp_state_lock); ++ espi = (struct aspeed_espi *)arg; + -+ ctx->is_open = false; ++ sts = readl(espi->regs + ESPI_INT_STS); ++ if (!sts) ++ return IRQ_NONE; + -+ spin_unlock(&otp_state_lock); ++ if (sts & ESPI_INT_STS_PERIF) ++ ast2600_espi_perif_isr(espi); + -+ return 0; -+} ++ if (sts & ESPI_INT_STS_VW) ++ ast2600_espi_vw_isr(espi); + -+static const struct file_operations otp_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = otp_ioctl, -+ .open = otp_open, -+ .release = otp_release, -+}; ++ if (sts & ESPI_INT_STS_OOB) ++ ast2600_espi_oob_isr(espi); + -+static const struct of_device_id aspeed_otp_of_matches[] = { -+ { .compatible = "aspeed,ast2600-sbc" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, aspeed_otp_of_matches); ++ if (sts & ESPI_INT_STS_FLASH) ++ ast2600_espi_flash_isr(espi); + -+static int aspeed_otp_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct regmap *scu; -+ struct aspeed_otp *priv; -+ struct resource *res; -+ u32 revid0, revid1; -+ int rc; ++ if (sts & ESPI_INT_STS_RST_DEASSERT) { ++ /* this will clear all interrupt enable and status */ ++ reset_control_assert(espi->rst); ++ reset_control_deassert(espi->rst); + -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ glob_ctx = priv; -+ if (!priv) -+ return -ENOMEM; ++ ast2600_espi_perif_sw_reset(espi); ++ ast2600_espi_perif_reset(espi); ++ ast2600_espi_vw_reset(espi); ++ ast2600_espi_oob_reset(espi); ++ ast2600_espi_flash_reset(espi); + -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) { -+ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM\n"); -+ return -ENOENT; ++ /* re-enable eSPI_RESET# interrupt */ ++ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN); + } + -+ priv->reg_base = devm_ioremap_resource(&pdev->dev, res); -+ if (!priv->reg_base) -+ return -EIO; ++ return IRQ_HANDLED; ++} + -+ scu = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu"); -+ if (IS_ERR(scu)) { -+ dev_err(dev, "failed to find 2600 SCU regmap\n"); -+ return PTR_ERR(scu); -+ } ++void ast2600_espi_pre_init(struct aspeed_espi *espi) ++{ ++ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN_CLR); ++} + -+ regmap_read(scu, ASPEED_REVISION_ID0, &revid0); -+ regmap_read(scu, ASPEED_REVISION_ID1, &revid1); ++void ast2600_espi_post_init(struct aspeed_espi *espi) ++{ ++ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN); ++} + -+ priv->otp_ver = chip_version(revid0, revid1); ++void ast2600_espi_deinit(struct aspeed_espi *espi) ++{ ++ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN_CLR); ++} +diff --git a/drivers/soc/aspeed/espi/ast2600-espi.h b/drivers/soc/aspeed/espi/ast2600-espi.h +--- a/drivers/soc/aspeed/espi/ast2600-espi.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2600-espi.h 2026-04-08 18:03:48.313705265 +0000 +@@ -0,0 +1,306 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Register definitions for Aspeed AST2600 eSPI controller ++ * Copyright 2023 Aspeed Technology Inc. ++ */ ++#ifndef _AST2600_ESPI_H_ ++#define _AST2600_ESPI_H_ + -+ if (priv->otp_ver == -1) { -+ dev_err(dev, "invalid SCU\n"); -+ return -EINVAL; -+ } ++#include ++#include + -+ priv->data = kmalloc(8192, GFP_KERNEL); -+ if (!priv->data) -+ return -ENOMEM; ++#include "aspeed-espi.h" + -+ dev_set_drvdata(dev, priv); ++/* registers */ ++#define ESPI_CTRL 0x000 ++#define ESPI_CTRL_FLASH_TX_SW_RST BIT(31) ++#define ESPI_CTRL_FLASH_RX_SW_RST BIT(30) ++#define ESPI_CTRL_OOB_TX_SW_RST BIT(29) ++#define ESPI_CTRL_OOB_RX_SW_RST BIT(28) ++#define ESPI_CTRL_PERIF_NP_TX_SW_RST BIT(27) ++#define ESPI_CTRL_PERIF_NP_RX_SW_RST BIT(26) ++#define ESPI_CTRL_PERIF_PC_TX_SW_RST BIT(25) ++#define ESPI_CTRL_PERIF_PC_RX_SW_RST BIT(24) ++#define ESPI_CTRL_FLASH_TX_DMA_EN BIT(23) ++#define ESPI_CTRL_FLASH_RX_DMA_EN BIT(22) ++#define ESPI_CTRL_OOB_TX_DMA_EN BIT(21) ++#define ESPI_CTRL_OOB_RX_DMA_EN BIT(20) ++#define ESPI_CTRL_PERIF_NP_TX_DMA_EN BIT(19) ++#define ESPI_CTRL_PERIF_PC_TX_DMA_EN BIT(17) ++#define ESPI_CTRL_PERIF_PC_RX_DMA_EN BIT(16) ++#define ESPI_CTRL_FLASH_EDAF_MODE GENMASK(11, 10) ++#define ESPI_CTRL_VW_GPIO_SW BIT(9) ++#define ESPI_CTRL_FLASH_SW_RDY BIT(7) ++#define ESPI_CTRL_OOB_SW_RDY BIT(4) ++#define ESPI_CTRL_VW_SW_RDY BIT(3) ++#define ESPI_CTRL_PERIF_SW_RDY BIT(1) ++#define ESPI_STS 0x004 ++#define ESPI_INT_STS 0x008 ++#define ESPI_INT_STS_RST_DEASSERT BIT(31) ++#define ESPI_INT_STS_OOB_RX_TMOUT BIT(23) ++#define ESPI_INT_STS_VW_SYSEVT1 BIT(22) ++#define ESPI_INT_STS_FLASH_TX_ERR BIT(21) ++#define ESPI_INT_STS_OOB_TX_ERR BIT(20) ++#define ESPI_INT_STS_FLASH_TX_ABT BIT(19) ++#define ESPI_INT_STS_OOB_TX_ABT BIT(18) ++#define ESPI_INT_STS_PERIF_NP_TX_ABT BIT(17) ++#define ESPI_INT_STS_PERIF_PC_TX_ABT BIT(16) ++#define ESPI_INT_STS_FLASH_RX_ABT BIT(15) ++#define ESPI_INT_STS_OOB_RX_ABT BIT(14) ++#define ESPI_INT_STS_PERIF_NP_RX_ABT BIT(13) ++#define ESPI_INT_STS_PERIF_PC_RX_ABT BIT(12) ++#define ESPI_INT_STS_PERIF_NP_TX_ERR BIT(11) ++#define ESPI_INT_STS_PERIF_PC_TX_ERR BIT(10) ++#define ESPI_INT_STS_VW_GPIO BIT(9) ++#define ESPI_INT_STS_VW_SYSEVT BIT(8) ++#define ESPI_INT_STS_FLASH_TX_CMPLT BIT(7) ++#define ESPI_INT_STS_FLASH_RX_CMPLT BIT(6) ++#define ESPI_INT_STS_OOB_TX_CMPLT BIT(5) ++#define ESPI_INT_STS_OOB_RX_CMPLT BIT(4) ++#define ESPI_INT_STS_PERIF_NP_TX_CMPLT BIT(3) ++#define ESPI_INT_STS_PERIF_PC_TX_CMPLT BIT(1) ++#define ESPI_INT_STS_PERIF_PC_RX_CMPLT BIT(0) ++#define ESPI_INT_EN 0x00c ++#define ESPI_INT_EN_RST_DEASSERT BIT(31) ++#define ESPI_INT_EN_OOB_RX_TMOUT BIT(23) ++#define ESPI_INT_EN_VW_SYSEVT1 BIT(22) ++#define ESPI_INT_EN_FLASH_TX_ERR BIT(21) ++#define ESPI_INT_EN_OOB_TX_ERR BIT(20) ++#define ESPI_INT_EN_FLASH_TX_ABT BIT(19) ++#define ESPI_INT_EN_OOB_TX_ABT BIT(18) ++#define ESPI_INT_EN_PERIF_NP_TX_ABT BIT(17) ++#define ESPI_INT_EN_PERIF_PC_TX_ABT BIT(16) ++#define ESPI_INT_EN_FLASH_RX_ABT BIT(15) ++#define ESPI_INT_EN_OOB_RX_ABT BIT(14) ++#define ESPI_INT_EN_PERIF_NP_RX_ABT BIT(13) ++#define ESPI_INT_EN_PERIF_PC_RX_ABT BIT(12) ++#define ESPI_INT_EN_PERIF_NP_TX_ERR BIT(11) ++#define ESPI_INT_EN_PERIF_PC_TX_ERR BIT(10) ++#define ESPI_INT_EN_VW_GPIO BIT(9) ++#define ESPI_INT_EN_VW_SYSEVT BIT(8) ++#define ESPI_INT_EN_FLASH_TX_CMPLT BIT(7) ++#define ESPI_INT_EN_FLASH_RX_CMPLT BIT(6) ++#define ESPI_INT_EN_OOB_TX_CMPLT BIT(5) ++#define ESPI_INT_EN_OOB_RX_CMPLT BIT(4) ++#define ESPI_INT_EN_PERIF_NP_TX_CMPLT BIT(3) ++#define ESPI_INT_EN_PERIF_PC_TX_CMPLT BIT(1) ++#define ESPI_INT_EN_PERIF_PC_RX_CMPLT BIT(0) ++#define ESPI_PERIF_PC_RX_DMA 0x010 ++#define ESPI_PERIF_PC_RX_CTRL 0x014 ++#define ESPI_PERIF_PC_RX_CTRL_SERV_PEND BIT(31) ++#define ESPI_PERIF_PC_RX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_PERIF_PC_RX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_PERIF_PC_RX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_PERIF_PC_RX_DATA 0x018 ++#define ESPI_PERIF_PC_TX_DMA 0x020 ++#define ESPI_PERIF_PC_TX_CTRL 0x024 ++#define ESPI_PERIF_PC_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_PERIF_PC_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_PERIF_PC_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_PERIF_PC_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_PERIF_PC_TX_DATA 0x028 ++#define ESPI_PERIF_NP_TX_DMA 0x030 ++#define ESPI_PERIF_NP_TX_CTRL 0x034 ++#define ESPI_PERIF_NP_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_PERIF_NP_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_PERIF_NP_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_PERIF_NP_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_PERIF_NP_TX_DATA 0x038 ++#define ESPI_OOB_RX_DMA 0x040 ++#define ESPI_OOB_RX_CTRL 0x044 ++#define ESPI_OOB_RX_CTRL_SERV_PEND BIT(31) ++#define ESPI_OOB_RX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_OOB_RX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_OOB_RX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_OOB_RX_DATA 0x048 ++#define ESPI_OOB_TX_DMA 0x050 ++#define ESPI_OOB_TX_CTRL 0x054 ++#define ESPI_OOB_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_OOB_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_OOB_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_OOB_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_OOB_TX_DATA 0x058 ++#define ESPI_FLASH_RX_DMA 0x060 ++#define ESPI_FLASH_RX_CTRL 0x064 ++#define ESPI_FLASH_RX_CTRL_SERV_PEND BIT(31) ++#define ESPI_FLASH_RX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_FLASH_RX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_FLASH_RX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_FLASH_RX_DATA 0x068 ++#define ESPI_FLASH_TX_DMA 0x070 ++#define ESPI_FLASH_TX_CTRL 0x074 ++#define ESPI_FLASH_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_FLASH_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_FLASH_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_FLASH_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_FLASH_TX_DATA 0x078 ++#define ESPI_CTRL2 0x080 ++#define ESPI_CTRL2_VW_TX_SORT BIT(30) ++#define ESPI_CTRL2_MCYC_RD_DIS_WDT BIT(11) ++#define ESPI_CTRL2_MCYC_WR_DIS_WDT BIT(10) ++#define ESPI_CTRL2_MCYC_RD_DIS BIT(6) ++#define ESPI_CTRL2_MMBI_RD_DIS ESPI_CTRL2_MCYC_RD_DIS ++#define ESPI_CTRL2_MCYC_WR_DIS BIT(4) ++#define ESPI_CTRL2_MMBI_WR_DIS ESPI_CTRL2_MCYC_WR_DIS ++#define ESPI_PERIF_MCYC_SADDR 0x084 ++#define ESPI_PERIF_MMBI_SADDR ESPI_PERIF_MCYC_SADDR ++#define ESPI_PERIF_MCYC_TADDR 0x088 ++#define ESPI_PERIF_MMBI_TADDR ESPI_PERIF_MCYC_TADDR ++#define ESPI_PERIF_MCYC_MASK 0x08c ++#define ESPI_PERIF_MMBI_MASK ESPI_PERIF_MCYC_MASK ++#define ESPI_FLASH_EDAF_TADDR 0x090 ++#define ESPI_FLASH_EDAF_TADDR_BASE GENMASK(31, 24) ++#define ESPI_FLASH_EDAF_TADDR_MASK GENMASK(15, 8) ++#define ESPI_VW_SYSEVT_INT_EN 0x094 ++#define ESPI_VW_SYSEVT 0x098 ++#define ESPI_VW_SYSEVT_HOST_RST_ACK BIT(27) ++#define ESPI_VW_SYSEVT_RST_CPU_INIT BIT(26) ++#define ESPI_VW_SYSEVT_SLV_BOOT_STS BIT(23) ++#define ESPI_VW_SYSEVT_NON_FATAL_ERR BIT(22) ++#define ESPI_VW_SYSEVT_FATAL_ERR BIT(21) ++#define ESPI_VW_SYSEVT_SLV_BOOT_DONE BIT(20) ++#define ESPI_VW_SYSEVT_OOB_RST_ACK BIT(16) ++#define ESPI_VW_SYSEVT_NMI_OUT BIT(10) ++#define ESPI_VW_SYSEVT_SMI_OUT BIT(9) ++#define ESPI_VW_SYSEVT_HOST_RST_WARN BIT(8) ++#define ESPI_VW_SYSEVT_OOB_RST_WARN BIT(6) ++#define ESPI_VW_SYSEVT_PLTRSTN BIT(5) ++#define ESPI_VW_SYSEVT_SUSPEND BIT(4) ++#define ESPI_VW_SYSEVT_S5_SLEEP BIT(2) ++#define ESPI_VW_SYSEVT_S4_SLEEP BIT(1) ++#define ESPI_VW_SYSEVT_S3_SLEEP BIT(0) ++#define ESPI_VW_GPIO_VAL 0x09c ++#define ESPI_GEN_CAP_N_CONF 0x0a0 ++#define ESPI_CH0_CAP_N_CONF 0x0a4 ++#define ESPI_CH1_CAP_N_CONF 0x0a8 ++#define ESPI_CH2_CAP_N_CONF 0x0ac ++#define ESPI_CH3_CAP_N_CONF 0x0b0 ++#define ESPI_CH3_CAP_N_CONF2 0x0b4 ++#define ESPI_VW_GPIO_DIR 0x0c0 ++#define ESPI_VW_GPIO_GRP 0x0c4 ++#define ESPI_INT_EN_CLR 0x0fc ++#define ESPI_VW_SYSEVT1_INT_EN 0x100 ++#define ESPI_VW_SYSEVT1 0x104 ++#define ESPI_VW_SYSEVT1_SUSPEND_ACK BIT(20) ++#define ESPI_VW_SYSEVT1_SUSPEND_WARN BIT(0) ++#define ESPI_VW_SYSEVT_INT_T0 0x110 ++#define ESPI_VW_SYSEVT_INT_T1 0x114 ++#define ESPI_VW_SYSEVT_INT_T2 0x118 ++#define ESPI_VW_SYSEVT_INT_STS 0x11c ++#define ESPI_VW_SYSEVT1_INT_T0 0x120 ++#define ESPI_VW_SYSEVT1_INT_T1 0x124 ++#define ESPI_VW_SYSEVT1_INT_T2 0x128 ++#define ESPI_VW_SYSEVT1_INT_STS 0x12c ++#define ESPI_OOB_RX_DESC_NUM 0x130 ++#define ESPI_OOB_RX_DESC_RPTR 0x134 ++#define ESPI_OOB_RX_DESC_RPTR_UPDATE BIT(31) ++#define ESPI_OOB_RX_DESC_RPTR_RP GENMASK(11, 0) ++#define ESPI_OOB_RX_DESC_WPTR 0x138 ++#define ESPI_OOB_RX_DESC_WPTR_RECV_EN BIT(31) ++#define ESPI_OOB_RX_DESC_WPTR_SP GENMASK(27, 16) ++#define ESPI_OOB_RX_DESC_WPTR_WP GENMASK(11, 0) ++#define ESPI_OOB_TX_DESC_NUM 0x140 ++#define ESPI_OOB_TX_DESC_RPTR 0x144 ++#define ESPI_OOB_TX_DESC_RPTR_UPDATE BIT(31) ++#define ESPI_OOB_TX_DESC_WPTR 0x148 ++#define ESPI_OOB_TX_DESC_WPTR_SEND_EN BIT(31) ++#define ESPI_MMBI_CTRL 0x800 ++#define ESPI_MMBI_CTRL_INST_SZ GENMASK(10, 8) ++#define ESPI_MMBI_CTRL_TOTAL_SZ GENMASK(6, 4) ++#define ESPI_MMBI_CTRL_EN BIT(0) ++#define ESPI_MMBI_INT_STS 0x808 ++#define ESPI_MMBI_INT_EN 0x80c ++#define ESPI_MMBI_HOST_RWP(x) (0x810 + ((x) << 3)) + -+ /* Set up the miscdevice */ -+ priv->miscdev.minor = MISC_DYNAMIC_MINOR; -+ priv->miscdev.name = "aspeed-otp"; -+ priv->miscdev.fops = &otp_fops; ++/* collect ESPI_INT_EN bits for convenience */ ++#define ESPI_INT_EN_PERIF \ ++ (ESPI_INT_EN_PERIF_NP_TX_ABT | \ ++ ESPI_INT_EN_PERIF_PC_TX_ABT | \ ++ ESPI_INT_EN_PERIF_NP_RX_ABT | \ ++ ESPI_INT_EN_PERIF_PC_RX_ABT | \ ++ ESPI_INT_EN_PERIF_NP_TX_ERR | \ ++ ESPI_INT_EN_PERIF_PC_TX_ERR | \ ++ ESPI_INT_EN_PERIF_NP_TX_CMPLT | \ ++ ESPI_INT_EN_PERIF_PC_TX_CMPLT | \ ++ ESPI_INT_EN_PERIF_PC_RX_CMPLT) + -+ /* Register the device */ -+ rc = misc_register(&priv->miscdev); -+ if (rc) { -+ dev_err(dev, "Unable to register device\n"); -+ return rc; -+ } ++#define ESPI_INT_EN_VW \ ++ (ESPI_INT_EN_VW_SYSEVT1 | \ ++ ESPI_INT_EN_VW_GPIO | \ ++ ESPI_INT_EN_VW_SYSEVT) + -+ return 0; -+} ++#define ESPI_INT_EN_OOB \ ++ (ESPI_INT_EN_OOB_RX_TMOUT | \ ++ ESPI_INT_EN_OOB_TX_ERR | \ ++ ESPI_INT_EN_OOB_TX_ABT | \ ++ ESPI_INT_EN_OOB_RX_ABT | \ ++ ESPI_INT_EN_OOB_TX_CMPLT | \ ++ ESPI_INT_EN_OOB_RX_CMPLT) + -+static void aspeed_otp_remove(struct platform_device *pdev) -+{ -+ struct aspeed_otp *ctx = dev_get_drvdata(&pdev->dev); ++#define ESPI_INT_EN_FLASH \ ++ (ESPI_INT_EN_FLASH_TX_ERR | \ ++ ESPI_INT_EN_FLASH_TX_ABT | \ ++ ESPI_INT_EN_FLASH_RX_ABT | \ ++ ESPI_INT_EN_FLASH_TX_CMPLT | \ ++ ESPI_INT_EN_FLASH_RX_CMPLT) + -+ kfree(ctx->data); -+ misc_deregister(&ctx->miscdev); -+} ++/* collect ESPI_INT_STS bits for convenience */ ++#define ESPI_INT_STS_PERIF \ ++ (ESPI_INT_STS_PERIF_NP_TX_ABT | \ ++ ESPI_INT_STS_PERIF_PC_TX_ABT | \ ++ ESPI_INT_STS_PERIF_NP_RX_ABT | \ ++ ESPI_INT_STS_PERIF_PC_RX_ABT | \ ++ ESPI_INT_STS_PERIF_NP_TX_ERR | \ ++ ESPI_INT_STS_PERIF_PC_TX_ERR | \ ++ ESPI_INT_STS_PERIF_NP_TX_CMPLT | \ ++ ESPI_INT_STS_PERIF_PC_TX_CMPLT | \ ++ ESPI_INT_STS_PERIF_PC_RX_CMPLT) + -+static struct platform_driver aspeed_otp_driver = { -+ .probe = aspeed_otp_probe, -+ .remove = aspeed_otp_remove, -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .of_match_table = aspeed_otp_of_matches, -+ }, -+}; ++#define ESPI_INT_STS_VW \ ++ (ESPI_INT_STS_VW_SYSEVT1 | \ ++ ESPI_INT_STS_VW_GPIO | \ ++ ESPI_INT_STS_VW_SYSEVT) + -+module_platform_driver(aspeed_otp_driver); ++#define ESPI_INT_STS_OOB \ ++ (ESPI_INT_STS_OOB_RX_TMOUT | \ ++ ESPI_INT_STS_OOB_TX_ERR | \ ++ ESPI_INT_STS_OOB_TX_ABT | \ ++ ESPI_INT_STS_OOB_RX_ABT | \ ++ ESPI_INT_STS_OOB_TX_CMPLT | \ ++ ESPI_INT_STS_OOB_RX_CMPLT) + -+MODULE_AUTHOR("Neal Liu "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("ASPEED OTP Driver"); -diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi.c ---- a/drivers/soc/aspeed/ast2700-espi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/ast2700-espi.c 2025-12-23 10:16:21.126032636 +0000 -@@ -0,0 +1,2304 @@ ++#define ESPI_INT_STS_FLASH \ ++ (ESPI_INT_STS_FLASH_TX_ERR | \ ++ ESPI_INT_STS_FLASH_TX_ABT | \ ++ ESPI_INT_STS_FLASH_RX_ABT | \ ++ ESPI_INT_STS_FLASH_TX_CMPLT | \ ++ ESPI_INT_STS_FLASH_RX_CMPLT) ++ ++ ++/* consistent with DTS property "perif-mmbi-instance-size" */ ++enum ast2600_mmbi_instance_size { ++ MMBI_INST_SIZE_8KB = 0x0, ++ MMBI_INST_SIZE_16KB, ++ MMBI_INST_SIZE_32KB, ++ MMBI_INST_SIZE_64KB, ++ MMBI_INST_SIZE_128KB, ++ MMBI_INST_SIZE_256KB, ++ MMBI_INST_SIZE_512KB, ++ MMBI_INST_SIZE_1024KB, ++ MMBI_INST_SIZE_TYPES, ++}; ++ ++/* function operators */ ++void ast2600_espi_pre_init(struct aspeed_espi *espi); ++void ast2600_espi_post_init(struct aspeed_espi *espi); ++void ast2600_espi_deinit(struct aspeed_espi *espi); ++int ast2600_espi_perif_probe(struct aspeed_espi *espi); ++int ast2600_espi_perif_remove(struct aspeed_espi *espi); ++int ast2600_espi_vw_probe(struct aspeed_espi *espi); ++int ast2600_espi_vw_remove(struct aspeed_espi *espi); ++int ast2600_espi_oob_probe(struct aspeed_espi *espi); ++int ast2600_espi_oob_remove(struct aspeed_espi *espi); ++int ast2600_espi_flash_probe(struct aspeed_espi *espi); ++int ast2600_espi_flash_remove(struct aspeed_espi *espi); ++irqreturn_t ast2600_espi_isr(int irq, void *arg); ++#endif +diff --git a/drivers/soc/aspeed/espi/ast2700-espi.c b/drivers/soc/aspeed/espi/ast2700-espi.c +--- a/drivers/soc/aspeed/espi/ast2700-espi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2700-espi.c 2026-04-08 18:03:48.313705265 +0000 +@@ -0,0 +1,2180 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 Aspeed Technology Inc. + */ ++#include "linux/err.h" ++#include "linux/fs.h" ++#include "linux/printk.h" ++#include "linux/spinlock.h" ++#include "linux/stddef.h" ++#include "linux/wait.h" ++#include "linux/wordpart.h" +#include +#include +#include @@ -72091,14 +79863,13 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi +#include + +#include "ast2700-espi.h" -+ -+#define DEVICE_NAME "aspeed-espi" ++#include "aspeed-espi-comm.h" ++#include "aspeed-espi.h" + +static DEFINE_IDA(ast2700_espi_ida); + +#define PERIF_MCYC_ALIGN SZ_64K +#define PERIF_MMBI_ALIGN SZ_64M -+#define PERIF_MMBI_MAX_INST 8 + +#define OOB_DMA_RPTR_KEY 0x4f4f4253 +#define OOB_DMA_DESC_NUM 8 @@ -72106,178 +79877,20 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + +#define FLASH_EDAF_ALIGN SZ_16M + -+struct ast2700_espi_perif_mmbi { -+ void *b2h_virt; -+ void *h2b_virt; -+ dma_addr_t b2h_addr; -+ dma_addr_t h2b_addr; -+ struct miscdevice b2h_mdev; -+ struct miscdevice h2b_mdev; -+ bool host_rwp_update; -+ wait_queue_head_t wq; -+ struct ast2700_espi_perif *perif; -+}; -+ -+struct ast2700_espi_perif { -+ struct { -+ bool enable; -+ int irq; -+ void *virt; -+ dma_addr_t taddr; -+ uint64_t saddr; -+ uint64_t size; -+ uint32_t inst_num; -+ uint32_t inst_size; -+ struct ast2700_espi_perif_mmbi inst[PERIF_MMBI_MAX_INST]; -+ } mmbi; -+ -+ struct { -+ bool enable; -+ void *virt; -+ dma_addr_t taddr; -+ uint64_t saddr; -+ uint64_t size; -+ } mcyc; -+ -+ struct { -+ bool enable; -+ void *np_tx_virt; -+ dma_addr_t np_tx_addr; -+ void *pc_tx_virt; -+ dma_addr_t pc_tx_addr; -+ void *pc_rx_virt; -+ dma_addr_t pc_rx_addr; -+ } dma; -+ -+ bool rtc_enable; -+ bool rx_ready; -+ wait_queue_head_t wq; -+ -+ spinlock_t lock; -+ struct mutex np_tx_mtx; -+ struct mutex pc_tx_mtx; -+ struct mutex pc_rx_mtx; -+ -+ struct miscdevice mdev; -+}; -+ -+struct ast2700_espi_vw { -+ struct { -+ bool hw_mode; -+ uint32_t grp; -+ uint32_t dir0; -+ uint32_t dir1; -+ uint32_t val0; -+ uint32_t val1; -+ } gpio; -+ -+ struct miscdevice mdev; -+}; -+ -+struct ast2700_espi_oob_dma_tx_desc { -+ uint32_t data_addrl; -+ uint32_t data_addrh; -+ uint8_t cyc; -+ uint16_t tag : 4; -+ uint16_t len : 12; -+ uint8_t msg_type : 3; -+ uint8_t raz0 : 1; -+ uint8_t pec : 1; -+ uint8_t int_en : 1; -+ uint8_t pause : 1; -+ uint8_t raz1 : 1; -+ uint32_t raz2; -+ uint32_t raz3; -+ uint32_t pad[3]; -+} __packed; -+ -+struct ast2700_espi_oob_dma_rx_desc { -+ uint32_t data_addrl; -+ uint32_t data_addrh; -+ uint8_t cyc; -+ uint16_t tag : 4; -+ uint16_t len : 12; -+ uint8_t raz : 7; -+ uint8_t dirty : 1; -+ uint32_t pad[1]; -+} __packed; -+ -+struct ast2700_espi_oob { -+ struct { -+ bool enable; -+ struct ast2700_espi_oob_dma_tx_desc *txd_virt; -+ dma_addr_t txd_addr; -+ struct ast2700_espi_oob_dma_rx_desc *rxd_virt; -+ dma_addr_t rxd_addr; -+ void *tx_virt; -+ dma_addr_t tx_addr; -+ void *rx_virt; -+ dma_addr_t rx_addr; -+ } dma; -+ -+ bool rx_ready; -+ wait_queue_head_t wq; -+ -+ spinlock_t lock; -+ struct mutex tx_mtx; -+ struct mutex rx_mtx; -+ -+ struct miscdevice mdev; -+}; -+ -+struct ast2700_espi_flash { -+ struct { -+ uint32_t mode; -+ phys_addr_t taddr; -+ uint64_t size; -+ } edaf; -+ -+ struct { -+ bool enable; -+ void *tx_virt; -+ dma_addr_t tx_addr; -+ void *rx_virt; -+ dma_addr_t rx_addr; -+ } dma; -+ -+ bool rx_ready; -+ wait_queue_head_t wq; -+ -+ spinlock_t lock; -+ struct mutex rx_mtx; -+ struct mutex tx_mtx; -+ -+ struct miscdevice mdev; -+}; -+ -+struct ast2700_espi { -+ struct device *dev; -+ void __iomem *regs; -+ struct clk *clk; -+ int dev_id; -+ int irq; -+ -+ struct ast2700_espi_perif perif; -+ struct ast2700_espi_vw vw; -+ struct ast2700_espi_oob oob; -+ struct ast2700_espi_flash flash; -+}; -+ +/* peripheral channel (CH0) */ +static int ast2700_espi_mmbi_b2h_mmap(struct file *fp, struct vm_area_struct *vma) +{ -+ struct ast2700_espi_perif_mmbi *mmbi; -+ struct ast2700_espi_perif *perif; -+ struct ast2700_espi *espi; ++ struct aspeed_espi_perif_mmbi *mmbi; ++ struct aspeed_espi_perif *perif; ++ struct aspeed_espi *espi; + unsigned long vm_size; + pgprot_t prot; + -+ mmbi = container_of(fp->private_data, struct ast2700_espi_perif_mmbi, b2h_mdev); ++ mmbi = container_of(fp->private_data, struct aspeed_espi_perif_mmbi, b2h_mdev); + + perif = mmbi->perif; + -+ espi = container_of(perif, struct ast2700_espi, perif); -+ ++ espi = container_of(perif, struct aspeed_espi, perif); + vm_size = vma->vm_end - vma->vm_start; + prot = vma->vm_page_prot; + @@ -72296,17 +79909,17 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + +static int ast2700_espi_mmbi_h2b_mmap(struct file *fp, struct vm_area_struct *vma) +{ -+ struct ast2700_espi_perif_mmbi *mmbi; -+ struct ast2700_espi_perif *perif; -+ struct ast2700_espi *espi; ++ struct aspeed_espi_perif_mmbi *mmbi; ++ struct aspeed_espi_perif *perif; ++ struct aspeed_espi *espi; + unsigned long vm_size; + pgprot_t prot; + -+ mmbi = container_of(fp->private_data, struct ast2700_espi_perif_mmbi, h2b_mdev); ++ mmbi = container_of(fp->private_data, struct aspeed_espi_perif_mmbi, h2b_mdev); + + perif = mmbi->perif; + -+ espi = container_of(perif, struct ast2700_espi, perif); ++ espi = container_of(perif, struct aspeed_espi, perif); + + vm_size = vma->vm_end - vma->vm_start; + prot = vma->vm_page_prot; @@ -72326,9 +79939,9 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + +static __poll_t ast2700_espi_mmbi_h2b_poll(struct file *fp, struct poll_table_struct *pt) +{ -+ struct ast2700_espi_perif_mmbi *mmbi; ++ struct aspeed_espi_perif_mmbi *mmbi; + -+ mmbi = container_of(fp->private_data, struct ast2700_espi_perif_mmbi, h2b_mdev); ++ mmbi = container_of(fp->private_data, struct aspeed_espi_perif_mmbi, h2b_mdev); + + poll_wait(fp, &mmbi->wq, pt); + @@ -72341,18 +79954,18 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi +} + +static long ast2700_espi_perif_pc_get_rx(struct file *fp, -+ struct ast2700_espi_perif *perif, ++ struct aspeed_espi_perif *perif, + struct aspeed_espi_ioc *ioc) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2700_espi *espi; ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; + struct espi_comm_hdr *hdr; + unsigned long flags; -+ uint32_t pkt_len; -+ uint8_t *pkt; ++ u32 pkt_len; ++ u8 *pkt; + int i, rc; + -+ espi = container_of(perif, struct ast2700_espi, perif); ++ espi = container_of(perif, struct aspeed_espi, perif); + + if (fp->f_flags & O_NONBLOCK) { + if (!mutex_trylock(&perif->pc_rx_mtx)) @@ -72460,16 +80073,16 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi +} + +static long ast2700_espi_perif_pc_put_tx(struct file *fp, -+ struct ast2700_espi_perif *perif, ++ struct aspeed_espi_perif *perif, + struct aspeed_espi_ioc *ioc) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2700_espi *espi; ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; + struct espi_comm_hdr *hdr; -+ uint8_t *pkt; ++ u8 *pkt; + int i, rc; + -+ espi = container_of(perif, struct ast2700_espi, perif); ++ espi = container_of(perif, struct aspeed_espi, perif); + + if (!mutex_trylock(&perif->pc_tx_mtx)) + return -EAGAIN; @@ -72527,16 +80140,16 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi +} + +static long ast2700_espi_perif_np_put_tx(struct file *fp, -+ struct ast2700_espi_perif *perif, ++ struct aspeed_espi_perif *perif, + struct aspeed_espi_ioc *ioc) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2700_espi *espi; ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; + struct espi_comm_hdr *hdr; -+ uint8_t *pkt; ++ u8 *pkt; + int i, rc; + -+ espi = container_of(perif, struct ast2700_espi, perif); ++ espi = container_of(perif, struct aspeed_espi, perif); + + if (!mutex_trylock(&perif->np_tx_mtx)) + return -EAGAIN; @@ -72595,10 +80208,10 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + +static long ast2700_espi_perif_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ -+ struct ast2700_espi_perif *perif; ++ struct aspeed_espi_perif *perif; + struct aspeed_espi_ioc ioc; + -+ perif = container_of(fp->private_data, struct ast2700_espi_perif, mdev); ++ perif = container_of(fp->private_data, struct aspeed_espi_perif, mdev); + + if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) + return -EFAULT; @@ -72622,11 +80235,11 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + +static int ast2700_espi_perif_mmap(struct file *fp, struct vm_area_struct *vma) +{ -+ struct ast2700_espi_perif *perif; ++ struct aspeed_espi_perif *perif; + unsigned long vm_size; + pgprot_t vm_prot; + -+ perif = container_of(fp->private_data, struct ast2700_espi_perif, mdev); ++ perif = container_of(fp->private_data, struct aspeed_espi_perif, mdev); + if (!perif->mcyc.enable) + return -EPERM; + @@ -72665,14 +80278,14 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + +static irqreturn_t ast2700_espi_perif_mmbi_isr(int irq, void *arg) +{ -+ struct ast2700_espi_perif_mmbi *mmbi; -+ struct ast2700_espi_perif *perif; -+ struct ast2700_espi *espi; -+ uint32_t sts, tmp; -+ uint32_t *p; ++ struct aspeed_espi_perif_mmbi *mmbi; ++ struct aspeed_espi_perif *perif; ++ struct aspeed_espi *espi; ++ u32 sts, tmp; ++ u32 *p; + int i; + -+ espi = (struct ast2700_espi *)arg; ++ espi = (struct aspeed_espi *)arg; + + perif = &espi->perif; + @@ -72686,7 +80299,7 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + + mmbi = &perif->mmbi.inst[i]; + -+ p = (uint32_t *)mmbi->h2b_virt; ++ p = (u32 *)mmbi->h2b_virt; + p[0] = readl(espi->regs + ESPI_MMBI_HOST_RWP(i)); + p[1] = readl(espi->regs + ESPI_MMBI_HOST_RWP(i) + 4); + @@ -72700,11 +80313,11 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + return IRQ_HANDLED; +} + -+static void ast2700_espi_perif_isr(struct ast2700_espi *espi) ++static void ast2700_espi_perif_isr(struct aspeed_espi *espi) +{ -+ struct ast2700_espi_perif *perif; ++ struct aspeed_espi_perif *perif; + unsigned long flags; -+ uint32_t sts; ++ u32 sts; + + perif = &espi->perif; + @@ -72721,10 +80334,10 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + } +} + -+static void ast2700_espi_perif_sw_reset(struct ast2700_espi *espi) ++static void ast2700_espi_perif_sw_reset(struct aspeed_espi *espi) +{ + struct device *dev; -+ uint32_t reg; ++ u32 reg; + + dev = espi->dev; + @@ -72748,12 +80361,12 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + writel(reg, espi->regs + ESPI_CH0_CTRL); +} + -+static void ast2700_espi_perif_reset(struct ast2700_espi *espi) ++static void ast2700_espi_perif_reset(struct aspeed_espi *espi) +{ -+ struct ast2700_espi_perif *perif; ++ struct aspeed_espi_perif *perif; + struct device *dev; -+ uint64_t mask; -+ uint32_t reg; ++ u64 mask; ++ u32 reg; + + dev = espi->dev; + @@ -72791,11 +80404,11 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + writel(reg, espi->regs + ESPI_MMBI_CTRL); + + mask = ~(perif->mmbi.size - 1); -+ writel(mask >> 32, espi->regs + ESPI_CH0_MCYC0_MASKH); ++ writel(upper_32_bits(mask), espi->regs + ESPI_CH0_MCYC0_MASKH); + writel(mask & 0xffffffff, espi->regs + ESPI_CH0_MCYC0_MASKL); -+ writel((perif->mmbi.saddr >> 32), espi->regs + ESPI_CH0_MCYC0_SADDRH); ++ writel(upper_32_bits(perif->mmbi.saddr), espi->regs + ESPI_CH0_MCYC0_SADDRH); + writel((perif->mmbi.saddr & 0xffffffff), espi->regs + ESPI_CH0_MCYC0_SADDRL); -+ writel((perif->mmbi.taddr >> 32), espi->regs + ESPI_CH0_MCYC0_TADDRH); ++ writel(upper_32_bits(perif->mmbi.taddr), espi->regs + ESPI_CH0_MCYC0_TADDRH); + writel((perif->mmbi.taddr & 0xffffffff), espi->regs + ESPI_CH0_MCYC0_TADDRL); + + writel((0x1 << (perif->mmbi.inst_num * 2)) - 1, espi->regs + ESPI_MMBI_INT_EN); @@ -72814,11 +80427,11 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + + if (perif->mcyc.enable) { + mask = ~(perif->mcyc.size - 1); -+ writel(mask >> 32, espi->regs + ESPI_CH0_MCYC1_MASKH); ++ writel(upper_32_bits(mask), espi->regs + ESPI_CH0_MCYC1_MASKH); + writel(mask & 0xffffffff, espi->regs + ESPI_CH0_MCYC1_MASKL); -+ writel((perif->mcyc.saddr >> 32), espi->regs + ESPI_CH0_MCYC1_SADDRH); ++ writel(upper_32_bits(perif->mcyc.saddr), espi->regs + ESPI_CH0_MCYC1_SADDRH); + writel((perif->mcyc.saddr & 0xffffffff), espi->regs + ESPI_CH0_MCYC1_SADDRL); -+ writel((perif->mcyc.taddr >> 32), espi->regs + ESPI_CH0_MCYC1_TADDRH); ++ writel(upper_32_bits(perif->mcyc.taddr), espi->regs + ESPI_CH0_MCYC1_TADDRH); + writel((perif->mcyc.taddr & 0xffffffff), espi->regs + ESPI_CH0_MCYC1_TADDRL); + + reg = readl(espi->regs + ESPI_CH0_MCYC1_MASKL) | ESPI_CH0_MCYC1_MASKL_EN; @@ -72830,11 +80443,11 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + } + + if (perif->dma.enable) { -+ writel((perif->dma.np_tx_addr >> 32), espi->regs + ESPI_CH0_NP_TX_DMAH); ++ writel(upper_32_bits(perif->dma.np_tx_addr), espi->regs + ESPI_CH0_NP_TX_DMAH); + writel((perif->dma.np_tx_addr & 0xffffffff), espi->regs + ESPI_CH0_NP_TX_DMAL); -+ writel((perif->dma.pc_tx_addr >> 32), espi->regs + ESPI_CH0_PC_TX_DMAH); ++ writel(upper_32_bits(perif->dma.pc_tx_addr), espi->regs + ESPI_CH0_PC_TX_DMAH); + writel((perif->dma.pc_tx_addr & 0xffffffff), espi->regs + ESPI_CH0_PC_TX_DMAL); -+ writel((perif->dma.pc_rx_addr >> 32), espi->regs + ESPI_CH0_PC_RX_DMAH); ++ writel(upper_32_bits(perif->dma.pc_rx_addr), espi->regs + ESPI_CH0_PC_RX_DMAH); + writel((perif->dma.pc_rx_addr & 0xffffffff), espi->regs + ESPI_CH0_PC_RX_DMAL); + + reg = readl(espi->regs + ESPI_CH0_CTRL) @@ -72855,15 +80468,16 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + writel(reg, espi->regs + ESPI_CH0_CTRL); +} + -+static int ast2700_espi_perif_probe(struct ast2700_espi *espi) ++int ast2700_espi_perif_probe(struct aspeed_espi *espi) +{ -+ struct ast2700_espi_perif_mmbi *mmbi; -+ struct ast2700_espi_perif *perif; ++ struct aspeed_espi_perif_mmbi *mmbi; ++ struct aspeed_espi_perif *perif; + struct platform_device *pdev; + struct device_node *np; + struct resource res; + struct device *dev; + int i, rc; ++ u64 temp; + + dev = espi->dev; + @@ -72887,16 +80501,16 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + return -ENODEV; + } + -+ rc = of_property_read_u64(dev->of_node, "perif-mmbi-src-addr", &perif->mmbi.saddr); -+ if (rc || !IS_ALIGNED(perif->mmbi.saddr, PERIF_MMBI_ALIGN)) { ++ rc = of_property_read_u64(dev->of_node, "perif-mmbi-src-addr", &temp); ++ if (rc || !IS_ALIGNED(temp, PERIF_MMBI_ALIGN)) { + dev_err(dev, "cannot get 64MB-aligned MMBI host address\n"); + return -ENODEV; + } -+ ++ perif->mmbi.saddr = temp; + rc = of_property_read_u32(dev->of_node, "perif-mmbi-instance-num", &perif->mmbi.inst_num); + if (rc || + perif->mmbi.inst_num == 0 || -+ perif->mmbi.inst_num > PERIF_MMBI_MAX_INST || ++ perif->mmbi.inst_num > PERIF_MMBI_INST_NUM || + (perif->mmbi.inst_num & (perif->mmbi.inst_num - 1))) { + dev_err(dev, "cannot get valid MMBI instance number, expect 1/2/4/8\n"); + return -EINVAL; @@ -72926,6 +80540,7 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + } + + memset_io(perif->mmbi.virt, 0, perif->mmbi.size); ++ perif->mmbi.mmbi_isr = ast2700_espi_perif_mmbi_isr; + + for (i = 0; i < perif->mmbi.inst_num; ++i) { + mmbi = &perif->mmbi.inst[i]; @@ -72965,17 +80580,19 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + + perif->mcyc.enable = of_property_read_bool(dev->of_node, "perif-mcyc-enable"); + if (perif->mcyc.enable) { -+ rc = of_property_read_u64(dev->of_node, "perif-mcyc-src-addr", &perif->mcyc.saddr); -+ if (rc || !IS_ALIGNED(perif->mcyc.saddr, PERIF_MCYC_ALIGN)) { ++ rc = of_property_read_u64(dev->of_node, "perif-mcyc-src-addr", &temp); ++ if (rc || !IS_ALIGNED(temp, PERIF_MCYC_ALIGN)) { + dev_err(dev, "cannot get 64KB-aligned memory cycle host address\n"); + return -ENODEV; + } ++ perif->mcyc.saddr = temp; + -+ rc = of_property_read_u64(dev->of_node, "perif-mcyc-size", &perif->mcyc.size); -+ if (rc || !IS_ALIGNED(perif->mcyc.size, PERIF_MCYC_ALIGN)) { ++ rc = of_property_read_u64(dev->of_node, "perif-mcyc-size", &temp); ++ if (rc || !IS_ALIGNED(temp, PERIF_MCYC_ALIGN)) { + dev_err(dev, "cannot get 64KB-aligned memory cycle size\n"); + return -EINVAL; + } ++ perif->mcyc.size = temp; + + np = of_parse_phandle(dev->of_node, "memory-region", 0); + if (np) { @@ -73032,24 +80649,15 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + + ast2700_espi_perif_reset(espi); + -+ if (perif->mmbi.enable) { -+ rc = devm_request_irq(dev, espi->perif.mmbi.irq, -+ ast2700_espi_perif_mmbi_isr, 0, dev_name(dev), espi); -+ if (rc) { -+ dev_err(dev, "cannot request MMBI IRQ\n"); -+ return rc; -+ } -+ } -+ + return 0; +} + -+static int ast2700_espi_perif_remove(struct ast2700_espi *espi) ++int ast2700_espi_perif_remove(struct aspeed_espi *espi) +{ -+ struct ast2700_espi_perif_mmbi *mmbi; -+ struct ast2700_espi_perif *perif; ++ struct aspeed_espi_perif_mmbi *mmbi; ++ struct aspeed_espi_perif *perif; + struct device *dev; -+ uint32_t reg; ++ u32 reg; + int i; + + dev = espi->dev; @@ -73114,13 +80722,13 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi +/* virtual wire channel (CH1) */ +static long ast2700_espi_vw_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ -+ struct ast2700_espi_vw *vw; -+ struct ast2700_espi *espi; -+ uint32_t gpio0, gpio1; -+ uint32_t hw_mode; ++ struct aspeed_espi_vw *vw; ++ struct aspeed_espi *espi; ++ u32 gpio0, gpio1; ++ u32 hw_mode; + -+ vw = container_of(fp->private_data, struct ast2700_espi_vw, mdev); -+ espi = container_of(vw, struct ast2700_espi, vw); ++ vw = container_of(fp->private_data, struct aspeed_espi_vw, mdev); ++ espi = container_of(vw, struct aspeed_espi, vw); + gpio0 = vw->gpio.val0; + gpio1 = vw->gpio.val1; + hw_mode = vw->gpio.hw_mode; @@ -73132,7 +80740,7 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + + switch (cmd) { + case ASPEED_ESPI_VW_GET_GPIO_VAL: -+ if (put_user(gpio0, (uint32_t __user *)arg)) { ++ if (put_user(gpio0, (u32 __user *)arg)) { + dev_err(espi->dev, "failed to get vGPIO value0\n"); + return -EFAULT; + } @@ -73141,7 +80749,7 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + break; + + case ASPEED_ESPI_VW_PUT_GPIO_VAL: -+ if (get_user(gpio0, (uint32_t __user *)arg)) { ++ if (get_user(gpio0, (u32 __user *)arg)) { + dev_err(espi->dev, "failed to put vGPIO value0\n"); + return -EFAULT; + } @@ -73149,9 +80757,9 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + dev_info(espi->dev, "Put vGPIO value0: 0x%x\n", gpio0); + writel(gpio0, espi->regs + ESPI_CH1_GPIO_VAL0); + break; -+ ++#ifdef CONFIG_ARM64 + case ASPEED_ESPI_VW_GET_GPIO_VAL1: -+ if (put_user(gpio1, (uint32_t __user *)arg)) { ++ if (put_user(gpio1, (u32 __user *)arg)) { + dev_err(espi->dev, "failed to get vGPIO value1\n"); + return -EFAULT; + } @@ -73160,7 +80768,7 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + break; + + case ASPEED_ESPI_VW_PUT_GPIO_VAL1: -+ if (get_user(gpio1, (uint32_t __user *)arg)) { ++ if (get_user(gpio1, (u32 __user *)arg)) { + dev_err(espi->dev, "failed to put vGPIO value1\n"); + return -EFAULT; + } @@ -73168,7 +80776,7 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + dev_info(espi->dev, "Put vGPIO value1: 0x%x\n", gpio1); + writel(gpio1, espi->regs + ESPI_CH1_GPIO_VAL1); + break; -+ ++#endif + default: + return -EINVAL; + }; @@ -73181,10 +80789,103 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + .unlocked_ioctl = ast2700_espi_vw_ioctl, +}; + -+static void ast2700_espi_vw_isr(struct ast2700_espi *espi) ++static inline struct aspeed_espi_vw *to_ast2700_espi_pltrst(struct file *filp) +{ -+ struct ast2700_espi_vw *vw; -+ uint32_t sts; ++ return container_of(filp->private_data, struct aspeed_espi_vw, ++ pltrst_mdev); ++} ++ ++static int ast2700_espi_vw_pltrst_open(struct inode *inode, struct file *filp) ++{ ++ struct aspeed_espi_vw *priv = to_ast2700_espi_pltrst(filp); ++ ++ if ((filp->f_flags & O_ACCMODE) != O_RDONLY) ++ return -EACCES; ++ priv->pltrst.pltrst_avail = true ; /*Setting true returns first data after file open*/ ++ ++ return 0; ++} ++ ++static ssize_t ast2700_espi_vw_pltrst_read(struct file *filp, char __user *buf, ++ size_t count, loff_t *offset) ++{ ++ struct aspeed_espi_vw *vw = to_ast2700_espi_pltrst(filp); ++ DECLARE_WAITQUEUE(wait, current); ++ char data, old_sample; ++ int ret = 0; ++ ++ spin_lock_irq(&vw->pltrst.pltrst_lock); ++ ++ if (filp->f_flags & O_NONBLOCK) { ++ if (!vw->pltrst.pltrst_avail) { ++ ret = -EAGAIN; ++ goto out_unlock; ++ } ++ data = vw->pltrst.pltrst_status; ++ vw->pltrst.pltrst_avail = false; ++ } else { ++ add_wait_queue(&vw->pltrst.pltrst_wq, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ old_sample = vw->pltrst.pltrst_status; ++ ++ do { ++ if (old_sample != vw->pltrst.pltrst_status) { ++ data = vw->pltrst.pltrst_status; ++ vw->pltrst.pltrst_avail = false; ++ break; ++ } ++ ++ if (signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ } else { ++ spin_unlock_irq(&vw->pltrst.pltrst_lock); ++ schedule(); ++ spin_lock_irq(&vw->pltrst.pltrst_lock); ++ } ++ } while (!ret); ++ ++ remove_wait_queue(&vw->pltrst.pltrst_wq, &wait); ++ set_current_state(TASK_RUNNING); ++ } ++out_unlock: ++ spin_unlock_irq(&vw->pltrst.pltrst_lock); ++ if (ret) ++ return ret; ++ ++ ret = put_user(data, buf); ++ if (!ret) ++ ret = sizeof(data); ++ ++ return ret; ++} ++ ++static unsigned int ast2700_espi_vw_pltrst_poll(struct file *file, ++ poll_table *wait) ++{ ++ struct aspeed_espi_vw *vw = to_ast2700_espi_pltrst(file); ++ unsigned int mask = 0; ++ ++ poll_wait(file, &vw->pltrst.pltrst_wq, wait); ++ if (vw->pltrst.pltrst_avail) ++ mask |= POLLIN; ++ return mask; ++} ++ ++static const struct file_operations ast2700_espi_vw_pltrst_fops = { ++ .owner = THIS_MODULE, ++ .open = ast2700_espi_vw_pltrst_open, ++ .read = ast2700_espi_vw_pltrst_read, ++ .poll = ast2700_espi_vw_pltrst_poll, ++}; ++ ++static void ast2700_espi_vw_isr(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_vw *vw; ++ u32 sts; ++ u32 sts_evt0; ++ u32 evt0; ++ unsigned long flags; + + vw = &espi->vw; + @@ -73195,12 +80896,29 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + vw->gpio.val1 = readl(espi->regs + ESPI_CH1_GPIO_VAL1); + writel(ESPI_CH1_INT_STS_GPIO, espi->regs + ESPI_CH1_INT_STS); + } ++ ++ if (sts & ESPI_CH1_INT_STS_EVT0) { ++ sts_evt0 = readl(espi->regs + ESPI_CH1_EVT0_INT_STS); ++ evt0 = readl(espi->regs + ESPI_CH1_EVT0); ++ if (sts_evt0 & ESPI_CH1_EVT0_INT_STS_PLTRSTN || vw->pltrst.pltrst_status == 'U') { ++ spin_lock_irqsave(&vw->pltrst.pltrst_lock, flags); ++ vw->pltrst.pltrst_status = (evt0 & ESPI_CH1_EVT0_PLTRSTN) ? '1' : '0'; ++ vw->pltrst.pltrst_avail = true; ++ spin_unlock_irqrestore(&vw->pltrst.pltrst_lock, flags); ++ ++ writel(ESPI_CH1_EVT0_INT_STS_PLTRSTN, ++ espi->regs + ESPI_CH1_EVT0_INT_STS); ++ ++ wake_up_interruptible(&vw->pltrst.pltrst_wq); ++ } ++ writel(ESPI_CH1_INT_STS_EVT0, espi->regs + ESPI_CH1_INT_STS); ++ } +} + -+static void ast2700_espi_vw_reset(struct ast2700_espi *espi) ++static void ast2700_espi_vw_reset(struct aspeed_espi *espi) +{ -+ uint32_t reg; -+ struct ast2700_espi_vw *vw = &espi->vw; ++ u32 reg; ++ struct aspeed_espi_vw *vw = &espi->vw; + + writel(0x0, espi->regs + ESPI_CH1_INT_EN); + writel(0xffffffff, espi->regs + ESPI_CH1_INT_STS); @@ -73212,7 +80930,20 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + vw->gpio.val0 = readl(espi->regs + ESPI_CH1_GPIO_VAL0); + vw->gpio.val1 = readl(espi->regs + ESPI_CH1_GPIO_VAL1); + -+ writel(ESPI_CH1_INT_EN_GPIO, espi->regs + ESPI_CH1_INT_EN); ++ if (vw->pltrst.enabled) { ++ spin_lock(&vw->pltrst.pltrst_lock); ++ vw->pltrst.pltrst_status = 'U'; /* Unknown */ ++ vw->pltrst.pltrst_avail = true; ++ spin_unlock(&vw->pltrst.pltrst_lock); ++ ++ writel(ESPI_CH1_EVT0_INT_T2_PLTRSTN, ++ espi->regs + ESPI_CH1_EVT0_INT_T2); ++ writel(ESPI_CH1_EVT0_INT_EN_PLTRSTN, espi->regs + ESPI_CH1_EVT0_INT_EN); ++ writel(ESPI_CH1_INT_EN_GPIO | ESPI_CH1_INT_EN_SYS_EVT0, ++ espi->regs + ESPI_CH1_INT_EN); ++ } else { ++ writel(ESPI_CH1_INT_EN_GPIO, espi->regs + ESPI_CH1_INT_EN); ++ } + + reg = readl(espi->regs + ESPI_CH1_CTRL) + | ((vw->gpio.hw_mode) ? ESPI_CH1_CTRL_GPIO_HW : 0) @@ -73220,17 +80951,36 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + writel(reg, espi->regs + ESPI_CH1_CTRL); +} + -+static int ast2700_espi_vw_probe(struct ast2700_espi *espi) ++int ast2700_espi_vw_probe(struct aspeed_espi *espi) +{ + int rc; + struct device *dev = espi->dev; -+ struct ast2700_espi_vw *vw = &espi->vw; ++ struct aspeed_espi_vw *vw = &espi->vw; + + vw->gpio.hw_mode = of_property_read_bool(dev->of_node, "vw-gpio-hw-mode"); + of_property_read_u32(dev->of_node, "vw-gpio-group", &vw->gpio.grp); + of_property_read_u32_index(dev->of_node, "vw-gpio-direction", 0, &vw->gpio.dir0); + of_property_read_u32_index(dev->of_node, "vw-gpio-direction", 1, &vw->gpio.dir1); + ++ vw->pltrst.enabled = of_property_read_bool(dev->of_node, "vw-pltrst-monitor"); ++ if (vw->pltrst.enabled) { ++ spin_lock_init(&vw->pltrst.pltrst_lock); ++ init_waitqueue_head(&vw->pltrst.pltrst_wq); ++ vw->pltrst.pltrst_status = 'U'; /* Unknown */ ++ vw->pltrst.pltrst_avail = false; ++ vw->pltrst_mdev.parent = dev; ++ vw->pltrst_mdev.minor = MISC_DYNAMIC_MINOR; ++ vw->pltrst_mdev.name = ++ devm_kasprintf(dev, GFP_KERNEL, "%s-pltrstn%d", ++ DEVICE_NAME, espi->dev_id); ++ vw->pltrst_mdev.fops = &ast2700_espi_vw_pltrst_fops; ++ rc = misc_register(&vw->pltrst_mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", vw->pltrst_mdev.name); ++ return rc; ++ } ++ } ++ + vw->mdev.parent = dev; + vw->mdev.minor = MISC_DYNAMIC_MINOR; + vw->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-vw%d", DEVICE_NAME, espi->dev_id); @@ -73246,9 +80996,9 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + return 0; +} + -+static int ast2700_espi_vw_remove(struct ast2700_espi *espi) ++int ast2700_espi_vw_remove(struct aspeed_espi *espi) +{ -+ struct ast2700_espi_vw *vw; ++ struct aspeed_espi_vw *vw; + + vw = &espi->vw; + @@ -73261,22 +81011,24 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + +/* out-of-band channel (CH2) */ +static long ast2700_espi_oob_dma_get_rx(struct file *fp, -+ struct ast2700_espi_oob *oob, ++ struct aspeed_espi_oob *oob, + struct aspeed_espi_ioc *ioc) +{ + struct ast2700_espi_oob_dma_rx_desc *d; -+ struct ast2700_espi *espi; ++ struct ast2700_espi_oob_dma_rx_desc *rx_descs; ++ struct aspeed_espi *espi; + struct espi_comm_hdr *hdr; -+ uint32_t wptr, pkt_len; ++ u32 wptr, pkt_len; + unsigned long flags; -+ uint8_t *pkt; ++ u8 *pkt; + int rc; + -+ espi = container_of(oob, struct ast2700_espi, oob); ++ espi = container_of(oob, struct aspeed_espi, oob); + + wptr = FIELD_PREP(ESPI_CH2_RX_DESC_WPTR_WP, readl(espi->regs + ESPI_CH2_RX_DESC_WPTR)); + -+ d = &oob->dma.rxd_virt[wptr]; ++ rx_descs = (struct ast2700_espi_oob_dma_rx_desc *)oob->dma.rxd_virt; ++ d = &rx_descs[wptr]; + + if (!d->dirty) + return -EFAULT; @@ -73307,492 +81059,49 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + /* make current descriptor available again */ + d->dirty = 0; + -+ wptr = ((wptr + 1) % OOB_DMA_DESC_NUM); -+ writel(wptr | ESPI_CH2_RX_DESC_WPTR_VALID, espi->regs + ESPI_CH2_RX_DESC_WPTR); -+ -+ /* set ready flag base on the next RX descriptor */ -+ oob->rx_ready = oob->dma.rxd_virt[wptr].dirty; -+ -+ spin_unlock_irqrestore(&oob->lock, flags); -+ -+ rc = 0; -+ -+free_n_out: -+ vfree(pkt); -+ -+ return rc; -+} -+ -+static long ast2700_espi_oob_get_rx(struct file *fp, -+ struct ast2700_espi_oob *oob, -+ struct aspeed_espi_ioc *ioc) -+{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2700_espi *espi; -+ struct espi_comm_hdr *hdr; -+ unsigned long flags; -+ uint32_t pkt_len; -+ uint8_t *pkt; -+ int i, rc; -+ -+ espi = container_of(oob, struct ast2700_espi, oob); -+ -+ if (fp->f_flags & O_NONBLOCK) { -+ if (!mutex_trylock(&oob->rx_mtx)) -+ return -EAGAIN; -+ -+ if (!oob->rx_ready) { -+ rc = -ENODATA; -+ goto unlock_mtx_n_out; -+ } -+ } else { -+ mutex_lock(&oob->rx_mtx); -+ -+ if (!oob->rx_ready) { -+ rc = wait_event_interruptible(oob->wq, oob->rx_ready); -+ if (rc == -ERESTARTSYS) { -+ rc = -EINTR; -+ goto unlock_mtx_n_out; -+ } -+ } -+ } -+ -+ if (oob->dma.enable) { -+ rc = ast2700_espi_oob_dma_get_rx(fp, oob, ioc); -+ goto unlock_mtx_n_out; -+ } -+ -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ reg = readl(espi->regs + ESPI_CH2_RX_CTRL); -+ cyc = FIELD_GET(ESPI_CH2_RX_CTRL_CYC, reg); -+ tag = FIELD_GET(ESPI_CH2_RX_CTRL_TAG, reg); -+ len = FIELD_GET(ESPI_CH2_RX_CTRL_LEN, reg); -+ -+ /* -+ * calculate the length of the rest part of the -+ * eSPI packet to be read from HW and copied to -+ * user space. -+ */ -+ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + sizeof(struct espi_comm_hdr); -+ -+ if (ioc->pkt_len < pkt_len) { -+ rc = -EINVAL; -+ goto unlock_mtx_n_out; -+ } -+ -+ pkt = vmalloc(pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_mtx_n_out; -+ } -+ -+ hdr = (struct espi_comm_hdr *)pkt; -+ hdr->cyc = cyc; -+ hdr->tag = tag; -+ hdr->len_h = len >> 8; -+ hdr->len_l = len & 0xff; -+ -+ for (i = sizeof(*hdr); i < pkt_len; ++i) { -+ reg = readl(espi->regs + ESPI_CH2_RX_DATA); -+ pkt[i] = reg & 0xff; -+ } -+ -+ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } -+ -+ spin_lock_irqsave(&oob->lock, flags); -+ -+ writel(ESPI_CH2_RX_CTRL_SERV_PEND, espi->regs + ESPI_CH2_RX_CTRL); -+ oob->rx_ready = 0; -+ -+ spin_unlock_irqrestore(&oob->lock, flags); -+ -+ rc = 0; -+ -+free_n_out: -+ vfree(pkt); -+ -+unlock_mtx_n_out: -+ mutex_unlock(&oob->rx_mtx); -+ -+ return rc; -+} -+ -+static long ast2700_espi_oob_dma_put_tx(struct file *fp, -+ struct ast2700_espi_oob *oob, -+ struct aspeed_espi_ioc *ioc) -+{ -+ struct ast2700_espi_oob_dma_tx_desc *d; -+ struct ast2700_espi *espi; -+ struct espi_comm_hdr *hdr; -+ uint32_t rptr, wptr; -+ uint8_t *pkt; -+ int rc; -+ -+ espi = container_of(oob, struct ast2700_espi, oob); -+ -+ pkt = vzalloc(ioc->pkt_len); -+ if (!pkt) -+ return -ENOMEM; -+ -+ hdr = (struct espi_comm_hdr *)pkt; -+ -+ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } -+ -+ /* kick HW to update descriptor read/write pointer */ -+ writel(ESPI_CH2_TX_DESC_RPTR_UPT, espi->regs + ESPI_CH2_TX_DESC_RPTR); -+ -+ rptr = readl(espi->regs + ESPI_CH2_TX_DESC_RPTR); -+ wptr = readl(espi->regs + ESPI_CH2_TX_DESC_WPTR); -+ -+ if (((wptr + 1) % OOB_DMA_DESC_NUM) == rptr) { -+ rc = -EBUSY; -+ goto free_n_out; -+ } -+ -+ d = &oob->dma.txd_virt[wptr]; -+ d->cyc = hdr->cyc; -+ d->tag = hdr->tag; -+ d->len = (hdr->len_h << 8) | (hdr->len_l & 0xff); -+ d->msg_type = OOB_DMA_DESC_CUSTOM; -+ -+ memcpy(oob->dma.tx_virt + (PAGE_SIZE * wptr), hdr + 1, ioc->pkt_len - sizeof(*hdr)); -+ -+ dma_wmb(); -+ -+ wptr = (wptr + 1) % OOB_DMA_DESC_NUM; -+ writel(wptr | ESPI_CH2_TX_DESC_WPTR_VALID, espi->regs + ESPI_CH2_TX_DESC_WPTR); -+ -+ rc = 0; -+ -+free_n_out: -+ vfree(pkt); -+ -+ return rc; -+} -+ -+static long ast2700_espi_oob_put_tx(struct file *fp, -+ struct ast2700_espi_oob *oob, -+ struct aspeed_espi_ioc *ioc) -+{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2700_espi *espi; -+ struct espi_comm_hdr *hdr; -+ uint8_t *pkt; -+ int i, rc; -+ -+ espi = container_of(oob, struct ast2700_espi, oob); -+ -+ if (!mutex_trylock(&oob->tx_mtx)) -+ return -EAGAIN; -+ -+ if (oob->dma.enable) { -+ rc = ast2700_espi_oob_dma_put_tx(fp, oob, ioc); -+ goto unlock_mtx_n_out; -+ } -+ -+ reg = readl(espi->regs + ESPI_CH2_TX_CTRL); -+ if (reg & ESPI_CH2_TX_CTRL_TRIG_PEND) { -+ rc = -EBUSY; -+ goto unlock_mtx_n_out; -+ } -+ -+ if (ioc->pkt_len > ESPI_MAX_PKT_LEN) { -+ rc = -EINVAL; -+ goto unlock_mtx_n_out; -+ } -+ -+ pkt = vmalloc(ioc->pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_mtx_n_out; -+ } -+ -+ hdr = (struct espi_comm_hdr *)pkt; -+ -+ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } -+ -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_CH2_TX_DATA); -+ -+ cyc = hdr->cyc; -+ tag = hdr->tag; -+ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); -+ -+ reg = FIELD_PREP(ESPI_CH2_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_CH2_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_CH2_TX_CTRL_LEN, len) -+ | ESPI_CH2_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_CH2_TX_CTRL); -+ -+ rc = 0; -+ -+free_n_out: -+ vfree(pkt); -+ -+unlock_mtx_n_out: -+ mutex_unlock(&oob->tx_mtx); -+ -+ return rc; -+} -+ -+static long ast2700_espi_oob_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) -+{ -+ struct ast2700_espi_oob *oob; -+ struct aspeed_espi_ioc ioc; -+ -+ oob = container_of(fp->private_data, struct ast2700_espi_oob, mdev); -+ -+ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) -+ return -EFAULT; -+ -+ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) -+ return -EINVAL; -+ -+ switch (cmd) { -+ case ASPEED_ESPI_OOB_GET_RX: -+ return ast2700_espi_oob_get_rx(fp, oob, &ioc); -+ case ASPEED_ESPI_OOB_PUT_TX: -+ return ast2700_espi_oob_put_tx(fp, oob, &ioc); -+ }; -+ -+ return -EINVAL; -+} -+ -+static const struct file_operations ast2700_espi_oob_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2700_espi_oob_ioctl, -+}; -+ -+static void ast2700_espi_oob_isr(struct ast2700_espi *espi) -+{ -+ struct ast2700_espi_oob *oob; -+ unsigned long flags; -+ uint32_t sts; -+ -+ oob = &espi->oob; -+ -+ sts = readl(espi->regs + ESPI_CH2_INT_STS); -+ -+ if (sts & ESPI_CH2_INT_STS_RX_CMPLT) { -+ writel(ESPI_CH2_INT_STS_RX_CMPLT, espi->regs + ESPI_CH2_INT_STS); -+ -+ spin_lock_irqsave(&oob->lock, flags); -+ oob->rx_ready = true; -+ spin_unlock_irqrestore(&oob->lock, flags); -+ -+ wake_up_interruptible(&oob->wq); -+ } -+} -+ -+static void ast2700_espi_oob_reset(struct ast2700_espi *espi) -+{ -+ struct ast2700_espi_oob *oob; -+ dma_addr_t tx_addr, rx_addr; -+ uint32_t reg; -+ int i; -+ -+ oob = &espi->oob; -+ -+ writel(0x0, espi->regs + ESPI_CH2_INT_EN); -+ writel(0xffffffff, espi->regs + ESPI_CH2_INT_STS); -+ -+ reg = readl(espi->regs + ESPI_CH2_CTRL); -+ reg &= ~(ESPI_CH2_CTRL_TX_RST -+ | ESPI_CH2_CTRL_RX_RST -+ | ESPI_CH2_CTRL_TX_DMA_EN -+ | ESPI_CH2_CTRL_RX_DMA_EN -+ | ESPI_CH2_CTRL_SW_RDY); -+ writel(reg, espi->regs + ESPI_CH2_CTRL); -+ -+ udelay(1); -+ -+ reg |= (ESPI_CH2_CTRL_TX_RST | ESPI_CH2_CTRL_RX_RST); -+ writel(reg, espi->regs + ESPI_CH2_CTRL); -+ -+ if (oob->dma.enable) { -+ tx_addr = oob->dma.tx_addr; -+ rx_addr = oob->dma.rx_addr; -+ -+ for (i = 0; i < OOB_DMA_DESC_NUM; ++i) { -+ oob->dma.txd_virt[i].data_addrh = tx_addr >> 32; -+ oob->dma.txd_virt[i].data_addrl = tx_addr & 0xffffffff; -+ tx_addr += PAGE_SIZE; -+ -+ oob->dma.rxd_virt[i].data_addrh = rx_addr >> 32; -+ oob->dma.rxd_virt[i].data_addrl = rx_addr & 0xffffffff; -+ oob->dma.rxd_virt[i].dirty = 0; -+ rx_addr += PAGE_SIZE; -+ } -+ -+ writel(oob->dma.txd_addr >> 32, espi->regs + ESPI_CH2_TX_DMAH); -+ writel(oob->dma.txd_addr & 0xffffffff, espi->regs + ESPI_CH2_TX_DMAL); -+ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_CH2_TX_DESC_RPTR); -+ writel(0x0, espi->regs + ESPI_CH2_TX_DESC_WPTR); -+ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_CH2_TX_DESC_EPTR); -+ -+ writel(oob->dma.rxd_addr >> 32, espi->regs + ESPI_CH2_RX_DMAH); -+ writel(oob->dma.rxd_addr & 0xffffffff, espi->regs + ESPI_CH2_RX_DMAL); -+ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_CH2_RX_DESC_RPTR); -+ writel(0x0, espi->regs + ESPI_CH2_RX_DESC_WPTR); -+ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_CH2_RX_DESC_EPTR); -+ -+ reg = readl(espi->regs + ESPI_CH2_CTRL) -+ | ESPI_CH2_CTRL_TX_DMA_EN -+ | ESPI_CH2_CTRL_RX_DMA_EN; -+ writel(reg, espi->regs + ESPI_CH2_CTRL); -+ -+ /* activate RX DMA to make OOB_FREE */ -+ reg = readl(espi->regs + ESPI_CH2_RX_DESC_WPTR) | ESPI_CH2_RX_DESC_WPTR_VALID; -+ writel(reg, espi->regs + ESPI_CH2_RX_DESC_WPTR); -+ } -+ -+ writel(ESPI_CH2_INT_EN_RX_CMPLT, espi->regs + ESPI_CH2_INT_EN); -+ -+ reg = readl(espi->regs + ESPI_CH2_CTRL) | ESPI_CH2_CTRL_SW_RDY; -+ writel(reg, espi->regs + ESPI_CH2_CTRL); -+} -+ -+static int ast2700_espi_oob_probe(struct ast2700_espi *espi) -+{ -+ struct ast2700_espi_oob *oob; -+ struct device *dev; -+ int rc; -+ -+ dev = espi->dev; -+ -+ oob = &espi->oob; -+ -+ init_waitqueue_head(&oob->wq); -+ -+ spin_lock_init(&oob->lock); -+ -+ mutex_init(&oob->tx_mtx); -+ mutex_init(&oob->rx_mtx); -+ -+ oob->dma.enable = of_property_read_bool(dev->of_node, "oob-dma-mode"); -+ if (oob->dma.enable) { -+ oob->dma.txd_virt = dmam_alloc_coherent(dev, sizeof(*oob->dma.txd_virt) * OOB_DMA_DESC_NUM, &oob->dma.txd_addr, GFP_KERNEL); -+ if (!oob->dma.txd_virt) { -+ dev_err(dev, "cannot allocate DMA TX descriptor\n"); -+ return -ENOMEM; -+ } -+ oob->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, &oob->dma.tx_addr, GFP_KERNEL); -+ if (!oob->dma.tx_virt) { -+ dev_err(dev, "cannot allocate DMA TX buffer\n"); -+ return -ENOMEM; -+ } -+ -+ oob->dma.rxd_virt = dmam_alloc_coherent(dev, sizeof(*oob->dma.rxd_virt) * OOB_DMA_DESC_NUM, &oob->dma.rxd_addr, GFP_KERNEL); -+ if (!oob->dma.rxd_virt) { -+ dev_err(dev, "cannot allocate DMA RX descriptor\n"); -+ return -ENOMEM; -+ } -+ -+ oob->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, &oob->dma.rx_addr, GFP_KERNEL); -+ if (!oob->dma.rx_virt) { -+ dev_err(dev, "cannot allocate DMA TX buffer\n"); -+ return -ENOMEM; -+ } -+ } -+ -+ oob->mdev.parent = dev; -+ oob->mdev.minor = MISC_DYNAMIC_MINOR; -+ oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-oob%d", DEVICE_NAME, espi->dev_id); -+ oob->mdev.fops = &ast2700_espi_oob_fops; -+ rc = misc_register(&oob->mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", oob->mdev.name); -+ return rc; -+ } -+ -+ ast2700_espi_oob_reset(espi); -+ -+ return 0; -+} -+ -+static int ast2700_espi_oob_remove(struct ast2700_espi *espi) -+{ -+ struct ast2700_espi_oob *oob; -+ struct device *dev; -+ uint32_t reg; -+ -+ dev = espi->dev; -+ -+ oob = &espi->oob; -+ -+ writel(0x0, espi->regs + ESPI_CH2_INT_EN); ++ wptr = ((wptr + 1) % OOB_DMA_DESC_NUM); ++ writel(wptr | ESPI_CH2_RX_DESC_WPTR_VALID, espi->regs + ESPI_CH2_RX_DESC_WPTR); + -+ reg = readl(espi->regs + ESPI_CH2_CTRL); -+ reg &= ~(ESPI_CH2_CTRL_TX_DMA_EN -+ | ESPI_CH2_CTRL_RX_DMA_EN -+ | ESPI_CH2_CTRL_SW_RDY); -+ writel(reg, espi->regs + ESPI_CH2_CTRL); ++ /* set ready flag base on the next RX descriptor */ ++ oob->rx_ready = rx_descs[wptr].dirty; + -+ if (oob->dma.enable) { -+ dmam_free_coherent(dev, sizeof(*oob->dma.txd_virt) * OOB_DMA_DESC_NUM, -+ oob->dma.txd_virt, oob->dma.txd_addr); -+ dmam_free_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, -+ oob->dma.tx_virt, oob->dma.tx_addr); -+ dmam_free_coherent(dev, sizeof(*oob->dma.rxd_virt) * OOB_DMA_DESC_NUM, -+ oob->dma.rxd_virt, oob->dma.rxd_addr); -+ dmam_free_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, -+ oob->dma.rx_virt, oob->dma.rx_addr); -+ } ++ spin_unlock_irqrestore(&oob->lock, flags); + -+ mutex_destroy(&oob->tx_mtx); -+ mutex_destroy(&oob->rx_mtx); ++ rc = 0; + -+ misc_deregister(&oob->mdev); ++free_n_out: ++ vfree(pkt); + -+ return 0; ++ return rc; +} + -+/* flash channel (CH3) */ -+static long ast2700_espi_flash_get_rx(struct file *fp, -+ struct ast2700_espi_flash *flash, -+ struct aspeed_espi_ioc *ioc) ++static long ast2700_espi_oob_get_rx(struct file *fp, ++ struct aspeed_espi_oob *oob, ++ struct aspeed_espi_ioc *ioc) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2700_espi *espi; ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; + struct espi_comm_hdr *hdr; + unsigned long flags; -+ uint32_t pkt_len; -+ uint8_t *pkt; ++ u32 pkt_len; ++ u8 *pkt; + int i, rc; + -+ rc = 0; -+ -+ espi = container_of(flash, struct ast2700_espi, flash); ++ espi = container_of(oob, struct aspeed_espi, oob); + + if (fp->f_flags & O_NONBLOCK) { -+ if (!mutex_trylock(&flash->rx_mtx)) ++ if (!mutex_trylock(&oob->rx_mtx)) + return -EAGAIN; + -+ if (!flash->rx_ready) { ++ if (!oob->rx_ready) { + rc = -ENODATA; + goto unlock_mtx_n_out; + } + } else { -+ mutex_lock(&flash->rx_mtx); ++ mutex_lock(&oob->rx_mtx); + -+ if (!flash->rx_ready) { -+ rc = wait_event_interruptible(flash->wq, flash->rx_ready); ++ if (!oob->rx_ready) { ++ rc = wait_event_interruptible(oob->wq, oob->rx_ready); + if (rc == -ERESTARTSYS) { + rc = -EINTR; + goto unlock_mtx_n_out; @@ -73800,44 +81109,26 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + } + } + ++ if (oob->dma.enable) { ++ rc = ast2700_espi_oob_dma_get_rx(fp, oob, ioc); ++ goto unlock_mtx_n_out; ++ } ++ + /* + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ -+ reg = readl(espi->regs + ESPI_CH3_RX_CTRL); -+ cyc = FIELD_GET(ESPI_CH3_RX_CTRL_CYC, reg); -+ tag = FIELD_GET(ESPI_CH3_RX_CTRL_TAG, reg); -+ len = FIELD_GET(ESPI_CH3_RX_CTRL_LEN, reg); ++ reg = readl(espi->regs + ESPI_CH2_RX_CTRL); ++ cyc = FIELD_GET(ESPI_CH2_RX_CTRL_CYC, reg); ++ tag = FIELD_GET(ESPI_CH2_RX_CTRL_TAG, reg); ++ len = FIELD_GET(ESPI_CH2_RX_CTRL_LEN, reg); + + /* + * calculate the length of the rest part of the + * eSPI packet to be read from HW and copied to + * user space. + */ -+ switch (cyc) { -+ case ESPI_FLASH_WRITE: -+ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + -+ sizeof(struct espi_flash_rwe); -+ break; -+ case ESPI_FLASH_READ: -+ case ESPI_FLASH_ERASE: -+ pkt_len = sizeof(struct espi_flash_rwe); -+ break; -+ case ESPI_FLASH_SUC_CMPLT_D_MIDDLE: -+ case ESPI_FLASH_SUC_CMPLT_D_FIRST: -+ case ESPI_FLASH_SUC_CMPLT_D_LAST: -+ case ESPI_FLASH_SUC_CMPLT_D_ONLY: -+ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + -+ sizeof(struct espi_flash_cmplt); -+ break; -+ case ESPI_FLASH_SUC_CMPLT: -+ case ESPI_FLASH_UNSUC_CMPLT: -+ pkt_len = sizeof(struct espi_flash_cmplt); -+ break; -+ default: -+ rc = -EFAULT; -+ goto unlock_mtx_n_out; -+ } ++ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + sizeof(struct espi_comm_hdr); + + if (ioc->pkt_len < pkt_len) { + rc = -EINVAL; @@ -73856,11 +81147,9 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + hdr->len_h = len >> 8; + hdr->len_l = len & 0xff; + -+ if (flash->dma.enable) { -+ memcpy(hdr + 1, flash->dma.rx_virt, pkt_len - sizeof(*hdr)); -+ } else { -+ for (i = sizeof(*hdr); i < pkt_len; ++i) -+ pkt[i] = readl(espi->regs + ESPI_CH3_RX_DATA) & 0xff; ++ for (i = sizeof(*hdr); i < pkt_len; ++i) { ++ reg = readl(espi->regs + ESPI_CH2_RX_DATA); ++ pkt[i] = reg & 0xff; + } + + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { @@ -73868,12 +81157,12 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + goto free_n_out; + } + -+ spin_lock_irqsave(&flash->lock, flags); ++ spin_lock_irqsave(&oob->lock, flags); + -+ writel(ESPI_CH3_RX_CTRL_SERV_PEND, espi->regs + ESPI_CH3_RX_CTRL); -+ flash->rx_ready = 0; ++ writel(ESPI_CH2_RX_CTRL_SERV_PEND, espi->regs + ESPI_CH2_RX_CTRL); ++ oob->rx_ready = 0; + -+ spin_unlock_irqrestore(&flash->lock, flags); ++ spin_unlock_irqrestore(&oob->lock, flags); + + rc = 0; + @@ -73881,32 +81170,100 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + vfree(pkt); + +unlock_mtx_n_out: -+ mutex_unlock(&flash->rx_mtx); ++ mutex_unlock(&oob->rx_mtx); + + return rc; +} + -+static long ast2700_espi_flash_put_tx(struct file *fp, -+ struct ast2700_espi_flash *flash, -+ struct aspeed_espi_ioc *ioc) ++static long ast2700_espi_oob_dma_put_tx(struct file *fp, ++ struct aspeed_espi_oob *oob, ++ struct aspeed_espi_ioc *ioc) ++{ ++ struct ast2700_espi_oob_dma_tx_desc *d; ++ struct ast2700_espi_oob_dma_tx_desc *tx_descs; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ u32 rptr, wptr; ++ u8 *pkt; ++ int rc; ++ ++ espi = container_of(oob, struct aspeed_espi, oob); ++ ++ pkt = vzalloc(ioc->pkt_len); ++ if (!pkt) ++ return -ENOMEM; ++ ++ hdr = (struct espi_comm_hdr *)pkt; ++ ++ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } ++ ++ /* kick HW to update descriptor read/write pointer */ ++ writel(ESPI_CH2_TX_DESC_RPTR_UPT, espi->regs + ESPI_CH2_TX_DESC_RPTR); ++ ++ rptr = readl(espi->regs + ESPI_CH2_TX_DESC_RPTR); ++ wptr = readl(espi->regs + ESPI_CH2_TX_DESC_WPTR); ++ ++ if (((wptr + 1) % OOB_DMA_DESC_NUM) == rptr) { ++ rc = -EBUSY; ++ goto free_n_out; ++ } ++ ++ tx_descs = (struct ast2700_espi_oob_dma_tx_desc *)oob->dma.txd_virt; ++ d = &tx_descs[wptr]; ++ d->cyc = hdr->cyc; ++ d->tag = hdr->tag; ++ d->len = (hdr->len_h << 8) | (hdr->len_l & 0xff); ++ d->msg_type = OOB_DMA_DESC_CUSTOM; ++ ++ memcpy(oob->dma.tx_virt + (PAGE_SIZE * wptr), hdr + 1, ioc->pkt_len - sizeof(*hdr)); ++ ++ dma_wmb(); ++ ++ wptr = (wptr + 1) % OOB_DMA_DESC_NUM; ++ writel(wptr | ESPI_CH2_TX_DESC_WPTR_VALID, espi->regs + ESPI_CH2_TX_DESC_WPTR); ++ ++ rc = 0; ++ ++free_n_out: ++ vfree(pkt); ++ ++ return rc; ++} ++ ++static long ast2700_espi_oob_put_tx(struct file *fp, ++ struct aspeed_espi_oob *oob, ++ struct aspeed_espi_ioc *ioc) +{ -+ uint32_t reg, cyc, tag, len; -+ struct ast2700_espi *espi; ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; + struct espi_comm_hdr *hdr; -+ uint8_t *pkt; ++ u8 *pkt; + int i, rc; + -+ espi = container_of(flash, struct ast2700_espi, flash); ++ espi = container_of(oob, struct aspeed_espi, oob); + -+ if (!mutex_trylock(&flash->tx_mtx)) ++ if (!mutex_trylock(&oob->tx_mtx)) + return -EAGAIN; + -+ reg = readl(espi->regs + ESPI_CH3_TX_CTRL); -+ if (reg & ESPI_CH3_TX_CTRL_TRIG_PEND) { ++ if (oob->dma.enable) { ++ rc = ast2700_espi_oob_dma_put_tx(fp, oob, ioc); ++ goto unlock_mtx_n_out; ++ } ++ ++ reg = readl(espi->regs + ESPI_CH2_TX_CTRL); ++ if (reg & ESPI_CH2_TX_CTRL_TRIG_PEND) { + rc = -EBUSY; + goto unlock_mtx_n_out; + } + ++ if (ioc->pkt_len > ESPI_MAX_PKT_LEN) { ++ rc = -EINVAL; ++ goto unlock_mtx_n_out; ++ } ++ + pkt = vmalloc(ioc->pkt_len); + if (!pkt) { + rc = -ENOMEM; @@ -73924,23 +81281,18 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ -+ if (flash->dma.enable) { -+ memcpy(flash->dma.tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); -+ dma_wmb(); -+ } else { -+ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_CH3_TX_DATA); -+ } ++ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) ++ writel(pkt[i], espi->regs + ESPI_CH2_TX_DATA); + + cyc = hdr->cyc; + tag = hdr->tag; + len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + -+ reg = FIELD_PREP(ESPI_CH3_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_CH3_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_CH3_TX_CTRL_LEN, len) -+ | ESPI_CH3_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_CH3_TX_CTRL); ++ reg = FIELD_PREP(ESPI_CH2_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_CH2_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_CH2_TX_CTRL_LEN, len) ++ | ESPI_CH2_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_CH2_TX_CTRL); + + rc = 0; + @@ -73948,17 +81300,17 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + vfree(pkt); + +unlock_mtx_n_out: -+ mutex_unlock(&flash->tx_mtx); ++ mutex_unlock(&oob->tx_mtx); + + return rc; +} + -+static long ast2700_espi_flash_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static long ast2700_espi_oob_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ -+ struct ast2700_espi_flash *flash; ++ struct aspeed_espi_oob *oob; + struct aspeed_espi_ioc ioc; + -+ flash = container_of(fp->private_data, struct ast2700_espi_flash, mdev); ++ oob = container_of(fp->private_data, struct aspeed_espi_oob, mdev); + + if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) + return -EFAULT; @@ -73967,1265 +81319,1000 @@ diff --git a/drivers/soc/aspeed/ast2700-espi.c b/drivers/soc/aspeed/ast2700-espi + return -EINVAL; + + switch (cmd) { -+ case ASPEED_ESPI_FLASH_GET_RX: -+ return ast2700_espi_flash_get_rx(fp, flash, &ioc); -+ case ASPEED_ESPI_FLASH_PUT_TX: -+ return ast2700_espi_flash_put_tx(fp, flash, &ioc); ++ case ASPEED_ESPI_OOB_GET_RX: ++ return ast2700_espi_oob_get_rx(fp, oob, &ioc); ++ case ASPEED_ESPI_OOB_PUT_TX: ++ return ast2700_espi_oob_put_tx(fp, oob, &ioc); + }; + + return -EINVAL; +} + -+static const struct file_operations ast2700_espi_flash_fops = { ++static const struct file_operations ast2700_espi_oob_fops = { + .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2700_espi_flash_ioctl, ++ .unlocked_ioctl = ast2700_espi_oob_ioctl, +}; + -+static void ast2700_espi_flash_isr(struct ast2700_espi *espi) ++static void ast2700_espi_oob_isr(struct aspeed_espi *espi) +{ -+ struct ast2700_espi_flash *flash; ++ struct aspeed_espi_oob *oob; + unsigned long flags; -+ uint32_t sts; -+ -+ flash = &espi->flash; -+ -+ sts = readl(espi->regs + ESPI_CH3_INT_STS); -+ -+ if (sts & ESPI_CH3_INT_STS_RX_CMPLT) { -+ writel(ESPI_CH3_INT_STS_RX_CMPLT, espi->regs + ESPI_CH3_INT_STS); -+ -+ spin_lock_irqsave(&flash->lock, flags); -+ flash->rx_ready = true; -+ spin_unlock_irqrestore(&flash->lock, flags); -+ -+ wake_up_interruptible(&flash->wq); -+ } -+} -+ -+static void ast2700_espi_flash_reset(struct ast2700_espi *espi) -+{ -+ uint32_t reg; -+ uint64_t mask; -+ struct ast2700_espi_flash *flash = &espi->flash; -+ -+ writel(0x0, espi->regs + ESPI_CH3_INT_EN); -+ writel(0xffffffff, espi->regs + ESPI_CH3_INT_STS); -+ -+ reg = readl(espi->regs + ESPI_CH3_CTRL); -+ reg &= ~(ESPI_CH3_CTRL_TX_RST -+ | ESPI_CH3_CTRL_RX_RST -+ | ESPI_CH3_CTRL_TX_DMA_EN -+ | ESPI_CH3_CTRL_RX_DMA_EN -+ | ESPI_CH3_CTRL_SW_RDY); -+ writel(reg, espi->regs + ESPI_CH3_CTRL); -+ -+ udelay(1); ++ u32 sts; + -+ reg |= (ESPI_CH3_CTRL_TX_RST | ESPI_CH3_CTRL_RX_RST); -+ writel(reg, espi->regs + ESPI_CH3_CTRL); ++ oob = &espi->oob; + -+ if (flash->edaf.mode == EDAF_MODE_MIX) { -+ mask = ~(flash->edaf.size - 1); -+ writel(mask >> 32, espi->regs + ESPI_CH3_EDAF_MASKH); -+ writel(mask & 0xffffffff, espi->regs + ESPI_CH3_EDAF_MASKL); -+ writel(flash->edaf.taddr >> 32, espi->regs + ESPI_CH3_EDAF_TADDRH); -+ writel(flash->edaf.taddr & 0xffffffff, espi->regs + ESPI_CH3_EDAF_TADDRL); -+ } ++ sts = readl(espi->regs + ESPI_CH2_INT_STS); + -+ reg = readl(espi->regs + ESPI_CH3_CTRL) & ~ESPI_CH3_CTRL_EDAF_MODE; -+ reg |= FIELD_PREP(ESPI_CH3_CTRL_EDAF_MODE, flash->edaf.mode); -+ writel(reg, espi->regs + ESPI_CH3_CTRL); ++ if (sts & ESPI_CH2_INT_STS_RX_CMPLT) { ++ writel(ESPI_CH2_INT_STS_RX_CMPLT, espi->regs + ESPI_CH2_INT_STS); + -+ if (flash->dma.enable) { -+ writel(flash->dma.tx_addr >> 32, espi->regs + ESPI_CH3_TX_DMAH); -+ writel(flash->dma.tx_addr & 0xffffffff, espi->regs + ESPI_CH3_TX_DMAL); -+ writel(flash->dma.rx_addr >> 32, espi->regs + ESPI_CH3_RX_DMAH); -+ writel(flash->dma.rx_addr & 0xffffffff, espi->regs + ESPI_CH3_RX_DMAL); ++ spin_lock_irqsave(&oob->lock, flags); ++ oob->rx_ready = true; ++ spin_unlock_irqrestore(&oob->lock, flags); + -+ reg = readl(espi->regs + ESPI_CH3_CTRL) -+ | ESPI_CH3_CTRL_TX_DMA_EN -+ | ESPI_CH3_CTRL_RX_DMA_EN; -+ writel(reg, espi->regs + ESPI_CH3_CTRL); ++ wake_up_interruptible(&oob->wq); + } -+ -+ writel(ESPI_CH3_INT_EN_RX_CMPLT, espi->regs + ESPI_CH3_INT_EN); -+ -+ reg = readl(espi->regs + ESPI_CH3_CTRL) | ESPI_CH3_CTRL_SW_RDY; -+ writel(reg, espi->regs + ESPI_CH3_CTRL); +} + -+static int ast2700_espi_flash_probe(struct ast2700_espi *espi) ++static void ast2700_espi_oob_reset(struct aspeed_espi *espi) +{ -+ struct ast2700_espi_flash *flash; -+ struct device_node *np; -+ struct resource res; -+ struct device *dev; -+ void *virt; -+ int rc; -+ -+ dev = espi->dev; -+ -+ flash = &espi->flash; -+ -+ init_waitqueue_head(&flash->wq); ++ struct aspeed_espi_oob *oob; ++ struct ast2700_espi_oob_dma_tx_desc *tx_desc; ++ struct ast2700_espi_oob_dma_rx_desc *rx_desc; ++ dma_addr_t tx_addr, rx_addr; ++ u32 reg; ++ int i; + -+ spin_lock_init(&flash->lock); ++ oob = &espi->oob; + -+ mutex_init(&flash->tx_mtx); -+ mutex_init(&flash->rx_mtx); ++ writel(0x0, espi->regs + ESPI_CH2_INT_EN); ++ writel(0xffffffff, espi->regs + ESPI_CH2_INT_STS); + -+ flash->edaf.mode = EDAF_MODE_HW; ++ reg = readl(espi->regs + ESPI_CH2_CTRL); ++ reg &= ~(ESPI_CH2_CTRL_TX_RST ++ | ESPI_CH2_CTRL_RX_RST ++ | ESPI_CH2_CTRL_TX_DMA_EN ++ | ESPI_CH2_CTRL_RX_DMA_EN ++ | ESPI_CH2_CTRL_SW_RDY); ++ writel(reg, espi->regs + ESPI_CH2_CTRL); + -+ of_property_read_u32(dev->of_node, "flash-edaf-mode", &flash->edaf.mode); -+ dev_info(dev, "eDAF mode: 0x%x\n", flash->edaf.mode); -+ if (flash->edaf.mode == EDAF_MODE_MIX) { -+ np = of_parse_phandle(dev->of_node, "flash-edaf-tgt-addr", 0); -+ if (!np || of_address_to_resource(np, 0, &res)) { -+ dev_err(dev, "cannot get eDAF memory region\n"); -+ return -ENODEV; -+ } ++ udelay(1); + -+ of_node_put(np); ++ reg |= (ESPI_CH2_CTRL_TX_RST | ESPI_CH2_CTRL_RX_RST); ++ writel(reg, espi->regs + ESPI_CH2_CTRL); + -+ flash->edaf.taddr = res.start; -+ flash->edaf.size = resource_size(&res); -+ dev_info(dev, "eDAF address: 0x%llx\n", flash->edaf.taddr); -+ dev_info(dev, "eDAF size: 0x%llx\n", flash->edaf.size); ++ if (oob->dma.enable) { ++ tx_addr = oob->dma.tx_addr; ++ rx_addr = oob->dma.rx_addr; + -+ virt = devm_ioremap_resource(dev, &res); -+ if (!virt) { -+ dev_err(dev, "cannot map eDAF memory region\n"); -+ return -ENOMEM; -+ } -+ } ++ tx_desc = (struct ast2700_espi_oob_dma_tx_desc *)oob->dma.txd_virt; ++ rx_desc = (struct ast2700_espi_oob_dma_rx_desc *)oob->dma.rxd_virt; + -+ flash->dma.enable = of_property_read_bool(dev->of_node, "flash-dma-mode"); -+ if (flash->dma.enable) { -+ flash->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.tx_addr, GFP_KERNEL); -+ if (!flash->dma.tx_virt) { -+ dev_err(dev, "cannot allocate DMA TX buffer\n"); -+ return -ENOMEM; -+ } ++ for (i = 0; i < OOB_DMA_DESC_NUM; ++i) { ++ tx_desc[i].data_addrh = upper_32_bits(tx_addr); ++ tx_desc[i].data_addrl = tx_addr & 0xffffffff; ++ tx_addr += PAGE_SIZE; + -+ flash->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.rx_addr, GFP_KERNEL); -+ if (!flash->dma.rx_virt) { -+ dev_err(dev, "cannot allocate DMA RX buffer\n"); -+ return -ENOMEM; ++ rx_desc[i].data_addrh = upper_32_bits(rx_addr); ++ rx_desc[i].data_addrl = rx_addr & 0xffffffff; ++ rx_desc[i].dirty = 0; ++ rx_addr += PAGE_SIZE; + } -+ } -+ -+ flash->mdev.parent = dev; -+ flash->mdev.minor = MISC_DYNAMIC_MINOR; -+ flash->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-flash%d", DEVICE_NAME, espi->dev_id); -+ flash->mdev.fops = &ast2700_espi_flash_fops; -+ rc = misc_register(&flash->mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", flash->mdev.name); -+ return rc; -+ } -+ -+ ast2700_espi_flash_reset(espi); -+ -+ return 0; -+} -+ -+static int ast2700_espi_flash_remove(struct ast2700_espi *espi) -+{ -+ struct ast2700_espi_flash *flash; -+ struct device *dev; -+ uint32_t reg; -+ -+ dev = espi->dev; -+ -+ flash = &espi->flash; -+ -+ writel(0x0, espi->regs + ESPI_CH3_INT_EN); -+ -+ reg = readl(espi->regs + ESPI_CH3_CTRL); -+ reg &= ~(ESPI_CH3_CTRL_TX_DMA_EN -+ | ESPI_CH3_CTRL_RX_DMA_EN -+ | ESPI_CH3_CTRL_SW_RDY); -+ writel(reg, espi->regs + ESPI_CH3_CTRL); -+ -+ if (flash->dma.enable) { -+ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.tx_virt, flash->dma.tx_addr); -+ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.rx_virt, flash->dma.rx_addr); -+ } -+ -+ mutex_destroy(&flash->tx_mtx); -+ mutex_destroy(&flash->rx_mtx); -+ -+ misc_deregister(&flash->mdev); -+ -+ return 0; -+} -+ -+/* global control */ -+static irqreturn_t ast2700_espi_isr(int irq, void *arg) -+{ -+ uint32_t sts; -+ struct ast2700_espi *espi = (struct ast2700_espi *)arg; -+ -+ sts = readl(espi->regs + ESPI_INT_STS); -+ if (!sts) -+ return IRQ_NONE; -+ -+ if (sts & ESPI_INT_STS_CH0) -+ ast2700_espi_perif_isr(espi); -+ -+ if (sts & ESPI_INT_STS_CH1) -+ ast2700_espi_vw_isr(espi); -+ -+ if (sts & ESPI_INT_STS_CH2) -+ ast2700_espi_oob_isr(espi); -+ -+ if (sts & ESPI_INT_STS_CH3) -+ ast2700_espi_flash_isr(espi); -+ -+ if (sts & ESPI_INT_STS_RST_DEASSERT) { -+ ast2700_espi_perif_sw_reset(espi); -+ ast2700_espi_perif_reset(espi); -+ ast2700_espi_vw_reset(espi); -+ ast2700_espi_oob_reset(espi); -+ ast2700_espi_flash_reset(espi); -+ writel(ESPI_INT_STS_RST_DEASSERT, espi->regs + ESPI_INT_STS); -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+static int ast2700_espi_probe(struct platform_device *pdev) -+{ -+ struct ast2700_espi *espi; -+ struct resource *res; -+ struct device *dev; -+ struct regmap *scu1; -+ uint32_t reg; -+ int rc; -+ -+ dev = &pdev->dev; -+ -+ espi = devm_kzalloc(dev, sizeof(*espi), GFP_KERNEL); -+ if (!espi) -+ return -ENOMEM; -+ -+ espi->dev = dev; -+ -+ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); -+ if (rc) { -+ dev_err(dev, "cannot set 64-bits DMA mask\n"); -+ return rc; -+ } -+ -+ scu1 = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); -+ if (IS_ERR(scu1)) { -+ dev_err(dev, "failed to find SCU1 regmap\n"); -+ return PTR_ERR(scu1); -+ } -+ rc = regmap_update_bits(scu1, SCU1_DDR, -+ SCU1_DDR_DIS_ESPI0_AHB | SCU1_DDR_DIS_ESPI1_AHB, -+ 0); -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) { -+ dev_err(dev, "cannot get resource\n"); -+ return -ENODEV; -+ } -+ -+ espi->regs = devm_ioremap_resource(dev, res); -+ if (IS_ERR(espi->regs)) { -+ dev_err(dev, "cannot map registers\n"); -+ return PTR_ERR(espi->regs); -+ } -+ -+ espi->irq = platform_get_irq(pdev, 0); -+ if (espi->irq < 0) { -+ dev_err(dev, "cannot get IRQ number\n"); -+ return -ENODEV; -+ } -+ -+ espi->clk = devm_clk_get(dev, NULL); -+ if (IS_ERR(espi->clk)) { -+ dev_err(dev, "cannot get clock control\n"); -+ return PTR_ERR(espi->clk); -+ } -+ -+ rc = clk_prepare_enable(espi->clk); -+ if (rc) { -+ dev_err(dev, "cannot enable clocks\n"); -+ return rc; -+ } -+ -+ espi->dev_id = ida_alloc(&ast2700_espi_ida, GFP_KERNEL); -+ if (espi->dev_id < 0) { -+ dev_err(dev, "cannote allocate device ID\n"); -+ return espi->dev_id; -+ } -+ -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~ESPI_INT_EN_RST_DEASSERT; -+ writel(reg, espi->regs + ESPI_INT_EN); -+ -+ rc = ast2700_espi_perif_probe(espi); -+ if (rc) { -+ dev_err(dev, "cannot init CH0, rc=%d\n", rc); -+ return rc; -+ } -+ -+ rc = ast2700_espi_vw_probe(espi); -+ if (rc) { -+ dev_err(dev, "cannot init CH1, rc=%d\n", rc); -+ goto err_remove_perif; -+ } -+ -+ rc = ast2700_espi_oob_probe(espi); -+ if (rc) { -+ dev_err(dev, "cannot init CH2, rc=%d\n", rc); -+ goto err_remove_vw; -+ } -+ -+ rc = ast2700_espi_flash_probe(espi); -+ if (rc) { -+ dev_err(dev, "cannot init CH3, rc=%d\n", rc); -+ goto err_remove_oob; -+ } -+ -+ rc = devm_request_irq(dev, espi->irq, ast2700_espi_isr, 0, dev_name(dev), espi); -+ if (rc) { -+ dev_err(dev, "cannot request IRQ\n"); -+ goto err_remove_flash; -+ } + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg |= ESPI_INT_EN_RST_DEASSERT; -+ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(upper_32_bits(oob->dma.txd_addr), espi->regs + ESPI_CH2_TX_DMAH); ++ writel(oob->dma.txd_addr & 0xffffffff, espi->regs + ESPI_CH2_TX_DMAL); ++ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_CH2_TX_DESC_RPTR); ++ writel(0x0, espi->regs + ESPI_CH2_TX_DESC_WPTR); ++ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_CH2_TX_DESC_EPTR); + -+ platform_set_drvdata(pdev, espi); ++ writel(upper_32_bits(oob->dma.rxd_addr), espi->regs + ESPI_CH2_RX_DMAH); ++ writel(oob->dma.rxd_addr & 0xffffffff, espi->regs + ESPI_CH2_RX_DMAL); ++ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_CH2_RX_DESC_RPTR); ++ writel(0x0, espi->regs + ESPI_CH2_RX_DESC_WPTR); ++ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_CH2_RX_DESC_EPTR); + -+ dev_info(dev, "module loaded\n"); ++ reg = readl(espi->regs + ESPI_CH2_CTRL) ++ | ESPI_CH2_CTRL_TX_DMA_EN ++ | ESPI_CH2_CTRL_RX_DMA_EN; ++ writel(reg, espi->regs + ESPI_CH2_CTRL); + -+ return 0; ++ /* activate RX DMA to make OOB_FREE */ ++ reg = readl(espi->regs + ESPI_CH2_RX_DESC_WPTR) | ESPI_CH2_RX_DESC_WPTR_VALID; ++ writel(reg, espi->regs + ESPI_CH2_RX_DESC_WPTR); ++ } + -+err_remove_flash: -+ ast2700_espi_flash_remove(espi); -+err_remove_oob: -+ ast2700_espi_oob_remove(espi); -+err_remove_vw: -+ ast2700_espi_vw_remove(espi); -+err_remove_perif: -+ ast2700_espi_perif_remove(espi); ++ writel(ESPI_CH2_INT_EN_RX_CMPLT, espi->regs + ESPI_CH2_INT_EN); + -+ return rc; ++ reg = readl(espi->regs + ESPI_CH2_CTRL) | ESPI_CH2_CTRL_SW_RDY; ++ writel(reg, espi->regs + ESPI_CH2_CTRL); +} + -+static void ast2700_espi_remove(struct platform_device *pdev) ++int ast2700_espi_oob_probe(struct aspeed_espi *espi) +{ -+ struct ast2700_espi *espi; ++ struct aspeed_espi_oob *oob; + struct device *dev; -+ uint32_t reg; + int rc; + -+ dev = &pdev->dev; -+ -+ espi = platform_get_drvdata(pdev); ++ dev = espi->dev; + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~ESPI_INT_EN_RST_DEASSERT; -+ writel(reg, espi->regs + ESPI_INT_EN); ++ oob = &espi->oob; + -+ rc = ast2700_espi_perif_remove(espi); -+ if (rc) -+ dev_warn(dev, "cannot remove peripheral channel, rc=%d\n", rc); ++ init_waitqueue_head(&oob->wq); + -+ rc = ast2700_espi_vw_remove(espi); -+ if (rc) -+ dev_warn(dev, "cannot remove peripheral channel, rc=%d\n", rc); ++ spin_lock_init(&oob->lock); + -+ rc = ast2700_espi_oob_remove(espi); -+ if (rc) -+ dev_warn(dev, "cannot remove peripheral channel, rc=%d\n", rc); ++ mutex_init(&oob->tx_mtx); ++ mutex_init(&oob->rx_mtx); + -+ rc = ast2700_espi_flash_remove(espi); -+ if (rc) -+ dev_warn(dev, "cannot remove peripheral channel, rc=%d\n", rc); -+} ++ oob->dma.enable = of_property_read_bool(dev->of_node, "oob-dma-mode"); ++ if (oob->dma.enable) { ++ oob->dma.txd_virt = dmam_alloc_coherent(dev, ++ sizeof(struct ast2700_espi_oob_dma_tx_desc) * ++ OOB_DMA_DESC_NUM, ++ &oob->dma.txd_addr, GFP_KERNEL); ++ if (!oob->dma.txd_virt) { ++ dev_err(dev, "cannot allocate DMA TX descriptor\n"); ++ return -ENOMEM; ++ } ++ oob->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, &oob->dma.tx_addr, GFP_KERNEL); ++ if (!oob->dma.tx_virt) { ++ dev_err(dev, "cannot allocate DMA TX buffer\n"); ++ return -ENOMEM; ++ } + -+static const struct of_device_id ast2700_espi_of_matches[] = { -+ { .compatible = "aspeed,ast2700-espi" }, -+ { }, -+}; ++ oob->dma.rxd_virt = dmam_alloc_coherent(dev, ++ sizeof(struct ast2700_espi_oob_dma_rx_desc) * ++ OOB_DMA_DESC_NUM, ++ &oob->dma.rxd_addr, GFP_KERNEL); ++ if (!oob->dma.rxd_virt) { ++ dev_err(dev, "cannot allocate DMA RX descriptor\n"); ++ return -ENOMEM; ++ } + -+static struct platform_driver ast2700_espi_driver = { -+ .driver = { -+ .name = "ast2700-espi", -+ .of_match_table = ast2700_espi_of_matches, -+ }, -+ .probe = ast2700_espi_probe, -+ .remove = ast2700_espi_remove, -+}; ++ oob->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, &oob->dma.rx_addr, GFP_KERNEL); ++ if (!oob->dma.rx_virt) { ++ dev_err(dev, "cannot allocate DMA TX buffer\n"); ++ return -ENOMEM; ++ } ++ } + -+module_platform_driver(ast2700_espi_driver); ++ oob->mdev.parent = dev; ++ oob->mdev.minor = MISC_DYNAMIC_MINOR; ++ oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-oob%d", DEVICE_NAME, espi->dev_id); ++ oob->mdev.fops = &ast2700_espi_oob_fops; ++ rc = misc_register(&oob->mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", oob->mdev.name); ++ return rc; ++ } + -+MODULE_AUTHOR("Chia-Wei Wang "); -+MODULE_DESCRIPTION("Control of AST2700 eSPI Device"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/soc/aspeed/ast2700-espi.h b/drivers/soc/aspeed/ast2700-espi.h ---- a/drivers/soc/aspeed/ast2700-espi.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/ast2700-espi.h 2025-12-23 10:16:21.126032636 +0000 -@@ -0,0 +1,281 @@ -+/* SPDX-License-Identifier: GPL-2.0+ */ -+/* -+ * Copyright 2023 Aspeed Technology Inc. -+ */ -+#ifndef _AST2700_ESPI_H_ -+#define _AST2700_ESPI_H_ ++ ast2700_espi_oob_reset(espi); + -+#include -+#include "aspeed-espi-comm.h" ++ return 0; ++} + -+/* SCU regiseters */ -+#define SCU1_DDR 0x0c8 -+#define SCU1_DDR_DIS_ESPI0_AHB BIT(0) -+#define SCU1_DDR_DIS_ESPI1_AHB BIT(1) ++int ast2700_espi_oob_remove(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_oob *oob; ++ struct device *dev; ++ u32 reg; + -+/* global registers */ -+#define ESPI_CTRL 0x000 -+#define ESPI_STS 0x004 -+#define ESPI_INT_STS 0x008 -+#define ESPI_INT_STS_RST_DEASSERT BIT(31) -+#define ESPI_INT_STS_RST_ASSERT BIT(30) -+#define ESPI_INT_STS_CH3 BIT(3) -+#define ESPI_INT_STS_CH2 BIT(2) -+#define ESPI_INT_STS_CH1 BIT(1) -+#define ESPI_INT_STS_CH0 BIT(0) -+#define ESPI_INT_EN 0x00c -+#define ESPI_INT_EN_RST_DEASSERT BIT(31) -+#define ESPI_INT_EN_RST_ASSERT BIT(30) -+#define ESPI_DEV_ID 0x010 -+#define ESPI_CAP_GEN 0x014 -+#define ESPI_CAP_GEN_RTC_SUP BIT(29) -+#define ESPI_CAP_CH0 0x018 -+#define ESPI_CAP_CH1 0x01c -+#define ESPI_CAP_CH2 0x020 -+#define ESPI_CAP_CH3_0 0x024 -+#define ESPI_CAP_CH3_1 0x028 -+#define ESPI_DEV_STS 0x030 -+#define ESPI_DBG_CTRL 0x034 -+#define ESPI_DBG_ADDRL 0x038 -+#define ESPI_DBG_ADDRH 0x03c -+#define ESPI_DBG_CMD 0x040 -+#define ESPI_DBG_RES 0x044 -+#define ESPI_CH_ACC_CTRL 0x04c -+#define ESPI_CH_ACC_OFST1 0x050 -+#define ESPI_CH_ACC_OFST2 0x054 -+#define ESPI_WPROT0 0x0f8 -+#define ESPI_WPROT1 0x0fc ++ dev = espi->dev; + -+/* peripheral channel (ch0) registers */ -+#define ESPI_CH0_CTRL 0x100 -+#define ESPI_CH0_CTRL_NP_TX_RST BIT(31) -+#define ESPI_CH0_CTRL_NP_RX_RST BIT(30) -+#define ESPI_CH0_CTRL_PC_TX_RST BIT(29) -+#define ESPI_CH0_CTRL_PC_RX_RST BIT(28) -+#define ESPI_CH0_CTRL_NP_TX_DMA_EN BIT(19) -+#define ESPI_CH0_CTRL_PC_TX_DMA_EN BIT(17) -+#define ESPI_CH0_CTRL_PC_RX_DMA_EN BIT(16) -+#define ESPI_CH0_CTRL_MCYC_RD_DIS_WDT BIT(9) -+#define ESPI_CH0_CTRL_MCYC_WR_DIS_WDT BIT(8) -+#define ESPI_CH0_CTRL_MCYC_RD_DIS BIT(6) -+#define ESPI_CH0_CTRL_MCYC_WR_DIS BIT(4) -+#define ESPI_CH0_CTRL_SW_RDY BIT(1) -+#define ESPI_CH0_STS 0x104 -+#define ESPI_CH0_INT_STS 0x108 -+#define ESPI_CH0_INT_STS_PC_RX_CMPLT BIT(0) -+#define ESPI_CH0_INT_EN 0x10c -+#define ESPI_CH0_INT_EN_PC_RX_CMPLT BIT(0) -+#define ESPI_CH0_PC_RX_DMAL 0x110 -+#define ESPI_CH0_PC_RX_DMAH 0x114 -+#define ESPI_CH0_PC_RX_CTRL 0x118 -+#define ESPI_CH0_PC_RX_CTRL_SERV_PEND BIT(31) -+#define ESPI_CH0_PC_RX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_CH0_PC_RX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_CH0_PC_RX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_CH0_PC_RX_DATA 0x11c -+#define ESPI_CH0_PC_TX_DMAL 0x120 -+#define ESPI_CH0_PC_TX_DMAH 0x124 -+#define ESPI_CH0_PC_TX_CTRL 0x128 -+#define ESPI_CH0_PC_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_CH0_PC_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_CH0_PC_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_CH0_PC_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_CH0_PC_TX_DATA 0x12c -+#define ESPI_CH0_NP_TX_DMAL 0x130 -+#define ESPI_CH0_NP_TX_DMAH 0x134 -+#define ESPI_CH0_NP_TX_CTRL 0x138 -+#define ESPI_CH0_NP_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_CH0_NP_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_CH0_NP_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_CH0_NP_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_CH0_NP_TX_DATA 0x13c -+#define ESPI_CH0_MCYC0_SADDRL 0x140 -+#define ESPI_CH0_MCYC0_SADDRH 0x144 -+#define ESPI_CH0_MCYC0_TADDRL 0x148 -+#define ESPI_CH0_MCYC0_TADDRH 0x14c -+#define ESPI_CH0_MCYC0_MASKL 0x150 -+#define ESPI_CH0_MCYC0_MASKL_EN BIT(0) -+#define ESPI_CH0_MCYC0_MASKH 0x154 -+#define ESPI_CH0_MCYC1_SADDRL 0x158 -+#define ESPI_CH0_MCYC1_SADDRH 0x15c -+#define ESPI_CH0_MCYC1_TADDRL 0x160 -+#define ESPI_CH0_MCYC1_TADDRH 0x164 -+#define ESPI_CH0_MCYC1_MASKL 0x168 -+#define ESPI_CH0_MCYC1_MASKL_EN BIT(0) -+#define ESPI_CH0_MCYC1_MASKH 0x16c -+#define ESPI_CH0_WPROT0 0x1f8 -+#define ESPI_CH0_WPROT1 0x1fc ++ oob = &espi->oob; + -+/* virtual wire channel (ch1) registers */ -+#define ESPI_CH1_CTRL 0x200 -+#define ESPI_CH1_CTRL_GPIO_HW BIT(9) -+#define ESPI_CH1_CTRL_SW_RDY BIT(1) -+#define ESPI_CH1_STS 0x204 -+#define ESPI_CH1_INT_STS 0x208 -+#define ESPI_CH1_INT_STS_GPIO BIT(2) -+#define ESPI_CH1_INT_EN 0x20c -+#define ESPI_CH1_INT_EN_GPIO BIT(2) -+#define ESPI_CH1_EVT0 0x210 -+#define ESPI_CH1_EVT0_INT_EN 0x214 -+#define ESPI_CH1_EVT0_INT_T0 0x218 -+#define ESPI_CH1_EVT0_INT_T1 0x21c -+#define ESPI_CH1_EVT0_INT_T2 0x220 -+#define ESPI_CH1_EVT0_INT_STS 0x224 -+#define ESPI_CH1_EVT1 0x230 -+#define ESPI_CH1_EVT1_INT_EN 0x234 -+#define ESPI_CH1_EVT1_INT_T0 0x238 -+#define ESPI_CH1_EVT1_INT_T1 0x23c -+#define ESPI_CH1_EVT1_INT_T2 0x240 -+#define ESPI_CH1_EVT1_INT_STS 0x244 -+#define ESPI_CH1_GPIO_VAL0 0x250 -+#define ESPI_CH1_GPIO_VAL1 0x254 -+#define ESPI_CH1_GPIO_DIR0 0x258 -+#define ESPI_CH1_GPIO_DIR1 0x25c -+#define ESPI_CH1_GPIO_RSTSEL0 0x260 -+#define ESPI_CH1_GPIO_RSTSEL1 0x264 -+#define ESPI_CH1_GPIO_GRP 0x268 -+#define ESPI_CH1_GP50_DIR0 0x270 -+#define ESPI_CH1_GP50_DIR1 0x274 -+#define ESPI_CH1_GP50_VAL0 0x278 -+#define ESPI_CH1_GP50_VAL1 0x27c -+#define ESPI_CH1_SW_INT 0x280 -+#define ESPI_CH1_INT_RSTSEL0 0x284 -+#define ESPI_CH1_INT_RSTSEL1 0x288 -+#define ESPI_CH1_WPROT0 0x2f8 -+#define ESPI_CH1_WPROT1 0x2fc ++ writel(0x0, espi->regs + ESPI_CH2_INT_EN); + -+/* out-of-band channel (ch2) registers */ -+#define ESPI_CH2_CTRL 0x300 -+#define ESPI_CH2_CTRL_TX_RST BIT(31) -+#define ESPI_CH2_CTRL_RX_RST BIT(30) -+#define ESPI_CH2_CTRL_TX_DMA_EN BIT(17) -+#define ESPI_CH2_CTRL_RX_DMA_EN BIT(16) -+#define ESPI_CH2_CTRL_SW_RDY BIT(4) -+#define ESPI_CH2_STS 0x304 -+#define ESPI_CH2_INT_STS 0x308 -+#define ESPI_CH2_INT_STS_RX_CMPLT BIT(0) -+#define ESPI_CH2_INT_EN 0x30c -+#define ESPI_CH2_INT_EN_RX_CMPLT BIT(0) -+#define ESPI_CH2_RX_DMAL 0x310 -+#define ESPI_CH2_RX_DMAH 0x314 -+#define ESPI_CH2_RX_CTRL 0x318 -+#define ESPI_CH2_RX_CTRL_SERV_PEND BIT(31) -+#define ESPI_CH2_RX_CTRL_PEC BIT(24) -+#define ESPI_CH2_RX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_CH2_RX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_CH2_RX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_CH2_RX_DATA 0x31c -+#define ESPI_CH2_TX_DMAL 0x320 -+#define ESPI_CH2_TX_DMAH 0x324 -+#define ESPI_CH2_TX_CTRL 0x328 -+#define ESPI_CH2_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_CH2_TX_CTRL_PEC BIT(24) -+#define ESPI_CH2_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_CH2_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_CH2_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_CH2_TX_DATA 0x32c -+#define ESPI_CH2_RX_DESC_EPTR 0x330 -+#define ESPI_CH2_RX_DESC_RPTR 0x334 -+#define ESPI_CH2_RX_DESC_RPTR_UPDATE BIT(31) -+#define ESPI_CH2_RX_DESC_RPTR_RP GENMASK(11, 0) -+#define ESPI_CH2_RX_DESC_WPTR 0x338 -+#define ESPI_CH2_RX_DESC_WPTR_VALID BIT(31) -+#define ESPI_CH2_RX_DESC_WPTR_SP GENMASK(27, 16) -+#define ESPI_CH2_RX_DESC_WPTR_WP GENMASK(11, 0) -+#define ESPI_CH2_RX_DESC_TMOUT 0x33c -+#define ESPI_CH2_TX_DESC_EPTR 0x340 -+#define ESPI_CH2_TX_DESC_RPTR 0x344 -+#define ESPI_CH2_TX_DESC_RPTR_UPT BIT(31) -+#define ESPI_CH2_TX_DESC_WPTR 0x348 -+#define ESPI_CH2_TX_DESC_WPTR_VALID BIT(31) -+#define ESPI_CH2_WPROT0 0x3f8 -+#define ESPI_CH2_WPROT1 0x3fc ++ reg = readl(espi->regs + ESPI_CH2_CTRL); ++ reg &= ~(ESPI_CH2_CTRL_TX_DMA_EN ++ | ESPI_CH2_CTRL_RX_DMA_EN ++ | ESPI_CH2_CTRL_SW_RDY); ++ writel(reg, espi->regs + ESPI_CH2_CTRL); + -+/* flash channel (ch3) registers */ -+#define ESPI_CH3_CTRL 0x400 -+#define ESPI_CH3_CTRL_TX_RST BIT(31) -+#define ESPI_CH3_CTRL_RX_RST BIT(30) -+#define ESPI_CH3_CTRL_TX_DMA_EN BIT(17) -+#define ESPI_CH3_CTRL_RX_DMA_EN BIT(16) -+#define ESPI_CH3_CTRL_EDAF_MODE GENMASK(9, 8) -+#define ESPI_CH3_CTRL_SW_RDY BIT(5) -+#define ESPI_CH3_STS 0x404 -+#define ESPI_CH3_INT_STS 0x408 -+#define ESPI_CH3_INT_STS_RX_CMPLT BIT(0) -+#define ESPI_CH3_INT_EN 0x40c -+#define ESPI_CH3_INT_EN_RX_CMPLT BIT(0) -+#define ESPI_CH3_RX_DMAL 0x410 -+#define ESPI_CH3_RX_DMAH 0x414 -+#define ESPI_CH3_RX_CTRL 0x418 -+#define ESPI_CH3_RX_CTRL_SERV_PEND BIT(31) -+#define ESPI_CH3_RX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_CH3_RX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_CH3_RX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_CH3_RX_DATA 0x41c -+#define ESPI_CH3_TX_DMAL 0x420 -+#define ESPI_CH3_TX_DMAH 0x424 -+#define ESPI_CH3_TX_CTRL 0x428 -+#define ESPI_CH3_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_CH3_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_CH3_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_CH3_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_CH3_TX_DATA 0x42c -+#define ESPI_CH3_EDAF_TADDRL 0x430 -+#define ESPI_CH3_EDAF_TADDRH 0x434 -+#define ESPI_CH3_EDAF_MASKL 0x438 -+#define ESPI_CH3_EDAF_MASKH 0x43c -+#define ESPI_CH3_WPROT0 0x4f8 -+#define ESPI_CH3_WPROT1 0x4fc ++ if (oob->dma.enable) { ++ dmam_free_coherent(dev, sizeof(struct ast2700_espi_oob_dma_tx_desc) * OOB_DMA_DESC_NUM, ++ oob->dma.txd_virt, oob->dma.txd_addr); ++ dmam_free_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, ++ oob->dma.tx_virt, oob->dma.tx_addr); ++ dmam_free_coherent(dev, sizeof(struct ast2700_espi_oob_dma_rx_desc) * OOB_DMA_DESC_NUM, ++ oob->dma.rxd_virt, oob->dma.rxd_addr); ++ dmam_free_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, ++ oob->dma.rx_virt, oob->dma.rx_addr); ++ } + -+/* eDAF filter registers */ -+#define ESPI_EDAF_FLTR_SADDR0 0x510 -+#define ESPI_EDAF_FLTR_EADDR0 0x514 -+#define ESPI_EDAF_FLTR_SADDR1 0x518 -+#define ESPI_EDAF_FLTR_EADDR1 0x51c -+#define ESPI_EDAF_FLTR_SADDR2 0x520 -+#define ESPI_EDAF_FLTR_EADDR2 0x524 -+#define ESPI_EDAF_FLTR_SADDR3 0x528 -+#define ESPI_EDAF_FLTR_EADDR3 0x52c -+#define ESPI_EDAF_FLTR_SADDR4 0x530 -+#define ESPI_EDAF_FLTR_EADDR4 0x534 -+#define ESPI_EDAF_FLTR_SADDR5 0x538 -+#define ESPI_EDAF_FLTR_EADDR5 0x53c -+#define ESPI_EDAF_FLTR_SADDR6 0x540 -+#define ESPI_EDAF_FLTR_EADDR6 0x544 -+#define ESPI_EDAF_FLTR_SADDR7 0x548 -+#define ESPI_EDAF_FLTR_EADDR7 0x54c -+#define ESPI_EDAF_FLTR_SADDR8 0x550 -+#define ESPI_EDAF_FLTR_EADDR8 0x554 -+#define ESPI_EDAF_FLTR_SADDR9 0x558 -+#define ESPI_EDAF_FLTR_EADDR9 0x55c -+#define ESPI_EDAF_FLTR_SADDR10 0x560 -+#define ESPI_EDAF_FLTR_EADDR10 0x564 -+#define ESPI_EDAF_FLTR_SADDR11 0x568 -+#define ESPI_EDAF_FLTR_EADDR11 0x56c -+#define ESPI_EDAF_FLTR_SADDR12 0x570 -+#define ESPI_EDAF_FLTR_EADDR12 0x574 -+#define ESPI_EDAF_FLTR_SADDR13 0x578 -+#define ESPI_EDAF_FLTR_EADDR13 0x57c -+#define ESPI_EDAF_FLTR_SADDR14 0x580 -+#define ESPI_EDAF_FLTR_EADDR14 0x584 -+#define ESPI_EDAF_FLTR_SADDR15 0x588 -+#define ESPI_EDAF_FLTR_EADDR15 0x58c -+#define ESPI_EDAF_WPROT0 0x5f8 -+#define ESPI_EDAF_WPROT1 0x5fc ++ mutex_destroy(&oob->tx_mtx); ++ mutex_destroy(&oob->rx_mtx); + -+/* MMBI registers */ -+#define ESPI_MMBI_CTRL 0x800 -+#define ESPI_MMBI_CTRL_INST_NUM GENMASK(6, 4) -+#define ESPI_MMBI_CTRL_EN BIT(0) -+#define ESPI_MMBI_INT_STS 0x808 -+#define ESPI_MMBI_INT_EN 0x80c -+#define ESPI_MMBI_HOST_RWP(x) (0x810 + ((x) << 3)) ++ misc_deregister(&oob->mdev); + -+enum ast2700_edaf_mode { -+ EDAF_MODE_MIX, -+ EDAF_MODE_SW, -+ EDAF_MODE_HW, -+ EDAF_MODES, -+}; ++ return 0; ++} + -+#endif -diff --git a/drivers/soc/aspeed/ast2700-otp.c b/drivers/soc/aspeed/ast2700-otp.c ---- a/drivers/soc/aspeed/ast2700-otp.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/ast2700-otp.c 2025-12-23 10:16:21.127032619 +0000 -@@ -0,0 +1,565 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Copyright 2024 Aspeed Technology Inc. -+ */ ++/* flash channel (CH3) */ ++static long ast2700_espi_flash_get_rx(struct file *fp, ++ struct aspeed_espi_flash *flash, ++ struct aspeed_espi_ioc *ioc) ++{ ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ unsigned long flags; ++ u32 pkt_len; ++ u8 *pkt; ++ int i, rc; + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++ rc = 0; + -+static DEFINE_SPINLOCK(otp_state_lock); ++ espi = container_of(flash, struct aspeed_espi, flash); + -+/*********************** -+ * * -+ * OTP regs definition * -+ * * -+ ***********************/ -+#define OTP_REG_SIZE 0x200 ++ if (fp->f_flags & O_NONBLOCK) { ++ if (!mutex_trylock(&flash->rx_mtx)) ++ return -EAGAIN; + -+#define OTP_PASSWD 0x349fe38a -+#define OTP_CMD_READ 0x23b1e361 -+#define OTP_CMD_PROG 0x23b1e364 -+#define OTP_CMD_PROG_MULTI 0x23b1e365 -+#define OTP_CMD_CMP 0x23b1e363 -+#define OTP_CMD_BIST 0x23b1e368 ++ if (!flash->rx_ready) { ++ rc = -ENODATA; ++ goto unlock_mtx_n_out; ++ } ++ } else { ++ mutex_lock(&flash->rx_mtx); + -+#define OTP_CMD_OFFSET 0x20 -+#define OTP_MASTER OTP_M0 ++ if (!flash->rx_ready) { ++ rc = wait_event_interruptible(flash->wq, flash->rx_ready); ++ if (rc == -ERESTARTSYS) { ++ rc = -EINTR; ++ goto unlock_mtx_n_out; ++ } ++ } ++ } + -+#define OTP_KEY 0x0 -+#define OTP_CMD (OTP_MASTER * OTP_CMD_OFFSET + 0x4) -+#define OTP_WDATA_0 (OTP_MASTER * OTP_CMD_OFFSET + 0x8) -+#define OTP_WDATA_1 (OTP_MASTER * OTP_CMD_OFFSET + 0xc) -+#define OTP_WDATA_2 (OTP_MASTER * OTP_CMD_OFFSET + 0x10) -+#define OTP_WDATA_3 (OTP_MASTER * OTP_CMD_OFFSET + 0x14) -+#define OTP_STATUS (OTP_MASTER * OTP_CMD_OFFSET + 0x18) -+#define OTP_ADDR (OTP_MASTER * OTP_CMD_OFFSET + 0x1c) -+#define OTP_RDATA (OTP_MASTER * OTP_CMD_OFFSET + 0x20) ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ reg = readl(espi->regs + ESPI_CH3_RX_CTRL); ++ cyc = FIELD_GET(ESPI_CH3_RX_CTRL_CYC, reg); ++ tag = FIELD_GET(ESPI_CH3_RX_CTRL_TAG, reg); ++ len = FIELD_GET(ESPI_CH3_RX_CTRL_LEN, reg); + -+#define OTP_DBG00 0x0C4 -+#define OTP_DBG01 0x0C8 -+#define OTP_MASTER_PID 0x0D0 -+#define OTP_ECC_EN 0x0D4 -+#define OTP_CMD_LOCK 0x0D8 -+#define OTP_SW_RST 0x0DC -+#define OTP_SLV_ID 0x0E0 -+#define OTP_PMC_CQ 0x0E4 -+#define OTP_FPGA 0x0EC -+#define OTP_CLR_FPGA 0x0F0 -+#define OTP_REGION_ROM_PATCH 0x100 -+#define OTP_REGION_OTPCFG 0x104 -+#define OTP_REGION_OTPSTRAP 0x108 -+#define OTP_REGION_OTPSTRAP_EXT 0x10C -+#define OTP_REGION_SECURE0 0x120 -+#define OTP_REGION_SECURE0_RANGE 0x124 -+#define OTP_REGION_SECURE1 0x128 -+#define OTP_REGION_SECURE1_RANGE 0x12C -+#define OTP_REGION_SECURE2 0x130 -+#define OTP_REGION_SECURE2_RANGE 0x134 -+#define OTP_REGION_SECURE3 0x138 -+#define OTP_REGION_SECURE3_RANGE 0x13C -+#define OTP_REGION_USR0 0x140 -+#define OTP_REGION_USR0_RANGE 0x144 -+#define OTP_REGION_USR1 0x148 -+#define OTP_REGION_USR1_RANGE 0x14C -+#define OTP_REGION_USR2 0x150 -+#define OTP_REGION_USR2_RANGE 0x154 -+#define OTP_REGION_USR3 0x158 -+#define OTP_REGION_USR3_RANGE 0x15C -+#define OTP_REGION_CALIPTRA_0 0x160 -+#define OTP_REGION_CALIPTRA_0_RANGE 0x164 -+#define OTP_REGION_CALIPTRA_1 0x168 -+#define OTP_REGION_CALIPTRA_1_RANGE 0x16C -+#define OTP_REGION_CALIPTRA_2 0x170 -+#define OTP_REGION_CALIPTRA_2_RANGE 0x174 -+#define OTP_REGION_CALIPTRA_3 0x178 -+#define OTP_REGION_CALIPTRA_3_RANGE 0x17C -+#define OTP_RBP_SOC_SVN 0x180 -+#define OTP_RBP_SOC_KEYRETIRE 0x184 -+#define OTP_RBP_CALIP_SVN 0x188 -+#define OTP_RBP_CALIP_KEYRETIRE 0x18C -+#define OTP_PUF 0x1A0 -+#define OTP_MASTER_ID 0x1B0 -+#define OTP_MASTER_ID_EXT 0x1B4 -+#define OTP_R_MASTER_ID 0x1B8 -+#define OTP_R_MASTER_ID_EXT 0x1BC -+#define OTP_SOC_ECCKEY 0x1C0 -+#define OTP_SEC_BOOT_EN 0x1C4 -+#define OTP_SOC_KEY 0x1C8 -+#define OTP_CALPITRA_MANU_KEY 0x1CC -+#define OTP_CALPITRA_OWNER_KEY 0x1D0 -+#define OTP_FW_ID_LSB 0x1D4 -+#define OTP_FW_ID_MSB 0x1D8 -+#define OTP_CALIP_FMC_SVN 0x1DC -+#define OTP_CALIP_RUNTIME_SVN0 0x1E0 -+#define OTP_CALIP_RUNTIME_SVN1 0x1E4 -+#define OTP_CALIP_RUNTIME_SVN2 0x1E8 -+#define OTP_CALIP_RUNTIME_SVN3 0x1EC -+#define OTP_SVN_WLOCK 0x1F0 -+#define OTP_INTR_EN 0x200 -+#define OTP_INTR_STS 0x204 -+#define OTP_INTR_MID 0x208 -+#define OTP_INTR_FUNC_INFO 0x20C -+#define OTP_INTR_M_INFO 0x210 -+#define OTP_INTR_R_INFO 0x214 ++ /* ++ * calculate the length of the rest part of the ++ * eSPI packet to be read from HW and copied to ++ * user space. ++ */ ++ switch (cyc) { ++ case ESPI_FLASH_WRITE: ++ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + ++ sizeof(struct espi_flash_rwe); ++ break; ++ case ESPI_FLASH_READ: ++ case ESPI_FLASH_ERASE: ++ pkt_len = sizeof(struct espi_flash_rwe); ++ break; ++ case ESPI_FLASH_SUC_CMPLT_D_MIDDLE: ++ case ESPI_FLASH_SUC_CMPLT_D_FIRST: ++ case ESPI_FLASH_SUC_CMPLT_D_LAST: ++ case ESPI_FLASH_SUC_CMPLT_D_ONLY: ++ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + ++ sizeof(struct espi_flash_cmplt); ++ break; ++ case ESPI_FLASH_SUC_CMPLT: ++ case ESPI_FLASH_UNSUC_CMPLT: ++ pkt_len = sizeof(struct espi_flash_cmplt); ++ break; ++ default: ++ rc = -EFAULT; ++ goto unlock_mtx_n_out; ++ } + -+#define OTP_PMC 0x400 -+#define OTP_DAP 0x500 ++ if (ioc->pkt_len < pkt_len) { ++ rc = -EINVAL; ++ goto unlock_mtx_n_out; ++ } + -+/* OTP status: [0] */ -+#define OTP_STS_IDLE 0x0 -+#define OTP_STS_BUSY 0x1 ++ pkt = vmalloc(pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_mtx_n_out; ++ } + -+/* OTP cmd status: [7:4] */ -+#define OTP_GET_CMD_STS(x) (((x) & 0xF0) >> 4) -+#define OTP_STS_PASS 0x0 -+#define OTP_STS_FAIL 0x1 -+#define OTP_STS_CMP_FAIL 0x2 -+#define OTP_STS_REGION_FAIL 0x3 -+#define OTP_STS_MASTER_FAIL 0x4 ++ hdr = (struct espi_comm_hdr *)pkt; ++ hdr->cyc = cyc; ++ hdr->tag = tag; ++ hdr->len_h = len >> 8; ++ hdr->len_l = len & 0xff; + -+/* OTP ECC EN */ -+#define ECC_ENABLE 0x1 -+#define ECC_DISABLE 0x0 -+#define ECCBRP_EN BIT(0) ++ if (flash->dma.enable) { ++ memcpy(hdr + 1, flash->dma.rx_virt, pkt_len - sizeof(*hdr)); ++ } else { ++ for (i = sizeof(*hdr); i < pkt_len; ++i) ++ pkt[i] = readl(espi->regs + ESPI_CH3_RX_DATA) & 0xff; ++ } + -+#define ROM_REGION_START_ADDR 0x0 -+#define ROM_REGION_END_ADDR 0x3e0 -+#define RBP_REGION_START_ADDR ROM_REGION_END_ADDR -+#define RBP_REGION_END_ADDR 0x400 -+#define CONF_REGION_START_ADDR RBP_REGION_END_ADDR -+#define CONF_REGION_END_ADDR 0x420 -+#define STRAP_REGION_START_ADDR CONF_REGION_END_ADDR -+#define STRAP_REGION_END_ADDR 0x430 -+#define STRAPEXT_REGION_START_ADDR STRAP_REGION_END_ADDR -+#define STRAPEXT_REGION_END_ADDR 0x440 -+#define USER_REGION_START_ADDR STRAPEXT_REGION_END_ADDR -+#define USER_REGION_END_ADDR 0x1000 -+#define SEC_REGION_START_ADDR USER_REGION_END_ADDR -+#define SEC_REGION_END_ADDR 0x1c00 -+#define CAL_REGION_START_ADDR SEC_REGION_END_ADDR -+#define CAL_REGION_END_ADDR 0x1f80 -+#define SW_PUF_REGION_START_ADDR CAL_REGION_END_ADDR -+#define SW_PUF_REGION_END_ADDR 0x1fc0 -+#define HW_PUF_REGION_START_ADDR SW_PUF_REGION_END_ADDR -+#define HW_PUF_REGION_END_ADDR 0x2000 ++ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } + -+#define OTP_MEMORY_SIZE (HW_PUF_REGION_END_ADDR * 2) ++ spin_lock_irqsave(&flash->lock, flags); + -+#define OTP_TIMEOUT_US 10000 ++ writel(ESPI_CH3_RX_CTRL_SERV_PEND, espi->regs + ESPI_CH3_RX_CTRL); ++ flash->rx_ready = 0; + -+/* OTPSTRAP */ -+#define OTPSTRAP0_ADDR STRAP_REGION_START_ADDR -+#define OTPSTRAP14_ADDR (OTPSTRAP0_ADDR + 0xe) ++ spin_unlock_irqrestore(&flash->lock, flags); + -+#define OTPTOOL_VERSION(a, b, c) (((a) << 24) + ((b) << 12) + (c)) -+#define OTPTOOL_VERSION_MAJOR(x) (((x) >> 24) & 0xff) -+#define OTPTOOL_VERSION_PATCHLEVEL(x) (((x) >> 12) & 0xfff) -+#define OTPTOOL_VERSION_SUBLEVEL(x) ((x) & 0xfff) -+#define OTPTOOL_COMPT_VERSION 2 ++ rc = 0; + -+enum otp_error_code { -+ OTP_SUCCESS, -+ OTP_READ_FAIL, -+ OTP_PROG_FAIL, -+ OTP_CMP_FAIL, -+}; ++free_n_out: ++ vfree(pkt); + -+enum aspeed_otp_master_id { -+ OTP_M0 = 0, -+ OTP_M1, -+ OTP_M2, -+ OTP_M3, -+ OTP_M4, -+ OTP_M5, -+ OTP_MID_MAX, -+}; ++unlock_mtx_n_out: ++ mutex_unlock(&flash->rx_mtx); + -+struct aspeed_otp { -+ struct miscdevice miscdev; -+ struct device *dev; -+ void __iomem *base; -+ u32 chip_revid0; -+ u32 chip_revid1; -+ bool is_open; -+ int gbl_ecc_en; -+ u8 *data; -+}; ++ return rc; ++} + -+enum otp_ioctl_cmds { -+ GET_ECC_STATUS = 1, -+ SET_ECC_ENABLE, -+}; ++static long ast2700_espi_flash_put_tx(struct file *fp, ++ struct aspeed_espi_flash *flash, ++ struct aspeed_espi_ioc *ioc) ++{ ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ u8 *pkt; ++ int i, rc; + -+enum otp_ecc_codes { -+ OTP_ECC_MISMATCH = -1, -+ OTP_ECC_DISABLE = 0, -+ OTP_ECC_ENABLE = 1, -+}; ++ espi = container_of(flash, struct aspeed_espi, flash); + -+static void otp_unlock(struct device *dev) -+{ -+ struct aspeed_otp *ctx = dev_get_drvdata(dev); ++ if (!mutex_trylock(&flash->tx_mtx)) ++ return -EAGAIN; + -+ writel(OTP_PASSWD, ctx->base + OTP_KEY); -+} ++ reg = readl(espi->regs + ESPI_CH3_TX_CTRL); ++ if (reg & ESPI_CH3_TX_CTRL_TRIG_PEND) { ++ rc = -EBUSY; ++ goto unlock_mtx_n_out; ++ } + -+static void otp_lock(struct device *dev) -+{ -+ struct aspeed_otp *ctx = dev_get_drvdata(dev); ++ pkt = vmalloc(ioc->pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_mtx_n_out; ++ } + -+ writel(0x1, ctx->base + OTP_KEY); -+} ++ hdr = (struct espi_comm_hdr *)pkt; + -+static int wait_complete(struct device *dev) -+{ -+ struct aspeed_otp *ctx = dev_get_drvdata(dev); -+ int ret; -+ u32 val; ++ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } + -+ ret = readl_poll_timeout(ctx->base + OTP_STATUS, val, (val == 0x0), -+ 1, OTP_TIMEOUT_US); -+ if (ret) -+ dev_warn(dev, "timeout. sts:0x%x\n", val); ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ if (flash->dma.enable) { ++ memcpy(flash->dma.tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); ++ dma_wmb(); ++ } else { ++ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) ++ writel(pkt[i], espi->regs + ESPI_CH3_TX_DATA); ++ } + -+ return ret; -+} ++ cyc = hdr->cyc; ++ tag = hdr->tag; ++ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + -+static int otp_read_data(struct aspeed_otp *ctx, u32 offset, u16 *data) -+{ -+ struct device *dev = ctx->dev; -+ int ret; ++ reg = FIELD_PREP(ESPI_CH3_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_CH3_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_CH3_TX_CTRL_LEN, len) ++ | ESPI_CH3_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_CH3_TX_CTRL); + -+ writel(ctx->gbl_ecc_en, ctx->base + OTP_ECC_EN); -+ writel(offset, ctx->base + OTP_ADDR); -+ writel(OTP_CMD_READ, ctx->base + OTP_CMD); -+ ret = wait_complete(dev); -+ if (ret) -+ return OTP_READ_FAIL; ++ rc = 0; + -+ data[0] = readl(ctx->base + OTP_RDATA); ++free_n_out: ++ vfree(pkt); + -+ return 0; ++unlock_mtx_n_out: ++ mutex_unlock(&flash->tx_mtx); ++ ++ return rc; +} + -+static int otp_prog_data(struct aspeed_otp *ctx, u32 offset, u16 data) ++static long ast2700_espi_flash_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ -+ struct device *dev = ctx->dev; -+ int ret; ++ struct aspeed_espi_flash *flash; ++ struct aspeed_espi_ioc ioc; + -+ writel(ctx->gbl_ecc_en, ctx->base + OTP_ECC_EN); -+ writel(offset, ctx->base + OTP_ADDR); -+ writel(data, ctx->base + OTP_WDATA_0); -+ writel(OTP_CMD_PROG, ctx->base + OTP_CMD); -+ ret = wait_complete(dev); -+ if (ret) -+ return OTP_PROG_FAIL; ++ flash = container_of(fp->private_data, struct aspeed_espi_flash, mdev); + -+ return 0; ++ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) ++ return -EFAULT; ++ ++ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) ++ return -EINVAL; ++ ++ switch (cmd) { ++ case ASPEED_ESPI_FLASH_GET_RX: ++ return ast2700_espi_flash_get_rx(fp, flash, &ioc); ++ case ASPEED_ESPI_FLASH_PUT_TX: ++ return ast2700_espi_flash_put_tx(fp, flash, &ioc); ++ }; ++ ++ return -EINVAL; +} + -+static int otp_prog_multi_data(struct aspeed_otp *ctx, u32 offset, u32 *data, int count) ++static const struct file_operations ast2700_espi_flash_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = ast2700_espi_flash_ioctl, ++}; ++ ++static void ast2700_espi_flash_isr(struct aspeed_espi *espi) +{ -+ struct device *dev = ctx->dev; -+ int ret; ++ struct aspeed_espi_flash *flash; ++ unsigned long flags; ++ u32 sts; + -+ writel(ctx->gbl_ecc_en, ctx->base + OTP_ECC_EN); -+ writel(offset, ctx->base + OTP_ADDR); -+ for (int i = 0; i < count; i++) -+ writel(data[i], ctx->base + OTP_WDATA_0 + 4 * i); ++ flash = &espi->flash; + -+ writel(OTP_CMD_PROG_MULTI, ctx->base + OTP_CMD); -+ ret = wait_complete(dev); -+ if (ret) -+ return OTP_PROG_FAIL; ++ sts = readl(espi->regs + ESPI_CH3_INT_STS); + -+ return 0; -+} ++ if (sts & ESPI_CH3_INT_STS_RX_CMPLT) { ++ writel(ESPI_CH3_INT_STS_RX_CMPLT, espi->regs + ESPI_CH3_INT_STS); + -+static int aspeed_otp_read(struct aspeed_otp *ctx, int offset, -+ void *buf, int size) -+{ -+ struct device *dev = ctx->dev; -+ u16 *data = buf; -+ int ret; ++ spin_lock_irqsave(&flash->lock, flags); ++ flash->rx_ready = true; ++ spin_unlock_irqrestore(&flash->lock, flags); + -+ otp_unlock(dev); -+ for (int i = 0; i < size; i++) { -+ ret = otp_read_data(ctx, offset + i, data + i); -+ if (ret) { -+ dev_warn(ctx->dev, "read failed\n"); -+ break; -+ } ++ wake_up_interruptible(&flash->wq); + } -+ -+ otp_lock(dev); -+ return ret; +} + -+static int aspeed_otp_write(struct aspeed_otp *ctx, int offset, -+ const void *buf, int size) ++static void ast2700_espi_flash_reset(struct aspeed_espi *espi) +{ -+ struct device *dev = ctx->dev; -+ u32 *data32 = (u32 *)buf; -+ u16 *data = (u16 *)buf; -+ int ret; ++ u32 reg; ++ u64 mask; ++ struct aspeed_espi_flash *flash = &espi->flash; + -+ otp_unlock(dev); ++ writel(0x0, espi->regs + ESPI_CH3_INT_EN); ++ writel(0xffffffff, espi->regs + ESPI_CH3_INT_STS); + -+ if (size == 1) -+ ret = otp_prog_data(ctx, offset, data[0]); -+ else -+ ret = otp_prog_multi_data(ctx, offset, data32, size / 2); ++ reg = readl(espi->regs + ESPI_CH3_CTRL); ++ reg &= ~(ESPI_CH3_CTRL_TX_RST ++ | ESPI_CH3_CTRL_RX_RST ++ | ESPI_CH3_CTRL_TX_DMA_EN ++ | ESPI_CH3_CTRL_RX_DMA_EN ++ | ESPI_CH3_CTRL_SW_RDY); ++ writel(reg, espi->regs + ESPI_CH3_CTRL); + -+ if (ret) -+ dev_warn(ctx->dev, "prog failed\n"); ++ udelay(1); + -+ otp_lock(dev); -+ return ret; -+} ++ reg |= (ESPI_CH3_CTRL_TX_RST | ESPI_CH3_CTRL_RX_RST); ++ writel(reg, espi->regs + ESPI_CH3_CTRL); + -+static int aspeed_otp_ecc_en(struct aspeed_otp *ctx) -+{ -+ struct device *dev = ctx->dev; -+ int ret = 0; ++ if (flash->edaf.mode == EDAF_MODE_MIX) { ++ mask = ~(flash->edaf.size - 1); ++ writel(upper_32_bits(mask), espi->regs + ESPI_CH3_EDAF_MASKH); ++ writel(mask & 0xffffffff, espi->regs + ESPI_CH3_EDAF_MASKL); ++ writel(upper_32_bits(flash->edaf.taddr), espi->regs + ESPI_CH3_EDAF_TADDRH); ++ writel(flash->edaf.taddr & 0xffffffff, espi->regs + ESPI_CH3_EDAF_TADDRL); ++ } + -+ /* Check ecc is already enabled */ -+ if (ctx->gbl_ecc_en == 1) -+ return 0; ++ reg = readl(espi->regs + ESPI_CH3_CTRL) & ~ESPI_CH3_CTRL_EDAF_MODE; ++ reg |= FIELD_PREP(ESPI_CH3_CTRL_EDAF_MODE, flash->edaf.mode); ++ writel(reg, espi->regs + ESPI_CH3_CTRL); + -+ otp_unlock(dev); ++ if (flash->dma.enable) { ++ writel(upper_32_bits(flash->dma.tx_addr), espi->regs + ESPI_CH3_TX_DMAH); ++ writel(flash->dma.tx_addr & 0xffffffff, espi->regs + ESPI_CH3_TX_DMAL); ++ writel(upper_32_bits(flash->dma.rx_addr), espi->regs + ESPI_CH3_RX_DMAH); ++ writel(flash->dma.rx_addr & 0xffffffff, espi->regs + ESPI_CH3_RX_DMAL); + -+ /* enable cfg ecc */ -+ ret = otp_prog_data(ctx, OTPSTRAP14_ADDR, 0x1); -+ if (ret) { -+ dev_warn(dev, "%s: prog failed\n", __func__); -+ goto end; ++ reg = readl(espi->regs + ESPI_CH3_CTRL) ++ | ESPI_CH3_CTRL_TX_DMA_EN ++ | ESPI_CH3_CTRL_RX_DMA_EN; ++ writel(reg, espi->regs + ESPI_CH3_CTRL); + } + -+ ctx->gbl_ecc_en = 1; -+end: -+ otp_lock(dev); ++ writel(ESPI_CH3_INT_EN_RX_CMPLT, espi->regs + ESPI_CH3_INT_EN); + -+ return ret; ++ reg = readl(espi->regs + ESPI_CH3_CTRL) | ESPI_CH3_CTRL_SW_RDY; ++ writel(reg, espi->regs + ESPI_CH3_CTRL); +} + -+static long aspeed_otp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++int ast2700_espi_flash_probe(struct aspeed_espi *espi) +{ -+ struct miscdevice *c = file->private_data; -+ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); -+ void __user *argp = (void __user *)arg; -+ struct otp_revid revid; -+ struct otp_read rdata; -+ struct otp_prog pdata; -+ int ret = 0; ++ struct aspeed_espi_flash *flash; ++ struct device_node *np; ++ struct resource res; ++ struct device *dev; ++ void *virt; ++ int rc; + -+ switch (cmd) { -+ case ASPEED_OTP_READ_DATA: -+ if (copy_from_user(&rdata, argp, sizeof(struct otp_read))) -+ return -EFAULT; ++ dev = espi->dev; + -+ ret = aspeed_otp_read(ctx, rdata.offset, ctx->data, rdata.len); -+ if (ret) -+ return -EFAULT; ++ flash = &espi->flash; + -+ if (copy_to_user(rdata.data, ctx->data, rdata.len * 2)) -+ return -EFAULT; ++ init_waitqueue_head(&flash->wq); + -+ break; ++ spin_lock_init(&flash->lock); + -+ case ASPEED_OTP_PROG_DATA: -+ if (copy_from_user(&pdata, argp, sizeof(struct otp_prog))) -+ return -EFAULT; ++ mutex_init(&flash->tx_mtx); ++ mutex_init(&flash->rx_mtx); + -+ ret = aspeed_otp_write(ctx, pdata.w_offset, pdata.data, pdata.len); -+ break; ++ flash->edaf.mode = EDAF_MODE_HW; + -+ case ASPEED_OTP_GET_ECC: -+ if (copy_to_user(argp, &ctx->gbl_ecc_en, sizeof(ctx->gbl_ecc_en))) -+ return -EFAULT; -+ break; ++ of_property_read_u32(dev->of_node, "flash-edaf-mode", &flash->edaf.mode); ++ dev_info(dev, "eDAF mode: 0x%x\n", flash->edaf.mode); ++ if (flash->edaf.mode == EDAF_MODE_MIX) { ++ np = of_parse_phandle(dev->of_node, "flash-edaf-tgt-addr", 0); ++ if (!np || of_address_to_resource(np, 0, &res)) { ++ dev_err(dev, "cannot get eDAF memory region\n"); ++ return -ENODEV; ++ } + -+ case ASPEED_OTP_SET_ECC: -+ ret = aspeed_otp_ecc_en(ctx); -+ break; ++ of_node_put(np); + -+ case ASPEED_OTP_GET_REVID: -+ revid.revid0 = ctx->chip_revid0; -+ revid.revid1 = ctx->chip_revid1; -+ if (copy_to_user(argp, &revid, sizeof(struct otp_revid))) -+ return -EFAULT; -+ break; -+ default: -+ dev_warn(ctx->dev, "cmd 0x%x is not supported\n", cmd); -+ break; ++ flash->edaf.taddr = res.start; ++ flash->edaf.size = resource_size(&res); ++ ++ virt = devm_ioremap_resource(dev, &res); ++ if (!virt) { ++ dev_err(dev, "cannot map eDAF memory region\n"); ++ return -ENOMEM; ++ } + } + -+ return ret; ++ flash->dma.enable = of_property_read_bool(dev->of_node, "flash-dma-mode"); ++ if (flash->dma.enable) { ++ flash->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.tx_addr, GFP_KERNEL); ++ if (!flash->dma.tx_virt) { ++ dev_err(dev, "cannot allocate DMA TX buffer\n"); ++ return -ENOMEM; ++ } ++ ++ flash->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.rx_addr, GFP_KERNEL); ++ if (!flash->dma.rx_virt) { ++ dev_err(dev, "cannot allocate DMA RX buffer\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ flash->mdev.parent = dev; ++ flash->mdev.minor = MISC_DYNAMIC_MINOR; ++ flash->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-flash%d", DEVICE_NAME, espi->dev_id); ++ flash->mdev.fops = &ast2700_espi_flash_fops; ++ rc = misc_register(&flash->mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", flash->mdev.name); ++ return rc; ++ } ++ ++ ast2700_espi_flash_reset(espi); ++ ++ return 0; +} + -+static int aspeed_otp_ecc_init(struct device *dev) ++int ast2700_espi_flash_remove(struct aspeed_espi *espi) +{ -+ struct aspeed_otp *ctx = dev_get_drvdata(dev); -+ int ret; -+ u32 val; ++ struct aspeed_espi_flash *flash; ++ struct device *dev; ++ u32 reg; + -+ otp_unlock(dev); ++ dev = espi->dev; + -+ /* Check cfg_ecc_en */ -+ writel(0, ctx->base + OTP_ECC_EN); -+ writel(OTPSTRAP14_ADDR, ctx->base + OTP_ADDR); -+ writel(OTP_CMD_READ, ctx->base + OTP_CMD); -+ ret = wait_complete(dev); -+ if (ret) -+ return OTP_READ_FAIL; ++ flash = &espi->flash; + -+ val = readl(ctx->base + OTP_RDATA); -+ if (val & 0x1) -+ ctx->gbl_ecc_en = 0x1; -+ else -+ ctx->gbl_ecc_en = 0x0; ++ writel(0x0, espi->regs + ESPI_CH3_INT_EN); + -+ otp_lock(dev); ++ reg = readl(espi->regs + ESPI_CH3_CTRL); ++ reg &= ~(ESPI_CH3_CTRL_TX_DMA_EN ++ | ESPI_CH3_CTRL_RX_DMA_EN ++ | ESPI_CH3_CTRL_SW_RDY); ++ writel(reg, espi->regs + ESPI_CH3_CTRL); ++ ++ if (flash->dma.enable) { ++ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.tx_virt, flash->dma.tx_addr); ++ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.rx_virt, flash->dma.rx_addr); ++ } ++ ++ mutex_destroy(&flash->tx_mtx); ++ mutex_destroy(&flash->rx_mtx); ++ ++ misc_deregister(&flash->mdev); + + return 0; +} + -+static int aspeed_otp_open(struct inode *inode, struct file *file) ++/* global control */ ++irqreturn_t ast2700_espi_isr(int irq, void *arg) +{ -+ struct miscdevice *c = file->private_data; -+ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); ++ u32 sts; ++ struct aspeed_espi *espi = (struct aspeed_espi *)arg; + -+ spin_lock(&otp_state_lock); ++ sts = readl(espi->regs + ESPI_INT_STS); ++ if (!sts) ++ return IRQ_NONE; + -+ if (ctx->is_open) { -+ spin_unlock(&otp_state_lock); -+ return -EBUSY; -+ } ++ if (sts & ESPI_INT_STS_CH0) ++ ast2700_espi_perif_isr(espi); + -+ ctx->is_open = true; ++ if (sts & ESPI_INT_STS_CH1) ++ ast2700_espi_vw_isr(espi); ++ ++ if (sts & ESPI_INT_STS_CH2) ++ ast2700_espi_oob_isr(espi); ++ ++ if (sts & ESPI_INT_STS_CH3) ++ ast2700_espi_flash_isr(espi); + -+ spin_unlock(&otp_state_lock); ++ if (sts & ESPI_INT_STS_RST_DEASSERT) { ++ ast2700_espi_perif_sw_reset(espi); ++ ast2700_espi_perif_reset(espi); ++ ast2700_espi_vw_reset(espi); ++ ast2700_espi_oob_reset(espi); ++ ast2700_espi_flash_reset(espi); ++ writel(ESPI_INT_STS_RST_DEASSERT, espi->regs + ESPI_INT_STS); ++ } + -+ return 0; ++ return IRQ_HANDLED; +} + -+static int aspeed_otp_release(struct inode *inode, struct file *file) ++void ast2700_espi_pre_init(struct aspeed_espi *espi) +{ -+ struct miscdevice *c = file->private_data; -+ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); ++ struct device *dev; ++ struct regmap *scu1; ++ u32 reg; ++ int rc; + -+ spin_lock(&otp_state_lock); ++ dev = espi->dev; + -+ ctx->is_open = false; ++ scu1 = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); ++ if (IS_ERR(scu1)) { ++ dev_err(dev, "failed to find SCU1 regmap, error %ld\n", PTR_ERR(scu1)); ++ return; ++ } ++ rc = regmap_update_bits(scu1, SCU1_DDR, ++ SCU1_DDR_DIS_ESPI0_AHB | SCU1_DDR_DIS_ESPI1_AHB, ++ 0); ++ if (!rc) { ++ dev_err(dev, "failed to update SCU1 regmap, error %d\n", rc); ++ return; ++ } + -+ spin_unlock(&otp_state_lock); ++ espi->dev_id = ida_alloc(&ast2700_espi_ida, GFP_KERNEL); ++ if (espi->dev_id < 0) { ++ dev_err(dev, "cannote allocate device ID, error %d\n", espi->dev_id); ++ return; ++ } + -+ return 0; ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~ESPI_INT_EN_RST_DEASSERT; ++ writel(reg, espi->regs + ESPI_INT_EN); +} + -+static const struct file_operations otp_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = aspeed_otp_ioctl, -+ .open = aspeed_otp_open, -+ .release = aspeed_otp_release, -+}; -+ -+static const struct of_device_id aspeed_otp_of_matches[] = { -+ { .compatible = "aspeed,ast2700-otp" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, aspeed_otp_of_matches); -+ -+static int aspeed_otp_probe(struct platform_device *pdev) ++void ast2700_espi_post_init(struct aspeed_espi *espi) +{ -+ struct device *dev = &pdev->dev; -+ struct regmap *scu0, *scu1; -+ struct aspeed_otp *priv; -+ struct resource *res; -+ int rc; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) { -+ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM\n"); -+ return -ENOENT; -+ } ++ u32 reg; + -+ priv->base = devm_ioremap_resource(&pdev->dev, res); -+ if (!priv->base) -+ return -EIO; ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg |= ESPI_INT_EN_RST_DEASSERT; ++ writel(reg, espi->regs + ESPI_INT_EN); ++} + -+ scu0 = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu0"); -+ scu1 = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu1"); -+ if (IS_ERR(scu0) || IS_ERR(scu1)) { -+ dev_err(dev, "failed to find SCU regmap\n"); -+ return PTR_ERR(scu0) || PTR_ERR(scu1); -+ } ++void ast2700_espi_deinit(struct aspeed_espi *espi) ++{ ++ struct device *dev; ++ u32 reg; + -+ regmap_read(scu0, 0x0, &priv->chip_revid0); -+ regmap_read(scu1, 0x0, &priv->chip_revid1); ++ dev = espi->dev; + -+ priv->dev = dev; -+ dev_set_drvdata(dev, priv); ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~ESPI_INT_EN_RST_DEASSERT; ++ writel(reg, espi->regs + ESPI_INT_EN); ++} +diff --git a/drivers/soc/aspeed/espi/ast2700-espi.h b/drivers/soc/aspeed/espi/ast2700-espi.h +--- a/drivers/soc/aspeed/espi/ast2700-espi.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2700-espi.h 2026-04-08 18:03:48.313705265 +0000 +@@ -0,0 +1,296 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Register definitions for Aspeed AST2700 eSPI controller ++ * Copyright 2023 Aspeed Technology Inc. ++ */ ++#ifndef _AST2700_ESPI_H_ ++#define _AST2700_ESPI_H_ + -+ /* OTP ECC init */ -+ rc = aspeed_otp_ecc_init(dev); -+ if (rc) -+ return -EIO; ++#include ++#include "aspeed-espi.h" + -+ priv->data = kmalloc(OTP_MEMORY_SIZE, GFP_KERNEL); -+ if (!priv->data) -+ return -ENOMEM; ++/* SCU regiseters */ ++#define SCU1_DDR 0x0c8 ++#define SCU1_DDR_DIS_ESPI0_AHB BIT(0) ++#define SCU1_DDR_DIS_ESPI1_AHB BIT(1) + -+ /* Set up the miscdevice */ -+ priv->miscdev.minor = MISC_DYNAMIC_MINOR; -+ priv->miscdev.name = "aspeed-otp"; -+ priv->miscdev.fops = &otp_fops; ++/* global registers */ ++#define ESPI_CTRL 0x000 ++#define ESPI_STS 0x004 ++#define ESPI_INT_STS 0x008 ++#define ESPI_INT_STS_RST_DEASSERT BIT(31) ++#define ESPI_INT_STS_RST_ASSERT BIT(30) ++#define ESPI_INT_STS_CH3 BIT(3) ++#define ESPI_INT_STS_CH2 BIT(2) ++#define ESPI_INT_STS_CH1 BIT(1) ++#define ESPI_INT_STS_CH0 BIT(0) ++#define ESPI_INT_EN 0x00c ++#define ESPI_INT_EN_RST_DEASSERT BIT(31) ++#define ESPI_INT_EN_RST_ASSERT BIT(30) ++#define ESPI_DEV_ID 0x010 ++#define ESPI_CAP_GEN 0x014 ++#define ESPI_CAP_GEN_RTC_SUP BIT(29) ++#define ESPI_CAP_CH0 0x018 ++#define ESPI_CAP_CH1 0x01c ++#define ESPI_CAP_CH2 0x020 ++#define ESPI_CAP_CH3_0 0x024 ++#define ESPI_CAP_CH3_1 0x028 ++#define ESPI_DEV_STS 0x030 ++#define ESPI_DBG_CTRL 0x034 ++#define ESPI_DBG_ADDRL 0x038 ++#define ESPI_DBG_ADDRH 0x03c ++#define ESPI_DBG_CMD 0x040 ++#define ESPI_DBG_RES 0x044 ++#define ESPI_CH_ACC_CTRL 0x04c ++#define ESPI_CH_ACC_OFST1 0x050 ++#define ESPI_CH_ACC_OFST2 0x054 ++#define ESPI_WPROT0 0x0f8 ++#define ESPI_WPROT1 0x0fc + -+ /* Register the device */ -+ rc = misc_register(&priv->miscdev); -+ if (rc) { -+ dev_err(dev, "Unable to register device\n"); -+ return rc; -+ } ++/* peripheral channel (ch0) registers */ ++#define ESPI_CH0_CTRL 0x100 ++#define ESPI_CH0_CTRL_NP_TX_RST BIT(31) ++#define ESPI_CH0_CTRL_NP_RX_RST BIT(30) ++#define ESPI_CH0_CTRL_PC_TX_RST BIT(29) ++#define ESPI_CH0_CTRL_PC_RX_RST BIT(28) ++#define ESPI_CH0_CTRL_NP_TX_DMA_EN BIT(19) ++#define ESPI_CH0_CTRL_PC_TX_DMA_EN BIT(17) ++#define ESPI_CH0_CTRL_PC_RX_DMA_EN BIT(16) ++#define ESPI_CH0_CTRL_MCYC_RD_DIS_WDT BIT(9) ++#define ESPI_CH0_CTRL_MCYC_WR_DIS_WDT BIT(8) ++#define ESPI_CH0_CTRL_MCYC_RD_DIS BIT(6) ++#define ESPI_CH0_CTRL_MCYC_WR_DIS BIT(4) ++#define ESPI_CH0_CTRL_SW_RDY BIT(1) ++#define ESPI_CH0_STS 0x104 ++#define ESPI_CH0_INT_STS 0x108 ++#define ESPI_CH0_INT_STS_PC_RX_CMPLT BIT(0) ++#define ESPI_CH0_INT_EN 0x10c ++#define ESPI_CH0_INT_EN_PC_RX_CMPLT BIT(0) ++#define ESPI_CH0_PC_RX_DMAL 0x110 ++#define ESPI_CH0_PC_RX_DMAH 0x114 ++#define ESPI_CH0_PC_RX_CTRL 0x118 ++#define ESPI_CH0_PC_RX_CTRL_SERV_PEND BIT(31) ++#define ESPI_CH0_PC_RX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_CH0_PC_RX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_CH0_PC_RX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_CH0_PC_RX_DATA 0x11c ++#define ESPI_CH0_PC_TX_DMAL 0x120 ++#define ESPI_CH0_PC_TX_DMAH 0x124 ++#define ESPI_CH0_PC_TX_CTRL 0x128 ++#define ESPI_CH0_PC_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_CH0_PC_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_CH0_PC_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_CH0_PC_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_CH0_PC_TX_DATA 0x12c ++#define ESPI_CH0_NP_TX_DMAL 0x130 ++#define ESPI_CH0_NP_TX_DMAH 0x134 ++#define ESPI_CH0_NP_TX_CTRL 0x138 ++#define ESPI_CH0_NP_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_CH0_NP_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_CH0_NP_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_CH0_NP_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_CH0_NP_TX_DATA 0x13c ++#define ESPI_CH0_MCYC0_SADDRL 0x140 ++#define ESPI_CH0_MCYC0_SADDRH 0x144 ++#define ESPI_CH0_MCYC0_TADDRL 0x148 ++#define ESPI_CH0_MCYC0_TADDRH 0x14c ++#define ESPI_CH0_MCYC0_MASKL 0x150 ++#define ESPI_CH0_MCYC0_MASKL_EN BIT(0) ++#define ESPI_CH0_MCYC0_MASKH 0x154 ++#define ESPI_CH0_MCYC1_SADDRL 0x158 ++#define ESPI_CH0_MCYC1_SADDRH 0x15c ++#define ESPI_CH0_MCYC1_TADDRL 0x160 ++#define ESPI_CH0_MCYC1_TADDRH 0x164 ++#define ESPI_CH0_MCYC1_MASKL 0x168 ++#define ESPI_CH0_MCYC1_MASKL_EN BIT(0) ++#define ESPI_CH0_MCYC1_MASKH 0x16c ++#define ESPI_CH0_WPROT0 0x1f8 ++#define ESPI_CH0_WPROT1 0x1fc + -+ dev_info(dev, "Aspeed OTP driver successfully registered\n"); ++/* virtual wire channel (ch1) registers */ ++#define ESPI_CH1_CTRL 0x200 ++#define ESPI_CH1_CTRL_GPIO_HW BIT(9) ++#define ESPI_CH1_CTRL_SW_RDY BIT(1) ++#define ESPI_CH1_STS 0x204 ++#define ESPI_CH1_INT_STS 0x208 ++#define ESPI_CH1_INT_STS_GPIO BIT(2) ++#define ESPI_CH1_INT_STS_EVT0 BIT(0) ++#define ESPI_CH1_INT_EN 0x20c ++#define ESPI_CH1_INT_EN_GPIO BIT(2) ++#define ESPI_CH1_INT_EN_SYS_EVT0 BIT(0) ++#define ESPI_CH1_EVT0 0x210 ++#define ESPI_CH1_EVT0_PLTRSTN BIT(5) ++#define ESPI_CH1_EVT0_INT_EN 0x214 ++#define ESPI_CH1_EVT0_INT_EN_PLTRSTN BIT(5) ++#define ESPI_CH1_EVT0_INT_T0 0x218 ++#define ESPI_CH1_EVT0_INT_T0_PLTRSTN BIT(5) ++#define ESPI_CH1_EVT0_INT_T1 0x21c ++#define ESPI_CH1_EVT0_INT_T1_PLTRSTN BIT(5) ++#define ESPI_CH1_EVT0_INT_T2 0x220 ++#define ESPI_CH1_EVT0_INT_T2_PLTRSTN BIT(5) ++#define ESPI_CH1_EVT0_INT_STS 0x224 ++#define ESPI_CH1_EVT0_INT_STS_PLTRSTN BIT(5) ++#define ESPI_CH1_EVT1 0x230 ++#define ESPI_CH1_EVT1_INT_EN 0x234 ++#define ESPI_CH1_EVT1_INT_T0 0x238 ++#define ESPI_CH1_EVT1_INT_T1 0x23c ++#define ESPI_CH1_EVT1_INT_T2 0x240 ++#define ESPI_CH1_EVT1_INT_STS 0x244 ++#define ESPI_CH1_GPIO_VAL0 0x250 ++#define ESPI_CH1_GPIO_VAL1 0x254 ++#define ESPI_CH1_GPIO_DIR0 0x258 ++#define ESPI_CH1_GPIO_DIR1 0x25c ++#define ESPI_CH1_GPIO_RSTSEL0 0x260 ++#define ESPI_CH1_GPIO_RSTSEL1 0x264 ++#define ESPI_CH1_GPIO_GRP 0x268 ++#define ESPI_CH1_GP50_DIR0 0x270 ++#define ESPI_CH1_GP50_DIR1 0x274 ++#define ESPI_CH1_GP50_VAL0 0x278 ++#define ESPI_CH1_GP50_VAL1 0x27c ++#define ESPI_CH1_SW_INT 0x280 ++#define ESPI_CH1_INT_RSTSEL0 0x284 ++#define ESPI_CH1_INT_RSTSEL1 0x288 ++#define ESPI_CH1_WPROT0 0x2f8 ++#define ESPI_CH1_WPROT1 0x2fc + -+ return 0; -+} ++/* out-of-band channel (ch2) registers */ ++#define ESPI_CH2_CTRL 0x300 ++#define ESPI_CH2_CTRL_TX_RST BIT(31) ++#define ESPI_CH2_CTRL_RX_RST BIT(30) ++#define ESPI_CH2_CTRL_TX_DMA_EN BIT(17) ++#define ESPI_CH2_CTRL_RX_DMA_EN BIT(16) ++#define ESPI_CH2_CTRL_SW_RDY BIT(4) ++#define ESPI_CH2_STS 0x304 ++#define ESPI_CH2_INT_STS 0x308 ++#define ESPI_CH2_INT_STS_RX_CMPLT BIT(0) ++#define ESPI_CH2_INT_EN 0x30c ++#define ESPI_CH2_INT_EN_RX_CMPLT BIT(0) ++#define ESPI_CH2_RX_DMAL 0x310 ++#define ESPI_CH2_RX_DMAH 0x314 ++#define ESPI_CH2_RX_CTRL 0x318 ++#define ESPI_CH2_RX_CTRL_SERV_PEND BIT(31) ++#define ESPI_CH2_RX_CTRL_PEC BIT(24) ++#define ESPI_CH2_RX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_CH2_RX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_CH2_RX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_CH2_RX_DATA 0x31c ++#define ESPI_CH2_TX_DMAL 0x320 ++#define ESPI_CH2_TX_DMAH 0x324 ++#define ESPI_CH2_TX_CTRL 0x328 ++#define ESPI_CH2_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_CH2_TX_CTRL_PEC BIT(24) ++#define ESPI_CH2_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_CH2_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_CH2_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_CH2_TX_DATA 0x32c ++#define ESPI_CH2_RX_DESC_EPTR 0x330 ++#define ESPI_CH2_RX_DESC_RPTR 0x334 ++#define ESPI_CH2_RX_DESC_RPTR_UPDATE BIT(31) ++#define ESPI_CH2_RX_DESC_RPTR_RP GENMASK(11, 0) ++#define ESPI_CH2_RX_DESC_WPTR 0x338 ++#define ESPI_CH2_RX_DESC_WPTR_VALID BIT(31) ++#define ESPI_CH2_RX_DESC_WPTR_SP GENMASK(27, 16) ++#define ESPI_CH2_RX_DESC_WPTR_WP GENMASK(11, 0) ++#define ESPI_CH2_RX_DESC_TMOUT 0x33c ++#define ESPI_CH2_TX_DESC_EPTR 0x340 ++#define ESPI_CH2_TX_DESC_RPTR 0x344 ++#define ESPI_CH2_TX_DESC_RPTR_UPT BIT(31) ++#define ESPI_CH2_TX_DESC_WPTR 0x348 ++#define ESPI_CH2_TX_DESC_WPTR_VALID BIT(31) ++#define ESPI_CH2_WPROT0 0x3f8 ++#define ESPI_CH2_WPROT1 0x3fc + -+static void aspeed_otp_remove(struct platform_device *pdev) -+{ -+ struct aspeed_otp *ctx = dev_get_drvdata(&pdev->dev); ++/* flash channel (ch3) registers */ ++#define ESPI_CH3_CTRL 0x400 ++#define ESPI_CH3_CTRL_TX_RST BIT(31) ++#define ESPI_CH3_CTRL_RX_RST BIT(30) ++#define ESPI_CH3_CTRL_TX_DMA_EN BIT(17) ++#define ESPI_CH3_CTRL_RX_DMA_EN BIT(16) ++#define ESPI_CH3_CTRL_EDAF_MODE GENMASK(9, 8) ++#define ESPI_CH3_CTRL_SW_RDY BIT(5) ++#define ESPI_CH3_STS 0x404 ++#define ESPI_CH3_INT_STS 0x408 ++#define ESPI_CH3_INT_STS_RX_CMPLT BIT(0) ++#define ESPI_CH3_INT_EN 0x40c ++#define ESPI_CH3_INT_EN_RX_CMPLT BIT(0) ++#define ESPI_CH3_RX_DMAL 0x410 ++#define ESPI_CH3_RX_DMAH 0x414 ++#define ESPI_CH3_RX_CTRL 0x418 ++#define ESPI_CH3_RX_CTRL_SERV_PEND BIT(31) ++#define ESPI_CH3_RX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_CH3_RX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_CH3_RX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_CH3_RX_DATA 0x41c ++#define ESPI_CH3_TX_DMAL 0x420 ++#define ESPI_CH3_TX_DMAH 0x424 ++#define ESPI_CH3_TX_CTRL 0x428 ++#define ESPI_CH3_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_CH3_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_CH3_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_CH3_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_CH3_TX_DATA 0x42c ++#define ESPI_CH3_EDAF_TADDRL 0x430 ++#define ESPI_CH3_EDAF_TADDRH 0x434 ++#define ESPI_CH3_EDAF_MASKL 0x438 ++#define ESPI_CH3_EDAF_MASKH 0x43c ++#define ESPI_CH3_WPROT0 0x4f8 ++#define ESPI_CH3_WPROT1 0x4fc + -+ kfree(ctx->data); -+ misc_deregister(&ctx->miscdev); -+} ++/* eDAF filter registers */ ++#define ESPI_EDAF_FLTR_SADDR0 0x510 ++#define ESPI_EDAF_FLTR_EADDR0 0x514 ++#define ESPI_EDAF_FLTR_SADDR1 0x518 ++#define ESPI_EDAF_FLTR_EADDR1 0x51c ++#define ESPI_EDAF_FLTR_SADDR2 0x520 ++#define ESPI_EDAF_FLTR_EADDR2 0x524 ++#define ESPI_EDAF_FLTR_SADDR3 0x528 ++#define ESPI_EDAF_FLTR_EADDR3 0x52c ++#define ESPI_EDAF_FLTR_SADDR4 0x530 ++#define ESPI_EDAF_FLTR_EADDR4 0x534 ++#define ESPI_EDAF_FLTR_SADDR5 0x538 ++#define ESPI_EDAF_FLTR_EADDR5 0x53c ++#define ESPI_EDAF_FLTR_SADDR6 0x540 ++#define ESPI_EDAF_FLTR_EADDR6 0x544 ++#define ESPI_EDAF_FLTR_SADDR7 0x548 ++#define ESPI_EDAF_FLTR_EADDR7 0x54c ++#define ESPI_EDAF_FLTR_SADDR8 0x550 ++#define ESPI_EDAF_FLTR_EADDR8 0x554 ++#define ESPI_EDAF_FLTR_SADDR9 0x558 ++#define ESPI_EDAF_FLTR_EADDR9 0x55c ++#define ESPI_EDAF_FLTR_SADDR10 0x560 ++#define ESPI_EDAF_FLTR_EADDR10 0x564 ++#define ESPI_EDAF_FLTR_SADDR11 0x568 ++#define ESPI_EDAF_FLTR_EADDR11 0x56c ++#define ESPI_EDAF_FLTR_SADDR12 0x570 ++#define ESPI_EDAF_FLTR_EADDR12 0x574 ++#define ESPI_EDAF_FLTR_SADDR13 0x578 ++#define ESPI_EDAF_FLTR_EADDR13 0x57c ++#define ESPI_EDAF_FLTR_SADDR14 0x580 ++#define ESPI_EDAF_FLTR_EADDR14 0x584 ++#define ESPI_EDAF_FLTR_SADDR15 0x588 ++#define ESPI_EDAF_FLTR_EADDR15 0x58c ++#define ESPI_EDAF_WPROT0 0x5f8 ++#define ESPI_EDAF_WPROT1 0x5fc + -+static struct platform_driver aspeed_otp_driver = { -+ .probe = aspeed_otp_probe, -+ .remove = aspeed_otp_remove, -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .of_match_table = aspeed_otp_of_matches, -+ }, -+}; ++/* MMBI registers */ ++#define ESPI_MMBI_CTRL 0x800 ++#define ESPI_MMBI_CTRL_INST_NUM GENMASK(6, 4) ++#define ESPI_MMBI_CTRL_EN BIT(0) ++#define ESPI_MMBI_INT_STS 0x808 ++#define ESPI_MMBI_INT_EN 0x80c ++#define ESPI_MMBI_HOST_RWP(x) (0x810 + ((x) << 3)) + -+module_platform_driver(aspeed_otp_driver); ++void ast2700_espi_pre_init(struct aspeed_espi *espi); ++void ast2700_espi_post_init(struct aspeed_espi *espi); ++void ast2700_espi_deinit(struct aspeed_espi *espi); ++int ast2700_espi_perif_probe(struct aspeed_espi *espi); ++int ast2700_espi_perif_remove(struct aspeed_espi *espi); ++int ast2700_espi_vw_probe(struct aspeed_espi *espi); ++int ast2700_espi_vw_remove(struct aspeed_espi *espi); ++int ast2700_espi_oob_probe(struct aspeed_espi *espi); ++int ast2700_espi_oob_remove(struct aspeed_espi *espi); ++int ast2700_espi_flash_probe(struct aspeed_espi *espi); ++int ast2700_espi_flash_remove(struct aspeed_espi *espi); ++irqreturn_t ast2700_espi_isr(int irq, void *arg); + -+MODULE_AUTHOR("Neal Liu "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("ASPEED OTP Driver"); -diff --git a/drivers/soc/aspeed/ast2700-rtc-over-espi.c b/drivers/soc/aspeed/ast2700-rtc-over-espi.c ---- a/drivers/soc/aspeed/ast2700-rtc-over-espi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/ast2700-rtc-over-espi.c 2025-12-23 10:16:21.127032619 +0000 ++#endif +diff --git a/drivers/soc/aspeed/espi/ast2700-rtc-over-espi.c b/drivers/soc/aspeed/espi/ast2700-rtc-over-espi.c +--- a/drivers/soc/aspeed/espi/ast2700-rtc-over-espi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2700-rtc-over-espi.c 2026-04-08 18:03:48.313705265 +0000 @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0 +/* @@ -75422,7 +82509,7 @@ diff --git a/drivers/soc/aspeed/ast2700-rtc-over-espi.c b/drivers/soc/aspeed/ast +MODULE_DESCRIPTION("RTC to eSPI RAM sync driver"); diff --git a/drivers/soc/aspeed/rvas/Kconfig b/drivers/soc/aspeed/rvas/Kconfig --- a/drivers/soc/aspeed/rvas/Kconfig 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/Kconfig 2025-12-23 10:16:21.127032619 +0000 ++++ b/drivers/soc/aspeed/rvas/Kconfig 2026-04-08 18:03:48.313705265 +0000 @@ -0,0 +1,9 @@ +menu "ASPEED RVAS drivers" + @@ -75435,14 +82522,14 @@ diff --git a/drivers/soc/aspeed/rvas/Kconfig b/drivers/soc/aspeed/rvas/Kconfig +endmenu diff --git a/drivers/soc/aspeed/rvas/Makefile b/drivers/soc/aspeed/rvas/Makefile --- a/drivers/soc/aspeed/rvas/Makefile 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/Makefile 2025-12-23 10:16:21.127032619 +0000 ++++ b/drivers/soc/aspeed/rvas/Makefile 2026-04-08 18:03:48.313705265 +0000 @@ -0,0 +1,3 @@ +obj-$(CONFIG_ASPEED_RVAS) += rvas.o +rvas-y := video_main.o hardware_engines.o video_engine.o + diff --git a/drivers/soc/aspeed/rvas/hardware_engines.c b/drivers/soc/aspeed/rvas/hardware_engines.c --- a/drivers/soc/aspeed/rvas/hardware_engines.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/hardware_engines.c 2025-12-23 10:16:21.127032619 +0000 ++++ b/drivers/soc/aspeed/rvas/hardware_engines.c 2026-04-08 18:03:48.313705265 +0000 @@ -0,0 +1,2201 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* @@ -77647,7 +84734,7 @@ diff --git a/drivers/soc/aspeed/rvas/hardware_engines.c b/drivers/soc/aspeed/rva + diff --git a/drivers/soc/aspeed/rvas/hardware_engines.h b/drivers/soc/aspeed/rvas/hardware_engines.h --- a/drivers/soc/aspeed/rvas/hardware_engines.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/hardware_engines.h 2025-12-23 10:16:21.127032619 +0000 ++++ b/drivers/soc/aspeed/rvas/hardware_engines.h 2026-04-08 18:03:48.314705247 +0000 @@ -0,0 +1,550 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* @@ -78201,7 +85288,7 @@ diff --git a/drivers/soc/aspeed/rvas/hardware_engines.h b/drivers/soc/aspeed/rva +#endif // __HARDWAREENGINES_H__ diff --git a/drivers/soc/aspeed/rvas/video.h b/drivers/soc/aspeed/rvas/video.h --- a/drivers/soc/aspeed/rvas/video.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/video.h 2025-12-23 10:16:21.127032619 +0000 ++++ b/drivers/soc/aspeed/rvas/video.h 2026-04-08 18:03:48.314705247 +0000 @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/****************************************************************************** @@ -78246,7 +85333,7 @@ diff --git a/drivers/soc/aspeed/rvas/video.h b/drivers/soc/aspeed/rvas/video.h +#endif // __RVAS_VIDEO_H__ diff --git a/drivers/soc/aspeed/rvas/video_debug.h b/drivers/soc/aspeed/rvas/video_debug.h --- a/drivers/soc/aspeed/rvas/video_debug.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/video_debug.h 2025-12-23 10:16:21.127032619 +0000 ++++ b/drivers/soc/aspeed/rvas/video_debug.h 2026-04-08 18:03:48.314705247 +0000 @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* @@ -78285,7 +85372,7 @@ diff --git a/drivers/soc/aspeed/rvas/video_debug.h b/drivers/soc/aspeed/rvas/vid +#endif // AST_VIDEO_DEBUG_H_ diff --git a/drivers/soc/aspeed/rvas/video_engine.c b/drivers/soc/aspeed/rvas/video_engine.c --- a/drivers/soc/aspeed/rvas/video_engine.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/video_engine.c 2025-12-23 10:16:21.127032619 +0000 ++++ b/drivers/soc/aspeed/rvas/video_engine.c 2026-04-08 18:03:48.314705247 +0000 @@ -0,0 +1,1338 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* @@ -79627,7 +86714,7 @@ diff --git a/drivers/soc/aspeed/rvas/video_engine.c b/drivers/soc/aspeed/rvas/vi +} diff --git a/drivers/soc/aspeed/rvas/video_engine.h b/drivers/soc/aspeed/rvas/video_engine.h --- a/drivers/soc/aspeed/rvas/video_engine.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/video_engine.h 2025-12-23 10:16:21.127032619 +0000 ++++ b/drivers/soc/aspeed/rvas/video_engine.h 2026-04-08 18:03:48.314705247 +0000 @@ -0,0 +1,270 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* @@ -79901,7 +86988,7 @@ diff --git a/drivers/soc/aspeed/rvas/video_engine.h b/drivers/soc/aspeed/rvas/vi +#endif // __VIDEO_ENGINE_H__ diff --git a/drivers/soc/aspeed/rvas/video_ioctl.h b/drivers/soc/aspeed/rvas/video_ioctl.h --- a/drivers/soc/aspeed/rvas/video_ioctl.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/video_ioctl.h 2025-12-23 10:16:21.128032602 +0000 ++++ b/drivers/soc/aspeed/rvas/video_ioctl.h 2026-04-08 18:03:48.314705247 +0000 @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* @@ -80180,8 +87267,8 @@ diff --git a/drivers/soc/aspeed/rvas/video_ioctl.h b/drivers/soc/aspeed/rvas/vid +#endif // _VIDEO_IOCTL_H diff --git a/drivers/soc/aspeed/rvas/video_main.c b/drivers/soc/aspeed/rvas/video_main.c --- a/drivers/soc/aspeed/rvas/video_main.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/video_main.c 2025-12-23 10:16:21.128032602 +0000 -@@ -0,0 +1,1848 @@ ++++ b/drivers/soc/aspeed/rvas/video_main.c 2026-04-08 18:03:48.313705265 +0000 +@@ -0,0 +1,1854 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * File Name : video_main.c @@ -81334,10 +88421,16 @@ diff --git a/drivers/soc/aspeed/rvas/video_main.c b/drivers/soc/aspeed/rvas/vide + +static void reset_rvas_engine(struct AstRVAS *pAstRVAS) +{ -+ disable_rvas_engines(pAstRVAS); -+ if (pAstRVAS->config->version == 7) -+ reset_control_deassert(pAstRVAS->rvas_reset); -+ enable_rvas_engines(pAstRVAS); ++ if (pAstRVAS->config->version == 7) { ++ regmap_write(pAstRVAS->scu, 0x200, 0x200); ++ mdelay(200); ++ regmap_write(pAstRVAS->scu, 0x244, 0x2000000); ++ mdelay(100); ++ regmap_write(pAstRVAS->scu, 0x204, 0x200); ++ } else { ++ disable_rvas_engines(pAstRVAS); ++ enable_rvas_engines(pAstRVAS); ++ } + rvas_init(pAstRVAS); +} + @@ -82032,7 +89125,7 @@ diff --git a/drivers/soc/aspeed/rvas/video_main.c b/drivers/soc/aspeed/rvas/vide +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig --- a/drivers/spi/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/spi/Kconfig 2025-12-23 10:16:09.442228501 +0000 ++++ b/drivers/spi/Kconfig 2026-04-08 18:03:35.072947026 +0000 @@ -129,6 +129,16 @@ controller (SPI) for the host firmware. The implementation only supports SPI NOR. @@ -82052,7 +89145,7 @@ diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig depends on ARCH_AT91 || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile --- a/drivers/spi/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/spi/Makefile 2025-12-23 10:16:13.600158773 +0000 ++++ b/drivers/spi/Makefile 2026-04-08 18:03:39.964857803 +0000 @@ -22,6 +22,7 @@ obj-$(CONFIG_SPI_AR934X) += spi-ar934x.o obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o @@ -82063,7 +89156,7 @@ diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile obj-$(CONFIG_SPI_AT91_USART) += spi-at91-usart.o diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c --- a/drivers/spi/spi-aspeed-smc.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/spi/spi-aspeed-smc.c 2025-12-23 10:16:21.071033558 +0000 ++++ b/drivers/spi/spi-aspeed-smc.c 2026-04-08 18:03:48.249706435 +0000 @@ -7,10 +7,16 @@ */ @@ -84592,7 +91685,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c MODULE_DEVICE_TABLE(of, aspeed_spi_matches); diff --git a/drivers/spi/spi-aspeed-txrx.c b/drivers/spi/spi-aspeed-txrx.c --- a/drivers/spi/spi-aspeed-txrx.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/spi/spi-aspeed-txrx.c 2025-12-23 10:16:21.065033658 +0000 ++++ b/drivers/spi/spi-aspeed-txrx.c 2026-04-08 18:03:48.243706544 +0000 @@ -0,0 +1,622 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* @@ -85218,8 +92311,8 @@ diff --git a/drivers/spi/spi-aspeed-txrx.c b/drivers/spi/spi-aspeed-txrx.c + diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/8250_aspeed.c --- a/drivers/tty/serial/8250/8250_aspeed.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/tty/serial/8250/8250_aspeed.c 2025-12-23 10:16:20.962035385 +0000 -@@ -0,0 +1,524 @@ ++++ b/drivers/tty/serial/8250/8250_aspeed.c 2026-04-08 18:03:48.129708627 +0000 +@@ -0,0 +1,545 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) ASPEED Technology Inc. @@ -85236,6 +92329,7 @@ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/825 +#include +#include +#include ++#include +#include +#include +#include @@ -85262,7 +92356,6 @@ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/825 +#define VUART_GCRG 0x38 +#define VUART_GCRG_CHARACTER_TIMEOUT_TIME_CONTROL BIT(1) + -+#define DMA_TX_BUFSZ PAGE_SIZE +#define DMA_RX_BUFSZ (64 * 1024) + +struct uart_ops ast8250_pops; @@ -85277,14 +92370,14 @@ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/825 +struct ast8250_udma { + u32 ch; + -+ u32 tx_fifosz; -+ u32 rx_fifosz; ++ u32 tx_rbsz; ++ u32 rx_rbsz; + + dma_addr_t tx_addr; + dma_addr_t rx_addr; + -+ struct kfifo *tx_fifo; -+ struct kfifo *rx_fifo; ++ struct tty_port *tport; ++ struct circ_buf *rx_rb; + + bool tx_tmout_dis; + bool rx_tmout_dis; @@ -85293,6 +92386,7 @@ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/825 +struct ast8250_data { + int line; + ++ struct resource *res; + u8 __iomem *regs; + + bool is_vuart; @@ -85305,62 +92399,76 @@ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/825 + struct ast8250_udma dma; +}; + -+static void ast8250_dma_tx_complete(int tx_fifo_rptr, void *id) ++static void ast8250_dma_tx_complete(int tx_rb_rptr, void *id) +{ -+ unsigned long flags; -+ struct uart_port *port = (struct uart_port*)id; ++ unsigned long flags; ++ struct uart_port *port = id; + struct ast8250_data *data = port->private_data; -+ struct kfifo *tx_fifo = data->dma.tx_fifo; -+ unsigned int len; ++ unsigned int count, tail; + -+ spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + -+ len = kfifo_out(tx_fifo, NULL, tx_fifo_rptr); -+ port->icount.tx += len; ++ count = kfifo_out_linear(&data->dma.tport->xmit_fifo, &tail, data->dma.tx_rbsz); ++ count = CIRC_CNT(tx_rb_rptr, tail, data->dma.tx_rbsz); ++ if (!count) ++ count = data->dma.tx_rbsz; ++ kfifo_dma_out_finish(&data->dma.tport->xmit_fifo, count); ++ port->icount.tx += count; + -+ if (kfifo_len(tx_fifo) < WAKEUP_CHARS) ++ if (kfifo_len(&data->dma.tport->xmit_fifo) < WAKEUP_CHARS) + uart_write_wakeup(port); + -+ spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); +} + -+static void ast8250_dma_rx_complete(int rx_fifo_wptr, void *id) ++static void ast8250_dma_rx_complete(int rx_rb_wptr, void *id) +{ + unsigned long flags; + struct uart_port *up = (struct uart_port*)id; + struct tty_port *tp = &up->state->port; + struct ast8250_data *data = up->private_data; + struct ast8250_udma *dma = &data->dma; -+ struct kfifo *rx_fifo = dma->rx_fifo; -+ u32 len = 0; -+ u8 buf[128]; ++ struct circ_buf *rx_rb = dma->rx_rb; ++ u32 rx_rbsz = dma->rx_rbsz; ++ u32 count = 0; ++ ++ uart_port_lock_irqsave(up, &flags); + -+ spin_lock_irqsave(&up->lock, flags); ++ rx_rb->head = rx_rb_wptr; + + dma_sync_single_for_cpu(up->dev, -+ dma->rx_addr, dma->rx_fifosz, DMA_FROM_DEVICE); ++ dma->rx_addr, dma->rx_rbsz, DMA_FROM_DEVICE); + -+ while (!kfifo_is_empty(rx_fifo)) { -+ len = kfifo_out(rx_fifo, buf, sizeof(buf)); -+ tty_insert_flip_string(tp, buf, len); -+ up->icount.rx += len; ++ while (CIRC_CNT(rx_rb->head, rx_rb->tail, rx_rbsz)) { ++ count = CIRC_CNT_TO_END(rx_rb->head, rx_rb->tail, rx_rbsz); ++ ++ tty_insert_flip_string(tp, rx_rb->buf + rx_rb->tail, count); ++ ++ rx_rb->tail += count; ++ rx_rb->tail %= rx_rbsz; ++ ++ up->icount.rx += count; + } + -+ tty_flip_buffer_push(tp); ++ if (count) { ++ aspeed_udma_set_rx_rptr(data->dma.ch, rx_rb->tail); ++ tty_flip_buffer_push(tp); ++ } + -+ spin_unlock_irqrestore(&up->lock, flags); ++ uart_port_unlock_irqrestore(up, flags); +} + +static void ast8250_dma_start_tx(struct uart_port *port) +{ + struct ast8250_data *data = port->private_data; + struct ast8250_udma *dma = &data->dma; -+ struct kfifo *tx_fifo = dma->tx_fifo; ++ typeof(&dma->tport->xmit_fifo) tmp = &dma->tport->xmit_fifo; ++ struct __kfifo *tx_rb = &tmp->kfifo; + + dma_sync_single_for_device(port->dev, -+ dma->tx_addr, dma->tx_fifosz, DMA_TO_DEVICE); ++ dma->tx_addr, dma->tx_rbsz, DMA_TO_DEVICE); + -+ aspeed_udma_set_tx_wptr(dma->ch, kfifo_len(tx_fifo)); ++ aspeed_udma_set_tx_wptr(dma->ch, tx_rb->in); +} + +static void ast8250_dma_pops_hook(struct uart_port *port) @@ -85454,49 +92562,53 @@ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/825 + if (data->use_dma) { + dma = &data->dma; + -+ dma->tx_fifosz = DMA_TX_BUFSZ; -+ dma->rx_fifosz = DMA_RX_BUFSZ; ++ dma->tx_rbsz = UART_XMIT_SIZE; ++ dma->rx_rbsz = DMA_RX_BUFSZ; + -+ if (kfifo_alloc(dma->tx_fifo, dma->tx_fifosz, GFP_KERNEL)) { -+ dev_err(port->dev, "failed to allocate TX DMA ring buffer\n"); ++ /* ++ * We take the xmit buffer passed from upper layers as ++ * the DMA TX buffer and allocate a new buffer for the ++ * RX use. ++ * ++ * To keep the TX/RX operation consistency, we use the ++ * streaming DMA interface instead of the coherent one ++ */ ++ dma->tport = &port->state->port; ++ dma->rx_rb->buf = kzalloc(data->dma.rx_rbsz, GFP_KERNEL); ++ if (IS_ERR_OR_NULL(dma->rx_rb->buf)) { ++ dev_err(port->dev, "failed to allcoate RX DMA buffer\n"); + rc = -ENOMEM; + goto out; + } + -+ if (kfifo_alloc(dma->rx_fifo, dma->rx_fifosz, GFP_KERNEL)) { -+ dev_err(port->dev, "failed to allocate RX DMA ring buffer\n"); -+ rc = -ENOMEM; -+ goto free_tx_fifo; -+ } -+ -+ dma->tx_addr = dma_map_single(port->dev, dma->tx_fifo->kfifo.data, -+ dma->tx_fifosz, DMA_TO_DEVICE); ++ dma->tx_addr = dma_map_single(port->dev, dma->tport->xmit_buf, ++ dma->tx_rbsz, DMA_TO_DEVICE); + if (dma_mapping_error(port->dev, dma->tx_addr)) { + dev_err(port->dev, "failed to map streaming TX DMA region\n"); + rc = -ENOMEM; -+ goto free_rx_fifo; ++ goto free_dma_n_out; + } + -+ dma->rx_addr = dma_map_single(port->dev, dma->rx_fifo->kfifo.data, -+ dma->rx_fifosz, DMA_FROM_DEVICE); ++ dma->rx_addr = dma_map_single(port->dev, dma->rx_rb->buf, ++ dma->rx_rbsz, DMA_FROM_DEVICE); + if (dma_mapping_error(port->dev, dma->rx_addr)) { + dev_err(port->dev, "failed to map streaming RX DMA region\n"); + rc = -ENOMEM; -+ goto free_rx_fifo; ++ goto free_dma_n_out; + } + + rc = aspeed_udma_request_tx_chan(dma->ch, dma->tx_addr, -+ dma->tx_fifo, dma->tx_fifosz, ast8250_dma_tx_complete, port, dma->tx_tmout_dis); ++ dma->tx_rbsz, ast8250_dma_tx_complete, port, dma->tx_tmout_dis); + if (rc) { + dev_err(port->dev, "failed to request DMA TX channel\n"); -+ goto free_rx_fifo; ++ goto free_dma_n_out; + } + + rc = aspeed_udma_request_rx_chan(dma->ch, dma->rx_addr, -+ dma->rx_fifo, dma->rx_fifosz, ast8250_dma_rx_complete, port, dma->rx_tmout_dis); ++ dma->rx_rbsz, ast8250_dma_rx_complete, port, dma->rx_tmout_dis); + if (rc) { + dev_err(port->dev, "failed to request DMA RX channel\n"); -+ goto free_rx_fifo; ++ goto free_dma_n_out; + } + + ast8250_dma_pops_hook(port); @@ -85508,12 +92620,8 @@ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/825 + memset(&port->icount, 0, sizeof(port->icount)); + return serial8250_do_startup(port); + -+free_rx_fifo: -+ kfifo_free(dma->rx_fifo); -+ -+free_tx_fifo: -+ kfifo_free(dma->tx_fifo); -+ ++free_dma_n_out: ++ kfree(dma->rx_rb->buf); +out: + return rc; +} @@ -85542,12 +92650,12 @@ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/825 + dev_err(port->dev, "failed to free DMA TX channel, rc=%d\n", rc); + + dma_unmap_single(port->dev, dma->tx_addr, -+ dma->tx_fifosz, DMA_TO_DEVICE); ++ dma->tx_rbsz, DMA_TO_DEVICE); + dma_unmap_single(port->dev, dma->rx_addr, -+ dma->rx_fifosz, DMA_FROM_DEVICE); ++ dma->rx_rbsz, DMA_FROM_DEVICE); + -+ kfree(dma->tx_fifo); -+ kfree(dma->rx_fifo); ++ if (dma->rx_rb->buf) ++ kfree(dma->rx_rb->buf); + } + + if (data->is_vuart) @@ -85570,46 +92678,19 @@ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/825 + return 0; +} + -+static int ast8250_probe(struct platform_device *pdev) ++static int ast8250_probe_of(struct platform_device *pdev, struct uart_port *p, ++ struct ast8250_data *data) +{ -+ int rc; -+ struct uart_8250_port uart = {}; -+ struct uart_port *port = &uart.port; + struct device *dev = &pdev->dev; -+ struct ast8250_data *data; -+ uint32_t plat = (unsigned long)of_device_get_match_data(dev); -+ -+ struct resource *res; -+ u32 irq; -+ -+ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); -+ if (rc) { -+ dev_err(dev, "cannot set 64-bits DMA mask\n"); -+ return rc; -+ } -+ -+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); -+ if (data == NULL) -+ return -ENOMEM; -+ -+ data->dma.rx_fifo = devm_kzalloc(dev, sizeof(data->dma.rx_fifo), GFP_KERNEL); -+ if (!data->dma.rx_fifo) -+ return -ENOMEM; -+ -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) { -+ if (irq != -EPROBE_DEFER) -+ dev_err(dev, "failed to get IRQ number\n"); -+ return irq; -+ } ++ int rc; + -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (res == NULL) { ++ data->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!data->res) { + dev_err(dev, "failed to get register base\n"); + return -ENODEV; + } + -+ data->regs = devm_ioremap(dev, res->start, resource_size(res)); ++ data->regs = devm_ioremap(dev, data->res->start, resource_size(data->res)); + if (IS_ERR(data->regs)) { + dev_err(dev, "failed to map registers\n"); + return PTR_ERR(data->regs); @@ -85621,18 +92702,12 @@ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/825 + return -ENODEV; + } + -+ rc = clk_prepare_enable(data->clk); -+ if (rc) { -+ dev_err(dev, "failed to enable clock\n"); -+ return rc; -+ } -+ + data->rst = devm_reset_control_get_optional_exclusive(dev, NULL); -+ if (!IS_ERR(data->rst)) -+ reset_control_deassert(data->rst); + + data->is_vuart = of_property_read_bool(dev->of_node, "virtual"); + if (data->is_vuart) { ++ u32 plat = (unsigned long)of_device_get_match_data(dev); ++ + rc = of_property_read_u32(dev->of_node, "port", &data->vuart.port); + if (rc) { + dev_err(dev, "failed to get VUART port address\n"); @@ -85655,17 +92730,10 @@ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/825 + data->vuart.character_timeout_time_en = true; + else + data->vuart.character_timeout_time_en = false; -+ -+ ast8250_vuart_init(data); -+ ast8250_vuart_set_host_tx_discard(data, true); -+ ast8250_vuart_set_enable(data, true); + } + + data->use_dma = of_property_read_bool(dev->of_node, "dma-mode"); + if (data->use_dma) { -+ dev_warn(dev, "DMA mode not ready\n"); -+ data->use_dma = false; -+ /* + rc = of_property_read_u32(dev->of_node, "dma-channel", &data->dma.ch); + if (rc) { + dev_err(dev, "failed to get DMA channel\n"); @@ -85674,27 +92742,73 @@ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/825 + + data->dma.tx_tmout_dis = of_property_read_bool(dev->of_node, "dma-tx-timeout-disable"); + data->dma.rx_tmout_dis = of_property_read_bool(dev->of_node, "dma-rx-timeout-disable"); -+ */ ++ } ++ ++ return 0; ++} ++ ++static int ast8250_probe(struct platform_device *pdev) ++{ ++ int rc; ++ struct uart_8250_port uart = {}; ++ struct uart_port *port = &uart.port; ++ struct device *dev = &pdev->dev; ++ struct ast8250_data *data; ++ ++ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); ++ if (rc) { ++ dev_err(dev, "cannot set 64-bits DMA mask\n"); ++ return rc; ++ } ++ ++ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ data->dma.rx_rb = devm_kzalloc(dev, sizeof(data->dma.rx_rb), GFP_KERNEL); ++ if (!data->dma.rx_rb) ++ return -ENOMEM; ++ ++ rc = ast8250_probe_of(pdev, port, data); ++ if (rc) ++ return rc; ++ ++ rc = clk_prepare_enable(data->clk); ++ if (rc) { ++ dev_err(dev, "failed to enable clock\n"); ++ return rc; ++ } ++ ++ if (!IS_ERR(data->rst)) ++ reset_control_deassert(data->rst); ++ ++ if (data->is_vuart) { ++ ast8250_vuart_init(data); ++ ast8250_vuart_set_host_tx_discard(data, true); ++ ast8250_vuart_set_enable(data, true); + } + + spin_lock_init(&port->lock); + port->dev = dev; -+ port->type = PORT_16550A; -+ port->irq = irq; -+ port->line = of_alias_get_id(dev->of_node, "serial"); -+ port->handle_irq = ast8250_handle_irq; -+ port->mapbase = res->start; -+ port->mapsize = resource_size(res); ++ port->mapbase = data->res->start; ++ port->mapsize = resource_size(data->res); + port->membase = data->regs; -+ port->uartclk = clk_get_rate(data->clk); -+ port->regshift = 2; -+ port->iotype = UPIO_MEM32; + port->flags = UPF_FIXED_TYPE | UPF_FIXED_PORT | UPF_SHARE_IRQ; + port->startup = ast8250_startup; + port->shutdown = ast8250_shutdown; + port->private_data = data; + uart.bugs |= UART_BUG_TXRACE; + ++ rc = uart_read_port_properties(port); ++ if (rc) ++ return rc; ++ ++ port->type = (data->is_vuart) ? PORT_ASPEED_VUART : PORT_16550A; ++ port->handle_irq = ast8250_handle_irq; ++ port->uartclk = clk_get_rate(data->clk); ++ ++ uart.capabilities = UART_CAP_FIFO | UART_CAP_AFE; ++ + data->line = serial8250_register_8250_port(&uart); + if (data->line < 0) { + dev_err(dev, "failed to register 8250 port\n"); @@ -85710,12 +92824,12 @@ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/825 + +static void ast8250_remove(struct platform_device *pdev) +{ -+ struct ast8250_data *data = platform_get_drvdata(pdev); ++ struct ast8250_data *data = platform_get_drvdata(pdev); + + if (data->is_vuart) + ast8250_vuart_set_enable(data, false); + -+ serial8250_unregister_port(data->line); ++ serial8250_unregister_port(data->line); +} + +static const struct dev_pm_ops ast8250_pm_ops = { @@ -85746,7 +92860,7 @@ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/825 +MODULE_DESCRIPTION("Aspeed UART Driver"); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig --- a/drivers/tty/serial/8250/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/tty/serial/8250/Kconfig 2025-12-23 10:16:08.799239284 +0000 ++++ b/drivers/tty/serial/8250/Kconfig 2026-04-08 18:03:34.152963806 +0000 @@ -256,6 +256,15 @@ To compile this driver as a module, choose M here: the module will be called 8250_accent. @@ -85765,7 +92879,7 @@ diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig depends on SERIAL_8250 diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile --- a/drivers/tty/serial/8250/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/tty/serial/8250/Makefile 2025-12-23 10:16:12.776172591 +0000 ++++ b/drivers/tty/serial/8250/Makefile 2026-04-08 18:03:38.836878376 +0000 @@ -21,6 +21,7 @@ obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o @@ -85776,7 +92890,7 @@ diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig --- a/drivers/ufs/host/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/ufs/host/Kconfig 2025-12-23 10:16:09.263231503 +0000 ++++ b/drivers/ufs/host/Kconfig 2026-04-08 18:03:34.751952881 +0000 @@ -142,3 +142,12 @@ Select this if you have UFS controller on Unisoc chipset. @@ -85792,7 +92906,7 @@ diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig + If unsure, say N. diff --git a/drivers/ufs/host/Makefile b/drivers/ufs/host/Makefile --- a/drivers/ufs/host/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/ufs/host/Makefile 2025-12-23 10:16:13.396162194 +0000 ++++ b/drivers/ufs/host/Makefile 2026-04-08 18:03:39.631863876 +0000 @@ -12,3 +12,4 @@ obj-$(CONFIG_SCSI_UFS_RENESAS) += ufs-renesas.o obj-$(CONFIG_SCSI_UFS_SPRD) += ufs-sprd.o @@ -85800,7 +92914,7 @@ diff --git a/drivers/ufs/host/Makefile b/drivers/ufs/host/Makefile +obj-$(CONFIG_SCSI_UFS_ASPEED) += ufs-aspeed.o diff --git a/drivers/ufs/host/ufs-aspeed.c b/drivers/ufs/host/ufs-aspeed.c --- a/drivers/ufs/host/ufs-aspeed.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/ufs/host/ufs-aspeed.c 2025-12-23 10:16:21.026034312 +0000 ++++ b/drivers/ufs/host/ufs-aspeed.c 2026-04-08 18:03:48.204707257 +0000 @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright (C) 2019 ASPEED Technology Inc. */ @@ -86229,7 +93343,7 @@ diff --git a/drivers/ufs/host/ufs-aspeed.c b/drivers/ufs/host/ufs-aspeed.c +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/udc/aspeed-vhub/Kconfig b/drivers/usb/gadget/udc/aspeed-vhub/Kconfig --- a/drivers/usb/gadget/udc/aspeed-vhub/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/usb/gadget/udc/aspeed-vhub/Kconfig 2025-12-23 10:16:21.160032066 +0000 ++++ b/drivers/usb/gadget/udc/aspeed-vhub/Kconfig 2026-04-08 18:03:48.336704845 +0000 @@ -4,5 +4,6 @@ depends on ARCH_ASPEED || COMPILE_TEST depends on USB_LIBCOMPOSITE @@ -86241,7 +93355,7 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/Kconfig b/drivers/usb/gadget/udc + USB2.0 diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c 2025-12-23 10:16:21.160032066 +0000 ++++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c 2026-04-08 18:03:48.336704845 +0000 @@ -23,9 +23,27 @@ #include #include @@ -86270,15 +93384,19 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/ void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req, int status) { -@@ -239,6 +257,7 @@ +@@ -239,6 +257,11 @@ if (vhub->force_usb1) ctrl |= VHUB_CTRL_FULL_SPEED_ONLY; ++ /* Enlarge FIFO for ast2700 soc0 vhub1 */ ++ if (vhub->enlarge_fifo) ++ ctrl |= VHUB_CTRL_ENLARGE_FIFO; ++ + ctrl |= VHUB_CTRL_AUTO_REMOTE_WAKEUP; ctrl |= VHUB_CTRL_UPSTREAM_CONNECT; writel(ctrl, vhub->regs + AST_VHUB_CTRL); -@@ -253,6 +272,52 @@ +@@ -253,6 +276,52 @@ vhub->regs + AST_VHUB_IER); } @@ -86331,7 +93449,7 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/ static void ast_vhub_remove(struct platform_device *pdev) { struct ast_vhub *vhub = platform_get_drvdata(pdev); -@@ -280,6 +345,9 @@ +@@ -280,6 +349,9 @@ if (vhub->clk) clk_disable_unprepare(vhub->clk); @@ -86341,7 +93459,7 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/ spin_unlock_irqrestore(&vhub->lock, flags); if (vhub->ep0_bufs) -@@ -291,6 +359,61 @@ +@@ -291,6 +363,61 @@ vhub->ep0_bufs = NULL; } @@ -86403,7 +93521,7 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/ static int ast_vhub_probe(struct platform_device *pdev) { enum usb_device_speed max_speed; -@@ -298,11 +421,19 @@ +@@ -298,11 +425,19 @@ struct resource *res; int i, rc = 0; const struct device_node *np = pdev->dev.of_node; @@ -86423,7 +93541,16 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/ rc = of_property_read_u32(np, "aspeed,vhub-downstream-ports", &vhub->max_ports); if (rc < 0) -@@ -337,6 +468,13 @@ +@@ -323,6 +458,8 @@ + if (!vhub->epns) + return -ENOMEM; + ++ vhub->enlarge_fifo = of_property_read_bool(np, "aspeed,enlarge-fifo"); ++ + spin_lock_init(&vhub->lock); + vhub->pdev = pdev; + vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1, +@@ -337,6 +474,13 @@ platform_set_drvdata(pdev, vhub); @@ -86437,7 +93564,7 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/ vhub->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(vhub->clk)) { rc = PTR_ERR(vhub->clk); -@@ -348,6 +486,28 @@ +@@ -348,6 +492,28 @@ goto err; } @@ -86466,7 +93593,7 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/ /* Check if we need to limit the HW to USB1 */ max_speed = usb_get_maximum_speed(&pdev->dev); if (max_speed != USB_SPEED_UNKNOWN && max_speed < USB_SPEED_HIGH) -@@ -370,6 +530,12 @@ +@@ -370,6 +536,12 @@ goto err; } @@ -86479,7 +93606,7 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/ /* * Allocate DMA buffers for all EP0s in one chunk, * one per port and one for the vHub itself -@@ -412,15 +578,86 @@ +@@ -412,15 +584,86 @@ return rc; } @@ -86568,7 +93695,7 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/ }; diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c --- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c 2025-12-23 10:16:21.160032066 +0000 ++++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c 2026-04-08 18:03:48.336704845 +0000 @@ -116,10 +116,14 @@ if (wValue == USB_DEVICE_REMOTE_WAKEUP) { @@ -86597,7 +93724,7 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/a diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/aspeed-vhub/epn.c --- a/drivers/usb/gadget/udc/aspeed-vhub/epn.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c 2025-12-23 10:16:21.160032066 +0000 ++++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c 2026-04-08 18:03:48.336704845 +0000 @@ -340,7 +340,9 @@ struct ast_vhub *vhub = ep->vhub; unsigned long flags; @@ -86700,7 +93827,7 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/a struct ast_vhub_ep *ast_vhub_alloc_epn(struct ast_vhub_dev *d, u8 addr) diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c --- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c 2025-12-23 10:16:21.160032066 +0000 ++++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c 2026-04-08 18:03:48.336704845 +0000 @@ -221,9 +221,8 @@ EPDBG(ep, "Hub remote wakeup %s\n", is_set ? "enabled" : "disabled"); @@ -86747,8 +93874,16 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/a return ast_vhub_simple_reply(ep, diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h --- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h 2025-12-23 10:16:21.160032066 +0000 -@@ -199,6 +199,14 @@ ++++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h 2026-04-08 18:03:48.336704845 +0000 +@@ -35,6 +35,7 @@ + #define VHUB_CTRL_PHY_LOOP_TEST (1 << 25) + #define VHUB_CTRL_DN_PWN (1 << 24) + #define VHUB_CTRL_DP_PWN (1 << 23) ++#define VHUB_CTRL_ENLARGE_FIFO (1 << 21) + #define VHUB_CTRL_LONG_DESC (1 << 18) + #define VHUB_CTRL_ISO_RSP_CTRL (1 << 17) + #define VHUB_CTRL_SPLIT_IN (1 << 16) +@@ -199,6 +200,14 @@ #define VHUB_DSC1_IN_SET_LEN(x) ((x) & 0xfff) #define VHUB_DSC1_IN_LEN(x) ((x) & 0xfff) @@ -86763,7 +93898,7 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/ /**************************************** * * * Data structures and misc definitions * -@@ -218,6 +226,8 @@ +@@ -218,6 +227,8 @@ * values are 256 and 32) */ @@ -86772,7 +93907,7 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/ struct ast_vhub; struct ast_vhub_dev; -@@ -388,6 +398,8 @@ +@@ -388,6 +399,8 @@ spinlock_t lock; struct work_struct wake_work; struct clk *clk; @@ -86781,8 +93916,13 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/ /* EP0 DMA buffers allocated in one chunk */ void *ep0_bufs; -@@ -419,6 +431,7 @@ +@@ -417,8 +430,12 @@ + /* Force full speed only */ + bool force_usb1 : 1; ++ /* Enlarge FIFO for ast2700 soc0 vhub1 */ ++ bool enlarge_fifo : 1; ++ /* Upstream bus speed captured at bus reset */ unsigned int speed; + u8 current_config; @@ -86791,7 +93931,7 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/ struct usb_device_descriptor vhub_dev_desc; diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c --- a/drivers/usb/gadget/udc/aspeed_udc.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/usb/gadget/udc/aspeed_udc.c 2025-12-23 10:16:21.115032820 +0000 ++++ b/drivers/usb/gadget/udc/aspeed_udc.c 2026-04-08 18:03:48.296705576 +0000 @@ -156,7 +156,7 @@ #define AST_EP_DMA_DESC_PID_DATA1 (2 << 14) #define AST_EP_DMA_DESC_PID_MDATA (3 << 14) @@ -86984,7 +94124,7 @@ diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed ep = &udc->ep[i]; diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c --- a/drivers/watchdog/aspeed_wdt.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/watchdog/aspeed_wdt.c 2025-12-23 10:16:21.077033457 +0000 ++++ b/drivers/watchdog/aspeed_wdt.c 2026-04-08 18:03:48.256706307 +0000 @@ -35,6 +35,8 @@ u32 irq_shift; u32 irq_mask; @@ -87217,7 +94357,7 @@ diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c aspeed_wdt_update_bootstatus(pdev, wdt); diff --git a/include/dt-bindings/clock/aspeed,ast1700-clk.h b/include/dt-bindings/clock/aspeed,ast1700-clk.h --- a/include/dt-bindings/clock/aspeed,ast1700-clk.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/dt-bindings/clock/aspeed,ast1700-clk.h 2025-12-23 10:16:21.275030139 +0000 ++++ b/include/dt-bindings/clock/aspeed,ast1700-clk.h 2026-04-08 18:03:48.454702689 +0000 @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* @@ -87318,7 +94458,7 @@ diff --git a/include/dt-bindings/clock/aspeed,ast1700-clk.h b/include/dt-binding +#endif diff --git a/include/dt-bindings/clock/aspeed,ast1800-clk.h b/include/dt-bindings/clock/aspeed,ast1800-clk.h --- a/include/dt-bindings/clock/aspeed,ast1800-clk.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/dt-bindings/clock/aspeed,ast1800-clk.h 2025-12-23 10:16:21.285029971 +0000 ++++ b/include/dt-bindings/clock/aspeed,ast1800-clk.h 2026-04-08 18:03:48.463702525 +0000 @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* @@ -87426,7 +94566,7 @@ diff --git a/include/dt-bindings/clock/aspeed,ast1800-clk.h b/include/dt-binding +#endif diff --git a/include/dt-bindings/clock/aspeed,ast2700-scu.h b/include/dt-bindings/clock/aspeed,ast2700-scu.h --- a/include/dt-bindings/clock/aspeed,ast2700-scu.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/dt-bindings/clock/aspeed,ast2700-scu.h 2025-12-23 10:16:21.289029904 +0000 ++++ b/include/dt-bindings/clock/aspeed,ast2700-scu.h 2026-04-08 18:03:48.468702433 +0000 @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* @@ -87597,7 +94737,7 @@ diff --git a/include/dt-bindings/clock/aspeed,ast2700-scu.h b/include/dt-binding +#endif diff --git a/include/dt-bindings/clock/aspeed-clock.h b/include/dt-bindings/clock/aspeed-clock.h --- a/include/dt-bindings/clock/aspeed-clock.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/dt-bindings/clock/aspeed-clock.h 2025-12-23 10:16:21.280030055 +0000 ++++ b/include/dt-bindings/clock/aspeed-clock.h 2026-04-08 18:03:48.459702598 +0000 @@ -53,5 +53,6 @@ #define ASPEED_RESET_AHB 8 #define ASPEED_RESET_CRT1 9 @@ -87607,7 +94747,7 @@ diff --git a/include/dt-bindings/clock/aspeed-clock.h b/include/dt-bindings/cloc #endif diff --git a/include/dt-bindings/clock/ast2600-clock.h b/include/dt-bindings/clock/ast2600-clock.h --- a/include/dt-bindings/clock/ast2600-clock.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/dt-bindings/clock/ast2600-clock.h 2025-12-23 10:16:21.294029820 +0000 ++++ b/include/dt-bindings/clock/ast2600-clock.h 2026-04-08 18:03:48.473702342 +0000 @@ -72,7 +72,7 @@ #define ASPEED_CLK_D1CLK 55 #define ASPEED_CLK_VCLK 56 @@ -87653,7 +94793,7 @@ diff --git a/include/dt-bindings/clock/ast2600-clock.h b/include/dt-bindings/clo #define ASPEED_RESET_AHB 1 diff --git a/include/dt-bindings/interrupt-controller/aspeed-e2m-ic.h b/include/dt-bindings/interrupt-controller/aspeed-e2m-ic.h --- a/include/dt-bindings/interrupt-controller/aspeed-e2m-ic.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/dt-bindings/interrupt-controller/aspeed-e2m-ic.h 2025-12-23 10:16:21.299029736 +0000 ++++ b/include/dt-bindings/interrupt-controller/aspeed-e2m-ic.h 2026-04-08 18:03:48.478702251 +0000 @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* @@ -87678,7 +94818,7 @@ diff --git a/include/dt-bindings/interrupt-controller/aspeed-e2m-ic.h b/include/ +#endif /* _DT_BINDINGS_INTERRUPT_CONTROLLER_ASPEED_E2M_IC_H_ */ diff --git a/include/dt-bindings/interrupt-controller/aspeed-scu-ic.h b/include/dt-bindings/interrupt-controller/aspeed-scu-ic.h --- a/include/dt-bindings/interrupt-controller/aspeed-scu-ic.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/dt-bindings/interrupt-controller/aspeed-scu-ic.h 2025-12-23 10:16:21.304029653 +0000 ++++ b/include/dt-bindings/interrupt-controller/aspeed-scu-ic.h 2026-04-08 18:03:48.483702159 +0000 @@ -20,4 +20,18 @@ #define ASPEED_AST2600_SCU_IC1_LPC_RESET_LO_TO_HI 0 #define ASPEED_AST2600_SCU_IC1_LPC_RESET_HI_TO_LO 1 @@ -87700,7 +94840,7 @@ diff --git a/include/dt-bindings/interrupt-controller/aspeed-scu-ic.h b/include/ #endif /* _DT_BINDINGS_INTERRUPT_CONTROLLER_ASPEED_SCU_IC_H_ */ diff --git a/include/dt-bindings/reset/aspeed,ast1700-reset.h b/include/dt-bindings/reset/aspeed,ast1700-reset.h --- a/include/dt-bindings/reset/aspeed,ast1700-reset.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/dt-bindings/reset/aspeed,ast1700-reset.h 2025-12-23 10:16:21.317029435 +0000 ++++ b/include/dt-bindings/reset/aspeed,ast1700-reset.h 2026-04-08 18:03:48.497701904 +0000 @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* @@ -87773,7 +94913,7 @@ diff --git a/include/dt-bindings/reset/aspeed,ast1700-reset.h b/include/dt-bindi +#endif /* _MACH_ASPEED_AST1700_RESET_H_ */ diff --git a/include/dt-bindings/reset/aspeed,ast1800-reset.h b/include/dt-bindings/reset/aspeed,ast1800-reset.h --- a/include/dt-bindings/reset/aspeed,ast1800-reset.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/dt-bindings/reset/aspeed,ast1800-reset.h 2025-12-23 10:16:21.326029284 +0000 ++++ b/include/dt-bindings/reset/aspeed,ast1800-reset.h 2026-04-08 18:03:48.507701721 +0000 @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* @@ -87838,7 +94978,7 @@ diff --git a/include/dt-bindings/reset/aspeed,ast1800-reset.h b/include/dt-bindi +#endif /* _MACH_ASPEED_AST1800_RESET_H_ */ diff --git a/include/dt-bindings/reset/aspeed,ast2700-scu.h b/include/dt-bindings/reset/aspeed,ast2700-scu.h --- a/include/dt-bindings/reset/aspeed,ast2700-scu.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/dt-bindings/reset/aspeed,ast2700-scu.h 2025-12-23 10:16:21.322029351 +0000 ++++ b/include/dt-bindings/reset/aspeed,ast2700-scu.h 2026-04-08 18:03:48.502701812 +0000 @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* @@ -87967,7 +95107,7 @@ diff --git a/include/dt-bindings/reset/aspeed,ast2700-scu.h b/include/dt-binding +#endif /* _MACH_ASPEED_AST2700_RESET_H_ */ diff --git a/include/dt-bindings/watchdog/aspeed-wdt.h b/include/dt-bindings/watchdog/aspeed-wdt.h --- a/include/dt-bindings/watchdog/aspeed-wdt.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/dt-bindings/watchdog/aspeed-wdt.h 2025-12-23 10:16:21.313029502 +0000 ++++ b/include/dt-bindings/watchdog/aspeed-wdt.h 2026-04-08 18:03:48.493701977 +0000 @@ -89,4 +89,142 @@ #define AST2600_WDT_RESET2_DEFAULT 0x03fffff1 @@ -88113,7 +95253,7 @@ diff --git a/include/dt-bindings/watchdog/aspeed-wdt.h b/include/dt-bindings/wat #endif diff --git a/include/linux/aspeed-mctp.h b/include/linux/aspeed-mctp.h --- a/include/linux/aspeed-mctp.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/linux/aspeed-mctp.h 2025-12-23 10:16:21.266030290 +0000 ++++ b/include/linux/aspeed-mctp.h 2026-04-08 18:03:48.444702872 +0000 @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (c) 2025 ASPEED Tech */ @@ -88274,7 +95414,7 @@ diff --git a/include/linux/aspeed-mctp.h b/include/linux/aspeed-mctp.h +#endif /* __LINUX_ASPEED_MCTP_H */ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h --- a/include/linux/clk-provider.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/linux/clk-provider.h 2025-12-23 10:16:22.000017988 +0000 ++++ b/include/linux/clk-provider.h 2026-04-08 18:03:49.279687616 +0000 @@ -623,6 +623,24 @@ NULL, (flags), (reg), (bit_idx), \ (clk_gate_flags), (lock)) @@ -88302,7 +95442,7 @@ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h * @dev: device that is registering this clock diff --git a/include/linux/mctp-pcie-vdm.h b/include/linux/mctp-pcie-vdm.h --- a/include/linux/mctp-pcie-vdm.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/linux/mctp-pcie-vdm.h 2025-12-23 10:16:21.973018440 +0000 ++++ b/include/linux/mctp-pcie-vdm.h 2026-04-08 18:03:49.251688128 +0000 @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* @@ -88342,7 +95482,7 @@ diff --git a/include/linux/mctp-pcie-vdm.h b/include/linux/mctp-pcie-vdm.h +#endif /* __LINUX_MCTP_PCIE_VDM_H */ diff --git a/include/linux/pwm.h b/include/linux/pwm.h --- a/include/linux/pwm.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/linux/pwm.h 2025-12-23 10:16:22.035017401 +0000 ++++ b/include/linux/pwm.h 2026-04-08 18:03:49.312687013 +0000 @@ -405,6 +405,9 @@ int __devm_pwmchip_add(struct device *dev, struct pwm_chip *chip, struct module *owner); #define devm_pwmchip_add(dev, chip) __devm_pwmchip_add(dev, chip, THIS_MODULE) @@ -88355,7 +95495,7 @@ diff --git a/include/linux/pwm.h b/include/linux/pwm.h struct pwm_device *of_pwm_single_xlate(struct pwm_chip *chip, diff --git a/include/linux/soc/aspeed/aspeed-otp.h b/include/linux/soc/aspeed/aspeed-otp.h --- a/include/linux/soc/aspeed/aspeed-otp.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/linux/soc/aspeed/aspeed-otp.h 2025-12-23 10:16:21.257030440 +0000 ++++ b/include/linux/soc/aspeed/aspeed-otp.h 2026-04-08 18:03:48.435703036 +0000 @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */ +/* @@ -88371,12 +95511,12 @@ diff --git a/include/linux/soc/aspeed/aspeed-otp.h b/include/linux/soc/aspeed/as +#endif /* _LINUX_ASPEED_OTP_H */ diff --git a/include/linux/soc/aspeed/aspeed-udma.h b/include/linux/soc/aspeed/aspeed-udma.h --- a/include/linux/soc/aspeed/aspeed-udma.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/linux/soc/aspeed/aspeed-udma.h 2025-12-23 10:16:21.252030524 +0000 -@@ -0,0 +1,28 @@ ++++ b/include/linux/soc/aspeed/aspeed-udma.h 2026-04-08 18:03:48.430703128 +0000 +@@ -0,0 +1,26 @@ +#ifndef __ASPEED_UDMA_H__ +#define __ASPEED_UDMA_H__ + -+typedef void (*aspeed_udma_cb_t)(int fifo_rwptr, void *id); ++typedef void (*aspeed_udma_cb_t)(int rb_rwptr, void *id); + +enum aspeed_udma_ops { + ASPEED_UDMA_OP_ENABLE, @@ -88390,11 +95530,9 @@ diff --git a/include/linux/soc/aspeed/aspeed-udma.h b/include/linux/soc/aspeed/a +void aspeed_udma_tx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op); +void aspeed_udma_rx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op); + -+int aspeed_udma_request_tx_chan(u32 ch_no, dma_addr_t addr, -+ struct kfifo *fifo, u32 fifo_sz, ++int aspeed_udma_request_tx_chan(u32 ch_no, dma_addr_t addr, u32 rb_sz, + aspeed_udma_cb_t cb, void *id, bool en_tmout); -+int aspeed_udma_request_rx_chan(u32 ch_no, dma_addr_t addr, -+ struct kfifo *fifo, u32 fifo_sz, ++int aspeed_udma_request_rx_chan(u32 ch_no, dma_addr_t addr, u32 rb_sz, + aspeed_udma_cb_t cb, void *id, bool en_tmout); + +int aspeed_udma_free_tx_chan(u32 ch_no); @@ -88403,7 +95541,7 @@ diff --git a/include/linux/soc/aspeed/aspeed-udma.h b/include/linux/soc/aspeed/a +#endif diff --git a/include/soc/aspeed/reset-aspeed.h b/include/soc/aspeed/reset-aspeed.h --- a/include/soc/aspeed/reset-aspeed.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/soc/aspeed/reset-aspeed.h 2025-12-23 10:16:21.214031161 +0000 ++++ b/include/soc/aspeed/reset-aspeed.h 2026-04-08 18:03:48.391703840 +0000 @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* @@ -88428,7 +95566,7 @@ diff --git a/include/soc/aspeed/reset-aspeed.h b/include/soc/aspeed/reset-aspeed +#endif /* __RESET_ASPEED_H__ */ diff --git a/include/trace/events/xdma.h b/include/trace/events/xdma.h --- a/include/trace/events/xdma.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/trace/events/xdma.h 2025-12-23 10:16:21.990018155 +0000 ++++ b/include/trace/events/xdma.h 2026-04-08 18:03:49.268687817 +0000 @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + @@ -88558,7 +95696,7 @@ diff --git a/include/trace/events/xdma.h b/include/trace/events/xdma.h +#include diff --git a/include/uapi/linux/aspeed-mctp.h b/include/uapi/linux/aspeed-mctp.h --- a/include/uapi/linux/aspeed-mctp.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/uapi/linux/aspeed-mctp.h 2025-12-23 10:16:21.247030608 +0000 ++++ b/include/uapi/linux/aspeed-mctp.h 2026-04-08 18:03:48.425703219 +0000 @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (c) 2020 Intel Corporation */ @@ -88698,7 +95836,7 @@ diff --git a/include/uapi/linux/aspeed-mctp.h b/include/uapi/linux/aspeed-mctp.h +#endif /* _UAPI_LINUX_ASPEED_MCTP_H */ diff --git a/include/uapi/linux/aspeed-video.h b/include/uapi/linux/aspeed-video.h --- a/include/uapi/linux/aspeed-video.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/uapi/linux/aspeed-video.h 2025-12-23 10:16:21.223031010 +0000 ++++ b/include/uapi/linux/aspeed-video.h 2026-04-08 18:03:48.401703657 +0000 @@ -8,6 +8,15 @@ #include @@ -88717,7 +95855,7 @@ diff --git a/include/uapi/linux/aspeed-video.h b/include/uapi/linux/aspeed-video diff --git a/include/uapi/linux/aspeed-xdma.h b/include/uapi/linux/aspeed-xdma.h --- a/include/uapi/linux/aspeed-xdma.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/uapi/linux/aspeed-xdma.h 2025-12-23 10:16:21.233030843 +0000 ++++ b/include/uapi/linux/aspeed-xdma.h 2026-04-08 18:03:48.410703493 +0000 @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* Copyright IBM Corp 2019 */ @@ -88763,7 +95901,7 @@ diff --git a/include/uapi/linux/aspeed-xdma.h b/include/uapi/linux/aspeed-xdma.h +#endif /* _UAPI_LINUX_ASPEED_XDMA_H_ */ diff --git a/include/uapi/linux/otp_ast2600.h b/include/uapi/linux/otp_ast2600.h --- a/include/uapi/linux/otp_ast2600.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/uapi/linux/otp_ast2600.h 2025-12-23 10:16:21.238030759 +0000 ++++ b/include/uapi/linux/otp_ast2600.h 2026-04-08 18:03:48.415703402 +0000 @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */ +/* @@ -88806,7 +95944,7 @@ diff --git a/include/uapi/linux/otp_ast2600.h b/include/uapi/linux/otp_ast2600.h +#endif /* _UAPI_LINUX_OTP_AST2600_H */ diff --git a/include/uapi/linux/otp_ast2700.h b/include/uapi/linux/otp_ast2700.h --- a/include/uapi/linux/otp_ast2700.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/uapi/linux/otp_ast2700.h 2025-12-23 10:16:21.218031094 +0000 ++++ b/include/uapi/linux/otp_ast2700.h 2026-04-08 18:03:48.396703749 +0000 @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */ +/* @@ -88857,7 +95995,7 @@ diff --git a/include/uapi/linux/otp_ast2700.h b/include/uapi/linux/otp_ast2700.h +#endif /* _UAPI_LINUX_OTP_AST2700_H */ diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h --- a/include/uapi/linux/videodev2.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/uapi/linux/videodev2.h 2025-12-23 10:16:22.013017770 +0000 ++++ b/include/uapi/linux/videodev2.h 2026-04-08 18:03:49.290687415 +0000 @@ -880,6 +880,7 @@ /* Flags */ #define V4L2_PIX_FMT_FLAG_PREMUL_ALPHA 0x00000001