Merge "clk: qcom: clk-rcg2: Configure the RCGs to a safe frequency as needed"

This commit is contained in:
Linux Build Service Account 2016-12-30 06:27:49 -08:00 committed by Gerrit - the friendly Code Review server
commit 62d89e4169
2 changed files with 85 additions and 14 deletions

View file

@ -159,6 +159,7 @@ extern const struct clk_ops clk_dyn_rcg_ops;
* @parent_map: map from software's parent index to hardware's src_sel field * @parent_map: map from software's parent index to hardware's src_sel field
* @freq_tbl: frequency table * @freq_tbl: frequency table
* @current_freq: last cached frequency when using branches with shared RCGs * @current_freq: last cached frequency when using branches with shared RCGs
* @enable_safe_config: When set, the RCG is parked at CXO when it's disabled
* @clkr: regmap clock handle * @clkr: regmap clock handle
* @flags: set if RCG needs to be force enabled/disabled during * @flags: set if RCG needs to be force enabled/disabled during
* power sequence. * power sequence.
@ -173,6 +174,7 @@ struct clk_rcg2 {
unsigned long current_freq; unsigned long current_freq;
u32 new_index; u32 new_index;
u32 curr_index; u32 curr_index;
bool enable_safe_config;
struct clk_regmap clkr; struct clk_regmap clkr;
u8 flags; u8 flags;

View file

@ -22,6 +22,7 @@
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/rational.h> #include <linux/rational.h>
#include <linux/math64.h> #include <linux/math64.h>
#include <linux/clk.h>
#include <asm/div64.h> #include <asm/div64.h>
@ -49,6 +50,14 @@
#define N_REG 0xc #define N_REG 0xc
#define D_REG 0x10 #define D_REG 0x10
static struct freq_tbl cxo_f = {
.freq = 19200000,
.src = 0,
.pre_div = 1,
.m = 0,
.n = 0,
};
static int clk_rcg2_is_enabled(struct clk_hw *hw) static int clk_rcg2_is_enabled(struct clk_hw *hw)
{ {
struct clk_rcg2 *rcg = to_clk_rcg2(hw); struct clk_rcg2 *rcg = to_clk_rcg2(hw);
@ -153,6 +162,17 @@ static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index)
return update_config(rcg); return update_config(rcg);
} }
static void clk_rcg_clear_force_enable(struct clk_hw *hw)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
/* force disable RCG - clear CMD_ROOT_EN bit */
regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
CMD_ROOT_EN, 0);
/* Add a hardware mandate delay to disable the RCG */
udelay(100);
}
/* /*
* Calculate m/n:d rate * Calculate m/n:d rate
* *
@ -184,6 +204,12 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
struct clk_rcg2 *rcg = to_clk_rcg2(hw); struct clk_rcg2 *rcg = to_clk_rcg2(hw);
u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask; u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask;
if (rcg->enable_safe_config && !clk_hw_is_prepared(hw)) {
if (!rcg->current_freq)
rcg->current_freq = cxo_f.freq;
return rcg->current_freq;
}
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
if (rcg->mnd_width) { if (rcg->mnd_width) {
@ -425,14 +451,6 @@ static void disable_unprepare_rcg_srcs(struct clk_hw *hw, struct clk *curr,
clk_unprepare(new); clk_unprepare(new);
} }
static struct freq_tbl cxo_f = {
.freq = 19200000,
.src = 0,
.pre_div = 1,
.m = 0,
.n = 0,
};
static int clk_enable_disable_prepare_unprepare(struct clk_hw *hw, int cindex, static int clk_enable_disable_prepare_unprepare(struct clk_hw *hw, int cindex,
int nindex, bool enable) int nindex, bool enable)
{ {
@ -452,6 +470,7 @@ static int clk_rcg2_enable(struct clk_hw *hw)
{ {
int ret = 0; int ret = 0;
const struct freq_tbl *f; const struct freq_tbl *f;
unsigned long rate;
struct clk_rcg2 *rcg = to_clk_rcg2(hw); struct clk_rcg2 *rcg = to_clk_rcg2(hw);
if (rcg->flags & FORCE_ENABLE_RCGR) { if (rcg->flags & FORCE_ENABLE_RCGR) {
@ -477,9 +496,31 @@ static int clk_rcg2_enable(struct clk_hw *hw)
clk_enable_disable_prepare_unprepare(hw, rcg->curr_index, clk_enable_disable_prepare_unprepare(hw, rcg->curr_index,
rcg->new_index, false); rcg->new_index, false);
return ret;
} }
return ret; if (!rcg->enable_safe_config)
return 0;
/*
* Switch from CXO to the stashed mux selection. Force enable and
* disable the RCG while configuring it to safeguard against any update
* signal coming from the downstream clock. The current parent has
* already been prepared and enabled at this point, and the CXO source
* is always on while APPS is online. Therefore, the RCG can safely be
* switched.
*/
rate = clk_get_rate(hw->clk);
f = qcom_find_freq(rcg->freq_tbl, rate);
if (!f)
return -EINVAL;
clk_rcg_set_force_enable(hw);
clk_rcg2_configure(rcg, f);
clk_rcg_clear_force_enable(hw);
return 0;
} }
static void clk_rcg2_disable(struct clk_hw *hw) static void clk_rcg2_disable(struct clk_hw *hw)
@ -487,12 +528,31 @@ static void clk_rcg2_disable(struct clk_hw *hw)
struct clk_rcg2 *rcg = to_clk_rcg2(hw); struct clk_rcg2 *rcg = to_clk_rcg2(hw);
if (rcg->flags & FORCE_ENABLE_RCGR) { if (rcg->flags & FORCE_ENABLE_RCGR) {
/* force disable RCG - clear CMD_ROOT_EN bit */ clk_rcg_clear_force_enable(hw);
regmap_update_bits(rcg->clkr.regmap, return;
rcg->cmd_rcgr + CMD_REG, CMD_ROOT_EN, 0);
/* Add a delay to disable the RCG */
udelay(100);
} }
if (!rcg->enable_safe_config)
return;
/*
* Park the RCG at a safe configuration - sourced off the CXO. This is
* needed for 2 reasons: In the case of RCGs sourcing PSCBCs, due to a
* default HW behavior, the RCG will turn on when its corresponding
* GDSC is enabled. We might also have cases when the RCG might be left
* enabled without the overlying SW knowing about it. This results from
* hard to track cases of downstream clocks being left enabled. In both
* these cases, scaling the RCG will fail since it's enabled but with
* its sources cut off.
*
* Save mux select and switch to CXO. Force enable and disable the RCG
* while configuring it to safeguard against any update signal coming
* from the downstream clock. The current parent is still prepared and
* enabled at this point, and the CXO source is always on while APPS is
* online. Therefore, the RCG can safely be switched.
*/
clk_rcg_set_force_enable(hw);
clk_rcg2_configure(rcg, &cxo_f);
clk_rcg_clear_force_enable(hw);
} }
@ -506,6 +566,15 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
if (rcg->flags & FORCE_ENABLE_RCGR) if (rcg->flags & FORCE_ENABLE_RCGR)
rcg->current_freq = clk_get_rate(hw->clk); rcg->current_freq = clk_get_rate(hw->clk);
/*
* Return if the RCG is currently disabled. This configuration update
* will happen as part of the RCG enable sequence.
*/
if (rcg->enable_safe_config && !clk_hw_is_prepared(hw)) {
rcg->current_freq = rate;
return 0;
}
f = qcom_find_freq(rcg->freq_tbl, rate); f = qcom_find_freq(rcg->freq_tbl, rate);
if (!f) if (!f)
return -EINVAL; return -EINVAL;