Skip to content

Commit 89da224

Browse files
Ansuelandersson
authored andcommitted
clk: qcom: clk-rcg2: add support for rcg2 freq multi ops
Some RCG frequency can be reached by multiple configuration. Add clk_rcg2_fm_ops ops to support these special RCG configurations. These alternative ops will select the frequency using a CEIL policy. When the correct frequency is found, the correct config is selected by calculating the final rate (by checking the defined parent and values in the config that is being checked) and deciding based on the one that is less different than the requested one. These check are skipped if there is just one config for the requested freq. qcom_find_freq_multi is added to search the freq with the new struct freq_multi_tbl. __clk_rcg2_select_conf is used to select the correct conf by simulating the final clock. If a conf can't be found due to parent not reachable, a WARN is printed and -EINVAL is returned. Tested-by: Wei Lei <[email protected]> Signed-off-by: Christian Marangi <[email protected]> Acked-by: Stephen Boyd <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Bjorn Andersson <[email protected]>
1 parent d06b104 commit 89da224

File tree

4 files changed

+187
-0
lines changed

4 files changed

+187
-0
lines changed

drivers/clk/qcom/clk-rcg.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ struct clk_rcg2_gfx3d {
190190

191191
extern const struct clk_ops clk_rcg2_ops;
192192
extern const struct clk_ops clk_rcg2_floor_ops;
193+
extern const struct clk_ops clk_rcg2_fm_ops;
193194
extern const struct clk_ops clk_rcg2_mux_closest_ops;
194195
extern const struct clk_ops clk_edp_pixel_ops;
195196
extern const struct clk_ops clk_byte_ops;

drivers/clk/qcom/clk-rcg2.c

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,115 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
260260
return 0;
261261
}
262262

263+
static const struct freq_conf *
264+
__clk_rcg2_select_conf(struct clk_hw *hw, const struct freq_multi_tbl *f,
265+
unsigned long req_rate)
266+
{
267+
unsigned long rate_diff, best_rate_diff = ULONG_MAX;
268+
const struct freq_conf *conf, *best_conf = NULL;
269+
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
270+
const char *name = clk_hw_get_name(hw);
271+
unsigned long parent_rate, rate;
272+
struct clk_hw *p;
273+
int index, i;
274+
275+
/* Exit early if only one config is defined */
276+
if (f->num_confs == 1) {
277+
best_conf = f->confs;
278+
goto exit;
279+
}
280+
281+
/* Search in each provided config the one that is near the wanted rate */
282+
for (i = 0, conf = f->confs; i < f->num_confs; i++, conf++) {
283+
index = qcom_find_src_index(hw, rcg->parent_map, conf->src);
284+
if (index < 0)
285+
continue;
286+
287+
p = clk_hw_get_parent_by_index(hw, index);
288+
if (!p)
289+
continue;
290+
291+
parent_rate = clk_hw_get_rate(p);
292+
rate = calc_rate(parent_rate, conf->n, conf->m, conf->n, conf->pre_div);
293+
294+
if (rate == req_rate) {
295+
best_conf = conf;
296+
goto exit;
297+
}
298+
299+
rate_diff = abs_diff(req_rate, rate);
300+
if (rate_diff < best_rate_diff) {
301+
best_rate_diff = rate_diff;
302+
best_conf = conf;
303+
}
304+
}
305+
306+
/*
307+
* Very unlikely. Warn if we couldn't find a correct config
308+
* due to parent not found in every config.
309+
*/
310+
if (unlikely(!best_conf)) {
311+
WARN(1, "%s: can't find a configuration for rate %lu\n",
312+
name, req_rate);
313+
return ERR_PTR(-EINVAL);
314+
}
315+
316+
exit:
317+
return best_conf;
318+
}
319+
320+
static int _freq_tbl_fm_determine_rate(struct clk_hw *hw, const struct freq_multi_tbl *f,
321+
struct clk_rate_request *req)
322+
{
323+
unsigned long clk_flags, rate = req->rate;
324+
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
325+
const struct freq_conf *conf;
326+
struct clk_hw *p;
327+
int index;
328+
329+
f = qcom_find_freq_multi(f, rate);
330+
if (!f || !f->confs)
331+
return -EINVAL;
332+
333+
conf = __clk_rcg2_select_conf(hw, f, rate);
334+
if (IS_ERR(conf))
335+
return PTR_ERR(conf);
336+
index = qcom_find_src_index(hw, rcg->parent_map, conf->src);
337+
if (index < 0)
338+
return index;
339+
340+
clk_flags = clk_hw_get_flags(hw);
341+
p = clk_hw_get_parent_by_index(hw, index);
342+
if (!p)
343+
return -EINVAL;
344+
345+
if (clk_flags & CLK_SET_RATE_PARENT) {
346+
rate = f->freq;
347+
if (conf->pre_div) {
348+
if (!rate)
349+
rate = req->rate;
350+
rate /= 2;
351+
rate *= conf->pre_div + 1;
352+
}
353+
354+
if (conf->n) {
355+
u64 tmp = rate;
356+
357+
tmp = tmp * conf->n;
358+
do_div(tmp, conf->m);
359+
rate = tmp;
360+
}
361+
} else {
362+
rate = clk_hw_get_rate(p);
363+
}
364+
365+
req->best_parent_hw = p;
366+
req->best_parent_rate = rate;
367+
req->rate = f->freq;
368+
369+
return 0;
370+
}
371+
263372
static int clk_rcg2_determine_rate(struct clk_hw *hw,
264373
struct clk_rate_request *req)
265374
{
@@ -276,6 +385,14 @@ static int clk_rcg2_determine_floor_rate(struct clk_hw *hw,
276385
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, FLOOR);
277386
}
278387

388+
static int clk_rcg2_fm_determine_rate(struct clk_hw *hw,
389+
struct clk_rate_request *req)
390+
{
391+
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
392+
393+
return _freq_tbl_fm_determine_rate(hw, rcg->freq_multi_tbl, req);
394+
}
395+
279396
static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f,
280397
u32 *_cfg)
281398
{
@@ -371,6 +488,30 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
371488
return clk_rcg2_configure(rcg, f);
372489
}
373490

491+
static int __clk_rcg2_fm_set_rate(struct clk_hw *hw, unsigned long rate)
492+
{
493+
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
494+
const struct freq_multi_tbl *f;
495+
const struct freq_conf *conf;
496+
struct freq_tbl f_tbl = {};
497+
498+
f = qcom_find_freq_multi(rcg->freq_multi_tbl, rate);
499+
if (!f || !f->confs)
500+
return -EINVAL;
501+
502+
conf = __clk_rcg2_select_conf(hw, f, rate);
503+
if (IS_ERR(conf))
504+
return PTR_ERR(conf);
505+
506+
f_tbl.freq = f->freq;
507+
f_tbl.src = conf->src;
508+
f_tbl.pre_div = conf->pre_div;
509+
f_tbl.m = conf->m;
510+
f_tbl.n = conf->n;
511+
512+
return clk_rcg2_configure(rcg, &f_tbl);
513+
}
514+
374515
static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
375516
unsigned long parent_rate)
376517
{
@@ -383,6 +524,12 @@ static int clk_rcg2_set_floor_rate(struct clk_hw *hw, unsigned long rate,
383524
return __clk_rcg2_set_rate(hw, rate, FLOOR);
384525
}
385526

527+
static int clk_rcg2_fm_set_rate(struct clk_hw *hw, unsigned long rate,
528+
unsigned long parent_rate)
529+
{
530+
return __clk_rcg2_fm_set_rate(hw, rate);
531+
}
532+
386533
static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw,
387534
unsigned long rate, unsigned long parent_rate, u8 index)
388535
{
@@ -395,6 +542,12 @@ static int clk_rcg2_set_floor_rate_and_parent(struct clk_hw *hw,
395542
return __clk_rcg2_set_rate(hw, rate, FLOOR);
396543
}
397544

545+
static int clk_rcg2_fm_set_rate_and_parent(struct clk_hw *hw,
546+
unsigned long rate, unsigned long parent_rate, u8 index)
547+
{
548+
return __clk_rcg2_fm_set_rate(hw, rate);
549+
}
550+
398551
static int clk_rcg2_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty)
399552
{
400553
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
@@ -505,6 +658,19 @@ const struct clk_ops clk_rcg2_floor_ops = {
505658
};
506659
EXPORT_SYMBOL_GPL(clk_rcg2_floor_ops);
507660

661+
const struct clk_ops clk_rcg2_fm_ops = {
662+
.is_enabled = clk_rcg2_is_enabled,
663+
.get_parent = clk_rcg2_get_parent,
664+
.set_parent = clk_rcg2_set_parent,
665+
.recalc_rate = clk_rcg2_recalc_rate,
666+
.determine_rate = clk_rcg2_fm_determine_rate,
667+
.set_rate = clk_rcg2_fm_set_rate,
668+
.set_rate_and_parent = clk_rcg2_fm_set_rate_and_parent,
669+
.get_duty_cycle = clk_rcg2_get_duty_cycle,
670+
.set_duty_cycle = clk_rcg2_set_duty_cycle,
671+
};
672+
EXPORT_SYMBOL_GPL(clk_rcg2_fm_ops);
673+
508674
const struct clk_ops clk_rcg2_mux_closest_ops = {
509675
.determine_rate = __clk_mux_determine_rate_closest,
510676
.get_parent = clk_rcg2_get_parent,

drivers/clk/qcom/common.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,24 @@ struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
4141
}
4242
EXPORT_SYMBOL_GPL(qcom_find_freq);
4343

44+
const struct freq_multi_tbl *qcom_find_freq_multi(const struct freq_multi_tbl *f,
45+
unsigned long rate)
46+
{
47+
if (!f)
48+
return NULL;
49+
50+
if (!f->freq)
51+
return f;
52+
53+
for (; f->freq; f++)
54+
if (rate <= f->freq)
55+
return f;
56+
57+
/* Default to our fastest rate */
58+
return f - 1;
59+
}
60+
EXPORT_SYMBOL_GPL(qcom_find_freq_multi);
61+
4462
const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
4563
unsigned long rate)
4664
{

drivers/clk/qcom/common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f,
4545
unsigned long rate);
4646
extern const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
4747
unsigned long rate);
48+
extern const struct freq_multi_tbl *qcom_find_freq_multi(const struct freq_multi_tbl *f,
49+
unsigned long rate);
4850
extern void
4951
qcom_pll_set_fsm_mode(struct regmap *m, u32 reg, u8 bias_count, u8 lock_count);
5052
extern int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map,

0 commit comments

Comments
 (0)