summaryrefslogtreecommitdiffstats
path: root/plat/allwinner
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--plat/allwinner/common/allwinner-common.mk114
-rw-r--r--plat/allwinner/common/arisc_off.S115
-rw-r--r--plat/allwinner/common/include/mentor_i2c_plat.h28
-rw-r--r--plat/allwinner/common/include/plat_macros.S27
-rw-r--r--plat/allwinner/common/include/platform_def.h84
-rw-r--r--plat/allwinner/common/include/sunxi_def.h26
-rw-r--r--plat/allwinner/common/include/sunxi_private.h61
-rw-r--r--plat/allwinner/common/plat_helpers.S49
-rw-r--r--plat/allwinner/common/sunxi_bl31_setup.c204
-rw-r--r--plat/allwinner/common/sunxi_common.c187
-rw-r--r--plat/allwinner/common/sunxi_cpu_ops.c108
-rw-r--r--plat/allwinner/common/sunxi_native_pm.c81
-rw-r--r--plat/allwinner/common/sunxi_pm.c60
-rw-r--r--plat/allwinner/common/sunxi_prepare_dtb.c52
-rw-r--r--plat/allwinner/common/sunxi_scpi_pm.c207
-rw-r--r--plat/allwinner/common/sunxi_security.c40
-rw-r--r--plat/allwinner/common/sunxi_topology.c39
-rw-r--r--plat/allwinner/sun50i_a64/include/core_off_arisc.h39
-rw-r--r--plat/allwinner/sun50i_a64/include/sunxi_ccu.h14
-rw-r--r--plat/allwinner/sun50i_a64/include/sunxi_cpucfg.h39
-rw-r--r--plat/allwinner/sun50i_a64/include/sunxi_mmap.h74
-rw-r--r--plat/allwinner/sun50i_a64/include/sunxi_spc.h16
-rw-r--r--plat/allwinner/sun50i_a64/platform.mk17
-rw-r--r--plat/allwinner/sun50i_a64/sunxi_idle_states.c27
-rw-r--r--plat/allwinner/sun50i_a64/sunxi_power.c259
-rw-r--r--plat/allwinner/sun50i_h6/include/sunxi_ccu.h14
-rw-r--r--plat/allwinner/sun50i_h6/include/sunxi_cpucfg.h35
-rw-r--r--plat/allwinner/sun50i_h6/include/sunxi_mmap.h63
-rw-r--r--plat/allwinner/sun50i_h6/include/sunxi_spc.h16
-rw-r--r--plat/allwinner/sun50i_h6/platform.mk14
-rw-r--r--plat/allwinner/sun50i_h6/sunxi_idle_states.c11
-rw-r--r--plat/allwinner/sun50i_h6/sunxi_power.c119
-rw-r--r--plat/allwinner/sun50i_h616/include/sunxi_ccu.h14
-rw-r--r--plat/allwinner/sun50i_h616/include/sunxi_cpucfg.h35
-rw-r--r--plat/allwinner/sun50i_h616/include/sunxi_mmap.h46
-rw-r--r--plat/allwinner/sun50i_h616/include/sunxi_spc.h16
-rw-r--r--plat/allwinner/sun50i_h616/platform.mk22
-rw-r--r--plat/allwinner/sun50i_h616/sunxi_idle_states.c11
-rw-r--r--plat/allwinner/sun50i_h616/sunxi_power.c121
-rw-r--r--plat/allwinner/sun50i_r329/include/sunxi_ccu.h14
-rw-r--r--plat/allwinner/sun50i_r329/include/sunxi_cpucfg.h31
-rw-r--r--plat/allwinner/sun50i_r329/include/sunxi_mmap.h55
-rw-r--r--plat/allwinner/sun50i_r329/include/sunxi_spc.h17
-rw-r--r--plat/allwinner/sun50i_r329/platform.mk20
-rw-r--r--plat/allwinner/sun50i_r329/sunxi_idle_states.c11
-rw-r--r--plat/allwinner/sun50i_r329/sunxi_power.c27
46 files changed, 2679 insertions, 0 deletions
diff --git a/plat/allwinner/common/allwinner-common.mk b/plat/allwinner/common/allwinner-common.mk
new file mode 100644
index 0000000..61c1dbe
--- /dev/null
+++ b/plat/allwinner/common/allwinner-common.mk
@@ -0,0 +1,114 @@
+#
+# Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+include lib/xlat_tables_v2/xlat_tables.mk
+include lib/libfdt/libfdt.mk
+include drivers/arm/gic/v2/gicv2.mk
+
+AW_PLAT := plat/allwinner
+
+PLAT_INCLUDES := -Iinclude/plat/arm/common/aarch64 \
+ -I${AW_PLAT}/common/include \
+ -I${AW_PLAT}/${PLAT}/include
+
+PLAT_BL_COMMON_SOURCES := drivers/ti/uart/${ARCH}/16550_console.S \
+ ${XLAT_TABLES_LIB_SRCS} \
+ ${AW_PLAT}/common/plat_helpers.S \
+ ${AW_PLAT}/common/sunxi_common.c
+
+BL31_SOURCES += drivers/allwinner/axp/common.c \
+ ${GICV2_SOURCES} \
+ drivers/delay_timer/delay_timer.c \
+ drivers/delay_timer/generic_delay_timer.c \
+ lib/cpus/${ARCH}/cortex_a53.S \
+ plat/common/plat_gicv2.c \
+ plat/common/plat_psci_common.c \
+ ${AW_PLAT}/common/sunxi_bl31_setup.c \
+ ${AW_PLAT}/${PLAT}/sunxi_idle_states.c \
+ ${AW_PLAT}/common/sunxi_pm.c \
+ ${AW_PLAT}/${PLAT}/sunxi_power.c \
+ ${AW_PLAT}/common/sunxi_security.c \
+ ${AW_PLAT}/common/sunxi_topology.c
+
+# By default, attempt to use SCPI to the ARISC management processor. If SCPI
+# is not enabled or SCP firmware is not loaded, fall back to a simpler native
+# implementation that does not support CPU or system suspend.
+#
+# If SCP firmware will always be present (or absent), the unused implementation
+# can be compiled out.
+SUNXI_PSCI_USE_NATIVE ?= 1
+SUNXI_PSCI_USE_SCPI ?= 1
+
+$(eval $(call assert_boolean,SUNXI_PSCI_USE_NATIVE))
+$(eval $(call assert_boolean,SUNXI_PSCI_USE_SCPI))
+$(eval $(call add_define,SUNXI_PSCI_USE_NATIVE))
+$(eval $(call add_define,SUNXI_PSCI_USE_SCPI))
+
+ifeq (${SUNXI_PSCI_USE_NATIVE}${SUNXI_PSCI_USE_SCPI},00)
+$(error "At least one of SCPI or native PSCI ops must be enabled")
+endif
+
+ifeq (${SUNXI_PSCI_USE_NATIVE},1)
+BL31_SOURCES += ${AW_PLAT}/common/sunxi_cpu_ops.c \
+ ${AW_PLAT}/common/sunxi_native_pm.c
+endif
+
+ifeq (${SUNXI_PSCI_USE_SCPI},1)
+BL31_SOURCES += drivers/allwinner/sunxi_msgbox.c \
+ drivers/arm/css/scpi/css_scpi.c \
+ ${AW_PLAT}/common/sunxi_scpi_pm.c
+endif
+
+SUNXI_SETUP_REGULATORS ?= 1
+$(eval $(call assert_boolean,SUNXI_SETUP_REGULATORS))
+$(eval $(call add_define,SUNXI_SETUP_REGULATORS))
+
+SUNXI_BL31_IN_DRAM ?= 0
+$(eval $(call assert_boolean,SUNXI_BL31_IN_DRAM))
+
+ifeq (${SUNXI_BL31_IN_DRAM},1)
+SUNXI_AMEND_DTB := 1
+$(eval $(call add_define,SUNXI_BL31_IN_DRAM))
+endif
+
+SUNXI_AMEND_DTB ?= 0
+$(eval $(call assert_boolean,SUNXI_AMEND_DTB))
+$(eval $(call add_define,SUNXI_AMEND_DTB))
+
+ifeq (${SUNXI_AMEND_DTB},1)
+BL31_SOURCES += common/fdt_fixup.c \
+ ${AW_PLAT}/common/sunxi_prepare_dtb.c
+endif
+
+# The bootloader is guaranteed to only run on CPU 0 by the boot ROM.
+COLD_BOOT_SINGLE_CPU := 1
+
+# Do not enable SPE (not supported on ARM v8.0).
+ENABLE_SPE_FOR_LOWER_ELS := 0
+
+# Do not enable SVE (not supported on ARM v8.0).
+ENABLE_SVE_FOR_NS := 0
+
+# Enable workarounds for Cortex-A53 errata. Allwinner uses at least r0p4.
+ERRATA_A53_835769 := 1
+ERRATA_A53_843419 := 1
+ERRATA_A53_855873 := 1
+ERRATA_A53_1530924 := 1
+
+# The traditional U-Boot load address is 160MB into DRAM.
+PRELOADED_BL33_BASE ?= 0x4a000000
+
+# The reset vector can be changed for each CPU.
+PROGRAMMABLE_RESET_ADDRESS := 1
+
+# Allow mapping read-only data as execute-never.
+SEPARATE_CODE_AND_RODATA := 1
+
+# BL31 gets loaded alongside BL33 (U-Boot) by U-Boot's SPL
+RESET_TO_BL31 := 1
+
+# This platform is single-cluster and does not require coherency setup.
+WARMBOOT_ENABLE_DCACHE_EARLY := 1
diff --git a/plat/allwinner/common/arisc_off.S b/plat/allwinner/common/arisc_off.S
new file mode 100644
index 0000000..ed10832
--- /dev/null
+++ b/plat/allwinner/common/arisc_off.S
@@ -0,0 +1,115 @@
+# turn_off_core.S
+#
+# Copyright (c) 2018, Andre Przywara <osp@andrep.de>
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# OpenRISC assembly to turn off an ARM core on an Allwinner SoC from
+# the arisc management controller.
+# Generate a binary representation with:
+# $ or1k-elf-as -c -o turn_off_core.o turn_off_core.S
+# $ or1k-elf-objcopy -O binary --reverse-bytes=4 turn_off_core.o \
+# turn_off_core.bin
+# The encoded instructions go into an array defined in
+# plat/allwinner/sun50i_*/include/core_off_arisc.h, to be handed off to
+# the arisc processor.
+#
+# This routine is meant to be called directly from arisc reset (put the
+# start address in the reset vector), to be actually triggered by that
+# very ARM core to be turned off.
+# It expects the core number presented as a mask in the upper half of
+# r3, so to be patched in the lower 16 bits of the first instruction,
+# overwriting the 0 in this code here.
+# The code will do the following:
+# - Read the C_CPU_STATUS register, which contains the status of the WFI
+# lines of each of the four A53 cores.
+# - Loop until the core in question reaches WFI.
+# - Using that mask, activate the core output clamps by setting the
+# respective core bit in CPUX_PWROFF_GATING_REG (0x1f01500).
+# Note that the clamp for core 0 covers more than just the core, activating
+# it hangs the whole system. So we skip this step for core 0.
+# - Using the negated mask, assert the core's reset line by clearing the
+# respective bit in C_RST_CTRL (0x1f01c30).
+# - Finally turn off the core's power switch by writing 0xff to the
+# respective CPUx_PWR_SWITCH_REG (0x1f01540 ff.)
+# - Assert the arisc's own reset to end execution.
+# This also signals other arisc users that the chip is free again.
+# So in C this would look like:
+# while (!(readl(0x1700030) & (1U << core_nr)))
+# ;
+# if (core_nr != 0)
+# writel(readl(0x1f01500) | (1U << core_nr), 0x1f01500);
+# writel(readl(0x1f01c30) & ~(1U << core_nr), 0x1f01c30);
+# writel(0xff, 0x1f01540 + (core_nr * 4));
+# (using A64/H5 addresses)
+
+.text
+_start:
+ l.movhi r3, 0 # FIXUP! with core mask
+ l.movhi r0, 0 # clear r0
+ l.movhi r13, 0x170 # r13: CPU_CFG_BASE=0x01700000
+wait_wfi:
+ l.lwz r5, 0x30(r13) # load C_CPU_STATUS
+ l.and r5, r5, r3 # mask requested core
+ l.sfeq r5, r0 # is it not yet in WFI?
+ l.bf wait_wfi # try again
+
+ l.srli r6, r3, 16 # move mask to lower 16 bits
+ l.sfeqi r6, 1 # core 0 is special
+ l.bf 1f # don't touch the bit for core 0
+ l.movhi r13, 0x1f0 # address of R_CPUCFG (delay)
+ l.lwz r5, 0x1500(r13) # core output clamps
+ l.or r5, r5, r6 # set bit to ...
+ l.sw 0x1500(r13), r5 # ... activate for our core
+
+1: l.lwz r5, 0x1c30(r13) # CPU power-on reset
+ l.xori r6, r6, -1 # negate core mask
+ l.and r5, r5, r6 # clear bit to ...
+ l.sw 0x1c30(r13), r5 # ... assert for our core
+
+ l.ff1 r6, r3 # get core number from high mask
+ l.addi r6, r6, -17 # convert to 0-3
+ l.slli r6, r6, 2 # r5: core number*4 (0-12)
+ l.add r6, r6, r13 # add to base address
+ l.ori r5, r0, 0xff # 0xff means all switches off
+ l.sw 0x1540(r6), r5 # core power switch registers
+
+reset: l.sw 0x1c00(r13),r0 # pull down our own reset line
+
+ l.j reset # just in case ....
+ l.nop 0x0 # (delay slot)
+
+# same as above, but with the MMIO addresses matching the H6 SoC
+_start_h6:
+ l.movhi r3, 0 # FIXUP! with core mask
+ l.movhi r0, 0 # clear r0
+ l.movhi r13, 0x901 # r13: CPU_CFG_BASE=0x09010000
+1:
+ l.lwz r5, 0x80(r13) # load C_CPU_STATUS
+ l.and r5, r5, r3 # mask requested core
+ l.sfeq r5, r0 # is it not yet in WFI?
+ l.bf 1b # try again
+
+ l.srli r6, r3, 16 # move mask to lower 16 bits(ds)
+ l.sfeqi r6, 1 # core 0 is special
+ l.bf 1f # don't touch the bit for core 0
+ l.movhi r13, 0x700 # address of R_CPUCFG (ds)
+ l.lwz r5, 0x0444(r13) # core output clamps
+ l.or r5, r5, r6 # set bit to ...
+ l.sw 0x0444(r13), r5 # ... activate for our core
+
+1: l.lwz r5, 0x0440(r13) # CPU power-on reset
+ l.xori r6, r6, -1 # negate core mask
+ l.and r5, r5, r6 # clear bit to ...
+ l.sw 0x0440(r13), r5 # ... assert for our core
+
+ l.ff1 r6, r3 # get core number from high mask
+ l.addi r6, r6, -17 # convert to 0-3
+ l.slli r6, r6, 2 # r5: core number*4 (0-12)
+ l.add r6, r6, r13 # add to base address
+ l.ori r5, r0, 0xff # 0xff means all switches off
+ l.sw 0x0450(r6), r5 # core power switch registers
+
+1: l.sw 0x0400(r13),r0 # pull down our own reset line
+
+ l.j 1b # just in case ...
+ l.nop 0x0 # (delay slot)
diff --git a/plat/allwinner/common/include/mentor_i2c_plat.h b/plat/allwinner/common/include/mentor_i2c_plat.h
new file mode 100644
index 0000000..d03f2d1
--- /dev/null
+++ b/plat/allwinner/common/include/mentor_i2c_plat.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 Icenowy Zheng <icenowy@aosc.io>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ * https://spdx.org/licenses
+ */
+/* This driver provides I2C support for Allwinner sunXi SoCs */
+
+#ifndef MENTOR_I2C_PLAT_H
+#define MENTOR_I2C_PLAT_H
+
+#define CONFIG_SYS_TCLK 24000000
+#define CONFIG_SYS_I2C_SPEED 100000
+#define CONFIG_SYS_I2C_SLAVE 0
+
+#define I2C_INTERRUPT_CLEAR_INVERTED
+
+struct mentor_i2c_regs {
+ uint32_t slave_address;
+ uint32_t xtnd_slave_addr;
+ uint32_t data;
+ uint32_t control;
+ uint32_t status;
+ uint32_t baudrate;
+ uint32_t soft_reset;
+};
+
+#endif /* MENTOR_I2C_PLAT_H */
diff --git a/plat/allwinner/common/include/plat_macros.S b/plat/allwinner/common/include/plat_macros.S
new file mode 100644
index 0000000..77f183d
--- /dev/null
+++ b/plat/allwinner/common/include/plat_macros.S
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PLAT_MACROS_S
+#define PLAT_MACROS_S
+
+#include <arm_macros.S>
+#include <sunxi_mmap.h>
+
+ /* ---------------------------------------------
+ * The below required platform porting macro
+ * prints out relevant GIC and CCI registers
+ * whenever an unhandled exception is taken in
+ * BL31.
+ * Clobbers: x0 - x10, x16, x17, sp
+ * ---------------------------------------------
+ */
+ .macro plat_crash_print_regs
+ mov_imm x17, SUNXI_GICC_BASE
+ mov_imm x16, SUNXI_GICD_BASE
+ arm_print_gic_regs
+ .endm
+
+#endif /* PLAT_MACROS_S */
diff --git a/plat/allwinner/common/include/platform_def.h b/plat/allwinner/common/include/platform_def.h
new file mode 100644
index 0000000..c9d075a
--- /dev/null
+++ b/plat/allwinner/common/include/platform_def.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PLATFORM_DEF_H
+#define PLATFORM_DEF_H
+
+#include <common/tbbr/tbbr_img_def.h>
+#include <lib/utils_def.h>
+#include <plat/common/common_def.h>
+
+#include <sunxi_mmap.h>
+
+#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 + \
+ SUNXI_SRAM_A2_BL31_OFFSET)
+#define BL31_LIMIT (SUNXI_SRAM_A2_BASE + \
+ SUNXI_SRAM_A2_SIZE - SUNXI_SCP_SIZE)
+
+/* 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
+
+/* The SCP firmware is allocated the last 16KiB of SRAM A2. */
+#define SUNXI_SCP_BASE BL31_LIMIT
+#define SUNXI_SCP_SIZE 0x4000
+
+#endif /* SUNXI_BL31_IN_DRAM */
+
+/* How much DRAM to map (to map BL33, for fetching the DTB from U-Boot) */
+#define SUNXI_DRAM_MAP_SIZE (64U << 20)
+
+#define CACHE_WRITEBACK_SHIFT 6
+#define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT)
+
+#define MAX_STATIC_MMAP_REGIONS 3
+#define MAX_MMAP_REGIONS (5 + MAX_STATIC_MMAP_REGIONS)
+
+#define PLAT_CSS_SCP_COM_SHARED_MEM_BASE \
+ (SUNXI_SRAM_A2_BASE + SUNXI_SRAM_A2_SIZE - 0x200)
+
+/* These states are used directly for SCPI communication. */
+#define PLAT_MAX_PWR_LVL_STATES U(3)
+#define PLAT_MAX_RET_STATE U(1)
+#define PLAT_MAX_OFF_STATE U(3)
+
+#define PLAT_MAX_PWR_LVL U(2)
+#define PLAT_NUM_PWR_DOMAINS (U(1) + \
+ PLATFORM_CLUSTER_COUNT + \
+ PLATFORM_CORE_COUNT)
+
+#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32)
+
+#define PLATFORM_CLUSTER_COUNT U(1)
+#define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER_COUNT * \
+ PLATFORM_MAX_CPUS_PER_CLUSTER)
+#define PLATFORM_MAX_CPUS_PER_CLUSTER U(4)
+#define PLATFORM_STACK_SIZE (0x1000 / PLATFORM_CORE_COUNT)
+
+#ifndef SPD_none
+#ifndef BL32_BASE
+#define BL32_BASE SUNXI_DRAM_BASE
+#endif
+#endif
+
+#endif /* PLATFORM_DEF_H */
diff --git a/plat/allwinner/common/include/sunxi_def.h b/plat/allwinner/common/include/sunxi_def.h
new file mode 100644
index 0000000..c17ef95
--- /dev/null
+++ b/plat/allwinner/common/include/sunxi_def.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SUNXI_DEF_H
+#define SUNXI_DEF_H
+
+/* Clock configuration */
+#define SUNXI_OSC24M_CLK_IN_HZ 24000000
+
+/* UART configuration */
+#define SUNXI_UART0_BAUDRATE 115200
+#define SUNXI_UART0_CLK_IN_HZ SUNXI_OSC24M_CLK_IN_HZ
+
+#define SUNXI_SOC_A64 0x1689
+#define SUNXI_SOC_H5 0x1718
+#define SUNXI_SOC_H6 0x1728
+#define SUNXI_SOC_H616 0x1823
+#define SUNXI_SOC_R329 0x1851
+
+#define JEDEC_ALLWINNER_BKID 9U
+#define JEDEC_ALLWINNER_MFID 0x9eU
+
+#endif /* SUNXI_DEF_H */
diff --git a/plat/allwinner/common/include/sunxi_private.h b/plat/allwinner/common/include/sunxi_private.h
new file mode 100644
index 0000000..6a38657
--- /dev/null
+++ b/plat/allwinner/common/include/sunxi_private.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SUNXI_PRIVATE_H
+#define SUNXI_PRIVATE_H
+
+#include <common/fdt_fixup.h>
+
+#include <lib/psci/psci.h>
+
+extern const struct psci_cpu_idle_state sunxi_idle_states[];
+
+void sunxi_configure_mmu_el3(int flags);
+
+void sunxi_cpu_on(u_register_t mpidr);
+void sunxi_cpu_power_off_others(void);
+void sunxi_cpu_power_off_self(void);
+void sunxi_power_down(void);
+
+#if SUNXI_PSCI_USE_NATIVE
+void sunxi_set_native_psci_ops(const plat_psci_ops_t **psci_ops);
+#else
+static inline void sunxi_set_native_psci_ops(const plat_psci_ops_t **psci_ops)
+{
+}
+#endif
+#if SUNXI_PSCI_USE_SCPI
+bool sunxi_psci_is_scpi(void);
+int sunxi_set_scpi_psci_ops(const plat_psci_ops_t **psci_ops);
+#else
+static inline bool sunxi_psci_is_scpi(void)
+{
+ return false;
+}
+static inline int sunxi_set_scpi_psci_ops(const plat_psci_ops_t **psci_ops)
+{
+ return -1;
+}
+#endif
+int sunxi_validate_ns_entrypoint(uintptr_t ns_entrypoint);
+
+int sunxi_pmic_setup(uint16_t socid, const void *fdt);
+void sunxi_security_setup(void);
+
+uint16_t sunxi_read_soc_id(void);
+void sunxi_set_gpio_out(char port, int pin, bool level_high);
+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);
+
+#if SUNXI_AMEND_DTB
+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/plat_helpers.S b/plat/allwinner/common/plat_helpers.S
new file mode 100644
index 0000000..b00c7ae
--- /dev/null
+++ b/plat/allwinner/common/plat_helpers.S
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <asm_macros.S>
+#include <sunxi_def.h>
+#include <sunxi_mmap.h>
+
+ .globl plat_crash_console_init
+ .globl plat_crash_console_putc
+ .globl plat_crash_console_flush
+ .globl plat_my_core_pos
+ .globl platform_mem_init
+ .globl plat_report_exception
+
+func plat_crash_console_init
+ mov_imm x0, SUNXI_UART0_BASE
+ mov_imm x1, SUNXI_UART0_CLK_IN_HZ
+ mov_imm x2, SUNXI_UART0_BAUDRATE
+ b console_16550_core_init
+endfunc plat_crash_console_init
+
+func plat_crash_console_putc
+ mov_imm x1, SUNXI_UART0_BASE
+ b console_16550_core_putc
+endfunc plat_crash_console_putc
+
+func plat_crash_console_flush
+ ret
+endfunc plat_crash_console_flush
+
+func plat_my_core_pos
+ mrs x0, mpidr_el1
+ and x1, x0, #MPIDR_CLUSTER_MASK
+ and x0, x0, #MPIDR_CPU_MASK
+ add x0, x0, x1, LSR #6
+ ret
+endfunc plat_my_core_pos
+
+func platform_mem_init
+ ret
+endfunc platform_mem_init
+
+func plat_report_exception
+ ret
+endfunc plat_report_exception
diff --git a/plat/allwinner/common/sunxi_bl31_setup.c b/plat/allwinner/common/sunxi_bl31_setup.c
new file mode 100644
index 0000000..a32124a
--- /dev/null
+++ b/plat/allwinner/common/sunxi_bl31_setup.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <libfdt.h>
+
+#include <platform_def.h>
+
+#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>
+#include <drivers/ti/uart/uart_16550.h>
+#include <lib/mmio.h>
+#include <plat/common/platform.h>
+
+#include <sunxi_def.h>
+#include <sunxi_mmap.h>
+#include <sunxi_private.h>
+
+
+static entry_point_info_t bl32_image_ep_info;
+static entry_point_info_t bl33_image_ep_info;
+
+static console_t console;
+
+static void *fdt;
+
+static const gicv2_driver_data_t sunxi_gic_data = {
+ .gicd_base = SUNXI_GICD_BASE,
+ .gicc_base = SUNXI_GICC_BASE,
+};
+
+/*
+ * Try to find a DTB loaded in memory by previous stages.
+ *
+ * At the moment we implement a heuristic to find the DTB attached to U-Boot:
+ * U-Boot appends its DTB to the end of the image. Assuming that BL33 is
+ * U-Boot, try to find the size of the U-Boot image to learn the DTB address.
+ * The generic ARMv8 U-Boot image contains the load address and its size
+ * as u64 variables at the beginning of the image. There might be padding
+ * or other headers before that data, so scan the first 2KB after the BL33
+ * entry point to find the load address, which should be followed by the
+ * size. Adding those together gives us the address of the DTB.
+ */
+static void sunxi_find_dtb(void)
+{
+ uint64_t *u_boot_base;
+ int i;
+
+ u_boot_base = (void *)SUNXI_BL33_VIRT_BASE;
+
+ for (i = 0; i < 2048 / sizeof(uint64_t); i++) {
+ void *dtb_base;
+
+ if (u_boot_base[i] != PRELOADED_BL33_BASE)
+ continue;
+
+ /* Does the suspected U-Boot size look anyhow reasonable? */
+ if (u_boot_base[i + 1] >= 256 * 1024 * 1024)
+ continue;
+
+ /* end of the image: base address + size */
+ dtb_base = (char *)u_boot_base + u_boot_base[i + 1];
+
+ if (fdt_check_header(dtb_base) == 0) {
+ fdt = dtb_base;
+ return;
+ }
+ }
+}
+
+void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1,
+ u_register_t arg2, u_register_t arg3)
+{
+ /* Initialize the debug console as soon as possible */
+ console_16550_register(SUNXI_UART0_BASE, SUNXI_UART0_CLK_IN_HZ,
+ SUNXI_UART0_BAUDRATE, &console);
+
+#ifdef BL32_BASE
+ /* Populate entry point information for BL32 */
+ SET_PARAM_HEAD(&bl32_image_ep_info, PARAM_EP, VERSION_1, 0);
+ SET_SECURITY_STATE(bl32_image_ep_info.h.attr, SECURE);
+ bl32_image_ep_info.pc = BL32_BASE;
+#endif
+
+ /* Populate entry point information for BL33 */
+ SET_PARAM_HEAD(&bl33_image_ep_info, PARAM_EP, VERSION_1, 0);
+ /*
+ * Tell BL31 where the non-trusted software image
+ * is located and the entry state information
+ */
+ bl33_image_ep_info.pc = PRELOADED_BL33_BASE;
+ bl33_image_ep_info.spsr = SPSR_64(MODE_EL2, MODE_SP_ELX,
+ DISABLE_ALL_EXCEPTIONS);
+ SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE);
+}
+
+void bl31_plat_arch_setup(void)
+{
+ sunxi_configure_mmu_el3(0);
+}
+
+void bl31_platform_setup(void)
+{
+ const char *soc_name;
+ uint16_t soc_id = sunxi_read_soc_id();
+
+ switch (soc_id) {
+ case SUNXI_SOC_A64:
+ soc_name = "A64/H64/R18";
+ break;
+ case SUNXI_SOC_H5:
+ soc_name = "H5";
+ break;
+ case SUNXI_SOC_H6:
+ soc_name = "H6";
+ break;
+ case SUNXI_SOC_H616:
+ soc_name = "H616";
+ break;
+ case SUNXI_SOC_R329:
+ soc_name = "R329";
+ break;
+ default:
+ soc_name = "unknown";
+ break;
+ }
+ NOTICE("BL31: Detected Allwinner %s SoC (%04x)\n", soc_name, soc_id);
+
+ generic_delay_timer_init();
+
+ sunxi_find_dtb();
+ if (fdt) {
+ const char *model;
+ int length;
+
+ model = fdt_getprop(fdt, 0, "model", &length);
+ NOTICE("BL31: Found U-Boot DTB at %p, model: %s\n", fdt,
+ model ?: "unknown");
+ } else {
+ NOTICE("BL31: No DTB found.\n");
+ }
+
+ /* Configure the interrupt controller */
+ gicv2_driver_init(&sunxi_gic_data);
+ gicv2_distif_init();
+ gicv2_pcpu_distif_init();
+ gicv2_cpuif_enable();
+
+ sunxi_security_setup();
+
+ /*
+ * On the A64 U-Boot's SPL sets the bus clocks to some conservative
+ * values, to work around FEL mode instabilities with SRAM C accesses.
+ * FEL mode is gone when we reach ATF, so bring the AHB1 bus
+ * (the "main" bus) clock frequency back to the recommended 200MHz,
+ * for improved performance.
+ */
+ if (soc_id == SUNXI_SOC_A64)
+ mmio_write_32(SUNXI_CCU_BASE + 0x54, 0x00003180);
+
+ /*
+ * U-Boot or the kernel don't setup AHB2, which leaves it at the
+ * AHB1 frequency (200 MHz, see above). However Allwinner recommends
+ * 300 MHz, for improved Ethernet and USB performance. Switch the
+ * clock to use "PLL_PERIPH0 / 2".
+ */
+ if (soc_id == SUNXI_SOC_A64 || soc_id == SUNXI_SOC_H5)
+ mmio_write_32(SUNXI_CCU_BASE + 0x5c, 0x1);
+
+ sunxi_pmic_setup(soc_id, fdt);
+
+ INFO("BL31: Platform setup done\n");
+}
+
+void bl31_plat_runtime_setup(void)
+{
+ /* Change the DTB if the configuration requires so. */
+ sunxi_prepare_dtb(fdt);
+
+ console_switch_state(CONSOLE_FLAG_RUNTIME);
+}
+
+entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type)
+{
+ assert(sec_state_is_valid(type) != 0);
+
+ if (type == NON_SECURE)
+ return &bl33_image_ep_info;
+
+ if ((type == SECURE) && bl32_image_ep_info.pc)
+ return &bl32_image_ep_info;
+
+ return NULL;
+}
diff --git a/plat/allwinner/common/sunxi_common.c b/plat/allwinner/common/sunxi_common.c
new file mode 100644
index 0000000..092659c
--- /dev/null
+++ b/plat/allwinner/common/sunxi_common.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+
+#include <common/debug.h>
+#include <lib/mmio.h>
+#include <lib/smccc.h>
+#include <lib/xlat_tables/xlat_tables_v2.h>
+#include <services/arm_arch_svc.h>
+
+#include <sunxi_def.h>
+#include <sunxi_mmap.h>
+#include <sunxi_private.h>
+
+static const mmap_region_t sunxi_mmap[MAX_STATIC_MMAP_REGIONS + 1] = {
+ MAP_REGION_FLAT(SUNXI_SRAM_BASE, SUNXI_SRAM_SIZE,
+ MT_DEVICE | MT_RW | MT_SECURE | MT_EXECUTE_NEVER),
+ MAP_REGION_FLAT(SUNXI_DEV_BASE, SUNXI_DEV_SIZE,
+ MT_DEVICE | MT_RW | MT_SECURE | MT_EXECUTE_NEVER),
+ MAP_REGION(PRELOADED_BL33_BASE, SUNXI_BL33_VIRT_BASE,
+ SUNXI_DRAM_MAP_SIZE, MT_RW_DATA | MT_NS),
+ {},
+};
+
+unsigned int plat_get_syscnt_freq2(void)
+{
+ return SUNXI_OSC24M_CLK_IN_HZ;
+}
+
+void sunxi_configure_mmu_el3(int flags)
+{
+ mmap_add_region(BL_CODE_BASE, BL_CODE_BASE,
+ BL_CODE_END - BL_CODE_BASE,
+ MT_CODE | MT_SECURE);
+ mmap_add_region(BL_CODE_END, BL_CODE_END,
+ BL_END - BL_CODE_END,
+ MT_RW_DATA | MT_SECURE);
+#if SEPARATE_CODE_AND_RODATA
+ mmap_add_region(BL_RO_DATA_BASE, BL_RO_DATA_BASE,
+ BL_RO_DATA_END - BL_RO_DATA_BASE,
+ MT_RO_DATA | MT_SECURE);
+#endif
+#if SEPARATE_NOBITS_REGION
+ mmap_add_region(BL_NOBITS_BASE, BL_NOBITS_BASE,
+ BL_NOBITS_END - BL_NOBITS_BASE,
+ MT_RW_DATA | MT_SECURE);
+#endif
+#if USE_COHERENT_MEM
+ mmap_add_region(BL_COHERENT_RAM_BASE, BL_COHERENT_RAM_BASE,
+ BL_COHERENT_RAM_END - BL_COHERENT_RAM_BASE,
+ MT_DEVICE | MT_RW | MT_SECURE | MT_EXECUTE_NEVER);
+#endif
+
+ mmap_add(sunxi_mmap);
+ init_xlat_tables();
+
+ enable_mmu_el3(0);
+}
+
+#define SRAM_VER_REG (SUNXI_SYSCON_BASE + 0x24)
+uint16_t sunxi_read_soc_id(void)
+{
+ uint32_t reg = mmio_read_32(SRAM_VER_REG);
+
+ /* Set bit 15 to prepare for the SOCID read. */
+ mmio_write_32(SRAM_VER_REG, reg | BIT(15));
+
+ reg = mmio_read_32(SRAM_VER_REG);
+
+ /* deactivate the SOCID access again */
+ mmio_write_32(SRAM_VER_REG, reg & ~BIT(15));
+
+ return reg >> 16;
+}
+
+/*
+ * Configure a given pin to the GPIO-OUT function and sets its level.
+ * The port is given as a capital letter, the pin is the number within
+ * this port group.
+ * So to set pin PC7 to high, use: sunxi_set_gpio_out('C', 7, true);
+ */
+void sunxi_set_gpio_out(char port, int pin, bool level_high)
+{
+ uintptr_t port_base;
+
+ if (port < 'A' || port > 'L')
+ return;
+ if (port == 'L')
+ port_base = SUNXI_R_PIO_BASE;
+ else
+ port_base = SUNXI_PIO_BASE + (port - 'A') * 0x24;
+
+ /* Set the new level first before configuring the pin. */
+ if (level_high)
+ mmio_setbits_32(port_base + 0x10, BIT(pin));
+ else
+ mmio_clrbits_32(port_base + 0x10, BIT(pin));
+
+ /* configure pin as GPIO out (4(3) bits per pin, 1: GPIO out */
+ mmio_clrsetbits_32(port_base + (pin / 8) * 4,
+ 0x7 << ((pin % 8) * 4),
+ 0x1 << ((pin % 8) * 4));
+}
+
+int sunxi_init_platform_r_twi(uint16_t socid, bool use_rsb)
+{
+ uint32_t pin_func = 0x77;
+ uint32_t device_bit;
+ unsigned int reset_offset = 0xb0;
+
+ switch (socid) {
+ case SUNXI_SOC_H5:
+ if (use_rsb)
+ return -ENODEV;
+ pin_func = 0x22;
+ 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;
+ break;
+ case SUNXI_SOC_A64:
+ pin_func = use_rsb ? 0x22 : 0x33;
+ device_bit = use_rsb ? BIT(3) : BIT(6);
+ break;
+ default:
+ INFO("R_I2C/RSB on Allwinner 0x%x SoC not supported\n", socid);
+ return -ENODEV;
+ }
+
+ /* un-gate R_PIO clock */
+ 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 */
+ mmio_clrsetbits_32(SUNXI_R_PIO_BASE + 0x00, 0xffU, pin_func);
+
+ /* level 2 drive strength */
+ mmio_clrsetbits_32(SUNXI_R_PIO_BASE + 0x14, 0x0fU, 0xaU);
+
+ /* set both pins to pull-up */
+ mmio_clrsetbits_32(SUNXI_R_PIO_BASE + 0x1c, 0x0fU, 0x5U);
+
+ /* un-gate clock */
+ 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));
+
+ /* assert, then de-assert reset of I2C/RSB controller */
+ mmio_clrbits_32(SUNXI_R_PRCM_BASE + reset_offset, device_bit);
+ mmio_setbits_32(SUNXI_R_PRCM_BASE + reset_offset, device_bit);
+
+ return 0;
+}
+
+int32_t plat_is_smccc_feature_available(u_register_t fid)
+{
+ switch (fid) {
+ case SMCCC_ARCH_SOC_ID:
+ return SMC_ARCH_CALL_SUCCESS;
+ default:
+ return SMC_ARCH_CALL_NOT_SUPPORTED;
+ }
+}
+
+int32_t plat_get_soc_version(void)
+{
+ int32_t ret;
+
+ ret = SOC_ID_SET_JEP_106(JEDEC_ALLWINNER_BKID, JEDEC_ALLWINNER_MFID);
+
+ return ret | (sunxi_read_soc_id() & SOC_ID_IMPL_DEF_MASK);
+}
+
+int32_t plat_get_soc_revision(void)
+{
+ uint32_t reg = mmio_read_32(SRAM_VER_REG);
+
+ return reg & GENMASK_32(7, 0);
+}
diff --git a/plat/allwinner/common/sunxi_cpu_ops.c b/plat/allwinner/common/sunxi_cpu_ops.c
new file mode 100644
index 0000000..46e7090
--- /dev/null
+++ b/plat/allwinner/common/sunxi_cpu_ops.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <platform_def.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <lib/mmio.h>
+#include <lib/utils_def.h>
+#include <plat/common/platform.h>
+
+#include <sunxi_cpucfg.h>
+#include <sunxi_mmap.h>
+#include <sunxi_private.h>
+
+static void sunxi_cpu_disable_power(unsigned int cluster, unsigned int core)
+{
+ if (mmio_read_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core)) == 0xff)
+ return;
+
+ VERBOSE("PSCI: Disabling power to cluster %d core %d\n", cluster, core);
+
+ mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0xff);
+}
+
+static void sunxi_cpu_enable_power(unsigned int cluster, unsigned int core)
+{
+ if (mmio_read_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core)) == 0)
+ return;
+
+ VERBOSE("PSCI: Enabling power to cluster %d core %d\n", cluster, core);
+
+ /* Power enable sequence from original Allwinner sources */
+ mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0xfe);
+ mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0xf8);
+ mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0xe0);
+ mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0x80);
+ mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0x00);
+ udelay(1);
+}
+
+/* We can't turn ourself off like this, but it works for other cores. */
+static void sunxi_cpu_off(u_register_t mpidr)
+{
+ unsigned int cluster = MPIDR_AFFLVL1_VAL(mpidr);
+ unsigned int core = MPIDR_AFFLVL0_VAL(mpidr);
+
+ VERBOSE("PSCI: Powering off cluster %d core %d\n", cluster, core);
+
+ /* Deassert DBGPWRDUP */
+ mmio_clrbits_32(SUNXI_CPUCFG_DBG_REG0, BIT(core));
+ /* Activate the core output clamps, but not for core 0. */
+ if (core != 0)
+ mmio_setbits_32(SUNXI_POWEROFF_GATING_REG(cluster), BIT(core));
+ /* Assert CPU power-on reset */
+ mmio_clrbits_32(SUNXI_POWERON_RST_REG(cluster), BIT(core));
+ /* Remove power from the CPU */
+ sunxi_cpu_disable_power(cluster, core);
+}
+
+void sunxi_cpu_on(u_register_t mpidr)
+{
+ unsigned int cluster = MPIDR_AFFLVL1_VAL(mpidr);
+ unsigned int core = MPIDR_AFFLVL0_VAL(mpidr);
+
+ VERBOSE("PSCI: Powering on cluster %d core %d\n", cluster, core);
+
+ /* Assert CPU core reset */
+ mmio_clrbits_32(SUNXI_CPUCFG_RST_CTRL_REG(cluster), BIT(core));
+ /* Assert CPU power-on reset */
+ mmio_clrbits_32(SUNXI_POWERON_RST_REG(cluster), BIT(core));
+ /* Set CPU to start in AArch64 mode */
+ mmio_setbits_32(SUNXI_AA64nAA32_REG(cluster),
+ BIT(SUNXI_AA64nAA32_OFFSET + core));
+ /* Apply power to the CPU */
+ sunxi_cpu_enable_power(cluster, core);
+ /* Release the core output clamps */
+ mmio_clrbits_32(SUNXI_POWEROFF_GATING_REG(cluster), BIT(core));
+ /* Deassert CPU power-on reset */
+ mmio_setbits_32(SUNXI_POWERON_RST_REG(cluster), BIT(core));
+ /* Deassert CPU core reset */
+ mmio_setbits_32(SUNXI_CPUCFG_RST_CTRL_REG(cluster), BIT(core));
+ /* Assert DBGPWRDUP */
+ mmio_setbits_32(SUNXI_CPUCFG_DBG_REG0, BIT(core));
+}
+
+void sunxi_cpu_power_off_others(void)
+{
+ u_register_t self = read_mpidr();
+ unsigned int cluster;
+ unsigned int core;
+
+ for (cluster = 0; cluster < PLATFORM_CLUSTER_COUNT; ++cluster) {
+ for (core = 0; core < PLATFORM_MAX_CPUS_PER_CLUSTER; ++core) {
+ u_register_t mpidr = (cluster << MPIDR_AFF1_SHIFT) |
+ (core << MPIDR_AFF0_SHIFT) |
+ BIT(31);
+ if (mpidr != self)
+ sunxi_cpu_off(mpidr);
+ }
+ }
+}
diff --git a/plat/allwinner/common/sunxi_native_pm.c b/plat/allwinner/common/sunxi_native_pm.c
new file mode 100644
index 0000000..148f50e
--- /dev/null
+++ b/plat/allwinner/common/sunxi_native_pm.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/arm/gicv2.h>
+#include <drivers/delay_timer.h>
+#include <lib/mmio.h>
+#include <lib/psci/psci.h>
+
+#include <sunxi_mmap.h>
+#include <sunxi_private.h>
+
+#define SUNXI_WDOG0_CTRL_REG (SUNXI_R_WDOG_BASE + 0x0010)
+#define SUNXI_WDOG0_CFG_REG (SUNXI_R_WDOG_BASE + 0x0014)
+#define SUNXI_WDOG0_MODE_REG (SUNXI_R_WDOG_BASE + 0x0018)
+
+static int sunxi_pwr_domain_on(u_register_t mpidr)
+{
+ sunxi_cpu_on(mpidr);
+
+ return PSCI_E_SUCCESS;
+}
+
+static void sunxi_pwr_domain_off(const psci_power_state_t *target_state)
+{
+ gicv2_cpuif_disable();
+
+ sunxi_cpu_power_off_self();
+}
+
+static void sunxi_pwr_domain_on_finish(const psci_power_state_t *target_state)
+{
+ gicv2_pcpu_distif_init();
+ gicv2_cpuif_enable();
+}
+
+static void __dead2 sunxi_system_off(void)
+{
+ gicv2_cpuif_disable();
+
+ /* Attempt to power down the board (may not return) */
+ sunxi_power_down();
+
+ /* Turn off all CPUs */
+ sunxi_cpu_power_off_others();
+ sunxi_cpu_power_off_self();
+ psci_power_down_wfi();
+}
+
+static void __dead2 sunxi_system_reset(void)
+{
+ gicv2_cpuif_disable();
+
+ /* Reset the whole system when the watchdog times out */
+ mmio_write_32(SUNXI_WDOG0_CFG_REG, 1);
+ /* Enable the watchdog with the shortest timeout (0.5 seconds) */
+ mmio_write_32(SUNXI_WDOG0_MODE_REG, (0 << 4) | 1);
+ /* Wait for twice the watchdog timeout before panicking */
+ mdelay(1000);
+
+ ERROR("PSCI: System reset failed\n");
+ panic();
+}
+
+static const plat_psci_ops_t sunxi_native_psci_ops = {
+ .pwr_domain_on = sunxi_pwr_domain_on,
+ .pwr_domain_off = sunxi_pwr_domain_off,
+ .pwr_domain_on_finish = sunxi_pwr_domain_on_finish,
+ .system_off = sunxi_system_off,
+ .system_reset = sunxi_system_reset,
+ .validate_ns_entrypoint = sunxi_validate_ns_entrypoint,
+};
+
+void sunxi_set_native_psci_ops(const plat_psci_ops_t **psci_ops)
+{
+ *psci_ops = &sunxi_native_psci_ops;
+}
diff --git a/plat/allwinner/common/sunxi_pm.c b/plat/allwinner/common/sunxi_pm.c
new file mode 100644
index 0000000..3772b4a
--- /dev/null
+++ b/plat/allwinner/common/sunxi_pm.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <platform_def.h>
+
+#include <common/debug.h>
+#include <common/fdt_fixup.h>
+#include <lib/mmio.h>
+#include <lib/psci/psci.h>
+
+#include <sunxi_cpucfg.h>
+#include <sunxi_private.h>
+
+static bool psci_is_scpi;
+
+#if SUNXI_PSCI_USE_SCPI
+bool sunxi_psci_is_scpi(void)
+{
+ return psci_is_scpi;
+}
+#endif
+
+int sunxi_validate_ns_entrypoint(uintptr_t ns_entrypoint)
+{
+ /* The non-secure entry point must be in DRAM */
+ if (ns_entrypoint < SUNXI_DRAM_BASE) {
+ return PSCI_E_INVALID_ADDRESS;
+ }
+
+ return PSCI_E_SUCCESS;
+}
+
+int plat_setup_psci_ops(uintptr_t sec_entrypoint,
+ const plat_psci_ops_t **psci_ops)
+{
+ assert(psci_ops);
+
+ /* Program all CPU entry points. */
+ for (unsigned int cpu = 0; cpu < PLATFORM_CORE_COUNT; ++cpu) {
+ mmio_write_32(SUNXI_CPUCFG_RVBAR_LO_REG(cpu),
+ sec_entrypoint & 0xffffffff);
+ mmio_write_32(SUNXI_CPUCFG_RVBAR_HI_REG(cpu),
+ sec_entrypoint >> 32);
+ }
+
+ if (sunxi_set_scpi_psci_ops(psci_ops) == 0) {
+ INFO("PSCI: Suspend is available via SCPI\n");
+ psci_is_scpi = true;
+ } else {
+ INFO("PSCI: Suspend is unavailable\n");
+ sunxi_set_native_psci_ops(psci_ops);
+ }
+
+ return 0;
+}
diff --git a/plat/allwinner/common/sunxi_prepare_dtb.c b/plat/allwinner/common/sunxi_prepare_dtb.c
new file mode 100644
index 0000000..66af35a
--- /dev/null
+++ b/plat/allwinner/common/sunxi_prepare_dtb.c
@@ -0,0 +1,52 @@
+/*
+ * 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, 0x10000);
+ if (ret < 0) {
+ ERROR("Preparing devicetree at %p: error %d\n", fdt, ret);
+ return;
+ }
+
+#ifdef SUNXI_BL31_IN_DRAM
+ /* 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");
+ }
+#endif
+
+ if (sunxi_psci_is_scpi()) {
+ ret = fdt_add_cpu_idle_states(fdt, sunxi_idle_states);
+ if (ret < 0) {
+ WARN("Failed to add idle states to DT: %d\n", ret);
+ }
+ }
+
+ ret = fdt_pack(fdt);
+ if (ret < 0) {
+ ERROR("Failed to pack devicetree at %p: error %d\n",
+ fdt, ret);
+ }
+
+ clean_dcache_range((uintptr_t)fdt, fdt_blob_size(fdt));
+ INFO("Changed devicetree.\n");
+}
diff --git a/plat/allwinner/common/sunxi_scpi_pm.c b/plat/allwinner/common/sunxi_scpi_pm.c
new file mode 100644
index 0000000..41dc563
--- /dev/null
+++ b/plat/allwinner/common/sunxi_scpi_pm.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <platform_def.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/arm/css/css_scpi.h>
+#include <drivers/arm/gicv2.h>
+#include <lib/mmio.h>
+#include <lib/psci/psci.h>
+
+#include <sunxi_mmap.h>
+#include <sunxi_private.h>
+
+/*
+ * The addresses for the SCP exception vectors are defined in the or1k
+ * architecture specification.
+ */
+#define OR1K_VEC_FIRST 0x01
+#define OR1K_VEC_LAST 0x0e
+#define OR1K_VEC_ADDR(n) (0x100 * (n))
+
+/*
+ * This magic value is the little-endian representation of the or1k
+ * instruction "l.mfspr r2, r0, 0x12", which is guaranteed to be the
+ * first instruction in the SCP firmware.
+ */
+#define SCP_FIRMWARE_MAGIC 0xb4400012
+
+#define PLAT_LOCAL_PSTATE_WIDTH U(4)
+#define PLAT_LOCAL_PSTATE_MASK ((U(1) << PLAT_LOCAL_PSTATE_WIDTH) - 1)
+
+#define CPU_PWR_LVL MPIDR_AFFLVL0
+#define CLUSTER_PWR_LVL MPIDR_AFFLVL1
+#define SYSTEM_PWR_LVL MPIDR_AFFLVL2
+
+#define CPU_PWR_STATE(state) \
+ ((state)->pwr_domain_state[CPU_PWR_LVL])
+#define CLUSTER_PWR_STATE(state) \
+ ((state)->pwr_domain_state[CLUSTER_PWR_LVL])
+#define SYSTEM_PWR_STATE(state) \
+ ((state)->pwr_domain_state[SYSTEM_PWR_LVL])
+
+static void sunxi_cpu_standby(plat_local_state_t cpu_state)
+{
+ u_register_t scr = read_scr_el3();
+
+ assert(is_local_state_retn(cpu_state));
+
+ write_scr_el3(scr | SCR_IRQ_BIT);
+ wfi();
+ write_scr_el3(scr);
+}
+
+static int sunxi_pwr_domain_on(u_register_t mpidr)
+{
+ scpi_set_css_power_state(mpidr,
+ scpi_power_on,
+ scpi_power_on,
+ scpi_power_on);
+
+ return PSCI_E_SUCCESS;
+}
+
+static void sunxi_pwr_domain_off(const psci_power_state_t *target_state)
+{
+ plat_local_state_t cpu_pwr_state = CPU_PWR_STATE(target_state);
+ plat_local_state_t cluster_pwr_state = CLUSTER_PWR_STATE(target_state);
+ plat_local_state_t system_pwr_state = SYSTEM_PWR_STATE(target_state);
+
+ if (is_local_state_off(cpu_pwr_state)) {
+ gicv2_cpuif_disable();
+ }
+
+ scpi_set_css_power_state(read_mpidr(),
+ cpu_pwr_state,
+ cluster_pwr_state,
+ system_pwr_state);
+}
+
+static void sunxi_pwr_domain_on_finish(const psci_power_state_t *target_state)
+{
+ if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) {
+ gicv2_distif_init();
+ }
+ if (is_local_state_off(CPU_PWR_STATE(target_state))) {
+ gicv2_pcpu_distif_init();
+ gicv2_cpuif_enable();
+ }
+}
+
+static void __dead2 sunxi_system_off(void)
+{
+ uint32_t ret;
+
+ gicv2_cpuif_disable();
+
+ /* Send the power down request to the SCP. */
+ ret = scpi_sys_power_state(scpi_system_shutdown);
+ if (ret != SCP_OK) {
+ ERROR("PSCI: SCPI %s failed: %d\n", "shutdown", ret);
+ }
+
+ psci_power_down_wfi();
+}
+
+static void __dead2 sunxi_system_reset(void)
+{
+ uint32_t ret;
+
+ gicv2_cpuif_disable();
+
+ /* Send the system reset request to the SCP. */
+ ret = scpi_sys_power_state(scpi_system_reboot);
+ if (ret != SCP_OK) {
+ ERROR("PSCI: SCPI %s failed: %d\n", "reboot", ret);
+ }
+
+ psci_power_down_wfi();
+}
+
+static int sunxi_validate_power_state(unsigned int power_state,
+ psci_power_state_t *req_state)
+{
+ unsigned int power_level = psci_get_pstate_pwrlvl(power_state);
+ unsigned int state_id = psci_get_pstate_id(power_state);
+ unsigned int type = psci_get_pstate_type(power_state);
+ unsigned int i;
+
+ assert(req_state != NULL);
+
+ if (power_level > PLAT_MAX_PWR_LVL) {
+ return PSCI_E_INVALID_PARAMS;
+ }
+
+ if (type == PSTATE_TYPE_STANDBY) {
+ return PSCI_E_INVALID_PARAMS;
+ }
+
+ /* Pass through the requested PSCI state as-is. */
+ for (i = 0; i <= power_level; ++i) {
+ unsigned int local_pstate = state_id & PLAT_LOCAL_PSTATE_MASK;
+
+ req_state->pwr_domain_state[i] = local_pstate;
+ state_id >>= PLAT_LOCAL_PSTATE_WIDTH;
+ }
+
+ /* Higher power domain levels should all remain running */
+ for (; i <= PLAT_MAX_PWR_LVL; ++i) {
+ req_state->pwr_domain_state[i] = PSCI_LOCAL_STATE_RUN;
+ }
+
+ return PSCI_E_SUCCESS;
+}
+
+static void sunxi_get_sys_suspend_power_state(psci_power_state_t *req_state)
+{
+ assert(req_state != NULL);
+
+ for (unsigned int i = 0; i <= PLAT_MAX_PWR_LVL; ++i) {
+ req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
+ }
+}
+
+static const plat_psci_ops_t sunxi_scpi_psci_ops = {
+ .cpu_standby = sunxi_cpu_standby,
+ .pwr_domain_on = sunxi_pwr_domain_on,
+ .pwr_domain_off = sunxi_pwr_domain_off,
+ .pwr_domain_suspend = sunxi_pwr_domain_off,
+ .pwr_domain_on_finish = sunxi_pwr_domain_on_finish,
+ .pwr_domain_suspend_finish = sunxi_pwr_domain_on_finish,
+ .system_off = sunxi_system_off,
+ .system_reset = sunxi_system_reset,
+ .validate_power_state = sunxi_validate_power_state,
+ .validate_ns_entrypoint = sunxi_validate_ns_entrypoint,
+ .get_sys_suspend_power_state = sunxi_get_sys_suspend_power_state,
+};
+
+int sunxi_set_scpi_psci_ops(const plat_psci_ops_t **psci_ops)
+{
+ *psci_ops = &sunxi_scpi_psci_ops;
+
+ /* Check for a valid SCP firmware. */
+ if (mmio_read_32(SUNXI_SCP_BASE) != SCP_FIRMWARE_MAGIC) {
+ return -1;
+ }
+
+ /* Program SCP exception vectors to the firmware entrypoint. */
+ for (unsigned int i = OR1K_VEC_FIRST; i <= OR1K_VEC_LAST; ++i) {
+ uint32_t vector = SUNXI_SRAM_A2_BASE + OR1K_VEC_ADDR(i);
+ uint32_t offset = SUNXI_SCP_BASE - vector;
+
+ mmio_write_32(vector, offset >> 2);
+ }
+
+ /* Take the SCP out of reset. */
+ mmio_setbits_32(SUNXI_R_CPUCFG_BASE, BIT(0));
+
+ /* Wait for the SCP firmware to boot. */
+ return scpi_wait_ready();
+}
diff --git a/plat/allwinner/common/sunxi_security.c b/plat/allwinner/common/sunxi_security.c
new file mode 100644
index 0000000..98b91c3
--- /dev/null
+++ b/plat/allwinner/common/sunxi_security.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <common/debug.h>
+#include <lib/mmio.h>
+
+#include <sunxi_ccu.h>
+#include <sunxi_mmap.h>
+#include <sunxi_private.h>
+#include <sunxi_spc.h>
+
+#define DMA_SEC_REG 0x20
+
+/*
+ * Setup the peripherals to be accessible by non-secure world.
+ * This will not work for the Secure Peripherals Controller (SPC) unless
+ * a fuse it burnt (seems to be an erratum), but we do it nevertheless,
+ * to allow booting on boards using secure boot.
+ */
+void sunxi_security_setup(void)
+{
+ int i;
+
+ INFO("Configuring SPC Controller\n");
+ /* SPC setup: set all devices to non-secure */
+ for (i = 0; i < SUNXI_SPC_NUM_PORTS; i++)
+ mmio_write_32(SUNXI_SPC_DECPORT_SET_REG(i), 0xffffffff);
+
+ /* set MBUS clocks, bus clocks (AXI/AHB/APB) and PLLs to non-secure */
+ mmio_write_32(SUNXI_CCU_SEC_SWITCH_REG, 0x7);
+
+ /* Set R_PRCM bus clocks to non-secure */
+ mmio_write_32(SUNXI_R_PRCM_SEC_SWITCH_REG, 0x1);
+
+ /* Set all DMA channels (16 max.) to non-secure */
+ mmio_write_32(SUNXI_DMA_BASE + DMA_SEC_REG, 0xffff);
+}
diff --git a/plat/allwinner/common/sunxi_topology.c b/plat/allwinner/common/sunxi_topology.c
new file mode 100644
index 0000000..45be1e0
--- /dev/null
+++ b/plat/allwinner/common/sunxi_topology.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <platform_def.h>
+
+#include <arch.h>
+#include <plat/common/platform.h>
+
+static const unsigned char plat_power_domain_tree_desc[PLAT_MAX_PWR_LVL + 1] = {
+ /* One root node for the SoC */
+ 1,
+ /* One node for each cluster */
+ PLATFORM_CLUSTER_COUNT,
+ /* One set of CPUs per cluster */
+ PLATFORM_MAX_CPUS_PER_CLUSTER,
+};
+
+int plat_core_pos_by_mpidr(u_register_t mpidr)
+{
+ unsigned int cluster = MPIDR_AFFLVL1_VAL(mpidr);
+ unsigned int core = MPIDR_AFFLVL0_VAL(mpidr);
+
+ if (MPIDR_AFFLVL3_VAL(mpidr) > 0 ||
+ MPIDR_AFFLVL2_VAL(mpidr) > 0 ||
+ cluster >= PLATFORM_CLUSTER_COUNT ||
+ core >= PLATFORM_MAX_CPUS_PER_CLUSTER) {
+ return -1;
+ }
+
+ return cluster * PLATFORM_MAX_CPUS_PER_CLUSTER + core;
+}
+
+const unsigned char *plat_get_power_domain_tree_desc(void)
+{
+ return plat_power_domain_tree_desc;
+}
diff --git a/plat/allwinner/sun50i_a64/include/core_off_arisc.h b/plat/allwinner/sun50i_a64/include/core_off_arisc.h
new file mode 100644
index 0000000..ae436ca
--- /dev/null
+++ b/plat/allwinner/sun50i_a64/include/core_off_arisc.h
@@ -0,0 +1,39 @@
+/*
+ * 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 */
+ 0x19a00170, /* l.movhi r13, 0x170 */
+ 0x84ad0030, /* l.lwz r5, 0x30(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 */
+ 0x19a001f0, /* l.movhi r13, 0x1f0 */
+ 0x84ad1500, /* l.lwz r5, 0x1500(r13) */
+ 0xe0a53004, /* l.or r5, r5, r6 */
+ 0xd44d2d00, /* l.sw 0x1500(r13), r5 */
+
+ 0x84ad1c30, /* l.lwz r5, 0x1c30(r13) */
+ 0xacc6ffff, /* l.xori r6, r6, -1 */
+ 0xe0a53003, /* l.and r5, r5, r6 */
+ 0xd46d2c30, /* l.sw 0x1c30(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 */
+ 0xd4462d40, /* l.sw 0x1540(r6), r5 */
+
+ 0xd46d0400, /* l.sw 0x1c00(r13), r0 */
+ 0x03ffffff, /* l.j -1 */
+ 0x15000000, /* l.nop */
+};
diff --git a/plat/allwinner/sun50i_a64/include/sunxi_ccu.h b/plat/allwinner/sun50i_a64/include/sunxi_ccu.h
new file mode 100644
index 0000000..2a24886
--- /dev/null
+++ b/plat/allwinner/sun50i_a64/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 + 0x02f0)
+
+#define SUNXI_R_PRCM_SEC_SWITCH_REG (SUNXI_R_PRCM_BASE + 0x01d0)
+
+#endif /* SUNXI_CCU_H */
diff --git a/plat/allwinner/sun50i_a64/include/sunxi_cpucfg.h b/plat/allwinner/sun50i_a64/include/sunxi_cpucfg.h
new file mode 100644
index 0000000..aed3585
--- /dev/null
+++ b/plat/allwinner/sun50i_a64/include/sunxi_cpucfg.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. 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 + 0x0000 + (c) * 16)
+#define SUNXI_CPUCFG_CLS_CTRL_REG1(c) (SUNXI_CPUCFG_BASE + 0x0004 + (c) * 16)
+#define SUNXI_CPUCFG_CACHE_CFG_REG0 (SUNXI_CPUCFG_BASE + 0x0008)
+#define SUNXI_CPUCFG_CACHE_CFG_REG1 (SUNXI_CPUCFG_BASE + 0x000c)
+#define SUNXI_CPUCFG_DBG_REG0 (SUNXI_CPUCFG_BASE + 0x0020)
+#define SUNXI_CPUCFG_GLB_CTRL_REG (SUNXI_CPUCFG_BASE + 0x0028)
+#define SUNXI_CPUCFG_CPU_STS_REG(c) (SUNXI_CPUCFG_BASE + 0x0030 + (c) * 4)
+#define SUNXI_CPUCFG_L2_STS_REG (SUNXI_CPUCFG_BASE + 0x003c)
+#define SUNXI_CPUCFG_RST_CTRL_REG(c) (SUNXI_CPUCFG_BASE + 0x0080 + (c) * 4)
+#define SUNXI_CPUCFG_RVBAR_LO_REG(n) (SUNXI_CPUCFG_BASE + 0x00a0 + (n) * 8)
+#define SUNXI_CPUCFG_RVBAR_HI_REG(n) (SUNXI_CPUCFG_BASE + 0x00a4 + (n) * 8)
+
+#define SUNXI_CPU_POWER_CLAMP_REG(c, n) (SUNXI_R_PRCM_BASE + 0x0140 + \
+ (c) * 16 + (n) * 4)
+#define SUNXI_POWEROFF_GATING_REG(c) (SUNXI_R_PRCM_BASE + 0x0100 + (c) * 4)
+#define SUNXI_R_CPUCFG_CPUS_RST_REG (SUNXI_R_CPUCFG_BASE + 0x0000)
+#define SUNXI_POWERON_RST_REG(c) (SUNXI_R_CPUCFG_BASE + 0x0030 + (c) * 4)
+#define SUNXI_R_CPUCFG_SYS_RST_REG (SUNXI_R_CPUCFG_BASE + 0x0140)
+#define SUNXI_R_CPUCFG_SS_FLAG_REG (SUNXI_R_CPUCFG_BASE + 0x01a0)
+#define SUNXI_R_CPUCFG_CPU_ENTRY_REG (SUNXI_R_CPUCFG_BASE + 0x01a4)
+#define SUNXI_R_CPUCFG_SS_ENTRY_REG (SUNXI_R_CPUCFG_BASE + 0x01a8)
+#define SUNXI_R_CPUCFG_HP_FLAG_REG (SUNXI_R_CPUCFG_BASE + 0x01ac)
+
+#define SUNXI_AA64nAA32_REG SUNXI_CPUCFG_CLS_CTRL_REG0
+#define SUNXI_AA64nAA32_OFFSET 24
+
+#endif /* SUNXI_CPUCFG_H */
diff --git a/plat/allwinner/sun50i_a64/include/sunxi_mmap.h b/plat/allwinner/sun50i_a64/include/sunxi_mmap.h
new file mode 100644
index 0000000..6d10921
--- /dev/null
+++ b/plat/allwinner/sun50i_a64/include/sunxi_mmap.h
@@ -0,0 +1,74 @@
+/*
+ * 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 0x00010000
+#define SUNXI_SRAM_SIZE 0x00044000
+#define SUNXI_SRAM_A1_BASE 0x00010000
+#define SUNXI_SRAM_A1_SIZE 0x00008000
+#define SUNXI_SRAM_A2_BASE 0x00040000
+#define SUNXI_SRAM_A2_BL31_OFFSET 0x00004000
+#define SUNXI_SRAM_A2_SIZE 0x00014000
+#define SUNXI_SRAM_C_BASE 0x00018000
+#define SUNXI_SRAM_C_SIZE 0x0001c000
+#define SUNXI_DEV_BASE 0x01000000
+#define SUNXI_DEV_SIZE 0x01000000
+#define SUNXI_DRAM_BASE 0x40000000
+#define SUNXI_DRAM_VIRT_BASE 0x02000000
+
+/* Memory-mapped devices */
+#define SUNXI_CPU_MBIST_BASE 0x01502000
+#define SUNXI_CPUCFG_BASE 0x01700000
+#define SUNXI_SYSCON_BASE 0x01c00000
+#define SUNXI_DMA_BASE 0x01c02000
+#define SUNXI_KEYMEM_BASE 0x01c0b000
+#define SUNXI_SMHC0_BASE 0x01c0f000
+#define SUNXI_SMHC1_BASE 0x01c10000
+#define SUNXI_SMHC2_BASE 0x01c11000
+#define SUNXI_SID_BASE 0x01c14000
+#define SUNXI_MSGBOX_BASE 0x01c17000
+#define SUNXI_SPINLOCK_BASE 0x01c18000
+#define SUNXI_CCU_BASE 0x01c20000
+#define SUNXI_PIO_BASE 0x01c20800
+#define SUNXI_TIMER_BASE 0x01c20c00
+#define SUNXI_WDOG_BASE 0x01c20ca0
+#define SUNXI_SPC_BASE 0x01c23400
+#define SUNXI_THS_BASE 0x01c25000
+#define SUNXI_UART0_BASE 0x01c28000
+#define SUNXI_UART1_BASE 0x01c28400
+#define SUNXI_UART2_BASE 0x01c28800
+#define SUNXI_UART3_BASE 0x01c28c00
+#define SUNXI_I2C0_BASE 0x01c2ac00
+#define SUNXI_I2C1_BASE 0x01c2b000
+#define SUNXI_I2C2_BASE 0x01c2b400
+#define SUNXI_DRAMCOM_BASE 0x01c62000
+#define SUNXI_DRAMCTL_BASE 0x01c63000
+#define SUNXI_DRAMPHY_BASE 0x01c65000
+#define SUNXI_SPI0_BASE 0x01c68000
+#define SUNXI_SPI1_BASE 0x01c69000
+#define SUNXI_SCU_BASE 0x01c80000
+#define SUNXI_GICD_BASE 0x01c81000
+#define SUNXI_GICC_BASE 0x01c82000
+#define SUNXI_RTC_BASE 0x01f00000
+#define SUNXI_R_TIMER_BASE 0x01f00800
+#define SUNXI_R_INTC_BASE 0x01f00c00
+#define SUNXI_R_WDOG_BASE 0x01f01000
+#define SUNXI_R_PRCM_BASE 0x01f01400
+#define SUNXI_R_TWD_BASE 0x01f01800
+#define SUNXI_R_CPUCFG_BASE 0x01f01c00
+#define SUNXI_R_CIR_BASE 0x01f02000
+#define SUNXI_R_I2C_BASE 0x01f02400
+#define SUNXI_R_UART_BASE 0x01f02800
+#define SUNXI_R_PIO_BASE 0x01f02c00
+#define SUNXI_R_RSB_BASE 0x01f03400
+#define SUNXI_R_PWM_BASE 0x01f03800
+
+#endif /* SUNXI_MMAP_H */
diff --git a/plat/allwinner/sun50i_a64/include/sunxi_spc.h b/plat/allwinner/sun50i_a64/include/sunxi_spc.h
new file mode 100644
index 0000000..5ba7e18
--- /dev/null
+++ b/plat/allwinner/sun50i_a64/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 6
+
+#define SUNXI_SPC_DECPORT_STA_REG(p) (SUNXI_SPC_BASE + 0x0004 + 0x0c * (p))
+#define SUNXI_SPC_DECPORT_SET_REG(p) (SUNXI_SPC_BASE + 0x0008 + 0x0c * (p))
+#define SUNXI_SPC_DECPORT_CLR_REG(p) (SUNXI_SPC_BASE + 0x000c + 0x0c * (p))
+
+#endif /* SUNXI_SPC_H */
diff --git a/plat/allwinner/sun50i_a64/platform.mk b/plat/allwinner/sun50i_a64/platform.mk
new file mode 100644
index 0000000..e3c7c52
--- /dev/null
+++ b/plat/allwinner/sun50i_a64/platform.mk
@@ -0,0 +1,17 @@
+#
+# Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+# The differences between the platform are covered by the include files.
+include plat/allwinner/common/allwinner-common.mk
+
+BL31_SOURCES += drivers/allwinner/axp/axp803.c \
+ drivers/allwinner/sunxi_rsb.c
+
+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_idle_states.c b/plat/allwinner/sun50i_a64/sunxi_idle_states.c
new file mode 100644
index 0000000..2918bb7
--- /dev/null
+++ b/plat/allwinner/sun50i_a64/sunxi_idle_states.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sunxi_private.h>
+
+const struct psci_cpu_idle_state sunxi_idle_states[] = {
+ {
+ .name = "cpu-sleep",
+ .power_state = 0x00010003,
+ .local_timer_stop = true,
+ .entry_latency_us = 800,
+ .exit_latency_us = 1500,
+ .min_residency_us = 25000
+ },
+ {
+ .name = "cluster-sleep",
+ .power_state = 0x01010013,
+ .local_timer_stop = true,
+ .entry_latency_us = 850,
+ .exit_latency_us = 1500,
+ .min_residency_us = 50000
+ },
+ {}
+};
diff --git a/plat/allwinner/sun50i_a64/sunxi_power.c b/plat/allwinner/sun50i_a64/sunxi_power.c
new file mode 100644
index 0000000..a35b9dd
--- /dev/null
+++ b/plat/allwinner/sun50i_a64/sunxi_power.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2018, Icenowy Zheng <icenowy@aosc.io>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+
+#include <platform_def.h>
+
+#include <common/debug.h>
+#include <drivers/allwinner/axp.h>
+#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>
+
+static enum pmic_type {
+ UNKNOWN,
+ GENERIC_H5,
+ GENERIC_A64,
+ REF_DESIGN_H5, /* regulators controlled by GPIO pins on port L */
+ AXP803_RSB, /* PMIC connected via RSB on most A64 boards */
+} pmic;
+
+#define AXP803_HW_ADDR 0x3a3
+#define AXP803_RT_ADDR 0x2d
+
+/*
+ * On boards without a proper PMIC we struggle to turn off the system properly.
+ * Try to turn off as much off the system as we can, to reduce power
+ * consumption. This should be entered with only one core running and SMP
+ * disabled.
+ * This function only cares about peripherals.
+ */
+static void sunxi_turn_off_soc(uint16_t socid)
+{
+ int i;
+
+ /** Turn off most peripherals, most importantly DRAM users. **/
+ /* Keep DRAM controller running for now. */
+ mmio_clrbits_32(SUNXI_CCU_BASE + 0x2c0, ~BIT_32(14));
+ mmio_clrbits_32(SUNXI_CCU_BASE + 0x60, ~BIT_32(14));
+ /* Contains msgbox (bit 21) and spinlock (bit 22) */
+ mmio_write_32(SUNXI_CCU_BASE + 0x2c4, 0);
+ mmio_write_32(SUNXI_CCU_BASE + 0x64, 0);
+ mmio_write_32(SUNXI_CCU_BASE + 0x2c8, 0);
+ /* Keep PIO controller running for now. */
+ mmio_clrbits_32(SUNXI_CCU_BASE + 0x68, ~(BIT_32(5)));
+ mmio_write_32(SUNXI_CCU_BASE + 0x2d0, 0);
+ /* Contains UART0 (bit 16) */
+ mmio_write_32(SUNXI_CCU_BASE + 0x2d8, 0);
+ mmio_write_32(SUNXI_CCU_BASE + 0x6c, 0);
+ mmio_write_32(SUNXI_CCU_BASE + 0x70, 0);
+
+ /** Turn off DRAM controller. **/
+ mmio_clrbits_32(SUNXI_CCU_BASE + 0x2c0, BIT_32(14));
+ mmio_clrbits_32(SUNXI_CCU_BASE + 0x60, BIT_32(14));
+
+ /** Migrate CPU and bus clocks away from the PLLs. **/
+ /* AHB1: use OSC24M/1, APB1 = AHB1 / 2 */
+ mmio_write_32(SUNXI_CCU_BASE + 0x54, 0x1000);
+ /* APB2: use OSC24M */
+ mmio_write_32(SUNXI_CCU_BASE + 0x58, 0x1000000);
+ /* AHB2: use AHB1 clock */
+ mmio_write_32(SUNXI_CCU_BASE + 0x5c, 0);
+ /* CPU: use OSC24M */
+ mmio_write_32(SUNXI_CCU_BASE + 0x50, 0x10000);
+
+ /** Turn off PLLs. **/
+ for (i = 0; i < 6; i++)
+ mmio_clrbits_32(SUNXI_CCU_BASE + i * 8, BIT(31));
+ switch (socid) {
+ case SUNXI_SOC_H5:
+ mmio_clrbits_32(SUNXI_CCU_BASE + 0x44, BIT(31));
+ break;
+ case SUNXI_SOC_A64:
+ mmio_clrbits_32(SUNXI_CCU_BASE + 0x2c, BIT(31));
+ mmio_clrbits_32(SUNXI_CCU_BASE + 0x4c, BIT(31));
+ break;
+ }
+}
+
+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(AXP803_HW_ADDR,
+ AXP803_RT_ADDR);
+ if (ret)
+ return ret;
+
+ return axp_check_id();
+}
+
+int axp_read(uint8_t reg)
+{
+ return rsb_read(AXP803_RT_ADDR, reg);
+}
+
+int axp_write(uint8_t reg, uint8_t val)
+{
+ return rsb_write(AXP803_RT_ADDR, reg, val);
+}
+
+int sunxi_pmic_setup(uint16_t socid, const void *fdt)
+{
+ int ret;
+
+ switch (socid) {
+ case SUNXI_SOC_H5:
+ NOTICE("PMIC: Assuming H5 reference regulator design\n");
+
+ pmic = REF_DESIGN_H5;
+
+ break;
+ case SUNXI_SOC_A64:
+ pmic = GENERIC_A64;
+
+ INFO("PMIC: Probing AXP803 on RSB\n");
+
+ ret = sunxi_init_platform_r_twi(socid, true);
+ if (ret)
+ return ret;
+
+ ret = rsb_init();
+ if (ret)
+ return ret;
+
+ pmic = AXP803_RSB;
+ axp_setup_regulators(fdt);
+
+ /* Switch the PMIC back to I2C mode. */
+ ret = axp_write(AXP20X_MODE_REG, AXP20X_MODE_I2C);
+ if (ret)
+ return ret;
+
+ break;
+ default:
+ return -ENODEV;
+ }
+ return 0;
+}
+
+void sunxi_power_down(void)
+{
+ switch (pmic) {
+ case GENERIC_H5:
+ /* Turn off as many peripherals and clocks as we can. */
+ sunxi_turn_off_soc(SUNXI_SOC_H5);
+ /* Turn off the pin controller now. */
+ mmio_write_32(SUNXI_CCU_BASE + 0x68, 0);
+ break;
+ case GENERIC_A64:
+ /* Turn off as many peripherals and clocks as we can. */
+ sunxi_turn_off_soc(SUNXI_SOC_A64);
+ /* Turn off the pin controller now. */
+ mmio_write_32(SUNXI_CCU_BASE + 0x68, 0);
+ break;
+ case REF_DESIGN_H5:
+ sunxi_turn_off_soc(SUNXI_SOC_H5);
+
+ /*
+ * Switch PL pins to power off the board:
+ * - PL5 (VCC_IO) -> high
+ * - PL8 (PWR-STB = CPU power supply) -> low
+ * - PL9 (PWR-DRAM) ->low
+ * - PL10 (power LED) -> low
+ * Note: Clearing PL8 will reset the board, so keep it up.
+ */
+ sunxi_set_gpio_out('L', 5, 1);
+ sunxi_set_gpio_out('L', 9, 0);
+ sunxi_set_gpio_out('L', 10, 0);
+
+ /* Turn off pin controller now. */
+ mmio_write_32(SUNXI_CCU_BASE + 0x68, 0);
+
+ break;
+ case AXP803_RSB:
+ /* (Re-)init RSB in case the rich OS has disabled it. */
+ sunxi_init_platform_r_twi(SUNXI_SOC_A64, true);
+ rsb_init();
+ axp_power_off();
+ break;
+ default:
+ break;
+ }
+
+}
+
+/* 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);
+
+ /* 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/sunxi_ccu.h b/plat/allwinner/sun50i_h6/include/sunxi_ccu.h
new file mode 100644
index 0000000..85fbb90
--- /dev/null
+++ b/plat/allwinner/sun50i_h6/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_h6/include/sunxi_cpucfg.h b/plat/allwinner/sun50i_h6/include/sunxi_cpucfg.h
new file mode 100644
index 0000000..5bfda5d
--- /dev/null
+++ b/plat/allwinner/sun50i_h6/include/sunxi_cpucfg.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017-2021, ARM Limited and Contributors. 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)
+
+#define SUNXI_AA64nAA32_REG SUNXI_CPUCFG_CLS_CTRL_REG0
+#define SUNXI_AA64nAA32_OFFSET 24
+
+#endif /* SUNXI_CPUCFG_H */
diff --git a/plat/allwinner/sun50i_h6/include/sunxi_mmap.h b/plat/allwinner/sun50i_h6/include/sunxi_mmap.h
new file mode 100644
index 0000000..58216d8
--- /dev/null
+++ b/plat/allwinner/sun50i_h6/include/sunxi_mmap.h
@@ -0,0 +1,63 @@
+/*
+ * 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 0x000f8000
+#define SUNXI_SRAM_A1_BASE 0x00020000
+#define SUNXI_SRAM_A1_SIZE 0x00008000
+#define SUNXI_SRAM_A2_BASE 0x00100000
+#define SUNXI_SRAM_A2_BL31_OFFSET 0x00004000
+#define SUNXI_SRAM_A2_SIZE 0x00018000
+#define SUNXI_SRAM_C_BASE 0x00028000
+#define SUNXI_SRAM_C_SIZE 0x0001e000
+#define SUNXI_DEV_BASE 0x01000000
+#define SUNXI_DEV_SIZE 0x09000000
+#define SUNXI_DRAM_BASE 0x40000000
+#define SUNXI_DRAM_VIRT_BASE 0x0a000000
+
+/* Memory-mapped devices */
+#define SUNXI_SYSCON_BASE 0x03000000
+#define SUNXI_CPUCFG_BASE 0x09010000
+#define SUNXI_SID_BASE 0x03006000
+#define SUNXI_DMA_BASE 0x03002000
+#define SUNXI_MSGBOX_BASE 0x03003000
+#define SUNXI_CCU_BASE 0x03001000
+#define SUNXI_PIO_BASE 0x0300b000
+#define SUNXI_SPC_BASE 0x03008000
+#define SUNXI_TIMER_BASE 0x03009000
+#define SUNXI_WDOG_BASE 0x030090a0
+#define SUNXI_THS_BASE 0x05070400
+#define SUNXI_UART0_BASE 0x05000000
+#define SUNXI_UART1_BASE 0x05000400
+#define SUNXI_UART2_BASE 0x05000800
+#define SUNXI_UART3_BASE 0x05000c00
+#define SUNXI_I2C0_BASE 0x05002000
+#define SUNXI_I2C1_BASE 0x05002400
+#define SUNXI_I2C2_BASE 0x05002800
+#define SUNXI_I2C3_BASE 0x05002c00
+#define SUNXI_SPI0_BASE 0x05010000
+#define SUNXI_SPI1_BASE 0x05011000
+#define SUNXI_SCU_BASE 0x03020000
+#define SUNXI_GICD_BASE 0x03021000
+#define SUNXI_GICC_BASE 0x03022000
+#define SUNXI_R_TIMER_BASE 0x07020000
+#define SUNXI_R_INTC_BASE 0x07021000
+#define SUNXI_R_WDOG_BASE 0x07020400
+#define SUNXI_R_PRCM_BASE 0x07010000
+#define SUNXI_R_TWD_BASE 0x07020800
+#define SUNXI_R_CPUCFG_BASE 0x07000400
+#define SUNXI_R_I2C_BASE 0x07081400
+#define SUNXI_R_RSB_BASE 0x07083000
+#define SUNXI_R_UART_BASE 0x07080000
+#define SUNXI_R_PIO_BASE 0x07022000
+
+#endif /* SUNXI_MMAP_H */
diff --git a/plat/allwinner/sun50i_h6/include/sunxi_spc.h b/plat/allwinner/sun50i_h6/include/sunxi_spc.h
new file mode 100644
index 0000000..0f5965b
--- /dev/null
+++ b/plat/allwinner/sun50i_h6/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_h6/platform.mk b/plat/allwinner/sun50i_h6/platform.mk
new file mode 100644
index 0000000..e13e8cb
--- /dev/null
+++ b/plat/allwinner/sun50i_h6/platform.mk
@@ -0,0 +1,14 @@
+#
+# Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+# The differences between the platform are covered by the include files.
+include plat/allwinner/common/allwinner-common.mk
+
+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_idle_states.c b/plat/allwinner/sun50i_h6/sunxi_idle_states.c
new file mode 100644
index 0000000..4339bcd
--- /dev/null
+++ b/plat/allwinner/sun50i_h6/sunxi_idle_states.c
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sunxi_private.h>
+
+const struct psci_cpu_idle_state sunxi_idle_states[] = {
+ {}
+};
diff --git a/plat/allwinner/sun50i_h6/sunxi_power.c b/plat/allwinner/sun50i_h6/sunxi_power.c
new file mode 100644
index 0000000..d298e6b
--- /dev/null
+++ b/plat/allwinner/sun50i_h6/sunxi_power.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2018, Icenowy Zheng <icenowy@aosc.io>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.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 AXP805_HW_ADDR 0x745
+#define AXP805_RT_ADDR 0x3a
+
+static enum pmic_type {
+ UNKNOWN,
+ AXP805,
+} pmic;
+
+int axp_read(uint8_t reg)
+{
+ return rsb_read(AXP805_RT_ADDR, reg);
+}
+
+int axp_write(uint8_t reg, uint8_t val)
+{
+ return rsb_write(AXP805_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(AXP805_HW_ADDR, AXP805_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 AXP805 on RSB\n");
+
+ ret = sunxi_init_platform_r_twi(socid, true);
+ if (ret)
+ return ret;
+
+ ret = rsb_init();
+ if (ret)
+ return ret;
+
+ /* Switch the AXP805 to master/single-PMIC mode. */
+ ret = axp_write(0xff, 0x0);
+ if (ret)
+ return ret;
+
+ pmic = AXP805;
+ 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 AXP805:
+ /* (Re-)init RSB in case the rich OS has disabled it. */
+ sunxi_init_platform_r_twi(SUNXI_SOC_H6, 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));
+}
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..dab663b
--- /dev/null
+++ b/plat/allwinner/sun50i_h616/include/sunxi_cpucfg.h
@@ -0,0 +1,35 @@
+/*
+ * 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)
+
+#define SUNXI_AA64nAA32_REG SUNXI_CPUCFG_CLS_CTRL_REG0
+#define SUNXI_AA64nAA32_OFFSET 24
+
+#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..de494a2
--- /dev/null
+++ b/plat/allwinner/sun50i_h616/platform.mk
@@ -0,0 +1,22 @@
+#
+# Copyright (c) 2017-2020, ARM Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+SUNXI_BL31_IN_DRAM := 1
+
+# 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 \
diff --git a/plat/allwinner/sun50i_h616/sunxi_idle_states.c b/plat/allwinner/sun50i_h616/sunxi_idle_states.c
new file mode 100644
index 0000000..4339bcd
--- /dev/null
+++ b/plat/allwinner/sun50i_h616/sunxi_idle_states.c
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sunxi_private.h>
+
+const struct psci_cpu_idle_state sunxi_idle_states[] = {
+ {}
+};
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));
+}
diff --git a/plat/allwinner/sun50i_r329/include/sunxi_ccu.h b/plat/allwinner/sun50i_r329/include/sunxi_ccu.h
new file mode 100644
index 0000000..0e6b543
--- /dev/null
+++ b/plat/allwinner/sun50i_r329/include/sunxi_ccu.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2021 Sipeed
+ *
+ * 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_r329/include/sunxi_cpucfg.h b/plat/allwinner/sun50i_r329/include/sunxi_cpucfg.h
new file mode 100644
index 0000000..9478f32
--- /dev/null
+++ b/plat/allwinner/sun50i_r329/include/sunxi_cpucfg.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2021 Sipeed
+ *
+ * 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_C0_CPUXCFG_BASE + 0x0010)
+#define SUNXI_CPUCFG_CLS_CTRL_REG1(c) (SUNXI_C0_CPUXCFG_BASE + 0x0014)
+#define SUNXI_CPUCFG_CACHE_CFG_REG (SUNXI_C0_CPUXCFG_BASE + 0x0024)
+#define SUNXI_CPUCFG_DBG_REG0 (SUNXI_C0_CPUXCFG_BASE + 0x00c0)
+
+#define SUNXI_CPUCFG_RST_CTRL_REG(c) (SUNXI_C0_CPUXCFG_BASE + 0x0000)
+#define SUNXI_CPUCFG_GEN_CTRL_REG0(c) (SUNXI_CPUCFG_BASE + 0x0000)
+#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_AA64nAA32_REG SUNXI_CPUCFG_GEN_CTRL_REG0
+#define SUNXI_AA64nAA32_OFFSET 4
+
+#endif /* SUNXI_CPUCFG_H */
diff --git a/plat/allwinner/sun50i_r329/include/sunxi_mmap.h b/plat/allwinner/sun50i_r329/include/sunxi_mmap.h
new file mode 100644
index 0000000..a4469b5
--- /dev/null
+++ b/plat/allwinner/sun50i_r329/include/sunxi_mmap.h
@@ -0,0 +1,55 @@
+/*
+ * 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
+/*
+ * In fact all SRAM from 0x100000 is SRAM A2. However as it's too big for
+ * firmware, and the user manual gives a tip on a 2*64K/27*64K partition,
+ * only use the first 2*64K for firmwares now, with the SPL using the first
+ * 64K and BL3-1 using the second one.
+ *
+ * Only the used 2*64K SRAM is defined here, to prevent a gaint translation
+ * table to be generated.
+ */
+#define SUNXI_SRAM_BASE 0x00100000
+#define SUNXI_SRAM_SIZE 0x00020000
+#define SUNXI_SRAM_A1_BASE 0x00100000
+#define SUNXI_SRAM_A1_SIZE 0x00010000
+#define SUNXI_SRAM_A2_BASE 0x00110000
+#define SUNXI_SRAM_A2_BL31_OFFSET 0x00000000
+#define SUNXI_SRAM_A2_SIZE 0x00010000
+#define SUNXI_DEV_BASE 0x01000000
+#define SUNXI_DEV_SIZE 0x09000000
+#define SUNXI_DRAM_BASE 0x40000000
+#define SUNXI_DRAM_VIRT_BASE 0x0a000000
+
+/* Memory-mapped devices */
+#define SUNXI_WDOG_BASE 0x020000a0
+#define SUNXI_R_WDOG_BASE SUNXI_WDOG_BASE
+#define SUNXI_PIO_BASE 0x02000400
+#define SUNXI_SPC_BASE 0x02000800
+#define SUNXI_CCU_BASE 0x02001000
+#define SUNXI_UART0_BASE 0x02500000
+#define SUNXI_SYSCON_BASE 0x03000000
+#define SUNXI_DMA_BASE 0x03002000
+#define SUNXI_SID_BASE 0x03006000
+#define SUNXI_GICD_BASE 0x03021000
+#define SUNXI_GICC_BASE 0x03022000
+#define SUNXI_SPI0_BASE 0x04025000
+#define SUNXI_R_CPUCFG_BASE 0x07000400
+#define SUNXI_R_PRCM_BASE 0x07010000
+#define SUNXI_R_PIO_BASE 0x07022000
+#define SUNXI_R_UART_BASE 0x07080000
+#define SUNXI_R_I2C_BASE 0x07081400
+#define SUNXI_CPUCFG_BASE 0x08100000
+#define SUNXI_C0_CPUXCFG_BASE 0x09010000
+
+#endif /* SUNXI_MMAP_H */
diff --git a/plat/allwinner/sun50i_r329/include/sunxi_spc.h b/plat/allwinner/sun50i_r329/include/sunxi_spc.h
new file mode 100644
index 0000000..2c87bca
--- /dev/null
+++ b/plat/allwinner/sun50i_r329/include/sunxi_spc.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2021 Sipeed
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SUNXI_SPC_H
+#define SUNXI_SPC_H
+
+/* Get by REing stock ATF and checking initialization loop boundary */
+#define SUNXI_SPC_NUM_PORTS 11
+
+#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_r329/platform.mk b/plat/allwinner/sun50i_r329/platform.mk
new file mode 100644
index 0000000..05d7cde
--- /dev/null
+++ b/plat/allwinner/sun50i_r329/platform.mk
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2021 Sipeed
+#
+# 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 "R329 does not support SCPI PSCI ops")
+endif
+
+# Put NOBITS memory in the first 64K of SRAM A2, overwriting U-Boot's SPL.
+SEPARATE_NOBITS_REGION := 1
diff --git a/plat/allwinner/sun50i_r329/sunxi_idle_states.c b/plat/allwinner/sun50i_r329/sunxi_idle_states.c
new file mode 100644
index 0000000..4339bcd
--- /dev/null
+++ b/plat/allwinner/sun50i_r329/sunxi_idle_states.c
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sunxi_private.h>
+
+const struct psci_cpu_idle_state sunxi_idle_states[] = {
+ {}
+};
diff --git a/plat/allwinner/sun50i_r329/sunxi_power.c b/plat/allwinner/sun50i_r329/sunxi_power.c
new file mode 100644
index 0000000..96a24d5
--- /dev/null
+++ b/plat/allwinner/sun50i_r329/sunxi_power.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2021 Sipeed
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <platform_def.h>
+
+#include <sunxi_mmap.h>
+#include <sunxi_cpucfg.h>
+#include <sunxi_private.h>
+
+int sunxi_pmic_setup(uint16_t socid, const void *fdt)
+{
+ /* Currently known hardware has no PMIC */
+
+ return 0;
+}
+
+void sunxi_power_down(void)
+{
+}
+
+void sunxi_cpu_power_off_self(void)
+{
+ /* TODO: It's still unknown whether CPUIDLE exists on R329 */
+}