5300c36dce70df0e8b5b4d77389e929eb5b95c33
[openwrt/staging/neocturne.git] /
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
5
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
9 available.
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.
13
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
16 time out.
17
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.
23
24 This can be seen in e.g. the display stack during probe on SM8350.
25
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
29 this safe source.
30
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.
35
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
40 ---
41 drivers/clk/qcom/clk-rcg.h | 2 +
42 drivers/clk/qcom/clk-rcg2.c | 126 ++++++++++++++++++++++++++++--------
43 2 files changed, 101 insertions(+), 27 deletions(-)
44
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
52 */
53 struct clk_rcg2 {
54 u32 cmd_rcgr;
55 @@ -149,6 +150,7 @@ struct clk_rcg2 {
56 const struct freq_tbl *freq_tbl;
57 struct clk_regmap clkr;
58 u8 cfg_off;
59 + u32 parked_cfg;
60 };
61
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;
67 }
68
69 -static u8 clk_rcg2_get_parent(struct clk_hw *hw)
70 +static u8 __clk_rcg2_get_parent(struct clk_hw *hw, u32 cfg)
71 {
72 struct clk_rcg2 *rcg = to_clk_rcg2(hw);
73 int num_parents = clk_hw_get_num_parents(hw);
74 - u32 cfg;
75 - int i, ret;
76 -
77 - ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
78 - if (ret)
79 - goto err;
80 + int i;
81
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)
86 return i;
87
88 -err:
89 pr_debug("%s: Clock %s has invalid parent, using default.\n",
90 __func__, clk_hw_get_name(hw));
91 return 0;
92 }
93
94 +static u8 clk_rcg2_get_parent(struct clk_hw *hw)
95 +{
96 + struct clk_rcg2 *rcg = to_clk_rcg2(hw);
97 + u32 cfg;
98 + int ret;
99 +
100 + ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
101 + if (ret) {
102 + pr_debug("%s: Unable to read CFG register for %s\n",
103 + __func__, clk_hw_get_name(hw));
104 + return 0;
105 + }
106 +
107 + return __clk_rcg2_get_parent(hw, cfg);
108 +}
109 +
110 static int update_config(struct clk_rcg2 *rcg)
111 {
112 int count, ret;
113 @@ -164,12 +174,10 @@ calc_rate(unsigned long rate, u32 m, u32
114 }
115
116 static unsigned long
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)
119 {
120 struct clk_rcg2 *rcg = to_clk_rcg2(hw);
121 - u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask;
122 -
123 - regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
124 + u32 hid_div, m = 0, n = 0, mode = 0, mask;
125
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);
130 }
131
132 +static unsigned long
133 +clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
134 +{
135 + struct clk_rcg2 *rcg = to_clk_rcg2(hw);
136 + u32 cfg;
137 +
138 + regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
139 +
140 + return __clk_rcg2_recalc_rate(hw, parent_rate, cfg);
141 +}
142 +
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);
148 }
149
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,
152 + u32 *_cfg)
153 {
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),
161 - mask, cfg);
162 +
163 + *_cfg &= ~mask;
164 + *_cfg |= cfg;
165 +
166 + return 0;
167 }
168
169 static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
170 {
171 + u32 cfg;
172 int ret;
173
174 - ret = __clk_rcg2_configure(rcg, f);
175 + ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
176 + if (ret)
177 + return ret;
178 +
179 + ret = __clk_rcg2_configure(rcg, f, &cfg);
180 + if (ret)
181 + return ret;
182 +
183 + ret = regmap_write(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), cfg);
184 if (ret)
185 return ret;
186
187 @@ -994,11 +1026,12 @@ static int clk_rcg2_shared_set_rate(stru
188 return -EINVAL;
189
190 /*
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
195 + * register.
196 */
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);
201
202 return clk_rcg2_shared_force_enable_clear(hw, f);
203 }
204 @@ -1022,6 +1055,11 @@ static int clk_rcg2_shared_enable(struct
205 if (ret)
206 return ret;
207
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);
210 + if (ret)
211 + return ret;
212 +
213 ret = update_config(rcg);
214 if (ret)
215 return ret;
216 @@ -1032,13 +1070,12 @@ static int clk_rcg2_shared_enable(struct
217 static void clk_rcg2_shared_disable(struct clk_hw *hw)
218 {
219 struct clk_rcg2 *rcg = to_clk_rcg2(hw);
220 - u32 cfg;
221
222 /*
223 * Store current configuration as switching to safe source would clear
224 * the SRC and DIV of CFG register
225 */
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);
228
229 /*
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
232 update_config(rcg);
233
234 clk_rcg2_clear_force_enable(hw);
235 +}
236
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)
240 +{
241 + struct clk_rcg2 *rcg = to_clk_rcg2(hw);
242 +
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);
246 +
247 + return clk_rcg2_get_parent(hw);
248 +}
249 +
250 +static int clk_rcg2_shared_set_parent(struct clk_hw *hw, u8 index)
251 +{
252 + struct clk_rcg2 *rcg = to_clk_rcg2(hw);
253 +
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;
258 +
259 + return 0;
260 + }
261 +
262 + return clk_rcg2_set_parent(hw, index);
263 +}
264 +
265 +static unsigned long
266 +clk_rcg2_shared_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
267 +{
268 + struct clk_rcg2 *rcg = to_clk_rcg2(hw);
269 +
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);
273 +
274 + return clk_rcg2_recalc_rate(hw, parent_rate);
275 }
276
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,