summaryrefslogtreecommitdiff
path: root/drivers/clk
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-12-30 06:27:49 -0800
committerGerrit - the friendly Code Review server <code-review@localhost>2016-12-30 06:27:49 -0800
commit62d89e4169cb1abcfb9a5db48d797f207c290ac2 (patch)
treed4eaac7efd51eeea70723091da232f3a76c00283 /drivers/clk
parent6bfadc272901b9811d9513555a5de473bf92d1f8 (diff)
parent87c6bb6b8b481a755d6e9345d87299b79f283977 (diff)
Merge "clk: qcom: clk-rcg2: Configure the RCGs to a safe frequency as needed"
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/qcom/clk-rcg.h2
-rw-r--r--drivers/clk/qcom/clk-rcg2.c97
2 files changed, 85 insertions, 14 deletions
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index accdac9fb964..58fbd07e6f15 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -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;
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 653722f9c4b0..c37716e8273d 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -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;