@@ -260,6 +260,115 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
260
260
return 0 ;
261
261
}
262
262
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
+
263
372
static int clk_rcg2_determine_rate (struct clk_hw * hw ,
264
373
struct clk_rate_request * req )
265
374
{
@@ -276,6 +385,14 @@ static int clk_rcg2_determine_floor_rate(struct clk_hw *hw,
276
385
return _freq_tbl_determine_rate (hw , rcg -> freq_tbl , req , FLOOR );
277
386
}
278
387
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
+
279
396
static int __clk_rcg2_configure (struct clk_rcg2 * rcg , const struct freq_tbl * f ,
280
397
u32 * _cfg )
281
398
{
@@ -371,6 +488,30 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
371
488
return clk_rcg2_configure (rcg , f );
372
489
}
373
490
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
+
374
515
static int clk_rcg2_set_rate (struct clk_hw * hw , unsigned long rate ,
375
516
unsigned long parent_rate )
376
517
{
@@ -383,6 +524,12 @@ static int clk_rcg2_set_floor_rate(struct clk_hw *hw, unsigned long rate,
383
524
return __clk_rcg2_set_rate (hw , rate , FLOOR );
384
525
}
385
526
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
+
386
533
static int clk_rcg2_set_rate_and_parent (struct clk_hw * hw ,
387
534
unsigned long rate , unsigned long parent_rate , u8 index )
388
535
{
@@ -395,6 +542,12 @@ static int clk_rcg2_set_floor_rate_and_parent(struct clk_hw *hw,
395
542
return __clk_rcg2_set_rate (hw , rate , FLOOR );
396
543
}
397
544
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
+
398
551
static int clk_rcg2_get_duty_cycle (struct clk_hw * hw , struct clk_duty * duty )
399
552
{
400
553
struct clk_rcg2 * rcg = to_clk_rcg2 (hw );
@@ -505,6 +658,19 @@ const struct clk_ops clk_rcg2_floor_ops = {
505
658
};
506
659
EXPORT_SYMBOL_GPL (clk_rcg2_floor_ops );
507
660
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
+
508
674
const struct clk_ops clk_rcg2_mux_closest_ops = {
509
675
.determine_rate = __clk_mux_determine_rate_closest ,
510
676
.get_parent = clk_rcg2_get_parent ,
0 commit comments