]> git.ipfire.org Git - thirdparty/openwrt.git/blob
478f60ed44c42e185551c5e98775ed02787237b7
[thirdparty/openwrt.git] /
1 From 89da22456af0762477d8c1345fdd17961b3ada80 Mon Sep 17 00:00:00 2001
2 From: Christian Marangi <ansuelsmth@gmail.com>
3 Date: Wed, 20 Dec 2023 23:17:23 +0100
4 Subject: [PATCH] clk: qcom: clk-rcg2: add support for rcg2 freq multi ops
5
6 Some RCG frequency can be reached by multiple configuration.
7
8 Add clk_rcg2_fm_ops ops to support these special RCG configurations.
9
10 These alternative ops will select the frequency using a CEIL policy.
11
12 When the correct frequency is found, the correct config is selected by
13 calculating the final rate (by checking the defined parent and values
14 in the config that is being checked) and deciding based on the one that
15 is less different than the requested one.
16
17 These check are skipped if there is just one config for the requested
18 freq.
19
20 qcom_find_freq_multi is added to search the freq with the new struct
21 freq_multi_tbl.
22 __clk_rcg2_select_conf is used to select the correct conf by simulating
23 the final clock.
24 If a conf can't be found due to parent not reachable, a WARN is printed
25 and -EINVAL is returned.
26
27 Tested-by: Wei Lei <quic_leiwei@quicinc.com>
28 Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
29 Acked-by: Stephen Boyd <sboyd@kernel.org>
30 Link: https://lore.kernel.org/r/20231220221724.3822-3-ansuelsmth@gmail.com
31 Signed-off-by: Bjorn Andersson <andersson@kernel.org>
32 ---
33 drivers/clk/qcom/clk-rcg.h | 1 +
34 drivers/clk/qcom/clk-rcg2.c | 166 ++++++++++++++++++++++++++++++++++++
35 drivers/clk/qcom/common.c | 18 ++++
36 drivers/clk/qcom/common.h | 2 +
37 4 files changed, 187 insertions(+)
38
39 --- a/drivers/clk/qcom/clk-rcg.h
40 +++ b/drivers/clk/qcom/clk-rcg.h
41 @@ -190,6 +190,7 @@ struct clk_rcg2_gfx3d {
42
43 extern const struct clk_ops clk_rcg2_ops;
44 extern const struct clk_ops clk_rcg2_floor_ops;
45 +extern const struct clk_ops clk_rcg2_fm_ops;
46 extern const struct clk_ops clk_rcg2_mux_closest_ops;
47 extern const struct clk_ops clk_edp_pixel_ops;
48 extern const struct clk_ops clk_byte_ops;
49 --- a/drivers/clk/qcom/clk-rcg2.c
50 +++ b/drivers/clk/qcom/clk-rcg2.c
51 @@ -260,6 +260,115 @@ static int _freq_tbl_determine_rate(stru
52 return 0;
53 }
54
55 +static const struct freq_conf *
56 +__clk_rcg2_select_conf(struct clk_hw *hw, const struct freq_multi_tbl *f,
57 + unsigned long req_rate)
58 +{
59 + unsigned long rate_diff, best_rate_diff = ULONG_MAX;
60 + const struct freq_conf *conf, *best_conf = NULL;
61 + struct clk_rcg2 *rcg = to_clk_rcg2(hw);
62 + const char *name = clk_hw_get_name(hw);
63 + unsigned long parent_rate, rate;
64 + struct clk_hw *p;
65 + int index, i;
66 +
67 + /* Exit early if only one config is defined */
68 + if (f->num_confs == 1) {
69 + best_conf = f->confs;
70 + goto exit;
71 + }
72 +
73 + /* Search in each provided config the one that is near the wanted rate */
74 + for (i = 0, conf = f->confs; i < f->num_confs; i++, conf++) {
75 + index = qcom_find_src_index(hw, rcg->parent_map, conf->src);
76 + if (index < 0)
77 + continue;
78 +
79 + p = clk_hw_get_parent_by_index(hw, index);
80 + if (!p)
81 + continue;
82 +
83 + parent_rate = clk_hw_get_rate(p);
84 + rate = calc_rate(parent_rate, conf->n, conf->m, conf->n, conf->pre_div);
85 +
86 + if (rate == req_rate) {
87 + best_conf = conf;
88 + goto exit;
89 + }
90 +
91 + rate_diff = abs_diff(req_rate, rate);
92 + if (rate_diff < best_rate_diff) {
93 + best_rate_diff = rate_diff;
94 + best_conf = conf;
95 + }
96 + }
97 +
98 + /*
99 + * Very unlikely. Warn if we couldn't find a correct config
100 + * due to parent not found in every config.
101 + */
102 + if (unlikely(!best_conf)) {
103 + WARN(1, "%s: can't find a configuration for rate %lu\n",
104 + name, req_rate);
105 + return ERR_PTR(-EINVAL);
106 + }
107 +
108 +exit:
109 + return best_conf;
110 +}
111 +
112 +static int _freq_tbl_fm_determine_rate(struct clk_hw *hw, const struct freq_multi_tbl *f,
113 + struct clk_rate_request *req)
114 +{
115 + unsigned long clk_flags, rate = req->rate;
116 + struct clk_rcg2 *rcg = to_clk_rcg2(hw);
117 + const struct freq_conf *conf;
118 + struct clk_hw *p;
119 + int index;
120 +
121 + f = qcom_find_freq_multi(f, rate);
122 + if (!f || !f->confs)
123 + return -EINVAL;
124 +
125 + conf = __clk_rcg2_select_conf(hw, f, rate);
126 + if (IS_ERR(conf))
127 + return PTR_ERR(conf);
128 + index = qcom_find_src_index(hw, rcg->parent_map, conf->src);
129 + if (index < 0)
130 + return index;
131 +
132 + clk_flags = clk_hw_get_flags(hw);
133 + p = clk_hw_get_parent_by_index(hw, index);
134 + if (!p)
135 + return -EINVAL;
136 +
137 + if (clk_flags & CLK_SET_RATE_PARENT) {
138 + rate = f->freq;
139 + if (conf->pre_div) {
140 + if (!rate)
141 + rate = req->rate;
142 + rate /= 2;
143 + rate *= conf->pre_div + 1;
144 + }
145 +
146 + if (conf->n) {
147 + u64 tmp = rate;
148 +
149 + tmp = tmp * conf->n;
150 + do_div(tmp, conf->m);
151 + rate = tmp;
152 + }
153 + } else {
154 + rate = clk_hw_get_rate(p);
155 + }
156 +
157 + req->best_parent_hw = p;
158 + req->best_parent_rate = rate;
159 + req->rate = f->freq;
160 +
161 + return 0;
162 +}
163 +
164 static int clk_rcg2_determine_rate(struct clk_hw *hw,
165 struct clk_rate_request *req)
166 {
167 @@ -276,6 +385,14 @@ static int clk_rcg2_determine_floor_rate
168 return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, FLOOR);
169 }
170
171 +static int clk_rcg2_fm_determine_rate(struct clk_hw *hw,
172 + struct clk_rate_request *req)
173 +{
174 + struct clk_rcg2 *rcg = to_clk_rcg2(hw);
175 +
176 + return _freq_tbl_fm_determine_rate(hw, rcg->freq_multi_tbl, req);
177 +}
178 +
179 static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f,
180 u32 *_cfg)
181 {
182 @@ -371,6 +488,30 @@ static int __clk_rcg2_set_rate(struct cl
183 return clk_rcg2_configure(rcg, f);
184 }
185
186 +static int __clk_rcg2_fm_set_rate(struct clk_hw *hw, unsigned long rate)
187 +{
188 + struct clk_rcg2 *rcg = to_clk_rcg2(hw);
189 + const struct freq_multi_tbl *f;
190 + const struct freq_conf *conf;
191 + struct freq_tbl f_tbl = {};
192 +
193 + f = qcom_find_freq_multi(rcg->freq_multi_tbl, rate);
194 + if (!f || !f->confs)
195 + return -EINVAL;
196 +
197 + conf = __clk_rcg2_select_conf(hw, f, rate);
198 + if (IS_ERR(conf))
199 + return PTR_ERR(conf);
200 +
201 + f_tbl.freq = f->freq;
202 + f_tbl.src = conf->src;
203 + f_tbl.pre_div = conf->pre_div;
204 + f_tbl.m = conf->m;
205 + f_tbl.n = conf->n;
206 +
207 + return clk_rcg2_configure(rcg, &f_tbl);
208 +}
209 +
210 static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
211 unsigned long parent_rate)
212 {
213 @@ -383,6 +524,12 @@ static int clk_rcg2_set_floor_rate(struc
214 return __clk_rcg2_set_rate(hw, rate, FLOOR);
215 }
216
217 +static int clk_rcg2_fm_set_rate(struct clk_hw *hw, unsigned long rate,
218 + unsigned long parent_rate)
219 +{
220 + return __clk_rcg2_fm_set_rate(hw, rate);
221 +}
222 +
223 static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw,
224 unsigned long rate, unsigned long parent_rate, u8 index)
225 {
226 @@ -395,6 +542,12 @@ static int clk_rcg2_set_floor_rate_and_p
227 return __clk_rcg2_set_rate(hw, rate, FLOOR);
228 }
229
230 +static int clk_rcg2_fm_set_rate_and_parent(struct clk_hw *hw,
231 + unsigned long rate, unsigned long parent_rate, u8 index)
232 +{
233 + return __clk_rcg2_fm_set_rate(hw, rate);
234 +}
235 +
236 static int clk_rcg2_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty)
237 {
238 struct clk_rcg2 *rcg = to_clk_rcg2(hw);
239 @@ -505,6 +658,19 @@ const struct clk_ops clk_rcg2_floor_ops
240 };
241 EXPORT_SYMBOL_GPL(clk_rcg2_floor_ops);
242
243 +const struct clk_ops clk_rcg2_fm_ops = {
244 + .is_enabled = clk_rcg2_is_enabled,
245 + .get_parent = clk_rcg2_get_parent,
246 + .set_parent = clk_rcg2_set_parent,
247 + .recalc_rate = clk_rcg2_recalc_rate,
248 + .determine_rate = clk_rcg2_fm_determine_rate,
249 + .set_rate = clk_rcg2_fm_set_rate,
250 + .set_rate_and_parent = clk_rcg2_fm_set_rate_and_parent,
251 + .get_duty_cycle = clk_rcg2_get_duty_cycle,
252 + .set_duty_cycle = clk_rcg2_set_duty_cycle,
253 +};
254 +EXPORT_SYMBOL_GPL(clk_rcg2_fm_ops);
255 +
256 const struct clk_ops clk_rcg2_mux_closest_ops = {
257 .determine_rate = __clk_mux_determine_rate_closest,
258 .get_parent = clk_rcg2_get_parent,
259 --- a/drivers/clk/qcom/common.c
260 +++ b/drivers/clk/qcom/common.c
261 @@ -41,6 +41,24 @@ struct freq_tbl *qcom_find_freq(const st
262 }
263 EXPORT_SYMBOL_GPL(qcom_find_freq);
264
265 +const struct freq_multi_tbl *qcom_find_freq_multi(const struct freq_multi_tbl *f,
266 + unsigned long rate)
267 +{
268 + if (!f)
269 + return NULL;
270 +
271 + if (!f->freq)
272 + return f;
273 +
274 + for (; f->freq; f++)
275 + if (rate <= f->freq)
276 + return f;
277 +
278 + /* Default to our fastest rate */
279 + return f - 1;
280 +}
281 +EXPORT_SYMBOL_GPL(qcom_find_freq_multi);
282 +
283 const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
284 unsigned long rate)
285 {
286 --- a/drivers/clk/qcom/common.h
287 +++ b/drivers/clk/qcom/common.h
288 @@ -45,6 +45,8 @@ extern const struct freq_tbl *qcom_find_
289 unsigned long rate);
290 extern const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
291 unsigned long rate);
292 +extern const struct freq_multi_tbl *qcom_find_freq_multi(const struct freq_multi_tbl *f,
293 + unsigned long rate);
294 extern void
295 qcom_pll_set_fsm_mode(struct regmap *m, u32 reg, u8 bias_count, u8 lock_count);
296 extern int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map,