|
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 <[email protected]>
|
9 | 9 | * Davorin Mista <[email protected]>
|
@@ -46,6 +46,7 @@ static DEFINE_HASHTABLE(pm_api_features_map, PM_API_FEATURE_CHECK_MAX_ORDER);
|
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 | /**
|
@@ -151,6 +152,9 @@ static noinline int do_fw_call_smc(u32 *ret_payload, u32 num_args, ...)
|
151 | 152 | ret_payload[1] = upper_32_bits(res.a0);
|
152 | 153 | ret_payload[2] = lower_32_bits(res.a1);
|
153 | 154 | ret_payload[3] = upper_32_bits(res.a1);
|
| 155 | + ret_payload[4] = lower_32_bits(res.a2); |
| 156 | + ret_payload[5] = upper_32_bits(res.a2); |
| 157 | + ret_payload[6] = lower_32_bits(res.a3); |
154 | 158 | }
|
155 | 159 |
|
156 | 160 | 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, ...)
|
191 | 195 | ret_payload[1] = upper_32_bits(res.a0);
|
192 | 196 | ret_payload[2] = lower_32_bits(res.a1);
|
193 | 197 | ret_payload[3] = upper_32_bits(res.a1);
|
| 198 | + ret_payload[4] = lower_32_bits(res.a2); |
| 199 | + ret_payload[5] = upper_32_bits(res.a2); |
| 200 | + ret_payload[6] = lower_32_bits(res.a3); |
194 | 201 | }
|
195 | 202 |
|
196 | 203 | 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)
|
331 | 338 | }
|
332 | 339 | EXPORT_SYMBOL_GPL(zynqmp_pm_is_function_supported);
|
333 | 340 |
|
| 341 | +/** |
| 342 | + * zynqmp_pm_invoke_fw_fn() - Invoke the system-level platform management layer |
| 343 | + * caller function depending on the configuration |
| 344 | + * @pm_api_id: Requested PM-API call |
| 345 | + * @ret_payload: Returned value array |
| 346 | + * @num_args: Number of arguments to requested PM-API call |
| 347 | + * |
| 348 | + * Invoke platform management function for SMC or HVC call, depending on |
| 349 | + * configuration. |
| 350 | + * Following SMC Calling Convention (SMCCC) for SMC64: |
| 351 | + * Pm Function Identifier, |
| 352 | + * PM_SIP_SVC + PASS_THROUGH_FW_CMD_ID = |
| 353 | + * ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT) |
| 354 | + * ((SMC_64) << FUNCID_CC_SHIFT) |
| 355 | + * ((SIP_START) << FUNCID_OEN_SHIFT) |
| 356 | + * (PASS_THROUGH_FW_CMD_ID)) |
| 357 | + * |
| 358 | + * PM_SIP_SVC - Registered ZynqMP SIP Service Call. |
| 359 | + * PASS_THROUGH_FW_CMD_ID - Fixed SiP SVC call ID for FW specific calls. |
| 360 | + * |
| 361 | + * Return: Returns status, either success or error+reason |
| 362 | + */ |
| 363 | +int zynqmp_pm_invoke_fw_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...) |
| 364 | +{ |
| 365 | + /* |
| 366 | + * Added SIP service call Function Identifier |
| 367 | + * Make sure to stay in x0 register |
| 368 | + */ |
| 369 | + u64 smc_arg[SMC_ARG_CNT_64]; |
| 370 | + int ret, i; |
| 371 | + va_list arg_list; |
| 372 | + u32 args[SMC_ARG_CNT_32] = {0}; |
| 373 | + u32 module_id; |
| 374 | + |
| 375 | + if (num_args > SMC_ARG_CNT_32) |
| 376 | + return -EINVAL; |
| 377 | + |
| 378 | + va_start(arg_list, num_args); |
| 379 | + |
| 380 | + /* Check if feature is supported or not */ |
| 381 | + ret = zynqmp_pm_feature(pm_api_id); |
| 382 | + if (ret < 0) |
| 383 | + return ret; |
| 384 | + |
| 385 | + for (i = 0; i < num_args; i++) |
| 386 | + args[i] = va_arg(arg_list, u32); |
| 387 | + |
| 388 | + va_end(arg_list); |
| 389 | + |
| 390 | + module_id = FIELD_GET(PLM_MODULE_ID_MASK, pm_api_id); |
| 391 | + |
| 392 | + if (module_id == 0) |
| 393 | + module_id = XPM_MODULE_ID; |
| 394 | + |
| 395 | + smc_arg[0] = PM_SIP_SVC | PASS_THROUGH_FW_CMD_ID; |
| 396 | + smc_arg[1] = ((u64)args[0] << 32U) | FIELD_PREP(PLM_MODULE_ID_MASK, module_id) | |
| 397 | + (pm_api_id & API_ID_MASK); |
| 398 | + for (i = 1; i < (SMC_ARG_CNT_64 - 1); i++) |
| 399 | + smc_arg[i + 1] = ((u64)args[(i * 2)] << 32U) | args[(i * 2) - 1]; |
| 400 | + |
| 401 | + return do_fw_call(ret_payload, 8, smc_arg[0], smc_arg[1], smc_arg[2], smc_arg[3], |
| 402 | + smc_arg[4], smc_arg[5], smc_arg[6], smc_arg[7]); |
| 403 | +} |
| 404 | + |
334 | 405 | /**
|
335 | 406 | * zynqmp_pm_invoke_fn() - Invoke the system-level platform management layer
|
336 | 407 | * caller function depending on the configuration
|
@@ -488,6 +559,35 @@ int zynqmp_pm_get_family_info(u32 *family, u32 *subfamily)
|
488 | 559 | }
|
489 | 560 | EXPORT_SYMBOL_GPL(zynqmp_pm_get_family_info);
|
490 | 561 |
|
| 562 | +/** |
| 563 | + * zynqmp_pm_get_sip_svc_version() - Get SiP service call version |
| 564 | + * @version: Returned version value |
| 565 | + * |
| 566 | + * Return: Returns status, either success or error+reason |
| 567 | + */ |
| 568 | +static int zynqmp_pm_get_sip_svc_version(u32 *version) |
| 569 | +{ |
| 570 | + struct arm_smccc_res res; |
| 571 | + u64 args[SMC_ARG_CNT_64] = {0}; |
| 572 | + |
| 573 | + if (!version) |
| 574 | + return -EINVAL; |
| 575 | + |
| 576 | + /* Check if SiP SVC version already verified */ |
| 577 | + if (sip_svc_version > 0) { |
| 578 | + *version = sip_svc_version; |
| 579 | + return 0; |
| 580 | + } |
| 581 | + |
| 582 | + args[0] = GET_SIP_SVC_VERSION; |
| 583 | + |
| 584 | + arm_smccc_smc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], &res); |
| 585 | + |
| 586 | + *version = ((lower_32_bits(res.a0) << 16U) | lower_32_bits(res.a1)); |
| 587 | + |
| 588 | + return zynqmp_pm_ret_code(XST_PM_SUCCESS); |
| 589 | +} |
| 590 | + |
491 | 591 | /**
|
492 | 592 | * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version
|
493 | 593 | * @version: Returned version value
|
@@ -552,10 +652,34 @@ static int get_set_conduit_method(struct device_node *np)
|
552 | 652 | */
|
553 | 653 | int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out)
|
554 | 654 | {
|
555 |
| - int ret; |
| 655 | + int ret, i = 0; |
| 656 | + u32 ret_payload[PAYLOAD_ARG_CNT] = {0}; |
| 657 | + |
| 658 | + if (sip_svc_version >= SIP_SVC_PASSTHROUGH_VERSION) { |
| 659 | + ret = zynqmp_pm_invoke_fw_fn(PM_QUERY_DATA, ret_payload, 4, |
| 660 | + qdata.qid, qdata.arg1, |
| 661 | + qdata.arg2, qdata.arg3); |
| 662 | + /* To support backward compatibility */ |
| 663 | + if (!ret && !ret_payload[0]) { |
| 664 | + /* |
| 665 | + * TF-A passes return status on 0th index but |
| 666 | + * api to get clock name reads data from 0th |
| 667 | + * index so pass data at 0th index instead of |
| 668 | + * return status |
| 669 | + */ |
| 670 | + if (qdata.qid == PM_QID_CLOCK_GET_NAME || |
| 671 | + qdata.qid == PM_QID_PINCTRL_GET_FUNCTION_NAME) |
| 672 | + i = 1; |
| 673 | + |
| 674 | + for (; i < PAYLOAD_ARG_CNT; i++, out++) |
| 675 | + *out = ret_payload[i]; |
556 | 676 |
|
557 |
| - ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, out, 4, qdata.qid, qdata.arg1, qdata.arg2, |
558 |
| - qdata.arg3); |
| 677 | + return ret; |
| 678 | + } |
| 679 | + } |
| 680 | + |
| 681 | + ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, out, 4, qdata.qid, |
| 682 | + qdata.arg1, qdata.arg2, qdata.arg3); |
559 | 683 |
|
560 | 684 | /*
|
561 | 685 | * For clock name query, all bytes in SMC response are clock name
|
@@ -1890,6 +2014,11 @@ static int zynqmp_firmware_probe(struct platform_device *pdev)
|
1890 | 2014 | if (ret)
|
1891 | 2015 | return ret;
|
1892 | 2016 |
|
| 2017 | + /* Get SiP SVC version number */ |
| 2018 | + ret = zynqmp_pm_get_sip_svc_version(&sip_svc_version); |
| 2019 | + if (ret) |
| 2020 | + return ret; |
| 2021 | + |
1893 | 2022 | ret = do_feature_check_call(PM_FEATURE_CHECK);
|
1894 | 2023 | if (ret >= 0 && ((ret & FIRMWARE_VERSION_MASK) >= PM_API_VERSION_1))
|
1895 | 2024 | feature_check_enabled = true;
|
|
0 commit comments