[PATCH 11/20] mach-k3: r5: common: add helper functions needed in LPM resume sequence
Richard Genoud (TI)
richard.genoud at bootlin.com
Thu Apr 30 10:44:04 CEST 2026
From: Prasanth Babu Mantena <p-mantena at ti.com>
Add helper functions that are used by respective SoCs in LPM resume flow.
- lpm_process() is called at boot time to:
- retrieve the LPM memory region from DTS
- save ATF/OPTEE certificates information and DM code in this memory
region
- Forward the LPM address to TIFS via TISCI_MSG_LPM_SAVE_ADDR
TIFS will use this address to save TFA context and its own minimal
context just before suspend.
- do_resume() is called at resume, just after bringing the DDR out of
retention to:
- retrieve the LPM memory region from DTS
- authenticate certificates from LPM memory region and apply firewalls
- ask TIFS to restore TFA and its own minimal context
- start TFA on remote proc
- load and jump to DM
https://software-dl.ti.com/tisci/esd/latest/2_tisci_msgs/pm/lpm.html#lpm-msg-lpm-save-addr
Signed-off-by: Prasanth Babu Mantena <p-mantena at ti.com>
Co-developed-by: Richard Genoud (TI) <richard.genoud at bootlin.com>
Signed-off-by: Richard Genoud (TI) <richard.genoud at bootlin.com>
---
arch/arm/mach-k3/common.h | 16 +++
arch/arm/mach-k3/lpm-common.h | 15 ++
arch/arm/mach-k3/r5/Kconfig | 4 +
arch/arm/mach-k3/r5/Makefile | 1 +
arch/arm/mach-k3/r5/common.c | 17 +--
arch/arm/mach-k3/r5/lpm-common.c | 228 +++++++++++++++++++++++++++++++
6 files changed, 269 insertions(+), 12 deletions(-)
create mode 100644 arch/arm/mach-k3/lpm-common.h
create mode 100644 arch/arm/mach-k3/r5/lpm-common.c
diff --git a/arch/arm/mach-k3/common.h b/arch/arm/mach-k3/common.h
index e970076d08ec..50e670f845e3 100644
--- a/arch/arm/mach-k3/common.h
+++ b/arch/arm/mach-k3/common.h
@@ -8,6 +8,7 @@
#include <asm/armv7_mpu.h>
#include <asm/hardware.h>
+#include <image.h>
#include <mach/security.h>
/* keep ram_top in the 32-bit address space */
@@ -16,6 +17,21 @@
#define K3_FIREWALL_BACKGROUND_BIT (8)
#define K3_SPEED_GRADE_UNKNOWN '\0'
+#if IS_ENABLED(CONFIG_SYS_K3_SPL_ATF)
+enum {
+ IMAGE_ID_ATF,
+ IMAGE_ID_OPTEE,
+ IMAGE_ID_SPL,
+ IMAGE_ID_DM_FW,
+ IMAGE_ID_TIFSSTUB_HS,
+ IMAGE_ID_TIFSSTUB_FS,
+ IMAGE_ID_TIFSSTUB_GP,
+ IMAGE_AMT,
+};
+
+extern struct image_info fit_image_info[IMAGE_AMT];
+#endif
+
struct fwl_data {
const char *name;
u16 fwl_id;
diff --git a/arch/arm/mach-k3/lpm-common.h b/arch/arm/mach-k3/lpm-common.h
new file mode 100644
index 000000000000..2b360e6f03f3
--- /dev/null
+++ b/arch/arm/mach-k3/lpm-common.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * K3: LPM Architecture common definitions
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ * Copyright (C) 2026 Bootlin
+ */
+
+#ifndef _LPM_COMMON_H_
+#define _LPM_COMMON_H_
+
+void __noreturn do_resume(void);
+void lpm_process(void);
+
+#endif
diff --git a/arch/arm/mach-k3/r5/Kconfig b/arch/arm/mach-k3/r5/Kconfig
index 12335880e106..7a9c005a6d7c 100644
--- a/arch/arm/mach-k3/r5/Kconfig
+++ b/arch/arm/mach-k3/r5/Kconfig
@@ -1,6 +1,10 @@
config K3_LOAD_SYSFW
bool
+config K3_LPM
+ bool
+ default y if SOC_K3_J721E || SOC_K3_J7200 || SOC_K3_J784S4 || SOC_K3_J722S || SOC_K3_J721S2
+
config K3_OPP_LOW
depends on ARCH_K3 && K3_AVS0
bool "Enable OPP_LOW on supported TI K3 SoCs"
diff --git a/arch/arm/mach-k3/r5/Makefile b/arch/arm/mach-k3/r5/Makefile
index 074e3b61a262..ccededbc0d60 100644
--- a/arch/arm/mach-k3/r5/Makefile
+++ b/arch/arm/mach-k3/r5/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_SOC_K3_J722S) += j722s/
obj-$(CONFIG_SOC_K3_J784S4) += j784s4/
obj-y += common.o
+obj-$(CONFIG_K3_LPM) += lpm-common.o
obj-y += lowlevel_init.o
obj-y += r5_mpu.o
diff --git a/arch/arm/mach-k3/r5/common.c b/arch/arm/mach-k3/r5/common.c
index 03638366046b..a80e903de85a 100644
--- a/arch/arm/mach-k3/r5/common.c
+++ b/arch/arm/mach-k3/r5/common.c
@@ -18,19 +18,9 @@
#include <elf.h>
#include "../common.h"
+#include "../lpm-common.h"
#if IS_ENABLED(CONFIG_SYS_K3_SPL_ATF)
-enum {
- IMAGE_ID_ATF,
- IMAGE_ID_OPTEE,
- IMAGE_ID_SPL,
- IMAGE_ID_DM_FW,
- IMAGE_ID_TIFSSTUB_HS,
- IMAGE_ID_TIFSSTUB_FS,
- IMAGE_ID_TIFSSTUB_GP,
- IMAGE_AMT,
-};
-
#if CONFIG_IS_ENABLED(FIT_IMAGE_POST_PROCESS)
static const char *image_os_match[IMAGE_AMT] = {
"arm-trusted-firmware",
@@ -43,7 +33,7 @@ static const char *image_os_match[IMAGE_AMT] = {
};
#endif
-static struct image_info fit_image_info[IMAGE_AMT];
+struct image_info fit_image_info[IMAGE_AMT];
void init_env(void)
{
@@ -170,6 +160,9 @@ void __noreturn jump_to_image(struct spl_image_info *spl_image)
if (ret)
panic("%s: ATF failed to load on rproc (%d)\n", __func__, ret);
+ if (IS_ENABLED(CONFIG_K3_LPM))
+ lpm_process();
+
#if CONFIG_IS_ENABLED(FIT_IMAGE_POST_PROCESS)
/* Authenticate ATF */
void *image_addr = (void *)fit_image_info[IMAGE_ID_ATF].image_start;
diff --git a/arch/arm/mach-k3/r5/lpm-common.c b/arch/arm/mach-k3/r5/lpm-common.c
new file mode 100644
index 000000000000..c39d85a43fd2
--- /dev/null
+++ b/arch/arm/mach-k3/r5/lpm-common.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * K3: R5 Common LPM Architecture initialization
+ *
+ * Copyright (C) 2023-2026 Texas Instruments Incorporated - https://www.ti.com/
+ * Copyright (C) 2026 Bootlin
+ */
+
+#include <clk.h>
+#include <dm/read.h>
+#include <elf.h>
+#include <linux/printk.h>
+#include <linux/soc/ti/ti_sci_protocol.h>
+#include <power-domain.h>
+#include <remoteproc.h>
+#include <mach/security.h>
+
+#include "../common.h"
+#include "../lpm-common.h"
+
+#define FW_IMAGE_SIZE 0x80000
+
+struct lpm_addr_info {
+ u32 *context_save_addr;
+ u32 *atf_cert_addr;
+ u32 *optee_cert_addr;
+ u32 *dm_save_addr;
+ u32 size;
+};
+
+struct lpm_addr_info mem_addr_lpm;
+
+__weak bool j7xx_board_is_resuming(void)
+{
+ return false;
+}
+
+static int extract_lpm_region(void)
+{
+ ofnode node;
+ fdt_addr_t lpm_reg_addr;
+ fdt_size_t lpm_reg_size;
+
+ node = ofnode_path("/reserved-memory/lpm-memory");
+ if (!ofnode_valid(node)) {
+ printf("lpm will not be functional\n");
+ return -ENODEV;
+ }
+
+ lpm_reg_addr = ofnode_get_addr(node);
+ if (lpm_reg_addr == FDT_ADDR_T_NONE) {
+ printf("Can't find a valid reserved node!\n");
+ return -ENODEV;
+ }
+
+ lpm_reg_size = ofnode_get_size(node);
+ if (lpm_reg_size == FDT_ADDR_T_NONE) {
+ printf("Can't find a valid reserved node!\n");
+ return -ENODEV;
+ }
+
+ mem_addr_lpm.context_save_addr = (u32 *)lpm_reg_addr;
+ mem_addr_lpm.atf_cert_addr = mem_addr_lpm.context_save_addr + FW_IMAGE_SIZE;
+ mem_addr_lpm.optee_cert_addr = mem_addr_lpm.atf_cert_addr + FW_IMAGE_SIZE;
+ mem_addr_lpm.dm_save_addr = mem_addr_lpm.optee_cert_addr + (2 * FW_IMAGE_SIZE);
+ mem_addr_lpm.size = lpm_reg_size;
+
+ return 0;
+}
+
+static int save_certificate(void)
+{
+ int ret;
+
+ if (!fit_image_info[IMAGE_ID_ATF].image_start ||
+ !fit_image_info[IMAGE_ID_OPTEE].image_start ||
+ !fit_image_info[IMAGE_ID_DM_FW].image_start) {
+ pr_err("Invalid images to save\n");
+ return -EINVAL;
+ }
+
+ ret = extract_lpm_region();
+ if (ret) {
+ pr_err("Cannot find valid LPM address range..\n");
+ return -ENOMEM;
+ }
+
+ memcpy(mem_addr_lpm.atf_cert_addr,
+ (void *)fit_image_info[IMAGE_ID_ATF].image_start,
+ fit_image_info[IMAGE_ID_ATF].image_len);
+
+ memcpy(mem_addr_lpm.optee_cert_addr,
+ (void *)fit_image_info[IMAGE_ID_OPTEE].image_start,
+ fit_image_info[IMAGE_ID_OPTEE].image_len);
+
+ memcpy(mem_addr_lpm.dm_save_addr,
+ (void *)fit_image_info[IMAGE_ID_DM_FW].image_start,
+ fit_image_info[IMAGE_ID_DM_FW].image_len);
+
+ return 0;
+}
+
+void lpm_process(void)
+{
+ int ret = 0;
+ unsigned long save_addr;
+ struct ti_sci_handle *ti_sci = get_ti_sci_handle();
+
+ ret = save_certificate();
+ if (ret)
+ return;
+ save_addr = (unsigned long)mem_addr_lpm.context_save_addr;
+ ret = ti_sci->ops.lpm_ops.lpm_save_addr(ti_sci, save_addr,
+ mem_addr_lpm.size);
+ if (ret)
+ pr_err("TIFS lpm save addr fail\n");
+}
+
+static u32 resume_to_dm_f(void)
+{
+ struct ti_sci_handle *ti_sci = get_ti_sci_handle();
+ u32 loadaddr = 0, save_addr = 0;
+ int ret = 0;
+
+ loadaddr = (u32)mem_addr_lpm.dm_save_addr;
+ if (!valid_elf_image(loadaddr))
+ panic("%s: DM-Firmware image is not valid, it cannot be loaded\n",
+ __func__);
+
+ loadaddr = load_elf_image_phdr(loadaddr);
+ save_addr = (uintptr_t)mem_addr_lpm.context_save_addr;
+ ret = ti_sci->ops.lpm_ops.lpm_save_addr(ti_sci, save_addr, mem_addr_lpm.size);
+ if (ret)
+ panic("TIFS lpm save addr fail : %x\n", ret);
+
+ /*
+ * TIFS minimal context restore
+ * This restores also the firewall
+ */
+ ret = ti_sci->ops.lpm_ops.min_context_restore(ti_sci, 0);
+ if (ret)
+ panic("TIFS restore_context failed (%d)\n", ret);
+
+ /*
+ * Restore TFA in msmc memory
+ */
+ ret = ti_sci->ops.lpm_ops.decrypt_tfa(ti_sci,
+ CONFIG_K3_ATF_LOAD_ADDR);
+ if (ret)
+ panic("%s: TIFS failed to decrytp TFA : %x\n", __func__, ret);
+
+ /* restore TFA resume vectore address in main core */
+ ret = ti_sci->ops.lpm_ops.core_resume(ti_sci);
+ if (ret)
+ panic("ATF failed to resume (%d)\n", ret);
+
+ return loadaddr;
+}
+
+static void resume_rproc_f(void)
+{
+ struct power_domain rproc_pwrdmn;
+ unsigned long gtc_rate;
+ struct udevice *dev;
+ struct clk gtc_clk;
+ void *gtc_base;
+ int ret;
+
+ ret = uclass_get_device_by_seq(UCLASS_REMOTEPROC, 1, &dev);
+ if (ret)
+ panic("Unknown remote processor 1 (%d)\n", ret);
+
+ ret = power_domain_get_by_index(dev, &rproc_pwrdmn, 1);
+ if (ret)
+ panic("power_domain_get_rproc() failed: %d\n", ret);
+
+ ret = clk_get_by_index(dev, 0, >c_clk);
+ if (ret)
+ panic("clk_get failed: %d\n", ret);
+
+ gtc_base = dev_read_addr_ptr(dev);
+ if (!gtc_base)
+ panic("Get GTC address failed\n");
+
+ gtc_rate = clk_get_rate(>c_clk);
+
+#define GTC_CNTCR_REG 0x0
+#define GTC_CNTFID0_REG 0x20
+#define GTC_CNTR_EN 0x3
+ /* TFA expect the Global Timebase Counter to be set-up */
+ writel((u32)gtc_rate, gtc_base + GTC_CNTFID0_REG);
+ writel(GTC_CNTR_EN, gtc_base + GTC_CNTCR_REG);
+
+ ret = power_domain_on(&rproc_pwrdmn);
+ if (ret)
+ panic("power_domain_on failed: %d\n", ret);
+}
+
+typedef void __noreturn (*image_entry_noargs_t)(void);
+
+void __noreturn do_resume(void)
+{
+ image_entry_noargs_t image_entry;
+ u32 loadaddr, size_int = FW_IMAGE_SIZE;
+ void *image_addr;
+ int ret;
+
+ ret = extract_lpm_region();
+ if (ret)
+ panic("Cannot find valid LPM address range..LPM resume failed\n");
+
+ image_addr = mem_addr_lpm.atf_cert_addr;
+ ret = rproc_load(1, (ulong)image_addr, 0x200);
+ if (ret)
+ panic("rproc failed to be initialized (%d)\n", ret);
+
+ ti_secure_image_auth_apply_fwls(&image_addr, &size_int);
+
+ image_addr = mem_addr_lpm.optee_cert_addr;
+ ti_secure_image_auth_apply_fwls(&image_addr, &size_int);
+
+ loadaddr = resume_to_dm_f();
+ printf("Starting ATF on ARM64 core...\n\n");
+ resume_rproc_f();
+
+ image_entry = (image_entry_noargs_t)loadaddr;
+ image_entry();
+}
More information about the U-Boot
mailing list