Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9caf899c authored by Deepak Katragadda's avatar Deepak Katragadda
Browse files

clk: qcom: clk-branch: Add support for branch clocks rate aggregation



There are cases when multiple branch clocks need to be able to
scale their RCG independently. The RCG should then be configured
to run at the maximum rate of all it's children clocks which are
enabled at that point. Add support for this.

Change-Id: Iabed22d492c4445e7cc97261671c1ea4240bed71
Signed-off-by: default avatarDeepak Katragadda <dkatraga@codeaurora.org>
parent b7d205ea
Loading
Loading
Loading
Loading
+92 −0
Original line number Diff line number Diff line
@@ -184,6 +184,53 @@ const struct clk_ops clk_branch_ops = {
};
EXPORT_SYMBOL_GPL(clk_branch_ops);

static int clk_branch2_set_rate(struct clk_hw *hw, unsigned long rate,
			    unsigned long parent_rate)
{
	struct clk_branch *branch = to_clk_branch(hw);
	struct clk_hw *parent = clk_hw_get_parent(hw);
	unsigned long curr_rate, new_rate, other_rate = 0;
	int ret = 0;

	if (!parent)
		return -EPERM;

	if (!branch->aggr_sibling_rates || !clk_hw_is_prepared(hw)) {
		branch->rate = rate;
		return 0;
	}

	other_rate = clk_aggregate_rate(hw, parent->core);
	curr_rate = max(other_rate, branch->rate);
	new_rate = max(other_rate, rate);

	if (new_rate != curr_rate) {
		ret = clk_set_rate(parent->clk, new_rate);
		if (ret)
			goto err;
	}
	branch->rate = rate;
err:
	return ret;
}

static long clk_branch2_round_rate(struct clk_hw *hw, unsigned long rate,
		unsigned long *parent_rate)
{
	struct clk_hw *parent = clk_hw_get_parent(hw);

	if (!parent)
		return -EPERM;

	return clk_hw_round_rate(parent, rate);
}

static unsigned long clk_branch2_recalc_rate(struct clk_hw *hw,
		unsigned long parent_rate)
{
	return to_clk_branch(hw)->rate;
}

static void clk_branch2_list_registers(struct seq_file *f, struct clk_hw *hw)
{
	struct clk_branch *br = to_clk_branch(hw);
@@ -226,15 +273,60 @@ static int clk_branch2_enable(struct clk_hw *hw)
	return clk_branch_toggle(hw, true, clk_branch2_check_halt);
}

static int clk_branch2_prepare(struct clk_hw *hw)
{
	struct clk_branch *branch = to_clk_branch(hw);
	struct clk_hw *parent = clk_hw_get_parent(hw);
	unsigned long curr_rate, branch_rate = branch->rate;
	int ret = 0;

	/*
	 * Do the rate aggregation and scaling of the RCG in the prepare/
	 * unprepare functions to avoid potential RPM(/h) communication due to
	 * votes on the voltage rails.
	 */
	if (branch->aggr_sibling_rates) {
		curr_rate = clk_aggregate_rate(hw, parent->core);
		if (branch_rate > curr_rate) {
			ret = clk_set_rate(parent->clk, branch_rate);
			if (ret)
				goto exit;
		}
	}
exit:
	return ret;
}

static void clk_branch2_disable(struct clk_hw *hw)
{
	clk_branch_toggle(hw, false, clk_branch2_check_halt);
}

static void clk_branch2_unprepare(struct clk_hw *hw)
{
	struct clk_branch *branch = to_clk_branch(hw);
	struct clk_hw *parent = clk_hw_get_parent(hw);
	unsigned long curr_rate, new_rate, branch_rate = branch->rate;

	if (branch->aggr_sibling_rates) {
		new_rate = clk_aggregate_rate(hw, parent->core);
		curr_rate = max(new_rate, branch_rate);
		if (new_rate < curr_rate)
			if (clk_set_rate(parent->clk, new_rate))
				pr_err("Failed to scale %s to %lu\n",
					clk_hw_get_name(parent), new_rate);
	}
}

const struct clk_ops clk_branch2_ops = {
	.prepare = clk_branch2_prepare,
	.enable = clk_branch2_enable,
	.unprepare = clk_branch2_unprepare,
	.disable = clk_branch2_disable,
	.is_enabled = clk_is_enabled_regmap,
	.set_rate = clk_branch2_set_rate,
	.round_rate = clk_branch2_round_rate,
	.recalc_rate = clk_branch2_recalc_rate,
	.set_flags = clk_branch_set_flags,
	.list_registers = clk_branch2_list_registers,
};
+4 −0
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@
 * @halt_reg: halt register
 * @halt_bit: ANDed with @halt_reg to test for clock halted
 * @halt_check: type of halt checking to perform
 * @aggr_sibling_rates: set if the branch clock's parent needs to be scaled
 *			based on an aggregation of its siblings votes.
 * @clkr: handle between common and hardware-specific interfaces
 *
 * Clock which can gate its output.
@@ -36,6 +38,8 @@ struct clk_branch {
	u8	hwcg_bit;
	u8	halt_bit;
	u8	halt_check;
	bool	aggr_sibling_rates;
	unsigned long rate;
#define BRANCH_VOTED			BIT(7) /* Delay on disable */
#define BRANCH_HALT			0 /* pol: 1 = halt */
#define BRANCH_HALT_VOTED		(BRANCH_HALT | BRANCH_VOTED)