diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index af838b2dc327..f44785aee808 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 @@ -223,34 +224,37 @@ 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]); - if (ret) - ret = -EOPNOTSUPP; - else - ret = ret_payload[1]; - return ret; + /* + * 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) + return ret; + + return ret_payload[1]; } static int do_feature_check_call(const u32 api_id) @@ -2065,6 +2069,71 @@ 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 and platform 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; + } + + /* 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; + } + + /* 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; +} + static int zynqmp_firmware_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -2118,6 +2187,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) @@ -2151,6 +2223,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 +2287,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/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; diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 7e27b0f7bf7e..69a2f74269f3 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 @@ -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 @@ -66,6 +67,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 @@ -144,6 +146,12 @@ #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 + +/* Node ID for all notifier callbacks */ +#define PM_ALL_NOTIFIERS 0xFFFFFFFFU + enum pm_module_id { PM_MODULE_ID = 0x0, XPM_MODULE_ID = 0x2,