On 15. 05. 19 10:56, Luca Ceresoli wrote: > Optionally allow U-Boot to load a configuration object into the Power > Management Unit (PMU) firmware on Xilinx ZynqMP. > > The configuration object is required by the PMU FW to enable most SoC > peripherals. So far the only way to boot using U-Boot SPL was to hard-code > the configuration object in the PMU firmware. Allow a different boot > process, where the PMU FW is equal for any ZynqMP chip and its > configuration is passed at runtime by U-Boot SPL. > > All the code for Inter-processor communication with the PMU is isolated in > a new file (pmu_ipc.c). The code is inspired by the same feature as > implemented in the Xilinx First Stage Bootloader (FSBL) and Arm Trusted > Firmware: > > * > https://github.com/Xilinx/embeddedsw/blob/fb647e6b4c00f5154eba52a88b948195b6f1dc2b/lib/sw_apps/zynqmp_fsbl/src/xfsbl_misc_drivers.c#L295 > * > https://github.com/ARM-software/arm-trusted-firmware/blob/c48d02bade88b07fa7f43aa44e5217f68e5d047f/plat/xilinx/zynqmp/pm_service/pm_api_sys.c#L357 > > SPL logs on the console before loading the configuration object: > > U-Boot SPL 2019.07-rc1-00511-gaec224515c87 (May 15 2019 - 08:43:41 +0200) > Loading PMUFW cfg obj (2008 bytes) > EL Level: EL3 > ... > > Signed-off-by: Luca Ceresoli <l...@lucaceresoli.net> > > --- > > Changes v3 -> v4: > - fix pointer-to-integer typecast warning > - fix integer-to-pointer typecast warning with proper typecasting and > add parentheses to avoid ambiguity > - fix kerneldoc syntax (Michal) > - remove empty line in Kconfig (Michal) > - remove #ifdefs around function declaration (Michal) > - remove unneeded file copy (Michal) > - move externs to a new .h file (Michal) > - avoid passing the extra -DZYNQMP_LOAD_PM_CFG_OBJ (using sizeof(), it > looks a bit of a hack but seems to be working) > - rename CONFIG_ZYNQMP_LOAD_PM_CFG_OBJ_FILE to > CONFIG_ZYNQMP_SPL_PM_CFG_OBJ_FILE and clarify in menuconfig that it's > SPL-related > > Changes RFC v2 -> v3: > - don't compile pm_cfg_obj.c from source, load it from a binary file > (suggested by Michal) > - move IPC code to arch/arm/mach-zynqmp/ and sys_proto.h (Michal) > > Changes RFC v1 -> RFC v2: > - Load the cfg_obj in SPL, not U-Boot proper: this required a complete > reimplementation since we cannot rely on ATF now > - Update and refine the Kconfig option help > --- > arch/arm/mach-zynqmp/Kconfig | 18 +++ > arch/arm/mach-zynqmp/Makefile | 4 + > arch/arm/mach-zynqmp/include/mach/sys_proto.h | 2 + > arch/arm/mach-zynqmp/pmu_ipc.c | 112 ++++++++++++++++++ > board/xilinx/zynqmp/Makefile | 5 + > board/xilinx/zynqmp/pm_cfg_obj.S | 17 +++ > board/xilinx/zynqmp/pm_cfg_obj.h | 9 ++ > board/xilinx/zynqmp/zynqmp.c | 9 ++ > 8 files changed, 176 insertions(+) > create mode 100644 arch/arm/mach-zynqmp/pmu_ipc.c > create mode 100644 board/xilinx/zynqmp/pm_cfg_obj.S > create mode 100644 board/xilinx/zynqmp/pm_cfg_obj.h > > diff --git a/arch/arm/mach-zynqmp/Kconfig b/arch/arm/mach-zynqmp/Kconfig > index 9bb5a5c20201..6cf17eb94e11 100644 > --- a/arch/arm/mach-zynqmp/Kconfig > +++ b/arch/arm/mach-zynqmp/Kconfig > @@ -65,6 +65,24 @@ config PMUFW_INIT_FILE > Include external PMUFW (Platform Management Unit FirmWare) to > a Xilinx bootable image (boot.bin). > > +config ZYNQMP_SPL_PM_CFG_OBJ_FILE > + string "PMU firmware configuration object to load at runtime by SPL" > + depends on SPL > + help > + Path to a binary PMU firmware configuration object to be linked > + into U-Boot SPL and loaded at runtime into the PMU firmware. > + > + The ZynqMP Power Management Unit (PMU) needs a configuration > + object for most SoC peripherals to work. To have it loaded by > + U-Boot SPL set here the file name (absolute path or relative to > + the top source tree) of your configuration, which must be a > + binary blob. It will be linked in the SPL binary and loaded > + into the PMU firmware by U-Boot SPL during board > + initialization. > + > + Leave this option empty if your PMU firmware has a hard-coded > + configuration object or you are loading it by any other means. > + > config ZYNQMP_USB > bool "Configure ZynqMP USB" > > diff --git a/arch/arm/mach-zynqmp/Makefile b/arch/arm/mach-zynqmp/Makefile > index 8a3b0747244a..f3765e45b1b9 100644 > --- a/arch/arm/mach-zynqmp/Makefile > +++ b/arch/arm/mach-zynqmp/Makefile > @@ -8,3 +8,7 @@ obj-y += cpu.o > obj-$(CONFIG_MP) += mp.o > obj-$(CONFIG_SPL_BUILD) += spl.o handoff.o > obj-$(CONFIG_ZYNQMP_PSU_INIT_ENABLED) += psu_spl_init.o > + > +ifneq ($(CONFIG_ZYNQMP_SPL_PM_CFG_OBJ_FILE),"") > +obj-$(CONFIG_SPL_BUILD) += pmu_ipc.o > +endif > diff --git a/arch/arm/mach-zynqmp/include/mach/sys_proto.h > b/arch/arm/mach-zynqmp/include/mach/sys_proto.h > index 385c8825f2f6..915badc6fbee 100644 > --- a/arch/arm/mach-zynqmp/include/mach/sys_proto.h > +++ b/arch/arm/mach-zynqmp/include/mach/sys_proto.h > @@ -72,4 +72,6 @@ int chip_id(unsigned char id); > void tcm_init(u8 mode); > #endif > > +void zynqmp_pmufw_load_config_object(const void *cfg_obj, size_t size); > + > #endif /* _ASM_ARCH_SYS_PROTO_H */ > diff --git a/arch/arm/mach-zynqmp/pmu_ipc.c b/arch/arm/mach-zynqmp/pmu_ipc.c > new file mode 100644 > index 000000000000..d8858ea3ff99 > --- /dev/null > +++ b/arch/arm/mach-zynqmp/pmu_ipc.c > @@ -0,0 +1,112 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Inter-Processor Communication with the Platform Management Unit (PMU) > + * firmware. > + * > + * (C) Copyright 2019 Luca Ceresoli > + * Luca Ceresoli <l...@lucaceresoli.net> > + */ > + > +#include <common.h> > +#include <asm/io.h> > +#include <asm/arch/sys_proto.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 > + > +#define PMUFW_PAYLOAD_ARG_CNT 8 > + > +/* PMUFW commands */ > +#define PMUFW_CMD_SET_CONFIGURATION 2 > + > +static void pmu_ipc_send_request(const u32 *req, size_t req_len) > +{ > + u32 *mbx = (u32 *)(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 = (u32 *)(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 request[] = { > + PMUFW_CMD_SET_CONFIGURATION, > + (u32)((u64)cfg_obj) > + }; > + u32 response; > + int err; > + > + printf("Loading PMUFW cfg obj (%ld bytes)\n", 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/Makefile b/board/xilinx/zynqmp/Makefile > index 80f8ca7e1e4b..b4d39edc118f 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_SPL_PM_CFG_OBJ_FILE),"") > +obj-$(CONFIG_SPL_BUILD) += pm_cfg_obj.o > +$(obj)/pm_cfg_obj.o: $(shell cd $(srctree); readlink -f > $(CONFIG_ZYNQMP_SPL_PM_CFG_OBJ_FILE)) FORCE > +endif > + > obj-$(CONFIG_MMC_SDHCI_ZYNQ) += tap_delays.o > > ifndef CONFIG_SPL_BUILD > diff --git a/board/xilinx/zynqmp/pm_cfg_obj.S > b/board/xilinx/zynqmp/pm_cfg_obj.S > new file mode 100644 > index 000000000000..c4ca77e396ce > --- /dev/null > +++ b/board/xilinx/zynqmp/pm_cfg_obj.S > @@ -0,0 +1,17 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > + > +.section .rodata > + > +.global zynqmp_pm_cfg_obj > +.type zynqmp_pm_cfg_obj, @object > +.global zynqmp_pm_cfg_obj_size > +.type zynqmp_pm_cfg_obj_size, @object > + > +zynqmp_pm_cfg_obj: > +.align 4 > +.incbin CONFIG_ZYNQMP_SPL_PM_CFG_OBJ_FILE > + > +zynqmp_pm_cfg_obj_end: > + > +zynqmp_pm_cfg_obj_size: > +.int zynqmp_pm_cfg_obj_end - zynqmp_pm_cfg_obj > diff --git a/board/xilinx/zynqmp/pm_cfg_obj.h > b/board/xilinx/zynqmp/pm_cfg_obj.h > new file mode 100644 > index 000000000000..86e785490ced > --- /dev/null > +++ b/board/xilinx/zynqmp/pm_cfg_obj.h > @@ -0,0 +1,9 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * (C) Copyright 2019 Luca Ceresoli <l...@lucaceresoli.net> > + * > + * 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; > diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c > index 5189925beb3a..c16bdb724c39 100644 > --- a/board/xilinx/zynqmp/zynqmp.c > +++ b/board/xilinx/zynqmp/zynqmp.c > @@ -22,6 +22,8 @@ > #include <zynqmppl.h> > #include <g_dnl.h> > > +#include "pm_cfg_obj.h" > + > DECLARE_GLOBAL_DATA_PTR; > > #if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_WDT) > @@ -331,6 +333,13 @@ int board_early_init_f(void) > > int board_init(void) > { > +#if defined(CONFIG_SPL_BUILD) > + /* Check *at build time* if the filename is an non-empty string */ > + if (sizeof(CONFIG_ZYNQMP_SPL_PM_CFG_OBJ_FILE) > 1) > + zynqmp_pmufw_load_config_object(zynqmp_pm_cfg_obj, > + zynqmp_pm_cfg_obj_size); > +#endif > + > printf("EL Level:\tEL%d\n", current_el()); > > #if defined(CONFIG_FPGA) && defined(CONFIG_FPGA_ZYNQMPPL) && \ >
This is good step forward that's why even some stuff can be done better I have not a problem with applying this. Thanks, Michal _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot