2018-09-12 22:38:36 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Xilinx Zynq MPSoC Firmware layer
*
* Copyright ( C ) 2014 - 2018 Xilinx , Inc .
*
* 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-02 01:08:50 +03:00
# include <linux/mfd/core.h>
2018-09-12 22:38:36 +03:00
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_platform.h>
# include <linux/slab.h>
# include <linux/uaccess.h>
# include <linux/firmware/xlnx-zynqmp.h>
2018-09-12 22:38:39 +03:00
# include "zynqmp-debug.h"
2018-09-12 22:38:36 +03:00
2019-03-05 02:18:08 +03:00
static const struct zynqmp_eemi_ops * eemi_ops_tbl ;
2019-11-12 11:35:55 +03:00
static bool feature_check_enabled ;
static u32 zynqmp_pm_features [ PM_API_MAX ] ;
2019-02-02 01:08:50 +03:00
static const struct mfd_cell firmware_devs [ ] = {
{
. name = " zynqmp_power_controller " ,
} ,
} ;
2018-09-12 22:38:36 +03: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 11:35:55 +03:00
case XST_PM_NO_FEATURE :
return - ENOTSUPP ;
2018-09-12 22:38:36 +03:00
case XST_PM_NO_ACCESS :
return - EACCES ;
case XST_PM_ABORT_SUSPEND :
return - ECANCELED ;
2019-12-05 09:35:56 +03:00
case XST_PM_MULT_USER :
return - EUSERS ;
2018-09-12 22:38:36 +03: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 11:35:55 +03: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 ] ;
if ( ! feature_check_enabled )
return 0 ;
/* Return value if feature is already checked */
if ( zynqmp_pm_features [ api_id ] ! = PM_FEATURE_UNCHECKED )
return zynqmp_pm_features [ api_id ] ;
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 ) ;
if ( ret ) {
zynqmp_pm_features [ api_id ] = PM_FEATURE_INVALID ;
return PM_FEATURE_INVALID ;
}
zynqmp_pm_features [ api_id ] = ret_payload [ 1 ] ;
return zynqmp_pm_features [ api_id ] ;
}
2018-09-12 22:38:36 +03: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 ] ;
2019-11-12 11:35:55 +03:00
if ( zynqmp_pm_feature ( pm_api_id ) = = PM_FEATURE_INVALID )
return - ENOTSUPP ;
2018-09-12 22:38:36 +03: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 23:57:43 +03:00
int zynqmp_pm_get_api_version ( u32 * version )
2018-09-12 22:38:36 +03: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 23:57:43 +03:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_get_api_version ) ;
2018-09-12 22:38:36 +03:00
2019-02-06 14:07:19 +03:00
/**
* 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 23:57:44 +03:00
int zynqmp_pm_get_chipid ( u32 * idcode , u32 * version )
2019-02-06 14:07:19 +03:00
{
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 23:57:44 +03:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_get_chipid ) ;
2019-02-06 14:07:19 +03:00
2018-09-12 22:38:36 +03: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 22:38:37 +03: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 23:57:45 +03:00
int zynqmp_pm_query_data ( struct zynqmp_pm_query_data qdata , u32 * out )
2018-09-12 22:38:37 +03:00
{
2018-09-12 22:38:38 +03: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 23:57:45 +03:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_query_data ) ;
2018-09-12 22:38:38 +03: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 23:57:46 +03:00
int zynqmp_pm_clock_enable ( u32 clock_id )
2018-09-12 22:38:38 +03:00
{
return zynqmp_pm_invoke_fn ( PM_CLOCK_ENABLE , clock_id , 0 , 0 , 0 , NULL ) ;
}
2020-04-24 23:57:46 +03:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_clock_enable ) ;
2018-09-12 22:38:38 +03: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 23:57:47 +03:00
int zynqmp_pm_clock_disable ( u32 clock_id )
2018-09-12 22:38:38 +03:00
{
return zynqmp_pm_invoke_fn ( PM_CLOCK_DISABLE , clock_id , 0 , 0 , 0 , NULL ) ;
}
2020-04-24 23:57:47 +03:00
EXPORT_SYMBOL_GPL ( zynqmp_pm_clock_disable ) ;
2018-09-12 22:38:38 +03: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
*/
static int zynqmp_pm_clock_getstate ( u32 clock_id , u32 * state )
{
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 ;
}
/**
* 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
*/
static int zynqmp_pm_clock_setdivider ( u32 clock_id , u32 divider )
{
return zynqmp_pm_invoke_fn ( PM_CLOCK_SETDIVIDER , clock_id , divider ,
0 , 0 , NULL ) ;
}
/**
* 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
*/
static int zynqmp_pm_clock_getdivider ( u32 clock_id , u32 * divider )
{
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 ;
}
/**
* 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
*/
static int zynqmp_pm_clock_setrate ( u32 clock_id , u64 rate )
{
return zynqmp_pm_invoke_fn ( PM_CLOCK_SETRATE , clock_id ,
lower_32_bits ( rate ) ,
upper_32_bits ( rate ) ,
0 , NULL ) ;
}
/**
* 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
*/
static int zynqmp_pm_clock_getrate ( u32 clock_id , u64 * rate )
{
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 ;
}
/**
* 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
*/
static int zynqmp_pm_clock_setparent ( u32 clock_id , u32 parent_id )
{
return zynqmp_pm_invoke_fn ( PM_CLOCK_SETPARENT , clock_id ,
parent_id , 0 , 0 , NULL ) ;
}
/**
* 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
*/
static int zynqmp_pm_clock_getparent ( u32 clock_id , u32 * parent_id )
{
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 22:38:37 +03:00
}
2018-10-08 21:21:44 +03:00
/**
* zynqmp_is_valid_ioctl ( ) - Check whether IOCTL ID is valid or not
* @ ioctl_id : IOCTL ID
*
* Return : 1 if IOCTL is valid else 0
*/
static inline int zynqmp_is_valid_ioctl ( u32 ioctl_id )
{
switch ( ioctl_id ) {
2020-01-21 13:21:33 +03:00
case IOCTL_SD_DLL_RESET :
2020-01-21 13:21:32 +03:00
case IOCTL_SET_SD_TAPDELAY :
2018-10-08 21:21:44 +03:00
case IOCTL_SET_PLL_FRAC_MODE :
case IOCTL_GET_PLL_FRAC_MODE :
case IOCTL_SET_PLL_FRAC_DATA :
case IOCTL_GET_PLL_FRAC_DATA :
return 1 ;
default :
return 0 ;
}
}
/**
* zynqmp_pm_ioctl ( ) - PM IOCTL API for device control and configs
* @ node_id : Node ID of the device
* @ ioctl_id : ID of the requested IOCTL
* @ arg1 : Argument 1 to requested IOCTL call
* @ arg2 : Argument 2 to requested IOCTL call
* @ out : Returned output value
*
* This function calls IOCTL to firmware for device control and configuration .
*
* Return : Returns status , either success or error + reason
*/
static int zynqmp_pm_ioctl ( u32 node_id , u32 ioctl_id , u32 arg1 , u32 arg2 ,
u32 * out )
{
if ( ! zynqmp_is_valid_ioctl ( ioctl_id ) )
return - EINVAL ;
return zynqmp_pm_invoke_fn ( PM_IOCTL , node_id , ioctl_id ,
arg1 , arg2 , out ) ;
}
2019-01-25 10:46:52 +03:00
/**
* 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
*/
static int zynqmp_pm_reset_assert ( const enum zynqmp_pm_reset reset ,
const enum zynqmp_pm_reset_action assert_flag )
{
return zynqmp_pm_invoke_fn ( PM_RESET_ASSERT , reset , assert_flag ,
0 , 0 , NULL ) ;
}
/**
* 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
*/
static int zynqmp_pm_reset_get_status ( const enum zynqmp_pm_reset reset ,
u32 * status )
{
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 ;
}
2019-04-15 10:17:46 +03:00
/**
* 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
*/
static int zynqmp_pm_fpga_load ( const u64 address , const u32 size ,
const u32 flags )
{
return zynqmp_pm_invoke_fn ( PM_FPGA_LOAD , lower_32_bits ( address ) ,
upper_32_bits ( address ) , size , flags , NULL ) ;
}
/**
* 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
*/
static int zynqmp_pm_fpga_get_status ( u32 * value )
{
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 ;
}
2019-01-29 23:38:20 +03:00
/**
* zynqmp_pm_init_finalize ( ) - PM call to inform firmware that the caller
* master has initialized its own power management
*
* This API function is to be used for notify the power management controller
* about the completed power management initialization .
*
* Return : Returns status , either success or error + reason
*/
static int zynqmp_pm_init_finalize ( void )
{
return zynqmp_pm_invoke_fn ( PM_PM_INIT_FINALIZE , 0 , 0 , 0 , 0 , NULL ) ;
}
/**
* 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
*/
static int zynqmp_pm_set_suspend_mode ( u32 mode )
{
return zynqmp_pm_invoke_fn ( PM_SET_SUSPEND_MODE , mode , 0 , 0 , 0 , NULL ) ;
}
2019-02-02 01:08:49 +03: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
*/
static int zynqmp_pm_request_node ( const u32 node , const u32 capabilities ,
const u32 qos ,
const enum zynqmp_pm_request_ack ack )
{
return zynqmp_pm_invoke_fn ( PM_REQUEST_NODE , node , capabilities ,
qos , ack , NULL ) ;
}
/**
* 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
*/
static int zynqmp_pm_release_node ( const u32 node )
{
return zynqmp_pm_invoke_fn ( PM_RELEASE_NODE , node , 0 , 0 , 0 , NULL ) ;
}
/**
* 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
*/
static int zynqmp_pm_set_requirement ( const u32 node , const u32 capabilities ,
const u32 qos ,
const enum zynqmp_pm_request_ack ack )
{
return zynqmp_pm_invoke_fn ( PM_SET_REQUIREMENT , node , capabilities ,
qos , ack , NULL ) ;
}
2020-02-17 13:26:41 +03:00
/**
* zynqmp_pm_aes - Access AES hardware to encrypt / decrypt the data using
* AES - GCM core .
* @ address : Address of the AesParams structure .
* @ out : Returned output value
*
* Return : Returns status , either success or error code .
*/
static int zynqmp_pm_aes_engine ( const u64 address , u32 * out )
{
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 ;
}
2018-09-12 22:38:36 +03:00
static const struct zynqmp_eemi_ops eemi_ops = {
2018-09-12 22:38:38 +03:00
. clock_getstate = zynqmp_pm_clock_getstate ,
. clock_setdivider = zynqmp_pm_clock_setdivider ,
. clock_getdivider = zynqmp_pm_clock_getdivider ,
. clock_setrate = zynqmp_pm_clock_setrate ,
. clock_getrate = zynqmp_pm_clock_getrate ,
. clock_setparent = zynqmp_pm_clock_setparent ,
. clock_getparent = zynqmp_pm_clock_getparent ,
2018-10-08 21:21:44 +03:00
. ioctl = zynqmp_pm_ioctl ,
2019-01-25 10:46:52 +03:00
. reset_assert = zynqmp_pm_reset_assert ,
. reset_get_status = zynqmp_pm_reset_get_status ,
2019-01-29 23:38:20 +03:00
. init_finalize = zynqmp_pm_init_finalize ,
. set_suspend_mode = zynqmp_pm_set_suspend_mode ,
2019-02-02 01:08:49 +03:00
. request_node = zynqmp_pm_request_node ,
. release_node = zynqmp_pm_release_node ,
. set_requirement = zynqmp_pm_set_requirement ,
2019-04-15 10:17:46 +03:00
. fpga_load = zynqmp_pm_fpga_load ,
. fpga_get_status = zynqmp_pm_fpga_get_status ,
2020-02-17 13:26:41 +03:00
. aes = zynqmp_pm_aes_engine ,
2018-09-12 22:38:36 +03:00
} ;
/**
* zynqmp_pm_get_eemi_ops - Get eemi ops functions
*
* Return : Pointer of eemi_ops structure
*/
const struct zynqmp_eemi_ops * zynqmp_pm_get_eemi_ops ( void )
{
2019-03-05 02:18:08 +03:00
if ( eemi_ops_tbl )
return eemi_ops_tbl ;
else
return ERR_PTR ( - EPROBE_DEFER ) ;
2018-09-12 22:38:36 +03:00
}
EXPORT_SYMBOL_GPL ( zynqmp_pm_get_eemi_ops ) ;
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 21:52:23 +03:00
if ( ! np ) {
np = of_find_compatible_node ( NULL , NULL , " xlnx,versal " ) ;
if ( ! np )
return 0 ;
2019-11-12 11:35:55 +03:00
feature_check_enabled = true ;
2019-10-07 21:52:23 +03:00
}
2018-09-12 22:38:36 +03: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-03-05 02:18:08 +03:00
/* Assign eemi_ops_table */
eemi_ops_tbl = & eemi_ops ;
2018-09-12 22:38:39 +03:00
zynqmp_pm_api_debugfs_init ( ) ;
2019-02-02 01:08:50 +03: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 ;
}
2018-09-12 22:38:36 +03:00
return of_platform_populate ( dev - > of_node , NULL , NULL , dev ) ;
}
static int zynqmp_firmware_remove ( struct platform_device * pdev )
{
2019-02-02 01:08:50 +03:00
mfd_remove_devices ( & pdev - > dev ) ;
2018-09-12 22:38:39 +03:00
zynqmp_pm_api_debugfs_exit ( ) ;
2018-09-12 22:38:36 +03:00
return 0 ;
}
static const struct of_device_id zynqmp_firmware_of_match [ ] = {
{ . compatible = " xlnx,zynqmp-firmware " } ,
2019-10-07 21:52:23 +03:00
{ . compatible = " xlnx,versal-firmware " } ,
2018-09-12 22:38:36 +03: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 ,
} ,
. probe = zynqmp_firmware_probe ,
. remove = zynqmp_firmware_remove ,
} ;
module_platform_driver ( zynqmp_firmware_driver ) ;