@@ -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+
263372static 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+
279396static 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+
374515static 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+
386533static 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+
398551static 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};
506659EXPORT_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+
508674const struct clk_ops clk_rcg2_mux_closest_ops = {
509675 .determine_rate = __clk_mux_determine_rate_closest ,
510676 .get_parent = clk_rcg2_get_parent ,
0 commit comments