[PATCH 2/2] clk: qcom: Modify RCG shared ops to support freq_tbl without XO entry

From: Amit Nischal
Date: Tue Oct 31 2017 - 03:20:38 EST


There could be some clock sources where there is no entry corresponding
XO in their frequency table, for such sources rcg2_shared_ops would
wrongly configure the RCG registers during enable/disable, which leads
to mismatch between the hardware and software rate so modify the shared
ops to handle such cases.

Signed-off-by: Amit Nischal <anischal@xxxxxxxxxxxxxx>
---
drivers/clk/qcom/clk-rcg2.c | 79 +++++++++++++++++++++++++++++++++++++++------
1 file changed, 70 insertions(+), 9 deletions(-)

diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index ac9ce61..8f7ca0c 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -48,6 +48,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,
+};
+
enum freq_policy {
FLOOR,
CEIL,
@@ -359,7 +367,7 @@ static int clk_rcg2_set_floor_rate_and_parent(struct clk_hw *hw,
};
EXPORT_SYMBOL_GPL(clk_rcg2_floor_ops);

-static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate)
+static int clk_rcg2_set_force_enable(struct clk_hw *hw)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
const char *name = clk_hw_get_name(hw);
@@ -373,22 +381,41 @@ static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate)

/* wait for RCG to turn ON */
for (count = 500; count > 0; count--) {
- ret = clk_rcg2_is_enabled(hw);
- if (ret)
- break;
+ if (clk_rcg2_is_enabled(hw))
+ return 0;
+
+ /* Delay for 1usec and retry polling the status bit */
udelay(1);
}
if (!count)
pr_err("%s: RCG did not turn on\n", name);

+ return -ETIMEDOUT;
+}
+
+static int clk_rcg2_clear_force_enable(struct clk_hw *hw)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+
+ /* clear force enable RCG */
+ return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
+ CMD_ROOT_EN, 0);
+}
+
+static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate)
+{
+ int ret;
+
+ ret = clk_rcg2_set_force_enable(hw);
+ if (ret)
+ return ret;
+
/* set clock rate */
ret = __clk_rcg2_set_rate(hw, rate, CEIL);
if (ret)
return ret;

- /* clear force enable RCG */
- return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
- CMD_ROOT_EN, 0);
+ return clk_rcg2_clear_force_enable(hw);
}

static int clk_rcg2_shared_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -399,6 +426,11 @@ static int clk_rcg2_shared_set_rate(struct clk_hw *hw, unsigned long rate,
/* cache the rate */
rcg->current_freq = rate;

+ /*
+ * Return if the RCG is currently disabled. This configuration
+ * update will happen as part of the RCG enable sequence.
+ */
+
if (!__clk_is_enabled(hw->clk))
return 0;

@@ -410,6 +442,12 @@ static int clk_rcg2_shared_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);

+ if (!__clk_is_enabled(hw->clk)) {
+ if (!rcg->current_freq)
+ rcg->current_freq = cxo_f.freq;
+ return rcg->current_freq;
+ }
+
return rcg->current_freq = clk_rcg2_recalc_rate(hw, parent_rate);
}

@@ -417,6 +455,20 @@ static int clk_rcg2_shared_enable(struct clk_hw *hw)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);

+ if (rcg->current_freq == cxo_f.freq) {
+ clk_rcg2_set_force_enable(hw);
+ clk_rcg2_configure(rcg, &cxo_f);
+ clk_rcg2_clear_force_enable(hw);
+
+ return 0;
+ }
+
+ /*
+ * Switch from CXO to the stashed mux selection. The current
+ * parent has already been prepared and enabled at this point,
+ * and the CXO source is always on while application processor
+ * subsystem is online. Therefore, the RCG can safely be switched.
+ */
return clk_rcg2_shared_force_enable(hw, rcg->current_freq);
}

@@ -424,8 +476,17 @@ static void clk_rcg2_shared_disable(struct clk_hw *hw)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);

- /* switch to XO, which is the lowest entry in the freq table */
- clk_rcg2_shared_set_rate(hw, rcg->freq_tbl[0].freq, 0);
+ /*
+ * Park the RCG at a safe configuration - sourced off the 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 application processor
+ * subsystem is online. Therefore, the RCG can safely be switched.
+ */
+ clk_rcg2_set_force_enable(hw);
+ clk_rcg2_configure(rcg, &cxo_f);
+ clk_rcg2_clear_force_enable(hw);
}

const struct clk_ops clk_rcg2_shared_ops = {
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation