clk: composite: Use rate_ops.determine_rate when also a mux is available
authorMartin Blumenstingl <martin.blumenstingl@googlemail.com>
Sat, 16 Oct 2021 10:50:22 +0000 (12:50 +0200)
committerStephen Boyd <sboyd@kernel.org>
Mon, 18 Oct 2021 19:59:46 +0000 (12:59 -0700)
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>
drivers/clk/clk-composite.c

index 510a996..075c7f2 100644 (file)
@@ -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)