diff --git a/arch/arm/mach-zynqmp/Kconfig b/arch/arm/mach-zynqmp/Kconfig
index f1301f6661a1..8578c6910701 100644
--- a/arch/arm/mach-zynqmp/Kconfig
+++ b/arch/arm/mach-zynqmp/Kconfig
@@ -1,5 +1,8 @@
 if ARCH_ZYNQMP
 
+config SPL_LDSCRIPT
+	default "arch/arm/mach-zynqmp/u-boot-spl.lds"
+
 config SPL_FS_FAT
 	default y
 
diff --git a/arch/arm/mach-zynqmp/clk.c b/arch/arm/mach-zynqmp/clk.c
index 1e6e726e8792..ad2636169a1a 100644
--- a/arch/arm/mach-zynqmp/clk.c
+++ b/arch/arm/mach-zynqmp/clk.c
@@ -14,7 +14,7 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-unsigned long zynqmp_get_system_timer_freq(void)
+unsigned long __splinit zynqmp_get_system_timer_freq(void)
 {
 	u32 ver = zynqmp_get_silicon_version();
 
@@ -35,7 +35,7 @@ unsigned long zynqmp_get_system_timer_freq(void)
  * clock framework. The framework must not be used before this function had been
  * called.
  */
-int set_cpu_clk_info(void)
+int __splinit set_cpu_clk_info(void)
 {
 	gd->cpu_clk = get_tbclk();
 
diff --git a/arch/arm/mach-zynqmp/cpu.c b/arch/arm/mach-zynqmp/cpu.c
index 29743cae5aab..fa2d6e730cba 100644
--- a/arch/arm/mach-zynqmp/cpu.c
+++ b/arch/arm/mach-zynqmp/cpu.c
@@ -105,7 +105,7 @@ void mem_map_fill(void)
 
 struct mm_region *mem_map = zynqmp_mem_map;
 
-u64 get_page_table_size(void)
+u64 __splinit get_page_table_size(void)
 {
 	return 0x14000;
 }
@@ -130,7 +130,7 @@ int arm_reserve_mmu(void)
 }
 #endif
 
-static unsigned int zynqmp_get_silicon_version_secure(void)
+static unsigned int __splinit zynqmp_get_silicon_version_secure(void)
 {
 	u32 ver;
 
@@ -141,7 +141,7 @@ static unsigned int zynqmp_get_silicon_version_secure(void)
 	return ver;
 }
 
-unsigned int zynqmp_get_silicon_version(void)
+unsigned int __splinit zynqmp_get_silicon_version(void)
 {
 	if (current_el() == 3)
 		return zynqmp_get_silicon_version_secure();
diff --git a/arch/arm/mach-zynqmp/handoff.c b/arch/arm/mach-zynqmp/handoff.c
index 7d7ab9da6ec2..4c5a81fc4cad 100644
--- a/arch/arm/mach-zynqmp/handoff.c
+++ b/arch/arm/mach-zynqmp/handoff.c
@@ -72,7 +72,7 @@ struct bl31_params *bl2_plat_get_bl31_params(uintptr_t bl32_entry,
 {
 	struct xfsbl_atf_handoff_params *atfhandoffparams;
 
-	atfhandoffparams = (void *)CONFIG_SPL_TEXT_BASE;
+	atfhandoffparams = (void *)0xfffe0000;
 	atfhandoffparams->magic[0] = 'X';
 	atfhandoffparams->magic[1] = 'L';
 	atfhandoffparams->magic[2] = 'N';
@@ -86,7 +86,7 @@ struct bl31_params *bl2_plat_get_bl31_params(uintptr_t bl32_entry,
 		atfhandoffparams->num_entries++;
 	}
 
-	writel(CONFIG_SPL_TEXT_BASE, &pmu_base->gen_storage6);
+	writel(0xfffe0000, &pmu_base->gen_storage6);
 
 	return NULL;
 }
diff --git a/arch/arm/mach-zynqmp/include/mach/psu_init_gpl.h b/arch/arm/mach-zynqmp/include/mach/psu_init_gpl.h
index e37acda2f89e..9cccd2139700 100644
--- a/arch/arm/mach-zynqmp/include/mach/psu_init_gpl.h
+++ b/arch/arm/mach-zynqmp/include/mach/psu_init_gpl.h
@@ -5,22 +5,23 @@
 
 #include <asm/io.h>
 #include <common.h>
+#include <asm/arch/sys_proto.h>
 
-int mask_pollonvalue(unsigned long add, u32 mask, u32 value);
+int __splinit mask_pollonvalue(unsigned long add, u32 mask, u32 value);
 
-int mask_poll(u32 add, u32 mask);
+int __splinit mask_poll(u32 add, u32 mask);
 
-u32 mask_read(u32 add, u32 mask);
+u32 __splinit mask_read(u32 add, u32 mask);
 
-void mask_delay(u32 delay);
+void __splinit mask_delay(u32 delay);
 
-void psu_mask_write(unsigned long offset, unsigned long mask,
-		    unsigned long val);
+void __splinit psu_mask_write(unsigned long offset, unsigned long mask,
+			      unsigned long val);
 
-void prog_reg(unsigned long addr, unsigned long mask,
-	      unsigned long shift, unsigned long value);
+void __splinit prog_reg(unsigned long addr, unsigned long mask,
+			unsigned long shift, unsigned long value);
 
-int psu_init(void);
-unsigned long psu_post_config_data(void);
+int __splinit psu_init(void);
+unsigned long __splinit psu_post_config_data(void);
 
 #endif /* _PSU_INIT_GPL_H_ */
diff --git a/arch/arm/mach-zynqmp/include/mach/sys_proto.h b/arch/arm/mach-zynqmp/include/mach/sys_proto.h
index 1c12eac715e5..c1ab685c3ce4 100644
--- a/arch/arm/mach-zynqmp/include/mach/sys_proto.h
+++ b/arch/arm/mach-zynqmp/include/mach/sys_proto.h
@@ -7,6 +7,12 @@
 #ifndef _ASM_ARCH_SYS_PROTO_H
 #define _ASM_ARCH_SYS_PROTO_H
 
+#if CONFIG_SPL_BUILD
+#define __splinit __section(.splinit)
+#else
+#define __splinit
+#endif
+
 #define ZYNQMP_CSU_SILICON_VER_MASK	0xF
 #define KEY_PTR_LEN	32
 #define IV_SIZE		12
diff --git a/arch/arm/mach-zynqmp/spl.c b/arch/arm/mach-zynqmp/spl.c
index 88386b23e5da..f57835143041 100644
--- a/arch/arm/mach-zynqmp/spl.c
+++ b/arch/arm/mach-zynqmp/spl.c
@@ -18,13 +18,13 @@
 #include <asm/arch/psu_init_gpl.h>
 #include <asm/arch/sys_proto.h>
 
-void board_init_f(ulong dummy)
+void __splinit board_init_f(ulong dummy)
 {
 	board_early_init_f();
 	board_early_init_r();
 }
 
-static void ps_mode_reset(ulong mode)
+static void __splinit ps_mode_reset(ulong mode)
 {
 	writel(mode << ZYNQMP_CRL_APB_BOOT_PIN_CTRL_OUT_EN_SHIFT,
 	       &crlapb_base->boot_pin_ctrl);
@@ -43,7 +43,7 @@ static void ps_mode_reset(ulong mode)
 #endif
 
 #ifdef CONFIG_SPL_BOARD_INIT
-void spl_board_init(void)
+void __splinit spl_board_init(void)
 {
 	preloader_console_init();
 	ps_mode_reset(MODE_RESET);
@@ -52,7 +52,7 @@ void spl_board_init(void)
 }
 #endif
 
-void board_boot_order(u32 *spl_boot_list)
+void __splinit board_boot_order(u32 *spl_boot_list)
 {
 	spl_boot_list[0] = spl_boot_device();
 
@@ -64,7 +64,7 @@ void board_boot_order(u32 *spl_boot_list)
 	spl_boot_list[2] = BOOT_DEVICE_RAM;
 }
 
-u32 spl_boot_device(void)
+u32 __splinit spl_boot_device(void)
 {
 	u32 reg = 0;
 	u8 bootmode;
@@ -114,7 +114,7 @@ u32 spl_boot_device(void)
 }
 
 #ifdef CONFIG_SPL_OS_BOOT
-int spl_start_uboot(void)
+int __splinit spl_start_uboot(void)
 {
 	return 0;
 }
diff --git a/arch/arm/mach-zynqmp/u-boot-spl.lds b/arch/arm/mach-zynqmp/u-boot-spl.lds
new file mode 100644
index 000000000000..5b71d5a35142
--- /dev/null
+++ b/arch/arm/mach-zynqmp/u-boot-spl.lds
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2013
+ * David Feng <fenghua@phytium.com.cn>
+ *
+ * (C) Copyright 2002
+ * Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
+ *
+ * (C) Copyright 2010
+ * Texas Instruments, <www.ti.com>
+ *	Aneesh V <aneesh@ti.com>
+ */
+
+MEMORY { .sram : ORIGIN = IMAGE_TEXT_BASE,
+		LENGTH = IMAGE_MAX_SIZE }
+MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR,
+		LENGTH = CONFIG_SPL_BSS_MAX_SIZE }
+
+OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64")
+OUTPUT_ARCH(aarch64)
+ENTRY(_start)
+SECTIONS
+{
+	.text : {
+		. = ALIGN(8);
+		*(.__image_copy_start)
+		CPUDIR/start.o (.text*)
+		*(.splinit*)
+		. = ALIGN(0x18000);
+		*(.text*)
+	} >.sram
+
+	.rodata : {
+		. = ALIGN(8);
+		*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))
+	} >.sram
+
+	.data : {
+		. = ALIGN(8);
+		*(.data*)
+	} >.sram
+
+#ifdef CONFIG_SPL_RECOVER_DATA_SECTION
+	.data_save : {
+		*(.__data_save_start)
+		. = SIZEOF(.data);
+		*(.__data_save_end)
+	} >.sram
+#endif
+
+	.u_boot_list : {
+		. = ALIGN(8);
+		KEEP(*(SORT(.u_boot_list*)));
+	} >.sram
+
+	.image_copy_end : {
+		. = ALIGN(8);
+		*(.__image_copy_end)
+	} >.sram
+
+	.end : {
+		. = ALIGN(8);
+		*(.__end)
+	} >.sram
+
+	_image_binary_end = .;
+
+	.bss_start (NOLOAD) : {
+		. = ALIGN(8);
+		KEEP(*(.__bss_start));
+	} >.sdram
+
+	.bss (NOLOAD) : {
+		*(.bss*)
+		 . = ALIGN(8);
+	} >.sdram
+
+	.bss_end (NOLOAD) : {
+		KEEP(*(.__bss_end));
+	} >.sdram
+
+	/DISCARD/ : { *(.dynsym) }
+	/DISCARD/ : { *(.dynstr*) }
+	/DISCARD/ : { *(.dynamic*) }
+	/DISCARD/ : { *(.plt*) }
+	/DISCARD/ : { *(.interp*) }
+	/DISCARD/ : { *(.gnu*) }
+}
diff --git a/board/xilinx/zynqmp/pm_cfg_obj.h b/board/xilinx/zynqmp/pm_cfg_obj.h
index 86e785490ced..232dcd0ce116 100644
--- a/board/xilinx/zynqmp/pm_cfg_obj.h
+++ b/board/xilinx/zynqmp/pm_cfg_obj.h
@@ -5,5 +5,5 @@
  * Declaration of PMU config object binary blob linked in at build time.
  */
 
-extern const u32 zynqmp_pm_cfg_obj[];
-extern const int zynqmp_pm_cfg_obj_size;
+extern const u32 __splinit zynqmp_pm_cfg_obj[];
+extern const int __splinit zynqmp_pm_cfg_obj_size;
diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c
index d05f0b2e1202..e56cfa2cc520 100644
--- a/board/xilinx/zynqmp/zynqmp.c
+++ b/board/xilinx/zynqmp/zynqmp.c
@@ -185,7 +185,7 @@ static const struct {
 	},
 };
 
-static char *zynqmp_get_silicon_idcode_name(void)
+static char __splinit *zynqmp_get_silicon_idcode_name(void)
 {
 	u32 i;
 	u32 idcode, idcode2;
@@ -278,7 +278,7 @@ static char *zynqmp_get_silicon_idcode_name(void)
 }
 #endif
 
-int board_early_init_f(void)
+int __splinit board_early_init_f(void)
 {
 #if defined(CONFIG_ZYNQMP_PSU_INIT_ENABLED)
 	int ret;
@@ -300,7 +300,7 @@ int board_early_init_f(void)
 	return 0;
 }
 
-static int multi_boot(void)
+static int __splinit multi_boot(void)
 {
 	u32 multiboot;
 
@@ -314,7 +314,7 @@ static int multi_boot(void)
 #define PS_SYSMON_ANALOG_BUS_VAL	0x3210
 #define PS_SYSMON_ANALOG_BUS_REG	0xFFA50914
 
-int board_init(void)
+int __splinit board_init(void)
 {
 #if defined(CONFIG_ZYNQMP_FIRMWARE)
 	struct udevice *dev;
@@ -353,7 +353,7 @@ int board_init(void)
 	return 0;
 }
 
-int board_early_init_r(void)
+int __splinit board_early_init_r(void)
 {
 	u32 val;
 
@@ -396,7 +396,7 @@ unsigned long do_go_exec(ulong (*entry)(int, char * const []), int argc,
 }
 
 #if !defined(CONFIG_SYS_SDRAM_BASE) && !defined(CONFIG_SYS_SDRAM_SIZE)
-int dram_init_banksize(void)
+int __splinit dram_init_banksize(void)
 {
 	int ret;
 
@@ -409,7 +409,7 @@ int dram_init_banksize(void)
 	return 0;
 }
 
-int dram_init(void)
+int __splinit dram_init(void)
 {
 	if (fdtdec_setup_mem_size_base() != 0)
 		return -EINVAL;
@@ -417,7 +417,7 @@ int dram_init(void)
 	return 0;
 }
 #else
-int dram_init_banksize(void)
+int __splinit dram_init_banksize(void)
 {
 	gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE;
 	gd->bd->bi_dram[0].size = get_effective_memsize();
@@ -427,7 +427,7 @@ int dram_init_banksize(void)
 	return 0;
 }
 
-int dram_init(void)
+int __splinit dram_init(void)
 {
 	gd->ram_size = get_ram_size((void *)CONFIG_SYS_SDRAM_BASE,
 				    CONFIG_SYS_SDRAM_SIZE);
@@ -440,7 +440,7 @@ void reset_cpu(void)
 {
 }
 
-static u8 __maybe_unused zynqmp_get_bootmode(void)
+static u8 __maybe_unused __splinit zynqmp_get_bootmode(void)
 {
 	u8 bootmode;
 	u32 reg = 0;
@@ -473,7 +473,7 @@ static const struct {
 	{}
 };
 
-static int reset_reason(void)
+static int __splinit reset_reason(void)
 {
 	u32 reg;
 	int i, ret;
diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c
index d4dc856bafa4..08bcdf5bb024 100644
--- a/drivers/firmware/firmware-zynqmp.c
+++ b/drivers/firmware/firmware-zynqmp.c
@@ -55,7 +55,7 @@ static int ipi_req(const u32 *req, size_t req_len, u32 *res, size_t res_maxlen)
 	return ret;
 }
 
-unsigned int zynqmp_firmware_version(void)
+unsigned int __splinit zynqmp_firmware_version(void)
 {
 	int ret;
 	u32 ret_payload[PAYLOAD_ARG_CNT];
@@ -88,7 +88,7 @@ unsigned int zynqmp_firmware_version(void)
  * @cfg_obj: Pointer to the configuration object
  * @size:    Size of @cfg_obj in bytes
  */
-void zynqmp_pmufw_load_config_object(const void *cfg_obj, size_t size)
+void __splinit zynqmp_pmufw_load_config_object(const void *cfg_obj, size_t size)
 {
 	int err;
 	u32 ret_payload[PAYLOAD_ARG_CNT];
