2017-06-06 13:22:51 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* System Control and Management Interface ( SCMI ) Performance Protocol
*
2022-03-30 18:05:38 +03:00
* Copyright ( C ) 2018 - 2022 ARM Ltd .
2017-06-06 13:22:51 +03:00
*/
2020-07-01 18:53:45 +03:00
# define pr_fmt(fmt) "SCMI Notifications PERF - " fmt
2019-07-08 11:41:12 +03:00
# include <linux/bits.h>
2017-06-06 13:22:51 +03:00
# include <linux/of.h>
2019-07-08 11:41:12 +03:00
# include <linux/io.h>
2021-03-16 15:49:02 +03:00
# include <linux/module.h>
2017-06-06 13:22:51 +03:00
# include <linux/platform_device.h>
# include <linux/pm_opp.h>
2020-07-01 18:53:45 +03:00
# include <linux/scmi_protocol.h>
2017-06-06 13:22:51 +03:00
# include <linux/sort.h>
2022-07-04 13:22:40 +03:00
# include <trace/events/scmi.h>
2022-03-30 18:05:38 +03:00
# include "protocols.h"
2020-07-01 18:53:45 +03:00
# include "notify.h"
2017-06-06 13:22:51 +03:00
2022-03-30 18:05:38 +03:00
# define MAX_OPPS 16
2017-06-06 13:22:51 +03:00
enum scmi_performance_protocol_cmd {
PERF_DOMAIN_ATTRIBUTES = 0x3 ,
PERF_DESCRIBE_LEVELS = 0x4 ,
PERF_LIMITS_SET = 0x5 ,
PERF_LIMITS_GET = 0x6 ,
PERF_LEVEL_SET = 0x7 ,
PERF_LEVEL_GET = 0x8 ,
PERF_NOTIFY_LIMITS = 0x9 ,
PERF_NOTIFY_LEVEL = 0xa ,
2019-07-08 11:41:12 +03:00
PERF_DESCRIBE_FASTCHANNEL = 0xb ,
2022-03-30 18:05:40 +03:00
PERF_DOMAIN_NAME_GET = 0xc ,
2017-06-06 13:22:51 +03:00
} ;
2022-07-04 13:22:37 +03:00
enum {
PERF_FC_LEVEL ,
PERF_FC_LIMIT ,
PERF_FC_MAX ,
} ;
2017-06-06 13:22:51 +03:00
struct scmi_opp {
u32 perf ;
u32 power ;
u32 trans_latency_us ;
} ;
struct scmi_msg_resp_perf_attributes {
__le16 num_domains ;
__le16 flags ;
# define POWER_SCALE_IN_MILLIWATT(x) ((x) & BIT(0))
2022-03-30 18:05:50 +03:00
# define POWER_SCALE_IN_MICROWATT(x) ((x) & BIT(1))
2017-06-06 13:22:51 +03:00
__le32 stats_addr_low ;
__le32 stats_addr_high ;
__le32 stats_size ;
} ;
struct scmi_msg_resp_perf_domain_attributes {
__le32 flags ;
# define SUPPORTS_SET_LIMITS(x) ((x) & BIT(31))
# define SUPPORTS_SET_PERF_LVL(x) ((x) & BIT(30))
# define SUPPORTS_PERF_LIMIT_NOTIFY(x) ((x) & BIT(29))
# define SUPPORTS_PERF_LEVEL_NOTIFY(x) ((x) & BIT(28))
2019-07-08 11:41:12 +03:00
# define SUPPORTS_PERF_FASTCHANNELS(x) ((x) & BIT(27))
2022-03-30 18:05:40 +03:00
# define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(26))
2017-06-06 13:22:51 +03:00
__le32 rate_limit_us ;
__le32 sustained_freq_khz ;
__le32 sustained_perf_level ;
2022-03-30 18:05:40 +03:00
u8 name [ SCMI_SHORT_NAME_MAX_SIZE ] ;
2017-06-06 13:22:51 +03:00
} ;
struct scmi_msg_perf_describe_levels {
__le32 domain ;
__le32 level_index ;
} ;
struct scmi_perf_set_limits {
__le32 domain ;
__le32 max_level ;
__le32 min_level ;
} ;
struct scmi_perf_get_limits {
__le32 max_level ;
__le32 min_level ;
} ;
struct scmi_perf_set_level {
__le32 domain ;
__le32 level ;
} ;
struct scmi_perf_notify_level_or_limits {
__le32 domain ;
__le32 notify_enable ;
} ;
2020-07-01 18:53:45 +03:00
struct scmi_perf_limits_notify_payld {
__le32 agent_id ;
__le32 domain_id ;
__le32 range_max ;
__le32 range_min ;
} ;
struct scmi_perf_level_notify_payld {
__le32 agent_id ;
__le32 domain_id ;
__le32 performance_level ;
} ;
2017-06-06 13:22:51 +03:00
struct scmi_msg_resp_perf_describe_levels {
__le16 num_returned ;
__le16 num_remaining ;
struct {
__le32 perf_val ;
__le32 power ;
__le16 transition_latency_us ;
__le16 reserved ;
2020-02-12 02:12:52 +03:00
} opp [ ] ;
2017-06-06 13:22:51 +03:00
} ;
struct perf_dom_info {
bool set_limits ;
bool set_perf ;
bool perf_limit_notify ;
bool perf_level_notify ;
2019-07-08 11:41:12 +03:00
bool perf_fastchannels ;
2017-06-06 13:22:51 +03:00
u32 opp_count ;
u32 sustained_freq_khz ;
u32 sustained_perf_level ;
u32 mult_factor ;
char name [ SCMI_MAX_STR_SIZE ] ;
struct scmi_opp opp [ MAX_OPPS ] ;
2019-07-08 11:41:12 +03:00
struct scmi_fc_info * fc_info ;
2017-06-06 13:22:51 +03:00
} ;
struct scmi_perf_info {
2019-11-22 17:48:40 +03:00
u32 version ;
2017-06-06 13:22:51 +03:00
int num_domains ;
2022-07-07 10:15:54 +03:00
enum scmi_power_scale power_scale ;
2017-06-06 13:22:51 +03:00
u64 stats_addr ;
u32 stats_size ;
struct perf_dom_info * dom_info ;
} ;
2020-07-01 18:53:45 +03:00
static enum scmi_performance_protocol_cmd evt_2_cmd [ ] = {
PERF_NOTIFY_LIMITS ,
PERF_NOTIFY_LEVEL ,
} ;
2021-03-16 15:48:36 +03:00
static int scmi_perf_attributes_get ( const struct scmi_protocol_handle * ph ,
2017-06-06 13:22:51 +03:00
struct scmi_perf_info * pi )
{
int ret ;
struct scmi_xfer * t ;
struct scmi_msg_resp_perf_attributes * attr ;
2021-03-16 15:48:36 +03:00
ret = ph - > xops - > xfer_get_init ( ph , PROTOCOL_ATTRIBUTES , 0 ,
sizeof ( * attr ) , & t ) ;
2017-06-06 13:22:51 +03:00
if ( ret )
return ret ;
attr = t - > rx . buf ;
2021-03-16 15:48:36 +03:00
ret = ph - > xops - > do_xfer ( ph , t ) ;
2017-06-06 13:22:51 +03:00
if ( ! ret ) {
u16 flags = le16_to_cpu ( attr - > flags ) ;
pi - > num_domains = le16_to_cpu ( attr - > num_domains ) ;
2022-07-07 10:15:54 +03:00
if ( POWER_SCALE_IN_MILLIWATT ( flags ) )
pi - > power_scale = SCMI_POWER_MILLIWATTS ;
2022-03-30 18:05:50 +03:00
if ( PROTOCOL_REV_MAJOR ( pi - > version ) > = 0x3 )
2022-07-07 10:15:54 +03:00
if ( POWER_SCALE_IN_MICROWATT ( flags ) )
pi - > power_scale = SCMI_POWER_MICROWATTS ;
2017-06-06 13:22:51 +03:00
pi - > stats_addr = le32_to_cpu ( attr - > stats_addr_low ) |
( u64 ) le32_to_cpu ( attr - > stats_addr_high ) < < 32 ;
pi - > stats_size = le32_to_cpu ( attr - > stats_size ) ;
}
2021-03-16 15:48:36 +03:00
ph - > xops - > xfer_put ( ph , t ) ;
2017-06-06 13:22:51 +03:00
return ret ;
}
static int
2021-03-16 15:48:36 +03:00
scmi_perf_domain_attributes_get ( const struct scmi_protocol_handle * ph ,
2022-03-30 18:05:40 +03:00
u32 domain , struct perf_dom_info * dom_info ,
u32 version )
2017-06-06 13:22:51 +03:00
{
int ret ;
2022-03-30 18:05:40 +03:00
u32 flags ;
2017-06-06 13:22:51 +03:00
struct scmi_xfer * t ;
struct scmi_msg_resp_perf_domain_attributes * attr ;
2021-03-16 15:48:36 +03:00
ret = ph - > xops - > xfer_get_init ( ph , PERF_DOMAIN_ATTRIBUTES ,
sizeof ( domain ) , sizeof ( * attr ) , & t ) ;
2017-06-06 13:22:51 +03:00
if ( ret )
return ret ;
2019-08-07 15:46:27 +03:00
put_unaligned_le32 ( domain , t - > tx . buf ) ;
2017-06-06 13:22:51 +03:00
attr = t - > rx . buf ;
2021-03-16 15:48:36 +03:00
ret = ph - > xops - > do_xfer ( ph , t ) ;
2017-06-06 13:22:51 +03:00
if ( ! ret ) {
2022-03-30 18:05:40 +03:00
flags = le32_to_cpu ( attr - > flags ) ;
2017-06-06 13:22:51 +03:00
dom_info - > set_limits = SUPPORTS_SET_LIMITS ( flags ) ;
dom_info - > set_perf = SUPPORTS_SET_PERF_LVL ( flags ) ;
dom_info - > perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY ( flags ) ;
dom_info - > perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY ( flags ) ;
2019-07-08 11:41:12 +03:00
dom_info - > perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS ( flags ) ;
2017-06-06 13:22:51 +03:00
dom_info - > sustained_freq_khz =
le32_to_cpu ( attr - > sustained_freq_khz ) ;
dom_info - > sustained_perf_level =
le32_to_cpu ( attr - > sustained_perf_level ) ;
2018-09-06 18:10:39 +03:00
if ( ! dom_info - > sustained_freq_khz | |
! dom_info - > sustained_perf_level )
/* CPUFreq converts to kHz, hence default 1000 */
dom_info - > mult_factor = 1000 ;
else
dom_info - > mult_factor =
( dom_info - > sustained_freq_khz * 1000 ) /
2017-06-06 13:22:51 +03:00
dom_info - > sustained_perf_level ;
2022-06-08 12:55:28 +03:00
strscpy ( dom_info - > name , attr - > name , SCMI_SHORT_NAME_MAX_SIZE ) ;
2017-06-06 13:22:51 +03:00
}
2021-03-16 15:48:36 +03:00
ph - > xops - > xfer_put ( ph , t ) ;
2022-03-30 18:05:40 +03:00
/*
* If supported overwrite short name with the extended one ;
* on error just carry on and use already provided short name .
*/
if ( ! ret & & PROTOCOL_REV_MAJOR ( version ) > = 0x3 & &
SUPPORTS_EXTENDED_NAMES ( flags ) )
ph - > hops - > extended_name_get ( ph , PERF_DOMAIN_NAME_GET , domain ,
dom_info - > name , SCMI_MAX_STR_SIZE ) ;
2017-06-06 13:22:51 +03:00
return ret ;
}
static int opp_cmp_func ( const void * opp1 , const void * opp2 )
{
const struct scmi_opp * t1 = opp1 , * t2 = opp2 ;
return t1 - > perf - t2 - > perf ;
}
2022-03-30 18:05:47 +03:00
struct scmi_perf_ipriv {
u32 domain ;
struct perf_dom_info * perf_dom ;
} ;
static void iter_perf_levels_prepare_message ( void * message ,
unsigned int desc_index ,
const void * priv )
{
struct scmi_msg_perf_describe_levels * msg = message ;
const struct scmi_perf_ipriv * p = priv ;
msg - > domain = cpu_to_le32 ( p - > domain ) ;
/* Set the number of OPPs to be skipped/already read */
msg - > level_index = cpu_to_le32 ( desc_index ) ;
}
static int iter_perf_levels_update_state ( struct scmi_iterator_state * st ,
const void * response , void * priv )
{
const struct scmi_msg_resp_perf_describe_levels * r = response ;
st - > num_returned = le16_to_cpu ( r - > num_returned ) ;
st - > num_remaining = le16_to_cpu ( r - > num_remaining ) ;
return 0 ;
}
2017-06-06 13:22:51 +03:00
static int
2022-03-30 18:05:47 +03:00
iter_perf_levels_process_response ( const struct scmi_protocol_handle * ph ,
const void * response ,
struct scmi_iterator_state * st , void * priv )
2017-06-06 13:22:51 +03:00
{
struct scmi_opp * opp ;
2022-03-30 18:05:47 +03:00
const struct scmi_msg_resp_perf_describe_levels * r = response ;
struct scmi_perf_ipriv * p = priv ;
opp = & p - > perf_dom - > opp [ st - > desc_index + st - > loop_idx ] ;
opp - > perf = le32_to_cpu ( r - > opp [ st - > loop_idx ] . perf_val ) ;
opp - > power = le32_to_cpu ( r - > opp [ st - > loop_idx ] . power ) ;
opp - > trans_latency_us =
le16_to_cpu ( r - > opp [ st - > loop_idx ] . transition_latency_us ) ;
p - > perf_dom - > opp_count + + ;
dev_dbg ( ph - > dev , " Level %d Power %d Latency %dus \n " ,
opp - > perf , opp - > power , opp - > trans_latency_us ) ;
return 0 ;
}
2017-06-06 13:22:51 +03:00
2022-03-30 18:05:47 +03:00
static int
scmi_perf_describe_levels_get ( const struct scmi_protocol_handle * ph , u32 domain ,
struct perf_dom_info * perf_dom )
{
int ret ;
void * iter ;
struct scmi_iterator_ops ops = {
. prepare_message = iter_perf_levels_prepare_message ,
. update_state = iter_perf_levels_update_state ,
. process_response = iter_perf_levels_process_response ,
} ;
struct scmi_perf_ipriv ppriv = {
. domain = domain ,
. perf_dom = perf_dom ,
} ;
iter = ph - > hops - > iter_response_init ( ph , & ops , MAX_OPPS ,
PERF_DESCRIBE_LEVELS ,
2022-05-30 14:52:36 +03:00
sizeof ( struct scmi_msg_perf_describe_levels ) ,
& ppriv ) ;
2022-03-30 18:05:47 +03:00
if ( IS_ERR ( iter ) )
return PTR_ERR ( iter ) ;
ret = ph - > hops - > iter_response_run ( iter ) ;
2017-06-06 13:22:51 +03:00
if ( ret )
return ret ;
2022-03-30 18:05:47 +03:00
if ( perf_dom - > opp_count )
sort ( perf_dom - > opp , perf_dom - > opp_count ,
sizeof ( struct scmi_opp ) , opp_cmp_func , NULL ) ;
2017-06-06 13:22:51 +03:00
return ret ;
}
2021-03-16 15:48:36 +03:00
static int scmi_perf_mb_limits_set ( const struct scmi_protocol_handle * ph ,
u32 domain , u32 max_perf , u32 min_perf )
2017-06-06 13:22:51 +03:00
{
int ret ;
struct scmi_xfer * t ;
struct scmi_perf_set_limits * limits ;
2021-03-16 15:48:36 +03:00
ret = ph - > xops - > xfer_get_init ( ph , PERF_LIMITS_SET ,
sizeof ( * limits ) , 0 , & t ) ;
2017-06-06 13:22:51 +03:00
if ( ret )
return ret ;
limits = t - > tx . buf ;
limits - > domain = cpu_to_le32 ( domain ) ;
limits - > max_level = cpu_to_le32 ( max_perf ) ;
limits - > min_level = cpu_to_le32 ( min_perf ) ;
2021-03-16 15:48:36 +03:00
ret = ph - > xops - > do_xfer ( ph , t ) ;
2017-06-06 13:22:51 +03:00
2021-03-16 15:48:36 +03:00
ph - > xops - > xfer_put ( ph , t ) ;
2017-06-06 13:22:51 +03:00
return ret ;
}
2021-03-16 15:48:36 +03:00
static int scmi_perf_limits_set ( const struct scmi_protocol_handle * ph ,
u32 domain , u32 max_perf , u32 min_perf )
2019-07-08 11:41:17 +03:00
{
2021-03-16 15:48:36 +03:00
struct scmi_perf_info * pi = ph - > get_priv ( ph ) ;
2019-07-08 11:41:17 +03:00
struct perf_dom_info * dom = pi - > dom_info + domain ;
2022-03-30 18:05:51 +03:00
if ( PROTOCOL_REV_MAJOR ( pi - > version ) > = 0x3 & & ! max_perf & & ! min_perf )
return - EINVAL ;
2022-07-04 13:22:37 +03:00
if ( dom - > fc_info & & dom - > fc_info [ PERF_FC_LIMIT ] . set_addr ) {
struct scmi_fc_info * fci = & dom - > fc_info [ PERF_FC_LIMIT ] ;
2022-07-04 13:22:40 +03:00
trace_scmi_fc_call ( SCMI_PROTOCOL_PERF , PERF_LIMITS_SET ,
domain , min_perf , max_perf ) ;
2022-07-04 13:22:37 +03:00
iowrite32 ( max_perf , fci - > set_addr ) ;
iowrite32 ( min_perf , fci - > set_addr + 4 ) ;
ph - > hops - > fastchannel_db_ring ( fci - > set_db ) ;
2019-07-08 11:41:17 +03:00
return 0 ;
}
2021-03-16 15:48:36 +03:00
return scmi_perf_mb_limits_set ( ph , domain , max_perf , min_perf ) ;
2019-07-08 11:41:17 +03:00
}
2021-03-16 15:48:36 +03:00
static int scmi_perf_mb_limits_get ( const struct scmi_protocol_handle * ph ,
u32 domain , u32 * max_perf , u32 * min_perf )
2017-06-06 13:22:51 +03:00
{
int ret ;
struct scmi_xfer * t ;
struct scmi_perf_get_limits * limits ;
2021-03-16 15:48:36 +03:00
ret = ph - > xops - > xfer_get_init ( ph , PERF_LIMITS_GET ,
sizeof ( __le32 ) , 0 , & t ) ;
2017-06-06 13:22:51 +03:00
if ( ret )
return ret ;
2019-08-07 15:46:27 +03:00
put_unaligned_le32 ( domain , t - > tx . buf ) ;
2017-06-06 13:22:51 +03:00
2021-03-16 15:48:36 +03:00
ret = ph - > xops - > do_xfer ( ph , t ) ;
2017-06-06 13:22:51 +03:00
if ( ! ret ) {
limits = t - > rx . buf ;
* max_perf = le32_to_cpu ( limits - > max_level ) ;
* min_perf = le32_to_cpu ( limits - > min_level ) ;
}
2021-03-16 15:48:36 +03:00
ph - > xops - > xfer_put ( ph , t ) ;
2017-06-06 13:22:51 +03:00
return ret ;
}
2021-03-16 15:48:36 +03:00
static int scmi_perf_limits_get ( const struct scmi_protocol_handle * ph ,
u32 domain , u32 * max_perf , u32 * min_perf )
2019-07-08 11:41:17 +03:00
{
2021-03-16 15:48:36 +03:00
struct scmi_perf_info * pi = ph - > get_priv ( ph ) ;
2019-07-08 11:41:17 +03:00
struct perf_dom_info * dom = pi - > dom_info + domain ;
2022-07-04 13:22:37 +03:00
if ( dom - > fc_info & & dom - > fc_info [ PERF_FC_LIMIT ] . get_addr ) {
struct scmi_fc_info * fci = & dom - > fc_info [ PERF_FC_LIMIT ] ;
* max_perf = ioread32 ( fci - > get_addr ) ;
* min_perf = ioread32 ( fci - > get_addr + 4 ) ;
2022-07-04 13:22:40 +03:00
trace_scmi_fc_call ( SCMI_PROTOCOL_PERF , PERF_LIMITS_GET ,
domain , * min_perf , * max_perf ) ;
2019-07-08 11:41:17 +03:00
return 0 ;
}
2021-03-16 15:48:36 +03:00
return scmi_perf_mb_limits_get ( ph , domain , max_perf , min_perf ) ;
2019-07-08 11:41:17 +03:00
}
2021-03-16 15:48:36 +03:00
static int scmi_perf_mb_level_set ( const struct scmi_protocol_handle * ph ,
u32 domain , u32 level , bool poll )
2017-06-06 13:22:51 +03:00
{
int ret ;
struct scmi_xfer * t ;
struct scmi_perf_set_level * lvl ;
2021-03-16 15:48:36 +03:00
ret = ph - > xops - > xfer_get_init ( ph , PERF_LEVEL_SET , sizeof ( * lvl ) , 0 , & t ) ;
2017-06-06 13:22:51 +03:00
if ( ret )
return ret ;
2017-07-21 13:42:24 +03:00
t - > hdr . poll_completion = poll ;
2017-06-06 13:22:51 +03:00
lvl = t - > tx . buf ;
lvl - > domain = cpu_to_le32 ( domain ) ;
lvl - > level = cpu_to_le32 ( level ) ;
2021-03-16 15:48:36 +03:00
ret = ph - > xops - > do_xfer ( ph , t ) ;
2017-06-06 13:22:51 +03:00
2021-03-16 15:48:36 +03:00
ph - > xops - > xfer_put ( ph , t ) ;
2017-06-06 13:22:51 +03:00
return ret ;
}
2021-03-16 15:48:36 +03:00
static int scmi_perf_level_set ( const struct scmi_protocol_handle * ph ,
u32 domain , u32 level , bool poll )
2019-07-08 11:41:17 +03:00
{
2021-03-16 15:48:36 +03:00
struct scmi_perf_info * pi = ph - > get_priv ( ph ) ;
2019-07-08 11:41:17 +03:00
struct perf_dom_info * dom = pi - > dom_info + domain ;
2022-07-04 13:22:37 +03:00
if ( dom - > fc_info & & dom - > fc_info [ PERF_FC_LEVEL ] . set_addr ) {
struct scmi_fc_info * fci = & dom - > fc_info [ PERF_FC_LEVEL ] ;
2022-07-04 13:22:40 +03:00
trace_scmi_fc_call ( SCMI_PROTOCOL_PERF , PERF_LEVEL_SET ,
domain , level , 0 ) ;
2022-07-04 13:22:37 +03:00
iowrite32 ( level , fci - > set_addr ) ;
ph - > hops - > fastchannel_db_ring ( fci - > set_db ) ;
2019-07-08 11:41:17 +03:00
return 0 ;
}
2021-03-16 15:48:36 +03:00
return scmi_perf_mb_level_set ( ph , domain , level , poll ) ;
}
static int scmi_perf_mb_level_get ( const struct scmi_protocol_handle * ph ,
u32 domain , u32 * level , bool poll )
2017-06-06 13:22:51 +03:00
{
int ret ;
struct scmi_xfer * t ;
2021-03-16 15:48:36 +03:00
ret = ph - > xops - > xfer_get_init ( ph , PERF_LEVEL_GET ,
sizeof ( u32 ) , sizeof ( u32 ) , & t ) ;
2017-06-06 13:22:51 +03:00
if ( ret )
return ret ;
2017-07-21 13:42:24 +03:00
t - > hdr . poll_completion = poll ;
2019-08-07 15:46:27 +03:00
put_unaligned_le32 ( domain , t - > tx . buf ) ;
2017-06-06 13:22:51 +03:00
2021-03-16 15:48:36 +03:00
ret = ph - > xops - > do_xfer ( ph , t ) ;
2017-06-06 13:22:51 +03:00
if ( ! ret )
2019-08-07 15:46:27 +03:00
* level = get_unaligned_le32 ( t - > rx . buf ) ;
2017-06-06 13:22:51 +03:00
2021-03-16 15:48:36 +03:00
ph - > xops - > xfer_put ( ph , t ) ;
2017-06-06 13:22:51 +03:00
return ret ;
}
2021-03-16 15:48:36 +03:00
static int scmi_perf_level_get ( const struct scmi_protocol_handle * ph ,
u32 domain , u32 * level , bool poll )
2019-07-08 11:41:17 +03:00
{
2021-03-16 15:48:36 +03:00
struct scmi_perf_info * pi = ph - > get_priv ( ph ) ;
2019-07-08 11:41:17 +03:00
struct perf_dom_info * dom = pi - > dom_info + domain ;
2022-07-04 13:22:37 +03:00
if ( dom - > fc_info & & dom - > fc_info [ PERF_FC_LEVEL ] . get_addr ) {
* level = ioread32 ( dom - > fc_info [ PERF_FC_LEVEL ] . get_addr ) ;
2022-07-04 13:22:40 +03:00
trace_scmi_fc_call ( SCMI_PROTOCOL_PERF , PERF_LEVEL_GET ,
domain , * level , 0 ) ;
2019-07-08 11:41:17 +03:00
return 0 ;
}
2021-03-16 15:48:36 +03:00
return scmi_perf_mb_level_get ( ph , domain , level , poll ) ;
}
static int scmi_perf_level_limits_notify ( const struct scmi_protocol_handle * ph ,
2020-07-01 18:53:45 +03:00
u32 domain , int message_id ,
bool enable )
{
int ret ;
struct scmi_xfer * t ;
struct scmi_perf_notify_level_or_limits * notify ;
2021-03-16 15:48:36 +03:00
ret = ph - > xops - > xfer_get_init ( ph , message_id , sizeof ( * notify ) , 0 , & t ) ;
2020-07-01 18:53:45 +03:00
if ( ret )
return ret ;
notify = t - > tx . buf ;
notify - > domain = cpu_to_le32 ( domain ) ;
notify - > notify_enable = enable ? cpu_to_le32 ( BIT ( 0 ) ) : 0 ;
2021-03-16 15:48:36 +03:00
ret = ph - > xops - > do_xfer ( ph , t ) ;
2020-07-01 18:53:45 +03:00
2021-03-16 15:48:36 +03:00
ph - > xops - > xfer_put ( ph , t ) ;
2020-07-01 18:53:45 +03:00
return ret ;
}
2021-03-16 15:48:36 +03:00
static void scmi_perf_domain_init_fc ( const struct scmi_protocol_handle * ph ,
2019-07-08 11:41:12 +03:00
u32 domain , struct scmi_fc_info * * p_fc )
{
struct scmi_fc_info * fc ;
2022-07-04 13:22:37 +03:00
fc = devm_kcalloc ( ph - > dev , PERF_FC_MAX , sizeof ( * fc ) , GFP_KERNEL ) ;
2019-07-08 11:41:12 +03:00
if ( ! fc )
return ;
2022-07-04 13:22:37 +03:00
ph - > hops - > fastchannel_init ( ph , PERF_DESCRIBE_FASTCHANNEL ,
PERF_LEVEL_SET , 4 , domain ,
& fc [ PERF_FC_LEVEL ] . set_addr ,
& fc [ PERF_FC_LEVEL ] . set_db ) ;
ph - > hops - > fastchannel_init ( ph , PERF_DESCRIBE_FASTCHANNEL ,
PERF_LEVEL_GET , 4 , domain ,
& fc [ PERF_FC_LEVEL ] . get_addr , NULL ) ;
ph - > hops - > fastchannel_init ( ph , PERF_DESCRIBE_FASTCHANNEL ,
PERF_LIMITS_SET , 8 , domain ,
& fc [ PERF_FC_LIMIT ] . set_addr ,
& fc [ PERF_FC_LIMIT ] . set_db ) ;
ph - > hops - > fastchannel_init ( ph , PERF_DESCRIBE_FASTCHANNEL ,
PERF_LIMITS_GET , 8 , domain ,
& fc [ PERF_FC_LIMIT ] . get_addr , NULL ) ;
2019-07-08 11:41:12 +03:00
* p_fc = fc ;
}
2017-06-06 13:22:51 +03:00
/* Device specific ops */
static int scmi_dev_domain_id ( struct device * dev )
{
struct of_phandle_args clkspec ;
if ( of_parse_phandle_with_args ( dev - > of_node , " clocks " , " #clock-cells " ,
0 , & clkspec ) )
return - EINVAL ;
return clkspec . args [ 0 ] ;
}
2021-03-16 15:48:36 +03:00
static int scmi_dvfs_device_opps_add ( const struct scmi_protocol_handle * ph ,
2018-05-09 19:52:06 +03:00
struct device * dev )
2017-06-06 13:22:51 +03:00
{
int idx , ret , domain ;
unsigned long freq ;
struct scmi_opp * opp ;
struct perf_dom_info * dom ;
2021-03-16 15:48:36 +03:00
struct scmi_perf_info * pi = ph - > get_priv ( ph ) ;
2017-06-06 13:22:51 +03:00
domain = scmi_dev_domain_id ( dev ) ;
if ( domain < 0 )
return domain ;
dom = pi - > dom_info + domain ;
for ( opp = dom - > opp , idx = 0 ; idx < dom - > opp_count ; idx + + , opp + + ) {
freq = opp - > perf * dom - > mult_factor ;
ret = dev_pm_opp_add ( dev , freq , 0 ) ;
if ( ret ) {
dev_warn ( dev , " failed to add opp %luHz \n " , freq ) ;
while ( idx - - > 0 ) {
freq = ( - - opp ) - > perf * dom - > mult_factor ;
dev_pm_opp_remove ( dev , freq ) ;
}
return ret ;
}
}
return 0 ;
}
2021-03-16 15:48:36 +03:00
static int
scmi_dvfs_transition_latency_get ( const struct scmi_protocol_handle * ph ,
struct device * dev )
2017-06-06 13:22:51 +03:00
{
struct perf_dom_info * dom ;
2021-03-16 15:48:36 +03:00
struct scmi_perf_info * pi = ph - > get_priv ( ph ) ;
2017-06-06 13:22:51 +03:00
int domain = scmi_dev_domain_id ( dev ) ;
if ( domain < 0 )
return domain ;
dom = pi - > dom_info + domain ;
/* uS to nS */
return dom - > opp [ dom - > opp_count - 1 ] . trans_latency_us * 1000 ;
}
2021-03-16 15:48:36 +03:00
static int scmi_dvfs_freq_set ( const struct scmi_protocol_handle * ph , u32 domain ,
2017-07-21 13:42:24 +03:00
unsigned long freq , bool poll )
2017-06-06 13:22:51 +03:00
{
2021-03-16 15:48:36 +03:00
struct scmi_perf_info * pi = ph - > get_priv ( ph ) ;
2017-06-06 13:22:51 +03:00
struct perf_dom_info * dom = pi - > dom_info + domain ;
2021-03-16 15:48:36 +03:00
return scmi_perf_level_set ( ph , domain , freq / dom - > mult_factor , poll ) ;
2017-06-06 13:22:51 +03:00
}
2021-03-16 15:48:36 +03:00
static int scmi_dvfs_freq_get ( const struct scmi_protocol_handle * ph , u32 domain ,
2017-07-21 13:42:24 +03:00
unsigned long * freq , bool poll )
2017-06-06 13:22:51 +03:00
{
int ret ;
u32 level ;
2021-03-16 15:48:36 +03:00
struct scmi_perf_info * pi = ph - > get_priv ( ph ) ;
2017-06-06 13:22:51 +03:00
struct perf_dom_info * dom = pi - > dom_info + domain ;
2021-03-16 15:48:36 +03:00
ret = scmi_perf_level_get ( ph , domain , & level , poll ) ;
2017-06-06 13:22:51 +03:00
if ( ! ret )
* freq = level * dom - > mult_factor ;
return ret ;
}
2021-03-16 15:48:36 +03:00
static int scmi_dvfs_est_power_get ( const struct scmi_protocol_handle * ph ,
u32 domain , unsigned long * freq ,
unsigned long * power )
{
struct scmi_perf_info * pi = ph - > get_priv ( ph ) ;
2018-09-10 19:28:10 +03:00
struct perf_dom_info * dom ;
unsigned long opp_freq ;
int idx , ret = - EINVAL ;
struct scmi_opp * opp ;
dom = pi - > dom_info + domain ;
if ( ! dom )
return - EIO ;
for ( opp = dom - > opp , idx = 0 ; idx < dom - > opp_count ; idx + + , opp + + ) {
opp_freq = opp - > perf * dom - > mult_factor ;
if ( opp_freq < * freq )
continue ;
* freq = opp_freq ;
* power = opp - > power ;
ret = 0 ;
break ;
}
return ret ;
}
2021-03-16 15:48:36 +03:00
static bool scmi_fast_switch_possible ( const struct scmi_protocol_handle * ph ,
2020-06-17 12:43:31 +03:00
struct device * dev )
{
struct perf_dom_info * dom ;
2021-03-16 15:48:36 +03:00
struct scmi_perf_info * pi = ph - > get_priv ( ph ) ;
2020-06-17 12:43:31 +03:00
dom = pi - > dom_info + scmi_dev_domain_id ( dev ) ;
2022-07-04 13:22:37 +03:00
return dom - > fc_info & & dom - > fc_info [ PERF_FC_LEVEL ] . set_addr ;
2020-06-17 12:43:31 +03:00
}
2022-07-07 10:15:54 +03:00
static enum scmi_power_scale
scmi_power_scale_get ( const struct scmi_protocol_handle * ph )
2021-03-16 15:48:36 +03:00
{
struct scmi_perf_info * pi = ph - > get_priv ( ph ) ;
2020-11-24 13:43:45 +03:00
2022-07-07 10:15:54 +03:00
return pi - > power_scale ;
2020-11-24 13:43:45 +03:00
}
2021-03-16 15:48:36 +03:00
static const struct scmi_perf_proto_ops perf_proto_ops = {
2017-06-06 13:22:51 +03:00
. limits_set = scmi_perf_limits_set ,
. limits_get = scmi_perf_limits_get ,
. level_set = scmi_perf_level_set ,
. level_get = scmi_perf_level_get ,
. device_domain_id = scmi_dev_domain_id ,
2018-05-09 19:52:06 +03:00
. transition_latency_get = scmi_dvfs_transition_latency_get ,
. device_opps_add = scmi_dvfs_device_opps_add ,
2017-06-06 13:22:51 +03:00
. freq_set = scmi_dvfs_freq_set ,
. freq_get = scmi_dvfs_freq_get ,
2018-09-10 19:28:10 +03:00
. est_power_get = scmi_dvfs_est_power_get ,
2020-06-17 12:43:31 +03:00
. fast_switch_possible = scmi_fast_switch_possible ,
2022-07-07 10:15:54 +03:00
. power_scale_get = scmi_power_scale_get ,
2017-06-06 13:22:51 +03:00
} ;
2021-03-16 15:48:59 +03:00
static int scmi_perf_set_notify_enabled ( const struct scmi_protocol_handle * ph ,
2020-07-01 18:53:45 +03:00
u8 evt_id , u32 src_id , bool enable )
{
int ret , cmd_id ;
if ( evt_id > = ARRAY_SIZE ( evt_2_cmd ) )
return - EINVAL ;
cmd_id = evt_2_cmd [ evt_id ] ;
2021-03-16 15:48:36 +03:00
ret = scmi_perf_level_limits_notify ( ph , src_id , cmd_id , enable ) ;
2020-07-01 18:53:45 +03:00
if ( ret )
pr_debug ( " FAIL_ENABLED - evt[%X] dom[%d] - ret:%d \n " ,
evt_id , src_id , ret ) ;
return ret ;
}
2021-03-16 15:48:59 +03:00
static void * scmi_perf_fill_custom_report ( const struct scmi_protocol_handle * ph ,
2020-07-10 16:39:19 +03:00
u8 evt_id , ktime_t timestamp ,
2020-07-01 18:53:45 +03:00
const void * payld , size_t payld_sz ,
void * report , u32 * src_id )
{
void * rep = NULL ;
switch ( evt_id ) {
case SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED :
{
const struct scmi_perf_limits_notify_payld * p = payld ;
struct scmi_perf_limits_report * r = report ;
if ( sizeof ( * p ) ! = payld_sz )
break ;
r - > timestamp = timestamp ;
r - > agent_id = le32_to_cpu ( p - > agent_id ) ;
r - > domain_id = le32_to_cpu ( p - > domain_id ) ;
r - > range_max = le32_to_cpu ( p - > range_max ) ;
r - > range_min = le32_to_cpu ( p - > range_min ) ;
* src_id = r - > domain_id ;
rep = r ;
break ;
}
case SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED :
{
const struct scmi_perf_level_notify_payld * p = payld ;
struct scmi_perf_level_report * r = report ;
if ( sizeof ( * p ) ! = payld_sz )
break ;
r - > timestamp = timestamp ;
r - > agent_id = le32_to_cpu ( p - > agent_id ) ;
r - > domain_id = le32_to_cpu ( p - > domain_id ) ;
r - > performance_level = le32_to_cpu ( p - > performance_level ) ;
* src_id = r - > domain_id ;
rep = r ;
break ;
}
default :
break ;
}
return rep ;
}
2021-03-16 15:48:59 +03:00
static int scmi_perf_get_num_sources ( const struct scmi_protocol_handle * ph )
2021-03-16 15:48:31 +03:00
{
2021-03-16 15:48:59 +03:00
struct scmi_perf_info * pi = ph - > get_priv ( ph ) ;
2021-03-16 15:48:31 +03:00
if ( ! pi )
return - EINVAL ;
return pi - > num_domains ;
}
2020-07-01 18:53:45 +03:00
static const struct scmi_event perf_events [ ] = {
{
. id = SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED ,
. max_payld_sz = sizeof ( struct scmi_perf_limits_notify_payld ) ,
. max_report_sz = sizeof ( struct scmi_perf_limits_report ) ,
} ,
{
. id = SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED ,
. max_payld_sz = sizeof ( struct scmi_perf_level_notify_payld ) ,
. max_report_sz = sizeof ( struct scmi_perf_level_report ) ,
} ,
} ;
static const struct scmi_event_ops perf_event_ops = {
2021-03-16 15:48:31 +03:00
. get_num_sources = scmi_perf_get_num_sources ,
2020-07-01 18:53:45 +03:00
. set_notify_enabled = scmi_perf_set_notify_enabled ,
. fill_custom_report = scmi_perf_fill_custom_report ,
} ;
2021-03-16 15:48:31 +03:00
static const struct scmi_protocol_events perf_protocol_events = {
. queue_sz = SCMI_PROTO_QUEUE_SZ ,
. ops = & perf_event_ops ,
. evts = perf_events ,
. num_events = ARRAY_SIZE ( perf_events ) ,
} ;
2021-03-16 15:48:36 +03:00
static int scmi_perf_protocol_init ( const struct scmi_protocol_handle * ph )
2017-06-06 13:22:51 +03:00
{
2022-03-30 18:05:31 +03:00
int domain , ret ;
2017-06-06 13:22:51 +03:00
u32 version ;
struct scmi_perf_info * pinfo ;
2022-03-30 18:05:31 +03:00
ret = ph - > xops - > version_get ( ph , & version ) ;
if ( ret )
return ret ;
2017-06-06 13:22:51 +03:00
2021-03-16 15:48:36 +03:00
dev_dbg ( ph - > dev , " Performance Version %d.%d \n " ,
2017-06-06 13:22:51 +03:00
PROTOCOL_REV_MAJOR ( version ) , PROTOCOL_REV_MINOR ( version ) ) ;
2021-03-16 15:48:36 +03:00
pinfo = devm_kzalloc ( ph - > dev , sizeof ( * pinfo ) , GFP_KERNEL ) ;
2017-06-06 13:22:51 +03:00
if ( ! pinfo )
return - ENOMEM ;
2022-03-30 18:05:31 +03:00
ret = scmi_perf_attributes_get ( ph , pinfo ) ;
if ( ret )
return ret ;
2017-06-06 13:22:51 +03:00
2021-03-16 15:48:36 +03:00
pinfo - > dom_info = devm_kcalloc ( ph - > dev , pinfo - > num_domains ,
2017-06-06 13:22:51 +03:00
sizeof ( * pinfo - > dom_info ) , GFP_KERNEL ) ;
if ( ! pinfo - > dom_info )
return - ENOMEM ;
for ( domain = 0 ; domain < pinfo - > num_domains ; domain + + ) {
struct perf_dom_info * dom = pinfo - > dom_info + domain ;
2022-03-30 18:05:40 +03:00
scmi_perf_domain_attributes_get ( ph , domain , dom , version ) ;
2021-03-16 15:48:36 +03:00
scmi_perf_describe_levels_get ( ph , domain , dom ) ;
2019-07-08 11:41:12 +03:00
if ( dom - > perf_fastchannels )
2021-03-16 15:48:36 +03:00
scmi_perf_domain_init_fc ( ph , domain , & dom - > fc_info ) ;
2017-06-06 13:22:51 +03:00
}
2019-11-22 17:48:40 +03:00
pinfo - > version = version ;
2021-03-16 15:48:36 +03:00
return ph - > set_priv ( ph , pinfo ) ;
2017-06-06 13:22:51 +03:00
}
2021-03-16 15:48:26 +03:00
static const struct scmi_protocol scmi_perf = {
. id = SCMI_PROTOCOL_PERF ,
2021-03-16 15:49:02 +03:00
. owner = THIS_MODULE ,
2021-03-16 15:48:36 +03:00
. instance_init = & scmi_perf_protocol_init ,
. ops = & perf_proto_ops ,
2021-03-16 15:48:31 +03:00
. events = & perf_protocol_events ,
2021-03-16 15:48:26 +03:00
} ;
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER ( perf , scmi_perf )