Merge changes from topic "allwinner_h616" into integration

* changes:
  allwinner: H616: Add reserved-memory node to DT
  allwinner: Add Allwinner H616 SoC support
  allwinner: Add H616 SoC ID
  allwinner: Express memmap more dynamically
  allwinner: Move sunxi_cpu_power_off_self() into platforms
  allwinner: Move SEPARATE_NOBITS_REGION to platforms
  doc: allwinner: Reorder sections, document memory mapping
diff --git a/docs/plat/allwinner.rst b/docs/plat/allwinner.rst
index d82380d..b696989 100644
--- a/docs/plat/allwinner.rst
+++ b/docs/plat/allwinner.rst
@@ -5,22 +5,8 @@
 SoCs with ARMv8 cores. Only BL31 is used to provide proper EL3 setup and
 PSCI runtime services.
 
-U-Boot's SPL acts as a loader, loading both BL31 and BL33 (typically U-Boot).
-Loading is done from SD card, eMMC or SPI flash, also via an USB debug
-interface (FEL).
-
-BL31 lives in SRAM A2, which is documented to be accessible from secure
-world only.
-
-Current limitations:
-
--  Missing PMIC support
-
-After building bl31.bin, the binary must be fed to the U-Boot build system
-to include it in the FIT image that the SPL loader will process.
-bl31.bin can be either copied (or sym-linked) into U-Boot's root directory,
-or the environment variable BL31 must contain the binary's path.
-See the respective `U-Boot documentation`_ for more details.
+Building TF-A
+-------------
 
 To build for machines with an A64 or H5 SoC:
 
@@ -34,8 +20,75 @@
 
     make CROSS_COMPILE=aarch64-linux-gnu- PLAT=sun50i_h6 DEBUG=1 bl31
 
+To build for machines with an H616 or H313 SoC:
+
+.. code:: shell
+
+    make CROSS_COMPILE=aarch64-linux-gnu- PLAT=sun50i_h616 DEBUG=1 bl31
+
+
+Installation
+------------
+
+U-Boot's SPL acts as a loader, loading both BL31 and BL33 (typically U-Boot).
+Loading is done from SD card, eMMC or SPI flash, also via an USB debug
+interface (FEL).
+
+After building bl31.bin, the binary must be fed to the U-Boot build system
+to include it in the FIT image that the SPL loader will process.
+bl31.bin can be either copied (or sym-linked) into U-Boot's root directory,
+or the environment variable BL31 must contain the binary's path.
+See the respective `U-Boot documentation`_ for more details.
+
 .. _U-Boot documentation: https://gitlab.denx.de/u-boot/u-boot/-/blob/master/board/sunxi/README.sunxi64
 
+Memory layout
+-------------
+
+A64, H5 and H6 SoCs
+~~~~~~~~~~~~~~~~~~~
+
+BL31 lives in SRAM A2, which is documented to be accessible from secure
+world only. Since this SRAM region is very limited (48 KB), we take
+several measures to reduce memory consumption. One of them is to confine
+BL31 to only 28 bits of virtual address space, which reduces the number
+of required page tables (each occupying 4KB of memory).
+The mapping we use on those SoCs is as follows:
+
+::
+
+   0 64K         16M             1GB         1G+160M     physical address
+   +-+------+-+---+------+--...---+-------+----+------+----------
+   |B|      |S|///|      |//...///|       |////|      |
+   |R| SRAM |C|///| dev  |//...///| (sec) |////| BL33 |  DRAM ...
+   |O|      |P|///| MMIO |//...///| DRAM  |////|      |
+   |M|      | |///|      |//...///| (32M) |////|      |
+   +-+------+-+---+------+--...---+-------+----+------+----------
+   | |      | |   |      |       /       /   /      /
+   | |      | |   |      |      /       /  /      /
+   | |      | |   |      |     /       / /      /
+   | |      | |   |      |    /       //      /
+   | |      | |   |      |   /       /      /
+   +-+------+-+---+------+--+-------+------+
+   |B|      |S|///|      |//|       |      |
+   |R| SRAM |C|///| dev  |//|  sec  | BL33 |
+   |O|      |P|///| MMIO |//| DRAM  |      |
+   |M|      | |///|      |//|       |      |
+   +-+------+-+---+------+--+-------+------+
+   0 64K         16M       160M    192M  256M             virtual address
+
+
+H616 SoC
+~~~~~~~~
+
+The H616 lacks the secure SRAM region present on the other SoCs, also
+lacks the "ARISC" management processor (SCP) we use. BL31 thus needs to
+run from DRAM, which prevents our compressed virtual memory map described
+above. Since running in DRAM also lifts the restriction of the limited
+SRAM size, we use the normal 1:1 mapping with 32 bits worth of virtual
+address space. So the virtual addresses used in BL31 match the physical
+addresses as presented above.
+
 Trusted OS dispatcher
 ---------------------
 
diff --git a/plat/allwinner/common/allwinner-common.mk b/plat/allwinner/common/allwinner-common.mk
index da83b5e..61ae9b6 100644
--- a/plat/allwinner/common/allwinner-common.mk
+++ b/plat/allwinner/common/allwinner-common.mk
@@ -85,9 +85,6 @@
 # Allow mapping read-only data as execute-never.
 SEPARATE_CODE_AND_RODATA	:=	1
 
-# Put NOBITS memory in SRAM A1, overwriting U-Boot's SPL.
-SEPARATE_NOBITS_REGION		:=	1
-
 # BL31 gets loaded alongside BL33 (U-Boot) by U-Boot's SPL
 RESET_TO_BL31			:=	1
 
diff --git a/plat/allwinner/common/include/platform_def.h b/plat/allwinner/common/include/platform_def.h
index 93720ff..4893368 100644
--- a/plat/allwinner/common/include/platform_def.h
+++ b/plat/allwinner/common/include/platform_def.h
@@ -13,18 +13,36 @@
 
 #include <sunxi_mmap.h>
 
+/* The SCP firmware is allocated the last 16KiB of SRAM A2. */
+#define SUNXI_SCP_SIZE			0x4000
+
+#ifdef SUNXI_BL31_IN_DRAM
+
+#define BL31_BASE			SUNXI_DRAM_BASE
+#define BL31_LIMIT			(SUNXI_DRAM_BASE + 0x40000)
+
+#define MAX_XLAT_TABLES			4
+#define PLAT_VIRT_ADDR_SPACE_SIZE	(1ULL << 32)
+
+#define SUNXI_BL33_VIRT_BASE		PRELOADED_BL33_BASE
+
+#else	/* !SUNXI_BL31_IN_DRAM */
+
 #define BL31_BASE			(SUNXI_SRAM_A2_BASE + 0x4000)
 #define BL31_LIMIT			(SUNXI_SRAM_A2_BASE + \
 					 SUNXI_SRAM_A2_SIZE - SUNXI_SCP_SIZE)
-
-/* The SCP firmware is allocated the last 16KiB of SRAM A2. */
 #define SUNXI_SCP_BASE			BL31_LIMIT
-#define SUNXI_SCP_SIZE			0x4000
 
 /* Overwrite U-Boot SPL, but reserve the first page for the SPL header. */
 #define BL31_NOBITS_BASE		(SUNXI_SRAM_A1_BASE + 0x1000)
 #define BL31_NOBITS_LIMIT		(SUNXI_SRAM_A1_BASE + SUNXI_SRAM_A1_SIZE)
 
+#define MAX_XLAT_TABLES			1
+#define PLAT_VIRT_ADDR_SPACE_SIZE	(1ULL << 28)
+#define SUNXI_BL33_VIRT_BASE		(SUNXI_DRAM_VIRT_BASE + SUNXI_DRAM_SEC_SIZE)
+
+#endif /* SUNXI_BL31_IN_DRAM */
+
 /* How much memory to reserve as secure for BL32, if configured */
 #define SUNXI_DRAM_SEC_SIZE		(32U << 20)
 
@@ -35,7 +53,6 @@
 #define CACHE_WRITEBACK_GRANULE		(1 << CACHE_WRITEBACK_SHIFT)
 
 #define MAX_MMAP_REGIONS		(3 + PLATFORM_MMAP_REGIONS)
-#define MAX_XLAT_TABLES			1
 
 #define PLAT_CSS_SCP_COM_SHARED_MEM_BASE \
 	(SUNXI_SRAM_A2_BASE + SUNXI_SRAM_A2_SIZE - 0x200)
@@ -50,7 +67,6 @@
 					 PLATFORM_CORE_COUNT)
 
 #define PLAT_PHY_ADDR_SPACE_SIZE	(1ULL << 32)
-#define PLAT_VIRT_ADDR_SPACE_SIZE	(1ULL << 28)
 
 #define PLATFORM_CLUSTER_COUNT		U(1)
 #define PLATFORM_CORE_COUNT		(PLATFORM_CLUSTER_COUNT * \
diff --git a/plat/allwinner/common/include/sunxi_def.h b/plat/allwinner/common/include/sunxi_def.h
index 73c4453..f036816 100644
--- a/plat/allwinner/common/include/sunxi_def.h
+++ b/plat/allwinner/common/include/sunxi_def.h
@@ -17,5 +17,6 @@
 #define SUNXI_SOC_A64			0x1689
 #define SUNXI_SOC_H5			0x1718
 #define SUNXI_SOC_H6			0x1728
+#define SUNXI_SOC_H616			0x1823
 
 #endif /* SUNXI_DEF_H */
diff --git a/plat/allwinner/common/include/sunxi_private.h b/plat/allwinner/common/include/sunxi_private.h
index b68d23f..6cf4670 100644
--- a/plat/allwinner/common/include/sunxi_private.h
+++ b/plat/allwinner/common/include/sunxi_private.h
@@ -41,4 +41,12 @@
 int sunxi_init_platform_r_twi(uint16_t socid, bool use_rsb);
 void sunxi_execute_arisc_code(uint32_t *code, size_t size, uint16_t param);
 
+#ifdef SUNXI_BL31_IN_DRAM
+void sunxi_prepare_dtb(void *fdt);
+#else
+static inline void sunxi_prepare_dtb(void *fdt)
+{
+}
+#endif
+
 #endif /* SUNXI_PRIVATE_H */
diff --git a/plat/allwinner/common/sunxi_bl31_setup.c b/plat/allwinner/common/sunxi_bl31_setup.c
index b619b18..72bfbd9 100644
--- a/plat/allwinner/common/sunxi_bl31_setup.c
+++ b/plat/allwinner/common/sunxi_bl31_setup.c
@@ -13,6 +13,8 @@
 #include <arch.h>
 #include <arch_helpers.h>
 #include <common/debug.h>
+#include <common/fdt_fixup.h>
+#include <common/fdt_wrappers.h>
 #include <drivers/arm/gicv2.h>
 #include <drivers/console.h>
 #include <drivers/generic_delay_timer.h>
@@ -52,7 +54,7 @@
 	uint64_t *u_boot_base;
 	int i;
 
-	u_boot_base = (void *)(SUNXI_DRAM_VIRT_BASE + SUNXI_DRAM_SEC_SIZE);
+	u_boot_base = (void *)SUNXI_BL33_VIRT_BASE;
 
 	for (i = 0; i < 2048 / sizeof(uint64_t); i++) {
 		uint32_t *dtb_base;
@@ -123,6 +125,9 @@
 	case SUNXI_SOC_H6:
 		soc_name = "H6";
 		break;
+	case SUNXI_SOC_H616:
+		soc_name = "H616";
+		break;
 	default:
 		soc_name = "unknown";
 		break;
@@ -172,6 +177,8 @@
 
 	sunxi_pmic_setup(soc_id, fdt);
 
+	sunxi_prepare_dtb(fdt);
+
 	INFO("BL31: Platform setup done\n");
 }
 
diff --git a/plat/allwinner/common/sunxi_common.c b/plat/allwinner/common/sunxi_common.c
index 5b536a0..d47d360 100644
--- a/plat/allwinner/common/sunxi_common.c
+++ b/plat/allwinner/common/sunxi_common.c
@@ -6,13 +6,9 @@
 
 #include <errno.h>
 
-#include <platform_def.h>
-
-#include <arch_helpers.h>
 #include <common/debug.h>
 #include <lib/mmio.h>
 #include <lib/xlat_tables/xlat_tables_v2.h>
-#include <plat/common/platform.h>
 
 #include <sunxi_def.h>
 #include <sunxi_mmap.h>
@@ -21,16 +17,16 @@
 static const mmap_region_t sunxi_mmap[PLATFORM_MMAP_REGIONS + 1] = {
 	MAP_REGION_FLAT(SUNXI_SRAM_BASE, SUNXI_SRAM_SIZE,
 			MT_RW_DATA | MT_SECURE),
+#ifdef SUNXI_SCP_BASE
 	MAP_REGION_FLAT(SUNXI_SCP_BASE, SUNXI_SCP_SIZE,
 			MT_DEVICE | MT_RW | MT_SECURE | MT_EXECUTE_NEVER),
+#endif
 	MAP_REGION_FLAT(SUNXI_DEV_BASE, SUNXI_DEV_SIZE,
 			MT_DEVICE | MT_RW | MT_SECURE | MT_EXECUTE_NEVER),
 	MAP_REGION(SUNXI_DRAM_BASE, SUNXI_DRAM_VIRT_BASE, SUNXI_DRAM_SEC_SIZE,
 		   MT_RW_DATA | MT_SECURE),
-	MAP_REGION(PRELOADED_BL33_BASE,
-		   SUNXI_DRAM_VIRT_BASE + SUNXI_DRAM_SEC_SIZE,
-		   SUNXI_DRAM_MAP_SIZE,
-		   MT_RO_DATA | MT_NS),
+	MAP_REGION(PRELOADED_BL33_BASE, SUNXI_BL33_VIRT_BASE,
+		   SUNXI_DRAM_MAP_SIZE, MT_RW_DATA | MT_NS),
 	{},
 };
 
@@ -116,6 +112,7 @@
 		device_bit = BIT(6);
 		break;
 	case SUNXI_SOC_H6:
+	case SUNXI_SOC_H616:
 		pin_func = use_rsb ? 0x22 : 0x33;
 		device_bit = BIT(16);
 		reset_offset = use_rsb ? 0x1bc : 0x19c;
@@ -130,7 +127,7 @@
 	}
 
 	/* un-gate R_PIO clock */
-	if (socid != SUNXI_SOC_H6)
+	if (socid != SUNXI_SOC_H6 && socid != SUNXI_SOC_H616)
 		mmio_setbits_32(SUNXI_R_PRCM_BASE + 0x28, BIT(0));
 
 	/* switch pins PL0 and PL1 to the desired function */
@@ -143,7 +140,7 @@
 	mmio_clrsetbits_32(SUNXI_R_PIO_BASE + 0x1c, 0x0fU, 0x5U);
 
 	/* un-gate clock */
-	if (socid != SUNXI_SOC_H6)
+	if (socid != SUNXI_SOC_H6 && socid != SUNXI_SOC_H616)
 		mmio_setbits_32(SUNXI_R_PRCM_BASE + 0x28, device_bit);
 	else
 		mmio_setbits_32(SUNXI_R_PRCM_BASE + reset_offset, BIT(0));
@@ -154,50 +151,3 @@
 
 	return 0;
 }
-
-/* This lock synchronises access to the arisc management processor. */
-DEFINE_BAKERY_LOCK(arisc_lock);
-
-/*
- * Tell the "arisc" SCP core (an OpenRISC core) to execute some code.
- * We don't have any service running there, so we place some OpenRISC code
- * in SRAM, put the address of that into the reset vector and release the
- * arisc reset line. The SCP will execute that code and pull the line up again.
- */
-void sunxi_execute_arisc_code(uint32_t *code, size_t size, uint16_t param)
-{
-	uintptr_t arisc_reset_vec = SUNXI_SRAM_A2_BASE + 0x100;
-
-	do {
-		bakery_lock_get(&arisc_lock);
-		/* Wait until the arisc is in reset state. */
-		if (!(mmio_read_32(SUNXI_R_CPUCFG_BASE) & BIT(0)))
-			break;
-
-		bakery_lock_release(&arisc_lock);
-	} while (1);
-
-	/* Patch up the code to feed in an input parameter. */
-	code[0] = (code[0] & ~0xffff) | param;
-	clean_dcache_range((uintptr_t)code, size);
-
-	/*
-	 * The OpenRISC unconditional branch has opcode 0, the branch offset
-	 * is in the lower 26 bits, containing the distance to the target,
-	 * in instruction granularity (32 bits).
-	 */
-	mmio_write_32(arisc_reset_vec, ((uintptr_t)code - arisc_reset_vec) / 4);
-	clean_dcache_range(arisc_reset_vec, 4);
-
-	/* De-assert the arisc reset line to let it run. */
-	mmio_setbits_32(SUNXI_R_CPUCFG_BASE, BIT(0));
-
-	/*
-	 * We release the lock here, although the arisc is still busy.
-	 * But as long as it runs, the reset line is high, so other users
-	 * won't leave the loop above.
-	 * Once it has finished, the code is supposed to clear the reset line,
-	 * to signal this to other users.
-	 */
-	bakery_lock_release(&arisc_lock);
-}
diff --git a/plat/allwinner/common/sunxi_cpu_ops.c b/plat/allwinner/common/sunxi_cpu_ops.c
index 43c03ac..420b507 100644
--- a/plat/allwinner/common/sunxi_cpu_ops.c
+++ b/plat/allwinner/common/sunxi_cpu_ops.c
@@ -19,10 +19,6 @@
 #include <sunxi_mmap.h>
 #include <sunxi_private.h>
 
-#ifndef SUNXI_CPUIDLE_EN_REG
-#include <core_off_arisc.h>
-#endif
-
 static void sunxi_cpu_disable_power(unsigned int cluster, unsigned int core)
 {
 	if (mmio_read_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core)) == 0xff)
@@ -67,32 +63,6 @@
 	sunxi_cpu_disable_power(cluster, core);
 }
 
-void sunxi_cpu_power_off_self(void)
-{
-	u_register_t mpidr = read_mpidr();
-	unsigned int core  = MPIDR_AFFLVL0_VAL(mpidr);
-
-	/* Simplifies assembly, all SoCs so far are single cluster anyway. */
-	assert(MPIDR_AFFLVL1_VAL(mpidr) == 0);
-
-#ifdef SUNXI_CPUIDLE_EN_REG
-	/* Enable the CPUIDLE hardware (only really needs to be done once). */
-	mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0x16aa0000);
-	mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0xaa160001);
-
-	/* Trigger power off for this core. */
-	mmio_write_32(SUNXI_CORE_CLOSE_REG, BIT_32(core));
-#else
-	/*
-	 * If we are supposed to turn ourself off, tell the arisc SCP
-	 * to do that work for us. The code expects the core mask to be
-	 * patched into the first instruction.
-	 */
-	sunxi_execute_arisc_code(arisc_core_off, sizeof(arisc_core_off),
-				 BIT_32(core));
-#endif
-}
-
 void sunxi_cpu_on(u_register_t mpidr)
 {
 	unsigned int cluster = MPIDR_AFFLVL1_VAL(mpidr);
diff --git a/plat/allwinner/sun50i_a64/platform.mk b/plat/allwinner/sun50i_a64/platform.mk
index 5f41035..e3c7c52 100644
--- a/plat/allwinner/sun50i_a64/platform.mk
+++ b/plat/allwinner/sun50i_a64/platform.mk
@@ -12,3 +12,6 @@
 
 FDT_ASSUME_MASK := "(ASSUME_LATEST | ASSUME_NO_ROLLBACK | ASSUME_LIBFDT_ORDER)"
 $(eval $(call add_define,FDT_ASSUME_MASK))
+
+# Put NOBITS memory in SRAM A1, overwriting U-Boot's SPL.
+SEPARATE_NOBITS_REGION	:=	1
diff --git a/plat/allwinner/sun50i_a64/sunxi_power.c b/plat/allwinner/sun50i_a64/sunxi_power.c
index 80a69c3..0fdb62d 100644
--- a/plat/allwinner/sun50i_a64/sunxi_power.c
+++ b/plat/allwinner/sun50i_a64/sunxi_power.c
@@ -14,6 +14,7 @@
 #include <drivers/allwinner/sunxi_rsb.h>
 #include <lib/mmio.h>
 
+#include <core_off_arisc.h>
 #include <sunxi_def.h>
 #include <sunxi_mmap.h>
 #include <sunxi_private.h>
@@ -205,3 +206,55 @@
 	}
 
 }
+
+/* This lock synchronises access to the arisc management processor. */
+static DEFINE_BAKERY_LOCK(arisc_lock);
+
+/*
+ * If we are supposed to turn ourself off, tell the arisc SCP to do that
+ * work for us. Without any SCPI provider running there, we place some
+ * OpenRISC code into SRAM, put the address of that into the reset vector
+ * and release the arisc reset line. The SCP will wait for the core to enter
+ * WFI, then execute that code and pull the line up again.
+ * The code expects the core mask to be patched into the first instruction.
+ */
+void sunxi_cpu_power_off_self(void)
+{
+	u_register_t mpidr = read_mpidr();
+	unsigned int core  = MPIDR_AFFLVL0_VAL(mpidr);
+	uintptr_t arisc_reset_vec = SUNXI_SRAM_A2_BASE + 0x100;
+	uint32_t *code = arisc_core_off;
+
+	do {
+		bakery_lock_get(&arisc_lock);
+		/* Wait until the arisc is in reset state. */
+		if (!(mmio_read_32(SUNXI_R_CPUCFG_BASE) & BIT(0)))
+			break;
+
+		bakery_lock_release(&arisc_lock);
+	} while (1);
+
+	/* Patch up the code to feed in an input parameter. */
+	code[0] = (code[0] & ~0xffff) | BIT_32(core);
+	clean_dcache_range((uintptr_t)code, sizeof(arisc_core_off));
+
+	/*
+	 * The OpenRISC unconditional branch has opcode 0, the branch offset
+	 * is in the lower 26 bits, containing the distance to the target,
+	 * in instruction granularity (32 bits).
+	 */
+	mmio_write_32(arisc_reset_vec, ((uintptr_t)code - arisc_reset_vec) / 4);
+	clean_dcache_range(arisc_reset_vec, 4);
+
+	/* De-assert the arisc reset line to let it run. */
+	mmio_setbits_32(SUNXI_R_CPUCFG_BASE, BIT(0));
+
+	/*
+	 * We release the lock here, although the arisc is still busy.
+	 * But as long as it runs, the reset line is high, so other users
+	 * won't leave the loop above.
+	 * Once it has finished, the code is supposed to clear the reset line,
+	 * to signal this to other users.
+	 */
+	bakery_lock_release(&arisc_lock);
+}
diff --git a/plat/allwinner/sun50i_h6/include/core_off_arisc.h b/plat/allwinner/sun50i_h6/include/core_off_arisc.h
deleted file mode 100644
index 63a5d8d..0000000
--- a/plat/allwinner/sun50i_h6/include/core_off_arisc.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-static uint32_t arisc_core_off[] = {
-	0x18600000, /* l.movhi	r3, <corenr>	*/
-	0x18000000, /* l.movhi	r0, 0x0		*/
-	0x19a00901, /* l.movhi	r13, 0x901	*/
-	0x84ad0080, /* l.lwz	r5, 0x80(r13)	*/
-	0xe0a51803, /* l.and	r5, r5, r3	*/
-	0xe4050000, /* l.sfeq	r5, r0		*/
-	0x13fffffd, /* l.bf	-12		*/
-	0xb8c30050, /* l.srli	r6, r3, 16	*/
-
-	0xbc060001, /* l.sfeqi	r6, 1		*/
-	0x10000005, /* l.bf	+20		*/
-	0x19a00700, /* l.movhi	r13, 0x700	*/
-	0x84ad0444, /* l.lwz	r5, 0x0444(r13)	*/
-	0xe0a53004, /* l.or	r5, r5, r6	*/
-	0xd40d2c44, /* l.sw	0x0444(r13), r5	*/
-
-	0x84ad0440, /* l.lwz	r5, 0x0440(r13)	*/
-	0xacc6ffff, /* l.xori	r6, r6, -1	*/
-	0xe0a53003, /* l.and	r5, r5, r6	*/
-	0xd40d2c40, /* l.sw	0x0440(r13), r5	*/
-
-	0xe0c3000f, /* l.ff1	r6, r3		*/
-	0x9cc6ffef, /* l.addi	r6, r6, -17	*/
-	0xb8c60002, /* l.slli	r6, r6, 2	*/
-	0xe0c66800, /* l.add	r6, r6, r13	*/
-	0xa8a000ff, /* l.ori	r5, r0, 0xff	*/
-	0xd4062c50, /* l.sw	0x0450(r6), r5	*/
-
-	0xd40d0400, /* l.sw	0x0400(r13), r0	*/
-	0x03ffffff, /* l.j	-1		*/
-	0x15000000, /* l.nop			*/
-};
diff --git a/plat/allwinner/sun50i_h6/platform.mk b/plat/allwinner/sun50i_h6/platform.mk
index 1c98919..e13e8cb 100644
--- a/plat/allwinner/sun50i_h6/platform.mk
+++ b/plat/allwinner/sun50i_h6/platform.mk
@@ -9,3 +9,6 @@
 
 BL31_SOURCES		+=	drivers/allwinner/axp/axp805.c		\
 				drivers/allwinner/sunxi_rsb.c
+
+# Put NOBITS memory in SRAM A1, overwriting U-Boot's SPL.
+SEPARATE_NOBITS_REGION	:=	1
diff --git a/plat/allwinner/sun50i_h6/sunxi_power.c b/plat/allwinner/sun50i_h6/sunxi_power.c
index a7865a5..d298e6b 100644
--- a/plat/allwinner/sun50i_h6/sunxi_power.c
+++ b/plat/allwinner/sun50i_h6/sunxi_power.c
@@ -10,7 +10,9 @@
 #include <common/debug.h>
 #include <drivers/allwinner/axp.h>
 #include <drivers/allwinner/sunxi_rsb.h>
+#include <lib/mmio.h>
 
+#include <sunxi_cpucfg.h>
 #include <sunxi_def.h>
 #include <sunxi_mmap.h>
 #include <sunxi_private.h>
@@ -102,3 +104,16 @@
 		break;
 	}
 }
+
+void sunxi_cpu_power_off_self(void)
+{
+	u_register_t mpidr = read_mpidr();
+	unsigned int core  = MPIDR_AFFLVL0_VAL(mpidr);
+
+	/* Enable the CPUIDLE hardware (only really needs to be done once). */
+	mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0x16aa0000);
+	mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0xaa160001);
+
+	/* Trigger power off for this core. */
+	mmio_write_32(SUNXI_CORE_CLOSE_REG, BIT_32(core));
+}
diff --git a/plat/allwinner/sun50i_h616/include/sunxi_ccu.h b/plat/allwinner/sun50i_h616/include/sunxi_ccu.h
new file mode 100644
index 0000000..85fbb90
--- /dev/null
+++ b/plat/allwinner/sun50i_h616/include/sunxi_ccu.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SUNXI_CCU_H
+#define SUNXI_CCU_H
+
+#define SUNXI_CCU_SEC_SWITCH_REG	(SUNXI_CCU_BASE + 0x0f00)
+
+#define SUNXI_R_PRCM_SEC_SWITCH_REG	(SUNXI_R_PRCM_BASE + 0x0290)
+
+#endif /* SUNXI_CCU_H */
diff --git a/plat/allwinner/sun50i_h616/include/sunxi_cpucfg.h b/plat/allwinner/sun50i_h616/include/sunxi_cpucfg.h
new file mode 100644
index 0000000..a637554
--- /dev/null
+++ b/plat/allwinner/sun50i_h616/include/sunxi_cpucfg.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017-2020, ARM Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SUNXI_CPUCFG_H
+#define SUNXI_CPUCFG_H
+
+#include <sunxi_mmap.h>
+
+/* c = cluster, n = core */
+#define SUNXI_CPUCFG_CLS_CTRL_REG0(c)	(SUNXI_CPUCFG_BASE + 0x0010 + (c) * 0x10)
+#define SUNXI_CPUCFG_CLS_CTRL_REG1(c)	(SUNXI_CPUCFG_BASE + 0x0014 + (c) * 0x10)
+#define SUNXI_CPUCFG_CACHE_CFG_REG	(SUNXI_CPUCFG_BASE + 0x0024)
+#define SUNXI_CPUCFG_DBG_REG0		(SUNXI_CPUCFG_BASE + 0x00c0)
+
+#define SUNXI_CPUCFG_RST_CTRL_REG(c)	(SUNXI_CPUCFG_BASE + 0x0000 + (c) * 4)
+#define SUNXI_CPUCFG_RVBAR_LO_REG(n)	(SUNXI_CPUCFG_BASE + 0x0040 + (n) * 8)
+#define SUNXI_CPUCFG_RVBAR_HI_REG(n)	(SUNXI_CPUCFG_BASE + 0x0044 + (n) * 8)
+
+#define SUNXI_POWERON_RST_REG(c)	(SUNXI_R_CPUCFG_BASE + 0x0040 + (c) * 4)
+#define SUNXI_POWEROFF_GATING_REG(c)	(SUNXI_R_CPUCFG_BASE + 0x0044 + (c) * 4)
+#define SUNXI_CPU_POWER_CLAMP_REG(c, n)	(SUNXI_R_CPUCFG_BASE + 0x0050 + \
+					(c) * 0x10 + (n) * 4)
+
+#define SUNXI_CPUIDLE_EN_REG		(SUNXI_R_CPUCFG_BASE + 0x0100)
+#define SUNXI_CORE_CLOSE_REG		(SUNXI_R_CPUCFG_BASE + 0x0104)
+#define SUNXI_PWR_SW_DELAY_REG		(SUNXI_R_CPUCFG_BASE + 0x0140)
+#define SUNXI_CONFIG_DELAY_REG		(SUNXI_R_CPUCFG_BASE + 0x0144)
+
+#endif /* SUNXI_CPUCFG_H */
diff --git a/plat/allwinner/sun50i_h616/include/sunxi_mmap.h b/plat/allwinner/sun50i_h616/include/sunxi_mmap.h
new file mode 100644
index 0000000..3b4f4a0
--- /dev/null
+++ b/plat/allwinner/sun50i_h616/include/sunxi_mmap.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SUNXI_MMAP_H
+#define SUNXI_MMAP_H
+
+/* Memory regions */
+#define SUNXI_ROM_BASE			0x00000000
+#define SUNXI_ROM_SIZE			0x00010000
+#define SUNXI_SRAM_BASE			0x00020000
+#define SUNXI_SRAM_SIZE			0x00038000
+#define SUNXI_SRAM_A1_BASE		0x00020000
+#define SUNXI_SRAM_A1_SIZE		0x00008000
+#define SUNXI_SRAM_C_BASE		0x00028000
+#define SUNXI_SRAM_C_SIZE		0x00030000
+#define SUNXI_DEV_BASE			0x01000000
+#define SUNXI_DEV_SIZE			0x09000000
+#define SUNXI_DRAM_BASE			0x40000000
+#define SUNXI_DRAM_VIRT_BASE		SUNXI_DRAM_BASE
+
+/* Memory-mapped devices */
+#define SUNXI_SYSCON_BASE		0x03000000
+#define SUNXI_CCU_BASE			0x03001000
+#define SUNXI_DMA_BASE			0x03002000
+#define SUNXI_SID_BASE			0x03006000
+#define SUNXI_SPC_BASE			0x03008000
+#define SUNXI_WDOG_BASE			0x030090a0
+#define SUNXI_PIO_BASE			0x0300b000
+#define SUNXI_GICD_BASE			0x03021000
+#define SUNXI_GICC_BASE			0x03022000
+#define SUNXI_UART0_BASE		0x05000000
+#define SUNXI_SPI0_BASE			0x05010000
+#define SUNXI_R_CPUCFG_BASE		0x07000400
+#define SUNXI_R_PRCM_BASE		0x07010000
+//#define SUNXI_R_WDOG_BASE		0x07020400
+#define SUNXI_R_WDOG_BASE		SUNXI_WDOG_BASE
+#define SUNXI_R_PIO_BASE		0x07022000
+#define SUNXI_R_UART_BASE		0x07080000
+#define SUNXI_R_I2C_BASE		0x07081400
+#define SUNXI_R_RSB_BASE		0x07083000
+#define SUNXI_CPUCFG_BASE		0x09010000
+
+#endif /* SUNXI_MMAP_H */
diff --git a/plat/allwinner/sun50i_h616/include/sunxi_spc.h b/plat/allwinner/sun50i_h616/include/sunxi_spc.h
new file mode 100644
index 0000000..0f5965b
--- /dev/null
+++ b/plat/allwinner/sun50i_h616/include/sunxi_spc.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SUNXI_SPC_H
+#define SUNXI_SPC_H
+
+#define SUNXI_SPC_NUM_PORTS		14
+
+#define SUNXI_SPC_DECPORT_STA_REG(p)	(SUNXI_SPC_BASE + 0x0000 + 0x10 * (p))
+#define SUNXI_SPC_DECPORT_SET_REG(p)	(SUNXI_SPC_BASE + 0x0004 + 0x10 * (p))
+#define SUNXI_SPC_DECPORT_CLR_REG(p)	(SUNXI_SPC_BASE + 0x0008 + 0x10 * (p))
+
+#endif /* SUNXI_SPC_H */
diff --git a/plat/allwinner/sun50i_h616/platform.mk b/plat/allwinner/sun50i_h616/platform.mk
new file mode 100644
index 0000000..fc09af7
--- /dev/null
+++ b/plat/allwinner/sun50i_h616/platform.mk
@@ -0,0 +1,24 @@
+#
+# Copyright (c) 2017-2020, ARM Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+# Without a management processor there is no SCPI support.
+SUNXI_PSCI_USE_SCPI	:=	0
+SUNXI_PSCI_USE_NATIVE	:=	1
+
+# The differences between the platforms are covered by the include files.
+include plat/allwinner/common/allwinner-common.mk
+
+# the above could be overwritten on the command line
+ifeq (${SUNXI_PSCI_USE_SCPI}, 1)
+    $(error "H616 does not support SCPI PSCI ops")
+endif
+
+BL31_SOURCES		+=	drivers/allwinner/axp/axp805.c		\
+				drivers/allwinner/sunxi_rsb.c		\
+				common/fdt_fixup.c			\
+				${AW_PLAT}/${PLAT}/prepare_dtb.c
+
+$(eval $(call add_define,SUNXI_BL31_IN_DRAM))
diff --git a/plat/allwinner/sun50i_h616/prepare_dtb.c b/plat/allwinner/sun50i_h616/prepare_dtb.c
new file mode 100644
index 0000000..e94b0b4
--- /dev/null
+++ b/plat/allwinner/sun50i_h616/prepare_dtb.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2021, ARM Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <libfdt.h>
+
+#include <common/debug.h>
+#include <common/fdt_fixup.h>
+#include <common/fdt_wrappers.h>
+
+#include <sunxi_private.h>
+
+void sunxi_prepare_dtb(void *fdt)
+{
+	int ret;
+
+	if (fdt == NULL || fdt_check_header(fdt) != 0) {
+		return;
+	}
+	ret = fdt_open_into(fdt, fdt, 0x100000);
+	if (ret < 0) {
+		ERROR("Preparing devicetree at %p: error %d\n", fdt, ret);
+		return;
+	}
+
+	/* Reserve memory used by Trusted Firmware. */
+	if (fdt_add_reserved_memory(fdt, "tf-a@40000000", BL31_BASE,
+				    BL31_LIMIT - BL31_BASE)) {
+		WARN("Failed to add reserved memory nodes to DT.\n");
+		return;
+	}
+
+	ret = fdt_pack(fdt);
+	if (ret < 0) {
+		ERROR("Failed to pack devicetree at %p: error %d\n",
+		      fdt, ret);
+	} else {
+		clean_dcache_range((uintptr_t)fdt, fdt_blob_size(fdt));
+		INFO("Changed devicetree to reserve BL31 memory.\n");
+	}
+}
diff --git a/plat/allwinner/sun50i_h616/sunxi_power.c b/plat/allwinner/sun50i_h616/sunxi_power.c
new file mode 100644
index 0000000..dd6ebba
--- /dev/null
+++ b/plat/allwinner/sun50i_h616/sunxi_power.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2017-2020, ARM Limited. All rights reserved.
+ * Copyright (c) 2018, Icenowy Zheng <icenowy@aosc.io>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/allwinner/axp.h>
+#include <drivers/allwinner/sunxi_rsb.h>
+#include <lib/mmio.h>
+
+#include <sunxi_cpucfg.h>
+#include <sunxi_def.h>
+#include <sunxi_mmap.h>
+#include <sunxi_private.h>
+
+#define AXP305_I2C_ADDR	0x36
+#define AXP305_HW_ADDR	0x745
+#define AXP305_RT_ADDR	0x3a
+
+static enum pmic_type {
+	UNKNOWN,
+	AXP305,
+} pmic;
+
+int axp_read(uint8_t reg)
+{
+	return rsb_read(AXP305_RT_ADDR, reg);
+}
+
+int axp_write(uint8_t reg, uint8_t val)
+{
+	return rsb_write(AXP305_RT_ADDR, reg, val);
+}
+
+static int rsb_init(void)
+{
+	int ret;
+
+	ret = rsb_init_controller();
+	if (ret)
+		return ret;
+
+	/* Switch to the recommended 3 MHz bus clock. */
+	ret = rsb_set_bus_speed(SUNXI_OSC24M_CLK_IN_HZ, 3000000);
+	if (ret)
+		return ret;
+
+	/* Initiate an I2C transaction to switch the PMIC to RSB mode. */
+	ret = rsb_set_device_mode(AXP20X_MODE_RSB << 16 | AXP20X_MODE_REG << 8);
+	if (ret)
+		return ret;
+
+	/* Associate the 8-bit runtime address with the 12-bit bus address. */
+	ret = rsb_assign_runtime_address(AXP305_HW_ADDR, AXP305_RT_ADDR);
+	if (ret)
+		return ret;
+
+	return axp_check_id();
+}
+
+int sunxi_pmic_setup(uint16_t socid, const void *fdt)
+{
+	int ret;
+
+	INFO("PMIC: Probing AXP305 on RSB\n");
+
+	ret = sunxi_init_platform_r_twi(socid, true);
+	if (ret) {
+		INFO("Could not init platform bus: %d\n", ret);
+		return ret;
+	}
+
+	ret = rsb_init();
+	if (ret) {
+		INFO("Could not init RSB: %d\n", ret);
+		return ret;
+	}
+
+	pmic = AXP305;
+	axp_setup_regulators(fdt);
+
+	/* Switch the PMIC back to I2C mode. */
+	ret = axp_write(AXP20X_MODE_REG, AXP20X_MODE_I2C);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+void sunxi_power_down(void)
+{
+	switch (pmic) {
+	case AXP305:
+		/* Re-initialise after rich OS might have used it. */
+		sunxi_init_platform_r_twi(SUNXI_SOC_H616, true);
+		rsb_init();
+		axp_power_off();
+		break;
+	default:
+		break;
+	}
+}
+
+void sunxi_cpu_power_off_self(void)
+{
+	u_register_t mpidr = read_mpidr();
+	unsigned int core  = MPIDR_AFFLVL0_VAL(mpidr);
+
+	/* Enable the CPUIDLE hardware (only really needs to be done once). */
+	mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0x16aa0000);
+	mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0xaa160001);
+
+	/* Trigger power off for this core. */
+	mmio_write_32(SUNXI_CORE_CLOSE_REG, BIT_32(core));
+}