// SPDX-License-Identifier: GPL-2.01/*2* Xilinx Zynq MPSoC Firmware layer3*4* Copyright (C) 2014-2022 Xilinx, Inc.5* Copyright (C) 2022 - 2025 Advanced Micro Devices, Inc.6*7* Michal Simek <[email protected]>8* Davorin Mista <[email protected]>9* Jolly Shah <[email protected]>10* Rajan Vaja <[email protected]>11*/1213#include <linux/arm-smccc.h>14#include <linux/compiler.h>15#include <linux/device.h>16#include <linux/init.h>17#include <linux/mfd/core.h>18#include <linux/module.h>19#include <linux/of.h>20#include <linux/of_platform.h>21#include <linux/platform_device.h>22#include <linux/pm_domain.h>23#include <linux/slab.h>24#include <linux/uaccess.h>25#include <linux/hashtable.h>2627#include <linux/firmware/xlnx-zynqmp.h>28#include <linux/firmware/xlnx-event-manager.h>29#include "zynqmp-debug.h"3031/* Max HashMap Order for PM API feature check (1<<7 = 128) */32#define PM_API_FEATURE_CHECK_MAX_ORDER 73334/* CRL registers and bitfields */35#define CRL_APB_BASE 0xFF5E0000U36/* BOOT_PIN_CTRL- Used to control the mode pins after boot */37#define CRL_APB_BOOT_PIN_CTRL (CRL_APB_BASE + (0x250U))38/* BOOT_PIN_CTRL_MASK- out_val[11:8], out_en[3:0] */39#define CRL_APB_BOOTPIN_CTRL_MASK 0xF0FU4041/* IOCTL/QUERY feature payload size */42#define FEATURE_PAYLOAD_SIZE 24344static bool feature_check_enabled;45static DEFINE_HASHTABLE(pm_api_features_map, PM_API_FEATURE_CHECK_MAX_ORDER);46static u32 ioctl_features[FEATURE_PAYLOAD_SIZE];47static u32 query_features[FEATURE_PAYLOAD_SIZE];4849static u32 sip_svc_version;50static struct platform_device *em_dev;5152/**53* struct zynqmp_devinfo - Structure for Zynqmp device instance54* @dev: Device Pointer55* @feature_conf_id: Feature conf id56*/57struct zynqmp_devinfo {58struct device *dev;59u32 feature_conf_id;60};6162/**63* struct pm_api_feature_data - PM API Feature data64* @pm_api_id: PM API Id, used as key to index into hashmap65* @feature_status: status of PM API feature: valid, invalid66* @hentry: hlist_node that hooks this entry into hashtable67*/68struct pm_api_feature_data {69u32 pm_api_id;70int feature_status;71struct hlist_node hentry;72};7374struct platform_fw_data {75/*76* Family code for platform.77*/78const u32 family_code;79};8081static struct platform_fw_data *active_platform_fw_data;8283static const struct mfd_cell firmware_devs[] = {84{85.name = "zynqmp_power_controller",86},87};8889/**90* zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes91* @ret_status: PMUFW return code92*93* Return: corresponding Linux error code94*/95static int zynqmp_pm_ret_code(u32 ret_status)96{97switch (ret_status) {98case XST_PM_SUCCESS:99case XST_PM_DOUBLE_REQ:100return 0;101case XST_PM_NO_FEATURE:102return -ENOTSUPP;103case XST_PM_INVALID_VERSION:104return -EOPNOTSUPP;105case XST_PM_NO_ACCESS:106return -EACCES;107case XST_PM_ABORT_SUSPEND:108return -ECANCELED;109case XST_PM_MULT_USER:110return -EUSERS;111case XST_PM_INTERNAL:112case XST_PM_CONFLICT:113case XST_PM_INVALID_NODE:114case XST_PM_INVALID_CRC:115default:116return -EINVAL;117}118}119120static noinline int do_fw_call_fail(u32 *ret_payload, u32 num_args, ...)121{122return -ENODEV;123}124125/*126* PM function call wrapper127* Invoke do_fw_call_smc or do_fw_call_hvc, depending on the configuration128*/129static int (*do_fw_call)(u32 *ret_payload, u32, ...) = do_fw_call_fail;130131/**132* do_fw_call_smc() - Call system-level platform management layer (SMC)133* @num_args: Number of variable arguments should be <= 8134* @ret_payload: Returned value array135*136* Invoke platform management function via SMC call (no hypervisor present).137*138* Return: Returns status, either success or error+reason139*/140static noinline int do_fw_call_smc(u32 *ret_payload, u32 num_args, ...)141{142struct arm_smccc_res res;143u64 args[8] = {0};144va_list arg_list;145u8 i;146147if (num_args > 8)148return -EINVAL;149150va_start(arg_list, num_args);151152for (i = 0; i < num_args; i++)153args[i] = va_arg(arg_list, u64);154155va_end(arg_list);156157arm_smccc_smc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], &res);158159if (ret_payload) {160ret_payload[0] = lower_32_bits(res.a0);161ret_payload[1] = upper_32_bits(res.a0);162ret_payload[2] = lower_32_bits(res.a1);163ret_payload[3] = upper_32_bits(res.a1);164ret_payload[4] = lower_32_bits(res.a2);165ret_payload[5] = upper_32_bits(res.a2);166ret_payload[6] = lower_32_bits(res.a3);167}168169return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);170}171172/**173* do_fw_call_hvc() - Call system-level platform management layer (HVC)174* @num_args: Number of variable arguments should be <= 8175* @ret_payload: Returned value array176*177* Invoke platform management function via HVC178* HVC-based for communication through hypervisor179* (no direct communication with ATF).180*181* Return: Returns status, either success or error+reason182*/183static noinline int do_fw_call_hvc(u32 *ret_payload, u32 num_args, ...)184{185struct arm_smccc_res res;186u64 args[8] = {0};187va_list arg_list;188u8 i;189190if (num_args > 8)191return -EINVAL;192193va_start(arg_list, num_args);194195for (i = 0; i < num_args; i++)196args[i] = va_arg(arg_list, u64);197198va_end(arg_list);199200arm_smccc_hvc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], &res);201202if (ret_payload) {203ret_payload[0] = lower_32_bits(res.a0);204ret_payload[1] = upper_32_bits(res.a0);205ret_payload[2] = lower_32_bits(res.a1);206ret_payload[3] = upper_32_bits(res.a1);207ret_payload[4] = lower_32_bits(res.a2);208ret_payload[5] = upper_32_bits(res.a2);209ret_payload[6] = lower_32_bits(res.a3);210}211212return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);213}214215static int __do_feature_check_call(const u32 api_id, u32 *ret_payload)216{217int ret;218u64 smc_arg[2];219u32 module_id;220u32 feature_check_api_id;221222module_id = FIELD_GET(MODULE_ID_MASK, api_id);223224/*225* Feature check of APIs belonging to PM, XSEM, and TF-A are handled by calling226* PM_FEATURE_CHECK API. For other modules, call PM_API_FEATURES API.227*/228if (module_id == PM_MODULE_ID || module_id == XSEM_MODULE_ID || module_id == TF_A_MODULE_ID)229feature_check_api_id = PM_FEATURE_CHECK;230else231feature_check_api_id = PM_API_FEATURES;232233/*234* Feature check of TF-A APIs is done in the TF-A layer and it expects for235* MODULE_ID_MASK bits of SMC's arg[0] to be the same as PM_MODULE_ID.236*/237if (module_id == TF_A_MODULE_ID) {238module_id = PM_MODULE_ID;239smc_arg[1] = api_id;240} else {241smc_arg[1] = (api_id & API_ID_MASK);242}243244smc_arg[0] = PM_SIP_SVC | FIELD_PREP(MODULE_ID_MASK, module_id) | feature_check_api_id;245246ret = do_fw_call(ret_payload, 2, smc_arg[0], smc_arg[1]);247if (ret)248ret = -EOPNOTSUPP;249else250ret = ret_payload[1];251252return ret;253}254255static int do_feature_check_call(const u32 api_id)256{257int ret;258u32 ret_payload[PAYLOAD_ARG_CNT];259struct pm_api_feature_data *feature_data;260261/* Check for existing entry in hash table for given api */262hash_for_each_possible(pm_api_features_map, feature_data, hentry,263api_id) {264if (feature_data->pm_api_id == api_id)265return feature_data->feature_status;266}267268/* Add new entry if not present */269feature_data = kmalloc(sizeof(*feature_data), GFP_ATOMIC);270if (!feature_data)271return -ENOMEM;272273feature_data->pm_api_id = api_id;274ret = __do_feature_check_call(api_id, ret_payload);275276feature_data->feature_status = ret;277hash_add(pm_api_features_map, &feature_data->hentry, api_id);278279if (api_id == PM_IOCTL)280/* Store supported IOCTL IDs mask */281memcpy(ioctl_features, &ret_payload[2], FEATURE_PAYLOAD_SIZE * 4);282else if (api_id == PM_QUERY_DATA)283/* Store supported QUERY IDs mask */284memcpy(query_features, &ret_payload[2], FEATURE_PAYLOAD_SIZE * 4);285286return ret;287}288289/**290* zynqmp_pm_feature() - Check whether given feature is supported or not and291* store supported IOCTL/QUERY ID mask292* @api_id: API ID to check293*294* Return: Returns status, either success or error+reason295*/296int zynqmp_pm_feature(const u32 api_id)297{298int ret;299300if (!feature_check_enabled)301return 0;302303ret = do_feature_check_call(api_id);304305return ret;306}307EXPORT_SYMBOL_GPL(zynqmp_pm_feature);308309/**310* zynqmp_pm_is_function_supported() - Check whether given IOCTL/QUERY function311* is supported or not312* @api_id: PM_IOCTL or PM_QUERY_DATA313* @id: IOCTL or QUERY function IDs314*315* Return: Returns status, either success or error+reason316*/317int zynqmp_pm_is_function_supported(const u32 api_id, const u32 id)318{319int ret;320u32 *bit_mask;321322/* Input arguments validation */323if (id >= 64 || (api_id != PM_IOCTL && api_id != PM_QUERY_DATA))324return -EINVAL;325326/* Check feature check API version */327ret = do_feature_check_call(PM_FEATURE_CHECK);328if (ret < 0)329return ret;330331/* Check if feature check version 2 is supported or not */332if ((ret & FIRMWARE_VERSION_MASK) == PM_API_VERSION_2) {333/*334* Call feature check for IOCTL/QUERY API to get IOCTL ID or335* QUERY ID feature status.336*/337ret = do_feature_check_call(api_id);338if (ret < 0)339return ret;340341bit_mask = (api_id == PM_IOCTL) ? ioctl_features : query_features;342343if ((bit_mask[(id / 32)] & BIT((id % 32))) == 0U)344return -EOPNOTSUPP;345} else {346return -ENODATA;347}348349return 0;350}351EXPORT_SYMBOL_GPL(zynqmp_pm_is_function_supported);352353/**354* zynqmp_pm_invoke_fw_fn() - Invoke the system-level platform management layer355* caller function depending on the configuration356* @pm_api_id: Requested PM-API call357* @ret_payload: Returned value array358* @num_args: Number of arguments to requested PM-API call359*360* Invoke platform management function for SMC or HVC call, depending on361* configuration.362* Following SMC Calling Convention (SMCCC) for SMC64:363* Pm Function Identifier,364* PM_SIP_SVC + PASS_THROUGH_FW_CMD_ID =365* ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT)366* ((SMC_64) << FUNCID_CC_SHIFT)367* ((SIP_START) << FUNCID_OEN_SHIFT)368* (PASS_THROUGH_FW_CMD_ID))369*370* PM_SIP_SVC - Registered ZynqMP SIP Service Call.371* PASS_THROUGH_FW_CMD_ID - Fixed SiP SVC call ID for FW specific calls.372*373* Return: Returns status, either success or error+reason374*/375int zynqmp_pm_invoke_fw_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...)376{377/*378* Added SIP service call Function Identifier379* Make sure to stay in x0 register380*/381u64 smc_arg[SMC_ARG_CNT_64];382int ret, i;383va_list arg_list;384u32 args[SMC_ARG_CNT_32] = {0};385u32 module_id;386387if (num_args > SMC_ARG_CNT_32)388return -EINVAL;389390va_start(arg_list, num_args);391392/* Check if feature is supported or not */393ret = zynqmp_pm_feature(pm_api_id);394if (ret < 0)395return ret;396397for (i = 0; i < num_args; i++)398args[i] = va_arg(arg_list, u32);399400va_end(arg_list);401402module_id = FIELD_GET(PLM_MODULE_ID_MASK, pm_api_id);403404if (module_id == 0)405module_id = XPM_MODULE_ID;406407smc_arg[0] = PM_SIP_SVC | PASS_THROUGH_FW_CMD_ID;408smc_arg[1] = ((u64)args[0] << 32U) | FIELD_PREP(PLM_MODULE_ID_MASK, module_id) |409(pm_api_id & API_ID_MASK);410for (i = 1; i < (SMC_ARG_CNT_64 - 1); i++)411smc_arg[i + 1] = ((u64)args[(i * 2)] << 32U) | args[(i * 2) - 1];412413return do_fw_call(ret_payload, 8, smc_arg[0], smc_arg[1], smc_arg[2], smc_arg[3],414smc_arg[4], smc_arg[5], smc_arg[6], smc_arg[7]);415}416417/**418* zynqmp_pm_invoke_fn() - Invoke the system-level platform management layer419* caller function depending on the configuration420* @pm_api_id: Requested PM-API call421* @ret_payload: Returned value array422* @num_args: Number of arguments to requested PM-API call423*424* Invoke platform management function for SMC or HVC call, depending on425* configuration.426* Following SMC Calling Convention (SMCCC) for SMC64:427* Pm Function Identifier,428* PM_SIP_SVC + PM_API_ID =429* ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT)430* ((SMC_64) << FUNCID_CC_SHIFT)431* ((SIP_START) << FUNCID_OEN_SHIFT)432* ((PM_API_ID) & FUNCID_NUM_MASK))433*434* PM_SIP_SVC - Registered ZynqMP SIP Service Call.435* PM_API_ID - Platform Management API ID.436*437* Return: Returns status, either success or error+reason438*/439int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...)440{441/*442* Added SIP service call Function Identifier443* Make sure to stay in x0 register444*/445u64 smc_arg[8];446int ret, i;447va_list arg_list;448u32 args[14] = {0};449450if (num_args > 14)451return -EINVAL;452453va_start(arg_list, num_args);454455/* Check if feature is supported or not */456ret = zynqmp_pm_feature(pm_api_id);457if (ret < 0)458return ret;459460for (i = 0; i < num_args; i++)461args[i] = va_arg(arg_list, u32);462463va_end(arg_list);464465smc_arg[0] = PM_SIP_SVC | pm_api_id;466for (i = 0; i < 7; i++)467smc_arg[i + 1] = ((u64)args[(i * 2) + 1] << 32) | args[i * 2];468469return do_fw_call(ret_payload, 8, smc_arg[0], smc_arg[1], smc_arg[2], smc_arg[3],470smc_arg[4], smc_arg[5], smc_arg[6], smc_arg[7]);471}472473static u32 pm_api_version;474static u32 pm_tz_version;475476int zynqmp_pm_register_sgi(u32 sgi_num, u32 reset)477{478int ret;479480ret = zynqmp_pm_invoke_fn(TF_A_PM_REGISTER_SGI, NULL, 2, sgi_num, reset);481if (ret != -EOPNOTSUPP && !ret)482return ret;483484/* try old implementation as fallback strategy if above fails */485return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, IOCTL_REGISTER_SGI, sgi_num, reset);486}487488/**489* zynqmp_pm_get_api_version() - Get version number of PMU PM firmware490* @version: Returned version value491*492* Return: Returns status, either success or error+reason493*/494int zynqmp_pm_get_api_version(u32 *version)495{496u32 ret_payload[PAYLOAD_ARG_CNT];497int ret;498499if (!version)500return -EINVAL;501502/* Check is PM API version already verified */503if (pm_api_version > 0) {504*version = pm_api_version;505return 0;506}507ret = zynqmp_pm_invoke_fn(PM_GET_API_VERSION, ret_payload, 0);508*version = ret_payload[1];509510return ret;511}512EXPORT_SYMBOL_GPL(zynqmp_pm_get_api_version);513514/**515* zynqmp_pm_get_chipid - Get silicon ID registers516* @idcode: IDCODE register517* @version: version register518*519* Return: Returns the status of the operation and the idcode and version520* registers in @idcode and @version.521*/522int zynqmp_pm_get_chipid(u32 *idcode, u32 *version)523{524u32 ret_payload[PAYLOAD_ARG_CNT];525int ret;526527if (!idcode || !version)528return -EINVAL;529530ret = zynqmp_pm_invoke_fn(PM_GET_CHIPID, ret_payload, 0);531*idcode = ret_payload[1];532*version = ret_payload[2];533534return ret;535}536EXPORT_SYMBOL_GPL(zynqmp_pm_get_chipid);537538/**539* zynqmp_pm_get_family_info() - Get family info of platform540* @family: Returned family code value541*542* Return: Returns status, either success or error+reason543*/544int zynqmp_pm_get_family_info(u32 *family)545{546if (!active_platform_fw_data)547return -ENODEV;548549if (!family)550return -EINVAL;551552*family = active_platform_fw_data->family_code;553554return 0;555}556EXPORT_SYMBOL_GPL(zynqmp_pm_get_family_info);557558/**559* zynqmp_pm_get_sip_svc_version() - Get SiP service call version560* @version: Returned version value561*562* Return: Returns status, either success or error+reason563*/564static int zynqmp_pm_get_sip_svc_version(u32 *version)565{566struct arm_smccc_res res;567u64 args[SMC_ARG_CNT_64] = {0};568569if (!version)570return -EINVAL;571572/* Check if SiP SVC version already verified */573if (sip_svc_version > 0) {574*version = sip_svc_version;575return 0;576}577578args[0] = GET_SIP_SVC_VERSION;579580arm_smccc_smc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], &res);581582*version = ((lower_32_bits(res.a0) << 16U) | lower_32_bits(res.a1));583584return zynqmp_pm_ret_code(XST_PM_SUCCESS);585}586587/**588* zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version589* @version: Returned version value590*591* Return: Returns status, either success or error+reason592*/593static int zynqmp_pm_get_trustzone_version(u32 *version)594{595u32 ret_payload[PAYLOAD_ARG_CNT];596int ret;597598if (!version)599return -EINVAL;600601/* Check is PM trustzone version already verified */602if (pm_tz_version > 0) {603*version = pm_tz_version;604return 0;605}606ret = zynqmp_pm_invoke_fn(PM_GET_TRUSTZONE_VERSION, ret_payload, 0);607*version = ret_payload[1];608609return ret;610}611612/**613* get_set_conduit_method() - Choose SMC or HVC based communication614* @np: Pointer to the device_node structure615*616* Use SMC or HVC-based functions to communicate with EL2/EL3.617*618* Return: Returns 0 on success or error code619*/620static int get_set_conduit_method(struct device_node *np)621{622const char *method;623624if (of_property_read_string(np, "method", &method)) {625pr_warn("%s missing \"method\" property\n", __func__);626return -ENXIO;627}628629if (!strcmp("hvc", method)) {630do_fw_call = do_fw_call_hvc;631} else if (!strcmp("smc", method)) {632do_fw_call = do_fw_call_smc;633} else {634pr_warn("%s Invalid \"method\" property: %s\n",635__func__, method);636return -EINVAL;637}638639return 0;640}641642/**643* zynqmp_pm_query_data() - Get query data from firmware644* @qdata: Variable to the zynqmp_pm_query_data structure645* @out: Returned output value646*647* Return: Returns status, either success or error+reason648*/649int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out)650{651int ret, i = 0;652u32 ret_payload[PAYLOAD_ARG_CNT] = {0};653654if (sip_svc_version >= SIP_SVC_PASSTHROUGH_VERSION) {655ret = zynqmp_pm_invoke_fw_fn(PM_QUERY_DATA, ret_payload, 4,656qdata.qid, qdata.arg1,657qdata.arg2, qdata.arg3);658/* To support backward compatibility */659if (!ret && !ret_payload[0]) {660/*661* TF-A passes return status on 0th index but662* api to get clock name reads data from 0th663* index so pass data at 0th index instead of664* return status665*/666if (qdata.qid == PM_QID_CLOCK_GET_NAME ||667qdata.qid == PM_QID_PINCTRL_GET_FUNCTION_NAME)668i = 1;669670for (; i < PAYLOAD_ARG_CNT; i++, out++)671*out = ret_payload[i];672673return ret;674}675}676677ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, out, 4, qdata.qid,678qdata.arg1, qdata.arg2, qdata.arg3);679680/*681* For clock name query, all bytes in SMC response are clock name682* characters and return code is always success. For invalid clocks,683* clock name bytes would be zeros.684*/685return qdata.qid == PM_QID_CLOCK_GET_NAME ? 0 : ret;686}687EXPORT_SYMBOL_GPL(zynqmp_pm_query_data);688689/**690* zynqmp_pm_clock_enable() - Enable the clock for given id691* @clock_id: ID of the clock to be enabled692*693* This function is used by master to enable the clock694* including peripherals and PLL clocks.695*696* Return: Returns status, either success or error+reason697*/698int zynqmp_pm_clock_enable(u32 clock_id)699{700return zynqmp_pm_invoke_fn(PM_CLOCK_ENABLE, NULL, 1, clock_id);701}702EXPORT_SYMBOL_GPL(zynqmp_pm_clock_enable);703704/**705* zynqmp_pm_clock_disable() - Disable the clock for given id706* @clock_id: ID of the clock to be disable707*708* This function is used by master to disable the clock709* including peripherals and PLL clocks.710*711* Return: Returns status, either success or error+reason712*/713int zynqmp_pm_clock_disable(u32 clock_id)714{715return zynqmp_pm_invoke_fn(PM_CLOCK_DISABLE, NULL, 1, clock_id);716}717EXPORT_SYMBOL_GPL(zynqmp_pm_clock_disable);718719/**720* zynqmp_pm_clock_getstate() - Get the clock state for given id721* @clock_id: ID of the clock to be queried722* @state: 1/0 (Enabled/Disabled)723*724* This function is used by master to get the state of clock725* including peripherals and PLL clocks.726*727* Return: Returns status, either success or error+reason728*/729int zynqmp_pm_clock_getstate(u32 clock_id, u32 *state)730{731u32 ret_payload[PAYLOAD_ARG_CNT];732int ret;733734ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETSTATE, ret_payload, 1, clock_id);735*state = ret_payload[1];736737return ret;738}739EXPORT_SYMBOL_GPL(zynqmp_pm_clock_getstate);740741/**742* zynqmp_pm_clock_setdivider() - Set the clock divider for given id743* @clock_id: ID of the clock744* @divider: divider value745*746* This function is used by master to set divider for any clock747* to achieve desired rate.748*749* Return: Returns status, either success or error+reason750*/751int zynqmp_pm_clock_setdivider(u32 clock_id, u32 divider)752{753return zynqmp_pm_invoke_fn(PM_CLOCK_SETDIVIDER, NULL, 2, clock_id, divider);754}755EXPORT_SYMBOL_GPL(zynqmp_pm_clock_setdivider);756757/**758* zynqmp_pm_clock_getdivider() - Get the clock divider for given id759* @clock_id: ID of the clock760* @divider: divider value761*762* This function is used by master to get divider values763* for any clock.764*765* Return: Returns status, either success or error+reason766*/767int zynqmp_pm_clock_getdivider(u32 clock_id, u32 *divider)768{769u32 ret_payload[PAYLOAD_ARG_CNT];770int ret;771772ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETDIVIDER, ret_payload, 1, clock_id);773*divider = ret_payload[1];774775return ret;776}777EXPORT_SYMBOL_GPL(zynqmp_pm_clock_getdivider);778779/**780* zynqmp_pm_clock_setparent() - Set the clock parent for given id781* @clock_id: ID of the clock782* @parent_id: parent id783*784* This function is used by master to set parent for any clock.785*786* Return: Returns status, either success or error+reason787*/788int zynqmp_pm_clock_setparent(u32 clock_id, u32 parent_id)789{790return zynqmp_pm_invoke_fn(PM_CLOCK_SETPARENT, NULL, 2, clock_id, parent_id);791}792EXPORT_SYMBOL_GPL(zynqmp_pm_clock_setparent);793794/**795* zynqmp_pm_clock_getparent() - Get the clock parent for given id796* @clock_id: ID of the clock797* @parent_id: parent id798*799* This function is used by master to get parent index800* for any clock.801*802* Return: Returns status, either success or error+reason803*/804int zynqmp_pm_clock_getparent(u32 clock_id, u32 *parent_id)805{806u32 ret_payload[PAYLOAD_ARG_CNT];807int ret;808809ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETPARENT, ret_payload, 1, clock_id);810*parent_id = ret_payload[1];811812return ret;813}814EXPORT_SYMBOL_GPL(zynqmp_pm_clock_getparent);815816/**817* zynqmp_pm_set_pll_frac_mode() - PM API for set PLL mode818*819* @clk_id: PLL clock ID820* @mode: PLL mode (PLL_MODE_FRAC/PLL_MODE_INT)821*822* This function sets PLL mode823*824* Return: Returns status, either success or error+reason825*/826int zynqmp_pm_set_pll_frac_mode(u32 clk_id, u32 mode)827{828return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_SET_PLL_FRAC_MODE, clk_id, mode);829}830EXPORT_SYMBOL_GPL(zynqmp_pm_set_pll_frac_mode);831832/**833* zynqmp_pm_get_pll_frac_mode() - PM API for get PLL mode834*835* @clk_id: PLL clock ID836* @mode: PLL mode837*838* This function return current PLL mode839*840* Return: Returns status, either success or error+reason841*/842int zynqmp_pm_get_pll_frac_mode(u32 clk_id, u32 *mode)843{844return zynqmp_pm_invoke_fn(PM_IOCTL, mode, 3, 0, IOCTL_GET_PLL_FRAC_MODE, clk_id);845}846EXPORT_SYMBOL_GPL(zynqmp_pm_get_pll_frac_mode);847848/**849* zynqmp_pm_set_pll_frac_data() - PM API for setting pll fraction data850*851* @clk_id: PLL clock ID852* @data: fraction data853*854* This function sets fraction data.855* It is valid for fraction mode only.856*857* Return: Returns status, either success or error+reason858*/859int zynqmp_pm_set_pll_frac_data(u32 clk_id, u32 data)860{861return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_SET_PLL_FRAC_DATA, clk_id, data);862}863EXPORT_SYMBOL_GPL(zynqmp_pm_set_pll_frac_data);864865/**866* zynqmp_pm_get_pll_frac_data() - PM API for getting pll fraction data867*868* @clk_id: PLL clock ID869* @data: fraction data870*871* This function returns fraction data value.872*873* Return: Returns status, either success or error+reason874*/875int zynqmp_pm_get_pll_frac_data(u32 clk_id, u32 *data)876{877return zynqmp_pm_invoke_fn(PM_IOCTL, data, 3, 0, IOCTL_GET_PLL_FRAC_DATA, clk_id);878}879EXPORT_SYMBOL_GPL(zynqmp_pm_get_pll_frac_data);880881/**882* zynqmp_pm_set_sd_tapdelay() - Set tap delay for the SD device883*884* @node_id: Node ID of the device885* @type: Type of tap delay to set (input/output)886* @value: Value to set fot the tap delay887*888* This function sets input/output tap delay for the SD device.889*890* Return: Returns status, either success or error+reason891*/892int zynqmp_pm_set_sd_tapdelay(u32 node_id, u32 type, u32 value)893{894u32 reg = (type == PM_TAPDELAY_INPUT) ? SD_ITAPDLY : SD_OTAPDLYSEL;895u32 mask = (node_id == NODE_SD_0) ? GENMASK(15, 0) : GENMASK(31, 16);896897if (value) {898return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, node_id, IOCTL_SET_SD_TAPDELAY, type,899value);900}901902/*903* Work around completely misdesigned firmware API on Xilinx ZynqMP.904* The IOCTL_SET_SD_TAPDELAY firmware call allows the caller to only905* ever set IOU_SLCR SD_ITAPDLY Register SD0_ITAPDLYENA/SD1_ITAPDLYENA906* bits, but there is no matching call to clear those bits. If those907* bits are not cleared, SDMMC tuning may fail.908*909* Luckily, there are PM_MMIO_READ/PM_MMIO_WRITE calls which seem to910* allow complete unrestricted access to all address space, including911* IOU_SLCR SD_ITAPDLY Register and all the other registers, access912* to which was supposed to be protected by the current firmware API.913*914* Use PM_MMIO_READ/PM_MMIO_WRITE to re-implement the missing counter915* part of IOCTL_SET_SD_TAPDELAY which clears SDx_ITAPDLYENA bits.916*/917return zynqmp_pm_invoke_fn(PM_MMIO_WRITE, NULL, 2, reg, mask);918}919EXPORT_SYMBOL_GPL(zynqmp_pm_set_sd_tapdelay);920921/**922* zynqmp_pm_sd_dll_reset() - Reset DLL logic923*924* @node_id: Node ID of the device925* @type: Reset type926*927* This function resets DLL logic for the SD device.928*929* Return: Returns status, either success or error+reason930*/931int zynqmp_pm_sd_dll_reset(u32 node_id, u32 type)932{933return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, node_id, IOCTL_SD_DLL_RESET, type);934}935EXPORT_SYMBOL_GPL(zynqmp_pm_sd_dll_reset);936937/**938* zynqmp_pm_ospi_mux_select() - OSPI Mux selection939*940* @dev_id: Device Id of the OSPI device.941* @select: OSPI Mux select value.942*943* This function select the OSPI Mux.944*945* Return: Returns status, either success or error+reason946*/947int zynqmp_pm_ospi_mux_select(u32 dev_id, u32 select)948{949return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, dev_id, IOCTL_OSPI_MUX_SELECT, select);950}951EXPORT_SYMBOL_GPL(zynqmp_pm_ospi_mux_select);952953/**954* zynqmp_pm_write_ggs() - PM API for writing global general storage (ggs)955* @index: GGS register index956* @value: Register value to be written957*958* This function writes value to GGS register.959*960* Return: Returns status, either success or error+reason961*/962int zynqmp_pm_write_ggs(u32 index, u32 value)963{964return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_WRITE_GGS, index, value);965}966EXPORT_SYMBOL_GPL(zynqmp_pm_write_ggs);967968/**969* zynqmp_pm_read_ggs() - PM API for reading global general storage (ggs)970* @index: GGS register index971* @value: Register value to be written972*973* This function returns GGS register value.974*975* Return: Returns status, either success or error+reason976*/977int zynqmp_pm_read_ggs(u32 index, u32 *value)978{979return zynqmp_pm_invoke_fn(PM_IOCTL, value, 3, 0, IOCTL_READ_GGS, index);980}981EXPORT_SYMBOL_GPL(zynqmp_pm_read_ggs);982983/**984* zynqmp_pm_write_pggs() - PM API for writing persistent global general985* storage (pggs)986* @index: PGGS register index987* @value: Register value to be written988*989* This function writes value to PGGS register.990*991* Return: Returns status, either success or error+reason992*/993int zynqmp_pm_write_pggs(u32 index, u32 value)994{995return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_WRITE_PGGS, index, value);996}997EXPORT_SYMBOL_GPL(zynqmp_pm_write_pggs);998999/**1000* zynqmp_pm_read_pggs() - PM API for reading persistent global general1001* storage (pggs)1002* @index: PGGS register index1003* @value: Register value to be written1004*1005* This function returns PGGS register value.1006*1007* Return: Returns status, either success or error+reason1008*/1009int zynqmp_pm_read_pggs(u32 index, u32 *value)1010{1011return zynqmp_pm_invoke_fn(PM_IOCTL, value, 3, 0, IOCTL_READ_PGGS, index);1012}1013EXPORT_SYMBOL_GPL(zynqmp_pm_read_pggs);10141015int zynqmp_pm_set_tapdelay_bypass(u32 index, u32 value)1016{1017return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_SET_TAPDELAY_BYPASS, index, value);1018}1019EXPORT_SYMBOL_GPL(zynqmp_pm_set_tapdelay_bypass);10201021/**1022* zynqmp_pm_set_boot_health_status() - PM API for setting healthy boot status1023* @value: Status value to be written1024*1025* This function sets healthy bit value to indicate boot health status1026* to firmware.1027*1028* Return: Returns status, either success or error+reason1029*/1030int zynqmp_pm_set_boot_health_status(u32 value)1031{1032return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, 0, IOCTL_SET_BOOT_HEALTH_STATUS, value);1033}10341035/**1036* zynqmp_pm_reset_assert - Request setting of reset (1 - assert, 0 - release)1037* @reset: Reset to be configured1038* @assert_flag: Flag stating should reset be asserted (1) or1039* released (0)1040*1041* Return: Returns status, either success or error+reason1042*/1043int zynqmp_pm_reset_assert(const u32 reset,1044const enum zynqmp_pm_reset_action assert_flag)1045{1046return zynqmp_pm_invoke_fn(PM_RESET_ASSERT, NULL, 2, reset, assert_flag);1047}1048EXPORT_SYMBOL_GPL(zynqmp_pm_reset_assert);10491050/**1051* zynqmp_pm_reset_get_status - Get status of the reset1052* @reset: Reset whose status should be returned1053* @status: Returned status1054*1055* Return: Returns status, either success or error+reason1056*/1057int zynqmp_pm_reset_get_status(const u32 reset, u32 *status)1058{1059u32 ret_payload[PAYLOAD_ARG_CNT];1060int ret;10611062if (!status)1063return -EINVAL;10641065ret = zynqmp_pm_invoke_fn(PM_RESET_GET_STATUS, ret_payload, 1, reset);1066*status = ret_payload[1];10671068return ret;1069}1070EXPORT_SYMBOL_GPL(zynqmp_pm_reset_get_status);10711072/**1073* zynqmp_pm_fpga_load - Perform the fpga load1074* @address: Address to write to1075* @size: pl bitstream size1076* @flags: Bitstream type1077* -XILINX_ZYNQMP_PM_FPGA_FULL: FPGA full reconfiguration1078* -XILINX_ZYNQMP_PM_FPGA_PARTIAL: FPGA partial reconfiguration1079*1080* This function provides access to pmufw. To transfer1081* the required bitstream into PL.1082*1083* Return: Returns status, either success or error+reason1084*/1085int zynqmp_pm_fpga_load(const u64 address, const u32 size, const u32 flags)1086{1087u32 ret_payload[PAYLOAD_ARG_CNT];1088int ret;10891090ret = zynqmp_pm_invoke_fn(PM_FPGA_LOAD, ret_payload, 4, lower_32_bits(address),1091upper_32_bits(address), size, flags);1092if (ret_payload[0])1093return -ret_payload[0];10941095return ret;1096}1097EXPORT_SYMBOL_GPL(zynqmp_pm_fpga_load);10981099/**1100* zynqmp_pm_fpga_get_status - Read value from PCAP status register1101* @value: Value to read1102*1103* This function provides access to the pmufw to get the PCAP1104* status1105*1106* Return: Returns status, either success or error+reason1107*/1108int zynqmp_pm_fpga_get_status(u32 *value)1109{1110u32 ret_payload[PAYLOAD_ARG_CNT];1111int ret;11121113if (!value)1114return -EINVAL;11151116ret = zynqmp_pm_invoke_fn(PM_FPGA_GET_STATUS, ret_payload, 0);1117*value = ret_payload[1];11181119return ret;1120}1121EXPORT_SYMBOL_GPL(zynqmp_pm_fpga_get_status);11221123/**1124* zynqmp_pm_fpga_get_config_status - Get the FPGA configuration status.1125* @value: Buffer to store FPGA configuration status.1126*1127* This function provides access to the pmufw to get the FPGA configuration1128* status1129*1130* Return: 0 on success, a negative value on error1131*/1132int zynqmp_pm_fpga_get_config_status(u32 *value)1133{1134u32 ret_payload[PAYLOAD_ARG_CNT];1135int ret;11361137if (!value)1138return -EINVAL;11391140ret = zynqmp_pm_invoke_fn(PM_FPGA_READ, ret_payload, 4,1141XILINX_ZYNQMP_PM_FPGA_CONFIG_STAT_OFFSET, 0, 0,1142XILINX_ZYNQMP_PM_FPGA_READ_CONFIG_REG);11431144*value = ret_payload[1];11451146return ret;1147}1148EXPORT_SYMBOL_GPL(zynqmp_pm_fpga_get_config_status);11491150/**1151* zynqmp_pm_pinctrl_request - Request Pin from firmware1152* @pin: Pin number to request1153*1154* This function requests pin from firmware.1155*1156* Return: Returns status, either success or error+reason.1157*/1158int zynqmp_pm_pinctrl_request(const u32 pin)1159{1160return zynqmp_pm_invoke_fn(PM_PINCTRL_REQUEST, NULL, 1, pin);1161}1162EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_request);11631164/**1165* zynqmp_pm_pinctrl_release - Inform firmware that Pin control is released1166* @pin: Pin number to release1167*1168* This function release pin from firmware.1169*1170* Return: Returns status, either success or error+reason.1171*/1172int zynqmp_pm_pinctrl_release(const u32 pin)1173{1174return zynqmp_pm_invoke_fn(PM_PINCTRL_RELEASE, NULL, 1, pin);1175}1176EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_release);11771178/**1179* zynqmp_pm_pinctrl_set_function - Set requested function for the pin1180* @pin: Pin number1181* @id: Function ID to set1182*1183* This function sets requested function for the given pin.1184*1185* Return: Returns status, either success or error+reason.1186*/1187int zynqmp_pm_pinctrl_set_function(const u32 pin, const u32 id)1188{1189return zynqmp_pm_invoke_fn(PM_PINCTRL_SET_FUNCTION, NULL, 2, pin, id);1190}1191EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_set_function);11921193/**1194* zynqmp_pm_pinctrl_get_config - Get configuration parameter for the pin1195* @pin: Pin number1196* @param: Parameter to get1197* @value: Buffer to store parameter value1198*1199* This function gets requested configuration parameter for the given pin.1200*1201* Return: Returns status, either success or error+reason.1202*/1203int zynqmp_pm_pinctrl_get_config(const u32 pin, const u32 param,1204u32 *value)1205{1206u32 ret_payload[PAYLOAD_ARG_CNT];1207int ret;12081209if (!value)1210return -EINVAL;12111212ret = zynqmp_pm_invoke_fn(PM_PINCTRL_CONFIG_PARAM_GET, ret_payload, 2, pin, param);1213*value = ret_payload[1];12141215return ret;1216}1217EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_get_config);12181219/**1220* zynqmp_pm_pinctrl_set_config - Set configuration parameter for the pin1221* @pin: Pin number1222* @param: Parameter to set1223* @value: Parameter value to set1224*1225* This function sets requested configuration parameter for the given pin.1226*1227* Return: Returns status, either success or error+reason.1228*/1229int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param,1230u32 value)1231{1232int ret;1233u32 pm_family_code;12341235ret = zynqmp_pm_get_family_info(&pm_family_code);1236if (ret)1237return ret;12381239if (pm_family_code == PM_ZYNQMP_FAMILY_CODE &&1240param == PM_PINCTRL_CONFIG_TRI_STATE) {1241ret = zynqmp_pm_feature(PM_PINCTRL_CONFIG_PARAM_SET);1242if (ret < PM_PINCTRL_PARAM_SET_VERSION) {1243pr_warn("The requested pinctrl feature is not supported in the current firmware.\n"1244"Expected firmware version is 2023.1 and above for this feature to work.\r\n");1245return -EOPNOTSUPP;1246}1247}12481249return zynqmp_pm_invoke_fn(PM_PINCTRL_CONFIG_PARAM_SET, NULL, 3, pin, param, value);1250}1251EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_set_config);12521253/**1254* zynqmp_pm_bootmode_read() - PM Config API for read bootpin status1255* @ps_mode: Returned output value of ps_mode1256*1257* This API function is to be used for notify the power management controller1258* to read bootpin status.1259*1260* Return: status, either success or error+reason1261*/1262unsigned int zynqmp_pm_bootmode_read(u32 *ps_mode)1263{1264unsigned int ret;1265u32 ret_payload[PAYLOAD_ARG_CNT];12661267ret = zynqmp_pm_invoke_fn(PM_MMIO_READ, ret_payload, 1, CRL_APB_BOOT_PIN_CTRL);12681269*ps_mode = ret_payload[1];12701271return ret;1272}1273EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_read);12741275/**1276* zynqmp_pm_bootmode_write() - PM Config API for Configure bootpin1277* @ps_mode: Value to be written to the bootpin ctrl register1278*1279* This API function is to be used for notify the power management controller1280* to configure bootpin.1281*1282* Return: Returns status, either success or error+reason1283*/1284int zynqmp_pm_bootmode_write(u32 ps_mode)1285{1286return zynqmp_pm_invoke_fn(PM_MMIO_WRITE, NULL, 3, CRL_APB_BOOT_PIN_CTRL,1287CRL_APB_BOOTPIN_CTRL_MASK, ps_mode);1288}1289EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_write);12901291/**1292* zynqmp_pm_init_finalize() - PM call to inform firmware that the caller1293* master has initialized its own power management1294*1295* Return: Returns status, either success or error+reason1296*1297* This API function is to be used for notify the power management controller1298* about the completed power management initialization.1299*/1300static int zynqmp_pm_init_finalize(void)1301{1302return zynqmp_pm_invoke_fn(PM_PM_INIT_FINALIZE, NULL, 0);1303}13041305/**1306* zynqmp_pm_set_suspend_mode() - Set system suspend mode1307* @mode: Mode to set for system suspend1308*1309* This API function is used to set mode of system suspend.1310*1311* Return: Returns status, either success or error+reason1312*/1313int zynqmp_pm_set_suspend_mode(u32 mode)1314{1315return zynqmp_pm_invoke_fn(PM_SET_SUSPEND_MODE, NULL, 1, mode);1316}1317EXPORT_SYMBOL_GPL(zynqmp_pm_set_suspend_mode);13181319/**1320* zynqmp_pm_request_node() - Request a node with specific capabilities1321* @node: Node ID of the slave1322* @capabilities: Requested capabilities of the slave1323* @qos: Quality of service (not supported)1324* @ack: Flag to specify whether acknowledge is requested1325*1326* This function is used by master to request particular node from firmware.1327* Every master must request node before using it.1328*1329* Return: Returns status, either success or error+reason1330*/1331int zynqmp_pm_request_node(const u32 node, const u32 capabilities,1332const u32 qos, const enum zynqmp_pm_request_ack ack)1333{1334return zynqmp_pm_invoke_fn(PM_REQUEST_NODE, NULL, 4, node, capabilities, qos, ack);1335}1336EXPORT_SYMBOL_GPL(zynqmp_pm_request_node);13371338/**1339* zynqmp_pm_release_node() - Release a node1340* @node: Node ID of the slave1341*1342* This function is used by master to inform firmware that master1343* has released node. Once released, master must not use that node1344* without re-request.1345*1346* Return: Returns status, either success or error+reason1347*/1348int zynqmp_pm_release_node(const u32 node)1349{1350return zynqmp_pm_invoke_fn(PM_RELEASE_NODE, NULL, 1, node);1351}1352EXPORT_SYMBOL_GPL(zynqmp_pm_release_node);13531354/**1355* zynqmp_pm_get_rpu_mode() - Get RPU mode1356* @node_id: Node ID of the device1357* @rpu_mode: return by reference value1358* either split or lockstep1359*1360* Return: return 0 on success or error+reason.1361* if success, then rpu_mode will be set1362* to current rpu mode.1363*/1364int zynqmp_pm_get_rpu_mode(u32 node_id, enum rpu_oper_mode *rpu_mode)1365{1366u32 ret_payload[PAYLOAD_ARG_CNT];1367int ret;13681369ret = zynqmp_pm_invoke_fn(PM_IOCTL, ret_payload, 2, node_id, IOCTL_GET_RPU_OPER_MODE);13701371/* only set rpu_mode if no error */1372if (ret == XST_PM_SUCCESS)1373*rpu_mode = ret_payload[0];13741375return ret;1376}1377EXPORT_SYMBOL_GPL(zynqmp_pm_get_rpu_mode);13781379/**1380* zynqmp_pm_set_rpu_mode() - Set RPU mode1381* @node_id: Node ID of the device1382* @rpu_mode: Argument 1 to requested IOCTL call. either split or lockstep1383*1384* This function is used to set RPU mode to split or1385* lockstep1386*1387* Return: Returns status, either success or error+reason1388*/1389int zynqmp_pm_set_rpu_mode(u32 node_id, enum rpu_oper_mode rpu_mode)1390{1391return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, node_id, IOCTL_SET_RPU_OPER_MODE,1392(u32)rpu_mode);1393}1394EXPORT_SYMBOL_GPL(zynqmp_pm_set_rpu_mode);13951396/**1397* zynqmp_pm_set_tcm_config - configure TCM1398* @node_id: Firmware specific TCM subsystem ID1399* @tcm_mode: Argument 1 to requested IOCTL call1400* either PM_RPU_TCM_COMB or PM_RPU_TCM_SPLIT1401*1402* This function is used to set RPU mode to split or combined1403*1404* Return: status: 0 for success, else failure1405*/1406int zynqmp_pm_set_tcm_config(u32 node_id, enum rpu_tcm_comb tcm_mode)1407{1408return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, node_id, IOCTL_TCM_COMB_CONFIG,1409(u32)tcm_mode);1410}1411EXPORT_SYMBOL_GPL(zynqmp_pm_set_tcm_config);14121413/**1414* zynqmp_pm_get_node_status - PM call to request a node's current power state1415* @node: ID of the component or sub-system in question1416* @status: Current operating state of the requested node1417* @requirements: Current requirements asserted on the node,1418* used for slave nodes only.1419* @usage: Usage information, used for slave nodes only:1420* PM_USAGE_NO_MASTER - No master is currently using1421* the node1422* PM_USAGE_CURRENT_MASTER - Only requesting master is1423* currently using the node1424* PM_USAGE_OTHER_MASTER - Only other masters are1425* currently using the node1426* PM_USAGE_BOTH_MASTERS - Both the current and at least1427* one other master is currently1428* using the node1429*1430* Return: Returns status, either success or error+reason1431*/1432int zynqmp_pm_get_node_status(const u32 node, u32 *const status,1433u32 *const requirements, u32 *const usage)1434{1435u32 ret_payload[PAYLOAD_ARG_CNT];1436int ret;14371438if (!status || !requirements || !usage)1439return -EINVAL;14401441ret = zynqmp_pm_invoke_fn(PM_GET_NODE_STATUS, ret_payload, 1, node);1442if (ret_payload[0] == XST_PM_SUCCESS) {1443*status = ret_payload[1];1444*requirements = ret_payload[2];1445*usage = ret_payload[3];1446}14471448return ret;1449}1450EXPORT_SYMBOL_GPL(zynqmp_pm_get_node_status);14511452/**1453* zynqmp_pm_force_pwrdwn - PM call to request for another PU or subsystem to1454* be powered down forcefully1455* @node: Node ID of the targeted PU or subsystem1456* @ack: Flag to specify whether acknowledge is requested1457*1458* Return: status, either success or error+reason1459*/1460int zynqmp_pm_force_pwrdwn(const u32 node,1461const enum zynqmp_pm_request_ack ack)1462{1463return zynqmp_pm_invoke_fn(PM_FORCE_POWERDOWN, NULL, 2, node, ack);1464}1465EXPORT_SYMBOL_GPL(zynqmp_pm_force_pwrdwn);14661467/**1468* zynqmp_pm_request_wake - PM call to wake up selected master or subsystem1469* @node: Node ID of the master or subsystem1470* @set_addr: Specifies whether the address argument is relevant1471* @address: Address from which to resume when woken up1472* @ack: Flag to specify whether acknowledge requested1473*1474* Return: status, either success or error+reason1475*/1476int zynqmp_pm_request_wake(const u32 node,1477const bool set_addr,1478const u64 address,1479const enum zynqmp_pm_request_ack ack)1480{1481/* set_addr flag is encoded into 1st bit of address */1482return zynqmp_pm_invoke_fn(PM_REQUEST_WAKEUP, NULL, 4, node, address | set_addr,1483address >> 32, ack);1484}1485EXPORT_SYMBOL_GPL(zynqmp_pm_request_wake);14861487/**1488* zynqmp_pm_set_requirement() - PM call to set requirement for PM slaves1489* @node: Node ID of the slave1490* @capabilities: Requested capabilities of the slave1491* @qos: Quality of service (not supported)1492* @ack: Flag to specify whether acknowledge is requested1493*1494* This API function is to be used for slaves a PU already has requested1495* to change its capabilities.1496*1497* Return: Returns status, either success or error+reason1498*/1499int zynqmp_pm_set_requirement(const u32 node, const u32 capabilities,1500const u32 qos,1501const enum zynqmp_pm_request_ack ack)1502{1503return zynqmp_pm_invoke_fn(PM_SET_REQUIREMENT, NULL, 4, node, capabilities, qos, ack);1504}1505EXPORT_SYMBOL_GPL(zynqmp_pm_set_requirement);15061507/**1508* zynqmp_pm_load_pdi - Load and process PDI1509* @src: Source device where PDI is located1510* @address: PDI src address1511*1512* This function provides support to load PDI from linux1513*1514* Return: Returns status, either success or error+reason1515*/1516int zynqmp_pm_load_pdi(const u32 src, const u64 address)1517{1518return zynqmp_pm_invoke_fn(PM_LOAD_PDI, NULL, 3, src, lower_32_bits(address),1519upper_32_bits(address));1520}1521EXPORT_SYMBOL_GPL(zynqmp_pm_load_pdi);15221523/**1524* zynqmp_pm_efuse_access - Provides access to efuse memory.1525* @address: Address of the efuse params structure1526* @out: Returned output value1527*1528* Return: Returns status, either success or error code.1529*/1530int zynqmp_pm_efuse_access(const u64 address, u32 *out)1531{1532u32 ret_payload[PAYLOAD_ARG_CNT];1533int ret;15341535if (!out)1536return -EINVAL;15371538ret = zynqmp_pm_invoke_fn(PM_EFUSE_ACCESS, ret_payload, 2,1539upper_32_bits(address),1540lower_32_bits(address));1541*out = ret_payload[1];15421543return ret;1544}1545EXPORT_SYMBOL_GPL(zynqmp_pm_efuse_access);15461547/**1548* zynqmp_pm_register_notifier() - PM API for register a subsystem1549* to be notified about specific1550* event/error.1551* @node: Node ID to which the event is related.1552* @event: Event Mask of Error events for which wants to get notified.1553* @wake: Wake subsystem upon capturing the event if value 11554* @enable: Enable the registration for value 1, disable for value 01555*1556* This function is used to register/un-register for particular node-event1557* combination in firmware.1558*1559* Return: Returns status, either success or error+reason1560*/15611562int zynqmp_pm_register_notifier(const u32 node, const u32 event,1563const u32 wake, const u32 enable)1564{1565return zynqmp_pm_invoke_fn(PM_REGISTER_NOTIFIER, NULL, 4, node, event, wake, enable);1566}1567EXPORT_SYMBOL_GPL(zynqmp_pm_register_notifier);15681569/**1570* zynqmp_pm_system_shutdown - PM call to request a system shutdown or restart1571* @type: Shutdown or restart? 0 for shutdown, 1 for restart1572* @subtype: Specifies which system should be restarted or shut down1573*1574* Return: Returns status, either success or error+reason1575*/1576int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype)1577{1578return zynqmp_pm_invoke_fn(PM_SYSTEM_SHUTDOWN, NULL, 2, type, subtype);1579}15801581/**1582* zynqmp_pm_set_feature_config - PM call to request IOCTL for feature config1583* @id: The config ID of the feature to be configured1584* @value: The config value of the feature to be configured1585*1586* Return: Returns 0 on success or error value on failure.1587*/1588int zynqmp_pm_set_feature_config(enum pm_feature_config_id id, u32 value)1589{1590return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_SET_FEATURE_CONFIG, id, value);1591}15921593/**1594* zynqmp_pm_get_feature_config - PM call to get value of configured feature1595* @id: The config id of the feature to be queried1596* @payload: Returned value array1597*1598* Return: Returns 0 on success or error value on failure.1599*/1600int zynqmp_pm_get_feature_config(enum pm_feature_config_id id,1601u32 *payload)1602{1603return zynqmp_pm_invoke_fn(PM_IOCTL, payload, 3, 0, IOCTL_GET_FEATURE_CONFIG, id);1604}16051606/**1607* zynqmp_pm_sec_read_reg - PM call to securely read from given offset1608* of the node1609* @node_id: Node Id of the device1610* @offset: Offset to be used (20-bit)1611* @ret_value: Output data read from the given offset after1612* firmware access policy is successfully enforced1613*1614* Return: Returns 0 on success or error value on failure1615*/1616int zynqmp_pm_sec_read_reg(u32 node_id, u32 offset, u32 *ret_value)1617{1618u32 ret_payload[PAYLOAD_ARG_CNT];1619u32 count = 1;1620int ret;16211622if (!ret_value)1623return -EINVAL;16241625ret = zynqmp_pm_invoke_fn(PM_IOCTL, ret_payload, 4, node_id, IOCTL_READ_REG,1626offset, count);16271628*ret_value = ret_payload[1];16291630return ret;1631}1632EXPORT_SYMBOL_GPL(zynqmp_pm_sec_read_reg);16331634/**1635* zynqmp_pm_sec_mask_write_reg - PM call to securely write to given offset1636* of the node1637* @node_id: Node Id of the device1638* @offset: Offset to be used (20-bit)1639* @mask: Mask to be used1640* @value: Value to be written1641*1642* Return: Returns 0 on success or error value on failure1643*/1644int zynqmp_pm_sec_mask_write_reg(const u32 node_id, const u32 offset, u32 mask,1645u32 value)1646{1647return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 5, node_id, IOCTL_MASK_WRITE_REG,1648offset, mask, value);1649}1650EXPORT_SYMBOL_GPL(zynqmp_pm_sec_mask_write_reg);16511652/**1653* zynqmp_pm_set_sd_config - PM call to set value of SD config registers1654* @node: SD node ID1655* @config: The config type of SD registers1656* @value: Value to be set1657*1658* Return: Returns 0 on success or error value on failure.1659*/1660int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value)1661{1662return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, node, IOCTL_SET_SD_CONFIG, config, value);1663}1664EXPORT_SYMBOL_GPL(zynqmp_pm_set_sd_config);16651666/**1667* zynqmp_pm_set_gem_config - PM call to set value of GEM config registers1668* @node: GEM node ID1669* @config: The config type of GEM registers1670* @value: Value to be set1671*1672* Return: Returns 0 on success or error value on failure.1673*/1674int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config,1675u32 value)1676{1677return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, node, IOCTL_SET_GEM_CONFIG, config, value);1678}1679EXPORT_SYMBOL_GPL(zynqmp_pm_set_gem_config);16801681/**1682* struct zynqmp_pm_shutdown_scope - Struct for shutdown scope1683* @subtype: Shutdown subtype1684* @name: Matching string for scope argument1685*1686* This struct encapsulates mapping between shutdown scope ID and string.1687*/1688struct zynqmp_pm_shutdown_scope {1689const enum zynqmp_pm_shutdown_subtype subtype;1690const char *name;1691};16921693static struct zynqmp_pm_shutdown_scope shutdown_scopes[] = {1694[ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM] = {1695.subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM,1696.name = "subsystem",1697},1698[ZYNQMP_PM_SHUTDOWN_SUBTYPE_PS_ONLY] = {1699.subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_PS_ONLY,1700.name = "ps_only",1701},1702[ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM] = {1703.subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM,1704.name = "system",1705},1706};17071708static struct zynqmp_pm_shutdown_scope *selected_scope =1709&shutdown_scopes[ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM];17101711/**1712* zynqmp_pm_is_shutdown_scope_valid - Check if shutdown scope string is valid1713* @scope_string: Shutdown scope string1714*1715* Return: Return pointer to matching shutdown scope struct from1716* array of available options in system if string is valid,1717* otherwise returns NULL.1718*/1719static struct zynqmp_pm_shutdown_scope*1720zynqmp_pm_is_shutdown_scope_valid(const char *scope_string)1721{1722int count;17231724for (count = 0; count < ARRAY_SIZE(shutdown_scopes); count++)1725if (sysfs_streq(scope_string, shutdown_scopes[count].name))1726return &shutdown_scopes[count];17271728return NULL;1729}17301731static ssize_t shutdown_scope_show(struct device *device,1732struct device_attribute *attr,1733char *buf)1734{1735int i;17361737for (i = 0; i < ARRAY_SIZE(shutdown_scopes); i++) {1738if (&shutdown_scopes[i] == selected_scope) {1739strcat(buf, "[");1740strcat(buf, shutdown_scopes[i].name);1741strcat(buf, "]");1742} else {1743strcat(buf, shutdown_scopes[i].name);1744}1745strcat(buf, " ");1746}1747strcat(buf, "\n");17481749return strlen(buf);1750}17511752static ssize_t shutdown_scope_store(struct device *device,1753struct device_attribute *attr,1754const char *buf, size_t count)1755{1756int ret;1757struct zynqmp_pm_shutdown_scope *scope;17581759scope = zynqmp_pm_is_shutdown_scope_valid(buf);1760if (!scope)1761return -EINVAL;17621763ret = zynqmp_pm_system_shutdown(ZYNQMP_PM_SHUTDOWN_TYPE_SETSCOPE_ONLY,1764scope->subtype);1765if (ret) {1766pr_err("unable to set shutdown scope %s\n", buf);1767return ret;1768}17691770selected_scope = scope;17711772return count;1773}17741775static DEVICE_ATTR_RW(shutdown_scope);17761777static ssize_t health_status_store(struct device *device,1778struct device_attribute *attr,1779const char *buf, size_t count)1780{1781int ret;1782unsigned int value;17831784ret = kstrtouint(buf, 10, &value);1785if (ret)1786return ret;17871788ret = zynqmp_pm_set_boot_health_status(value);1789if (ret) {1790dev_err(device, "unable to set healthy bit value to %u\n",1791value);1792return ret;1793}17941795return count;1796}17971798static DEVICE_ATTR_WO(health_status);17991800static ssize_t ggs_show(struct device *device,1801struct device_attribute *attr,1802char *buf,1803u32 reg)1804{1805int ret;1806u32 ret_payload[PAYLOAD_ARG_CNT];18071808ret = zynqmp_pm_read_ggs(reg, ret_payload);1809if (ret)1810return ret;18111812return sprintf(buf, "0x%x\n", ret_payload[1]);1813}18141815static ssize_t ggs_store(struct device *device,1816struct device_attribute *attr,1817const char *buf, size_t count,1818u32 reg)1819{1820long value;1821int ret;18221823if (reg >= GSS_NUM_REGS)1824return -EINVAL;18251826ret = kstrtol(buf, 16, &value);1827if (ret) {1828count = -EFAULT;1829goto err;1830}18311832ret = zynqmp_pm_write_ggs(reg, value);1833if (ret)1834count = -EFAULT;1835err:1836return count;1837}18381839/* GGS register show functions */1840#define GGS0_SHOW(N) \1841ssize_t ggs##N##_show(struct device *device, \1842struct device_attribute *attr, \1843char *buf) \1844{ \1845return ggs_show(device, attr, buf, N); \1846}18471848static GGS0_SHOW(0);1849static GGS0_SHOW(1);1850static GGS0_SHOW(2);1851static GGS0_SHOW(3);18521853/* GGS register store function */1854#define GGS0_STORE(N) \1855ssize_t ggs##N##_store(struct device *device, \1856struct device_attribute *attr, \1857const char *buf, \1858size_t count) \1859{ \1860return ggs_store(device, attr, buf, count, N); \1861}18621863static GGS0_STORE(0);1864static GGS0_STORE(1);1865static GGS0_STORE(2);1866static GGS0_STORE(3);18671868static ssize_t pggs_show(struct device *device,1869struct device_attribute *attr,1870char *buf,1871u32 reg)1872{1873int ret;1874u32 ret_payload[PAYLOAD_ARG_CNT];18751876ret = zynqmp_pm_read_pggs(reg, ret_payload);1877if (ret)1878return ret;18791880return sprintf(buf, "0x%x\n", ret_payload[1]);1881}18821883static ssize_t pggs_store(struct device *device,1884struct device_attribute *attr,1885const char *buf, size_t count,1886u32 reg)1887{1888long value;1889int ret;18901891if (reg >= GSS_NUM_REGS)1892return -EINVAL;18931894ret = kstrtol(buf, 16, &value);1895if (ret) {1896count = -EFAULT;1897goto err;1898}18991900ret = zynqmp_pm_write_pggs(reg, value);1901if (ret)1902count = -EFAULT;19031904err:1905return count;1906}19071908#define PGGS0_SHOW(N) \1909ssize_t pggs##N##_show(struct device *device, \1910struct device_attribute *attr, \1911char *buf) \1912{ \1913return pggs_show(device, attr, buf, N); \1914}19151916#define PGGS0_STORE(N) \1917ssize_t pggs##N##_store(struct device *device, \1918struct device_attribute *attr, \1919const char *buf, \1920size_t count) \1921{ \1922return pggs_store(device, attr, buf, count, N); \1923}19241925/* PGGS register show functions */1926static PGGS0_SHOW(0);1927static PGGS0_SHOW(1);1928static PGGS0_SHOW(2);1929static PGGS0_SHOW(3);19301931/* PGGS register store functions */1932static PGGS0_STORE(0);1933static PGGS0_STORE(1);1934static PGGS0_STORE(2);1935static PGGS0_STORE(3);19361937/* GGS register attributes */1938static DEVICE_ATTR_RW(ggs0);1939static DEVICE_ATTR_RW(ggs1);1940static DEVICE_ATTR_RW(ggs2);1941static DEVICE_ATTR_RW(ggs3);19421943/* PGGS register attributes */1944static DEVICE_ATTR_RW(pggs0);1945static DEVICE_ATTR_RW(pggs1);1946static DEVICE_ATTR_RW(pggs2);1947static DEVICE_ATTR_RW(pggs3);19481949static ssize_t feature_config_id_show(struct device *device,1950struct device_attribute *attr,1951char *buf)1952{1953struct zynqmp_devinfo *devinfo = dev_get_drvdata(device);19541955return sysfs_emit(buf, "%d\n", devinfo->feature_conf_id);1956}19571958static ssize_t feature_config_id_store(struct device *device,1959struct device_attribute *attr,1960const char *buf, size_t count)1961{1962u32 config_id;1963int ret;1964struct zynqmp_devinfo *devinfo = dev_get_drvdata(device);19651966if (!buf)1967return -EINVAL;19681969ret = kstrtou32(buf, 10, &config_id);1970if (ret)1971return ret;19721973devinfo->feature_conf_id = config_id;19741975return count;1976}19771978static DEVICE_ATTR_RW(feature_config_id);19791980static ssize_t feature_config_value_show(struct device *device,1981struct device_attribute *attr,1982char *buf)1983{1984int ret;1985u32 ret_payload[PAYLOAD_ARG_CNT];1986struct zynqmp_devinfo *devinfo = dev_get_drvdata(device);19871988ret = zynqmp_pm_get_feature_config(devinfo->feature_conf_id,1989ret_payload);1990if (ret)1991return ret;19921993return sysfs_emit(buf, "%d\n", ret_payload[1]);1994}19951996static ssize_t feature_config_value_store(struct device *device,1997struct device_attribute *attr,1998const char *buf, size_t count)1999{2000u32 value;2001int ret;2002struct zynqmp_devinfo *devinfo = dev_get_drvdata(device);20032004if (!buf)2005return -EINVAL;20062007ret = kstrtou32(buf, 10, &value);2008if (ret)2009return ret;20102011ret = zynqmp_pm_set_feature_config(devinfo->feature_conf_id,2012value);2013if (ret)2014return ret;20152016return count;2017}20182019static DEVICE_ATTR_RW(feature_config_value);20202021static struct attribute *zynqmp_firmware_attrs[] = {2022&dev_attr_ggs0.attr,2023&dev_attr_ggs1.attr,2024&dev_attr_ggs2.attr,2025&dev_attr_ggs3.attr,2026&dev_attr_pggs0.attr,2027&dev_attr_pggs1.attr,2028&dev_attr_pggs2.attr,2029&dev_attr_pggs3.attr,2030&dev_attr_shutdown_scope.attr,2031&dev_attr_health_status.attr,2032&dev_attr_feature_config_id.attr,2033&dev_attr_feature_config_value.attr,2034NULL,2035};20362037ATTRIBUTE_GROUPS(zynqmp_firmware);20382039static int zynqmp_firmware_probe(struct platform_device *pdev)2040{2041struct device *dev = &pdev->dev;2042struct zynqmp_devinfo *devinfo;2043u32 pm_family_code;2044int ret;20452046ret = get_set_conduit_method(dev->of_node);2047if (ret)2048return ret;20492050/* Get platform-specific firmware data from device tree match */2051active_platform_fw_data = (struct platform_fw_data *)device_get_match_data(dev);2052if (!active_platform_fw_data)2053return -EINVAL;20542055/* Get SiP SVC version number */2056ret = zynqmp_pm_get_sip_svc_version(&sip_svc_version);2057if (ret)2058return ret;20592060ret = do_feature_check_call(PM_FEATURE_CHECK);2061if (ret >= 0 && ((ret & FIRMWARE_VERSION_MASK) >= PM_API_VERSION_1))2062feature_check_enabled = true;20632064devinfo = devm_kzalloc(dev, sizeof(*devinfo), GFP_KERNEL);2065if (!devinfo)2066return -ENOMEM;20672068devinfo->dev = dev;20692070platform_set_drvdata(pdev, devinfo);20712072/* Check PM API version number */2073ret = zynqmp_pm_get_api_version(&pm_api_version);2074if (ret)2075return ret;20762077if (pm_api_version < ZYNQMP_PM_VERSION) {2078panic("%s Platform Management API version error. Expected: v%d.%d - Found: v%d.%d\n",2079__func__,2080ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR,2081pm_api_version >> 16, pm_api_version & 0xFFFF);2082}20832084pr_info("%s Platform Management API v%d.%d\n", __func__,2085pm_api_version >> 16, pm_api_version & 0xFFFF);20862087/* Get the Family code of platform */2088ret = zynqmp_pm_get_family_info(&pm_family_code);2089if (ret < 0)2090return ret;20912092/* Check trustzone version number */2093ret = zynqmp_pm_get_trustzone_version(&pm_tz_version);2094if (ret)2095panic("Legacy trustzone found without version support\n");20962097if (pm_tz_version < ZYNQMP_TZ_VERSION)2098panic("%s Trustzone version error. Expected: v%d.%d - Found: v%d.%d\n",2099__func__,2100ZYNQMP_TZ_VERSION_MAJOR, ZYNQMP_TZ_VERSION_MINOR,2101pm_tz_version >> 16, pm_tz_version & 0xFFFF);21022103pr_info("%s Trustzone version v%d.%d\n", __func__,2104pm_tz_version >> 16, pm_tz_version & 0xFFFF);21052106ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, firmware_devs,2107ARRAY_SIZE(firmware_devs), NULL, 0, NULL);2108if (ret) {2109dev_err(&pdev->dev, "failed to add MFD devices %d\n", ret);2110return ret;2111}21122113zynqmp_pm_api_debugfs_init();21142115if (pm_family_code != PM_ZYNQMP_FAMILY_CODE) {2116em_dev = platform_device_register_data(&pdev->dev, "xlnx_event_manager",2117-1, NULL, 0);2118if (IS_ERR(em_dev))2119dev_err_probe(&pdev->dev, PTR_ERR(em_dev), "EM register fail with error\n");2120}21212122return of_platform_populate(dev->of_node, NULL, NULL, dev);2123}21242125static void zynqmp_firmware_remove(struct platform_device *pdev)2126{2127struct pm_api_feature_data *feature_data;2128struct hlist_node *tmp;2129int i;21302131mfd_remove_devices(&pdev->dev);2132zynqmp_pm_api_debugfs_exit();21332134hash_for_each_safe(pm_api_features_map, i, tmp, feature_data, hentry) {2135hash_del(&feature_data->hentry);2136kfree(feature_data);2137}21382139platform_device_unregister(em_dev);2140}21412142static void zynqmp_firmware_sync_state(struct device *dev)2143{2144struct device_node *np = dev->of_node;21452146if (!of_device_is_compatible(np, "xlnx,zynqmp-firmware"))2147return;21482149of_genpd_sync_state(np);21502151if (zynqmp_pm_init_finalize())2152dev_warn(dev, "failed to release power management to firmware\n");2153}21542155static const struct platform_fw_data platform_fw_data_versal = {2156.family_code = PM_VERSAL_FAMILY_CODE,2157};21582159static const struct platform_fw_data platform_fw_data_versal_net = {2160.family_code = PM_VERSAL_NET_FAMILY_CODE,2161};21622163static const struct platform_fw_data platform_fw_data_zynqmp = {2164.family_code = PM_ZYNQMP_FAMILY_CODE,2165};21662167static const struct of_device_id zynqmp_firmware_of_match[] = {2168{.compatible = "xlnx,zynqmp-firmware", .data = &platform_fw_data_zynqmp},2169{.compatible = "xlnx,versal-firmware", .data = &platform_fw_data_versal},2170{.compatible = "xlnx,versal-net-firmware", .data = &platform_fw_data_versal_net},2171{},2172};2173MODULE_DEVICE_TABLE(of, zynqmp_firmware_of_match);21742175static struct platform_driver zynqmp_firmware_driver = {2176.driver = {2177.name = "zynqmp_firmware",2178.of_match_table = zynqmp_firmware_of_match,2179.dev_groups = zynqmp_firmware_groups,2180.sync_state = zynqmp_firmware_sync_state,2181},2182.probe = zynqmp_firmware_probe,2183.remove = zynqmp_firmware_remove,2184};2185module_platform_driver(zynqmp_firmware_driver);218621872188