From patchwork Mon Apr 15 16:47:37 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 1085743 X-Patchwork-Delegate: monstr@monstr.eu Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=lucaceresoli.net Authentication-Results: ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=lucaceresoli.net header.i=@lucaceresoli.net header.b="n2fW0I0a"; dkim-atps=neutral Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 44jZFX39Xpz9s8m for ; Tue, 16 Apr 2019 02:48:32 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 45514C21E15; Mon, 15 Apr 2019 16:48:13 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=T_DKIM_INVALID autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id B3C6BC21DD4; Mon, 15 Apr 2019 16:47:52 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 31483C21C29; Mon, 15 Apr 2019 16:47:50 +0000 (UTC) Received: from hostingweb31-40.netsons.net (hostingweb31-40.netsons.net [89.40.174.40]) by lists.denx.de (Postfix) with ESMTPS id D0960C21CB1 for ; Mon, 15 Apr 2019 16:47:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lucaceresoli.net; s=default; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To: Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help: List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=+2lVZhWkj8UExiGvMa7JrrZyNU8jgJvj88IZQK/y7SQ=; b=n2fW0I0axGZgvziOezoFsuDeRR hlnNkkVqVBsgRW6BwSPy7Lxmf1pSsL0WfnvD6TLt3oR4zMwOMxFZ26GTZVo03PmloqN3VzsUrv5MX 5zsART0OX8m+ib1PdNV5eqNPpumg22Yg9uUZXtXgJsHNBca4BgfO0f5317M49a9X8974=; Received: from [109.168.11.45] (port=44232 helo=pc-ceresoli.dev.aim) by hostingweb31.netsons.net with esmtpa (Exim 4.91) (envelope-from ) id 1hG4lo-003sxr-Pw; Mon, 15 Apr 2019 18:47:48 +0200 From: Luca Ceresoli To: u-boot@lists.denx.de Date: Mon, 15 Apr 2019 18:47:37 +0200 Message-Id: <20190415164738.28024-2-luca@lucaceresoli.net> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190415164738.28024-1-luca@lucaceresoli.net> References: <20190415164738.28024-1-luca@lucaceresoli.net> MIME-Version: 1.0 X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - hostingweb31.netsons.net X-AntiAbuse: Original Domain - lists.denx.de X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - lucaceresoli.net X-Get-Message-Sender-Via: hostingweb31.netsons.net: authenticated_id: luca+lucaceresoli.net/only user confirmed/virtual account not confirmed X-Authenticated-Sender: hostingweb31.netsons.net: luca@lucaceresoli.net X-Source: X-Source-Args: X-Source-Dir: Cc: Luca Ceresoli , Michal Simek Subject: [U-Boot] [PATCH v3 1/2] arm64: zynqmp: spl: install a PMU firmware config object at runtime X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" 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 SPL logs on the console before loading the configuration object: 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 --- 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 | 17 +++ arch/arm/mach-zynqmp/Makefile | 4 + arch/arm/mach-zynqmp/include/mach/sys_proto.h | 4 + arch/arm/mach-zynqmp/pmu_ipc.c | 112 ++++++++++++++++++ board/xilinx/zynqmp/Makefile | 12 ++ board/xilinx/zynqmp/pm_cfg_obj.S | 17 +++ board/xilinx/zynqmp/zynqmp.c | 8 ++ 7 files changed, 174 insertions(+) create mode 100644 arch/arm/mach-zynqmp/pmu_ipc.c create mode 100644 board/xilinx/zynqmp/pm_cfg_obj.S diff --git a/arch/arm/mach-zynqmp/Kconfig b/arch/arm/mach-zynqmp/Kconfig index 9bb5a5c20201..b88d1d313839 100644 --- a/arch/arm/mach-zynqmp/Kconfig +++ b/arch/arm/mach-zynqmp/Kconfig @@ -65,6 +65,23 @@ config PMUFW_INIT_FILE Include external PMUFW (Platform Management Unit FirmWare) to a Xilinx bootable image (boot.bin). +config ZYNQMP_LOAD_PM_CFG_OBJ_FILE + string "PMU firmware configuration object to load at runtime" + 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 + board/xilinx/zynqmp/) 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..d391b7c1d54e 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_LOAD_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..e2b9a79ed539 100644 --- a/arch/arm/mach-zynqmp/include/mach/sys_proto.h +++ b/arch/arm/mach-zynqmp/include/mach/sys_proto.h @@ -72,4 +72,8 @@ int chip_id(unsigned char id); void tcm_init(u8 mode); #endif +#if defined(CONFIG_SPL_BUILD) && defined(ZYNQMP_LOAD_PM_CFG_OBJ) +void zynqmp_pmufw_load_config_object(const void *cfg_obj, size_t size); +#endif + #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..5feb8568f8de --- /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 + */ + +#include +#include +#include + +/* 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 = 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 request[] = { + PMUFW_CMD_SET_CONFIGURATION, + 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..d7a3cb244521 100644 --- a/board/xilinx/zynqmp/Makefile +++ b/board/xilinx/zynqmp/Makefile @@ -33,6 +33,18 @@ 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),"") +PM_CFG_OBJ_FILE := $(shell cd $(srctree); readlink -f $(CONFIG_ZYNQMP_LOAD_PM_CFG_OBJ_FILE)) + +$(obj)/pm_cfg_obj.bin: $(PM_CFG_OBJ_FILE) FORCE + $(call if_changed,shipped) + +$(obj)/pm_cfg_obj.o: $(obj)/pm_cfg_obj.bin + +CFLAGS_zynqmp.o += -DZYNQMP_LOAD_PM_CFG_OBJ +obj-$(CONFIG_SPL_BUILD) += pm_cfg_obj.o +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..72ca0fb785dc --- /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 "spl/board/xilinx/zynqmp/pm_cfg_obj.bin" + +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/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c index db272478506f..7fcb3e120688 100644 --- a/board/xilinx/zynqmp/zynqmp.c +++ b/board/xilinx/zynqmp/zynqmp.c @@ -327,6 +327,14 @@ int board_early_init_f(void) int board_init(void) { +#if defined(CONFIG_SPL_BUILD) && defined(ZYNQMP_LOAD_PM_CFG_OBJ) + extern const u32 zynqmp_pm_cfg_obj[]; + extern const int zynqmp_pm_cfg_obj_size; + + 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) && \