diff --git a/drivers/clk/qcom/clock-generic.c b/drivers/clk/qcom/clock-generic.c index 32243ab3fce5..511d77f7a836 100644 --- a/drivers/clk/qcom/clock-generic.c +++ b/drivers/clk/qcom/clock-generic.c @@ -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; } diff --git a/include/linux/clk/msm-clock-generic.h b/include/linux/clk/msm-clock-generic.h index 739a29162938..8c12c50412ae 100644 --- a/include/linux/clk/msm-clock-generic.h +++ b/include/linux/clk/msm-clock-generic.h @@ -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)