From d73e353efd3e35f0e661043b916b9ce93b6c3c72 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Thu, 23 Jul 2026 10:28:54 +0800 Subject: [PATCH 1/6] soc: xilinx: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_threaded_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Reviewed-by: Radhey Shyam Pandey Acked-by: Michal Simek Link: https://patch.msgid.link/20260722023523.42269-6-panchuang@vivo.com Signed-off-by: Michal Simek --- drivers/soc/xilinx/zynqmp_power.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c index 370e61ac47d8..54c796afb89e 100644 --- a/drivers/soc/xilinx/zynqmp_power.c +++ b/drivers/soc/xilinx/zynqmp_power.c @@ -368,11 +368,8 @@ static int zynqmp_pm_probe(struct platform_device *pdev) IRQF_NO_SUSPEND | IRQF_ONESHOT, dev_name(&pdev->dev), &pdev->dev); - if (ret) { - dev_err(&pdev->dev, "devm_request_threaded_irq '%d' failed with %d\n", - irq, ret); + if (ret) return ret; - } } else { dev_err(&pdev->dev, "Required property not found in DT node\n"); return -ENOENT; From 1689ca12dd78d61fd2663da795197d820a9d7c48 Mon Sep 17 00:00:00 2001 From: Jay Buddhabhatti Date: Fri, 24 Jul 2026 02:53:51 -0700 Subject: [PATCH 2/6] firmware: xilinx: Use TF-A feature check for TF-A-specific APIs Currently, TF-A-specific APIs are validated using the firmware PM_FEATURE_CHECK API, even though TF-A provides a dedicated mechanism via PM_API_FEATURES API. Update the feature check logic for TF-A-specific API calls to use PM_API_FEATURES. If this check fails, fall back to the legacy PM_FEATURE_CHECK to support backward compatibility. Signed-off-by: Jay Buddhabhatti Reviewed-by: Radhey Shyam Pandey Reviewed-by: Prasanna Kumar T S M Link: https://patch.msgid.link/20260724095352.2890326-2-jay.buddhabhatti@amd.com Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index af838b2dc327..79993ac8452a 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -223,28 +223,33 @@ static int __do_feature_check_call(const u32 api_id, u32 *ret_payload) module_id = FIELD_GET(MODULE_ID_MASK, api_id); /* - * Feature check of APIs belonging to PM, XSEM, and TF-A are handled by calling + * Feature check of APIs belonging to PM and XSEM are handled by calling * PM_FEATURE_CHECK API. For other modules, call PM_API_FEATURES API. */ - if (module_id == PM_MODULE_ID || module_id == XSEM_MODULE_ID || module_id == TF_A_MODULE_ID) + if (module_id == PM_MODULE_ID || module_id == XSEM_MODULE_ID) feature_check_api_id = PM_FEATURE_CHECK; else feature_check_api_id = PM_API_FEATURES; - /* - * Feature check of TF-A APIs is done in the TF-A layer and it expects for - * MODULE_ID_MASK bits of SMC's arg[0] to be the same as PM_MODULE_ID. - */ - if (module_id == TF_A_MODULE_ID) { - module_id = PM_MODULE_ID; + if (module_id == TF_A_MODULE_ID) smc_arg[1] = api_id; - } else { + else smc_arg[1] = (api_id & API_ID_MASK); - } smc_arg[0] = PM_SIP_SVC | FIELD_PREP(MODULE_ID_MASK, module_id) | feature_check_api_id; ret = do_fw_call(ret_payload, 2, smc_arg[0], smc_arg[1]); + + /* + * For TF-A APIs, if the feature check with PM_API_FEATURES fails, + * retry with the legacy PM_FEATURE_CHECK for backward compatibility. + */ + if (module_id == TF_A_MODULE_ID && ret) { + smc_arg[0] = PM_SIP_SVC | FIELD_PREP(MODULE_ID_MASK, PM_MODULE_ID) | + PM_FEATURE_CHECK; + ret = do_fw_call(ret_payload, 2, smc_arg[0], smc_arg[1]); + } + if (ret) ret = -EOPNOTSUPP; else From 5b8c4b238b02db7af65f349a9d677c2dd60359ac Mon Sep 17 00:00:00 2001 From: Jay Buddhabhatti Date: Fri, 24 Jul 2026 02:53:52 -0700 Subject: [PATCH 3/6] firmware: xilinx: Propagate actual error from feature check When do_fw_call() fails in __do_feature_check_call(), propagate the actual errno from zynqmp_pm_ret_code() instead of always returning -EOPNOTSUPP. This gives callers more precise error information. Existing callers only test ret < 0 and are unchanged by this. Signed-off-by: Jay Buddhabhatti Reviewed-by: Radhey Shyam Pandey Reviewed-by: Prasanna Kumar T S M Link: https://patch.msgid.link/20260724095352.2890326-3-jay.buddhabhatti@amd.com Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 79993ac8452a..1e3ade42b69f 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -251,11 +251,9 @@ static int __do_feature_check_call(const u32 api_id, u32 *ret_payload) } if (ret) - ret = -EOPNOTSUPP; - else - ret = ret_payload[1]; + return ret; - return ret; + return ret_payload[1]; } static int do_feature_check_call(const u32 api_id) From 00bee072c8ae394b029ed4ce6fc8def3e5627e1c Mon Sep 17 00:00:00 2001 From: Jay Buddhabhatti Date: Wed, 29 Jul 2026 05:25:20 -0700 Subject: [PATCH 4/6] firmware: xilinx: Add support to clear EL3 PM state Currently, during a kexec restart, only the kernel is reloaded, while EL3-specific data remain unchanged. This leads to a mismatch between the kernel state and secure firmware state like SGI number and shutdown scope variable. For example, the kernel registers an SGI number with EL3 firmware so that secure firmware can notify the kernel of events via that SGI. EL3 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 EL3 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 EL3 PM subsystem 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 Reviewed-by: Radhey Shyam Pandey Reviewed-by: Prasanna Kumar T S M Link: https://patch.msgid.link/20260729122522.3732875-2-jay.buddhabhatti@amd.com Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp.c | 50 +++++++++++++++++++++++++++- include/linux/firmware/xlnx-zynqmp.h | 3 +- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 1e3ade42b69f..e697153ff056 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 * Davorin Mista @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -2068,6 +2069,44 @@ 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 EL3 firmware. + * + * Return: Returns status, either success or error + */ +static int zynqmp_clear_pm_state(struct device *dev) +{ + u32 pm_family_code; + int ret; + + /* Get the Family code of platform */ + ret = zynqmp_pm_get_family_info(&pm_family_code); + if (ret < 0) + return ret; + + /* Supporting on Versal and Versal Net platforms only */ + if (pm_family_code == PM_VERSAL_FAMILY_CODE || + pm_family_code == PM_VERSAL_NET_FAMILY_CODE) { + /* Check if EL3 firmware supports TF_A_CLEAR_PM_STATE */ + ret = do_feature_check_call(TF_A_CLEAR_PM_STATE); + if (ret >= 0 && ((ret & FIRMWARE_VERSION_MASK) >= PM_API_VERSION_1)) { + /* Clear PM specific data in EL3 firmware */ + ret = zynqmp_pm_invoke_fn(TF_A_CLEAR_PM_STATE, NULL, 0); + if (ret) + dev_err(dev, + "Failed to clear EL3 PM subsystem state: %d\n", ret); + } else { + dev_warn(dev, "TF_A_CLEAR_PM_STATE is not supported by EL3 firmware: %d\n", ret); + ret = 0; + } + } + + return ret; +} + static int zynqmp_firmware_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -2121,6 +2160,9 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) if (ret < 0) return ret; + if (is_kdump_kernel()) + zynqmp_clear_pm_state(dev); + /* Check trustzone version number */ ret = zynqmp_pm_get_trustzone_version(&pm_tz_version); if (ret) @@ -2154,6 +2196,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; @@ -2213,5 +2260,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 * Davorin Mista @@ -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 From 84dbfa5519cf6583d09ee0123ac580542b144553 Mon Sep 17 00:00:00 2001 From: Jay Buddhabhatti Date: Wed, 29 Jul 2026 05:25:21 -0700 Subject: [PATCH 5/6] firmware: xilinx: Release all peripheral devices from firmware During a kexec restart, only the kernel is reloaded while devices allocated in firmware persist, causing state mismatches between the kernel and firmware. Introduce PM_DEV_ALL_PERIPH node ID (0x18224FFFU) to release all peripheral devices during kexec. On graceful restarts, this happens in zynqmp_firmware_shutdown(). On crash kernel restarts, it happens in zynqmp_firmware_probe() of the reloaded kernel. Releasing all peripherals depends on firmware support for the PM_DEV_ALL_PERIPH node ID. On firmware that does not implement it (the feature check reports a version below PM_API_VERSION_3) the release is skipped and a warning such as "Bulk device release is not supported by firmware" is logged, e.g. on Versal NET firmware that predates this API. Signed-off-by: Jay Buddhabhatti Reviewed-by: Radhey Shyam Pandey Reviewed-by: Prasanna Kumar T S M Link: https://patch.msgid.link/20260729122522.3732875-3-jay.buddhabhatti@amd.com Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp.c | 15 ++++++++++++++- include/linux/firmware/xlnx-zynqmp.h | 4 ++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index e697153ff056..4bf6c9eabebc 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -2073,7 +2073,7 @@ ATTRIBUTE_GROUPS(zynqmp_firmware); * zynqmp_clear_pm_state() - Clear subsystem state * @dev: Device pointer used for logging * - * Clears PM specific data in EL3 firmware. + * Clears PM specific data in EL3 and platform firmware. * * Return: Returns status, either success or error */ @@ -2102,6 +2102,19 @@ static int zynqmp_clear_pm_state(struct device *dev) dev_warn(dev, "TF_A_CLEAR_PM_STATE is not supported by EL3 firmware: %d\n", ret); ret = 0; } + + /* Check if the firmware supports the PM_DEV_ALL_PERIPH node ID */ + ret = do_feature_check_call(PM_RELEASE_NODE); + if (ret >= 0 && ((ret & FIRMWARE_VERSION_MASK) >= PM_API_VERSION_3)) { + /* Attempt to release all peripheral devices via firmware */ + ret = zynqmp_pm_release_node(PM_DEV_ALL_PERIPH); + if (ret) + dev_err(dev, "Failed to release all peripheral devices: %d\n", ret); + } else { + dev_warn(dev, + "Bulk device release is not supported by firmware: %d\n", ret); + ret = 0; + } } return ret; diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index baaa88b0b197..ac39e5492961 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -50,6 +50,7 @@ /* PM API versions */ #define PM_API_VERSION_1 1 #define PM_API_VERSION_2 2 +#define PM_API_VERSION_3 3 #define PM_PINCTRL_PARAM_SET_VERSION 2 @@ -145,6 +146,9 @@ #define XPM_EVENT_ERROR_MASK_NOC_NCR BIT(13) #define XPM_EVENT_ERROR_MASK_NOC_CR BIT(12) +/* Node ID for all peripheral devices */ +#define PM_DEV_ALL_PERIPH 0x18224FFFU + enum pm_module_id { PM_MODULE_ID = 0x0, XPM_MODULE_ID = 0x2, From 0e8860367da261116cd290bcd6a54d2e597a0c5c Mon Sep 17 00:00:00 2001 From: Jay Buddhabhatti Date: Wed, 29 Jul 2026 05:25:22 -0700 Subject: [PATCH 6/6] firmware: xilinx: Clear firmware notifiers across kexec transitions During a kexec restart, only the kernel is reloaded but notifier callbacks in firmware persist, causing state mismatches between kernel and firmware. To address this, introduce PM_ALL_NOTIFIERS node ID to unregister all notifier callbacks during kexec. On a graceful kexec restart, this occurs in zynqmp_firmware_shutdown(). On a crash kernel restart, it happens in zynqmp_firmware_probe() in the reloaded kernel. Unregistering all notifiers depends on firmware support for the PM_ALL_NOTIFIERS node ID. On firmware that does not implement it (the feature check reports a version below PM_API_VERSION_3) the step is skipped and a warning such as "Firmware doesn't support unregister all notifiers at once" is logged, e.g. on Versal NET firmware that predates this API. Signed-off-by: Jay Buddhabhatti Reviewed-by: Radhey Shyam Pandey Reviewed-by: Prasanna Kumar T S M Link: https://patch.msgid.link/20260729122522.3732875-4-jay.buddhabhatti@amd.com Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp.c | 14 ++++++++++++++ include/linux/firmware/xlnx-zynqmp.h | 3 +++ 2 files changed, 17 insertions(+) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 4bf6c9eabebc..f44785aee808 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -2115,6 +2115,20 @@ static int zynqmp_clear_pm_state(struct device *dev) "Bulk device release is not supported by firmware: %d\n", ret); ret = 0; } + + /* Check if the firmware supports the PM_ALL_NOTIFIERS node ID */ + ret = do_feature_check_call(PM_REGISTER_NOTIFIER); + if (ret >= 0 && ((ret & FIRMWARE_VERSION_MASK) >= PM_API_VERSION_3)) { + /* Attempt to unregister all notifier callbacks via firmware */ + ret = zynqmp_pm_register_notifier(PM_ALL_NOTIFIERS, 0, 0, 0); + if (ret) + dev_err(dev, "Failed to unregister all notifiers: %d\n", ret); + } else { + dev_warn(dev, + "Firmware doesn't support unregister all notifiers at once: %d\n", + ret); + ret = 0; + } } return ret; diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index ac39e5492961..69a2f74269f3 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -149,6 +149,9 @@ /* Node ID for all peripheral devices */ #define PM_DEV_ALL_PERIPH 0x18224FFFU +/* Node ID for all notifier callbacks */ +#define PM_ALL_NOTIFIERS 0xFFFFFFFFU + enum pm_module_id { PM_MODULE_ID = 0x0, XPM_MODULE_ID = 0x2,