clk: qcom: Add support for voting to multiple clk_vdd_class

Some clocks have requirement to vote on multiple clk_vdd_class for clock
operations. So add the support to pass multiple vdd_class within the clock
structure to support this functionality. A new clk_vdd_classes list is
added in clk_vdd_class_data structure to pass clk_vdd_class as an array
to support voting on multiple rail.

Change-Id: I11732a39ef99fdbcb7874e1042776e7007bed35f
Signed-off-by: Naveen Yadav <naveenky@codeaurora.org>
Signed-off-by: Mike Tipton <mdtipton@codeaurora.org>
This commit is contained in:
Naveen Yadav 2020-07-21 15:48:06 +05:30 committed by Mike Tipton
parent 9bb68c3604
commit 9c9f3ff339
3 changed files with 93 additions and 42 deletions

View File

@ -108,7 +108,7 @@ int clk_pre_change_regmap(struct clk_hw *hw, unsigned long cur_rate,
int new_vdd_level;
int ret = 0;
if (!rclk->vdd_data.vdd_class)
if (!rclk->vdd_data.rate_max)
return 0;
new_vdd_level = clk_find_vdd_level(hw, &rclk->vdd_data, new_rate);
@ -153,7 +153,7 @@ int clk_post_change_regmap(struct clk_hw *hw, unsigned long old_rate,
int cur_vdd_level;
int ret = 0;
if (!rclk->vdd_data.vdd_class)
if (!rclk->vdd_data.rate_max)
return 0;
cur_vdd_level = clk_find_vdd_level(hw, &rclk->vdd_data, cur_rate);
@ -196,7 +196,7 @@ int clk_prepare_regmap(struct clk_hw *hw)
int rate = clk_hw_get_rate(hw);
int vdd_level;
if (!rclk->vdd_data.vdd_class)
if (!rclk->vdd_data.rate_max)
return 0;
vdd_level = clk_find_vdd_level(hw, &rclk->vdd_data, rate);
@ -226,7 +226,7 @@ void clk_unprepare_regmap(struct clk_hw *hw)
{
struct clk_regmap *rclk = to_clk_regmap(hw);
if (!rclk->vdd_data.vdd_class)
if (!rclk->vdd_data.rate_max)
return;
clk_unvote_vdd_level(&rclk->vdd_data, rclk->vdd_data.vdd_level);

View File

@ -68,22 +68,9 @@ static int clk_aggregate_vdd(struct clk_vdd_class *vdd_class)
return ret;
}
/**
* clk_vote_vdd_level - Add a vote for a given voltage level
* @vdd_data: vdd_class data for the clock
* @level: voltage level to add a vote for
*
* Add a regulator framework voltage request for the vdd_class regulator(s)
* by decrementing the reference count for a given voltage level. This ensures
* that the supply regulator is allowed to potentially lower its voltage after
* re-aggregating overall voltage requests.
*
* Returns 0 on success, -EERROR otherwise.
*/
int clk_vote_vdd_level(struct clk_vdd_class_data *vdd_data, int level)
static int clk_vote_vdd_class_level(struct clk_vdd_class *vdd_class, int level)
{
struct clk_vdd_class *vdd_class = vdd_data->vdd_class;
int ret = 0;
int ret;
if (level >= vdd_class->num_levels)
return -EINVAL;
@ -99,6 +86,69 @@ int clk_vote_vdd_level(struct clk_vdd_class_data *vdd_data, int level)
return ret;
}
static int clk_unvote_vdd_class_level(struct clk_vdd_class *vdd_class, int level)
{
int ret;
if (level >= vdd_class->num_levels)
return -EINVAL;
if (WARN(!vdd_class->level_votes[level],
"Reference counts are incorrect for %s level %d\n",
vdd_class->class_name, level)) {
return -EINVAL;
}
mutex_lock(&vdd_lock);
vdd_class->level_votes[level]--;
ret = clk_aggregate_vdd(vdd_class);
if (ret)
vdd_class->level_votes[level]++;
mutex_unlock(&vdd_lock);
return ret;
}
/**
* clk_vote_vdd_level - Add a vote for a given voltage level
* @vdd_data: vdd_class data for the clock
* @level: voltage level to add a vote for
*
* Add a regulator framework voltage request for the vdd_class regulator(s)
* by decrementing the reference count for a given voltage level. This ensures
* that the supply regulator is allowed to potentially lower its voltage after
* re-aggregating overall voltage requests.
*
* Returns 0 on success, -EERROR otherwise.
*/
int clk_vote_vdd_level(struct clk_vdd_class_data *vdd_data, int level)
{
int ret, i;
for (i = 0; i < vdd_data->num_vdd_classes; i++) {
ret = clk_vote_vdd_class_level(vdd_data->vdd_classes[i], level);
if (ret)
goto vote_fail;
}
if (vdd_data->vdd_class) {
ret = clk_vote_vdd_class_level(vdd_data->vdd_class, level);
if (ret)
goto vote_fail;
}
return 0;
vote_fail:
for (i--; i >= 0; i--)
clk_unvote_vdd_class_level(vdd_data->vdd_classes[i], level);
return ret;
}
EXPORT_SYMBOL(clk_vote_vdd_level);
/**
@ -114,26 +164,25 @@ EXPORT_SYMBOL(clk_vote_vdd_level);
*/
int clk_unvote_vdd_level(struct clk_vdd_class_data *vdd_data, int level)
{
struct clk_vdd_class *vdd_class = vdd_data->vdd_class;
int ret = 0;
int ret, i;
if (level >= vdd_class->num_levels)
return -EINVAL;
if (WARN(!vdd_class->level_votes[level],
"Reference counts are incorrect for %s level %d\n",
vdd_class->class_name, level)) {
return -EINVAL;
for (i = 0; i < vdd_data->num_vdd_classes; i++) {
ret = clk_unvote_vdd_class_level(vdd_data->vdd_classes[i], level);
if (ret)
goto unvote_fail;
}
mutex_lock(&vdd_lock);
vdd_class->level_votes[level]--;
if (vdd_data->vdd_class) {
ret = clk_unvote_vdd_class_level(vdd_data->vdd_class, level);
if (ret)
goto unvote_fail;
}
ret = clk_aggregate_vdd(vdd_class);
if (ret)
vdd_class->level_votes[level]++;
return 0;
mutex_unlock(&vdd_lock);
unvote_fail:
for (i--; i >= 0; i--)
clk_vote_vdd_class_level(vdd_data->vdd_classes[i], level);
return ret;
}
@ -213,16 +262,15 @@ int clk_regulator_init(struct device *dev, const struct qcom_cc_desc *desc)
int clk_vdd_proxy_vote(struct device *dev, const struct qcom_cc_desc *desc)
{
struct clk_vdd_class_data vdd_data;
struct clk_vdd_class *vdd_class;
u32 i;
int ret = 0;
for (i = 0; i < desc->num_clk_regulators; i++) {
vdd_data.vdd_class = vdd_class = desc->clk_regulators[i];
vdd_class = desc->clk_regulators[i];
ret = clk_vote_vdd_level(&vdd_data,
vdd_class->num_levels - 1);
ret = clk_vote_vdd_class_level(vdd_class,
vdd_class->num_levels - 1);
if (ret)
WARN(ret, "%s failed, ret=%d\n", __func__, ret);
}
@ -232,16 +280,15 @@ int clk_vdd_proxy_vote(struct device *dev, const struct qcom_cc_desc *desc)
int clk_vdd_proxy_unvote(struct device *dev, const struct qcom_cc_desc *desc)
{
struct clk_vdd_class_data vdd_data;
struct clk_vdd_class *vdd_class;
u32 i;
int ret = 0;
for (i = 0; i < desc->num_clk_regulators; i++) {
vdd_data.vdd_class = vdd_class = desc->clk_regulators[i];
vdd_class = desc->clk_regulators[i];
ret = clk_unvote_vdd_level(&vdd_data,
vdd_class->num_levels - 1);
ret = clk_unvote_vdd_class_level(vdd_class,
vdd_class->num_levels - 1);
if (ret)
WARN(ret, "clk_unvote_vdd_level failed ret=%d\n", ret);
}

View File

@ -35,16 +35,20 @@ struct clk_vdd_class {
* struct clk_vdd_class_data - per-clock vdd_class rate data
*
* @vdd_class: voltage scaling requirement class
* @vdd_classes: array of voltage scaling requirement class
* @rate_max: array of maximum clock rate in Hz supported at each
* voltage level, indexed by voltage level
* @num_rate_max: size of rate_max array
* @vdd_rate: cached rate of current vdd vote
* @num_vdd_classes: size of vdd_classes array
*/
struct clk_vdd_class_data {
struct clk_vdd_class *vdd_class;
struct clk_vdd_class **vdd_classes;
unsigned long *rate_max;
int num_rate_max;
int vdd_level;
int num_vdd_classes;
};
#define DEFINE_VDD_REGULATORS(_name, _num_levels, _num_regulators, _vdd_uv) \