diff --git a/arch/arm/cpu/armv8/Makefile b/arch/arm/cpu/armv8/Makefile
index 28ba7862072a2cc165c87bea0877e9422a76c520..e780afcde25ae918f9767952d96e500c092e9217 100644
--- a/arch/arm/cpu/armv8/Makefile
+++ b/arch/arm/cpu/armv8/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_S32V234) += s32v234/
 obj-$(CONFIG_ARCH_ZYNQMP) += zynqmp/
 obj-$(CONFIG_TARGET_HIKEY) += hisilicon/
 obj-$(CONFIG_ARMV8_PSCI) += psci.o
+obj-$(CONFIG_ARCH_SUNXI) += lowlevel_init.o
diff --git a/arch/arm/cpu/armv8/lowlevel_init.S b/arch/arm/cpu/armv8/lowlevel_init.S
new file mode 100644
index 0000000000000000000000000000000000000000..189e35f0ea35c7c0b520cb98f3cc11833a972e49
--- /dev/null
+++ b/arch/arm/cpu/armv8/lowlevel_init.S
@@ -0,0 +1,44 @@
+/*
+ * A lowlevel_init function that sets up the stack to call a C function to
+ * perform further init.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <asm-offsets.h>
+#include <config.h>
+#include <linux/linkage.h>
+
+ENTRY(lowlevel_init)
+	/*
+	 * Setup a temporary stack. Global data is not available yet.
+	 */
+#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
+	ldr	w0, =CONFIG_SPL_STACK
+#else
+	ldr	w0, =CONFIG_SYS_INIT_SP_ADDR
+#endif
+	bic	sp, x0, #0xf	/* 16-byte alignment for ABI compliance */
+
+	/*
+	 * Save the old LR(passed in x29) and the current LR to stack
+	 */
+	stp	x29, x30, [sp, #-16]!
+
+	/*
+	 * Call the very early init function. This should do only the
+	 * absolute bare minimum to get started. It should not:
+	 *
+	 * - set up DRAM
+	 * - use global_data
+	 * - clear BSS
+	 * - try to start a console
+	 *
+	 * For boards with SPL this should be empty since SPL can do all of
+	 * this init in the SPL board_init_f() function which is called
+	 * immediately after this.
+	 */
+	bl	s_init
+	ldp	x29, x30, [sp]
+	ret
+ENDPROC(lowlevel_init)