// SPDX-License-Identifier: GPL-2.01/*2* Xilinx Zynq MPSoC Firmware layer3*4* Copyright (C) 2014-2022 Xilinx, Inc.5* Copyright (C) 2022 - 2024, 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};7374static const struct mfd_cell firmware_devs[] = {75{76.name = "zynqmp_power_controller",77},78};7980/**81* zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes82* @ret_status: PMUFW return code83*84* Return: corresponding Linux error code85*/86static int zynqmp_pm_ret_code(u32 ret_status)87{88switch (ret_status) {89case XST_PM_SUCCESS:90case XST_PM_DOUBLE_REQ:91return 0;92case XST_PM_NO_FEATURE:93return -ENOTSUPP;94case XST_PM_INVALID_VERSION:95return -EOPNOTSUPP;96case XST_PM_NO_ACCESS:97return -EACCES;98case XST_PM_ABORT_SUSPEND:99return -ECANCELED;100case XST_PM_MULT_USER:101return -EUSERS;102case XST_PM_INTERNAL:103case XST_PM_CONFLICT:104case XST_PM_INVALID_NODE:105case XST_PM_INVALID_CRC:106default:107return -EINVAL;108}109}110111static noinline int do_fw_call_fail(u32 *ret_payload, u32 num_args, ...)112{113return -ENODEV;114}115116/*117* PM function call wrapper118* Invoke do_fw_call_smc or do_fw_call_hvc, depending on the configuration119*/120static int (*do_fw_call)(u32 *ret_payload, u32, ...) = do_fw_call_fail;121122/**123* do_fw_call_smc() - Call system-level platform management layer (SMC)124* @num_args: Number of variable arguments should be <= 8125* @ret_payload: Returned value array126*127* Invoke platform management function via SMC call (no hypervisor present).128*129* Return: Returns status, either success or error+reason130*/131static noinline int do_fw_call_smc(u32 *ret_payload, u32 num_args, ...)132{133struct arm_smccc_res res;134u64 args[8] = {0};135va_list arg_list;136u8 i;137138if (num_args > 8)139return -EINVAL;140141va_start(arg_list, num_args);142143for (i = 0; i < num_args; i++)144args[i] = va_arg(arg_list, u64);145146va_end(arg_list);147148arm_smccc_smc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], &res);149150if (ret_payload) {151ret_payload[0] = lower_32_bits(res.a0);152ret_payload[1] = upper_32_bits(res.a0);153ret_payload[2] = lower_32_bits(res.a1);154ret_payload[3] = upper_32_bits(res.a1);155ret_payload[4] = lower_32_bits(res.a2);156ret_payload[5] = upper_32_bits(res.a2);157ret_payload[6] = lower_32_bits(res.a3);158}159160return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);161}162163/**164* do_fw_call_hvc() - Call system-level platform management layer (HVC)165* @num_args: Number of variable arguments should be <= 8166* @ret_payload: Returned value array167*168* Invoke platform management function via HVC169* HVC-based for communication through hypervisor170* (no direct communication with ATF).171*172* Return: Returns status, either success or error+reason173*/174static noinline int do_fw_call_hvc(u32 *ret_payload, u32 num_args, ...)175{176struct arm_smccc_res res;177u64 args[8] = {0};178va_list arg_list;179u8 i;180181if (num_args > 8)182return -EINVAL;183184va_start(arg_list, num_args);185186for (i = 0; i < num_args; i++)187args[i] = va_arg(arg_list, u64);188189va_end(arg_list);190191arm_smccc_hvc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], &res);192193if (ret_payload) {194ret_payload[0] = lower_32_bits(res.a0);195ret_payload[1] = upper_32_bits(res.a0);196ret_payload[2] = lower_32_bits(res.a1);197ret_payload[3] = upper_32_bits(res.a1);198ret_payload[4] = lower_32_bits(res.a2);199ret_payload[5] = upper_32_bits(res.a2);200ret_payload[6] = lower_32_bits(res.a3);201}202203return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);204}205206static int __do_feature_check_call(const u32 api_id, u32 *ret_payload)207{208int ret;209u64 smc_arg[2];210u32 module_id;211u32 feature_check_api_id;212213module_id = FIELD_GET(MODULE_ID_MASK, api_id);214215/*216* Feature check of APIs belonging to PM, XSEM, and TF-A are handled by calling217* PM_FEATURE_CHECK API. For other modules, call PM_API_FEATURES API.218*/219if (module_id == PM_MODULE_ID || module_id == XSEM_MODULE_ID || module_id == TF_A_MODULE_ID)220feature_check_api_id = PM_FEATURE_CHECK;221else222feature_check_api_id = PM_API_FEATURES;223224/*225* Feature check of TF-A APIs is done in the TF-A layer and it expects for226* MODULE_ID_MASK bits of SMC's arg[0] to be the same as PM_MODULE_ID.227*/228if (module_id == TF_A_MODULE_ID) {229module_id = PM_MODULE_ID;230smc_arg[1] = api_id;231} else {232smc_arg[1] = (api_id & API_ID_MASK);233}234235smc_arg[0] = PM_SIP_SVC | FIELD_PREP(MODULE_ID_MASK, module_id) | feature_check_api_id;236237ret = do_fw_call(ret_payload, 2, smc_arg[0], smc_arg[1]);238if (ret)239ret = -EOPNOTSUPP;240else241ret = ret_payload[1];242243return ret;244}245246static int do_feature_check_call(const u32 api_id)247{248int ret;249u32 ret_payload[PAYLOAD_ARG_CNT];250struct pm_api_feature_data *feature_data;251252/* Check for existing entry in hash table for given api */253hash_for_each_possible(pm_api_features_map, feature_data, hentry,254api_id) {255if (feature_data->pm_api_id == api_id)256return feature_data->feature_status;257}258259/* Add new entry if not present */260feature_data = kmalloc(sizeof(*feature_data), GFP_ATOMIC);261if (!feature_data)262return -ENOMEM;263264feature_data->pm_api_id = api_id;265ret = __do_feature_check_call(api_id, ret_payload);266267feature_data->feature_status = ret;268hash_add(pm_api_features_map, &feature_data->hentry, api_id);269270if (api_id == PM_IOCTL)271/* Store supported IOCTL IDs mask */272memcpy(ioctl_features, &ret_payload[2], FEATURE_PAYLOAD_SIZE * 4);273else if (api_id == PM_QUERY_DATA)274/* Store supported QUERY IDs mask */275memcpy(query_features, &ret_payload[2], FEATURE_PAYLOAD_SIZE * 4);276277return ret;278}279280/**281* zynqmp_pm_feature() - Check whether given feature is supported or not and282* store supported IOCTL/QUERY ID mask283* @api_id: API ID to check284*285* Return: Returns status, either success or error+reason286*/287int zynqmp_pm_feature(const u32 api_id)288{289int ret;290291if (!feature_check_enabled)292return 0;293294ret = do_feature_check_call(api_id);295296return ret;297}298EXPORT_SYMBOL_GPL(zynqmp_pm_feature);299300/**301* zynqmp_pm_is_function_supported() - Check whether given IOCTL/QUERY function302* is supported or not303* @api_id: PM_IOCTL or PM_QUERY_DATA304* @id: IOCTL or QUERY function IDs305*306* Return: Returns status, either success or error+reason307*/308int zynqmp_pm_is_function_supported(const u32 api_id, const u32 id)309{310int ret;311u32 *bit_mask;312313/* Input arguments validation */314if (id >= 64 || (api_id != PM_IOCTL && api_id != PM_QUERY_DATA))315return -EINVAL;316317/* Check feature check API version */318ret = do_feature_check_call(PM_FEATURE_CHECK);319if (ret < 0)320return ret;321322/* Check if feature check version 2 is supported or not */323if ((ret & FIRMWARE_VERSION_MASK) == PM_API_VERSION_2) {324/*325* Call feature check for IOCTL/QUERY API to get IOCTL ID or326* QUERY ID feature status.327*/328ret = do_feature_check_call(api_id);329if (ret < 0)330return ret;331332bit_mask = (api_id == PM_IOCTL) ? ioctl_features : query_features;333334if ((bit_mask[(id / 32)] & BIT((id % 32))) == 0U)335return -EOPNOTSUPP;336} else {337return -ENODATA;338}339340return 0;341}342EXPORT_SYMBOL_GPL(zynqmp_pm_is_function_supported);343344/**345* zynqmp_pm_invoke_fw_fn() - Invoke the system-level platform management layer346* caller function depending on the configuration347* @pm_api_id: Requested PM-API call348* @ret_payload: Returned value array349* @num_args: Number of arguments to requested PM-API call350*351* Invoke platform management function for SMC or HVC call, depending on352* configuration.353* Following SMC Calling Convention (SMCCC) for SMC64:354* Pm Function Identifier,355* PM_SIP_SVC + PASS_THROUGH_FW_CMD_ID =356* ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT)357* ((SMC_64) << FUNCID_CC_SHIFT)358* ((SIP_START) << FUNCID_OEN_SHIFT)359* (PASS_THROUGH_FW_CMD_ID))360*361* PM_SIP_SVC - Registered ZynqMP SIP Service Call.362* PASS_THROUGH_FW_CMD_ID - Fixed SiP SVC call ID for FW specific calls.363*364* Return: Returns status, either success or error+reason365*/366int zynqmp_pm_invoke_fw_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...)367{368/*369* Added SIP service call Function Identifier370* Make sure to stay in x0 register371*/372u64 smc_arg[SMC_ARG_CNT_64];373int ret, i;374va_list arg_list;375u32 args[SMC_ARG_CNT_32] = {0};376u32 module_id;377378if (num_args > SMC_ARG_CNT_32)379return -EINVAL;380381va_start(arg_list, num_args);382383/* Check if feature is supported or not */384ret = zynqmp_pm_feature(pm_api_id);385if (ret < 0)386return ret;387388for (i = 0; i < num_args; i++)389args[i] = va_arg(arg_list, u32);390391va_end(arg_list);392393module_id = FIELD_GET(PLM_MODULE_ID_MASK, pm_api_id);394395if (module_id == 0)396module_id = XPM_MODULE_ID;397398smc_arg[0] = PM_SIP_SVC | PASS_THROUGH_FW_CMD_ID;399smc_arg[1] = ((u64)args[0] << 32U) | FIELD_PREP(PLM_MODULE_ID_MASK, module_id) |400(pm_api_id & API_ID_MASK);401for (i = 1; i < (SMC_ARG_CNT_64 - 1); i++)402smc_arg[i + 1] = ((u64)args[(i * 2)] << 32U) | args[(i * 2) - 1];403404return do_fw_call(ret_payload, 8, smc_arg[0], smc_arg[1], smc_arg[2], smc_arg[3],405smc_arg[4], smc_arg[5], smc_arg[6], smc_arg[7]);406}407408/**409* zynqmp_pm_invoke_fn() - Invoke the system-level platform management layer410* caller function depending on the configuration411* @pm_api_id: Requested PM-API call412* @ret_payload: Returned value array413* @num_args: Number of arguments to requested PM-API call414*415* Invoke platform management function for SMC or HVC call, depending on416* configuration.417* Following SMC Calling Convention (SMCCC) for SMC64:418* Pm Function Identifier,419* PM_SIP_SVC + PM_API_ID =420* ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT)421* ((SMC_64) << FUNCID_CC_SHIFT)422* ((SIP_START) << FUNCID_OEN_SHIFT)423* ((PM_API_ID) & FUNCID_NUM_MASK))424*425* PM_SIP_SVC - Registered ZynqMP SIP Service Call.426* PM_API_ID - Platform Management API ID.427*428* Return: Returns status, either success or error+reason429*/430int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...)431{432/*433* Added SIP service call Function Identifier434* Make sure to stay in x0 register435*/436u64 smc_arg[8];437int ret, i;438va_list arg_list;439u32 args[14] = {0};440441if (num_args > 14)442return -EINVAL;443444va_start(arg_list, num_args);445446/* Check if feature is supported or not */447ret = zynqmp_pm_feature(pm_api_id);448if (ret < 0)449return ret;450451for (i = 0; i < num_args; i++)452args[i] = va_arg(arg_list, u32);453454va_end(arg_list);455456smc_arg[0] = PM_SIP_SVC | pm_api_id;457for (i = 0; i < 7; i++)458smc_arg[i + 1] = ((u64)args[(i * 2) + 1] << 32) | args[i * 2];459460return do_fw_call(ret_payload, 8, smc_arg[0], smc_arg[1], smc_arg[2], smc_arg[3],461smc_arg[4], smc_arg[5], smc_arg[6], smc_arg[7]);462}463464static u32 pm_api_version;465static u32 pm_tz_version;466static u32 pm_family_code;467static u32 pm_sub_family_code;468469int zynqmp_pm_register_sgi(u32 sgi_num, u32 reset)470{471int ret;472473ret = zynqmp_pm_invoke_fn(TF_A_PM_REGISTER_SGI, NULL, 2, sgi_num, reset);474if (ret != -EOPNOTSUPP && !ret)475return ret;476477/* try old implementation as fallback strategy if above fails */478return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, IOCTL_REGISTER_SGI, sgi_num, reset);479}480481/**482* zynqmp_pm_get_api_version() - Get version number of PMU PM firmware483* @version: Returned version value484*485* Return: Returns status, either success or error+reason486*/487int zynqmp_pm_get_api_version(u32 *version)488{489u32 ret_payload[PAYLOAD_ARG_CNT];490int ret;491492if (!version)493return -EINVAL;494495/* Check is PM API version already verified */496if (pm_api_version > 0) {497*version = pm_api_version;498return 0;499}500ret = zynqmp_pm_invoke_fn(PM_GET_API_VERSION, ret_payload, 0);501*version = ret_payload[1];502503return ret;504}505EXPORT_SYMBOL_GPL(zynqmp_pm_get_api_version);506507/**508* zynqmp_pm_get_chipid - Get silicon ID registers509* @idcode: IDCODE register510* @version: version register511*512* Return: Returns the status of the operation and the idcode and version513* registers in @idcode and @version.514*/515int zynqmp_pm_get_chipid(u32 *idcode, u32 *version)516{517u32 ret_payload[PAYLOAD_ARG_CNT];518int ret;519520if (!idcode || !version)521return -EINVAL;522523ret = zynqmp_pm_invoke_fn(PM_GET_CHIPID, ret_payload, 0);524*idcode = ret_payload[1];525*version = ret_payload[2];526527return ret;528}529EXPORT_SYMBOL_GPL(zynqmp_pm_get_chipid);530531/**532* zynqmp_pm_get_family_info() - Get family info of platform533* @family: Returned family code value534* @subfamily: Returned sub-family code value535*536* Return: Returns status, either success or error+reason537*/538int zynqmp_pm_get_family_info(u32 *family, u32 *subfamily)539{540u32 ret_payload[PAYLOAD_ARG_CNT];541u32 idcode;542int ret;543544/* Check is family or sub-family code already received */545if (pm_family_code && pm_sub_family_code) {546*family = pm_family_code;547*subfamily = pm_sub_family_code;548return 0;549}550551ret = zynqmp_pm_invoke_fn(PM_GET_CHIPID, ret_payload, 0);552if (ret < 0)553return ret;554555idcode = ret_payload[1];556pm_family_code = FIELD_GET(FAMILY_CODE_MASK, idcode);557pm_sub_family_code = FIELD_GET(SUB_FAMILY_CODE_MASK, idcode);558*family = pm_family_code;559*subfamily = pm_sub_family_code;560561return 0;562}563EXPORT_SYMBOL_GPL(zynqmp_pm_get_family_info);564565/**566* zynqmp_pm_get_sip_svc_version() - Get SiP service call version567* @version: Returned version value568*569* Return: Returns status, either success or error+reason570*/571static int zynqmp_pm_get_sip_svc_version(u32 *version)572{573struct arm_smccc_res res;574u64 args[SMC_ARG_CNT_64] = {0};575576if (!version)577return -EINVAL;578579/* Check if SiP SVC version already verified */580if (sip_svc_version > 0) {581*version = sip_svc_version;582return 0;583}584585args[0] = GET_SIP_SVC_VERSION;586587arm_smccc_smc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], &res);588589*version = ((lower_32_bits(res.a0) << 16U) | lower_32_bits(res.a1));590591return zynqmp_pm_ret_code(XST_PM_SUCCESS);592}593594/**595* zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version596* @version: Returned version value597*598* Return: Returns status, either success or error+reason599*/600static int zynqmp_pm_get_trustzone_version(u32 *version)601{602u32 ret_payload[PAYLOAD_ARG_CNT];603int ret;604605if (!version)606return -EINVAL;607608/* Check is PM trustzone version already verified */609if (pm_tz_version > 0) {610*version = pm_tz_version;611return 0;612}613ret = zynqmp_pm_invoke_fn(PM_GET_TRUSTZONE_VERSION, ret_payload, 0);614*version = ret_payload[1];615616return ret;617}618619/**620* get_set_conduit_method() - Choose SMC or HVC based communication621* @np: Pointer to the device_node structure622*623* Use SMC or HVC-based functions to communicate with EL2/EL3.624*625* Return: Returns 0 on success or error code626*/627static int get_set_conduit_method(struct device_node *np)628{629const char *method;630631if (of_property_read_string(np, "method", &method)) {632pr_warn("%s missing \"method\" property\n", __func__);633return -ENXIO;634}635636if (!strcmp("hvc", method)) {637do_fw_call = do_fw_call_hvc;638} else if (!strcmp("smc", method)) {639do_fw_call = do_fw_call_smc;640} else {641pr_warn("%s Invalid \"method\" property: %s\n",642__func__, method);643return -EINVAL;644}645646return 0;647}648649/**650* zynqmp_pm_query_data() - Get query data from firmware651* @qdata: Variable to the zynqmp_pm_query_data structure652* @out: Returned output value653*654* Return: Returns status, either success or error+reason655*/656int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out)657{658int ret, i = 0;659u32 ret_payload[PAYLOAD_ARG_CNT] = {0};660661if (sip_svc_version >= SIP_SVC_PASSTHROUGH_VERSION) {662ret = zynqmp_pm_invoke_fw_fn(PM_QUERY_DATA, ret_payload, 4,663qdata.qid, qdata.arg1,664qdata.arg2, qdata.arg3);665/* To support backward compatibility */666if (!ret && !ret_payload[0]) {667/*668* TF-A passes return status on 0th index but669* api to get clock name reads data from 0th670* index so pass data at 0th index instead of671* return status672*/673if (qdata.qid == PM_QID_CLOCK_GET_NAME ||674qdata.qid == PM_QID_PINCTRL_GET_FUNCTION_NAME)675i = 1;676677for (; i < PAYLOAD_ARG_CNT; i++, out++)678*out = ret_payload[i];679680return ret;681}682}683684ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, out, 4, qdata.qid,685qdata.arg1, qdata.arg2, qdata.arg3);686687/*688* For clock name query, all bytes in SMC response are clock name689* characters and return code is always success. For invalid clocks,690* clock name bytes would be zeros.691*/692return qdata.qid == PM_QID_CLOCK_GET_NAME ? 0 : ret;693}694EXPORT_SYMBOL_GPL(zynqmp_pm_query_data);695696/**697* zynqmp_pm_clock_enable() - Enable the clock for given id698* @clock_id: ID of the clock to be enabled699*700* This function is used by master to enable the clock701* including peripherals and PLL clocks.702*703* Return: Returns status, either success or error+reason704*/705int zynqmp_pm_clock_enable(u32 clock_id)706{707return zynqmp_pm_invoke_fn(PM_CLOCK_ENABLE, NULL, 1, clock_id);708}709EXPORT_SYMBOL_GPL(zynqmp_pm_clock_enable);710711/**712* zynqmp_pm_clock_disable() - Disable the clock for given id713* @clock_id: ID of the clock to be disable714*715* This function is used by master to disable the clock716* including peripherals and PLL clocks.717*718* Return: Returns status, either success or error+reason719*/720int zynqmp_pm_clock_disable(u32 clock_id)721{722return zynqmp_pm_invoke_fn(PM_CLOCK_DISABLE, NULL, 1, clock_id);723}724EXPORT_SYMBOL_GPL(zynqmp_pm_clock_disable);725726/**727* zynqmp_pm_clock_getstate() - Get the clock state for given id728* @clock_id: ID of the clock to be queried729* @state: 1/0 (Enabled/Disabled)730*731* This function is used by master to get the state of clock732* including peripherals and PLL clocks.733*734* Return: Returns status, either success or error+reason735*/736int zynqmp_pm_clock_getstate(u32 clock_id, u32 *state)737{738u32 ret_payload[PAYLOAD_ARG_CNT];739int ret;740741ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETSTATE, ret_payload, 1, clock_id);742*state = ret_payload[1];743744return ret;745}746EXPORT_SYMBOL_GPL(zynqmp_pm_clock_getstate);747748/**749* zynqmp_pm_clock_setdivider() - Set the clock divider for given id750* @clock_id: ID of the clock751* @divider: divider value752*753* This function is used by master to set divider for any clock754* to achieve desired rate.755*756* Return: Returns status, either success or error+reason757*/758int zynqmp_pm_clock_setdivider(u32 clock_id, u32 divider)759{760return zynqmp_pm_invoke_fn(PM_CLOCK_SETDIVIDER, NULL, 2, clock_id, divider);761}762EXPORT_SYMBOL_GPL(zynqmp_pm_clock_setdivider);763764/**765* zynqmp_pm_clock_getdivider() - Get the clock divider for given id766* @clock_id: ID of the clock767* @divider: divider value768*769* This function is used by master to get divider values770* for any clock.771*772* Return: Returns status, either success or error+reason773*/774int zynqmp_pm_clock_getdivider(u32 clock_id, u32 *divider)775{776u32 ret_payload[PAYLOAD_ARG_CNT];777int ret;778779ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETDIVIDER, ret_payload, 1, clock_id);780*divider = ret_payload[1];781782return ret;783}784EXPORT_SYMBOL_GPL(zynqmp_pm_clock_getdivider);785786/**787* zynqmp_pm_clock_setparent() - Set the clock parent for given id788* @clock_id: ID of the clock789* @parent_id: parent id790*791* This function is used by master to set parent for any clock.792*793* Return: Returns status, either success or error+reason794*/795int zynqmp_pm_clock_setparent(u32 clock_id, u32 parent_id)796{797return zynqmp_pm_invoke_fn(PM_CLOCK_SETPARENT, NULL, 2, clock_id, parent_id);798}799EXPORT_SYMBOL_GPL(zynqmp_pm_clock_setparent);800801/**802* zynqmp_pm_clock_getparent() - Get the clock parent for given id803* @clock_id: ID of the clock804* @parent_id: parent id805*806* This function is used by master to get parent index807* for any clock.808*809* Return: Returns status, either success or error+reason810*/811int zynqmp_pm_clock_getparent(u32 clock_id, u32 *parent_id)812{813u32 ret_payload[PAYLOAD_ARG_CNT];814int ret;815816ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETPARENT, ret_payload, 1, clock_id);817*parent_id = ret_payload[1];818819return ret;820}821EXPORT_SYMBOL_GPL(zynqmp_pm_clock_getparent);822823/**824* zynqmp_pm_set_pll_frac_mode() - PM API for set PLL mode825*826* @clk_id: PLL clock ID827* @mode: PLL mode (PLL_MODE_FRAC/PLL_MODE_INT)828*829* This function sets PLL mode830*831* Return: Returns status, either success or error+reason832*/833int zynqmp_pm_set_pll_frac_mode(u32 clk_id, u32 mode)834{835return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_SET_PLL_FRAC_MODE, clk_id, mode);836}837EXPORT_SYMBOL_GPL(zynqmp_pm_set_pll_frac_mode);838839/**840* zynqmp_pm_get_pll_frac_mode() - PM API for get PLL mode841*842* @clk_id: PLL clock ID843* @mode: PLL mode844*845* This function return current PLL mode846*847* Return: Returns status, either success or error+reason848*/849int zynqmp_pm_get_pll_frac_mode(u32 clk_id, u32 *mode)850{851return zynqmp_pm_invoke_fn(PM_IOCTL, mode, 3, 0, IOCTL_GET_PLL_FRAC_MODE, clk_id);852}853EXPORT_SYMBOL_GPL(zynqmp_pm_get_pll_frac_mode);854855/**856* zynqmp_pm_set_pll_frac_data() - PM API for setting pll fraction data857*858* @clk_id: PLL clock ID859* @data: fraction data860*861* This function sets fraction data.862* It is valid for fraction mode only.863*864* Return: Returns status, either success or error+reason865*/866int zynqmp_pm_set_pll_frac_data(u32 clk_id, u32 data)867{868return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_SET_PLL_FRAC_DATA, clk_id, data);869}870EXPORT_SYMBOL_GPL(zynqmp_pm_set_pll_frac_data);871872/**873* zynqmp_pm_get_pll_frac_data() - PM API for getting pll fraction data874*875* @clk_id: PLL clock ID876* @data: fraction data877*878* This function returns fraction data value.879*880* Return: Returns status, either success or error+reason881*/882int zynqmp_pm_get_pll_frac_data(u32 clk_id, u32 *data)883{884return zynqmp_pm_invoke_fn(PM_IOCTL, data, 3, 0, IOCTL_GET_PLL_FRAC_DATA, clk_id);885}886EXPORT_SYMBOL_GPL(zynqmp_pm_get_pll_frac_data);887888/**889* zynqmp_pm_set_sd_tapdelay() - Set tap delay for the SD device890*891* @node_id: Node ID of the device892* @type: Type of tap delay to set (input/output)893* @value: Value to set fot the tap delay894*895* This function sets input/output tap delay for the SD device.896*897* Return: Returns status, either success or error+reason898*/899int zynqmp_pm_set_sd_tapdelay(u32 node_id, u32 type, u32 value)900{901u32 reg = (type == PM_TAPDELAY_INPUT) ? SD_ITAPDLY : SD_OTAPDLYSEL;902u32 mask = (node_id == NODE_SD_0) ? GENMASK(15, 0) : GENMASK(31, 16);903904if (value) {905return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, node_id, IOCTL_SET_SD_TAPDELAY, type,906value);907}908909/*910* Work around completely misdesigned firmware API on Xilinx ZynqMP.911* The IOCTL_SET_SD_TAPDELAY firmware call allows the caller to only912* ever set IOU_SLCR SD_ITAPDLY Register SD0_ITAPDLYENA/SD1_ITAPDLYENA913* bits, but there is no matching call to clear those bits. If those914* bits are not cleared, SDMMC tuning may fail.915*916* Luckily, there are PM_MMIO_READ/PM_MMIO_WRITE calls which seem to917* allow complete unrestricted access to all address space, including918* IOU_SLCR SD_ITAPDLY Register and all the other registers, access919* to which was supposed to be protected by the current firmware API.920*921* Use PM_MMIO_READ/PM_MMIO_WRITE to re-implement the missing counter922* part of IOCTL_SET_SD_TAPDELAY which clears SDx_ITAPDLYENA bits.923*/924return zynqmp_pm_invoke_fn(PM_MMIO_WRITE, NULL, 2, reg, mask);925}926EXPORT_SYMBOL_GPL(zynqmp_pm_set_sd_tapdelay);927928/**929* zynqmp_pm_sd_dll_reset() - Reset DLL logic930*931* @node_id: Node ID of the device932* @type: Reset type933*934* This function resets DLL logic for the SD device.935*936* Return: Returns status, either success or error+reason937*/938int zynqmp_pm_sd_dll_reset(u32 node_id, u32 type)939{940return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, node_id, IOCTL_SD_DLL_RESET, type);941}942EXPORT_SYMBOL_GPL(zynqmp_pm_sd_dll_reset);943944/**945* zynqmp_pm_ospi_mux_select() - OSPI Mux selection946*947* @dev_id: Device Id of the OSPI device.948* @select: OSPI Mux select value.949*950* This function select the OSPI Mux.951*952* Return: Returns status, either success or error+reason953*/954int zynqmp_pm_ospi_mux_select(u32 dev_id, u32 select)955{956return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, dev_id, IOCTL_OSPI_MUX_SELECT, select);957}958EXPORT_SYMBOL_GPL(zynqmp_pm_ospi_mux_select);959960/**961* zynqmp_pm_write_ggs() - PM API for writing global general storage (ggs)962* @index: GGS register index963* @value: Register value to be written964*965* This function writes value to GGS register.966*967* Return: Returns status, either success or error+reason968*/969int zynqmp_pm_write_ggs(u32 index, u32 value)970{971return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_WRITE_GGS, index, value);972}973EXPORT_SYMBOL_GPL(zynqmp_pm_write_ggs);974975/**976* zynqmp_pm_read_ggs() - PM API for reading global general storage (ggs)977* @index: GGS register index978* @value: Register value to be written979*980* This function returns GGS register value.981*982* Return: Returns status, either success or error+reason983*/984int zynqmp_pm_read_ggs(u32 index, u32 *value)985{986return zynqmp_pm_invoke_fn(PM_IOCTL, value, 3, 0, IOCTL_READ_GGS, index);987}988EXPORT_SYMBOL_GPL(zynqmp_pm_read_ggs);989990/**991* zynqmp_pm_write_pggs() - PM API for writing persistent global general992* storage (pggs)993* @index: PGGS register index994* @value: Register value to be written995*996* This function writes value to PGGS register.997*998* Return: Returns status, either success or error+reason999*/1000int zynqmp_pm_write_pggs(u32 index, u32 value)1001{1002return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_WRITE_PGGS, index, value);1003}1004EXPORT_SYMBOL_GPL(zynqmp_pm_write_pggs);10051006/**1007* zynqmp_pm_read_pggs() - PM API for reading persistent global general1008* storage (pggs)1009* @index: PGGS register index1010* @value: Register value to be written1011*1012* This function returns PGGS register value.1013*1014* Return: Returns status, either success or error+reason1015*/1016int zynqmp_pm_read_pggs(u32 index, u32 *value)1017{1018return zynqmp_pm_invoke_fn(PM_IOCTL, value, 3, 0, IOCTL_READ_PGGS, index);1019}1020EXPORT_SYMBOL_GPL(zynqmp_pm_read_pggs);10211022int zynqmp_pm_set_tapdelay_bypass(u32 index, u32 value)1023{1024return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_SET_TAPDELAY_BYPASS, index, value);1025}1026EXPORT_SYMBOL_GPL(zynqmp_pm_set_tapdelay_bypass);10271028/**1029* zynqmp_pm_set_boot_health_status() - PM API for setting healthy boot status1030* @value: Status value to be written1031*1032* This function sets healthy bit value to indicate boot health status1033* to firmware.1034*1035* Return: Returns status, either success or error+reason1036*/1037int zynqmp_pm_set_boot_health_status(u32 value)1038{1039return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, 0, IOCTL_SET_BOOT_HEALTH_STATUS, value);1040}10411042/**1043* zynqmp_pm_reset_assert - Request setting of reset (1 - assert, 0 - release)1044* @reset: Reset to be configured1045* @assert_flag: Flag stating should reset be asserted (1) or1046* released (0)1047*1048* Return: Returns status, either success or error+reason1049*/1050int zynqmp_pm_reset_assert(const u32 reset,1051const enum zynqmp_pm_reset_action assert_flag)1052{1053return zynqmp_pm_invoke_fn(PM_RESET_ASSERT, NULL, 2, reset, assert_flag);1054}1055EXPORT_SYMBOL_GPL(zynqmp_pm_reset_assert);10561057/**1058* zynqmp_pm_reset_get_status - Get status of the reset1059* @reset: Reset whose status should be returned1060* @status: Returned status1061*1062* Return: Returns status, either success or error+reason1063*/1064int zynqmp_pm_reset_get_status(const u32 reset, u32 *status)1065{1066u32 ret_payload[PAYLOAD_ARG_CNT];1067int ret;10681069if (!status)1070return -EINVAL;10711072ret = zynqmp_pm_invoke_fn(PM_RESET_GET_STATUS, ret_payload, 1, reset);1073*status = ret_payload[1];10741075return ret;1076}1077EXPORT_SYMBOL_GPL(zynqmp_pm_reset_get_status);10781079/**1080* zynqmp_pm_fpga_load - Perform the fpga load1081* @address: Address to write to1082* @size: pl bitstream size1083* @flags: Bitstream type1084* -XILINX_ZYNQMP_PM_FPGA_FULL: FPGA full reconfiguration1085* -XILINX_ZYNQMP_PM_FPGA_PARTIAL: FPGA partial reconfiguration1086*1087* This function provides access to pmufw. To transfer1088* the required bitstream into PL.1089*1090* Return: Returns status, either success or error+reason1091*/1092int zynqmp_pm_fpga_load(const u64 address, const u32 size, const u32 flags)1093{1094u32 ret_payload[PAYLOAD_ARG_CNT];1095int ret;10961097ret = zynqmp_pm_invoke_fn(PM_FPGA_LOAD, ret_payload, 4, lower_32_bits(address),1098upper_32_bits(address), size, flags);1099if (ret_payload[0])1100return -ret_payload[0];11011102return ret;1103}1104EXPORT_SYMBOL_GPL(zynqmp_pm_fpga_load);11051106/**1107* zynqmp_pm_fpga_get_status - Read value from PCAP status register1108* @value: Value to read1109*1110* This function provides access to the pmufw to get the PCAP1111* status1112*1113* Return: Returns status, either success or error+reason1114*/1115int zynqmp_pm_fpga_get_status(u32 *value)1116{1117u32 ret_payload[PAYLOAD_ARG_CNT];1118int ret;11191120if (!value)1121return -EINVAL;11221123ret = zynqmp_pm_invoke_fn(PM_FPGA_GET_STATUS, ret_payload, 0);1124*value = ret_payload[1];11251126return ret;1127}1128EXPORT_SYMBOL_GPL(zynqmp_pm_fpga_get_status);11291130/**1131* zynqmp_pm_fpga_get_config_status - Get the FPGA configuration status.1132* @value: Buffer to store FPGA configuration status.1133*1134* This function provides access to the pmufw to get the FPGA configuration1135* status1136*1137* Return: 0 on success, a negative value on error1138*/1139int zynqmp_pm_fpga_get_config_status(u32 *value)1140{1141u32 ret_payload[PAYLOAD_ARG_CNT];1142int ret;11431144if (!value)1145return -EINVAL;11461147ret = zynqmp_pm_invoke_fn(PM_FPGA_READ, ret_payload, 4,1148XILINX_ZYNQMP_PM_FPGA_CONFIG_STAT_OFFSET, 0, 0,1149XILINX_ZYNQMP_PM_FPGA_READ_CONFIG_REG);11501151*value = ret_payload[1];11521153return ret;1154}1155EXPORT_SYMBOL_GPL(zynqmp_pm_fpga_get_config_status);11561157/**1158* zynqmp_pm_pinctrl_request - Request Pin from firmware1159* @pin: Pin number to request1160*1161* This function requests pin from firmware.1162*1163* Return: Returns status, either success or error+reason.1164*/1165int zynqmp_pm_pinctrl_request(const u32 pin)1166{1167return zynqmp_pm_invoke_fn(PM_PINCTRL_REQUEST, NULL, 1, pin);1168}1169EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_request);11701171/**1172* zynqmp_pm_pinctrl_release - Inform firmware that Pin control is released1173* @pin: Pin number to release1174*1175* This function release pin from firmware.1176*1177* Return: Returns status, either success or error+reason.1178*/1179int zynqmp_pm_pinctrl_release(const u32 pin)1180{1181return zynqmp_pm_invoke_fn(PM_PINCTRL_RELEASE, NULL, 1, pin);1182}1183EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_release);11841185/**1186* zynqmp_pm_pinctrl_set_function - Set requested function for the pin1187* @pin: Pin number1188* @id: Function ID to set1189*1190* This function sets requested function for the given pin.1191*1192* Return: Returns status, either success or error+reason.1193*/1194int zynqmp_pm_pinctrl_set_function(const u32 pin, const u32 id)1195{1196return zynqmp_pm_invoke_fn(PM_PINCTRL_SET_FUNCTION, NULL, 2, pin, id);1197}1198EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_set_function);11991200/**1201* zynqmp_pm_pinctrl_get_config - Get configuration parameter for the pin1202* @pin: Pin number1203* @param: Parameter to get1204* @value: Buffer to store parameter value1205*1206* This function gets requested configuration parameter for the given pin.1207*1208* Return: Returns status, either success or error+reason.1209*/1210int zynqmp_pm_pinctrl_get_config(const u32 pin, const u32 param,1211u32 *value)1212{1213u32 ret_payload[PAYLOAD_ARG_CNT];1214int ret;12151216if (!value)1217return -EINVAL;12181219ret = zynqmp_pm_invoke_fn(PM_PINCTRL_CONFIG_PARAM_GET, ret_payload, 2, pin, param);1220*value = ret_payload[1];12211222return ret;1223}1224EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_get_config);12251226/**1227* zynqmp_pm_pinctrl_set_config - Set configuration parameter for the pin1228* @pin: Pin number1229* @param: Parameter to set1230* @value: Parameter value to set1231*1232* This function sets requested configuration parameter for the given pin.1233*1234* Return: Returns status, either success or error+reason.1235*/1236int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param,1237u32 value)1238{1239int ret;12401241if (pm_family_code == ZYNQMP_FAMILY_CODE &&1242param == PM_PINCTRL_CONFIG_TRI_STATE) {1243ret = zynqmp_pm_feature(PM_PINCTRL_CONFIG_PARAM_SET);1244if (ret < PM_PINCTRL_PARAM_SET_VERSION) {1245pr_warn("The requested pinctrl feature is not supported in the current firmware.\n"1246"Expected firmware version is 2023.1 and above for this feature to work.\r\n");1247return -EOPNOTSUPP;1248}1249}12501251return zynqmp_pm_invoke_fn(PM_PINCTRL_CONFIG_PARAM_SET, NULL, 3, pin, param, value);1252}1253EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_set_config);12541255/**1256* zynqmp_pm_bootmode_read() - PM Config API for read bootpin status1257* @ps_mode: Returned output value of ps_mode1258*1259* This API function is to be used for notify the power management controller1260* to read bootpin status.1261*1262* Return: status, either success or error+reason1263*/1264unsigned int zynqmp_pm_bootmode_read(u32 *ps_mode)1265{1266unsigned int ret;1267u32 ret_payload[PAYLOAD_ARG_CNT];12681269ret = zynqmp_pm_invoke_fn(PM_MMIO_READ, ret_payload, 1, CRL_APB_BOOT_PIN_CTRL);12701271*ps_mode = ret_payload[1];12721273return ret;1274}1275EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_read);12761277/**1278* zynqmp_pm_bootmode_write() - PM Config API for Configure bootpin1279* @ps_mode: Value to be written to the bootpin ctrl register1280*1281* This API function is to be used for notify the power management controller1282* to configure bootpin.1283*1284* Return: Returns status, either success or error+reason1285*/1286int zynqmp_pm_bootmode_write(u32 ps_mode)1287{1288return zynqmp_pm_invoke_fn(PM_MMIO_WRITE, NULL, 3, CRL_APB_BOOT_PIN_CTRL,1289CRL_APB_BOOTPIN_CTRL_MASK, ps_mode);1290}1291EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_write);12921293/**1294* zynqmp_pm_init_finalize() - PM call to inform firmware that the caller1295* master has initialized its own power management1296*1297* Return: Returns status, either success or error+reason1298*1299* This API function is to be used for notify the power management controller1300* about the completed power management initialization.1301*/1302static int zynqmp_pm_init_finalize(void)1303{1304return zynqmp_pm_invoke_fn(PM_PM_INIT_FINALIZE, NULL, 0);1305}13061307/**1308* zynqmp_pm_set_suspend_mode() - Set system suspend mode1309* @mode: Mode to set for system suspend1310*1311* This API function is used to set mode of system suspend.1312*1313* Return: Returns status, either success or error+reason1314*/1315int zynqmp_pm_set_suspend_mode(u32 mode)1316{1317return zynqmp_pm_invoke_fn(PM_SET_SUSPEND_MODE, NULL, 1, mode);1318}1319EXPORT_SYMBOL_GPL(zynqmp_pm_set_suspend_mode);13201321/**1322* zynqmp_pm_request_node() - Request a node with specific capabilities1323* @node: Node ID of the slave1324* @capabilities: Requested capabilities of the slave1325* @qos: Quality of service (not supported)1326* @ack: Flag to specify whether acknowledge is requested1327*1328* This function is used by master to request particular node from firmware.1329* Every master must request node before using it.1330*1331* Return: Returns status, either success or error+reason1332*/1333int zynqmp_pm_request_node(const u32 node, const u32 capabilities,1334const u32 qos, const enum zynqmp_pm_request_ack ack)1335{1336return zynqmp_pm_invoke_fn(PM_REQUEST_NODE, NULL, 4, node, capabilities, qos, ack);1337}1338EXPORT_SYMBOL_GPL(zynqmp_pm_request_node);13391340/**1341* zynqmp_pm_release_node() - Release a node1342* @node: Node ID of the slave1343*1344* This function is used by master to inform firmware that master1345* has released node. Once released, master must not use that node1346* without re-request.1347*1348* Return: Returns status, either success or error+reason1349*/1350int zynqmp_pm_release_node(const u32 node)1351{1352return zynqmp_pm_invoke_fn(PM_RELEASE_NODE, NULL, 1, node);1353}1354EXPORT_SYMBOL_GPL(zynqmp_pm_release_node);13551356/**1357* zynqmp_pm_get_rpu_mode() - Get RPU mode1358* @node_id: Node ID of the device1359* @rpu_mode: return by reference value1360* either split or lockstep1361*1362* Return: return 0 on success or error+reason.1363* if success, then rpu_mode will be set1364* to current rpu mode.1365*/1366int zynqmp_pm_get_rpu_mode(u32 node_id, enum rpu_oper_mode *rpu_mode)1367{1368u32 ret_payload[PAYLOAD_ARG_CNT];1369int ret;13701371ret = zynqmp_pm_invoke_fn(PM_IOCTL, ret_payload, 2, node_id, IOCTL_GET_RPU_OPER_MODE);13721373/* only set rpu_mode if no error */1374if (ret == XST_PM_SUCCESS)1375*rpu_mode = ret_payload[0];13761377return ret;1378}1379EXPORT_SYMBOL_GPL(zynqmp_pm_get_rpu_mode);13801381/**1382* zynqmp_pm_set_rpu_mode() - Set RPU mode1383* @node_id: Node ID of the device1384* @rpu_mode: Argument 1 to requested IOCTL call. either split or lockstep1385*1386* This function is used to set RPU mode to split or1387* lockstep1388*1389* Return: Returns status, either success or error+reason1390*/1391int zynqmp_pm_set_rpu_mode(u32 node_id, enum rpu_oper_mode rpu_mode)1392{1393return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, node_id, IOCTL_SET_RPU_OPER_MODE,1394(u32)rpu_mode);1395}1396EXPORT_SYMBOL_GPL(zynqmp_pm_set_rpu_mode);13971398/**1399* zynqmp_pm_set_tcm_config - configure TCM1400* @node_id: Firmware specific TCM subsystem ID1401* @tcm_mode: Argument 1 to requested IOCTL call1402* either PM_RPU_TCM_COMB or PM_RPU_TCM_SPLIT1403*1404* This function is used to set RPU mode to split or combined1405*1406* Return: status: 0 for success, else failure1407*/1408int zynqmp_pm_set_tcm_config(u32 node_id, enum rpu_tcm_comb tcm_mode)1409{1410return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, node_id, IOCTL_TCM_COMB_CONFIG,1411(u32)tcm_mode);1412}1413EXPORT_SYMBOL_GPL(zynqmp_pm_set_tcm_config);14141415/**1416* zynqmp_pm_force_pwrdwn - PM call to request for another PU or subsystem to1417* be powered down forcefully1418* @node: Node ID of the targeted PU or subsystem1419* @ack: Flag to specify whether acknowledge is requested1420*1421* Return: status, either success or error+reason1422*/1423int zynqmp_pm_force_pwrdwn(const u32 node,1424const enum zynqmp_pm_request_ack ack)1425{1426return zynqmp_pm_invoke_fn(PM_FORCE_POWERDOWN, NULL, 2, node, ack);1427}1428EXPORT_SYMBOL_GPL(zynqmp_pm_force_pwrdwn);14291430/**1431* zynqmp_pm_request_wake - PM call to wake up selected master or subsystem1432* @node: Node ID of the master or subsystem1433* @set_addr: Specifies whether the address argument is relevant1434* @address: Address from which to resume when woken up1435* @ack: Flag to specify whether acknowledge requested1436*1437* Return: status, either success or error+reason1438*/1439int zynqmp_pm_request_wake(const u32 node,1440const bool set_addr,1441const u64 address,1442const enum zynqmp_pm_request_ack ack)1443{1444/* set_addr flag is encoded into 1st bit of address */1445return zynqmp_pm_invoke_fn(PM_REQUEST_WAKEUP, NULL, 4, node, address | set_addr,1446address >> 32, ack);1447}1448EXPORT_SYMBOL_GPL(zynqmp_pm_request_wake);14491450/**1451* zynqmp_pm_set_requirement() - PM call to set requirement for PM slaves1452* @node: Node ID of the slave1453* @capabilities: Requested capabilities of the slave1454* @qos: Quality of service (not supported)1455* @ack: Flag to specify whether acknowledge is requested1456*1457* This API function is to be used for slaves a PU already has requested1458* to change its capabilities.1459*1460* Return: Returns status, either success or error+reason1461*/1462int zynqmp_pm_set_requirement(const u32 node, const u32 capabilities,1463const u32 qos,1464const enum zynqmp_pm_request_ack ack)1465{1466return zynqmp_pm_invoke_fn(PM_SET_REQUIREMENT, NULL, 4, node, capabilities, qos, ack);1467}1468EXPORT_SYMBOL_GPL(zynqmp_pm_set_requirement);14691470/**1471* zynqmp_pm_load_pdi - Load and process PDI1472* @src: Source device where PDI is located1473* @address: PDI src address1474*1475* This function provides support to load PDI from linux1476*1477* Return: Returns status, either success or error+reason1478*/1479int zynqmp_pm_load_pdi(const u32 src, const u64 address)1480{1481return zynqmp_pm_invoke_fn(PM_LOAD_PDI, NULL, 3, src, lower_32_bits(address),1482upper_32_bits(address));1483}1484EXPORT_SYMBOL_GPL(zynqmp_pm_load_pdi);14851486/**1487* zynqmp_pm_aes_engine - Access AES hardware to encrypt/decrypt the data using1488* AES-GCM core.1489* @address: Address of the AesParams structure.1490* @out: Returned output value1491*1492* Return: Returns status, either success or error code.1493*/1494int zynqmp_pm_aes_engine(const u64 address, u32 *out)1495{1496u32 ret_payload[PAYLOAD_ARG_CNT];1497int ret;14981499if (!out)1500return -EINVAL;15011502ret = zynqmp_pm_invoke_fn(PM_SECURE_AES, ret_payload, 2, upper_32_bits(address),1503lower_32_bits(address));1504*out = ret_payload[1];15051506return ret;1507}1508EXPORT_SYMBOL_GPL(zynqmp_pm_aes_engine);15091510/**1511* zynqmp_pm_efuse_access - Provides access to efuse memory.1512* @address: Address of the efuse params structure1513* @out: Returned output value1514*1515* Return: Returns status, either success or error code.1516*/1517int zynqmp_pm_efuse_access(const u64 address, u32 *out)1518{1519u32 ret_payload[PAYLOAD_ARG_CNT];1520int ret;15211522if (!out)1523return -EINVAL;15241525ret = zynqmp_pm_invoke_fn(PM_EFUSE_ACCESS, ret_payload, 2,1526upper_32_bits(address),1527lower_32_bits(address));1528*out = ret_payload[1];15291530return ret;1531}1532EXPORT_SYMBOL_GPL(zynqmp_pm_efuse_access);15331534/**1535* zynqmp_pm_sha_hash - Access the SHA engine to calculate the hash1536* @address: Address of the data/ Address of output buffer where1537* hash should be stored.1538* @size: Size of the data.1539* @flags:1540* BIT(0) - for initializing csudma driver and SHA3(Here address1541* and size inputs can be NULL).1542* BIT(1) - to call Sha3_Update API which can be called multiple1543* times when data is not contiguous.1544* BIT(2) - to get final hash of the whole updated data.1545* Hash will be overwritten at provided address with1546* 48 bytes.1547*1548* Return: Returns status, either success or error code.1549*/1550int zynqmp_pm_sha_hash(const u64 address, const u32 size, const u32 flags)1551{1552u32 lower_addr = lower_32_bits(address);1553u32 upper_addr = upper_32_bits(address);15541555return zynqmp_pm_invoke_fn(PM_SECURE_SHA, NULL, 4, upper_addr, lower_addr, size, flags);1556}1557EXPORT_SYMBOL_GPL(zynqmp_pm_sha_hash);15581559/**1560* zynqmp_pm_register_notifier() - PM API for register a subsystem1561* to be notified about specific1562* event/error.1563* @node: Node ID to which the event is related.1564* @event: Event Mask of Error events for which wants to get notified.1565* @wake: Wake subsystem upon capturing the event if value 11566* @enable: Enable the registration for value 1, disable for value 01567*1568* This function is used to register/un-register for particular node-event1569* combination in firmware.1570*1571* Return: Returns status, either success or error+reason1572*/15731574int zynqmp_pm_register_notifier(const u32 node, const u32 event,1575const u32 wake, const u32 enable)1576{1577return zynqmp_pm_invoke_fn(PM_REGISTER_NOTIFIER, NULL, 4, node, event, wake, enable);1578}1579EXPORT_SYMBOL_GPL(zynqmp_pm_register_notifier);15801581/**1582* zynqmp_pm_system_shutdown - PM call to request a system shutdown or restart1583* @type: Shutdown or restart? 0 for shutdown, 1 for restart1584* @subtype: Specifies which system should be restarted or shut down1585*1586* Return: Returns status, either success or error+reason1587*/1588int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype)1589{1590return zynqmp_pm_invoke_fn(PM_SYSTEM_SHUTDOWN, NULL, 2, type, subtype);1591}15921593/**1594* zynqmp_pm_set_feature_config - PM call to request IOCTL for feature config1595* @id: The config ID of the feature to be configured1596* @value: The config value of the feature to be configured1597*1598* Return: Returns 0 on success or error value on failure.1599*/1600int zynqmp_pm_set_feature_config(enum pm_feature_config_id id, u32 value)1601{1602return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_SET_FEATURE_CONFIG, id, value);1603}16041605/**1606* zynqmp_pm_get_feature_config - PM call to get value of configured feature1607* @id: The config id of the feature to be queried1608* @payload: Returned value array1609*1610* Return: Returns 0 on success or error value on failure.1611*/1612int zynqmp_pm_get_feature_config(enum pm_feature_config_id id,1613u32 *payload)1614{1615return zynqmp_pm_invoke_fn(PM_IOCTL, payload, 3, 0, IOCTL_GET_FEATURE_CONFIG, id);1616}16171618/**1619* zynqmp_pm_set_sd_config - PM call to set value of SD config registers1620* @node: SD node ID1621* @config: The config type of SD registers1622* @value: Value to be set1623*1624* Return: Returns 0 on success or error value on failure.1625*/1626int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value)1627{1628return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, node, IOCTL_SET_SD_CONFIG, config, value);1629}1630EXPORT_SYMBOL_GPL(zynqmp_pm_set_sd_config);16311632/**1633* zynqmp_pm_set_gem_config - PM call to set value of GEM config registers1634* @node: GEM node ID1635* @config: The config type of GEM registers1636* @value: Value to be set1637*1638* Return: Returns 0 on success or error value on failure.1639*/1640int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config,1641u32 value)1642{1643return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, node, IOCTL_SET_GEM_CONFIG, config, value);1644}1645EXPORT_SYMBOL_GPL(zynqmp_pm_set_gem_config);16461647/**1648* struct zynqmp_pm_shutdown_scope - Struct for shutdown scope1649* @subtype: Shutdown subtype1650* @name: Matching string for scope argument1651*1652* This struct encapsulates mapping between shutdown scope ID and string.1653*/1654struct zynqmp_pm_shutdown_scope {1655const enum zynqmp_pm_shutdown_subtype subtype;1656const char *name;1657};16581659static struct zynqmp_pm_shutdown_scope shutdown_scopes[] = {1660[ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM] = {1661.subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM,1662.name = "subsystem",1663},1664[ZYNQMP_PM_SHUTDOWN_SUBTYPE_PS_ONLY] = {1665.subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_PS_ONLY,1666.name = "ps_only",1667},1668[ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM] = {1669.subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM,1670.name = "system",1671},1672};16731674static struct zynqmp_pm_shutdown_scope *selected_scope =1675&shutdown_scopes[ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM];16761677/**1678* zynqmp_pm_is_shutdown_scope_valid - Check if shutdown scope string is valid1679* @scope_string: Shutdown scope string1680*1681* Return: Return pointer to matching shutdown scope struct from1682* array of available options in system if string is valid,1683* otherwise returns NULL.1684*/1685static struct zynqmp_pm_shutdown_scope*1686zynqmp_pm_is_shutdown_scope_valid(const char *scope_string)1687{1688int count;16891690for (count = 0; count < ARRAY_SIZE(shutdown_scopes); count++)1691if (sysfs_streq(scope_string, shutdown_scopes[count].name))1692return &shutdown_scopes[count];16931694return NULL;1695}16961697static ssize_t shutdown_scope_show(struct device *device,1698struct device_attribute *attr,1699char *buf)1700{1701int i;17021703for (i = 0; i < ARRAY_SIZE(shutdown_scopes); i++) {1704if (&shutdown_scopes[i] == selected_scope) {1705strcat(buf, "[");1706strcat(buf, shutdown_scopes[i].name);1707strcat(buf, "]");1708} else {1709strcat(buf, shutdown_scopes[i].name);1710}1711strcat(buf, " ");1712}1713strcat(buf, "\n");17141715return strlen(buf);1716}17171718static ssize_t shutdown_scope_store(struct device *device,1719struct device_attribute *attr,1720const char *buf, size_t count)1721{1722int ret;1723struct zynqmp_pm_shutdown_scope *scope;17241725scope = zynqmp_pm_is_shutdown_scope_valid(buf);1726if (!scope)1727return -EINVAL;17281729ret = zynqmp_pm_system_shutdown(ZYNQMP_PM_SHUTDOWN_TYPE_SETSCOPE_ONLY,1730scope->subtype);1731if (ret) {1732pr_err("unable to set shutdown scope %s\n", buf);1733return ret;1734}17351736selected_scope = scope;17371738return count;1739}17401741static DEVICE_ATTR_RW(shutdown_scope);17421743static ssize_t health_status_store(struct device *device,1744struct device_attribute *attr,1745const char *buf, size_t count)1746{1747int ret;1748unsigned int value;17491750ret = kstrtouint(buf, 10, &value);1751if (ret)1752return ret;17531754ret = zynqmp_pm_set_boot_health_status(value);1755if (ret) {1756dev_err(device, "unable to set healthy bit value to %u\n",1757value);1758return ret;1759}17601761return count;1762}17631764static DEVICE_ATTR_WO(health_status);17651766static ssize_t ggs_show(struct device *device,1767struct device_attribute *attr,1768char *buf,1769u32 reg)1770{1771int ret;1772u32 ret_payload[PAYLOAD_ARG_CNT];17731774ret = zynqmp_pm_read_ggs(reg, ret_payload);1775if (ret)1776return ret;17771778return sprintf(buf, "0x%x\n", ret_payload[1]);1779}17801781static ssize_t ggs_store(struct device *device,1782struct device_attribute *attr,1783const char *buf, size_t count,1784u32 reg)1785{1786long value;1787int ret;17881789if (reg >= GSS_NUM_REGS)1790return -EINVAL;17911792ret = kstrtol(buf, 16, &value);1793if (ret) {1794count = -EFAULT;1795goto err;1796}17971798ret = zynqmp_pm_write_ggs(reg, value);1799if (ret)1800count = -EFAULT;1801err:1802return count;1803}18041805/* GGS register show functions */1806#define GGS0_SHOW(N) \1807ssize_t ggs##N##_show(struct device *device, \1808struct device_attribute *attr, \1809char *buf) \1810{ \1811return ggs_show(device, attr, buf, N); \1812}18131814static GGS0_SHOW(0);1815static GGS0_SHOW(1);1816static GGS0_SHOW(2);1817static GGS0_SHOW(3);18181819/* GGS register store function */1820#define GGS0_STORE(N) \1821ssize_t ggs##N##_store(struct device *device, \1822struct device_attribute *attr, \1823const char *buf, \1824size_t count) \1825{ \1826return ggs_store(device, attr, buf, count, N); \1827}18281829static GGS0_STORE(0);1830static GGS0_STORE(1);1831static GGS0_STORE(2);1832static GGS0_STORE(3);18331834static ssize_t pggs_show(struct device *device,1835struct device_attribute *attr,1836char *buf,1837u32 reg)1838{1839int ret;1840u32 ret_payload[PAYLOAD_ARG_CNT];18411842ret = zynqmp_pm_read_pggs(reg, ret_payload);1843if (ret)1844return ret;18451846return sprintf(buf, "0x%x\n", ret_payload[1]);1847}18481849static ssize_t pggs_store(struct device *device,1850struct device_attribute *attr,1851const char *buf, size_t count,1852u32 reg)1853{1854long value;1855int ret;18561857if (reg >= GSS_NUM_REGS)1858return -EINVAL;18591860ret = kstrtol(buf, 16, &value);1861if (ret) {1862count = -EFAULT;1863goto err;1864}18651866ret = zynqmp_pm_write_pggs(reg, value);1867if (ret)1868count = -EFAULT;18691870err:1871return count;1872}18731874#define PGGS0_SHOW(N) \1875ssize_t pggs##N##_show(struct device *device, \1876struct device_attribute *attr, \1877char *buf) \1878{ \1879return pggs_show(device, attr, buf, N); \1880}18811882#define PGGS0_STORE(N) \1883ssize_t pggs##N##_store(struct device *device, \1884struct device_attribute *attr, \1885const char *buf, \1886size_t count) \1887{ \1888return pggs_store(device, attr, buf, count, N); \1889}18901891/* PGGS register show functions */1892static PGGS0_SHOW(0);1893static PGGS0_SHOW(1);1894static PGGS0_SHOW(2);1895static PGGS0_SHOW(3);18961897/* PGGS register store functions */1898static PGGS0_STORE(0);1899static PGGS0_STORE(1);1900static PGGS0_STORE(2);1901static PGGS0_STORE(3);19021903/* GGS register attributes */1904static DEVICE_ATTR_RW(ggs0);1905static DEVICE_ATTR_RW(ggs1);1906static DEVICE_ATTR_RW(ggs2);1907static DEVICE_ATTR_RW(ggs3);19081909/* PGGS register attributes */1910static DEVICE_ATTR_RW(pggs0);1911static DEVICE_ATTR_RW(pggs1);1912static DEVICE_ATTR_RW(pggs2);1913static DEVICE_ATTR_RW(pggs3);19141915static ssize_t feature_config_id_show(struct device *device,1916struct device_attribute *attr,1917char *buf)1918{1919struct zynqmp_devinfo *devinfo = dev_get_drvdata(device);19201921return sysfs_emit(buf, "%d\n", devinfo->feature_conf_id);1922}19231924static ssize_t feature_config_id_store(struct device *device,1925struct device_attribute *attr,1926const char *buf, size_t count)1927{1928u32 config_id;1929int ret;1930struct zynqmp_devinfo *devinfo = dev_get_drvdata(device);19311932if (!buf)1933return -EINVAL;19341935ret = kstrtou32(buf, 10, &config_id);1936if (ret)1937return ret;19381939devinfo->feature_conf_id = config_id;19401941return count;1942}19431944static DEVICE_ATTR_RW(feature_config_id);19451946static ssize_t feature_config_value_show(struct device *device,1947struct device_attribute *attr,1948char *buf)1949{1950int ret;1951u32 ret_payload[PAYLOAD_ARG_CNT];1952struct zynqmp_devinfo *devinfo = dev_get_drvdata(device);19531954ret = zynqmp_pm_get_feature_config(devinfo->feature_conf_id,1955ret_payload);1956if (ret)1957return ret;19581959return sysfs_emit(buf, "%d\n", ret_payload[1]);1960}19611962static ssize_t feature_config_value_store(struct device *device,1963struct device_attribute *attr,1964const char *buf, size_t count)1965{1966u32 value;1967int ret;1968struct zynqmp_devinfo *devinfo = dev_get_drvdata(device);19691970if (!buf)1971return -EINVAL;19721973ret = kstrtou32(buf, 10, &value);1974if (ret)1975return ret;19761977ret = zynqmp_pm_set_feature_config(devinfo->feature_conf_id,1978value);1979if (ret)1980return ret;19811982return count;1983}19841985static DEVICE_ATTR_RW(feature_config_value);19861987static struct attribute *zynqmp_firmware_attrs[] = {1988&dev_attr_ggs0.attr,1989&dev_attr_ggs1.attr,1990&dev_attr_ggs2.attr,1991&dev_attr_ggs3.attr,1992&dev_attr_pggs0.attr,1993&dev_attr_pggs1.attr,1994&dev_attr_pggs2.attr,1995&dev_attr_pggs3.attr,1996&dev_attr_shutdown_scope.attr,1997&dev_attr_health_status.attr,1998&dev_attr_feature_config_id.attr,1999&dev_attr_feature_config_value.attr,2000NULL,2001};20022003ATTRIBUTE_GROUPS(zynqmp_firmware);20042005static int zynqmp_firmware_probe(struct platform_device *pdev)2006{2007struct device *dev = &pdev->dev;2008struct zynqmp_devinfo *devinfo;2009int ret;20102011ret = get_set_conduit_method(dev->of_node);2012if (ret)2013return ret;20142015/* Get SiP SVC version number */2016ret = zynqmp_pm_get_sip_svc_version(&sip_svc_version);2017if (ret)2018return ret;20192020ret = do_feature_check_call(PM_FEATURE_CHECK);2021if (ret >= 0 && ((ret & FIRMWARE_VERSION_MASK) >= PM_API_VERSION_1))2022feature_check_enabled = true;20232024devinfo = devm_kzalloc(dev, sizeof(*devinfo), GFP_KERNEL);2025if (!devinfo)2026return -ENOMEM;20272028devinfo->dev = dev;20292030platform_set_drvdata(pdev, devinfo);20312032/* Check PM API version number */2033ret = zynqmp_pm_get_api_version(&pm_api_version);2034if (ret)2035return ret;20362037if (pm_api_version < ZYNQMP_PM_VERSION) {2038panic("%s Platform Management API version error. Expected: v%d.%d - Found: v%d.%d\n",2039__func__,2040ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR,2041pm_api_version >> 16, pm_api_version & 0xFFFF);2042}20432044pr_info("%s Platform Management API v%d.%d\n", __func__,2045pm_api_version >> 16, pm_api_version & 0xFFFF);20462047/* Get the Family code and sub family code of platform */2048ret = zynqmp_pm_get_family_info(&pm_family_code, &pm_sub_family_code);2049if (ret < 0)2050return ret;20512052/* Check trustzone version number */2053ret = zynqmp_pm_get_trustzone_version(&pm_tz_version);2054if (ret)2055panic("Legacy trustzone found without version support\n");20562057if (pm_tz_version < ZYNQMP_TZ_VERSION)2058panic("%s Trustzone version error. Expected: v%d.%d - Found: v%d.%d\n",2059__func__,2060ZYNQMP_TZ_VERSION_MAJOR, ZYNQMP_TZ_VERSION_MINOR,2061pm_tz_version >> 16, pm_tz_version & 0xFFFF);20622063pr_info("%s Trustzone version v%d.%d\n", __func__,2064pm_tz_version >> 16, pm_tz_version & 0xFFFF);20652066ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, firmware_devs,2067ARRAY_SIZE(firmware_devs), NULL, 0, NULL);2068if (ret) {2069dev_err(&pdev->dev, "failed to add MFD devices %d\n", ret);2070return ret;2071}20722073zynqmp_pm_api_debugfs_init();20742075if (pm_family_code == VERSAL_FAMILY_CODE) {2076em_dev = platform_device_register_data(&pdev->dev, "xlnx_event_manager",2077-1, NULL, 0);2078if (IS_ERR(em_dev))2079dev_err_probe(&pdev->dev, PTR_ERR(em_dev), "EM register fail with error\n");2080}20812082return of_platform_populate(dev->of_node, NULL, NULL, dev);2083}20842085static void zynqmp_firmware_remove(struct platform_device *pdev)2086{2087struct pm_api_feature_data *feature_data;2088struct hlist_node *tmp;2089int i;20902091mfd_remove_devices(&pdev->dev);2092zynqmp_pm_api_debugfs_exit();20932094hash_for_each_safe(pm_api_features_map, i, tmp, feature_data, hentry) {2095hash_del(&feature_data->hentry);2096kfree(feature_data);2097}20982099platform_device_unregister(em_dev);2100}21012102static void zynqmp_firmware_sync_state(struct device *dev)2103{2104struct device_node *np = dev->of_node;21052106if (!of_device_is_compatible(np, "xlnx,zynqmp-firmware"))2107return;21082109of_genpd_sync_state(np);21102111if (zynqmp_pm_init_finalize())2112dev_warn(dev, "failed to release power management to firmware\n");2113}21142115static const struct of_device_id zynqmp_firmware_of_match[] = {2116{.compatible = "xlnx,zynqmp-firmware"},2117{.compatible = "xlnx,versal-firmware"},2118{},2119};2120MODULE_DEVICE_TABLE(of, zynqmp_firmware_of_match);21212122static struct platform_driver zynqmp_firmware_driver = {2123.driver = {2124.name = "zynqmp_firmware",2125.of_match_table = zynqmp_firmware_of_match,2126.dev_groups = zynqmp_firmware_groups,2127.sync_state = zynqmp_firmware_sync_state,2128},2129.probe = zynqmp_firmware_probe,2130.remove = zynqmp_firmware_remove,2131};2132module_platform_driver(zynqmp_firmware_driver);213321342135