From 2a8511ddcad03f6b192272aea7848f70b1dd726e Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sat, 17 Apr 2021 13:33:54 -0500
Subject: [PATCH 1/9] sunxi: binman: Enable SPL FIT loading for 32-bit SoCs

Now that Crust (SCP firmware) has support for H3, we need a FIT image to
load it. H3 also needs to load a SoC-specific eGon blob to support CPU 0
hotplug. Let's first enable FIT support before adding extra firmware.

Update the binman description to work on either 32-bit or 64-bit SoCs:
 - Make BL31 optional, since it is not used on 32-bit SoCs (though BL32
   may be used in the future).
 - Explicitly set the minimum offset of the FIT to 32 KiB, since SPL on
   some boards is still only 24 KiB large even with FIT support enabled.
   CONFIG_SPL_PAD_TO cannot be used because it is not defined for H616.

FIT unlocks more features (signatures, multiple DTBs, etc.), so enable
it by default. A10 (sun4i) only has 24 KiB of SRAM A1, so it needs
SPL_FIT_IMAGE_TINY. For simplicity, enable that option everywhere.

Cover-letter:
sunxi: SPL FIT support for 32-bit sunxi SoCs
This series makes the necessary changes so 32-bit sunxi SoCs can load
additional device trees or firmware from SPL along with U-Boot proper.

There was no existing binman entry property that put the FIT at the
right offset. The minimum offset is 32k, but this matches neither the
SPL size (which is no more than 24k on some SoCs) nor the FIT alignment
(which is 512 bytes in practice due to SPL size constraints). So instead
of adding a new property, I fixed what is arguably a bug in the offset
property -- though this strategy will not work if someone is
intentionally creating overlapping entries.
END
Series-to: sunxi
Series-to: sjg
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
 arch/arm/Kconfig               |  1 +
 arch/arm/dts/sunxi-u-boot.dtsi | 44 ++++++++++++++++++++++------------
 common/spl/Kconfig             |  7 +++---
 3 files changed, 34 insertions(+), 18 deletions(-)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 3f70503b61b..e9724e39338 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1169,6 +1169,7 @@ config ARCH_SUNXI
 	imply SPL_GPIO
 	imply SPL_LIBCOMMON_SUPPORT
 	imply SPL_LIBGENERIC_SUPPORT
+	imply SPL_LOAD_FIT
 	imply SPL_MMC if MMC
 	imply SPL_POWER
 	imply SPL_SERIAL
diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi
index a0c8abb7033..3c6ec626ea6 100644
--- a/arch/arm/dts/sunxi-u-boot.dtsi
+++ b/arch/arm/dts/sunxi-u-boot.dtsi
@@ -1,13 +1,19 @@
 #include <config.h>
 
-#ifdef CONFIG_MACH_SUN50I_H6
-#define BL31_ADDR 0x104000
-#define  SCP_ADDR 0x114000
-#elif defined(CONFIG_MACH_SUN50I_H616)
-#define BL31_ADDR 0x40000000
+#ifdef CONFIG_ARM64
+#define ARCH "arm64"
 #else
-#define BL31_ADDR  0x44000
-#define  SCP_ADDR  0x50000
+#define ARCH "arm"
+#endif
+
+#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN50I_H5)
+#define BL31_ADDR	0x00044000
+#define SCP_ADDR	0x00050000
+#elif defined(CONFIG_MACH_SUN50I_H6)
+#define BL31_ADDR	0x00104000
+#define SCP_ADDR	0x00114000
+#elif defined(CONFIG_MACH_SUN50I_H616)
+#define BL31_ADDR	0x40000000
 #endif
 
 / {
@@ -44,30 +50,33 @@
 			filename = "spl/sunxi-spl.bin";
 		};
 
-#ifdef CONFIG_ARM64
+#ifdef CONFIG_SPL_LOAD_FIT
 		fit {
-			description = "Configuration to load ATF before U-Boot";
+			description = "Configuration to load U-Boot and firmware";
+			offset = <32768>;
 			#address-cells = <1>;
 			fit,fdt-list = "of-list";
 
 			images {
 				uboot {
-					description = "U-Boot (64-bit)";
+					description = "U-Boot";
 					type = "standalone";
 					os = "u-boot";
-					arch = "arm64";
+					arch = ARCH;
 					compression = "none";
 					load = <CONFIG_TEXT_BASE>;
+					entry = <CONFIG_TEXT_BASE>;
 
 					u-boot-nodtb {
 					};
 				};
 
+#ifdef BL31_ADDR
 				atf {
 					description = "ARM Trusted Firmware";
 					type = "firmware";
 					os = "arm-trusted-firmware";
-					arch = "arm64";
+					arch = ARCH;
 					compression = "none";
 					load = <BL31_ADDR>;
 					entry = <BL31_ADDR>;
@@ -77,6 +86,7 @@
 						missing-msg = "atf-bl31-sunxi";
 					};
 				};
+#endif
 
 #ifdef SCP_ADDR
 				scp {
@@ -105,12 +115,16 @@
 
 				@config-SEQ {
 					description = "NAME";
+#ifdef BL31_ADDR
 					firmware = "atf";
-#ifndef SCP_ADDR
-					loadables = "uboot";
 #else
-					loadables = "scp", "uboot";
+					firmware = "uboot";
+#endif
+					loadables =
+#ifdef SCP_ADDR
+						    "scp",
 #endif
+						    "uboot";
 					fdt = "fdt-SEQ";
 				};
 			};
diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index 25cd18afda7..8176cd59cf8 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -76,7 +76,9 @@ config SPL_SIZE_LIMIT_PROVIDE_STACK
 
 config SPL_MAX_SIZE
 	hex "Maximum size of the SPL image, excluding BSS"
+	default 0x37fa0 if MACH_SUN50I_H616
 	default 0x30000 if ARCH_MX6 && MX6_OCRAM_256KB
+	default 0x25fa0 if MACH_SUN50I_H6
 	default 0x1b000 if AM33XX && !TI_SECURE_DEVICE
 	default 0x10000 if ARCH_MX6 && !MX6_OCRAM_256KB
 	default 0x7fa0 if SUNXI_SRAM_ADDRESS = 0x10000
@@ -97,7 +99,7 @@ config SPL_PAD_TO
 	default 0x31000 if ARCH_MX6 && MX6_OCRAM_256KB
 	default 0x11000 if ARCH_MX7 || (ARCH_MX6 && !MX6_OCRAM_256KB)
 	default 0x10000 if ARCH_KEYSTONE
-	default 0x8000 if ARCH_SUNXI && !MACH_SUN50I_H616
+	default 0x0 if ARCH_SUNXI
 	default 0x0 if ARCH_MTMIPS
 	default TPL_MAX_SIZE if TPL_MAX_SIZE > SPL_MAX_SIZE
 	default SPL_MAX_SIZE
@@ -568,8 +570,7 @@ config SYS_MMCSD_RAW_MODE_EMMC_BOOT_PARTITION
 config SPL_FIT_IMAGE_TINY
 	bool "Remove functionality from SPL FIT loading to reduce size"
 	depends on SPL_FIT
-	default y if MACH_SUN50I || MACH_SUN50I_H5 || SUN50I_GEN_H6
-	default y if ARCH_IMX8M || ARCH_IMX9
+	default y if ARCH_IMX8M || ARCH_IMX9 || ARCH_SUNXI
 	help
 	  Enable this to reduce the size of the FIT image loading code
 	  in SPL, if space for the SPL binary is very tight.
-- 
2.34.1


From 86a09f129dc685411c05086239a8befbb715b716 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sat, 9 Oct 2021 22:00:22 -0500
Subject: [PATCH 2/9] sunxi: psci: Avoid hanging when CPU 0 is hot-unplugged

Do not try to send an SGI from CPU 0 to itself. Since FIQs are masked
when entering monitor mode, this will hang. Plus, CPU 0 cannot fully
power itself off anyway. Instead, have it turn FIQs back on and continue
servicing SGIs from other cores.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---
 arch/arm/cpu/armv7/sunxi/psci.c | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c
index 5cb8cfa6cf3..098e2b12bf0 100644
--- a/arch/arm/cpu/armv7/sunxi/psci.c
+++ b/arch/arm/cpu/armv7/sunxi/psci.c
@@ -65,6 +65,15 @@
 #define SUNXI_R_CPUCFG_BASE			0
 #endif
 
+static inline u32 __secure cp15_read_mpidr(void)
+{
+	u32 val;
+
+	asm volatile ("mrc p15, 0, %0, c0, c0, 5" : "=r" (val));
+
+	return val;
+}
+
 static void __secure cp15_write_cntp_tval(u32 tval)
 {
 	asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval));
@@ -331,9 +340,14 @@ s32 __secure psci_cpu_off(void)
 {
 	psci_cpu_off_common();
 
-	/* Ask CPU0 via SGI15 to pull the rug... */
-	writel(BIT(16) | 15, GICD_BASE + GICD_SGIR);
-	dsb();
+	if (cp15_read_mpidr() & 3) {
+		/* Ask CPU0 via SGI15 to pull the rug... */
+		writel(BIT(16) | 15, GICD_BASE + GICD_SGIR);
+		dsb();
+	} else {
+		/* Unmask FIQs to service SGI15. */
+		asm volatile ("cpsie f");
+	}
 
 	/* Wait to be turned off */
 	while (1)
-- 
2.34.1


From 1f66111fddccfea38c7100f26b716880e23fb00b Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sat, 9 Oct 2021 17:12:57 -0500
Subject: [PATCH 3/9] sunxi: psci: Add support for H3 CPU 0 hotplug

Due to a bug in the H3 SoC, where the CPU 0 hotplug flag cannot be
written, resuming CPU 0 requires using the "Super Standby" code path in
the BROM instead of the hotplug path. This path requires jumping to an
eGON image in SRAM.

Add support to the build system to generate this eGON image and include
it in the FIT, and add code to direct the BROM to its location in SRAM.

Since the Super Standby code path in the BROM initializes the CPU and
AHB1 clocks to 24 MHz, those registers need to be restored after control
passes back to U-Boot. Furthermore, because the BROM lowers the AHB1
clock divider to /1 before switching to the lower-frequency parent,
PLL_PERIPH0 must be bypassed to prevent AHB1 from temporarily running at
600 MHz. Otherwise, this locks up the SoC.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---
 Makefile                        | 17 +++++++++++++++++
 arch/arm/cpu/armv7/sunxi/psci.c | 33 +++++++++++++++++++++++++++++++++
 arch/arm/dts/sunxi-u-boot.dtsi  | 23 ++++++++++++++++++++++-
 include/configs/sun8i.h         |  4 ++++
 4 files changed, 76 insertions(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index 750bbdb1b71..7ef706d79f1 100644
--- a/Makefile
+++ b/Makefile
@@ -1024,6 +1024,23 @@ ifeq ($(CONFIG_ARCH_ROCKCHIP)_$(CONFIG_SPL_FRAMEWORK),y_)
 INPUTS-y += u-boot.img
 endif
 
+ifeq ($(CONFIG_MACH_SUN8I_H3)$(CONFIG_ARMV7_PSCI),yy)
+INPUTS-$(CONFIG_ARMV7_PSCI) += u-boot-resume.img
+
+MKIMAGEFLAGS_u-boot-resume.img := -B 0x400 -T sunxi_egon
+
+u-boot-resume.img: u-boot-resume.bin
+	$(call if_changed,mkimage)
+
+OBJCOPYFLAGS_u-boot-resume.bin := -O binary
+
+u-boot-resume.bin: u-boot-resume.o
+	$(call if_changed,objcopy)
+
+u-boot-resume.S: u-boot
+	@sed -En 's/(0x[[:xdigit:]]+) +psci_cpu_entry/ldr pc, =\1/p' $<.map > $@
+endif
+
 INPUTS-$(CONFIG_X86) += u-boot-x86-start16.bin u-boot-x86-reset16.bin \
 	$(if $(CONFIG_SPL_X86_16BIT_INIT),spl/u-boot-spl.bin) \
 	$(if $(CONFIG_TPL_X86_16BIT_INIT),tpl/u-boot-tpl.bin)
diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c
index 098e2b12bf0..889190792e7 100644
--- a/arch/arm/cpu/armv7/sunxi/psci.c
+++ b/arch/arm/cpu/armv7/sunxi/psci.c
@@ -10,6 +10,7 @@
 #include <common.h>
 #include <asm/cache.h>
 
+#include <asm/arch/clock.h>
 #include <asm/arch/cpu.h>
 #include <asm/armv7.h>
 #include <asm/gic.h>
@@ -31,7 +32,9 @@
 #define SUNXI_CPU_RST(cpu)			(0x40 + (cpu) * 0x40 + 0x0)
 #define SUNXI_CPU_STATUS(cpu)			(0x40 + (cpu) * 0x40 + 0x8)
 #define SUNXI_GEN_CTRL				(0x184)
+#define SUNXI_SUPER_STANDY_FLAG			(0x1a0)
 #define SUNXI_PRIV0				(0x1a4)
+#define SUNXI_PRIV1				(0x1a8)
 #define SUN7I_CPU1_PWR_CLAMP			(0x1b0)
 #define SUN7I_CPU1_PWROFF			(0x1b4)
 #define SUNXI_DBG_CTRL1				(0x1e4)
@@ -138,6 +141,13 @@ static void __secure sunxi_cpu_set_entry(int __always_unused cpu, void *entry)
 		       SUNXI_R_CPUCFG_BASE + SUN8I_R528_SOFT_ENTRY);
 	} else {
 		writel((u32)entry, SUNXI_CPUCFG_BASE + SUNXI_PRIV0);
+
+		if (IS_ENABLED(CONFIG_MACH_SUN8I_H3) {
+			/* Redirect CPU 0 to the secure monitor via the resume shim. */
+			writel(0x16aaefe8, SUNXI_CPUCFG_BASE + SUNXI_SUPER_STANDY_FLAG);
+			writel(0xaa16efe8, SUNXI_CPUCFG_BASE + SUNXI_SUPER_STANDY_FLAG);
+			writel(SUNXI_RESUME_BASE, SUNXI_CPUCFG_BASE + SUNXI_PRIV1);
+		}
 	}
 }
 
@@ -307,7 +317,10 @@ out:
 int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc,
 			 u32 context_id)
 {
+	struct sunxi_ccm_reg *ccu = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
 	u32 cpu = (mpidr & 0x3);
+	u32 cpu_clk;
+	u32 bus_clk;
 
 	/* store target PC and context id */
 	psci_save(cpu, pc, context_id);
@@ -324,12 +337,32 @@ int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc,
 	/* Lock CPU (Disable external debug access) */
 	sunxi_cpu_set_locking(cpu, true);
 
+	if (IS_ENABLED(CONFIG_MACH_SUN8I_H3) && cpu == 0) {
+		/* Save registers that will be clobbered by the BROM. */
+		cpu_clk = readl(&ccu->cpu_axi_cfg);
+		bus_clk = readl(&ccu->ahb1_apb1_div);
+
+		/* Bypass PLL_PERIPH0 so AHB1 frequency does not spike. */
+		setbits_le32(&ccu->pll6_cfg, BIT(25));
+	}
+
 	/* Power up target CPU */
 	sunxi_cpu_set_power(cpu, true);
 
 	/* De-assert reset on target CPU */
 	sunxi_cpu_set_reset(cpu, false);
 
+	if (IS_ENABLED(CONFIG_MACH_SUN8I_H3) && cpu == 0) {
+		/* Spin until the BROM has clobbered the clock registers. */
+		while (readl(&ccu->ahb1_apb1_div) != 0x00001100);
+
+		/* Restore the registers and turn off PLL_PERIPH0 bypass. */
+		writel(cpu_clk, &ccu->cpu_axi_cfg);
+		writel(bus_clk, &ccu->ahb1_apb1_div);
+
+		clrbits_le32(&ccu->pll6_cfg, BIT(25));
+	}
+
 	/* Unlock CPU (Reenable external debug access) */
 	sunxi_cpu_set_locking(cpu, false);
 
diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi
index 3c6ec626ea6..55b2dbd7992 100644
--- a/arch/arm/dts/sunxi-u-boot.dtsi
+++ b/arch/arm/dts/sunxi-u-boot.dtsi
@@ -6,7 +6,11 @@
 #define ARCH "arm"
 #endif
 
-#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN50I_H5)
+#if defined(CONFIG_MACH_SUN8I_H3)
+#ifdef CONFIG_ARMV7_PSCI
+#define RESUME_ADDR	SUNXI_RESUME_BASE
+#endif
+#elif defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN50I_H5)
 #define BL31_ADDR	0x00044000
 #define SCP_ADDR	0x00050000
 #elif defined(CONFIG_MACH_SUN50I_H6)
@@ -88,6 +92,20 @@
 				};
 #endif
 
+#ifdef RESUME_ADDR
+				resume {
+					description = "Super Standby resume image";
+					type = "standalone";
+					arch = ARCH;
+					compression = "none";
+					load = <RESUME_ADDR>;
+
+					blob-ext {
+						filename = "u-boot-resume.img";
+					};
+				};
+#endif
+
 #ifdef SCP_ADDR
 				scp {
 					description = "SCP firmware";
@@ -121,6 +139,9 @@
 					firmware = "uboot";
 #endif
 					loadables =
+#ifdef RESUME_ADDR
+						    "resume",
+#endif
 #ifdef SCP_ADDR
 						    "scp",
 #endif
diff --git a/include/configs/sun8i.h b/include/configs/sun8i.h
index 608a055892d..98e68e068bd 100644
--- a/include/configs/sun8i.h
+++ b/include/configs/sun8i.h
@@ -8,6 +8,10 @@
 #ifndef __CONFIG_H
 #define __CONFIG_H
 
+#define SUNXI_RESUME_BASE		(CONFIG_ARMV7_SECURE_BASE + \
+					 CONFIG_ARMV7_SECURE_MAX_SIZE)
+#define SUNXI_RESUME_SIZE		1024
+
 #include <configs/sunxi-common.h>
 
 #define CONFIG_MACH_TYPE	(0x1029)
-- 
2.34.1


From e856c2fd27de2bf3da5882ec773285e1960e181e Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sat, 9 Oct 2021 14:58:27 -0500
Subject: [PATCH 4/9] remoteproc: Add a driver for the Allwinner AR100

Signed-off-by: Samuel Holland <samuel@sholland.org>
---
 drivers/remoteproc/Kconfig             |   9 ++
 drivers/remoteproc/Makefile            |   1 +
 drivers/remoteproc/sun6i_ar100_rproc.c | 111 +++++++++++++++++++++++++
 3 files changed, 121 insertions(+)
 create mode 100644 drivers/remoteproc/sun6i_ar100_rproc.c

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 781de530aff..73b2bc5cfe1 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -41,6 +41,15 @@ config REMOTEPROC_STM32_COPRO
 	  Say 'y' here to add support for STM32 Cortex-M4 coprocessors via the
 	  remoteproc framework.
 
+config REMOTEPROC_SUN6I_AR100
+	bool "Support for Allwinner AR100 SCP"
+	select REMOTEPROC
+	depends on ARCH_SUNXI
+	help
+	  Say 'y' here to support Allwinner's AR100 System Control Processor
+	  (SCP), found in various sun6i/sun8i/sun50i family SoCs, through the
+	  remoteproc framework.
+
 config REMOTEPROC_TI_K3_ARM64
 	bool "Support for TI's K3 based ARM64 remoteproc driver"
 	select REMOTEPROC
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index e09ed1aa4d4..6e788662ce7 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_$(SPL_)REMOTEPROC) += rproc-uclass.o rproc-elf-loader.o
 obj-$(CONFIG_K3_SYSTEM_CONTROLLER) += k3_system_controller.o
 obj-$(CONFIG_REMOTEPROC_SANDBOX) += sandbox_testproc.o
 obj-$(CONFIG_REMOTEPROC_STM32_COPRO) += stm32_copro.o
+obj-$(CONFIG_REMOTEPROC_SUN6I_AR100) += sun6i_ar100_rproc.o
 obj-$(CONFIG_REMOTEPROC_TI_K3_ARM64) += ti_k3_arm64_rproc.o
 obj-$(CONFIG_REMOTEPROC_TI_K3_DSP) += ti_k3_dsp_rproc.o
 obj-$(CONFIG_REMOTEPROC_TI_K3_R5F) += ti_k3_r5f_rproc.o
diff --git a/drivers/remoteproc/sun6i_ar100_rproc.c b/drivers/remoteproc/sun6i_ar100_rproc.c
new file mode 100644
index 00000000000..c94f6c752bd
--- /dev/null
+++ b/drivers/remoteproc/sun6i_ar100_rproc.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <dm.h>
+#include <errno.h>
+#include <remoteproc.h>
+#include <asm/io.h>
+
+#define SUNXI_SCP_MAGIC			0xb4400012
+
+#define OR1K_VEC_FIRST			0x01
+#define OR1K_VEC_LAST			0x0e
+#define OR1K_VEC_ADDR(n)		(0x100 * (n))
+
+struct sun6i_ar100_rproc_priv {
+	void	*cfg_base;
+	ulong	sram_base;
+};
+
+static int sun6i_ar100_rproc_load(struct udevice *dev, ulong addr, ulong size)
+{
+	struct sun6i_ar100_rproc_priv *priv = dev_get_priv(dev);
+
+	/* Check for a valid SCP firmware. */
+	if (readl_relaxed(addr) != SUNXI_SCP_MAGIC)
+		return -ENOENT;
+
+	/* Program exception vectors to the firmware entry point. */
+	for (u32 i = OR1K_VEC_FIRST; i <= OR1K_VEC_LAST; ++i) {
+		ulong vector = priv->sram_base + OR1K_VEC_ADDR(i);
+		ulong offset = addr - vector;
+
+		writel_relaxed(offset >> 2, vector);
+	}
+
+	return 0;
+}
+
+static int sun6i_ar100_rproc_start(struct udevice *dev)
+{
+	struct sun6i_ar100_rproc_priv *priv = dev_get_priv(dev);
+
+	setbits_le32(priv->cfg_base, BIT(0));
+
+	return 0;
+}
+
+static int sun6i_ar100_rproc_stop(struct udevice *dev)
+{
+	struct sun6i_ar100_rproc_priv *priv = dev_get_priv(dev);
+
+	clrbits_le32(priv->cfg_base, BIT(0));
+
+	return 0;
+}
+
+static int sun6i_ar100_rproc_reset(struct udevice *dev)
+{
+	int ret;
+
+	ret = sun6i_ar100_rproc_stop(dev);
+	if (ret)
+		return ret;
+
+	return sun6i_ar100_rproc_start(dev);
+}
+
+static int sun6i_ar100_rproc_is_running(struct udevice *dev)
+{
+	struct sun6i_ar100_rproc_priv *priv = dev_get_priv(dev);
+
+	return !(readl_relaxed(priv->cfg_base) & BIT(0));
+}
+
+static const struct dm_rproc_ops sun6i_ar100_rproc_ops = {
+	.load		= sun6i_ar100_rproc_load,
+	.start		= sun6i_ar100_rproc_start,
+	.stop		= sun6i_ar100_rproc_stop,
+	.reset		= sun6i_ar100_rproc_reset,
+	.is_running	= sun6i_ar100_rproc_is_running,
+};
+
+static int sun6i_ar100_rproc_probe(struct udevice *dev)
+{
+	struct sun6i_ar100_rproc_priv *priv = dev_get_priv(dev);
+	struct ofnode_phandle_args sram_handle;
+	int ret;
+
+	priv->cfg_base = dev_read_addr_ptr(dev);
+
+	ret = dev_read_phandle_with_args(dev, "sram", NULL, 0, 0, &sram_handle);
+	if (ret)
+		return ret;
+
+	priv->sram_base = ofnode_get_addr(sram_handle.node);
+
+	return 0;
+}
+
+static const struct udevice_id sun6i_ar100_rproc_ids[] = {
+	{ .compatible = "allwinner,sun6i-a31-ar100" },
+	{ }
+};
+
+U_BOOT_DRIVER(sun6i_ar100_rproc) = {
+	.name		= "sun6i_ar100_rproc",
+	.id		= UCLASS_REMOTEPROC,
+	.of_match	= sun6i_ar100_rproc_ids,
+	.probe		= sun6i_ar100_rproc_probe,
+	.priv_auto	= sizeof(struct sun6i_ar100_rproc_priv),
+	.ops		= &sun6i_ar100_rproc_ops,
+};
-- 
2.34.1


From 617f16f5ec1e38620b162746559f01d32108291d Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sat, 9 Oct 2021 15:04:16 -0500
Subject: [PATCH 5/9] arm: dts: sunxi: h3: Add nodes for AR100 remoteproc

Signed-off-by: Samuel Holland <samuel@sholland.org>
---
 arch/arm/dts/sun8i-h3.dtsi | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/arch/arm/dts/sun8i-h3.dtsi b/arch/arm/dts/sun8i-h3.dtsi
index eac2349a238..b88dcd42720 100644
--- a/arch/arm/dts/sun8i-h3.dtsi
+++ b/arch/arm/dts/sun8i-h3.dtsi
@@ -170,6 +170,14 @@
 			#size-cells = <1>;
 			ranges;
 
+			sram_a2: sram@40000 {
+				compatible = "mmio-sram";
+				reg = <0x00040000 0xc000>;
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0 0x00040000 0xc000>;
+			};
+
 			sram_c: sram@1d00000 {
 				compatible = "mmio-sram";
 				reg = <0x01d00000 0x80000>;
@@ -239,6 +247,12 @@
 			nvmem-cell-names = "calibration";
 			#thermal-sensor-cells = <0>;
 		};
+
+		remoteproc@1f01c00 {
+			compatible = "allwinner,sun6i-a31-ar100";
+			reg = <0x01f01c00 0x400>;
+			sram = <&sram_a2>;
+		};
 	};
 
 	thermal-zones {
-- 
2.34.1


From ccd19c93d7ba4f9ffc77e2a4acce7f97023fb6fc Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sat, 17 Apr 2021 13:33:54 -0500
Subject: [PATCH 6/9] sunxi: Enable support for SCP firmware on H3

Now that issues with the BROM have been sorted out, we can implement
PSCI system suspend on H3 by delegating to SCP firmware. Let's start by
including the firmware in the FIT image and starting the coprocessor if
valid firmware is loaded.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---
 arch/arm/dts/sunxi-u-boot.dtsi | 1 +
 board/sunxi/board.c            | 8 ++++++++
 include/configs/sun8i.h        | 3 +++
 3 files changed, 12 insertions(+)

diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi
index 55b2dbd7992..b8585dea2a6 100644
--- a/arch/arm/dts/sunxi-u-boot.dtsi
+++ b/arch/arm/dts/sunxi-u-boot.dtsi
@@ -9,6 +9,7 @@
 #if defined(CONFIG_MACH_SUN8I_H3)
 #ifdef CONFIG_ARMV7_PSCI
 #define RESUME_ADDR	SUNXI_RESUME_BASE
+#define SCP_ADDR	SUNXI_SCP_BASE
 #endif
 #elif defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN50I_H5)
 #define BL31_ADDR	0x00044000
diff --git a/board/sunxi/board.c b/board/sunxi/board.c
index ec45ed3e05f..95513a651b1 100644
--- a/board/sunxi/board.c
+++ b/board/sunxi/board.c
@@ -19,6 +19,7 @@
 #include <init.h>
 #include <log.h>
 #include <mmc.h>
+#include <remoteproc.h>
 #include <axp_pmic.h>
 #include <generic-phy.h>
 #include <phy-sun4i-usb.h>
@@ -866,6 +867,13 @@ int board_late_init(void)
 	usb_ether_init();
 #endif
 
+#ifdef CONFIG_REMOTEPROC_SUN6I_AR100
+	if (!rproc_load(0, SUNXI_SCP_BASE, SUNXI_SCP_MAX_SIZE)) {
+		puts("Starting SCP...\n");
+		rproc_start(0);
+	}
+#endif
+
 	return 0;
 }
 
diff --git a/include/configs/sun8i.h b/include/configs/sun8i.h
index 98e68e068bd..506ce357078 100644
--- a/include/configs/sun8i.h
+++ b/include/configs/sun8i.h
@@ -12,6 +12,9 @@
 					 CONFIG_ARMV7_SECURE_MAX_SIZE)
 #define SUNXI_RESUME_SIZE		1024
 
+#define SUNXI_SCP_BASE			(SUNXI_RESUME_BASE + SUNXI_RESUME_SIZE)
+#define SUNXI_SCP_MAX_SIZE		(16 * 1024)
+
 #include <configs/sunxi-common.h>
 
 #define CONFIG_MACH_TYPE	(0x1029)
-- 
2.34.1


From 9058b73486acedc5230679232574c678ef346546 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sat, 9 Oct 2021 22:43:26 -0500
Subject: [PATCH 7/9] arm: psci: Add definitions for PSCI v1.1

Add the new option, function IDs, and prototypes for PSCI v1.1
implementations. In the process, fix some issues with the existing
definitions:
 - Fix the incorrectly-named ARM_PSCI_0_2_FN64_SYSTEM_RESET2.
 - Replace the deprecated "affinity_level" naming with "power_level".

Signed-off-by: Samuel Holland <samuel@sholland.org>
---
 arch/arm/cpu/armv7/Kconfig    |  3 +++
 arch/arm/include/asm/psci.h   |  5 +++--
 arch/arm/include/asm/system.h | 14 +++++++++-----
 arch/arm/lib/psci-dt.c        |  2 ++
 4 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/arch/arm/cpu/armv7/Kconfig b/arch/arm/cpu/armv7/Kconfig
index 4eb34b7b449..ec3d31e7505 100644
--- a/arch/arm/cpu/armv7/Kconfig
+++ b/arch/arm/cpu/armv7/Kconfig
@@ -91,6 +91,9 @@ choice
 	help
 	  Select the supported PSCI version.
 
+config ARMV7_PSCI_1_1
+	bool "PSCI V1.1"
+
 config ARMV7_PSCI_1_0
 	bool "PSCI V1.0"
 
diff --git a/arch/arm/include/asm/psci.h b/arch/arm/include/asm/psci.h
index 7343b941ef0..27c2d15521f 100644
--- a/arch/arm/include/asm/psci.h
+++ b/arch/arm/include/asm/psci.h
@@ -22,9 +22,9 @@
 #include <linux/bitops.h>
 #endif
 
-#define ARM_PSCI_VER_1_1		(0x00010001)
-#define ARM_PSCI_VER_1_0		(0x00010000)
 #define ARM_PSCI_VER_0_2		(0x00000002)
+#define ARM_PSCI_VER_1_0		(0x00010000)
+#define ARM_PSCI_VER_1_1		(0x00010001)
 
 /* PSCI 0.1 interface */
 #define ARM_PSCI_FN_BASE		0x95c1ba5e
@@ -87,6 +87,7 @@
 #define ARM_PSCI_1_0_FN64_STAT_COUNT		ARM_PSCI_0_2_FN64(17)
 
 /* PSCI 1.1 interface */
+#define ARM_PSCI_1_1_FN_SYSTEM_RESET2		ARM_PSCI_0_2_FN(18)
 #define ARM_PSCI_1_1_FN64_SYSTEM_RESET2		ARM_PSCI_0_2_FN64(18)
 
 /* 1KB stack per core */
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h
index 0eae857e73a..63f0c4c39ea 100644
--- a/arch/arm/include/asm/system.h
+++ b/arch/arm/include/asm/system.h
@@ -558,16 +558,20 @@ void mmu_page_table_flush(unsigned long start, unsigned long stop);
 #ifdef CONFIG_ARMV7_PSCI
 void psci_arch_cpu_entry(void);
 void psci_arch_init(void);
+
 u32 psci_version(void);
-s32 psci_features(u32 function_id, u32 psci_fid);
+s32 psci_cpu_suspend(u32 function_id, u32 power_state, u32 pc, u32 context_id);
 s32 psci_cpu_off(void);
-s32 psci_cpu_on(u32 function_id, u32 target_cpu, u32 pc,
-		u32 context_id);
-s32 psci_affinity_info(u32 function_id, u32 target_affinity,
-		       u32  lowest_affinity_level);
+s32 psci_cpu_on(u32 function_id, u32 target_cpu, u32 pc, u32 context_id);
+s32 psci_affinity_info(u32 function_id, u32 target_affinity, u32 power_level);
 u32 psci_migrate_info_type(void);
 void psci_system_off(void);
 void psci_system_reset(void);
+s32 psci_features(u32 function_id, u32 psci_fid);
+s32 psci_cpu_default_suspend(u32 function_id, u32 pc, u32 context_id);
+s32 psci_node_hw_state(u32 function_id, u32 target_cpu, u32 power_level);
+s32 psci_system_suspend(u32 function_id, u32 pc, u32 context_id);
+s32 psci_system_reset2(u32 function_id, u32 reset_type, u32 cookie);
 #endif
 
 #endif /* __ASSEMBLY__ */
diff --git a/arch/arm/lib/psci-dt.c b/arch/arm/lib/psci-dt.c
index 903b3357048..ea9d1c8355c 100644
--- a/arch/arm/lib/psci-dt.c
+++ b/arch/arm/lib/psci-dt.c
@@ -66,6 +66,8 @@ int fdt_psci(void *fdt)
 init_psci_node:
 #if CONFIG_IS_ENABLED(ARMV8_SEC_FIRMWARE_SUPPORT)
 	psci_ver = sec_firmware_support_psci_version();
+#elif defined(CONFIG_ARMV7_PSCI_1_1)
+	psci_ver = ARM_PSCI_VER_1_1;
 #elif defined(CONFIG_ARMV7_PSCI_1_0) || defined(CONFIG_ARMV8_PSCI)
 	psci_ver = ARM_PSCI_VER_1_0;
 #elif defined(CONFIG_ARMV7_PSCI_0_2)
-- 
2.34.1


From ff51f57f8cdbeeef4366f0c95675b8641bc6dcef Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sat, 9 Oct 2021 23:01:05 -0500
Subject: [PATCH 8/9] sunxi: psci: Delegate PSCI to SCPI

This adds a new PSCI implementation which communicates with SCP firmware
running on the AR100 using the SCPI protocol. This allows it to support
the full set of PSCI v1.1 features, including CPU idle states, system
suspend, and multiple reset methods.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---
 arch/arm/cpu/armv7/Kconfig           |   1 +
 arch/arm/cpu/armv7/sunxi/Makefile    |   4 +
 arch/arm/cpu/armv7/sunxi/psci-scpi.c | 451 +++++++++++++++++++++++++++
 3 files changed, 456 insertions(+)
 create mode 100644 arch/arm/cpu/armv7/sunxi/psci-scpi.c

diff --git a/arch/arm/cpu/armv7/Kconfig b/arch/arm/cpu/armv7/Kconfig
index ec3d31e7505..9dccc122538 100644
--- a/arch/arm/cpu/armv7/Kconfig
+++ b/arch/arm/cpu/armv7/Kconfig
@@ -86,6 +86,7 @@ config ARMV7_PSCI
 choice
 	prompt "Supported PSCI version"
 	depends on ARMV7_PSCI
+	default ARMV7_PSCI_1_1 if MACH_SUN8I_H3
 	default ARMV7_PSCI_0_1 if ARCH_SUNXI
 	default ARMV7_PSCI_1_0
 	help
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile
index 3e975b366c4..6473b9acbde 100644
--- a/arch/arm/cpu/armv7/sunxi/Makefile
+++ b/arch/arm/cpu/armv7/sunxi/Makefile
@@ -13,8 +13,12 @@ obj-$(CONFIG_MACH_SUN6I)	+= sram.o
 obj-$(CONFIG_MACH_SUN8I)	+= sram.o
 
 ifndef CONFIG_SPL_BUILD
+ifdef CONFIG_MACH_SUN8I_H3
+obj-$(CONFIG_ARMV7_PSCI)	+= psci-scpi.o
+else
 obj-$(CONFIG_ARMV7_PSCI)	+= psci.o
 endif
+endif
 
 ifdef CONFIG_SPL_BUILD
 obj-y	+= fel_utils.o
diff --git a/arch/arm/cpu/armv7/sunxi/psci-scpi.c b/arch/arm/cpu/armv7/sunxi/psci-scpi.c
new file mode 100644
index 00000000000..9672eae8aef
--- /dev/null
+++ b/arch/arm/cpu/armv7/sunxi/psci-scpi.c
@@ -0,0 +1,451 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2016 Chen-Yu Tsai <wens@csie.org>
+ * Copyright (C) 2018-2021 Samuel Holland <samuel@sholland.org>
+ */
+
+#include <common.h>
+#include <asm/arch/cpu.h>
+#include <asm/armv7.h>
+#include <asm/gic.h>
+#include <asm/io.h>
+#include <asm/psci.h>
+#include <asm/secure.h>
+#include <asm/system.h>
+
+#define	GICD_BASE		(SUNXI_GIC400_BASE + GIC_DIST_OFFSET)
+#define	GICC_BASE		(SUNXI_GIC400_BASE + GIC_CPU_OFFSET_A15)
+
+#define HW_ON			0
+#define HW_OFF			1
+#define HW_STANDBY		2
+
+#define MPIDR_AFFLVL0(mpidr)	(mpidr & 0xf)
+#define MPIDR_AFFLVL1(mpidr)	(mpidr >> 8 & 0xf)
+
+#define SCPI_SHMEM_BASE		0x0004be00
+#define SCPI_SHMEM		((struct scpi_shmem *)SCPI_SHMEM_BASE)
+
+#define SCPI_RX_CHANNEL		1
+#define SCPI_TX_CHANNEL		0
+#define SCPI_VIRTUAL_CHANNEL	BIT(0)
+
+#define SCPI_MESSAGE_SIZE	0x100
+#define SCPI_PAYLOAD_SIZE	(SCPI_MESSAGE_SIZE - sizeof(struct scpi_header))
+
+#define SUNXI_MSGBOX_BASE	0x01c17000
+#define REMOTE_IRQ_STAT_REG	(SUNXI_MSGBOX_BASE + 0x0050)
+#define LOCAL_IRQ_STAT_REG	(SUNXI_MSGBOX_BASE + 0x0070)
+#define MSG_STAT_REG(n)		(SUNXI_MSGBOX_BASE + 0x0140 + 0x4 * (n))
+#define MSG_DATA_REG(n)		(SUNXI_MSGBOX_BASE + 0x0180 + 0x4 * (n))
+
+#define RX_IRQ(n)		BIT(0 + 2 * (n))
+#define TX_IRQ(n)		BIT(1 + 2 * (n))
+
+#define SUNXI_SUPER_STANDY_FLAG			(0x1a0)
+#define SUNXI_PRIV0				(0x1a4)
+#define SUNXI_PRIV1				(0x1a8)
+
+enum {
+	CORE_POWER_LEVEL		= 0,
+	CLUSTER_POWER_LEVEL		= 1,
+	CSS_POWER_LEVEL			= 2,
+};
+
+enum {
+	SCPI_CMD_SCP_READY		= 0x01,
+	SCPI_CMD_SET_CSS_POWER_STATE	= 0x03,
+	SCPI_CMD_GET_CSS_POWER_STATE	= 0x04,
+	SCPI_CMD_SET_SYS_POWER_STATE	= 0x05,
+};
+
+enum {
+	SCPI_E_OK			= 0,
+	SCPI_E_PARAM			= 1,
+	SCPI_E_ALIGN			= 2,
+	SCPI_E_SIZE			= 3,
+	SCPI_E_HANDLER			= 4,
+	SCPI_E_ACCESS			= 5,
+	SCPI_E_RANGE			= 6,
+	SCPI_E_TIMEOUT			= 7,
+	SCPI_E_NOMEM			= 8,
+	SCPI_E_PWRSTATE			= 9,
+	SCPI_E_SUPPORT			= 10,
+	SCPI_E_DEVICE			= 11,
+	SCPI_E_BUSY			= 12,
+	SCPI_E_OS			= 13,
+	SCPI_E_DATA			= 14,
+	SCPI_E_STATE			= 15,
+};
+
+enum {
+	SCPI_POWER_ON			= 0x00,
+	SCPI_POWER_RETENTION		= 0x01,
+	SCPI_POWER_OFF			= 0x03,
+};
+
+enum {
+	SCPI_SYSTEM_SHUTDOWN		= 0x00,
+	SCPI_SYSTEM_REBOOT		= 0x01,
+	SCPI_SYSTEM_RESET		= 0x02,
+};
+
+struct scpi_header {
+	u8			command;
+	u8			sender;
+	u16			size;
+	u32			status;
+};
+
+struct scpi_message {
+	struct scpi_header	header;
+	u8			payload[SCPI_PAYLOAD_SIZE];
+};
+
+struct scpi_shmem {
+	struct scpi_message	rx;
+	struct scpi_message	tx;
+};
+
+static bool __secure_data gic_dist_init;
+
+static u32 __secure_data lock;
+
+static inline u32 __secure read_mpidr(void)
+{
+	u32 val;
+
+	asm volatile ("mrc p15, 0, %0, c0, c0, 5" : "=r" (val));
+
+	return val;
+}
+
+static void __secure scpi_begin_command(void)
+{
+	u32 mpidr = read_mpidr();
+
+	do {
+		while (readl(&lock));
+		writel(mpidr, &lock);
+		dsb();
+	} while (readl(&lock) != mpidr);
+	while (readl(REMOTE_IRQ_STAT_REG) & RX_IRQ(SCPI_TX_CHANNEL));
+}
+
+static void __secure scpi_send_command(void)
+{
+	writel(SCPI_VIRTUAL_CHANNEL, MSG_DATA_REG(SCPI_TX_CHANNEL));
+}
+
+static void __secure scpi_wait_response(void)
+{
+	while (!readl(MSG_STAT_REG(SCPI_RX_CHANNEL)));
+}
+
+static void __secure scpi_end_command(void)
+{
+	while (readl(MSG_STAT_REG(SCPI_RX_CHANNEL)))
+		readl(MSG_DATA_REG(SCPI_RX_CHANNEL));
+	writel(RX_IRQ(SCPI_RX_CHANNEL), LOCAL_IRQ_STAT_REG);
+	writel(0, &lock);
+}
+
+static void __secure scpi_set_css_power_state(u32 target_cpu, u32 core_state,
+					      u32 cluster_state, u32 css_state)
+{
+	struct scpi_shmem *shmem = SCPI_SHMEM;
+
+	scpi_begin_command();
+
+	shmem->tx.header.command = SCPI_CMD_SET_CSS_POWER_STATE;
+	shmem->tx.header.size = 4;
+
+	shmem->tx.payload[0] = target_cpu    >> 4 | target_cpu;
+	shmem->tx.payload[1] = cluster_state << 4 | core_state;
+	shmem->tx.payload[2] = css_state;
+	shmem->tx.payload[3] = 0;
+
+	scpi_send_command();
+	scpi_end_command();
+}
+
+static s32 __secure scpi_get_css_power_state(u32 target_cpu, u8 *core_states,
+					     u8 *cluster_state)
+{
+	struct scpi_shmem *shmem = SCPI_SHMEM;
+	u32 cluster = MPIDR_AFFLVL1(target_cpu);
+	u32 offset;
+	s32 ret;
+
+	scpi_begin_command();
+
+	shmem->tx.header.command = SCPI_CMD_GET_CSS_POWER_STATE;
+	shmem->tx.header.size = 0;
+
+	scpi_send_command();
+	scpi_wait_response();
+
+	for (offset = 0; offset < shmem->rx.header.size; offset += 2) {
+		if ((shmem->rx.payload[offset] & 0xf) == cluster) {
+			*cluster_state = shmem->rx.payload[offset+0] >> 4;
+			*core_states   = shmem->rx.payload[offset+1];
+
+			break;
+		}
+	}
+
+	ret = shmem->rx.header.status;
+
+	scpi_end_command();
+
+	return ret;
+}
+
+static s32 __secure scpi_set_sys_power_state(u32 sys_state)
+{
+	struct scpi_shmem *shmem = SCPI_SHMEM;
+	s32 ret;
+
+	scpi_begin_command();
+
+	shmem->tx.header.command = SCPI_CMD_SET_SYS_POWER_STATE;
+	shmem->tx.header.size = 1;
+
+	shmem->tx.payload[0] = sys_state;
+
+	scpi_send_command();
+	scpi_wait_response();
+
+	ret = shmem->rx.header.status;
+
+	scpi_end_command();
+
+	return ret;
+}
+
+void psci_enable_smp(void);
+
+static s32 __secure psci_suspend_common(u32 pc, u32 context_id, u32 core_state,
+					u32 cluster_state, u32 css_state)
+
+{
+	u32 target_cpu = read_mpidr();
+
+	if (core_state == SCPI_POWER_OFF)
+		psci_save(MPIDR_AFFLVL0(target_cpu), pc, context_id);
+	if (css_state == SCPI_POWER_OFF)
+		gic_dist_init = true;
+
+	scpi_set_css_power_state(target_cpu, core_state,
+				 cluster_state, css_state);
+
+	psci_cpu_off_common();
+
+	wfi();
+
+	psci_enable_smp();
+
+	return ARM_PSCI_RET_SUCCESS;
+}
+
+u32 __secure psci_version(void)
+{
+	return ARM_PSCI_VER_1_1;
+}
+
+s32 __secure psci_cpu_suspend(u32 __always_unused function_id,
+			      u32 power_state, u32 pc, u32 context_id)
+{
+	return psci_suspend_common(pc, context_id,
+				   power_state >> 0 & 0xf,
+				   power_state >> 4 & 0xf,
+				   power_state >> 8 & 0xf);
+}
+
+s32 __secure psci_cpu_off(void)
+{
+	u32 pc = 0, context_id = 0;
+
+	return psci_suspend_common(pc, context_id, SCPI_POWER_OFF,
+				   SCPI_POWER_OFF, SCPI_POWER_ON);
+}
+
+s32 __secure psci_cpu_on(u32 __always_unused function_id,
+			 u32 target_cpu, u32 pc, u32 context_id)
+{
+	psci_save(MPIDR_AFFLVL0(target_cpu), pc, context_id);
+
+	scpi_set_css_power_state(target_cpu, SCPI_POWER_ON,
+				 SCPI_POWER_ON, SCPI_POWER_ON);
+
+	return ARM_PSCI_RET_SUCCESS;
+}
+
+s32 __secure psci_affinity_info(u32 function_id,
+				u32 target_cpu, u32 power_level)
+{
+	if (power_level != CORE_POWER_LEVEL)
+		return ARM_PSCI_RET_INVAL;
+
+	/* This happens to have the same HW_ON/HW_OFF encoding. */
+	return psci_node_hw_state(function_id, target_cpu, power_level);
+}
+
+void __secure psci_system_off(void)
+{
+	scpi_set_sys_power_state(SCPI_SYSTEM_SHUTDOWN);
+
+	/* Wait to be turned off. */
+	for (;;) wfi();
+}
+
+void __secure psci_system_reset(void)
+{
+	scpi_set_sys_power_state(SCPI_SYSTEM_REBOOT);
+
+	/* Wait to be turned off. */
+	for (;;) wfi();
+}
+
+s32 __secure psci_features(u32 __always_unused function_id,
+			   u32 psci_fid)
+{
+	switch (psci_fid) {
+	case ARM_PSCI_0_2_FN_PSCI_VERSION:
+	case ARM_PSCI_0_2_FN_CPU_SUSPEND:
+	case ARM_PSCI_0_2_FN_CPU_OFF:
+	case ARM_PSCI_0_2_FN_CPU_ON:
+	case ARM_PSCI_0_2_FN_AFFINITY_INFO:
+	case ARM_PSCI_0_2_FN_SYSTEM_OFF:
+	case ARM_PSCI_0_2_FN_SYSTEM_RESET:
+	case ARM_PSCI_1_0_FN_PSCI_FEATURES:
+	case ARM_PSCI_1_0_FN_CPU_DEFAULT_SUSPEND:
+	case ARM_PSCI_1_0_FN_NODE_HW_STATE:
+	case ARM_PSCI_1_0_FN_SYSTEM_SUSPEND:
+	case ARM_PSCI_1_1_FN_SYSTEM_RESET2:
+		return ARM_PSCI_RET_SUCCESS;
+	default:
+		return ARM_PSCI_RET_NI;
+	}
+}
+
+s32 __secure psci_cpu_default_suspend(u32 __always_unused function_id,
+				      u32 pc, u32 context_id)
+{
+	return psci_suspend_common(pc, context_id, SCPI_POWER_OFF,
+				   SCPI_POWER_OFF, SCPI_POWER_RETENTION);
+}
+
+s32 __secure psci_node_hw_state(u32 __always_unused function_id,
+				u32 target_cpu, u32 power_level)
+{
+	u32 core = MPIDR_AFFLVL0(target_cpu);
+	u8 core_states, cluster_state;
+
+	if (power_level >= CSS_POWER_LEVEL)
+		return HW_ON;
+	if (scpi_get_css_power_state(target_cpu, &core_states, &cluster_state))
+		return ARM_PSCI_RET_NI;
+	if (power_level == CLUSTER_POWER_LEVEL) {
+		if (cluster_state == SCPI_POWER_ON)
+			return HW_ON;
+		if (cluster_state < SCPI_POWER_OFF)
+			return HW_STANDBY;
+		return HW_OFF;
+	}
+
+	return (core_states & BIT(core)) ? HW_ON : HW_OFF;
+}
+
+s32 __secure psci_system_suspend(u32 __always_unused function_id,
+				 u32 pc, u32 context_id)
+{
+	return psci_suspend_common(pc, context_id, SCPI_POWER_OFF,
+				   SCPI_POWER_OFF, SCPI_POWER_OFF);
+}
+
+s32 __secure psci_system_reset2(u32 __always_unused function_id,
+				u32 reset_type, u32 cookie)
+{
+	s32 ret;
+
+	if (reset_type)
+		return ARM_PSCI_RET_INVAL;
+
+	ret = scpi_set_sys_power_state(SCPI_SYSTEM_RESET);
+	if (ret)
+		return ARM_PSCI_RET_INVAL;
+
+	/* Wait to be turned off. */
+	for (;;) wfi();
+}
+
+/*
+ * R40 is different from other single cluster SoCs. The secondary core
+ * entry address register is in the SRAM controller address range.
+ */
+#define SUN8I_R40_SRAMC_SOFT_ENTRY_REG0		(0xbc)
+
+#ifdef CONFIG_MACH_SUN8I_R40
+/* secondary core entry address is programmed differently on R40 */
+static void __secure sunxi_set_entry_address(void *entry)
+{
+	writel((u32)entry,
+	       SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0);
+}
+#else
+static void __secure sunxi_set_entry_address(void *entry)
+{
+	writel((u32)entry, SUNXI_CPUCFG_BASE + SUNXI_PRIV0);
+
+#ifdef CONFIG_MACH_SUN8I_H3
+	/* Redirect CPU 0 to the secure monitor via the resume shim. */
+	writel(0x16aaefe8, SUNXI_CPUCFG_BASE + SUNXI_SUPER_STANDY_FLAG);
+	writel(0xaa16efe8, SUNXI_CPUCFG_BASE + SUNXI_SUPER_STANDY_FLAG);
+	writel(SUNXI_RESUME_BASE, SUNXI_CPUCFG_BASE + SUNXI_PRIV1);
+#endif
+}
+#endif
+
+void __secure psci_arch_init(void)
+{
+	static bool __secure_data one_time_init = true;
+
+	if (one_time_init) {
+		/* Set secondary core power-on PC. */
+		sunxi_set_entry_address(psci_cpu_entry);
+
+		/* Wait for the SCP firmware to boot. */
+		scpi_begin_command();
+		scpi_wait_response();
+		scpi_end_command();
+
+		one_time_init = false;
+	}
+
+	/*
+	 * Copied from arch/arm/cpu/armv7/virt-v7.c
+	 * See also gic_resume() in arch/arm/mach-imx/mx7/psci-mx7.c
+	 */
+	if (gic_dist_init) {
+		u32 i, itlinesnr;
+
+		/* enable the GIC distributor */
+		writel(readl(GICD_BASE + GICD_CTLR) | 0x03, GICD_BASE + GICD_CTLR);
+
+		/* TYPER[4:0] contains an encoded number of available interrupts */
+		itlinesnr = readl(GICD_BASE + GICD_TYPER) & 0x1f;
+
+		/* set all bits in the GIC group registers to one to allow access
+		 * from non-secure state. The first 32 interrupts are private per
+		 * CPU and will be set later when enabling the GIC for each core
+		 */
+		for (i = 1; i <= itlinesnr; i++)
+			writel((unsigned)-1, GICD_BASE + GICD_IGROUPRn + 4 * i);
+
+		gic_dist_init = false;
+	}
+
+	/* Be cool with non-secure. */
+	writel(0xff, GICC_BASE + GICC_PMR);
+}
-- 
2.34.1


From 51ff2947b4a9acab17a0b86215f902046d546a2e Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Wed, 8 Jun 2022 07:55:54 -0500
Subject: [PATCH 9/9] sunxi: Enable SCP/SCPI on A33 as well

Signed-off-by: Samuel Holland <samuel@sholland.org>
---
 arch/arm/cpu/armv7/Kconfig           |  2 +-
 arch/arm/cpu/armv7/sunxi/Makefile    |  2 +-
 arch/arm/cpu/armv7/sunxi/psci-scpi.c |  4 ++++
 arch/arm/dts/sun8i-a23-a33.dtsi      | 14 ++++++++++++++
 arch/arm/dts/sunxi-u-boot.dtsi       |  4 ++--
 5 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/arch/arm/cpu/armv7/Kconfig b/arch/arm/cpu/armv7/Kconfig
index 9dccc122538..e60b0049ea1 100644
--- a/arch/arm/cpu/armv7/Kconfig
+++ b/arch/arm/cpu/armv7/Kconfig
@@ -86,7 +86,7 @@ config ARMV7_PSCI
 choice
 	prompt "Supported PSCI version"
 	depends on ARMV7_PSCI
-	default ARMV7_PSCI_1_1 if MACH_SUN8I_H3
+	default ARMV7_PSCI_1_1 if MACH_SUN8I_A33 || MACH_SUN8I_H3
 	default ARMV7_PSCI_0_1 if ARCH_SUNXI
 	default ARMV7_PSCI_1_0
 	help
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile
index 6473b9acbde..033042666f0 100644
--- a/arch/arm/cpu/armv7/sunxi/Makefile
+++ b/arch/arm/cpu/armv7/sunxi/Makefile
@@ -13,7 +13,7 @@ obj-$(CONFIG_MACH_SUN6I)	+= sram.o
 obj-$(CONFIG_MACH_SUN8I)	+= sram.o
 
 ifndef CONFIG_SPL_BUILD
-ifdef CONFIG_MACH_SUN8I_H3
+ifneq ($(CONFIG_MACH_SUN8I_A33)$(CONFIG_MACH_SUN8I_H3),)
 obj-$(CONFIG_ARMV7_PSCI)	+= psci-scpi.o
 else
 obj-$(CONFIG_ARMV7_PSCI)	+= psci.o
diff --git a/arch/arm/cpu/armv7/sunxi/psci-scpi.c b/arch/arm/cpu/armv7/sunxi/psci-scpi.c
index 9672eae8aef..50628693ac1 100644
--- a/arch/arm/cpu/armv7/sunxi/psci-scpi.c
+++ b/arch/arm/cpu/armv7/sunxi/psci-scpi.c
@@ -23,7 +23,11 @@
 #define MPIDR_AFFLVL0(mpidr)	(mpidr & 0xf)
 #define MPIDR_AFFLVL1(mpidr)	(mpidr >> 8 & 0xf)
 
+#if defined(CONFIG_MACH_SUN8I_H3)
 #define SCPI_SHMEM_BASE		0x0004be00
+#else
+#define SCPI_SHMEM_BASE		0x00053e00
+#endif
 #define SCPI_SHMEM		((struct scpi_shmem *)SCPI_SHMEM_BASE)
 
 #define SCPI_RX_CHANNEL		1
diff --git a/arch/arm/dts/sun8i-a23-a33.dtsi b/arch/arm/dts/sun8i-a23-a33.dtsi
index a0cac966af3..a26edbbf317 100644
--- a/arch/arm/dts/sun8i-a23-a33.dtsi
+++ b/arch/arm/dts/sun8i-a23-a33.dtsi
@@ -138,6 +138,14 @@
 			#size-cells = <1>;
 			ranges;
 
+			sram_a2: sram@40000 {
+				compatible = "mmio-sram";
+				reg = <0x00040000 0x14000>;
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0 0x00040000 0x14000>;
+			};
+
 			sram_c: sram@1d00000 {
 				compatible = "mmio-sram";
 				reg = <0x01d00000 0x80000>;
@@ -847,5 +855,11 @@
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
+
+		remoteproc@1f01c00 {
+			compatible = "allwinner,sun6i-a31-ar100";
+			reg = <0x01f01c00 0x400>;
+			sram = <&sram_a2>;
+		};
 	};
 };
diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi
index b8585dea2a6..ee76b16563e 100644
--- a/arch/arm/dts/sunxi-u-boot.dtsi
+++ b/arch/arm/dts/sunxi-u-boot.dtsi
@@ -6,11 +6,11 @@
 #define ARCH "arm"
 #endif
 
+#if defined(CONFIG_ARMV7_PSCI) && (defined(CONFIG_MACH_SUN8I_A33) || defined(CONFIG_MACH_SUN8I_H3))
 #if defined(CONFIG_MACH_SUN8I_H3)
-#ifdef CONFIG_ARMV7_PSCI
 #define RESUME_ADDR	SUNXI_RESUME_BASE
-#define SCP_ADDR	SUNXI_SCP_BASE
 #endif
+#define SCP_ADDR	SUNXI_SCP_BASE
 #elif defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN50I_H5)
 #define BL31_ADDR	0x00044000
 #define SCP_ADDR	0x00050000
-- 
2.34.1

