1 From e8e7ce92a49dc87f0d006cfbfe419b8e0b25476d Mon Sep 17 00:00:00 2001
2 From: Bjorn Andersson <bjorn.andersson@linaro.org>
3 Date: Tue, 26 Apr 2022 14:21:36 -0700
4 Subject: [PATCH] clk: qcom: rcg2: Cache CFG register updates for parked RCGs
6 As GDSCs are turned on and off some associated clocks are momentarily
7 enabled for house keeping purposes. For this, and similar, purposes the
8 "shared RCGs" will park the RCG on a source clock which is known to be
10 When the RCG is parked, a safe clock source will be selected and
11 committed, then the original source would be written back and upon enable
12 the change back to the unparked source would be committed.
14 But starting with SM8350 this fails, as the value in CFG is committed by
15 the GDSC handshake and without a ticking parent the GDSC enablement will
18 This becomes a concrete problem if the runtime supended state of a
19 device includes disabling such rcg's parent clock. As the device
20 attempts to power up the domain again the rcg will fail to enable and
21 hence the GDSC enablement will fail, preventing the device from
22 returning from the suspended state.
24 This can be seen in e.g. the display stack during probe on SM8350.
26 To avoid this problem, the software needs to ensure that the RCG is
27 configured to a active parent clock while it is disabled. This is done
28 by caching the CFG register content while the shared RCG is parked on
31 Writes to M, N and D registers are committed as they are requested. New
32 helpers for get_parent() and recalc_rate() are extracted from their
33 previous implementations and __clk_rcg2_configure() is modified to allow
34 it to operate on the cached value.
36 Fixes: 7ef6f11887bd ("clk: qcom: Configure the RCGs to a safe source as needed")
37 Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
38 Reviewed-by: Stephen Boyd <sboyd@kernel.org>
39 Link: https://lore.kernel.org/r/20220426212136.1543984-1-bjorn.andersson@linaro.org
41 drivers/clk/qcom/clk-rcg.h | 2 +
42 drivers/clk/qcom/clk-rcg2.c | 126 ++++++++++++++++++++++++++++--------
43 2 files changed, 101 insertions(+), 27 deletions(-)
45 --- a/drivers/clk/qcom/clk-rcg.h
46 +++ b/drivers/clk/qcom/clk-rcg.h
47 @@ -139,6 +139,7 @@ extern const struct clk_ops clk_dyn_rcg_
48 * @freq_tbl: frequency table
49 * @clkr: regmap clock handle
50 * @cfg_off: defines the cfg register offset from the CMD_RCGR + CFG_REG
51 + * @parked_cfg: cached value of the CFG register for parked RCGs
55 @@ -149,6 +150,7 @@ struct clk_rcg2 {
56 const struct freq_tbl *freq_tbl;
57 struct clk_regmap clkr;
62 #define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr)
63 --- a/drivers/clk/qcom/clk-rcg2.c
64 +++ b/drivers/clk/qcom/clk-rcg2.c
65 @@ -74,16 +74,11 @@ static int clk_rcg2_is_enabled(struct cl
66 return (cmd & CMD_ROOT_OFF) == 0;
69 -static u8 clk_rcg2_get_parent(struct clk_hw *hw)
70 +static u8 __clk_rcg2_get_parent(struct clk_hw *hw, u32 cfg)
72 struct clk_rcg2 *rcg = to_clk_rcg2(hw);
73 int num_parents = clk_hw_get_num_parents(hw);
77 - ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
82 cfg &= CFG_SRC_SEL_MASK;
83 cfg >>= CFG_SRC_SEL_SHIFT;
84 @@ -92,12 +87,27 @@ static u8 clk_rcg2_get_parent(struct clk
85 if (cfg == rcg->parent_map[i].cfg)
89 pr_debug("%s: Clock %s has invalid parent, using default.\n",
90 __func__, clk_hw_get_name(hw));
94 +static u8 clk_rcg2_get_parent(struct clk_hw *hw)
96 + struct clk_rcg2 *rcg = to_clk_rcg2(hw);
100 + ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
102 + pr_debug("%s: Unable to read CFG register for %s\n",
103 + __func__, clk_hw_get_name(hw));
107 + return __clk_rcg2_get_parent(hw, cfg);
110 static int update_config(struct clk_rcg2 *rcg)
113 @@ -164,12 +174,10 @@ calc_rate(unsigned long rate, u32 m, u32
117 -clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
118 +__clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate, u32 cfg)
120 struct clk_rcg2 *rcg = to_clk_rcg2(hw);
121 - u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask;
123 - regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
124 + u32 hid_div, m = 0, n = 0, mode = 0, mask;
126 if (rcg->mnd_width) {
127 mask = BIT(rcg->mnd_width) - 1;
128 @@ -190,6 +198,17 @@ clk_rcg2_recalc_rate(struct clk_hw *hw,
129 return calc_rate(parent_rate, m, n, mode, hid_div);
132 +static unsigned long
133 +clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
135 + struct clk_rcg2 *rcg = to_clk_rcg2(hw);
138 + regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
140 + return __clk_rcg2_recalc_rate(hw, parent_rate, cfg);
143 static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
144 struct clk_rate_request *req,
145 enum freq_policy policy)
146 @@ -263,7 +282,8 @@ static int clk_rcg2_determine_floor_rate
147 return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, FLOOR);
150 -static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
151 +static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f,
154 u32 cfg, mask, d_val, not2d_val, n_minus_m;
155 struct clk_hw *hw = &rcg->clkr.hw;
156 @@ -305,15 +325,27 @@ static int __clk_rcg2_configure(struct c
157 cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
158 if (rcg->mnd_width && f->n && (f->m != f->n))
159 cfg |= CFG_MODE_DUAL_EDGE;
160 - return regmap_update_bits(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg),
169 static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
174 - ret = __clk_rcg2_configure(rcg, f);
175 + ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
179 + ret = __clk_rcg2_configure(rcg, f, &cfg);
183 + ret = regmap_write(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), cfg);
187 @@ -994,11 +1026,12 @@ static int clk_rcg2_shared_set_rate(stru
191 - * In case clock is disabled, update the CFG, M, N and D registers
192 - * and don't hit the update bit of CMD register.
193 + * In case clock is disabled, update the M, N and D registers, cache
194 + * the CFG value in parked_cfg and don't hit the update bit of CMD
197 - if (!__clk_is_enabled(hw->clk))
198 - return __clk_rcg2_configure(rcg, f);
199 + if (!clk_hw_is_enabled(hw))
200 + return __clk_rcg2_configure(rcg, f, &rcg->parked_cfg);
202 return clk_rcg2_shared_force_enable_clear(hw, f);
204 @@ -1022,6 +1055,11 @@ static int clk_rcg2_shared_enable(struct
208 + /* Write back the stored configuration corresponding to current rate */
209 + ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, rcg->parked_cfg);
213 ret = update_config(rcg);
216 @@ -1032,13 +1070,12 @@ static int clk_rcg2_shared_enable(struct
217 static void clk_rcg2_shared_disable(struct clk_hw *hw)
219 struct clk_rcg2 *rcg = to_clk_rcg2(hw);
223 * Store current configuration as switching to safe source would clear
224 * the SRC and DIV of CFG register
226 - regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
227 + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &rcg->parked_cfg);
230 * Park the RCG at a safe configuration - sourced off of safe source.
231 @@ -1056,17 +1093,52 @@ static void clk_rcg2_shared_disable(stru
234 clk_rcg2_clear_force_enable(hw);
237 - /* Write back the stored configuration corresponding to current rate */
238 - regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg);
239 +static u8 clk_rcg2_shared_get_parent(struct clk_hw *hw)
241 + struct clk_rcg2 *rcg = to_clk_rcg2(hw);
243 + /* If the shared rcg is parked use the cached cfg instead */
244 + if (!clk_hw_is_enabled(hw))
245 + return __clk_rcg2_get_parent(hw, rcg->parked_cfg);
247 + return clk_rcg2_get_parent(hw);
250 +static int clk_rcg2_shared_set_parent(struct clk_hw *hw, u8 index)
252 + struct clk_rcg2 *rcg = to_clk_rcg2(hw);
254 + /* If the shared rcg is parked only update the cached cfg */
255 + if (!clk_hw_is_enabled(hw)) {
256 + rcg->parked_cfg &= ~CFG_SRC_SEL_MASK;
257 + rcg->parked_cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
262 + return clk_rcg2_set_parent(hw, index);
265 +static unsigned long
266 +clk_rcg2_shared_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
268 + struct clk_rcg2 *rcg = to_clk_rcg2(hw);
270 + /* If the shared rcg is parked use the cached cfg instead */
271 + if (!clk_hw_is_enabled(hw))
272 + return __clk_rcg2_recalc_rate(hw, parent_rate, rcg->parked_cfg);
274 + return clk_rcg2_recalc_rate(hw, parent_rate);
277 const struct clk_ops clk_rcg2_shared_ops = {
278 .enable = clk_rcg2_shared_enable,
279 .disable = clk_rcg2_shared_disable,
280 - .get_parent = clk_rcg2_get_parent,
281 - .set_parent = clk_rcg2_set_parent,
282 - .recalc_rate = clk_rcg2_recalc_rate,
283 + .get_parent = clk_rcg2_shared_get_parent,
284 + .set_parent = clk_rcg2_shared_set_parent,
285 + .recalc_rate = clk_rcg2_shared_recalc_rate,
286 .determine_rate = clk_rcg2_determine_rate,
287 .set_rate = clk_rcg2_shared_set_rate,
288 .set_rate_and_parent = clk_rcg2_shared_set_rate_and_parent,