diff --git a/board/xilinx/zynqmp/Kconfig b/board/xilinx/zynqmp/Kconfig
index 7d1f7398c3e9..4201e38571e7 100644
--- a/board/xilinx/zynqmp/Kconfig
+++ b/board/xilinx/zynqmp/Kconfig
@@ -15,4 +15,30 @@ config CMD_ZYNQMP
 	  and authentication feature enabled while generating
 	  BOOT.BIN using Xilinx bootgen tool.
 
+config ZYNQMP_LOAD_PM_CFG_OBJ_FILE
+	string "PMU firmware configuration object to load at runtime"
+	help
+	  Path to a PMU firmware configuration object to be built into
+	  U-Boot and loaded at runtime by SPL into the PMU firmware.
+
+	  The ZynqMP Power Management Unit (PMU) needs a configuration
+	  object for most SoC peripherals to work. It can be either
+	  hard-coded in the PMUFW or passed at runtime.
+
+	  If the configuration object is not hard-coded in the PMU
+	  firmware, U-Boot SPL can load it at runtime. To enable this
+	  feature set here the file name (absolute path or relative to
+	  board/xilinx/zynqmp/). It will be loaded into the PMU firmware by
+	  U-Boot SPL during board initialization.
+
+	  Note: if your pm_cfg_obj.c is generated by the Xilinx tools,
+	  you must remove any linking directives from the
+	  XPm_ConfigObject declaration! E.g.:
+
+	    -const u32 XPm_ConfigObject[] __attribute__((used, section(".sys_cfg_data"))) = {
+	    +const u32 XPm_ConfigObject[] = {
+
+	  Leave this option empty if your PMU firmware has a hard-coded
+	  configuration object or you are loading it by any other means.
+
 endif
diff --git a/board/xilinx/zynqmp/Makefile b/board/xilinx/zynqmp/Makefile
index 80f8ca7e1e4b..0d8f52ecd631 100644
--- a/board/xilinx/zynqmp/Makefile
+++ b/board/xilinx/zynqmp/Makefile
@@ -33,6 +33,11 @@ ifneq ($(call ifdef_any_of, CONFIG_ZYNQMP_PSU_INIT_ENABLED CONFIG_SPL_BUILD),)
 obj-y += $(init-objs)
 endif
 
+ifneq ($(CONFIG_ZYNQMP_LOAD_PM_CFG_OBJ_FILE),"")
+CFLAGS_zynqmp.o += -DZYNQMP_LOAD_PM_CFG_OBJ -I$(srctree)/$(src)
+obj-$(CONFIG_SPL_BUILD) += pmu_ipc.o
+endif
+
 obj-$(CONFIG_MMC_SDHCI_ZYNQ) += tap_delays.o
 
 ifndef CONFIG_SPL_BUILD
diff --git a/board/xilinx/zynqmp/pmu_ipc.c b/board/xilinx/zynqmp/pmu_ipc.c
new file mode 100644
index 000000000000..6306d33d6f17
--- /dev/null
+++ b/board/xilinx/zynqmp/pmu_ipc.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Inter-Processor Communication with the Platform Management Unit (PMU)
+ * firmware.
+ *
+ * (C) Copyright 2019 Luca Ceresoli
+ * Luca Ceresoli <luca@lucaceresoli.net>
+ */
+
+#include "pmu_ipc.h"
+#include <common.h>
+#include <asm/io.h>
+
+/* IPI bitmasks, register base and register offsets */
+#define IPI_BIT_MASK_APU      0x00001
+#define IPI_BIT_MASK_PMU0     0x10000
+#define IPI_REG_BASE_APU      0xFF300000
+#define IPI_REG_BASE_PMU0     0xFF330000
+#define IPI_REG_OFFSET_TRIG   0x00
+#define IPI_REG_OFFSET_OBR    0x04
+
+/* IPI mailbox buffer offsets */
+#define IPI_BUF_BASE_APU               0xFF990400
+#define IPI_BUF_OFFSET_TARGET_PMU      0x1C0
+#define IPI_BUF_OFFSET_REQ             0x00
+#define IPI_BUF_OFFSET_RESP            0x20
+
+/* Xilinx FSBL sets 8, ATF sets 6. Which one is correct? */
+#define PMUFW_PAYLOAD_ARG_CNT          6
+
+/* PMUFW commands */
+#define PMUFW_CMD_SET_CONFIGURATION    2
+
+static void pmu_ipc_send_request(const u32 *req, size_t req_len)
+{
+	u32 *mbx = IPI_BUF_BASE_APU +
+		   IPI_BUF_OFFSET_TARGET_PMU +
+		   IPI_BUF_OFFSET_REQ;
+	size_t i;
+
+	for (i = 0; i < req_len; i++)
+		writel(req[i], &mbx[i]);
+}
+
+static void pmu_ipc_read_response(unsigned int *value, size_t count)
+{
+	u32 *mbx = IPI_BUF_BASE_APU +
+		   IPI_BUF_OFFSET_TARGET_PMU +
+		   IPI_BUF_OFFSET_RESP;
+	size_t i;
+
+	for (i = 0; i < count; i++)
+		value[i] = readl(&mbx[i]);
+}
+
+/**
+ * Send request to PMU and get the response.
+ *
+ * @req        Request buffer. Byte 0 is the API ID, other bytes are optional
+ *             parameters.
+ * @req_len    Request length in number of 32-bit words.
+ * @res        Response buffer. Byte 0 is the error code, other bytes are
+ *             optional parameters. Optional, if @res_maxlen==0 the parameters
+ *             will not be read.
+ * @res_maxlen Space allocated for the response in number of 32-bit words.
+ *
+ * @return Error code returned by the PMU (i.e. the first word of the response)
+ */
+static int pmu_ipc_request(const u32 *req, size_t req_len,
+			   u32 *res, size_t res_maxlen)
+{
+	u32 status;
+
+	if (req_len > PMUFW_PAYLOAD_ARG_CNT ||
+	    res_maxlen > PMUFW_PAYLOAD_ARG_CNT)
+		return -EINVAL;
+
+	pmu_ipc_send_request(req, req_len);
+
+	/* Raise Inter-Processor Interrupt to PMU and wait for response */
+	writel(IPI_BIT_MASK_PMU0, IPI_REG_BASE_APU + IPI_REG_OFFSET_TRIG);
+	do {
+		status = readl(IPI_REG_BASE_APU + IPI_REG_OFFSET_OBR);
+	} while (status & IPI_BIT_MASK_PMU0);
+
+	pmu_ipc_read_response(res, res_maxlen);
+
+	return 0;
+}
+
+/**
+ * Send a configuration object to the PMU firmware.
+ *
+ * @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)
+{
+	const u32 *ocm = (u32 *)CONFIG_SPL_TEXT_BASE;
+	const u32 request[] = {
+		PMUFW_CMD_SET_CONFIGURATION,
+		CONFIG_SPL_TEXT_BASE
+	};
+	u32 response;
+	int err;
+
+	printf("Loading PMUFW cfg obj (%ld bytes)\n", size);
+
+	memcpy(ocm, cfg_obj, size);
+
+	err = pmu_ipc_request(request,  ARRAY_SIZE(request), &response, 1);
+	if (err)
+		panic("Cannot load PMUFW configuration object (%d)\n", err);
+	if (response != 0)
+		panic("PMUFW returned 0x%08x status!\n", response);
+}
diff --git a/board/xilinx/zynqmp/pmu_ipc.h b/board/xilinx/zynqmp/pmu_ipc.h
new file mode 100644
index 000000000000..37bb72c1b20a
--- /dev/null
+++ b/board/xilinx/zynqmp/pmu_ipc.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2019 Luca Ceresoli
+ * Luca Ceresoli <luca@lucaceresoli.net>
+ */
+
+#ifndef __ZYNQMP_PMU_IPC_H__
+#define __ZYNQMP_PMU_IPC_H__
+
+#include <linux/types.h>
+
+void zynqmp_pmufw_load_config_object(const void *cfg_obj, size_t size);
+
+#endif /* __ZYNQMP_PMU_IPC_H__ */
diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c
index 5e1d2116bc32..1d5e25961863 100644
--- a/board/xilinx/zynqmp/zynqmp.c
+++ b/board/xilinx/zynqmp/zynqmp.c
@@ -4,6 +4,8 @@
  * Michal Simek <michal.simek@xilinx.com>
  */
 
+#include "pmu_ipc.h"
+
 #include <common.h>
 #include <sata.h>
 #include <ahci.h>
@@ -302,6 +304,10 @@ static char *zynqmp_get_silicon_idcode_name(void)
 }
 #endif
 
+#ifdef ZYNQMP_LOAD_PM_CFG_OBJ
+#include CONFIG_ZYNQMP_LOAD_PM_CFG_OBJ_FILE
+#endif
+
 int board_early_init_f(void)
 {
 	int ret = 0;
@@ -332,6 +338,11 @@ int board_early_init_f(void)
 
 int board_init(void)
 {
+#if defined(CONFIG_SPL_BUILD) && defined(ZYNQMP_LOAD_PM_CFG_OBJ)
+	zynqmp_pmufw_load_config_object(XPm_ConfigObject,
+					sizeof(XPm_ConfigObject));
+#endif
+
 	printf("EL Level:\tEL%d\n", current_el());
 
 #if defined(CONFIG_FPGA) && defined(CONFIG_FPGA_ZYNQMPPL) && \
