[PATCH v2 1/4] firmware: xilinx: Add support to clear TF-A PM state
From: Jay Buddhabhatti <hidden>
Date: 2026-07-24 09:53:28
Also in:
lkml
Subsystem:
the rest · Maintainer:
Linus Torvalds
Currently, during a kexec restart, only the kernel is reloaded, while the TF-A specific data remain unchanged. This leads to a mismatch between the kernel state and the TF-A firmware state like SGI number and shutdown scope variable. For example, the kernel registers an SGI number with TF-A so that TF-A can notify the kernel of events via that SGI. TF-A stores this SGI number in its internal state. After a kexec, the newly loaded kernel re-registers and may request a different SGI number, but the stale value programmed in TF-A remains, so event notifications are delivered on the old SGI and are missed by the new kernel. The shutdown scope variable has a similar stale state problem. To resolve this, the TF_A_CLEAR_PM_STATE PM API is introduced to clear TF-A-specific state during kexec. On a graceful reboot, this API is triggered by zynqmp_firmware_shutdown(), while in a crash kernel scenario, it is invoked by zynqmp_firmware_probe() in the reloaded kernel. Signed-off-by: Jay Buddhabhatti <redacted> Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com> Reviewed-by: Prasanna Kumar T S M <redacted> --- drivers/firmware/xilinx/zynqmp.c | 51 +++++++++++++++++++++++++++- include/linux/firmware/xlnx-zynqmp.h | 3 +- 2 files changed, 52 insertions(+), 2 deletions(-)
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c
index af838b2dc327..b5e372190b44 100644
--- a/drivers/firmware/xilinx/zynqmp.c
+++ b/drivers/firmware/xilinx/zynqmp.c@@ -3,7 +3,7 @@ * Xilinx Zynq MPSoC Firmware layer * * Copyright (C) 2014-2022 Xilinx, Inc. - * Copyright (C) 2022 - 2025 Advanced Micro Devices, Inc. + * Copyright (C) 2022 - 2026 Advanced Micro Devices, Inc. * * Michal Simek <michal.simek@amd.com> * Davorin Mista <davorin.mista@aggios.com>
@@ -13,6 +13,7 @@ #include <linux/arm-smccc.h> #include <linux/compiler.h> +#include <linux/crash_dump.h> #include <linux/device.h> #include <linux/init.h> #include <linux/mfd/core.h>
@@ -2065,6 +2066,42 @@ static struct attribute *zynqmp_firmware_attrs[] = { ATTRIBUTE_GROUPS(zynqmp_firmware); +/** + * zynqmp_clear_pm_state() - Clear subsystem state. + * @dev: Device pointer used for logging. + * + * Clears PM specific data in TF-A. + * + * Return: Returns status, either success or error + */ +static int zynqmp_clear_pm_state(struct device *dev) +{ + int ret; + u32 pm_family_code; + + /* Get the Family code of platform */ + ret = zynqmp_pm_get_family_info(&pm_family_code); + if (ret < 0) + return ret; + + if (pm_family_code != PM_ZYNQMP_FAMILY_CODE) { + /* Check if the TF-A supports the TF_A_CLEAR_PM_STATE */ + ret = do_feature_check_call(TF_A_CLEAR_PM_STATE); + if ((ret & FIRMWARE_VERSION_MASK) >= PM_API_VERSION_1) { + /* Clear PM specific data in TF-A */ + ret = zynqmp_pm_invoke_fn(TF_A_CLEAR_PM_STATE, NULL, 0); + if (ret) + dev_err(dev, + "Failed to clear TF-A specific subsystem state: %d\n", ret); + } else { + dev_warn(dev, "TF_A_CLEAR_PM_STATE is not supported in TF-A: %d\n", ret); + ret = 0; + } + } + + return ret; +} + static int zynqmp_firmware_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev;
@@ -2118,6 +2155,12 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) if (ret < 0) return ret; + if (is_kdump_kernel()) { + ret = zynqmp_clear_pm_state(dev); + if (ret) + return ret; + } + /* Check trustzone version number */ ret = zynqmp_pm_get_trustzone_version(&pm_tz_version); if (ret)
@@ -2151,6 +2194,11 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) return of_platform_populate(dev->of_node, NULL, NULL, dev); } +static void zynqmp_firmware_shutdown(struct platform_device *pdev) +{ + zynqmp_clear_pm_state(&pdev->dev); +} + static void zynqmp_firmware_remove(struct platform_device *pdev) { struct pm_api_feature_data *feature_data;
@@ -2210,5 +2258,6 @@ static struct platform_driver zynqmp_firmware_driver = { }, .probe = zynqmp_firmware_probe, .remove = zynqmp_firmware_remove, + .shutdown = zynqmp_firmware_shutdown, }; module_platform_driver(zynqmp_firmware_driver);
diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
index 7e27b0f7bf7e..baaa88b0b197 100644
--- a/include/linux/firmware/xlnx-zynqmp.h
+++ b/include/linux/firmware/xlnx-zynqmp.h@@ -3,7 +3,7 @@ * Xilinx Zynq MPSoC Firmware layer * * Copyright (C) 2014-2021 Xilinx - * Copyright (C) 2022 - 2025 Advanced Micro Devices, Inc. + * Copyright (C) 2022 - 2026 Advanced Micro Devices, Inc. * * Michal Simek <michal.simek@amd.com> * Davorin Mista <davorin.mista@aggios.com>
@@ -66,6 +66,7 @@ #define FIRMWARE_VERSION_MASK 0xFFFFU /* ATF only commands */ +#define TF_A_CLEAR_PM_STATE 0xa05 #define TF_A_PM_REGISTER_SGI 0xa04 #define PM_GET_TRUSTZONE_VERSION 0xa03 #define PM_SET_SUSPEND_MODE 0xa02
--
2.34.1