summaryrefslogtreecommitdiff
path: root/drivers/clk
diff options
context:
space:
mode:
authorMartin Blumenstingl <martin.blumenstingl@googlemail.com>2021-10-16 13:50:22 +0300
committerStephen Boyd <sboyd@kernel.org>2021-10-18 22:59:46 +0300
commit6594988fd625ff0d9a8f90f1788e16185358a3e6 (patch)
tree355a00f5ba4adb581c6deb28618035237463b45b /drivers/clk
parent675c496d0f92b481ebe4abf4fb06eadad7789de6 (diff)
downloadlinux-6594988fd625ff0d9a8f90f1788e16185358a3e6.tar.xz
clk: composite: Use rate_ops.determine_rate when also a mux is available
Update clk_composite_determine_rate() to use rate_ops.determine_rate when available in combination with a mux. So far clk_divider_ops provide both, .round_rate and .determine_rate. Removing the former would make clk-composite fail silently for example on Rockchip platforms (which heavily use composite clocks). Add support for using rate_ops.determine_rate when either rate_ops.round_rate is not available or both (.round_rate and .determine_rate) are provided. Suggested-by: Alex Bee <knaerzche@gmail.com> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Link: https://lore.kernel.org/r/20211016105022.303413-3-martin.blumenstingl@googlemail.com Tested-by: Alex Bee <knaerzche@gmail.com> Tested-by: Chen-Yu Tsai <wens@csie.org> Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/clk-composite.c68
1 files changed, 48 insertions, 20 deletions
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index 510a9965633b..075c7f2a7ec4 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -42,6 +42,29 @@ static unsigned long clk_composite_recalc_rate(struct clk_hw *hw,
return rate_ops->recalc_rate(rate_hw, parent_rate);
}
+static int clk_composite_determine_rate_for_parent(struct clk_hw *rate_hw,
+ struct clk_rate_request *req,
+ struct clk_hw *parent_hw,
+ const struct clk_ops *rate_ops)
+{
+ long rate;
+
+ req->best_parent_hw = parent_hw;
+ req->best_parent_rate = clk_hw_get_rate(parent_hw);
+
+ if (rate_ops->determine_rate)
+ return rate_ops->determine_rate(rate_hw, req);
+
+ rate = rate_ops->round_rate(rate_hw, req->rate,
+ &req->best_parent_rate);
+ if (rate < 0)
+ return rate;
+
+ req->rate = rate;
+
+ return 0;
+}
+
static int clk_composite_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
@@ -51,51 +74,56 @@ static int clk_composite_determine_rate(struct clk_hw *hw,
struct clk_hw *rate_hw = composite->rate_hw;
struct clk_hw *mux_hw = composite->mux_hw;
struct clk_hw *parent;
- unsigned long parent_rate;
- long tmp_rate, best_rate = 0;
unsigned long rate_diff;
unsigned long best_rate_diff = ULONG_MAX;
- long rate;
- int i;
+ unsigned long best_rate = 0;
+ int i, ret;
- if (rate_hw && rate_ops && rate_ops->round_rate &&
+ if (rate_hw && rate_ops &&
+ (rate_ops->determine_rate || rate_ops->round_rate) &&
mux_hw && mux_ops && mux_ops->set_parent) {
req->best_parent_hw = NULL;
if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT) {
+ struct clk_rate_request tmp_req = *req;
+
parent = clk_hw_get_parent(mux_hw);
- req->best_parent_hw = parent;
- req->best_parent_rate = clk_hw_get_rate(parent);
- rate = rate_ops->round_rate(rate_hw, req->rate,
- &req->best_parent_rate);
- if (rate < 0)
- return rate;
+ ret = clk_composite_determine_rate_for_parent(rate_hw,
+ &tmp_req,
+ parent,
+ rate_ops);
+ if (ret)
+ return ret;
+
+ req->rate = tmp_req.rate;
+ req->best_parent_rate = tmp_req.best_parent_rate;
- req->rate = rate;
return 0;
}
for (i = 0; i < clk_hw_get_num_parents(mux_hw); i++) {
+ struct clk_rate_request tmp_req = *req;
+
parent = clk_hw_get_parent_by_index(mux_hw, i);
if (!parent)
continue;
- parent_rate = clk_hw_get_rate(parent);
-
- tmp_rate = rate_ops->round_rate(rate_hw, req->rate,
- &parent_rate);
- if (tmp_rate < 0)
+ ret = clk_composite_determine_rate_for_parent(rate_hw,
+ &tmp_req,
+ parent,
+ rate_ops);
+ if (ret)
continue;
- rate_diff = abs(req->rate - tmp_rate);
+ rate_diff = abs(req->rate - tmp_req.rate);
if (!rate_diff || !req->best_parent_hw
|| best_rate_diff > rate_diff) {
req->best_parent_hw = parent;
- req->best_parent_rate = parent_rate;
+ req->best_parent_rate = tmp_req.best_parent_rate;
best_rate_diff = rate_diff;
- best_rate = tmp_rate;
+ best_rate = tmp_req.rate;
}
if (!rate_diff)