From 07e8f7920dc27e9f09363c6b22d2a99b00088042 Mon Sep 17 00:00:00 2001 From: David Dai Date: Wed, 26 Jun 2019 18:04:53 -0700 Subject: [PATCH 1/2] clk: qcom: clk-alpha-pll: check pcal_done from mode register The sequence for alpha_pll_lucid_prepare is checking an incorrect bit to determine whether or not a lucid PLL has calibrated successfully. The bit in the status register is not observable unless pll_test_ctl[69] is set and user_ctl[43:37] is 0 which should only be used as a fallback option. Use the correct PCAL_DONE bit from the mode register to properly determine PLL calibration status. Change-Id: If952c30cacacd68c07e8d7dfd622069dbb70c909 Signed-off-by: David Dai --- drivers/clk/qcom/clk-alpha-pll.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 439af7e416a5..6b7fc49be25b 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -157,7 +157,7 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); /* LUCID PLL specific settings and offsets */ #define LUCID_PLL_CAL_VAL 0x44 -#define LUCID_PCAL_DONE BIT(26) +#define LUCID_PCAL_DONE BIT(27) /* ZONDA PLL specific offsets */ #define ZONDA_PLL_OUT_MASK 0xF @@ -1614,7 +1614,7 @@ static int alpha_pll_lucid_prepare(struct clk_hw *hw) int ret; /* Return early if calibration is not needed. */ - regmap_read(pll->clkr.regmap, PLL_STATUS(pll), ®val); + regmap_read(pll->clkr.regmap, PLL_MODE(pll), ®val); if (regval & LUCID_PCAL_DONE) return 0; From 3e549714a84923645fa23109cb6a7a5dd2e07f97 Mon Sep 17 00:00:00 2001 From: Deepak Katragadda Date: Tue, 11 Sep 2018 15:07:31 -0700 Subject: [PATCH 2/2] clk: qcom: clk-rcg2: Configure the RCGs to a safe frequency as needed In certain cases, an RCG might be prone to being enabled even though the overlying software thinks that it disabled the RCG. In order to avoid letting the RCG go into an invalid state, support parking it at a safe frequency during clk_disable() and deferring all the RCG configuration updates to be done during clk_enable(), if a scaling request comes in whilst the clock is disabled. Change-Id: I55f1d1d346182a2b480127c57d6659fc9a63331b Signed-off-by: Deepak Katragadda Signed-off-by: David Dai Signed-off-by: David Collins --- drivers/clk/qcom/clk-rcg.h | 8 +- drivers/clk/qcom/clk-rcg2.c | 164 ++++++++++++++++++++++++++++-------- 2 files changed, 136 insertions(+), 36 deletions(-) diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index c25b57c3cbc8..9007cddb33d3 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -1,5 +1,5 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2013, 2018, The Linux Foundation. All rights reserved. */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2013, 2016-2018, The Linux Foundation. All rights reserved. */ #ifndef __QCOM_CLK_RCG_H__ #define __QCOM_CLK_RCG_H__ @@ -137,6 +137,8 @@ extern const struct clk_ops clk_dyn_rcg_ops; * @safe_src_index: safe src index value * @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 * @cfg_off: defines the cfg register offset from the CMD_RCGR + CFG_REG */ @@ -147,6 +149,8 @@ struct clk_rcg2 { u8 safe_src_index; const struct parent_map *parent_map; const struct freq_tbl *freq_tbl; + unsigned long current_freq; + bool enable_safe_config; struct clk_regmap clkr; u8 cfg_off; }; diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index 8c02bffe50df..bc4ba87af073 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -1,6 +1,6 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2013, 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2013, 2016-2018, The Linux Foundation. All rights reserved. */ #include @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,14 @@ enum freq_policy { CEIL, }; +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); @@ -136,6 +145,35 @@ static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index) return update_config(rcg); } +static int clk_rcg2_set_force_enable(struct clk_hw *hw) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + int ret = 0, count = 500; + + ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, + CMD_ROOT_EN, CMD_ROOT_EN); + if (ret) + return ret; + + for (; count > 0; count--) { + if (clk_rcg2_is_enabled(hw)) + return ret; + /* Delay for 1usec and retry polling the status bit */ + udelay(1); + } + + WARN(1, "%s: rcg didn't turn on.", clk_hw_get_name(hw)); + return ret; +} + +static int clk_rcg2_clear_force_enable(struct clk_hw *hw) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + + return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, + CMD_ROOT_EN, 0); +} + /* * Calculate m/n:d rate * @@ -167,6 +205,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_CFG_OFFSET(rcg), &cfg); if (rcg->mnd_width) { @@ -309,6 +353,7 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, { struct clk_rcg2 *rcg = to_clk_rcg2(hw); const struct freq_tbl *f; + int ret; switch (policy) { case FLOOR: @@ -324,7 +369,22 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, if (!f) return -EINVAL; - return clk_rcg2_configure(rcg, f); + /* + * 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; + } + + ret = clk_rcg2_configure(rcg, f); + if (ret) + return ret; + + /* Update current frequency with the requested frequency. */ + rcg->current_freq = rate; + return ret; } static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, @@ -351,8 +411,75 @@ static int clk_rcg2_set_floor_rate_and_parent(struct clk_hw *hw, return __clk_rcg2_set_rate(hw, rate, FLOOR); } +static int clk_rcg2_enable(struct clk_hw *hw) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + unsigned long rate; + const struct freq_tbl *f; + + 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 = rcg->current_freq; + f = qcom_find_freq(rcg->freq_tbl, rate); + if (!f) + return -EINVAL; + + /* + * If CXO is not listed as a supported frequency in the frequency + * table, the above API would return the lowest supported frequency + * instead. This will lead to incorrect configuration of the RCG. + * Check if the RCG rate is CXO and configure it accordingly. + */ + if (rate == cxo_f.freq) + f = &cxo_f; + + clk_rcg2_set_force_enable(hw); + clk_rcg2_configure(rcg, f); + clk_rcg2_clear_force_enable(hw); + + return 0; +} + +static void clk_rcg2_disable(struct clk_hw *hw) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + + 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_rcg2_set_force_enable(hw); + clk_rcg2_configure(rcg, &cxo_f); + clk_rcg2_clear_force_enable(hw); +} + const struct clk_ops clk_rcg2_ops = { .is_enabled = clk_rcg2_is_enabled, + .enable = clk_rcg2_enable, + .disable = clk_rcg2_disable, .get_parent = clk_rcg2_get_parent, .set_parent = clk_rcg2_set_parent, .recalc_rate = clk_rcg2_recalc_rate, @@ -805,37 +932,6 @@ const struct clk_ops clk_gfx3d_ops = { }; EXPORT_SYMBOL_GPL(clk_gfx3d_ops); -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); - int ret, count; - - ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, - CMD_ROOT_EN, CMD_ROOT_EN); - if (ret) - return ret; - - /* wait for RCG to turn ON */ - for (count = 500; count > 0; count--) { - if (clk_rcg2_is_enabled(hw)) - return 0; - - udelay(1); - } - - 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); - - return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, - CMD_ROOT_EN, 0); -} - static int clk_rcg2_shared_force_enable_clear(struct clk_hw *hw, const struct freq_tbl *f) {