[U-Boot] [PATCH v4 1/2] arm64: zynqmp: spl: install a PMU firmware config object at runtime
Michal Simek
michal.simek at xilinx.com
Tue May 21 12:21:26 UTC 2019
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 <luca at 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 <luca at 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 <luca at 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
More information about the U-Boot
mailing list