Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firmware: xilinx: add support for new SMC call format

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>
Link: https://lore.kernel.org/r/20240920055501.2658642-1-ronak.jain@amd.com
Signed-off-by: Michal Simek <michal.simek@amd.com>

authored by

Ronak Jain and committed by
Michal Simek
92fb7133 e8415a8a

+157 -6
+133 -4
drivers/firmware/xilinx/zynqmp.c
··· 3 3 * Xilinx Zynq MPSoC Firmware layer 4 4 * 5 5 * Copyright (C) 2014-2022 Xilinx, Inc. 6 - * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc. 6 + * Copyright (C) 2022 - 2024, Advanced Micro Devices, Inc. 7 7 * 8 8 * Michal Simek <michal.simek@amd.com> 9 9 * Davorin Mista <davorin.mista@aggios.com> ··· 46 46 static u32 ioctl_features[FEATURE_PAYLOAD_SIZE]; 47 47 static u32 query_features[FEATURE_PAYLOAD_SIZE]; 48 48 49 + static u32 sip_svc_version; 49 50 static struct platform_device *em_dev; 50 51 51 52 /** ··· 152 151 ret_payload[1] = upper_32_bits(res.a0); 153 152 ret_payload[2] = lower_32_bits(res.a1); 154 153 ret_payload[3] = upper_32_bits(res.a1); 154 + ret_payload[4] = lower_32_bits(res.a2); 155 + ret_payload[5] = upper_32_bits(res.a2); 156 + ret_payload[6] = lower_32_bits(res.a3); 155 157 } 156 158 157 159 return zynqmp_pm_ret_code((enum pm_ret_status)res.a0); ··· 195 191 ret_payload[1] = upper_32_bits(res.a0); 196 192 ret_payload[2] = lower_32_bits(res.a1); 197 193 ret_payload[3] = upper_32_bits(res.a1); 194 + ret_payload[4] = lower_32_bits(res.a2); 195 + ret_payload[5] = upper_32_bits(res.a2); 196 + ret_payload[6] = lower_32_bits(res.a3); 198 197 } 199 198 200 199 return zynqmp_pm_ret_code((enum pm_ret_status)res.a0); ··· 337 330 return 0; 338 331 } 339 332 EXPORT_SYMBOL_GPL(zynqmp_pm_is_function_supported); 333 + 334 + /** 335 + * zynqmp_pm_invoke_fw_fn() - Invoke the system-level platform management layer 336 + * caller function depending on the configuration 337 + * @pm_api_id: Requested PM-API call 338 + * @ret_payload: Returned value array 339 + * @num_args: Number of arguments to requested PM-API call 340 + * 341 + * Invoke platform management function for SMC or HVC call, depending on 342 + * configuration. 343 + * Following SMC Calling Convention (SMCCC) for SMC64: 344 + * Pm Function Identifier, 345 + * PM_SIP_SVC + PASS_THROUGH_FW_CMD_ID = 346 + * ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT) 347 + * ((SMC_64) << FUNCID_CC_SHIFT) 348 + * ((SIP_START) << FUNCID_OEN_SHIFT) 349 + * (PASS_THROUGH_FW_CMD_ID)) 350 + * 351 + * PM_SIP_SVC - Registered ZynqMP SIP Service Call. 352 + * PASS_THROUGH_FW_CMD_ID - Fixed SiP SVC call ID for FW specific calls. 353 + * 354 + * Return: Returns status, either success or error+reason 355 + */ 356 + int zynqmp_pm_invoke_fw_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...) 357 + { 358 + /* 359 + * Added SIP service call Function Identifier 360 + * Make sure to stay in x0 register 361 + */ 362 + u64 smc_arg[SMC_ARG_CNT_64]; 363 + int ret, i; 364 + va_list arg_list; 365 + u32 args[SMC_ARG_CNT_32] = {0}; 366 + u32 module_id; 367 + 368 + if (num_args > SMC_ARG_CNT_32) 369 + return -EINVAL; 370 + 371 + va_start(arg_list, num_args); 372 + 373 + /* Check if feature is supported or not */ 374 + ret = zynqmp_pm_feature(pm_api_id); 375 + if (ret < 0) 376 + return ret; 377 + 378 + for (i = 0; i < num_args; i++) 379 + args[i] = va_arg(arg_list, u32); 380 + 381 + va_end(arg_list); 382 + 383 + module_id = FIELD_GET(PLM_MODULE_ID_MASK, pm_api_id); 384 + 385 + if (module_id == 0) 386 + module_id = XPM_MODULE_ID; 387 + 388 + smc_arg[0] = PM_SIP_SVC | PASS_THROUGH_FW_CMD_ID; 389 + smc_arg[1] = ((u64)args[0] << 32U) | FIELD_PREP(PLM_MODULE_ID_MASK, module_id) | 390 + (pm_api_id & API_ID_MASK); 391 + for (i = 1; i < (SMC_ARG_CNT_64 - 1); i++) 392 + smc_arg[i + 1] = ((u64)args[(i * 2)] << 32U) | args[(i * 2) - 1]; 393 + 394 + return do_fw_call(ret_payload, 8, smc_arg[0], smc_arg[1], smc_arg[2], smc_arg[3], 395 + smc_arg[4], smc_arg[5], smc_arg[6], smc_arg[7]); 396 + } 340 397 341 398 /** 342 399 * zynqmp_pm_invoke_fn() - Invoke the system-level platform management layer ··· 560 489 EXPORT_SYMBOL_GPL(zynqmp_pm_get_family_info); 561 490 562 491 /** 492 + * zynqmp_pm_get_sip_svc_version() - Get SiP service call version 493 + * @version: Returned version value 494 + * 495 + * Return: Returns status, either success or error+reason 496 + */ 497 + static int zynqmp_pm_get_sip_svc_version(u32 *version) 498 + { 499 + struct arm_smccc_res res; 500 + u64 args[SMC_ARG_CNT_64] = {0}; 501 + 502 + if (!version) 503 + return -EINVAL; 504 + 505 + /* Check if SiP SVC version already verified */ 506 + if (sip_svc_version > 0) { 507 + *version = sip_svc_version; 508 + return 0; 509 + } 510 + 511 + args[0] = GET_SIP_SVC_VERSION; 512 + 513 + arm_smccc_smc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], &res); 514 + 515 + *version = ((lower_32_bits(res.a0) << 16U) | lower_32_bits(res.a1)); 516 + 517 + return zynqmp_pm_ret_code(XST_PM_SUCCESS); 518 + } 519 + 520 + /** 563 521 * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version 564 522 * @version: Returned version value 565 523 * ··· 652 552 */ 653 553 int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out) 654 554 { 655 - int ret; 555 + int ret, i = 0; 556 + u32 ret_payload[PAYLOAD_ARG_CNT] = {0}; 656 557 657 - ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, out, 4, qdata.qid, qdata.arg1, qdata.arg2, 658 - qdata.arg3); 558 + if (sip_svc_version >= SIP_SVC_PASSTHROUGH_VERSION) { 559 + ret = zynqmp_pm_invoke_fw_fn(PM_QUERY_DATA, ret_payload, 4, 560 + qdata.qid, qdata.arg1, 561 + qdata.arg2, qdata.arg3); 562 + /* To support backward compatibility */ 563 + if (!ret && !ret_payload[0]) { 564 + /* 565 + * TF-A passes return status on 0th index but 566 + * api to get clock name reads data from 0th 567 + * index so pass data at 0th index instead of 568 + * return status 569 + */ 570 + if (qdata.qid == PM_QID_CLOCK_GET_NAME || 571 + qdata.qid == PM_QID_PINCTRL_GET_FUNCTION_NAME) 572 + i = 1; 573 + 574 + for (; i < PAYLOAD_ARG_CNT; i++, out++) 575 + *out = ret_payload[i]; 576 + 577 + return ret; 578 + } 579 + } 580 + 581 + ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, out, 4, qdata.qid, 582 + qdata.arg1, qdata.arg2, qdata.arg3); 659 583 660 584 /* 661 585 * For clock name query, all bytes in SMC response are clock name ··· 2011 1887 int ret; 2012 1888 2013 1889 ret = get_set_conduit_method(dev->of_node); 1890 + if (ret) 1891 + return ret; 1892 + 1893 + /* Get SiP SVC version number */ 1894 + ret = zynqmp_pm_get_sip_svc_version(&sip_svc_version); 2014 1895 if (ret) 2015 1896 return ret; 2016 1897
+24 -2
include/linux/firmware/xlnx-zynqmp.h
··· 3 3 * Xilinx Zynq MPSoC Firmware layer 4 4 * 5 5 * Copyright (C) 2014-2021 Xilinx 6 - * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc. 6 + * Copyright (C) 2022 - 2024, Advanced Micro Devices, Inc. 7 7 * 8 8 * Michal Simek <michal.simek@amd.com> 9 9 * Davorin Mista <davorin.mista@aggios.com> ··· 32 32 /* SMC SIP service Call Function Identifier Prefix */ 33 33 #define PM_SIP_SVC 0xC2000000 34 34 35 + /* SMC function ID to get SiP SVC version */ 36 + #define GET_SIP_SVC_VERSION (0x8200ff03U) 37 + 38 + /* SiP Service Calls version numbers */ 39 + #define SIP_SVC_VERSION_MAJOR (0U) 40 + #define SIP_SVC_VERSION_MINOR (2U) 41 + 42 + #define SIP_SVC_PASSTHROUGH_VERSION ((SIP_SVC_VERSION_MAJOR << 16) | \ 43 + SIP_SVC_VERSION_MINOR) 44 + 45 + /* Fixed ID for FW specific APIs */ 46 + #define PASS_THROUGH_FW_CMD_ID GENMASK(11, 0) 47 + 35 48 /* PM API versions */ 36 49 #define PM_API_VERSION_1 1 37 50 #define PM_API_VERSION_2 2 ··· 64 51 65 52 #define API_ID_MASK GENMASK(7, 0) 66 53 #define MODULE_ID_MASK GENMASK(11, 8) 54 + #define PLM_MODULE_ID_MASK GENMASK(15, 8) 67 55 68 56 /* Firmware feature check version mask */ 69 57 #define FIRMWARE_VERSION_MASK 0xFFFFU ··· 76 62 #define GET_CALLBACK_DATA 0xa01 77 63 78 64 /* Number of 32bits values in payload */ 79 - #define PAYLOAD_ARG_CNT 4U 65 + #define PAYLOAD_ARG_CNT 7U 66 + 67 + /* Number of 64bits arguments for SMC call */ 68 + #define SMC_ARG_CNT_64 8U 69 + 70 + /* Number of 32bits arguments for SMC call */ 71 + #define SMC_ARG_CNT_32 13U 80 72 81 73 /* Number of arguments for a callback */ 82 74 #define CB_ARG_CNT 4 ··· 150 130 151 131 enum pm_module_id { 152 132 PM_MODULE_ID = 0x0, 133 + XPM_MODULE_ID = 0x2, 153 134 XSEM_MODULE_ID = 0x3, 154 135 TF_A_MODULE_ID = 0xa, 155 136 }; ··· 558 537 }; 559 538 560 539 int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...); 540 + int zynqmp_pm_invoke_fw_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...); 561 541 562 542 #if IS_REACHABLE(CONFIG_ZYNQMP_FIRMWARE) 563 543 int zynqmp_pm_get_api_version(u32 *version);