diff --git a/drivers/soc/rockchip/rockchip_opp_select.c b/drivers/soc/rockchip/rockchip_opp_select.c index 12cd86a1c81e..8a785e6b64c0 100644 --- a/drivers/soc/rockchip/rockchip_opp_select.c +++ b/drivers/soc/rockchip/rockchip_opp_select.c @@ -977,6 +977,58 @@ out_put: } EXPORT_SYMBOL(rockchip_pvtpll_calibrate_opp); +void rockchip_pvtpll_add_length(struct rockchip_opp_info *info) +{ + struct device_node *np; + struct opp_table *opp_table; + struct dev_pm_opp *opp; + unsigned long old_rate; + unsigned int min_rate = 0, max_rate = 0, margin = 0; + u32 opp_flag = 0; + int ret; + + if (!info || !info->grf) + return; + + np = of_parse_phandle(info->dev->of_node, "operating-points-v2", 0); + if (!np) { + dev_warn(info->dev, "OPP-v2 not supported\n"); + return; + } + + if (of_property_read_u32(np, "rockchip,pvtpll-len-min-rate", &min_rate)) + return; + if (of_property_read_u32(np, "rockchip,pvtpll-len-max-rate", &max_rate)) + return; + if (of_property_read_u32(np, "rockchip,pvtpll-len-margin", &margin)) + return; + + opp_table = dev_pm_opp_get_opp_table(info->dev); + if (!opp_table) + return; + old_rate = clk_get_rate(opp_table->clk); + opp_flag = OPP_ADD_LENGTH | ((margin & OPP_LENGTH_MASK) << OPP_LENGTH_SHIFT); + + mutex_lock(&opp_table->lock); + list_for_each_entry(opp, &opp_table->opp_list, node) { + if (opp->rate < min_rate * 1000 || opp->rate > max_rate * 1000) + continue; + ret = clk_set_rate(opp_table->clk, opp->rate | opp_flag); + if (ret) { + dev_err(info->dev, + "failed to change %lu len margin %d\n", + opp->rate, margin); + break; + } + } + mutex_unlock(&opp_table->lock); + + clk_set_rate(opp_table->clk, old_rate); + + dev_pm_opp_put_opp_table(opp_table); +} +EXPORT_SYMBOL(rockchip_pvtpll_add_length); + static int rockchip_get_pvtm_pvtpll(struct device *dev, struct device_node *np, char *reg_name) { @@ -1719,6 +1771,7 @@ next: } rockchip_adjust_power_scale(dev, scale); rockchip_pvtpll_calibrate_opp(info); + rockchip_pvtpll_add_length(info); dis_opp_clk: if (info && info->clks) diff --git a/include/soc/rockchip/rockchip_opp_select.h b/include/soc/rockchip/rockchip_opp_select.h index da9cab568295..2277fbeac360 100644 --- a/include/soc/rockchip/rockchip_opp_select.h +++ b/include/soc/rockchip/rockchip_opp_select.h @@ -8,11 +8,28 @@ #define VOLT_RM_TABLE_END ~1 -#define OPP_INTERMEDIATE_MASK 0x3f +/* + * [0]: set intermediate rate + * [1]: scaling up rate or scaling down rate + * [1]: add length for pvtpll + * [2:5]: length + * [2]: use low length for pvtpll + * [3:5]: reserved + */ +#define OPP_RATE_MASK 0x3f + +/* Set intermediate rate */ #define OPP_INTERMEDIATE_RATE BIT(0) #define OPP_SCALING_UP_RATE BIT(1) #define OPP_SCALING_UP_INTER (OPP_INTERMEDIATE_RATE | OPP_SCALING_UP_RATE) #define OPP_SCALING_DOWN_INTER OPP_INTERMEDIATE_RATE + +/* Add length for pvtpll */ +#define OPP_ADD_LENGTH BIT(1) +#define OPP_LENGTH_MASK 0xf +#define OPP_LENGTH_SHIFT 2 + +/* Use low length for pvtpll */ #define OPP_LENGTH_LOW BIT(2) struct rockchip_opp_info; @@ -69,6 +86,7 @@ void rockchip_of_get_lkg_sel(struct device *dev, struct device_node *np, char *lkg_name, int process, int *volt_sel, int *scale_sel); void rockchip_pvtpll_calibrate_opp(struct rockchip_opp_info *info); +void rockchip_pvtpll_add_length(struct rockchip_opp_info *info); void rockchip_of_get_pvtm_sel(struct device *dev, struct device_node *np, char *reg_name, int process, int *volt_sel, int *scale_sel); @@ -125,6 +143,10 @@ static inline void rockchip_pvtpll_calibrate_opp(struct rockchip_opp_info *info) { } +static inline void rockchip_pvtpll_add_length(struct rockchip_opp_info *info) +{ +} + static inline void rockchip_of_get_pvtm_sel(struct device *dev, struct device_node *np, char *reg_name, int process,