Message ID | 20240920055501.2658642-1-ronak.jain@amd.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | firmware: xilinx: add support for new SMC call format | expand |
On 9/20/24 07:55, Ronak Jain wrote: > Added zynqmp_pm_invoke_fw_fn() to use new SMC format in which > lower 12 bits of SMC id are fixed and firmware header is moved to > subsequent SMC arguments. The new SMC format supports full request and > response buffers. > > Added zynqmp_pm_get_sip_svc_version() to get SiP SVC version > number to check if TF-A is newer or older and use the SMC format > accordingly to handle backward compatibility. > > Used new SMC format for PM_QUERY_DATA API as more response values are > required in it. > > Signed-off-by: Ronak Jain <ronak.jain@amd.com> > Signed-off-by: Jay Buddhabhatti <jay.buddhabhatti@amd.com> > --- > drivers/firmware/xilinx/zynqmp.c | 137 ++++++++++++++++++++++++++- > include/linux/firmware/xlnx-zynqmp.h | 26 ++++- > 2 files changed, 157 insertions(+), 6 deletions(-) > > diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c > index c8be32d9c6af..5ab0dfb2b225 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 - 2023, Advanced Micro Devices, Inc. > + * Copyright (C) 2022 - 2024, Advanced Micro Devices, Inc. > * > * Michal Simek <michal.simek@amd.com> > * Davorin Mista <davorin.mista@aggios.com> > @@ -46,6 +46,7 @@ static DEFINE_HASHTABLE(pm_api_features_map, PM_API_FEATURE_CHECK_MAX_ORDER); > static u32 ioctl_features[FEATURE_PAYLOAD_SIZE]; > static u32 query_features[FEATURE_PAYLOAD_SIZE]; > > +static u32 sip_svc_version; > static struct platform_device *em_dev; > > /** > @@ -151,6 +152,9 @@ static noinline int do_fw_call_smc(u32 *ret_payload, u32 num_args, ...) > ret_payload[1] = upper_32_bits(res.a0); > ret_payload[2] = lower_32_bits(res.a1); > ret_payload[3] = upper_32_bits(res.a1); > + ret_payload[4] = lower_32_bits(res.a2); > + ret_payload[5] = upper_32_bits(res.a2); > + ret_payload[6] = lower_32_bits(res.a3); > } > > return zynqmp_pm_ret_code((enum pm_ret_status)res.a0); > @@ -191,6 +195,9 @@ static noinline int do_fw_call_hvc(u32 *ret_payload, u32 num_args, ...) > ret_payload[1] = upper_32_bits(res.a0); > ret_payload[2] = lower_32_bits(res.a1); > ret_payload[3] = upper_32_bits(res.a1); > + ret_payload[4] = lower_32_bits(res.a2); > + ret_payload[5] = upper_32_bits(res.a2); > + ret_payload[6] = lower_32_bits(res.a3); > } > > return zynqmp_pm_ret_code((enum pm_ret_status)res.a0); > @@ -331,6 +338,70 @@ int zynqmp_pm_is_function_supported(const u32 api_id, const u32 id) > } > EXPORT_SYMBOL_GPL(zynqmp_pm_is_function_supported); > > +/** > + * zynqmp_pm_invoke_fw_fn() - Invoke the system-level platform management layer > + * caller function depending on the configuration > + * @pm_api_id: Requested PM-API call > + * @ret_payload: Returned value array > + * @num_args: Number of arguments to requested PM-API call > + * > + * Invoke platform management function for SMC or HVC call, depending on > + * configuration. > + * Following SMC Calling Convention (SMCCC) for SMC64: > + * Pm Function Identifier, > + * PM_SIP_SVC + PASS_THROUGH_FW_CMD_ID = > + * ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT) > + * ((SMC_64) << FUNCID_CC_SHIFT) > + * ((SIP_START) << FUNCID_OEN_SHIFT) > + * (PASS_THROUGH_FW_CMD_ID)) > + * > + * PM_SIP_SVC - Registered ZynqMP SIP Service Call. > + * PASS_THROUGH_FW_CMD_ID - Fixed SiP SVC call ID for FW specific calls. > + * > + * Return: Returns status, either success or error+reason > + */ > +int zynqmp_pm_invoke_fw_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...) > +{ > + /* > + * Added SIP service call Function Identifier > + * Make sure to stay in x0 register > + */ > + u64 smc_arg[SMC_ARG_CNT_64]; > + int ret, i; > + va_list arg_list; > + u32 args[SMC_ARG_CNT_32] = {0}; > + u32 module_id; > + > + if (num_args > SMC_ARG_CNT_32) > + return -EINVAL; > + > + va_start(arg_list, num_args); > + > + /* Check if feature is supported or not */ > + ret = zynqmp_pm_feature(pm_api_id); > + if (ret < 0) > + return ret; > + > + for (i = 0; i < num_args; i++) > + args[i] = va_arg(arg_list, u32); > + > + va_end(arg_list); > + > + module_id = FIELD_GET(PLM_MODULE_ID_MASK, pm_api_id); > + > + if (module_id == 0) > + module_id = XPM_MODULE_ID; > + > + smc_arg[0] = PM_SIP_SVC | PASS_THROUGH_FW_CMD_ID; > + smc_arg[1] = ((u64)args[0] << 32U) | FIELD_PREP(PLM_MODULE_ID_MASK, module_id) | > + (pm_api_id & API_ID_MASK); > + for (i = 1; i < (SMC_ARG_CNT_64 - 1); i++) > + smc_arg[i + 1] = ((u64)args[(i * 2)] << 32U) | args[(i * 2) - 1]; > + > + return do_fw_call(ret_payload, 8, smc_arg[0], smc_arg[1], smc_arg[2], smc_arg[3], > + smc_arg[4], smc_arg[5], smc_arg[6], smc_arg[7]); > +} > + > /** > * zynqmp_pm_invoke_fn() - Invoke the system-level platform management layer > * caller function depending on the configuration > @@ -488,6 +559,35 @@ int zynqmp_pm_get_family_info(u32 *family, u32 *subfamily) > } > EXPORT_SYMBOL_GPL(zynqmp_pm_get_family_info); > > +/** > + * zynqmp_pm_get_sip_svc_version() - Get SiP service call version > + * @version: Returned version value > + * > + * Return: Returns status, either success or error+reason > + */ > +static int zynqmp_pm_get_sip_svc_version(u32 *version) > +{ > + struct arm_smccc_res res; > + u64 args[SMC_ARG_CNT_64] = {0}; > + > + if (!version) > + return -EINVAL; > + > + /* Check if SiP SVC version already verified */ > + if (sip_svc_version > 0) { > + *version = sip_svc_version; > + return 0; > + } > + > + args[0] = GET_SIP_SVC_VERSION; > + > + arm_smccc_smc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], &res); > + > + *version = ((lower_32_bits(res.a0) << 16U) | lower_32_bits(res.a1)); > + > + return zynqmp_pm_ret_code(XST_PM_SUCCESS); > +} > + > /** > * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version > * @version: Returned version value > @@ -552,10 +652,34 @@ static int get_set_conduit_method(struct device_node *np) > */ > int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out) > { > - int ret; > + int ret, i = 0; > + u32 ret_payload[PAYLOAD_ARG_CNT] = {0}; > + > + if (sip_svc_version >= SIP_SVC_PASSTHROUGH_VERSION) { > + ret = zynqmp_pm_invoke_fw_fn(PM_QUERY_DATA, ret_payload, 4, > + qdata.qid, qdata.arg1, > + qdata.arg2, qdata.arg3); > + /* To support backward compatibility */ > + if (!ret && !ret_payload[0]) { > + /* > + * TF-A passes return status on 0th index but > + * api to get clock name reads data from 0th > + * index so pass data at 0th index instead of > + * return status > + */ > + if (qdata.qid == PM_QID_CLOCK_GET_NAME || > + qdata.qid == PM_QID_PINCTRL_GET_FUNCTION_NAME) > + i = 1; > + > + for (; i < PAYLOAD_ARG_CNT; i++, out++) > + *out = ret_payload[i]; > > - ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, out, 4, qdata.qid, qdata.arg1, qdata.arg2, > - qdata.arg3); > + return ret; > + } > + } > + > + ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, out, 4, qdata.qid, > + qdata.arg1, qdata.arg2, qdata.arg3); > > /* > * For clock name query, all bytes in SMC response are clock name > @@ -1887,6 +2011,11 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) > if (ret) > return ret; > > + /* Get SiP SVC version number */ > + ret = zynqmp_pm_get_sip_svc_version(&sip_svc_version); > + if (ret) > + return ret; > + > ret = do_feature_check_call(PM_FEATURE_CHECK); > if (ret >= 0 && ((ret & FIRMWARE_VERSION_MASK) >= PM_API_VERSION_1)) > feature_check_enabled = true; > diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h > index 5b938fc2adad..76d85ad82ec0 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 - 2023, Advanced Micro Devices, Inc. > + * Copyright (C) 2022 - 2024, Advanced Micro Devices, Inc. > * > * Michal Simek <michal.simek@amd.com> > * Davorin Mista <davorin.mista@aggios.com> > @@ -32,6 +32,19 @@ > /* SMC SIP service Call Function Identifier Prefix */ > #define PM_SIP_SVC 0xC2000000 > > +/* SMC function ID to get SiP SVC version */ > +#define GET_SIP_SVC_VERSION (0x8200ff03U) > + > +/* SiP Service Calls version numbers */ > +#define SIP_SVC_VERSION_MAJOR (0U) > +#define SIP_SVC_VERSION_MINOR (2U) > + > +#define SIP_SVC_PASSTHROUGH_VERSION ((SIP_SVC_VERSION_MAJOR << 16) | \ > + SIP_SVC_VERSION_MINOR) > + > +/* Fixed ID for FW specific APIs */ > +#define PASS_THROUGH_FW_CMD_ID GENMASK(11, 0) > + > /* PM API versions */ > #define PM_API_VERSION_1 1 > #define PM_API_VERSION_2 2 > @@ -51,6 +64,7 @@ > > #define API_ID_MASK GENMASK(7, 0) > #define MODULE_ID_MASK GENMASK(11, 8) > +#define PLM_MODULE_ID_MASK GENMASK(15, 8) > > /* Firmware feature check version mask */ > #define FIRMWARE_VERSION_MASK 0xFFFFU > @@ -62,7 +76,13 @@ > #define GET_CALLBACK_DATA 0xa01 > > /* Number of 32bits values in payload */ > -#define PAYLOAD_ARG_CNT 4U > +#define PAYLOAD_ARG_CNT 7U > + > +/* Number of 64bits arguments for SMC call */ > +#define SMC_ARG_CNT_64 8U > + > +/* Number of 32bits arguments for SMC call */ > +#define SMC_ARG_CNT_32 13U > > /* Number of arguments for a callback */ > #define CB_ARG_CNT 4 > @@ -130,6 +150,7 @@ > > enum pm_module_id { > PM_MODULE_ID = 0x0, > + XPM_MODULE_ID = 0x2, > XSEM_MODULE_ID = 0x3, > TF_A_MODULE_ID = 0xa, > }; > @@ -537,6 +558,7 @@ struct zynqmp_pm_query_data { > }; > > int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...); > +int zynqmp_pm_invoke_fw_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...); > > #if IS_REACHABLE(CONFIG_ZYNQMP_FIRMWARE) > int zynqmp_pm_get_api_version(u32 *version); Applied. M
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index c8be32d9c6af..5ab0dfb2b225 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 - 2023, Advanced Micro Devices, Inc. + * Copyright (C) 2022 - 2024, Advanced Micro Devices, Inc. * * Michal Simek <michal.simek@amd.com> * Davorin Mista <davorin.mista@aggios.com> @@ -46,6 +46,7 @@ static DEFINE_HASHTABLE(pm_api_features_map, PM_API_FEATURE_CHECK_MAX_ORDER); static u32 ioctl_features[FEATURE_PAYLOAD_SIZE]; static u32 query_features[FEATURE_PAYLOAD_SIZE]; +static u32 sip_svc_version; static struct platform_device *em_dev; /** @@ -151,6 +152,9 @@ static noinline int do_fw_call_smc(u32 *ret_payload, u32 num_args, ...) ret_payload[1] = upper_32_bits(res.a0); ret_payload[2] = lower_32_bits(res.a1); ret_payload[3] = upper_32_bits(res.a1); + ret_payload[4] = lower_32_bits(res.a2); + ret_payload[5] = upper_32_bits(res.a2); + ret_payload[6] = lower_32_bits(res.a3); } return zynqmp_pm_ret_code((enum pm_ret_status)res.a0); @@ -191,6 +195,9 @@ static noinline int do_fw_call_hvc(u32 *ret_payload, u32 num_args, ...) ret_payload[1] = upper_32_bits(res.a0); ret_payload[2] = lower_32_bits(res.a1); ret_payload[3] = upper_32_bits(res.a1); + ret_payload[4] = lower_32_bits(res.a2); + ret_payload[5] = upper_32_bits(res.a2); + ret_payload[6] = lower_32_bits(res.a3); } return zynqmp_pm_ret_code((enum pm_ret_status)res.a0); @@ -331,6 +338,70 @@ int zynqmp_pm_is_function_supported(const u32 api_id, const u32 id) } EXPORT_SYMBOL_GPL(zynqmp_pm_is_function_supported); +/** + * zynqmp_pm_invoke_fw_fn() - Invoke the system-level platform management layer + * caller function depending on the configuration + * @pm_api_id: Requested PM-API call + * @ret_payload: Returned value array + * @num_args: Number of arguments to requested PM-API call + * + * Invoke platform management function for SMC or HVC call, depending on + * configuration. + * Following SMC Calling Convention (SMCCC) for SMC64: + * Pm Function Identifier, + * PM_SIP_SVC + PASS_THROUGH_FW_CMD_ID = + * ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT) + * ((SMC_64) << FUNCID_CC_SHIFT) + * ((SIP_START) << FUNCID_OEN_SHIFT) + * (PASS_THROUGH_FW_CMD_ID)) + * + * PM_SIP_SVC - Registered ZynqMP SIP Service Call. + * PASS_THROUGH_FW_CMD_ID - Fixed SiP SVC call ID for FW specific calls. + * + * Return: Returns status, either success or error+reason + */ +int zynqmp_pm_invoke_fw_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...) +{ + /* + * Added SIP service call Function Identifier + * Make sure to stay in x0 register + */ + u64 smc_arg[SMC_ARG_CNT_64]; + int ret, i; + va_list arg_list; + u32 args[SMC_ARG_CNT_32] = {0}; + u32 module_id; + + if (num_args > SMC_ARG_CNT_32) + return -EINVAL; + + va_start(arg_list, num_args); + + /* Check if feature is supported or not */ + ret = zynqmp_pm_feature(pm_api_id); + if (ret < 0) + return ret; + + for (i = 0; i < num_args; i++) + args[i] = va_arg(arg_list, u32); + + va_end(arg_list); + + module_id = FIELD_GET(PLM_MODULE_ID_MASK, pm_api_id); + + if (module_id == 0) + module_id = XPM_MODULE_ID; + + smc_arg[0] = PM_SIP_SVC | PASS_THROUGH_FW_CMD_ID; + smc_arg[1] = ((u64)args[0] << 32U) | FIELD_PREP(PLM_MODULE_ID_MASK, module_id) | + (pm_api_id & API_ID_MASK); + for (i = 1; i < (SMC_ARG_CNT_64 - 1); i++) + smc_arg[i + 1] = ((u64)args[(i * 2)] << 32U) | args[(i * 2) - 1]; + + return do_fw_call(ret_payload, 8, smc_arg[0], smc_arg[1], smc_arg[2], smc_arg[3], + smc_arg[4], smc_arg[5], smc_arg[6], smc_arg[7]); +} + /** * zynqmp_pm_invoke_fn() - Invoke the system-level platform management layer * caller function depending on the configuration @@ -488,6 +559,35 @@ int zynqmp_pm_get_family_info(u32 *family, u32 *subfamily) } EXPORT_SYMBOL_GPL(zynqmp_pm_get_family_info); +/** + * zynqmp_pm_get_sip_svc_version() - Get SiP service call version + * @version: Returned version value + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_get_sip_svc_version(u32 *version) +{ + struct arm_smccc_res res; + u64 args[SMC_ARG_CNT_64] = {0}; + + if (!version) + return -EINVAL; + + /* Check if SiP SVC version already verified */ + if (sip_svc_version > 0) { + *version = sip_svc_version; + return 0; + } + + args[0] = GET_SIP_SVC_VERSION; + + arm_smccc_smc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], &res); + + *version = ((lower_32_bits(res.a0) << 16U) | lower_32_bits(res.a1)); + + return zynqmp_pm_ret_code(XST_PM_SUCCESS); +} + /** * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version * @version: Returned version value @@ -552,10 +652,34 @@ static int get_set_conduit_method(struct device_node *np) */ int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out) { - int ret; + int ret, i = 0; + u32 ret_payload[PAYLOAD_ARG_CNT] = {0}; + + if (sip_svc_version >= SIP_SVC_PASSTHROUGH_VERSION) { + ret = zynqmp_pm_invoke_fw_fn(PM_QUERY_DATA, ret_payload, 4, + qdata.qid, qdata.arg1, + qdata.arg2, qdata.arg3); + /* To support backward compatibility */ + if (!ret && !ret_payload[0]) { + /* + * TF-A passes return status on 0th index but + * api to get clock name reads data from 0th + * index so pass data at 0th index instead of + * return status + */ + if (qdata.qid == PM_QID_CLOCK_GET_NAME || + qdata.qid == PM_QID_PINCTRL_GET_FUNCTION_NAME) + i = 1; + + for (; i < PAYLOAD_ARG_CNT; i++, out++) + *out = ret_payload[i]; - ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, out, 4, qdata.qid, qdata.arg1, qdata.arg2, - qdata.arg3); + return ret; + } + } + + ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, out, 4, qdata.qid, + qdata.arg1, qdata.arg2, qdata.arg3); /* * For clock name query, all bytes in SMC response are clock name @@ -1887,6 +2011,11 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) if (ret) return ret; + /* Get SiP SVC version number */ + ret = zynqmp_pm_get_sip_svc_version(&sip_svc_version); + if (ret) + return ret; + ret = do_feature_check_call(PM_FEATURE_CHECK); if (ret >= 0 && ((ret & FIRMWARE_VERSION_MASK) >= PM_API_VERSION_1)) feature_check_enabled = true; diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 5b938fc2adad..76d85ad82ec0 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 - 2023, Advanced Micro Devices, Inc. + * Copyright (C) 2022 - 2024, Advanced Micro Devices, Inc. * * Michal Simek <michal.simek@amd.com> * Davorin Mista <davorin.mista@aggios.com> @@ -32,6 +32,19 @@ /* SMC SIP service Call Function Identifier Prefix */ #define PM_SIP_SVC 0xC2000000 +/* SMC function ID to get SiP SVC version */ +#define GET_SIP_SVC_VERSION (0x8200ff03U) + +/* SiP Service Calls version numbers */ +#define SIP_SVC_VERSION_MAJOR (0U) +#define SIP_SVC_VERSION_MINOR (2U) + +#define SIP_SVC_PASSTHROUGH_VERSION ((SIP_SVC_VERSION_MAJOR << 16) | \ + SIP_SVC_VERSION_MINOR) + +/* Fixed ID for FW specific APIs */ +#define PASS_THROUGH_FW_CMD_ID GENMASK(11, 0) + /* PM API versions */ #define PM_API_VERSION_1 1 #define PM_API_VERSION_2 2 @@ -51,6 +64,7 @@ #define API_ID_MASK GENMASK(7, 0) #define MODULE_ID_MASK GENMASK(11, 8) +#define PLM_MODULE_ID_MASK GENMASK(15, 8) /* Firmware feature check version mask */ #define FIRMWARE_VERSION_MASK 0xFFFFU @@ -62,7 +76,13 @@ #define GET_CALLBACK_DATA 0xa01 /* Number of 32bits values in payload */ -#define PAYLOAD_ARG_CNT 4U +#define PAYLOAD_ARG_CNT 7U + +/* Number of 64bits arguments for SMC call */ +#define SMC_ARG_CNT_64 8U + +/* Number of 32bits arguments for SMC call */ +#define SMC_ARG_CNT_32 13U /* Number of arguments for a callback */ #define CB_ARG_CNT 4 @@ -130,6 +150,7 @@ enum pm_module_id { PM_MODULE_ID = 0x0, + XPM_MODULE_ID = 0x2, XSEM_MODULE_ID = 0x3, TF_A_MODULE_ID = 0xa, }; @@ -537,6 +558,7 @@ struct zynqmp_pm_query_data { }; int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...); +int zynqmp_pm_invoke_fw_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...); #if IS_REACHABLE(CONFIG_ZYNQMP_FIRMWARE) int zynqmp_pm_get_api_version(u32 *version);