Merge "clk: qcom: clk-rcg2: Configure the RCGs to a safe frequency as needed"
This commit is contained in:
commit
62d89e4169
2 changed files with 85 additions and 14 deletions
|
@ -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
|
||||
* @freq_tbl: frequency table
|
||||
* @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
|
||||
* @flags: set if RCG needs to be force enabled/disabled during
|
||||
* power sequence.
|
||||
|
@ -173,6 +174,7 @@ struct clk_rcg2 {
|
|||
unsigned long current_freq;
|
||||
u32 new_index;
|
||||
u32 curr_index;
|
||||
bool enable_safe_config;
|
||||
struct clk_regmap clkr;
|
||||
|
||||
u8 flags;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/regmap.h>
|
||||
#include <linux/rational.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <asm/div64.h>
|
||||
|
||||
|
@ -49,6 +50,14 @@
|
|||
#define N_REG 0xc
|
||||
#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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
|
@ -184,6 +204,12 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
|||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
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);
|
||||
|
||||
if (rcg->mnd_width) {
|
||||
|
@ -425,14 +451,6 @@ static void disable_unprepare_rcg_srcs(struct clk_hw *hw, struct clk *curr,
|
|||
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,
|
||||
int nindex, bool enable)
|
||||
{
|
||||
|
@ -452,6 +470,7 @@ static int clk_rcg2_enable(struct clk_hw *hw)
|
|||
{
|
||||
int ret = 0;
|
||||
const struct freq_tbl *f;
|
||||
unsigned long rate;
|
||||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
|
||||
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,
|
||||
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)
|
||||
|
@ -487,12 +528,31 @@ static void clk_rcg2_disable(struct clk_hw *hw)
|
|||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
|
||||
if (rcg->flags & FORCE_ENABLE_RCGR) {
|
||||
/* 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 delay to disable the RCG */
|
||||
udelay(100);
|
||||
clk_rcg_clear_force_enable(hw);
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
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);
|
||||
if (!f)
|
||||
return -EINVAL;
|
||||
|
|
Loading…
Add table
Reference in a new issue