[U-Boot] [RFC v2 2/2] arm64: zynqmp: spl: install a PMU firmware config object at runtime

Michal Simek michal.simek at xilinx.com
Wed Mar 27 15:03:32 UTC 2019


On 21. 03. 19 16:48, Luca Ceresoli wrote:
> Optionally allow U-Boot to load at the PMU firmware configuration object
> into the Power Management Unit (PMU) 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
> 
> The load is logged on the console during boot:
> 
>   U-Boot SPL 2018.01 (Mar 20 2019 - 08:12:21)
>   Loading PMUFW cfg obj (2008 bytes)
>   EL Level:	EL3
>   ...
> 
> Signed-off-by: Luca Ceresoli <luca at lucaceresoli.net>
> 
> ---
> 
> 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
> 
> Todo:
>  - don't compile pm_cfg_obj.c from source, load it from a binary file
> ---
>  board/xilinx/zynqmp/Kconfig   |  26 ++++++++
>  board/xilinx/zynqmp/Makefile  |   5 ++
>  board/xilinx/zynqmp/pmu_ipc.c | 116 ++++++++++++++++++++++++++++++++++
>  board/xilinx/zynqmp/pmu_ipc.h |  14 ++++
>  board/xilinx/zynqmp/zynqmp.c  |  11 ++++
>  5 files changed, 172 insertions(+)
>  create mode 100644 board/xilinx/zynqmp/pmu_ipc.c
>  create mode 100644 board/xilinx/zynqmp/pmu_ipc.h
> 
> 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 at 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;
> +}

All above should be mailbox driver. It means this should go to
drivers/mailbox and be split to mbox send/recv functions.

But I have no problem to use this configuration in the first patch and
move to mbox driver in separate patch.

> +
> +/**
> + * 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);
> +}

And this can stay here or go to arch/arm/mach-zynq/

> 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 at 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);
> +


arch/arm/mach-zynqmp/include/mach/sys_proto.h should be fine.



> +#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 at 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

As we discussed over IRC. I think that this should be simply bin
firmware file compare to C built by u-boot.

Thanks,
Michal


More information about the U-Boot mailing list