2020-03-03 19:02:52 +02:00
// SPDX-License-Identifier: GPL-2.0
/*
2021-10-21 16:10:56 +05:30
* Copyright ( c ) 2020 - 2021 , The Linux Foundation . All rights reserved .
2020-03-03 19:02:52 +02:00
*/
# include <linux/bitfield.h>
# include <linux/clk.h>
# include <linux/interconnect-provider.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <dt-bindings/interconnect/qcom,osm-l3.h>
2020-03-03 19:02:52 +02:00
# include "sc7180.h"
2021-10-21 16:10:56 +05:30
# include "sc7280.h"
2021-07-24 19:58:34 -07:00
# include "sc8180x.h"
2020-03-03 19:02:52 +02:00
# include "sdm845.h"
2020-08-01 18:00:44 +05:30
# include "sm8150.h"
2020-09-08 16:29:01 +03:00
# include "sm8250.h"
2020-03-03 19:02:52 +02:00
# define LUT_MAX_ENTRIES 40U
# define LUT_SRC GENMASK(31, 30)
# define LUT_L_VAL GENMASK(7, 0)
# define CLK_HW_DIV 2
2020-09-08 16:29:01 +03:00
/* OSM Register offsets */
2020-03-03 19:02:52 +02:00
# define REG_ENABLE 0x0
2020-09-08 16:29:01 +03:00
# define OSM_LUT_ROW_SIZE 32
# define OSM_REG_FREQ_LUT 0x110
# define OSM_REG_PERF_STATE 0x920
2020-03-03 19:02:52 +02:00
2020-09-08 16:29:01 +03:00
/* EPSS Register offsets */
# define EPSS_LUT_ROW_SIZE 4
# define EPSS_REG_FREQ_LUT 0x100
# define EPSS_REG_PERF_STATE 0x320
2020-03-03 19:02:52 +02:00
# define OSM_L3_MAX_LINKS 1
2021-07-24 20:14:14 -07:00
# define to_osm_l3_provider(_provider) \
2020-03-03 19:02:52 +02:00
container_of ( _provider , struct qcom_osm_l3_icc_provider , provider )
struct qcom_osm_l3_icc_provider {
void __iomem * base ;
unsigned int max_state ;
2020-09-08 16:29:01 +03:00
unsigned int reg_perf_state ;
2020-03-03 19:02:52 +02:00
unsigned long lut_tables [ LUT_MAX_ENTRIES ] ;
struct icc_provider provider ;
} ;
/**
2021-07-24 20:14:14 -07:00
* struct qcom_osm_l3_node - Qualcomm specific interconnect nodes
2020-03-03 19:02:52 +02:00
* @ name : the node name used in debugfs
* @ links : an array of nodes where we can go next while traversing
* @ id : a unique node identifier
* @ num_links : the total number of @ links
* @ buswidth : width of the interconnect between a node and the bus
*/
2021-07-24 20:14:14 -07:00
struct qcom_osm_l3_node {
2020-03-03 19:02:52 +02:00
const char * name ;
u16 links [ OSM_L3_MAX_LINKS ] ;
u16 id ;
u16 num_links ;
u16 buswidth ;
} ;
2021-07-24 20:14:14 -07:00
struct qcom_osm_l3_desc {
2022-04-12 12:26:22 +02:00
const struct qcom_osm_l3_node * const * nodes ;
2020-03-03 19:02:52 +02:00
size_t num_nodes ;
2020-09-08 16:29:01 +03:00
unsigned int lut_row_size ;
unsigned int reg_freq_lut ;
unsigned int reg_perf_state ;
2020-03-03 19:02:52 +02:00
} ;
# define DEFINE_QNODE(_name, _id, _buswidth, ...) \
2021-07-24 20:14:14 -07:00
static const struct qcom_osm_l3_node _name = { \
2020-03-03 19:02:52 +02:00
. name = # _name , \
. id = _id , \
. buswidth = _buswidth , \
. num_links = ARRAY_SIZE ( ( ( int [ ] ) { __VA_ARGS__ } ) ) , \
. links = { __VA_ARGS__ } , \
}
DEFINE_QNODE ( sdm845_osm_apps_l3 , SDM845_MASTER_OSM_L3_APPS , 16 , SDM845_SLAVE_OSM_L3 ) ;
DEFINE_QNODE ( sdm845_osm_l3 , SDM845_SLAVE_OSM_L3 , 16 ) ;
2022-04-12 12:26:22 +02:00
static const struct qcom_osm_l3_node * const sdm845_osm_l3_nodes [ ] = {
2020-03-03 19:02:52 +02:00
[ MASTER_OSM_L3_APPS ] = & sdm845_osm_apps_l3 ,
[ SLAVE_OSM_L3 ] = & sdm845_osm_l3 ,
} ;
2021-07-24 20:14:14 -07:00
static const struct qcom_osm_l3_desc sdm845_icc_osm_l3 = {
2020-03-03 19:02:52 +02:00
. nodes = sdm845_osm_l3_nodes ,
. num_nodes = ARRAY_SIZE ( sdm845_osm_l3_nodes ) ,
2020-09-08 16:29:01 +03:00
. lut_row_size = OSM_LUT_ROW_SIZE ,
. reg_freq_lut = OSM_REG_FREQ_LUT ,
. reg_perf_state = OSM_REG_PERF_STATE ,
2020-03-03 19:02:52 +02:00
} ;
2020-03-03 19:02:52 +02:00
DEFINE_QNODE ( sc7180_osm_apps_l3 , SC7180_MASTER_OSM_L3_APPS , 16 , SC7180_SLAVE_OSM_L3 ) ;
DEFINE_QNODE ( sc7180_osm_l3 , SC7180_SLAVE_OSM_L3 , 16 ) ;
2022-04-12 12:26:22 +02:00
static const struct qcom_osm_l3_node * const sc7180_osm_l3_nodes [ ] = {
2020-03-03 19:02:52 +02:00
[ MASTER_OSM_L3_APPS ] = & sc7180_osm_apps_l3 ,
[ SLAVE_OSM_L3 ] = & sc7180_osm_l3 ,
} ;
2021-07-24 20:14:14 -07:00
static const struct qcom_osm_l3_desc sc7180_icc_osm_l3 = {
2020-03-03 19:02:52 +02:00
. nodes = sc7180_osm_l3_nodes ,
. num_nodes = ARRAY_SIZE ( sc7180_osm_l3_nodes ) ,
2020-09-08 16:29:01 +03:00
. lut_row_size = OSM_LUT_ROW_SIZE ,
. reg_freq_lut = OSM_REG_FREQ_LUT ,
. reg_perf_state = OSM_REG_PERF_STATE ,
2020-03-03 19:02:52 +02:00
} ;
2021-10-21 16:10:56 +05:30
DEFINE_QNODE ( sc7280_epss_apps_l3 , SC7280_MASTER_EPSS_L3_APPS , 32 , SC7280_SLAVE_EPSS_L3 ) ;
DEFINE_QNODE ( sc7280_epss_l3 , SC7280_SLAVE_EPSS_L3 , 32 ) ;
2022-04-12 12:26:22 +02:00
static const struct qcom_osm_l3_node * const sc7280_epss_l3_nodes [ ] = {
2021-10-21 16:10:56 +05:30
[ MASTER_EPSS_L3_APPS ] = & sc7280_epss_apps_l3 ,
[ SLAVE_EPSS_L3_SHARED ] = & sc7280_epss_l3 ,
} ;
static const struct qcom_osm_l3_desc sc7280_icc_epss_l3 = {
. nodes = sc7280_epss_l3_nodes ,
. num_nodes = ARRAY_SIZE ( sc7280_epss_l3_nodes ) ,
. lut_row_size = EPSS_LUT_ROW_SIZE ,
. reg_freq_lut = EPSS_REG_FREQ_LUT ,
. reg_perf_state = EPSS_REG_PERF_STATE ,
} ;
2021-07-24 19:58:34 -07:00
DEFINE_QNODE ( sc8180x_osm_apps_l3 , SC8180X_MASTER_OSM_L3_APPS , 32 , SC8180X_SLAVE_OSM_L3 ) ;
DEFINE_QNODE ( sc8180x_osm_l3 , SC8180X_SLAVE_OSM_L3 , 32 ) ;
2022-04-12 12:26:22 +02:00
static const struct qcom_osm_l3_node * const sc8180x_osm_l3_nodes [ ] = {
2021-07-24 19:58:34 -07:00
[ MASTER_OSM_L3_APPS ] = & sc8180x_osm_apps_l3 ,
[ SLAVE_OSM_L3 ] = & sc8180x_osm_l3 ,
} ;
2021-07-24 20:14:14 -07:00
static const struct qcom_osm_l3_desc sc8180x_icc_osm_l3 = {
2021-07-24 19:58:34 -07:00
. nodes = sc8180x_osm_l3_nodes ,
. num_nodes = ARRAY_SIZE ( sc8180x_osm_l3_nodes ) ,
. lut_row_size = OSM_LUT_ROW_SIZE ,
. reg_freq_lut = OSM_REG_FREQ_LUT ,
. reg_perf_state = OSM_REG_PERF_STATE ,
} ;
2020-08-01 18:00:44 +05:30
DEFINE_QNODE ( sm8150_osm_apps_l3 , SM8150_MASTER_OSM_L3_APPS , 32 , SM8150_SLAVE_OSM_L3 ) ;
DEFINE_QNODE ( sm8150_osm_l3 , SM8150_SLAVE_OSM_L3 , 32 ) ;
2022-04-12 12:26:22 +02:00
static const struct qcom_osm_l3_node * const sm8150_osm_l3_nodes [ ] = {
2020-08-01 18:00:44 +05:30
[ MASTER_OSM_L3_APPS ] = & sm8150_osm_apps_l3 ,
[ SLAVE_OSM_L3 ] = & sm8150_osm_l3 ,
} ;
2021-07-24 20:14:14 -07:00
static const struct qcom_osm_l3_desc sm8150_icc_osm_l3 = {
2020-08-01 18:00:44 +05:30
. nodes = sm8150_osm_l3_nodes ,
. num_nodes = ARRAY_SIZE ( sm8150_osm_l3_nodes ) ,
2020-09-08 16:29:01 +03:00
. lut_row_size = OSM_LUT_ROW_SIZE ,
. reg_freq_lut = OSM_REG_FREQ_LUT ,
. reg_perf_state = OSM_REG_PERF_STATE ,
2020-08-01 18:00:44 +05:30
} ;
2020-09-08 16:29:01 +03:00
DEFINE_QNODE ( sm8250_epss_apps_l3 , SM8250_MASTER_EPSS_L3_APPS , 32 , SM8250_SLAVE_EPSS_L3 ) ;
DEFINE_QNODE ( sm8250_epss_l3 , SM8250_SLAVE_EPSS_L3 , 32 ) ;
2022-04-12 12:26:22 +02:00
static const struct qcom_osm_l3_node * const sm8250_epss_l3_nodes [ ] = {
2020-09-08 16:29:01 +03:00
[ MASTER_EPSS_L3_APPS ] = & sm8250_epss_apps_l3 ,
[ SLAVE_EPSS_L3_SHARED ] = & sm8250_epss_l3 ,
} ;
2021-07-24 20:14:14 -07:00
static const struct qcom_osm_l3_desc sm8250_icc_epss_l3 = {
2020-09-08 16:29:01 +03:00
. nodes = sm8250_epss_l3_nodes ,
. num_nodes = ARRAY_SIZE ( sm8250_epss_l3_nodes ) ,
. lut_row_size = EPSS_LUT_ROW_SIZE ,
. reg_freq_lut = EPSS_REG_FREQ_LUT ,
. reg_perf_state = EPSS_REG_PERF_STATE ,
} ;
2021-07-24 20:14:14 -07:00
static int qcom_osm_l3_set ( struct icc_node * src , struct icc_node * dst )
2020-03-03 19:02:52 +02:00
{
struct qcom_osm_l3_icc_provider * qp ;
struct icc_provider * provider ;
2021-07-24 20:14:14 -07:00
const struct qcom_osm_l3_node * qn ;
2020-03-03 19:02:52 +02:00
struct icc_node * n ;
unsigned int index ;
u32 agg_peak = 0 ;
u32 agg_avg = 0 ;
u64 rate ;
qn = src - > data ;
provider = src - > provider ;
2021-07-24 20:14:14 -07:00
qp = to_osm_l3_provider ( provider ) ;
2020-03-03 19:02:52 +02:00
list_for_each_entry ( n , & provider - > nodes , node_list )
provider - > aggregate ( n , 0 , n - > avg_bw , n - > peak_bw ,
& agg_avg , & agg_peak ) ;
rate = max ( agg_avg , agg_peak ) ;
rate = icc_units_to_bps ( rate ) ;
do_div ( rate , qn - > buswidth ) ;
for ( index = 0 ; index < qp - > max_state - 1 ; index + + ) {
if ( qp - > lut_tables [ index ] > = rate )
break ;
}
2020-09-08 16:29:01 +03:00
writel_relaxed ( index , qp - > base + qp - > reg_perf_state ) ;
2020-03-03 19:02:52 +02:00
return 0 ;
}
static int qcom_osm_l3_remove ( struct platform_device * pdev )
{
struct qcom_osm_l3_icc_provider * qp = platform_get_drvdata ( pdev ) ;
icc_nodes_remove ( & qp - > provider ) ;
return icc_provider_del ( & qp - > provider ) ;
}
static int qcom_osm_l3_probe ( struct platform_device * pdev )
{
u32 info , src , lval , i , prev_freq = 0 , freq ;
static unsigned long hw_rate , xo_rate ;
struct qcom_osm_l3_icc_provider * qp ;
2021-07-24 20:14:14 -07:00
const struct qcom_osm_l3_desc * desc ;
2020-03-03 19:02:52 +02:00
struct icc_onecell_data * data ;
struct icc_provider * provider ;
2022-04-12 12:26:22 +02:00
const struct qcom_osm_l3_node * const * qnodes ;
2020-03-03 19:02:52 +02:00
struct icc_node * node ;
size_t num_nodes ;
struct clk * clk ;
int ret ;
clk = clk_get ( & pdev - > dev , " xo " ) ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
xo_rate = clk_get_rate ( clk ) ;
clk_put ( clk ) ;
clk = clk_get ( & pdev - > dev , " alternate " ) ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
hw_rate = clk_get_rate ( clk ) / CLK_HW_DIV ;
clk_put ( clk ) ;
qp = devm_kzalloc ( & pdev - > dev , sizeof ( * qp ) , GFP_KERNEL ) ;
if ( ! qp )
return - ENOMEM ;
qp - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( qp - > base ) )
return PTR_ERR ( qp - > base ) ;
/* HW should be in enabled state to proceed */
if ( ! ( readl_relaxed ( qp - > base + REG_ENABLE ) & 0x1 ) ) {
dev_err ( & pdev - > dev , " error hardware not enabled \n " ) ;
return - ENODEV ;
}
2020-09-08 16:29:01 +03:00
desc = device_get_match_data ( & pdev - > dev ) ;
if ( ! desc )
return - EINVAL ;
qp - > reg_perf_state = desc - > reg_perf_state ;
2020-03-03 19:02:52 +02:00
for ( i = 0 ; i < LUT_MAX_ENTRIES ; i + + ) {
2020-09-08 16:29:01 +03:00
info = readl_relaxed ( qp - > base + desc - > reg_freq_lut +
i * desc - > lut_row_size ) ;
2020-03-03 19:02:52 +02:00
src = FIELD_GET ( LUT_SRC , info ) ;
lval = FIELD_GET ( LUT_L_VAL , info ) ;
if ( src )
freq = xo_rate * lval ;
else
freq = hw_rate ;
/* Two of the same frequencies signify end of table */
if ( i > 0 & & prev_freq = = freq )
break ;
dev_dbg ( & pdev - > dev , " index=%d freq=%d \n " , i , freq ) ;
qp - > lut_tables [ i ] = freq ;
prev_freq = freq ;
}
qp - > max_state = i ;
qnodes = desc - > nodes ;
num_nodes = desc - > num_nodes ;
data = devm_kcalloc ( & pdev - > dev , num_nodes , sizeof ( * node ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
provider = & qp - > provider ;
provider - > dev = & pdev - > dev ;
2021-07-24 20:14:14 -07:00
provider - > set = qcom_osm_l3_set ;
2020-03-03 19:02:52 +02:00
provider - > aggregate = icc_std_aggregate ;
provider - > xlate = of_icc_xlate_onecell ;
INIT_LIST_HEAD ( & provider - > nodes ) ;
provider - > data = data ;
ret = icc_provider_add ( provider ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " error adding interconnect provider \n " ) ;
return ret ;
}
for ( i = 0 ; i < num_nodes ; i + + ) {
size_t j ;
node = icc_node_create ( qnodes [ i ] - > id ) ;
if ( IS_ERR ( node ) ) {
ret = PTR_ERR ( node ) ;
goto err ;
}
node - > name = qnodes [ i ] - > name ;
2021-07-24 20:14:14 -07:00
/* Cast away const and add it back in qcom_osm_l3_set() */
2020-09-15 09:10:21 +03:00
node - > data = ( void * ) qnodes [ i ] ;
2020-03-03 19:02:52 +02:00
icc_node_add ( node , provider ) ;
for ( j = 0 ; j < qnodes [ i ] - > num_links ; j + + )
icc_link_create ( node , qnodes [ i ] - > links [ j ] ) ;
data - > nodes [ i ] = node ;
}
data - > num_nodes = num_nodes ;
platform_set_drvdata ( pdev , qp ) ;
return 0 ;
err :
icc_nodes_remove ( provider ) ;
icc_provider_del ( provider ) ;
return ret ;
}
static const struct of_device_id osm_l3_of_match [ ] = {
2020-03-03 19:02:52 +02:00
{ . compatible = " qcom,sc7180-osm-l3 " , . data = & sc7180_icc_osm_l3 } ,
2021-10-21 16:10:56 +05:30
{ . compatible = " qcom,sc7280-epss-l3 " , . data = & sc7280_icc_epss_l3 } ,
2020-03-03 19:02:52 +02:00
{ . compatible = " qcom,sdm845-osm-l3 " , . data = & sdm845_icc_osm_l3 } ,
2020-08-01 18:00:44 +05:30
{ . compatible = " qcom,sm8150-osm-l3 " , . data = & sm8150_icc_osm_l3 } ,
2021-07-24 19:58:34 -07:00
{ . compatible = " qcom,sc8180x-osm-l3 " , . data = & sc8180x_icc_osm_l3 } ,
2020-09-08 16:29:01 +03:00
{ . compatible = " qcom,sm8250-epss-l3 " , . data = & sm8250_icc_epss_l3 } ,
2020-03-03 19:02:52 +02:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , osm_l3_of_match ) ;
static struct platform_driver osm_l3_driver = {
. probe = qcom_osm_l3_probe ,
. remove = qcom_osm_l3_remove ,
. driver = {
. name = " osm-l3 " ,
. of_match_table = osm_l3_of_match ,
2020-08-25 20:01:52 +03:00
. sync_state = icc_sync_state ,
2020-03-03 19:02:52 +02:00
} ,
} ;
module_platform_driver ( osm_l3_driver ) ;
MODULE_DESCRIPTION ( " Qualcomm OSM L3 interconnect driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;