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

Commit 24bb2aa2 authored by Deepak Katragadda's avatar Deepak Katragadda
Browse files

clk: msm: clock-local2: 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: I90b7a9a3007792f65e0292d375e409ce1dbf0c08
Signed-off-by: default avatarDeepak Katragadda <dkatraga@codeaurora.org>
parent f3c17935
Loading
Loading
Loading
Loading
+91 −5
Original line number Diff line number Diff line
/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -577,6 +577,20 @@ static void branch_clk_halt_check(struct clk *c, u32 halt_check,
	}
}

static unsigned long branch_clk_aggregate_rate(const struct clk *parent)
{
	struct clk *clk;
	unsigned long rate = 0;

	list_for_each_entry(clk, &parent->children, siblings) {
		struct branch_clk *v = to_branch_clk(clk);

		if (v->is_prepared)
			rate = max(clk->rate, rate);
	}
	return rate;
}

static int cbcr_set_flags(void * __iomem regaddr, unsigned flags)
{
	u32 cbcr_val;
@@ -625,6 +639,30 @@ static int branch_clk_set_flags(struct clk *c, unsigned flags)
	return cbcr_set_flags(CBCR_REG(to_branch_clk(c)), flags);
}

static DEFINE_MUTEX(branch_clk_lock);

static int branch_clk_prepare(struct clk *c)
{
	struct branch_clk *branch = to_branch_clk(c);
	unsigned long curr_rate;
	int ret = 0;

	mutex_lock(&branch_clk_lock);
	branch->is_prepared = false;
	if (branch->aggr_sibling_rates) {
		curr_rate = branch_clk_aggregate_rate(c->parent);
		if (c->rate > curr_rate) {
			ret = clk_set_rate(c->parent, c->rate);
			if (ret)
				goto exit;
		}
	}
	branch->is_prepared = true;
exit:
	mutex_unlock(&branch_clk_lock);
	return ret;
}

static int branch_clk_enable(struct clk *c)
{
	unsigned long flags;
@@ -656,6 +694,22 @@ static int branch_clk_enable(struct clk *c)
	return 0;
}

static void branch_clk_unprepare(struct clk *c)
{
	struct branch_clk *branch = to_branch_clk(c);
	unsigned long curr_rate, new_rate;

	mutex_lock(&branch_clk_lock);
	branch->is_prepared = false;
	if (branch->aggr_sibling_rates) {
		new_rate = branch_clk_aggregate_rate(c->parent);
		curr_rate = max(new_rate, c->rate);
		if (new_rate < curr_rate)
			clk_set_rate(c->parent, new_rate);
	}
	mutex_unlock(&branch_clk_lock);
}

static void branch_clk_disable(struct clk *c)
{
	unsigned long flags;
@@ -699,15 +753,45 @@ static int branch_cdiv_set_rate(struct branch_clk *branch, unsigned long rate)

static int branch_clk_set_rate(struct clk *c, unsigned long rate)
{
	struct branch_clk *branch = to_branch_clk(c);
	struct branch_clk *clkh, *branch = to_branch_clk(c);
	struct clk *clkp, *parent = c->parent;
	unsigned long curr_rate, new_rate, other_rate = 0;
	int ret = 0;

	if (branch->max_div)
		return branch_cdiv_set_rate(branch, rate);

	if (!branch->has_sibling)
		return clk_set_rate(c->parent, rate);

	if (branch->has_sibling)
		return -EPERM;

	mutex_lock(&branch_clk_lock);
	if (branch->aggr_sibling_rates) {
		if (!branch->is_prepared) {
			c->rate = rate;
			goto exit;
		}
		/*
		 * Get the aggregate rate without this clock's vote and update
		 * if the new rate is different than the current rate.
		 */
		list_for_each_entry(clkp, &parent->children, siblings) {
			clkh = to_branch_clk(clkp);
			if (clkh->is_prepared && clkh != branch)
				other_rate = max(clkp->rate, other_rate);
		}
		curr_rate = max(other_rate, c->rate);
		new_rate = max(other_rate, rate);
		if (new_rate != curr_rate) {
			ret = clk_set_rate(parent, new_rate);
			if (!ret)
				c->rate = rate;
		}
		goto exit;
	}
	ret = clk_set_rate(c->parent, rate);
exit:
	mutex_unlock(&branch_clk_lock);
	return ret;
}

static long branch_clk_round_rate(struct clk *c, unsigned long rate)
@@ -2019,7 +2103,9 @@ struct clk_ops clk_ops_rcg_edp = {

struct clk_ops clk_ops_branch = {
	.enable = branch_clk_enable,
	.prepare = branch_clk_prepare,
	.disable = branch_clk_disable,
	.unprepare = branch_clk_unprepare,
	.set_rate = branch_clk_set_rate,
	.get_rate = branch_clk_get_rate,
	.list_rate = branch_clk_list_rate,
+6 −1
Original line number Diff line number Diff line
/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -97,6 +97,9 @@ extern struct clk_freq_tbl rcg_dummy_freq;
 *			      clk_disable().
 * @check_enable_bit: Check the enable bit to determine clock status
				during handoff.
 * @aggr_sibling_rates: Set if there are multiple branch clocks with rate
			setting capability on the common RCG.
 * @is_prepared: Set if clock's prepare count is greater than 0.
 * @base: pointer to base address of ioremapped registers.
 */
struct branch_clk {
@@ -111,6 +114,8 @@ struct branch_clk {
	bool toggle_memory;
	bool no_halt_check_on_disable;
	bool check_enable_bit;
	bool aggr_sibling_rates;
	bool is_prepared;
	void *const __iomem *base;
};