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
|
* @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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue