2018-09-12 12:38:36 -07:00
// SPDX-License-Identifier: GPL-2.0
/*
* Xilinx Zynq MPSoC Firmware layer
*
2021-02-07 22:31:23 -08:00
* Copyright ( C ) 2014 - 2021 Xilinx , Inc .
2018-09-12 12:38:36 -07:00
*
* Michal Simek < michal . simek @ xilinx . com >
* Davorin Mista < davorin . mista @ aggios . com >
* Jolly Shah < jollys @ xilinx . com >
* Rajan Vaja < rajanv @ xilinx . com >
*/
# include <linux/arm-smccc.h>
# include <linux/compiler.h>
# include <linux/device.h>
# include <linux/init.h>
2019-02-01 14:08:50 -08:00
# include <linux/mfd/core.h>
2018-09-12 12:38:36 -07:00
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_platform.h>
# include <linux/slab.h>
# include <linux/uaccess.h>
2020-11-23 21:52:41 -08:00
# include <linux/hashtable.h>
2018-09-12 12:38:36 -07:00
# include <linux/firmware/xlnx-zynqmp.h>
2018-09-12 12:38:39 -07:00
# include "zynqmp-debug.h"
2018-09-12 12:38:36 -07:00
2020-11-23 21:52:41 -08:00
/* Max HashMap Order for PM API feature check (1<<7 = 128) */
# define PM_API_FEATURE_CHECK_MAX_ORDER 7
2019-11-12 00:35:55 -08:00
static bool feature_check_enabled ;
2020-12-01 19:51:53 +08:00
static DEFINE_HASHTABLE ( pm_api_features_map , PM_API_FEATURE_CHECK_MAX_ORDER ) ;
2020-11-23 21:52:41 -08:00
/**
* struct pm_api_feature_data - PM API Feature data
* @ pm_api_id : PM API Id , used as key to index into hashmap
* @ feature_status : status of PM API feature : valid , invalid
* @ hentry : hlist_node that hooks this entry into hashtable
*/
struct pm_api_feature_data {
u32 pm_api_id ;
int feature_status ;
struct hlist_node hentry ;
} ;
2019-11-12 00:35:55 -08:00
2019-02-01 14:08:50 -08:00
static const struct mfd_cell firmware_devs [ ] = {
{
. name = " zynqmp_power_controller " ,
} ,
} ;
2018-09-12 12:38:36 -07:00
/**
* zynqmp_pm_ret_code ( ) - Convert PMU - FW error codes to Linux error codes
* @ ret_status : PMUFW return code
*
* Return : corresponding Linux error code
*/
static int zynqmp_pm_ret_code ( u32 ret_status )
{
switch ( ret_status ) {
case XST_PM_SUCCESS :
case XST_PM_DOUBLE_REQ :
return 0 ;
2019-11-12 00:35:55 -08:00
case XST_PM_NO_FEATURE :
return - ENOTSUPP ;
2018-09-12 12:38:36 -07:00
case XST_PM_NO_ACCESS :
return - EACCES ;
case XST_PM_ABORT_SUSPEND :
return - ECANCELED ;
2019-12-04 22:35:56 -08:00
case XST_PM_MULT_USER :
return - EUSERS ;
2018-09-12 12:38:36 -07:00
case XST_PM_INTERNAL :
case XST_PM_CONFLICT :
case XST_PM_INVALID_NODE :
default :
return - EINVAL ;
}
}
static noinline int do_fw_call_fail ( u64 arg0 , u64 arg1 , u64 arg2 ,
u32 * ret_payload )
{
return - ENODEV ;
}
/*
* PM function call wrapper
* Invoke do_fw_call_smc or do_fw_call_hvc , depending on the configuration
*/
static int ( * do_fw_call ) ( u64 , u64 , u64 , u32 * ret_payload ) = do_fw_call_fail ;
/**
* do_fw_call_smc ( ) - Call system - level platform management layer ( SMC )
* @ arg0 : Argument 0 to SMC call
* @ arg1 : Argument 1 to SMC call
* @ arg2 : Argument 2 to SMC call
* @ ret_payload : Returned value array
*
* Invoke platform management function via SMC call ( no hypervisor present ) .
*
* Return : Returns status , either success or error + reason
*/
static noinline int do_fw_call_smc ( u64 arg0 , u64 arg1 , u64 arg2 ,
u32 * ret_payload )
{
struct arm_smccc_res res ;
arm_smccc_smc ( arg0 , arg1 , arg2 , 0 , 0 , 0 , 0 , 0 , & res ) ;
if ( ret_payload ) {
ret_payload [ 0 ] = lower_32_bits ( res . a0 ) ;
ret_payload [ 1 ] = upper_32_bits ( res . a0 ) ;
ret_payload [ 2 ] = lower_32_bits ( res . a1 ) ;
ret_payload [ 3 ] = upper_32_bits ( res . a1 ) ;
}
return zynqmp_pm_ret_code ( ( enum pm_ret_status ) res . a0 ) ;
}
/**
* do_fw_call_hvc ( ) - Call system - level platform management layer ( HVC )
* @ arg0 : Argument 0 to HVC call
* @ arg1 : Argument 1 to HVC call
* @ arg2 : Argument 2 to HVC call
* @ ret_payload : Returned value array
*
* Invoke platform management function via HVC
* HVC - based for communication through hypervisor
* ( no direct communication with ATF ) .
*
* Return : Returns status , either success or error + reason
*/
static noinline int do_fw_call_hvc ( u64 arg0 , u64 arg1 , u64 arg2 ,
u32 * ret_payload )
{
struct arm_smccc_res res ;
arm_smccc_hvc ( arg0 , arg1 , arg2 , 0 , 0 , 0 , 0 , 0 , & res ) ;
if ( ret_payload ) {
ret_payload [ 0 ] = lower_32_bits ( res . a0 ) ;
ret_payload [ 1 ] = upper_32_bits ( res . a0 ) ;
ret_payload [ 2 ] = lower_32_bits ( res . a1 ) ;
ret_payload [ 3 ] = upper_32_bits ( res . a1 ) ;
}
return zynqmp_pm_ret_code ( ( enum pm_ret_status ) res . a0 ) ;
}
2019-11-12 00:35:55 -08:00
/**
* zynqmp_pm_feature ( ) - Check weather given feature is supported or not
* @ api_id : API ID to check
*
* Return : Returns status , either success or error + reason
*/
static int zynqmp_pm_feature ( u32 api_id )
{
int ret ;
u32 ret_payload [ PAYLOAD_ARG_CNT ] ;
u64 smc_arg [ 2 ] ;
2020-11-23 21:52:41 -08:00
struct pm_api_feature_data * feature_data ;
2019-11-12 00:35:55 -08:00
if ( ! feature_check_enabled )
return 0 ;
2020-11-23 21:52:41 -08:00
/* Check for existing entry in hash table for given api */
hash_for_each_possible ( pm_api_features_map , feature_data , hentry ,
api_id ) {
if ( feature_data - > pm_api_id = = api_id )
return feature_data - > feature_status ;
}
2020-10-26 16:54:36 +01:00
2020-11-23 21:52:41 -08:00
/* Add new entry if not present */
feature_data = kmalloc ( sizeof ( * feature_data ) , GFP_KERNEL ) ;
if ( ! feature_data )
return - ENOMEM ;
2019-11-12 00:35:55 -08:00
2020-11-23 21:52:41 -08:00
feature_data - > pm_api_id = api_id ;
2019-11-12 00:35:55 -08:00
smc_arg [ 0 ] = PM_SIP_SVC | PM_FEATURE_CHECK ;
smc_arg [ 1 ] = api_id ;
ret = do_fw_call ( smc_arg [ 0 ] , smc_arg [ 1 ] , 0 , ret_payload ) ;
2020-11-23 21:52:41 -08:00
if ( ret )
ret = - EOPNOTSUPP ;
else
ret = ret_payload [ 1 ] ;
2019-11-12 00:35:55 -08:00
2020-11-23 21:52:41 -08:00
feature_data - > feature_status = ret ;
hash_add ( pm_api_features_map , & feature_data - > hentry , api_id ) ;
2019-11-12 00:35:55 -08:00
2020-11-23 21:52:41 -08:00
return ret ;
2019-11-12 00:35:55 -08:00
}
2018-09-12 12:38:36 -07:00
/**
* zynqmp_pm_invoke_fn ( ) - Invoke the system - level platform management layer
* caller function depending on the configuration
* @ pm_api_id : Requested PM - API call
* @ arg0 : Argument 0 to requested PM - API call
* @ arg1 : Argument 1 to requested PM - API call
* @ arg2 : Argument 2 to requested PM - API call
* @ arg3 : Argument 3 to requested PM - API call
* @ ret_payload : Returned value array
*
* Invoke platform management function for SMC or HVC call , depending on
* configuration .
* Following SMC Calling Convention ( SMCCC ) for SMC64 :
* Pm Function Identifier ,
* PM_SIP_SVC + PM_API_ID =
* ( ( SMC_TYPE_FAST < < FUNCID_TYPE_SHIFT )
* ( ( SMC_64 ) < < FUNCID_CC_SHIFT )
* ( ( SIP_START ) < < FUNCID_OEN_SHIFT )
* ( ( PM_API_ID ) & FUNCID_NUM_MASK ) )
*
* PM_SIP_SVC - Registered ZynqMP SIP Service Call .
* PM_API_ID - Platform Management API ID .
*
* Return : Returns status , either success or error + reason
*/
int zynqmp_pm_invoke_fn ( u32 pm_api_id , u32 arg0 , u32 arg1 ,
u32 arg2 , u32 arg3 , u32 * ret_payload )
{
/*
* Added SIP service call Function Identifier
* Make sure to stay in x0 register
*/
u64 smc_arg [ 4 ] ;
2020-11-23 21:52:41 -08:00
int ret ;
2018-09-12 12:38:36 -07:00
2020-11-23 21:52:41 -08:00
/* Check if feature is supported or not */
ret = zynqmp_pm_feature ( pm_api_id ) ;
if ( ret < 0 )
return ret ;
2019-11-12 00:35:55 -08:00
2018-09-12 12:38:36 -07:00
smc_arg [ 0 ] = PM_SIP_SVC | pm_api_id ;
smc_arg [ 1 ] = ( ( u64 ) arg1 < < 32 ) | arg0 ;
smc_arg [ 2 ] = ( ( u64 ) arg3 < < 32 ) | arg2 ;
return do_fw_call ( smc_arg [ 0 ] , smc_arg [ 1 ] , smc_arg [ 2 ] , ret_payload ) ;
}
static u32 pm_api_version ;
static u32 pm_tz_version ;
/**
* zynqmp_pm_get_api_version ( ) - Get version number of PMU PM firmware
* @ version : Returned version value
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:43 -07:00
int zynqmp_pm_get_api_version ( u32 * version )
2018-09-12 12:38:36 -07:00
{
u32 ret_payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
if ( ! version )
return - EINVAL ;
/* Check is PM API version already verified */
if ( pm_api_version > 0 ) {
* version = pm_api_version ;
return 0 ;
}
ret = zynqmp_pm_invoke_fn ( PM_GET_API_VERSION , 0 , 0 , 0 , 0 , ret_payload ) ;
* version = ret_payload [ 1 ] ;
return ret ;
}
2020-04-24 13:57:43 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_get_api_version ) ;
2018-09-12 12:38:36 -07:00
2019-02-06 16:37:19 +05:30
/**
* zynqmp_pm_get_chipid - Get silicon ID registers
* @ idcode : IDCODE register
* @ version : version register
*
* Return : Returns the status of the operation and the idcode and version
* registers in @ idcode and @ version .
*/
2020-04-24 13:57:44 -07:00
int zynqmp_pm_get_chipid ( u32 * idcode , u32 * version )
2019-02-06 16:37:19 +05:30
{
u32 ret_payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
if ( ! idcode | | ! version )
return - EINVAL ;
ret = zynqmp_pm_invoke_fn ( PM_GET_CHIPID , 0 , 0 , 0 , 0 , ret_payload ) ;
* idcode = ret_payload [ 1 ] ;
* version = ret_payload [ 2 ] ;
return ret ;
}
2020-04-24 13:57:44 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_get_chipid ) ;
2019-02-06 16:37:19 +05:30
2018-09-12 12:38:36 -07:00
/**
* zynqmp_pm_get_trustzone_version ( ) - Get secure trustzone firmware version
* @ version : Returned version value
*
* Return : Returns status , either success or error + reason
*/
static int zynqmp_pm_get_trustzone_version ( u32 * version )
{
u32 ret_payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
if ( ! version )
return - EINVAL ;
/* Check is PM trustzone version already verified */
if ( pm_tz_version > 0 ) {
* version = pm_tz_version ;
return 0 ;
}
ret = zynqmp_pm_invoke_fn ( PM_GET_TRUSTZONE_VERSION , 0 , 0 ,
0 , 0 , ret_payload ) ;
* version = ret_payload [ 1 ] ;
return ret ;
}
/**
* get_set_conduit_method ( ) - Choose SMC or HVC based communication
* @ np : Pointer to the device_node structure
*
* Use SMC or HVC - based functions to communicate with EL2 / EL3 .
*
* Return : Returns 0 on success or error code
*/
static int get_set_conduit_method ( struct device_node * np )
{
const char * method ;
if ( of_property_read_string ( np , " method " , & method ) ) {
pr_warn ( " %s missing \" method \" property \n " , __func__ ) ;
return - ENXIO ;
}
if ( ! strcmp ( " hvc " , method ) ) {
do_fw_call = do_fw_call_hvc ;
} else if ( ! strcmp ( " smc " , method ) ) {
do_fw_call = do_fw_call_smc ;
} else {
pr_warn ( " %s Invalid \" method \" property: %s \n " ,
__func__ , method ) ;
return - EINVAL ;
}
return 0 ;
}
2018-09-12 12:38:37 -07:00
/**
* zynqmp_pm_query_data ( ) - Get query data from firmware
* @ qdata : Variable to the zynqmp_pm_query_data structure
* @ out : Returned output value
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:45 -07:00
int zynqmp_pm_query_data ( struct zynqmp_pm_query_data qdata , u32 * out )
2018-09-12 12:38:37 -07:00
{
2018-09-12 12:38:38 -07:00
int ret ;
ret = zynqmp_pm_invoke_fn ( PM_QUERY_DATA , qdata . qid , qdata . arg1 ,
qdata . arg2 , qdata . arg3 , out ) ;
/*
* For clock name query , all bytes in SMC response are clock name
* characters and return code is always success . For invalid clocks ,
* clock name bytes would be zeros .
*/
return qdata . qid = = PM_QID_CLOCK_GET_NAME ? 0 : ret ;
}
2020-04-24 13:57:45 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_query_data ) ;
2018-09-12 12:38:38 -07:00
/**
* zynqmp_pm_clock_enable ( ) - Enable the clock for given id
* @ clock_id : ID of the clock to be enabled
*
* This function is used by master to enable the clock
* including peripherals and PLL clocks .
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:46 -07:00
int zynqmp_pm_clock_enable ( u32 clock_id )
2018-09-12 12:38:38 -07:00
{
return zynqmp_pm_invoke_fn ( PM_CLOCK_ENABLE , clock_id , 0 , 0 , 0 , NULL ) ;
}
2020-04-24 13:57:46 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_clock_enable ) ;
2018-09-12 12:38:38 -07:00
/**
* zynqmp_pm_clock_disable ( ) - Disable the clock for given id
* @ clock_id : ID of the clock to be disable
*
* This function is used by master to disable the clock
* including peripherals and PLL clocks .
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:47 -07:00
int zynqmp_pm_clock_disable ( u32 clock_id )
2018-09-12 12:38:38 -07:00
{
return zynqmp_pm_invoke_fn ( PM_CLOCK_DISABLE , clock_id , 0 , 0 , 0 , NULL ) ;
}
2020-04-24 13:57:47 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_clock_disable ) ;
2018-09-12 12:38:38 -07:00
/**
* zynqmp_pm_clock_getstate ( ) - Get the clock state for given id
* @ clock_id : ID of the clock to be queried
* @ state : 1 / 0 ( Enabled / Disabled )
*
* This function is used by master to get the state of clock
* including peripherals and PLL clocks .
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:48 -07:00
int zynqmp_pm_clock_getstate ( u32 clock_id , u32 * state )
2018-09-12 12:38:38 -07:00
{
u32 ret_payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
ret = zynqmp_pm_invoke_fn ( PM_CLOCK_GETSTATE , clock_id , 0 ,
0 , 0 , ret_payload ) ;
* state = ret_payload [ 1 ] ;
return ret ;
}
2020-04-24 13:57:48 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_clock_getstate ) ;
2018-09-12 12:38:38 -07:00
/**
* zynqmp_pm_clock_setdivider ( ) - Set the clock divider for given id
* @ clock_id : ID of the clock
* @ divider : divider value
*
* This function is used by master to set divider for any clock
* to achieve desired rate .
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:49 -07:00
int zynqmp_pm_clock_setdivider ( u32 clock_id , u32 divider )
2018-09-12 12:38:38 -07:00
{
return zynqmp_pm_invoke_fn ( PM_CLOCK_SETDIVIDER , clock_id , divider ,
0 , 0 , NULL ) ;
}
2020-04-24 13:57:49 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_clock_setdivider ) ;
2018-09-12 12:38:38 -07:00
/**
* zynqmp_pm_clock_getdivider ( ) - Get the clock divider for given id
* @ clock_id : ID of the clock
* @ divider : divider value
*
* This function is used by master to get divider values
* for any clock .
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:50 -07:00
int zynqmp_pm_clock_getdivider ( u32 clock_id , u32 * divider )
2018-09-12 12:38:38 -07:00
{
u32 ret_payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
ret = zynqmp_pm_invoke_fn ( PM_CLOCK_GETDIVIDER , clock_id , 0 ,
0 , 0 , ret_payload ) ;
* divider = ret_payload [ 1 ] ;
return ret ;
}
2020-04-24 13:57:50 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_clock_getdivider ) ;
2018-09-12 12:38:38 -07:00
/**
* zynqmp_pm_clock_setrate ( ) - Set the clock rate for given id
* @ clock_id : ID of the clock
* @ rate : rate value in hz
*
* This function is used by master to set rate for any clock .
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:51 -07:00
int zynqmp_pm_clock_setrate ( u32 clock_id , u64 rate )
2018-09-12 12:38:38 -07:00
{
return zynqmp_pm_invoke_fn ( PM_CLOCK_SETRATE , clock_id ,
lower_32_bits ( rate ) ,
upper_32_bits ( rate ) ,
0 , NULL ) ;
}
2020-04-24 13:57:51 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_clock_setrate ) ;
2018-09-12 12:38:38 -07:00
/**
* zynqmp_pm_clock_getrate ( ) - Get the clock rate for given id
* @ clock_id : ID of the clock
* @ rate : rate value in hz
*
* This function is used by master to get rate
* for any clock .
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:51 -07:00
int zynqmp_pm_clock_getrate ( u32 clock_id , u64 * rate )
2018-09-12 12:38:38 -07:00
{
u32 ret_payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
ret = zynqmp_pm_invoke_fn ( PM_CLOCK_GETRATE , clock_id , 0 ,
0 , 0 , ret_payload ) ;
* rate = ( ( u64 ) ret_payload [ 2 ] < < 32 ) | ret_payload [ 1 ] ;
return ret ;
}
2020-04-24 13:57:51 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_clock_getrate ) ;
2018-09-12 12:38:38 -07:00
/**
* zynqmp_pm_clock_setparent ( ) - Set the clock parent for given id
* @ clock_id : ID of the clock
* @ parent_id : parent id
*
* This function is used by master to set parent for any clock .
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:52 -07:00
int zynqmp_pm_clock_setparent ( u32 clock_id , u32 parent_id )
2018-09-12 12:38:38 -07:00
{
return zynqmp_pm_invoke_fn ( PM_CLOCK_SETPARENT , clock_id ,
parent_id , 0 , 0 , NULL ) ;
}
2020-04-24 13:57:52 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_clock_setparent ) ;
2018-09-12 12:38:38 -07:00
/**
* zynqmp_pm_clock_getparent ( ) - Get the clock parent for given id
* @ clock_id : ID of the clock
* @ parent_id : parent id
*
* This function is used by master to get parent index
* for any clock .
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:52 -07:00
int zynqmp_pm_clock_getparent ( u32 clock_id , u32 * parent_id )
2018-09-12 12:38:38 -07:00
{
u32 ret_payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
ret = zynqmp_pm_invoke_fn ( PM_CLOCK_GETPARENT , clock_id , 0 ,
0 , 0 , ret_payload ) ;
* parent_id = ret_payload [ 1 ] ;
return ret ;
2018-09-12 12:38:37 -07:00
}
2020-04-24 13:57:52 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_clock_getparent ) ;
2018-09-12 12:38:37 -07:00
2018-10-08 11:21:44 -07:00
/**
2020-04-24 13:57:53 -07:00
* zynqmp_pm_set_pll_frac_mode ( ) - PM API for set PLL mode
2018-10-08 11:21:44 -07:00
*
2020-04-24 13:57:53 -07:00
* @ clk_id : PLL clock ID
* @ mode : PLL mode ( PLL_MODE_FRAC / PLL_MODE_INT )
*
* This function sets PLL mode
*
* Return : Returns status , either success or error + reason
2018-10-08 11:21:44 -07:00
*/
2020-04-24 13:57:53 -07:00
int zynqmp_pm_set_pll_frac_mode ( u32 clk_id , u32 mode )
2018-10-08 11:21:44 -07:00
{
2020-04-24 13:57:53 -07:00
return zynqmp_pm_invoke_fn ( PM_IOCTL , 0 , IOCTL_SET_PLL_FRAC_MODE ,
clk_id , mode , NULL ) ;
2018-10-08 11:21:44 -07:00
}
2020-04-24 13:57:53 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_set_pll_frac_mode ) ;
2018-10-08 11:21:44 -07:00
/**
2020-04-24 13:57:53 -07:00
* zynqmp_pm_get_pll_frac_mode ( ) - PM API for get PLL mode
*
* @ clk_id : PLL clock ID
* @ mode : PLL mode
2018-10-08 11:21:44 -07:00
*
2020-04-24 13:57:53 -07:00
* This function return current PLL mode
2018-10-08 11:21:44 -07:00
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:53 -07:00
int zynqmp_pm_get_pll_frac_mode ( u32 clk_id , u32 * mode )
2018-10-08 11:21:44 -07:00
{
2020-04-24 13:57:53 -07:00
return zynqmp_pm_invoke_fn ( PM_IOCTL , 0 , IOCTL_GET_PLL_FRAC_MODE ,
clk_id , 0 , mode ) ;
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_get_pll_frac_mode ) ;
/**
* zynqmp_pm_set_pll_frac_data ( ) - PM API for setting pll fraction data
*
* @ clk_id : PLL clock ID
* @ data : fraction data
*
* This function sets fraction data .
* It is valid for fraction mode only .
*
* Return : Returns status , either success or error + reason
*/
int zynqmp_pm_set_pll_frac_data ( u32 clk_id , u32 data )
{
return zynqmp_pm_invoke_fn ( PM_IOCTL , 0 , IOCTL_SET_PLL_FRAC_DATA ,
clk_id , data , NULL ) ;
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_set_pll_frac_data ) ;
/**
* zynqmp_pm_get_pll_frac_data ( ) - PM API for getting pll fraction data
*
* @ clk_id : PLL clock ID
* @ data : fraction data
*
* This function returns fraction data value .
*
* Return : Returns status , either success or error + reason
*/
int zynqmp_pm_get_pll_frac_data ( u32 clk_id , u32 * data )
{
return zynqmp_pm_invoke_fn ( PM_IOCTL , 0 , IOCTL_GET_PLL_FRAC_DATA ,
clk_id , 0 , data ) ;
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_get_pll_frac_data ) ;
2018-10-08 11:21:44 -07:00
2020-04-24 13:57:53 -07:00
/**
* zynqmp_pm_set_sd_tapdelay ( ) - Set tap delay for the SD device
*
2020-12-01 15:29:59 +01:00
* @ node_id : Node ID of the device
* @ type : Type of tap delay to set ( input / output )
* @ value : Value to set fot the tap delay
2020-04-24 13:57:53 -07:00
*
* This function sets input / output tap delay for the SD device .
*
2020-12-01 15:29:59 +01:00
* Return : Returns status , either success or error + reason
2020-04-24 13:57:53 -07:00
*/
int zynqmp_pm_set_sd_tapdelay ( u32 node_id , u32 type , u32 value )
{
return zynqmp_pm_invoke_fn ( PM_IOCTL , node_id , IOCTL_SET_SD_TAPDELAY ,
type , value , NULL ) ;
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_set_sd_tapdelay ) ;
/**
* zynqmp_pm_sd_dll_reset ( ) - Reset DLL logic
*
2020-12-01 15:29:59 +01:00
* @ node_id : Node ID of the device
* @ type : Reset type
2020-04-24 13:57:53 -07:00
*
* This function resets DLL logic for the SD device .
*
2020-12-01 15:29:59 +01:00
* Return : Returns status , either success or error + reason
2020-04-24 13:57:53 -07:00
*/
int zynqmp_pm_sd_dll_reset ( u32 node_id , u32 type )
{
2020-11-16 19:22:24 +05:30
return zynqmp_pm_invoke_fn ( PM_IOCTL , node_id , IOCTL_SD_DLL_RESET ,
2020-04-24 13:57:53 -07:00
type , 0 , NULL ) ;
2018-10-08 11:21:44 -07:00
}
2020-04-24 13:57:53 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_sd_dll_reset ) ;
2018-10-08 11:21:44 -07:00
2020-04-24 13:58:03 -07:00
/**
* zynqmp_pm_write_ggs ( ) - PM API for writing global general storage ( ggs )
2020-12-01 15:29:59 +01:00
* @ index : GGS register index
* @ value : Register value to be written
2020-04-24 13:58:03 -07:00
*
* This function writes value to GGS register .
*
2020-12-01 15:29:59 +01:00
* Return : Returns status , either success or error + reason
2020-04-24 13:58:03 -07:00
*/
int zynqmp_pm_write_ggs ( u32 index , u32 value )
{
return zynqmp_pm_invoke_fn ( PM_IOCTL , 0 , IOCTL_WRITE_GGS ,
index , value , NULL ) ;
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_write_ggs ) ;
/**
2021-08-06 12:32:36 +02:00
* zynqmp_pm_read_ggs ( ) - PM API for reading global general storage ( ggs )
2020-12-01 15:29:59 +01:00
* @ index : GGS register index
* @ value : Register value to be written
2020-04-24 13:58:03 -07:00
*
* This function returns GGS register value .
*
2020-12-01 15:29:59 +01:00
* Return : Returns status , either success or error + reason
2020-04-24 13:58:03 -07:00
*/
int zynqmp_pm_read_ggs ( u32 index , u32 * value )
{
return zynqmp_pm_invoke_fn ( PM_IOCTL , 0 , IOCTL_READ_GGS ,
index , 0 , value ) ;
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_read_ggs ) ;
/**
* zynqmp_pm_write_pggs ( ) - PM API for writing persistent global general
* storage ( pggs )
2020-12-01 15:29:59 +01:00
* @ index : PGGS register index
* @ value : Register value to be written
2020-04-24 13:58:03 -07:00
*
* This function writes value to PGGS register .
*
2020-12-01 15:29:59 +01:00
* Return : Returns status , either success or error + reason
2020-04-24 13:58:03 -07:00
*/
int zynqmp_pm_write_pggs ( u32 index , u32 value )
{
return zynqmp_pm_invoke_fn ( PM_IOCTL , 0 , IOCTL_WRITE_PGGS , index , value ,
NULL ) ;
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_write_pggs ) ;
/**
2021-08-06 12:32:36 +02:00
* zynqmp_pm_read_pggs ( ) - PM API for reading persistent global general
2020-04-24 13:58:03 -07:00
* storage ( pggs )
2020-12-01 15:29:59 +01:00
* @ index : PGGS register index
* @ value : Register value to be written
2020-04-24 13:58:03 -07:00
*
* This function returns PGGS register value .
*
2020-12-01 15:29:59 +01:00
* Return : Returns status , either success or error + reason
2020-04-24 13:58:03 -07:00
*/
int zynqmp_pm_read_pggs ( u32 index , u32 * value )
{
return zynqmp_pm_invoke_fn ( PM_IOCTL , 0 , IOCTL_READ_PGGS , index , 0 ,
value ) ;
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_read_pggs ) ;
2020-04-24 13:58:07 -07:00
/**
* zynqmp_pm_set_boot_health_status ( ) - PM API for setting healthy boot status
2020-12-01 15:29:59 +01:00
* @ value : Status value to be written
2020-04-24 13:58:07 -07:00
*
* This function sets healthy bit value to indicate boot health status
* to firmware .
*
2020-12-01 15:29:59 +01:00
* Return : Returns status , either success or error + reason
2020-04-24 13:58:07 -07:00
*/
int zynqmp_pm_set_boot_health_status ( u32 value )
{
return zynqmp_pm_invoke_fn ( PM_IOCTL , 0 , IOCTL_SET_BOOT_HEALTH_STATUS ,
value , 0 , NULL ) ;
}
2019-01-25 13:16:52 +05:30
/**
* zynqmp_pm_reset_assert - Request setting of reset ( 1 - assert , 0 - release )
* @ reset : Reset to be configured
* @ assert_flag : Flag stating should reset be asserted ( 1 ) or
* released ( 0 )
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:54 -07:00
int zynqmp_pm_reset_assert ( const enum zynqmp_pm_reset reset ,
const enum zynqmp_pm_reset_action assert_flag )
2019-01-25 13:16:52 +05:30
{
return zynqmp_pm_invoke_fn ( PM_RESET_ASSERT , reset , assert_flag ,
0 , 0 , NULL ) ;
}
2020-04-24 13:57:54 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_reset_assert ) ;
2019-01-25 13:16:52 +05:30
/**
* zynqmp_pm_reset_get_status - Get status of the reset
* @ reset : Reset whose status should be returned
* @ status : Returned status
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:55 -07:00
int zynqmp_pm_reset_get_status ( const enum zynqmp_pm_reset reset , u32 * status )
2019-01-25 13:16:52 +05:30
{
u32 ret_payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
if ( ! status )
return - EINVAL ;
ret = zynqmp_pm_invoke_fn ( PM_RESET_GET_STATUS , reset , 0 ,
0 , 0 , ret_payload ) ;
* status = ret_payload [ 1 ] ;
return ret ;
}
2020-04-24 13:57:55 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_reset_get_status ) ;
2019-01-25 13:16:52 +05:30
2019-04-15 12:47:46 +05:30
/**
* zynqmp_pm_fpga_load - Perform the fpga load
* @ address : Address to write to
* @ size : pl bitstream size
* @ flags : Bitstream type
* - XILINX_ZYNQMP_PM_FPGA_FULL : FPGA full reconfiguration
* - XILINX_ZYNQMP_PM_FPGA_PARTIAL : FPGA partial reconfiguration
*
* This function provides access to pmufw . To transfer
* the required bitstream into PL .
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:58:02 -07:00
int zynqmp_pm_fpga_load ( const u64 address , const u32 size , const u32 flags )
2019-04-15 12:47:46 +05:30
{
return zynqmp_pm_invoke_fn ( PM_FPGA_LOAD , lower_32_bits ( address ) ,
upper_32_bits ( address ) , size , flags , NULL ) ;
}
2020-05-01 19:59:58 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_fpga_load ) ;
2019-04-15 12:47:46 +05:30
/**
* zynqmp_pm_fpga_get_status - Read value from PCAP status register
* @ value : Value to read
*
* This function provides access to the pmufw to get the PCAP
* status
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:58:02 -07:00
int zynqmp_pm_fpga_get_status ( u32 * value )
2019-04-15 12:47:46 +05:30
{
u32 ret_payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
if ( ! value )
return - EINVAL ;
ret = zynqmp_pm_invoke_fn ( PM_FPGA_GET_STATUS , 0 , 0 , 0 , 0 , ret_payload ) ;
* value = ret_payload [ 1 ] ;
return ret ;
}
2020-05-01 19:59:58 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_fpga_get_status ) ;
2019-04-15 12:47:46 +05:30
2021-04-22 14:00:00 +05:30
/**
* zynqmp_pm_pinctrl_request - Request Pin from firmware
* @ pin : Pin number to request
*
* This function requests pin from firmware .
*
* Return : Returns status , either success or error + reason .
*/
int zynqmp_pm_pinctrl_request ( const u32 pin )
{
return zynqmp_pm_invoke_fn ( PM_PINCTRL_REQUEST , pin , 0 , 0 , 0 , NULL ) ;
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_pinctrl_request ) ;
/**
* zynqmp_pm_pinctrl_release - Inform firmware that Pin control is released
* @ pin : Pin number to release
*
* This function release pin from firmware .
*
* Return : Returns status , either success or error + reason .
*/
int zynqmp_pm_pinctrl_release ( const u32 pin )
{
return zynqmp_pm_invoke_fn ( PM_PINCTRL_RELEASE , pin , 0 , 0 , 0 , NULL ) ;
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_pinctrl_release ) ;
/**
* zynqmp_pm_pinctrl_get_function - Read function id set for the given pin
* @ pin : Pin number
* @ id : Buffer to store function ID
*
* This function provides the function currently set for the given pin .
*
* Return : Returns status , either success or error + reason
*/
int zynqmp_pm_pinctrl_get_function ( const u32 pin , u32 * id )
{
u32 ret_payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
if ( ! id )
return - EINVAL ;
ret = zynqmp_pm_invoke_fn ( PM_PINCTRL_GET_FUNCTION , pin , 0 ,
0 , 0 , ret_payload ) ;
* id = ret_payload [ 1 ] ;
return ret ;
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_pinctrl_get_function ) ;
/**
* zynqmp_pm_pinctrl_set_function - Set requested function for the pin
* @ pin : Pin number
* @ id : Function ID to set
*
* This function sets requested function for the given pin .
*
* Return : Returns status , either success or error + reason .
*/
int zynqmp_pm_pinctrl_set_function ( const u32 pin , const u32 id )
{
return zynqmp_pm_invoke_fn ( PM_PINCTRL_SET_FUNCTION , pin , id ,
0 , 0 , NULL ) ;
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_pinctrl_set_function ) ;
/**
* zynqmp_pm_pinctrl_get_config - Get configuration parameter for the pin
* @ pin : Pin number
* @ param : Parameter to get
* @ value : Buffer to store parameter value
*
* This function gets requested configuration parameter for the given pin .
*
* Return : Returns status , either success or error + reason .
*/
int zynqmp_pm_pinctrl_get_config ( const u32 pin , const u32 param ,
u32 * value )
{
u32 ret_payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
if ( ! value )
return - EINVAL ;
ret = zynqmp_pm_invoke_fn ( PM_PINCTRL_CONFIG_PARAM_GET , pin , param ,
0 , 0 , ret_payload ) ;
* value = ret_payload [ 1 ] ;
return ret ;
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_pinctrl_get_config ) ;
/**
* zynqmp_pm_pinctrl_set_config - Set configuration parameter for the pin
* @ pin : Pin number
* @ param : Parameter to set
* @ value : Parameter value to set
*
* This function sets requested configuration parameter for the given pin .
*
* Return : Returns status , either success or error + reason .
*/
int zynqmp_pm_pinctrl_set_config ( const u32 pin , const u32 param ,
u32 value )
{
return zynqmp_pm_invoke_fn ( PM_PINCTRL_CONFIG_PARAM_SET , pin ,
param , value , 0 , NULL ) ;
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_pinctrl_set_config ) ;
2019-01-29 12:38:20 -08:00
/**
* zynqmp_pm_init_finalize ( ) - PM call to inform firmware that the caller
* master has initialized its own power management
*
2020-12-01 15:29:59 +01:00
* Return : Returns status , either success or error + reason
*
2019-01-29 12:38:20 -08:00
* This API function is to be used for notify the power management controller
* about the completed power management initialization .
*/
2020-04-24 13:57:56 -07:00
int zynqmp_pm_init_finalize ( void )
2019-01-29 12:38:20 -08:00
{
return zynqmp_pm_invoke_fn ( PM_PM_INIT_FINALIZE , 0 , 0 , 0 , 0 , NULL ) ;
}
2020-04-24 13:57:56 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_init_finalize ) ;
2019-01-29 12:38:20 -08:00
/**
* zynqmp_pm_set_suspend_mode ( ) - Set system suspend mode
* @ mode : Mode to set for system suspend
*
* This API function is used to set mode of system suspend .
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:57 -07:00
int zynqmp_pm_set_suspend_mode ( u32 mode )
2019-01-29 12:38:20 -08:00
{
return zynqmp_pm_invoke_fn ( PM_SET_SUSPEND_MODE , mode , 0 , 0 , 0 , NULL ) ;
}
2020-04-24 13:57:57 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_set_suspend_mode ) ;
2019-01-29 12:38:20 -08:00
2019-02-01 14:08:49 -08:00
/**
* zynqmp_pm_request_node ( ) - Request a node with specific capabilities
* @ node : Node ID of the slave
* @ capabilities : Requested capabilities of the slave
* @ qos : Quality of service ( not supported )
* @ ack : Flag to specify whether acknowledge is requested
*
* This function is used by master to request particular node from firmware .
* Every master must request node before using it .
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:58 -07:00
int zynqmp_pm_request_node ( const u32 node , const u32 capabilities ,
const u32 qos , const enum zynqmp_pm_request_ack ack )
2019-02-01 14:08:49 -08:00
{
return zynqmp_pm_invoke_fn ( PM_REQUEST_NODE , node , capabilities ,
qos , ack , NULL ) ;
}
2020-04-24 13:57:58 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_request_node ) ;
2019-02-01 14:08:49 -08:00
/**
* zynqmp_pm_release_node ( ) - Release a node
* @ node : Node ID of the slave
*
* This function is used by master to inform firmware that master
* has released node . Once released , master must not use that node
* without re - request .
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:57:59 -07:00
int zynqmp_pm_release_node ( const u32 node )
2019-02-01 14:08:49 -08:00
{
return zynqmp_pm_invoke_fn ( PM_RELEASE_NODE , node , 0 , 0 , 0 , NULL ) ;
}
2020-04-24 13:57:59 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_release_node ) ;
2019-02-01 14:08:49 -08:00
/**
* zynqmp_pm_set_requirement ( ) - PM call to set requirement for PM slaves
* @ node : Node ID of the slave
* @ capabilities : Requested capabilities of the slave
* @ qos : Quality of service ( not supported )
* @ ack : Flag to specify whether acknowledge is requested
*
* This API function is to be used for slaves a PU already has requested
* to change its capabilities .
*
* Return : Returns status , either success or error + reason
*/
2020-04-24 13:58:00 -07:00
int zynqmp_pm_set_requirement ( const u32 node , const u32 capabilities ,
const u32 qos ,
const enum zynqmp_pm_request_ack ack )
2019-02-01 14:08:49 -08:00
{
return zynqmp_pm_invoke_fn ( PM_SET_REQUIREMENT , node , capabilities ,
qos , ack , NULL ) ;
}
2020-04-24 13:58:00 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_set_requirement ) ;
2019-02-01 14:08:49 -08:00
2021-06-26 21:22:44 +05:30
/**
* zynqmp_pm_load_pdi - Load and process PDI
* @ src : Source device where PDI is located
* @ address : PDI src address
*
* This function provides support to load PDI from linux
*
* Return : Returns status , either success or error + reason
*/
int zynqmp_pm_load_pdi ( const u32 src , const u64 address )
{
return zynqmp_pm_invoke_fn ( PM_LOAD_PDI , src ,
lower_32_bits ( address ) ,
upper_32_bits ( address ) , 0 , NULL ) ;
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_load_pdi ) ;
2020-02-17 15:56:41 +05:30
/**
2021-08-06 12:32:36 +02:00
* zynqmp_pm_aes_engine - Access AES hardware to encrypt / decrypt the data using
2020-02-17 15:56:41 +05:30
* AES - GCM core .
* @ address : Address of the AesParams structure .
* @ out : Returned output value
*
* Return : Returns status , either success or error code .
*/
2020-04-24 13:58:01 -07:00
int zynqmp_pm_aes_engine ( const u64 address , u32 * out )
2020-02-17 15:56:41 +05:30
{
u32 ret_payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
if ( ! out )
return - EINVAL ;
ret = zynqmp_pm_invoke_fn ( PM_SECURE_AES , upper_32_bits ( address ) ,
lower_32_bits ( address ) ,
0 , 0 , ret_payload ) ;
* out = ret_payload [ 1 ] ;
return ret ;
}
2020-04-24 13:58:01 -07:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_aes_engine ) ;
2020-02-17 15:56:41 +05:30
2020-04-24 13:58:05 -07:00
/**
* zynqmp_pm_system_shutdown - PM call to request a system shutdown or restart
* @ type : Shutdown or restart ? 0 for shutdown , 1 for restart
* @ subtype : Specifies which system should be restarted or shut down
*
* Return : Returns status , either success or error + reason
*/
int zynqmp_pm_system_shutdown ( const u32 type , const u32 subtype )
{
return zynqmp_pm_invoke_fn ( PM_SYSTEM_SHUTDOWN , type , subtype ,
0 , 0 , NULL ) ;
}
2020-04-24 13:58:06 -07:00
/**
* struct zynqmp_pm_shutdown_scope - Struct for shutdown scope
* @ subtype : Shutdown subtype
* @ name : Matching string for scope argument
*
* This struct encapsulates mapping between shutdown scope ID and string .
*/
struct zynqmp_pm_shutdown_scope {
const enum zynqmp_pm_shutdown_subtype subtype ;
const char * name ;
} ;
static struct zynqmp_pm_shutdown_scope shutdown_scopes [ ] = {
[ ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM ] = {
. subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM ,
. name = " subsystem " ,
} ,
[ ZYNQMP_PM_SHUTDOWN_SUBTYPE_PS_ONLY ] = {
. subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_PS_ONLY ,
. name = " ps_only " ,
} ,
[ ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM ] = {
. subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM ,
. name = " system " ,
} ,
} ;
static struct zynqmp_pm_shutdown_scope * selected_scope =
& shutdown_scopes [ ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM ] ;
/**
* zynqmp_pm_is_shutdown_scope_valid - Check if shutdown scope string is valid
* @ scope_string : Shutdown scope string
*
* Return : Return pointer to matching shutdown scope struct from
* array of available options in system if string is valid ,
* otherwise returns NULL .
*/
static struct zynqmp_pm_shutdown_scope *
zynqmp_pm_is_shutdown_scope_valid ( const char * scope_string )
{
int count ;
for ( count = 0 ; count < ARRAY_SIZE ( shutdown_scopes ) ; count + + )
if ( sysfs_streq ( scope_string , shutdown_scopes [ count ] . name ) )
return & shutdown_scopes [ count ] ;
return NULL ;
}
static ssize_t shutdown_scope_show ( struct device * device ,
struct device_attribute * attr ,
char * buf )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( shutdown_scopes ) ; i + + ) {
if ( & shutdown_scopes [ i ] = = selected_scope ) {
strcat ( buf , " [ " ) ;
strcat ( buf , shutdown_scopes [ i ] . name ) ;
strcat ( buf , " ] " ) ;
} else {
strcat ( buf , shutdown_scopes [ i ] . name ) ;
}
strcat ( buf , " " ) ;
}
strcat ( buf , " \n " ) ;
return strlen ( buf ) ;
}
static ssize_t shutdown_scope_store ( struct device * device ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
int ret ;
struct zynqmp_pm_shutdown_scope * scope ;
scope = zynqmp_pm_is_shutdown_scope_valid ( buf ) ;
if ( ! scope )
return - EINVAL ;
ret = zynqmp_pm_system_shutdown ( ZYNQMP_PM_SHUTDOWN_TYPE_SETSCOPE_ONLY ,
scope - > subtype ) ;
if ( ret ) {
pr_err ( " unable to set shutdown scope %s \n " , buf ) ;
return ret ;
}
selected_scope = scope ;
return count ;
}
static DEVICE_ATTR_RW ( shutdown_scope ) ;
2020-04-24 13:58:07 -07:00
static ssize_t health_status_store ( struct device * device ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
int ret ;
unsigned int value ;
ret = kstrtouint ( buf , 10 , & value ) ;
if ( ret )
return ret ;
ret = zynqmp_pm_set_boot_health_status ( value ) ;
if ( ret ) {
dev_err ( device , " unable to set healthy bit value to %u \n " ,
value ) ;
return ret ;
}
return count ;
}
static DEVICE_ATTR_WO ( health_status ) ;
2020-04-24 13:58:04 -07:00
static ssize_t ggs_show ( struct device * device ,
struct device_attribute * attr ,
char * buf ,
u32 reg )
{
int ret ;
u32 ret_payload [ PAYLOAD_ARG_CNT ] ;
ret = zynqmp_pm_read_ggs ( reg , ret_payload ) ;
if ( ret )
return ret ;
return sprintf ( buf , " 0x%x \n " , ret_payload [ 1 ] ) ;
}
static ssize_t ggs_store ( struct device * device ,
struct device_attribute * attr ,
const char * buf , size_t count ,
u32 reg )
{
long value ;
int ret ;
if ( reg > = GSS_NUM_REGS )
return - EINVAL ;
ret = kstrtol ( buf , 16 , & value ) ;
if ( ret ) {
count = - EFAULT ;
goto err ;
}
ret = zynqmp_pm_write_ggs ( reg , value ) ;
if ( ret )
count = - EFAULT ;
err :
return count ;
}
/* GGS register show functions */
# define GGS0_SHOW(N) \
ssize_t ggs # # N # # _show ( struct device * device , \
struct device_attribute * attr , \
char * buf ) \
{ \
return ggs_show ( device , attr , buf , N ) ; \
}
static GGS0_SHOW ( 0 ) ;
static GGS0_SHOW ( 1 ) ;
static GGS0_SHOW ( 2 ) ;
static GGS0_SHOW ( 3 ) ;
/* GGS register store function */
# define GGS0_STORE(N) \
ssize_t ggs # # N # # _store ( struct device * device , \
struct device_attribute * attr , \
const char * buf , \
size_t count ) \
{ \
return ggs_store ( device , attr , buf , count , N ) ; \
}
static GGS0_STORE ( 0 ) ;
static GGS0_STORE ( 1 ) ;
static GGS0_STORE ( 2 ) ;
static GGS0_STORE ( 3 ) ;
static ssize_t pggs_show ( struct device * device ,
struct device_attribute * attr ,
char * buf ,
u32 reg )
{
int ret ;
u32 ret_payload [ PAYLOAD_ARG_CNT ] ;
ret = zynqmp_pm_read_pggs ( reg , ret_payload ) ;
if ( ret )
return ret ;
return sprintf ( buf , " 0x%x \n " , ret_payload [ 1 ] ) ;
}
static ssize_t pggs_store ( struct device * device ,
struct device_attribute * attr ,
const char * buf , size_t count ,
u32 reg )
{
long value ;
int ret ;
if ( reg > = GSS_NUM_REGS )
return - EINVAL ;
ret = kstrtol ( buf , 16 , & value ) ;
if ( ret ) {
count = - EFAULT ;
goto err ;
}
ret = zynqmp_pm_write_pggs ( reg , value ) ;
if ( ret )
count = - EFAULT ;
err :
return count ;
}
# define PGGS0_SHOW(N) \
ssize_t pggs # # N # # _show ( struct device * device , \
struct device_attribute * attr , \
char * buf ) \
{ \
return pggs_show ( device , attr , buf , N ) ; \
}
# define PGGS0_STORE(N) \
ssize_t pggs # # N # # _store ( struct device * device , \
struct device_attribute * attr , \
const char * buf , \
size_t count ) \
{ \
return pggs_store ( device , attr , buf , count , N ) ; \
}
/* PGGS register show functions */
static PGGS0_SHOW ( 0 ) ;
static PGGS0_SHOW ( 1 ) ;
static PGGS0_SHOW ( 2 ) ;
static PGGS0_SHOW ( 3 ) ;
/* PGGS register store functions */
static PGGS0_STORE ( 0 ) ;
static PGGS0_STORE ( 1 ) ;
static PGGS0_STORE ( 2 ) ;
static PGGS0_STORE ( 3 ) ;
/* GGS register attributes */
static DEVICE_ATTR_RW ( ggs0 ) ;
static DEVICE_ATTR_RW ( ggs1 ) ;
static DEVICE_ATTR_RW ( ggs2 ) ;
static DEVICE_ATTR_RW ( ggs3 ) ;
/* PGGS register attributes */
static DEVICE_ATTR_RW ( pggs0 ) ;
static DEVICE_ATTR_RW ( pggs1 ) ;
static DEVICE_ATTR_RW ( pggs2 ) ;
static DEVICE_ATTR_RW ( pggs3 ) ;
static struct attribute * zynqmp_firmware_attrs [ ] = {
& dev_attr_ggs0 . attr ,
& dev_attr_ggs1 . attr ,
& dev_attr_ggs2 . attr ,
& dev_attr_ggs3 . attr ,
& dev_attr_pggs0 . attr ,
& dev_attr_pggs1 . attr ,
& dev_attr_pggs2 . attr ,
& dev_attr_pggs3 . attr ,
2020-04-24 13:58:06 -07:00
& dev_attr_shutdown_scope . attr ,
2020-04-24 13:58:07 -07:00
& dev_attr_health_status . attr ,
2020-04-24 13:58:04 -07:00
NULL ,
} ;
ATTRIBUTE_GROUPS ( zynqmp_firmware ) ;
2018-09-12 12:38:36 -07:00
static int zynqmp_firmware_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct device_node * np ;
int ret ;
np = of_find_compatible_node ( NULL , NULL , " xlnx,zynqmp " ) ;
2019-10-07 11:52:23 -07:00
if ( ! np ) {
np = of_find_compatible_node ( NULL , NULL , " xlnx,versal " ) ;
if ( ! np )
return 0 ;
2019-11-12 00:35:55 -08:00
feature_check_enabled = true ;
2019-10-07 11:52:23 -07:00
}
2018-09-12 12:38:36 -07:00
of_node_put ( np ) ;
ret = get_set_conduit_method ( dev - > of_node ) ;
if ( ret )
return ret ;
/* Check PM API version number */
zynqmp_pm_get_api_version ( & pm_api_version ) ;
if ( pm_api_version < ZYNQMP_PM_VERSION ) {
panic ( " %s Platform Management API version error. Expected: v%d.%d - Found: v%d.%d \n " ,
__func__ ,
ZYNQMP_PM_VERSION_MAJOR , ZYNQMP_PM_VERSION_MINOR ,
pm_api_version > > 16 , pm_api_version & 0xFFFF ) ;
}
pr_info ( " %s Platform Management API v%d.%d \n " , __func__ ,
pm_api_version > > 16 , pm_api_version & 0xFFFF ) ;
/* Check trustzone version number */
ret = zynqmp_pm_get_trustzone_version ( & pm_tz_version ) ;
if ( ret )
panic ( " Legacy trustzone found without version support \n " ) ;
if ( pm_tz_version < ZYNQMP_TZ_VERSION )
panic ( " %s Trustzone version error. Expected: v%d.%d - Found: v%d.%d \n " ,
__func__ ,
ZYNQMP_TZ_VERSION_MAJOR , ZYNQMP_TZ_VERSION_MINOR ,
pm_tz_version > > 16 , pm_tz_version & 0xFFFF ) ;
pr_info ( " %s Trustzone version v%d.%d \n " , __func__ ,
pm_tz_version > > 16 , pm_tz_version & 0xFFFF ) ;
2019-02-01 14:08:50 -08:00
ret = mfd_add_devices ( & pdev - > dev , PLATFORM_DEVID_NONE , firmware_devs ,
ARRAY_SIZE ( firmware_devs ) , NULL , 0 , NULL ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to add MFD devices %d \n " , ret ) ;
return ret ;
}
2020-05-10 15:03:57 +02:00
zynqmp_pm_api_debugfs_init ( ) ;
2018-09-12 12:38:36 -07:00
return of_platform_populate ( dev - > of_node , NULL , NULL , dev ) ;
}
static int zynqmp_firmware_remove ( struct platform_device * pdev )
{
2020-11-23 21:52:41 -08:00
struct pm_api_feature_data * feature_data ;
2021-02-07 22:31:23 -08:00
struct hlist_node * tmp ;
2020-11-23 21:52:41 -08:00
int i ;
2019-02-01 14:08:50 -08:00
mfd_remove_devices ( & pdev - > dev ) ;
2018-09-12 12:38:39 -07:00
zynqmp_pm_api_debugfs_exit ( ) ;
2021-02-07 22:31:23 -08:00
hash_for_each_safe ( pm_api_features_map , i , tmp , feature_data , hentry ) {
2020-11-23 21:52:41 -08:00
hash_del ( & feature_data - > hentry ) ;
kfree ( feature_data ) ;
}
2018-09-12 12:38:36 -07:00
return 0 ;
}
static const struct of_device_id zynqmp_firmware_of_match [ ] = {
{ . compatible = " xlnx,zynqmp-firmware " } ,
2019-10-07 11:52:23 -07:00
{ . compatible = " xlnx,versal-firmware " } ,
2018-09-12 12:38:36 -07:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , zynqmp_firmware_of_match ) ;
static struct platform_driver zynqmp_firmware_driver = {
. driver = {
. name = " zynqmp_firmware " ,
. of_match_table = zynqmp_firmware_of_match ,
2020-04-24 13:58:04 -07:00
. dev_groups = zynqmp_firmware_groups ,
2018-09-12 12:38:36 -07:00
} ,
. probe = zynqmp_firmware_probe ,
. remove = zynqmp_firmware_remove ,
} ;
module_platform_driver ( zynqmp_firmware_driver ) ;