diff --git a/drivers/clk/qcom/clk-regmap.c b/drivers/clk/qcom/clk-regmap.c index d2ede9cb6e30..ba2ac7bae554 100644 --- a/drivers/clk/qcom/clk-regmap.c +++ b/drivers/clk/qcom/clk-regmap.c @@ -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); diff --git a/drivers/clk/qcom/vdd-class.c b/drivers/clk/qcom/vdd-class.c index c3ecf1c2b639..f2d1ff3d94bf 100644 --- a/drivers/clk/qcom/vdd-class.c +++ b/drivers/clk/qcom/vdd-class.c @@ -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); } diff --git a/drivers/clk/qcom/vdd-class.h b/drivers/clk/qcom/vdd-class.h index f50ebe0a0e9d..12cbfe6ed75f 100644 --- a/drivers/clk/qcom/vdd-class.h +++ b/drivers/clk/qcom/vdd-class.h @@ -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) \