2020-02-28 12:11:40 +02:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2020 , The Linux Foundation . All rights reserved .
*/
# include <linux/interconnect.h>
# include <linux/interconnect-provider.h>
# include <linux/module.h>
2020-09-03 16:31:30 +03:00
# include <linux/of.h>
2023-07-14 11:46:36 -06:00
# include <linux/of_platform.h>
2020-09-03 16:31:30 +03:00
# include <linux/slab.h>
2020-02-28 12:11:40 +02:00
# include "bcm-voter.h"
2022-07-12 09:59:26 +08:00
# include "icc-common.h"
2020-02-28 12:11:40 +02:00
# include "icc-rpmh.h"
/**
* qcom_icc_pre_aggregate - cleans up stale values from prior icc_set
* @ node : icc node to operate on
*/
void qcom_icc_pre_aggregate ( struct icc_node * node )
{
size_t i ;
struct qcom_icc_node * qn ;
2021-11-25 19:47:51 +02:00
struct qcom_icc_provider * qp ;
2020-02-28 12:11:40 +02:00
qn = node - > data ;
2021-11-25 19:47:51 +02:00
qp = to_qcom_provider ( node - > provider ) ;
2020-02-28 12:11:40 +02:00
for ( i = 0 ; i < QCOM_ICC_NUM_BUCKETS ; i + + ) {
qn - > sum_avg [ i ] = 0 ;
qn - > max_peak [ i ] = 0 ;
}
2021-11-25 19:47:51 +02:00
for ( i = 0 ; i < qn - > num_bcms ; i + + )
qcom_icc_bcm_voter_add ( qp - > voter , qn - > bcms [ i ] ) ;
2020-02-28 12:11:40 +02:00
}
EXPORT_SYMBOL_GPL ( qcom_icc_pre_aggregate ) ;
/**
* qcom_icc_aggregate - aggregate bw for buckets indicated by tag
* @ node : node to aggregate
* @ tag : tag to indicate which buckets to aggregate
* @ avg_bw : new bw to sum aggregate
* @ peak_bw : new bw to max aggregate
* @ agg_avg : existing aggregate avg bw val
* @ agg_peak : existing aggregate peak bw val
*/
int qcom_icc_aggregate ( struct icc_node * node , u32 tag , u32 avg_bw ,
u32 peak_bw , u32 * agg_avg , u32 * agg_peak )
{
size_t i ;
struct qcom_icc_node * qn ;
qn = node - > data ;
if ( ! tag )
tag = QCOM_ICC_TAG_ALWAYS ;
for ( i = 0 ; i < QCOM_ICC_NUM_BUCKETS ; i + + ) {
if ( tag & BIT ( i ) ) {
qn - > sum_avg [ i ] + = avg_bw ;
qn - > max_peak [ i ] = max_t ( u32 , qn - > max_peak [ i ] , peak_bw ) ;
}
2021-07-21 10:54:31 -07:00
if ( node - > init_avg | | node - > init_peak ) {
qn - > sum_avg [ i ] = max_t ( u64 , qn - > sum_avg [ i ] , node - > init_avg ) ;
qn - > max_peak [ i ] = max_t ( u64 , qn - > max_peak [ i ] , node - > init_peak ) ;
}
2020-02-28 12:11:40 +02:00
}
* agg_avg + = avg_bw ;
* agg_peak = max_t ( u32 , * agg_peak , peak_bw ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( qcom_icc_aggregate ) ;
/**
* qcom_icc_set - set the constraints based on path
* @ src : source node for the path to set constraints on
* @ dst : destination node for the path to set constraints on
*
* Return : 0 on success , or an error code otherwise
*/
int qcom_icc_set ( struct icc_node * src , struct icc_node * dst )
{
struct qcom_icc_provider * qp ;
struct icc_node * node ;
if ( ! src )
node = dst ;
else
node = src ;
qp = to_qcom_provider ( node - > provider ) ;
qcom_icc_bcm_voter_commit ( qp - > voter ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( qcom_icc_set ) ;
/**
* qcom_icc_bcm_init - populates bcm aux data and connect qnodes
* @ bcm : bcm to be initialized
* @ dev : associated provider device
*
* Return : 0 on success , or an error code otherwise
*/
int qcom_icc_bcm_init ( struct qcom_icc_bcm * bcm , struct device * dev )
{
struct qcom_icc_node * qn ;
const struct bcm_db * data ;
size_t data_count ;
int i ;
/* BCM is already initialised*/
if ( bcm - > addr )
return 0 ;
bcm - > addr = cmd_db_read_addr ( bcm - > name ) ;
if ( ! bcm - > addr ) {
dev_err ( dev , " %s could not find RPMh address \n " ,
bcm - > name ) ;
return - EINVAL ;
}
data = cmd_db_read_aux_data ( bcm - > name , & data_count ) ;
if ( IS_ERR ( data ) ) {
dev_err ( dev , " %s command db read error (%ld) \n " ,
bcm - > name , PTR_ERR ( data ) ) ;
return PTR_ERR ( data ) ;
}
if ( ! data_count ) {
dev_err ( dev , " %s command db missing or partial aux data \n " ,
bcm - > name ) ;
return - EINVAL ;
}
bcm - > aux_data . unit = le32_to_cpu ( data - > unit ) ;
bcm - > aux_data . width = le16_to_cpu ( data - > width ) ;
bcm - > aux_data . vcd = data - > vcd ;
bcm - > aux_data . reserved = data - > reserved ;
INIT_LIST_HEAD ( & bcm - > list ) ;
INIT_LIST_HEAD ( & bcm - > ws_list ) ;
2020-09-03 12:21:49 -07:00
if ( ! bcm - > vote_scale )
bcm - > vote_scale = 1000 ;
2020-02-28 12:11:40 +02:00
/* Link Qnodes to their respective BCMs */
for ( i = 0 ; i < bcm - > num_nodes ; i + + ) {
qn = bcm - > nodes [ i ] ;
qn - > bcms [ qn - > num_bcms ] = bcm ;
qn - > num_bcms + + ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( qcom_icc_bcm_init ) ;
2021-06-21 14:42:41 -07:00
int qcom_icc_rpmh_probe ( struct platform_device * pdev )
{
const struct qcom_icc_desc * desc ;
struct device * dev = & pdev - > dev ;
struct icc_onecell_data * data ;
struct icc_provider * provider ;
2022-04-12 12:26:22 +02:00
struct qcom_icc_node * const * qnodes , * qn ;
2021-06-21 14:42:41 -07:00
struct qcom_icc_provider * qp ;
struct icc_node * node ;
size_t num_nodes , i , j ;
int ret ;
desc = of_device_get_match_data ( dev ) ;
if ( ! desc )
return - EINVAL ;
qnodes = desc - > nodes ;
num_nodes = desc - > num_nodes ;
qp = devm_kzalloc ( dev , sizeof ( * qp ) , GFP_KERNEL ) ;
if ( ! qp )
return - ENOMEM ;
data = devm_kzalloc ( dev , struct_size ( data , nodes , num_nodes ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
2023-08-17 13:42:15 -07:00
data - > num_nodes = num_nodes ;
2021-06-21 14:42:41 -07:00
provider = & qp - > provider ;
provider - > dev = dev ;
provider - > set = qcom_icc_set ;
provider - > pre_aggregate = qcom_icc_pre_aggregate ;
provider - > aggregate = qcom_icc_aggregate ;
provider - > xlate_extended = qcom_icc_xlate_extended ;
provider - > data = data ;
2023-03-06 08:56:38 +01:00
icc_provider_init ( provider ) ;
2021-06-21 14:42:41 -07:00
qp - > dev = dev ;
qp - > bcms = desc - > bcms ;
qp - > num_bcms = desc - > num_bcms ;
qp - > voter = of_bcm_voter_get ( qp - > dev , NULL ) ;
if ( IS_ERR ( qp - > voter ) )
return PTR_ERR ( qp - > voter ) ;
for ( i = 0 ; i < qp - > num_bcms ; i + + )
qcom_icc_bcm_init ( qp - > bcms [ i ] , dev ) ;
for ( i = 0 ; i < num_nodes ; i + + ) {
qn = qnodes [ i ] ;
if ( ! qn )
continue ;
node = icc_node_create ( qn - > id ) ;
if ( IS_ERR ( node ) ) {
ret = PTR_ERR ( node ) ;
2023-03-06 08:56:38 +01:00
goto err_remove_nodes ;
2021-06-21 14:42:41 -07:00
}
node - > name = qn - > name ;
node - > data = qn ;
icc_node_add ( node , provider ) ;
for ( j = 0 ; j < qn - > num_links ; j + + )
icc_link_create ( node , qn - > links [ j ] ) ;
data - > nodes [ i ] = node ;
}
2023-03-06 08:56:38 +01:00
ret = icc_provider_register ( provider ) ;
if ( ret )
goto err_remove_nodes ;
2021-06-21 14:42:41 -07:00
platform_set_drvdata ( pdev , qp ) ;
2022-05-25 16:43:57 +02:00
/* Populate child NoC devices if any */
2023-03-06 08:56:37 +01:00
if ( of_get_child_count ( dev - > of_node ) > 0 ) {
ret = of_platform_populate ( dev - > of_node , NULL , NULL , dev ) ;
if ( ret )
2023-03-06 08:56:38 +01:00
goto err_deregister_provider ;
2023-03-06 08:56:37 +01:00
}
2022-05-25 16:43:57 +02:00
2021-06-21 14:42:41 -07:00
return 0 ;
2023-03-06 08:56:38 +01:00
err_deregister_provider :
icc_provider_deregister ( provider ) ;
err_remove_nodes :
2021-06-21 14:42:41 -07:00
icc_nodes_remove ( provider ) ;
2023-03-06 08:56:38 +01:00
2021-06-21 14:42:41 -07:00
return ret ;
}
EXPORT_SYMBOL_GPL ( qcom_icc_rpmh_probe ) ;
2023-10-15 15:59:56 +02:00
void qcom_icc_rpmh_remove ( struct platform_device * pdev )
2021-06-21 14:42:41 -07:00
{
struct qcom_icc_provider * qp = platform_get_drvdata ( pdev ) ;
2023-03-06 08:56:38 +01:00
icc_provider_deregister ( & qp - > provider ) ;
2021-06-21 14:42:41 -07:00
icc_nodes_remove ( & qp - > provider ) ;
}
EXPORT_SYMBOL_GPL ( qcom_icc_rpmh_remove ) ;
2020-02-28 12:11:40 +02:00
MODULE_LICENSE ( " GPL v2 " ) ;