mirror of
https://github.com/team-infusion-developers/android_kernel_samsung_msm8976.git
synced 2024-11-01 02:21:16 +00:00
clk: qcom: Support multiple safe frequency for clocks
While switching to new frequencies check if the new rate is higher than safe_freq_next and accordingly modify the safe_freq/divider for switching. Also make sure the new safe parent is turned on before the divider is set and later once re-parenting to new frequency is completed turn off the safe parent. Change-Id: Ie0841a15256e3e2dd00b72546dffbc1d75976946 Signed-off-by: Taniya Das <tdas@codeaurora.org>
This commit is contained in:
parent
e753dc6232
commit
d181f8f4b8
2 changed files with 67 additions and 19 deletions
|
@ -725,7 +725,11 @@ static int safe_parent_init_once(struct clk *c)
|
|||
|
||||
if (IS_ERR(md->safe_parent))
|
||||
return -EINVAL;
|
||||
if (!md->safe_freq || md->safe_parent)
|
||||
|
||||
/*
|
||||
* Update safe_div and safe_parent if there is a valid new safe_freq
|
||||
*/
|
||||
if (!md->safe_freq)
|
||||
return 0;
|
||||
|
||||
rrate = __mux_div_round_rate(c, md->safe_freq, &best_parent,
|
||||
|
@ -745,10 +749,26 @@ static int mux_div_clk_set_rate(struct clk *c, unsigned long rate)
|
|||
{
|
||||
struct mux_div_clk *md = to_mux_div_clk(c);
|
||||
unsigned long flags, rrate;
|
||||
unsigned long new_prate, old_prate;
|
||||
unsigned long new_prate, old_prate, old_safe_freq = 0;
|
||||
unsigned long max_safe_freq = 0;
|
||||
struct clk *old_parent, *new_parent;
|
||||
u32 new_div, old_div;
|
||||
int rc;
|
||||
int rc, i;
|
||||
|
||||
if (md->safe_num) {
|
||||
max_safe_freq = max(c->rate, rate);
|
||||
|
||||
for (i = (md->safe_num - 1); i >= 0; i--) {
|
||||
if ((md->safe_freqs[i]) &&
|
||||
(md->safe_freqs[i] < max_safe_freq))
|
||||
break;
|
||||
}
|
||||
/* Move to the new safe frequency */
|
||||
if (i >= 0) {
|
||||
old_safe_freq = md->safe_freq;
|
||||
md->safe_freq = md->safe_freqs[i];
|
||||
}
|
||||
}
|
||||
|
||||
rc = safe_parent_init_once(c);
|
||||
if (rc)
|
||||
|
@ -764,10 +784,17 @@ static int mux_div_clk_set_rate(struct clk *c, unsigned long rate)
|
|||
old_prate = clk_get_rate(c->parent);
|
||||
|
||||
/* Refer to the description of safe_freq in clock-generic.h */
|
||||
if (md->safe_freq)
|
||||
if (md->safe_freq) {
|
||||
/* enable the aux clock safe parent before setting div */
|
||||
rc = clk_prepare_enable(md->safe_parent);
|
||||
if (rc) {
|
||||
pr_err("failed to prepare enable %s\n",
|
||||
clk_name(md->safe_parent));
|
||||
return rc;
|
||||
}
|
||||
rc = set_src_div(md, md->safe_parent, md->safe_div);
|
||||
|
||||
else if (new_parent == old_parent && new_div >= old_div) {
|
||||
} else if (new_parent == old_parent && new_div >= old_div) {
|
||||
/*
|
||||
* If both the parent_rate and divider changes, there may be an
|
||||
* intermediate frequency generated. Ensure this intermediate
|
||||
|
@ -797,6 +824,15 @@ static int mux_div_clk_set_rate(struct clk *c, unsigned long rate)
|
|||
c->parent = new_parent;
|
||||
|
||||
__clk_post_reparent(c, old_parent, &flags);
|
||||
|
||||
if (md->safe_freq)
|
||||
/* disable aux clock safe parent */
|
||||
clk_disable_unprepare(md->safe_parent);
|
||||
|
||||
/* Valid old_safe_freq, move back to the safe_freq value */
|
||||
if (old_safe_freq)
|
||||
md->safe_freq = old_safe_freq;
|
||||
|
||||
return 0;
|
||||
|
||||
err_set_src_div:
|
||||
|
@ -810,6 +846,10 @@ err_set_rate:
|
|||
rc = set_src_div(md, old_parent, old_div);
|
||||
WARN(rc, "%s: error changing back to original div (%d) and parent (%s)\n",
|
||||
clk_name(c), old_div, clk_name(old_parent));
|
||||
if (!rc && md->safe_freq)
|
||||
clk_disable_unprepare(md->safe_parent);
|
||||
if (old_safe_freq)
|
||||
md->safe_freq = old_safe_freq;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -236,24 +236,30 @@ struct mux_div_ops {
|
|||
/*
|
||||
* struct mux_div_clk - combined mux/divider clock
|
||||
* @priv
|
||||
parameters needed by ops
|
||||
* parameters needed by ops
|
||||
* @safe_freq
|
||||
when switching rates from A to B, the mux div clock will
|
||||
instead switch from A -> safe_freq -> B. This allows the
|
||||
mux_div clock to change rates while enabled, even if this
|
||||
behavior is not supported by the parent clocks.
|
||||
|
||||
If changing the rate of parent A also causes the rate of
|
||||
parent B to change, then safe_freq must be defined.
|
||||
|
||||
safe_freq is expected to have a source clock which is always
|
||||
on and runs at only one rate.
|
||||
* when switching rates from A to B, the mux div clock will
|
||||
* instead switch from A -> safe_freq -> B. This allows the
|
||||
* mux_div clock to change rates while enabled, even if this
|
||||
* behavior is not supported by the parent clocks.
|
||||
*
|
||||
* If changing the rate of parent A also causes the rate of
|
||||
* parent B to change, then safe_freq must be defined.
|
||||
*
|
||||
* safe_freq is expected to have a source clock which is always
|
||||
* on and runs at only one rate.
|
||||
*
|
||||
* @safe_freqs
|
||||
* list of safe frequencies which should be considered while
|
||||
* switching from A->safe_freq->B.
|
||||
* @safe_num
|
||||
* Number of safe frequencies.
|
||||
* @parents
|
||||
list of parents and mux indicies
|
||||
* list of parents and mux indicies
|
||||
* @ops
|
||||
function pointers for hw specific operations
|
||||
* function pointers for hw specific operations
|
||||
* @src_sel
|
||||
the mux index which will be used if the clock is enabled.
|
||||
* the mux index which will be used if the clock is enabled.
|
||||
*/
|
||||
|
||||
struct mux_div_clk {
|
||||
|
@ -283,6 +289,8 @@ struct mux_div_clk {
|
|||
u32 safe_div;
|
||||
struct clk *safe_parent;
|
||||
unsigned long safe_freq;
|
||||
unsigned long *safe_freqs;
|
||||
int safe_num;
|
||||
};
|
||||
|
||||
static inline struct mux_div_clk *to_mux_div_clk(struct clk *clk)
|
||||
|
|
Loading…
Reference in a new issue