Commit 6594988f authored by Martin Blumenstingl's avatar Martin Blumenstingl Committed by Stephen Boyd
Browse files

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: default avatarAlex Bee <knaerzche@gmail.com>
Signed-off-by: default avatarMartin Blumenstingl <martin.blumenstingl@googlemail.com>
Link: https://lore.kernel.org/r/20211016105022.303413-3-martin.blumenstingl@googlemail.com


Tested-by: default avatarAlex Bee <knaerzche@gmail.com>
Tested-by: default avatarChen-Yu Tsai <wens@csie.org>
Signed-off-by: default avatarStephen Boyd <sboyd@kernel.org>
parent 675c496d
Loading
Loading
Loading
Loading
+48 −20
Original line number Diff line number Diff line
@@ -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)