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:
Taniya Das 2015-06-13 14:12:34 +05:30
parent e753dc6232
commit d181f8f4b8
2 changed files with 67 additions and 19 deletions

View file

@ -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;
}

View file

@ -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)