1212#include <linux/of_platform.h>
1313#include <linux/platform_device.h>
1414#include <linux/slab.h>
15+ #include <linux/units.h>
1516
1617#include <asm/smp_plat.h>
1718
@@ -65,12 +66,36 @@ struct tegra_cpufreq_soc {
6566
6667struct tegra194_cpufreq_data {
6768 void __iomem * regs ;
68- struct cpufreq_frequency_table * * tables ;
69+ struct cpufreq_frequency_table * * bpmp_luts ;
6970 const struct tegra_cpufreq_soc * soc ;
71+ bool icc_dram_bw_scaling ;
7072};
7173
7274static struct workqueue_struct * read_counters_wq ;
7375
76+ static int tegra_cpufreq_set_bw (struct cpufreq_policy * policy , unsigned long freq_khz )
77+ {
78+ struct tegra194_cpufreq_data * data = cpufreq_get_driver_data ();
79+ struct dev_pm_opp * opp ;
80+ struct device * dev ;
81+ int ret ;
82+
83+ dev = get_cpu_device (policy -> cpu );
84+ if (!dev )
85+ return - ENODEV ;
86+
87+ opp = dev_pm_opp_find_freq_exact (dev , freq_khz * KHZ , true);
88+ if (IS_ERR (opp ))
89+ return PTR_ERR (opp );
90+
91+ ret = dev_pm_opp_set_opp (dev , opp );
92+ if (ret )
93+ data -> icc_dram_bw_scaling = false;
94+
95+ dev_pm_opp_put (opp );
96+ return ret ;
97+ }
98+
7499static void tegra_get_cpu_mpidr (void * mpidr )
75100{
76101 * ((u64 * )mpidr ) = read_cpuid_mpidr () & MPIDR_HWID_BITMASK ;
@@ -354,7 +379,7 @@ static unsigned int tegra194_get_speed(u32 cpu)
354379 * to the last written ndiv value from freq_table. This is
355380 * done to return consistent value.
356381 */
357- cpufreq_for_each_valid_entry (pos , data -> tables [clusterid ]) {
382+ cpufreq_for_each_valid_entry (pos , data -> bpmp_luts [clusterid ]) {
358383 if (pos -> driver_data != ndiv )
359384 continue ;
360385
@@ -369,16 +394,93 @@ static unsigned int tegra194_get_speed(u32 cpu)
369394 return rate ;
370395}
371396
397+ static int tegra_cpufreq_init_cpufreq_table (struct cpufreq_policy * policy ,
398+ struct cpufreq_frequency_table * bpmp_lut ,
399+ struct cpufreq_frequency_table * * opp_table )
400+ {
401+ struct tegra194_cpufreq_data * data = cpufreq_get_driver_data ();
402+ struct cpufreq_frequency_table * freq_table = NULL ;
403+ struct cpufreq_frequency_table * pos ;
404+ struct device * cpu_dev ;
405+ struct dev_pm_opp * opp ;
406+ unsigned long rate ;
407+ int ret , max_opps ;
408+ int j = 0 ;
409+
410+ cpu_dev = get_cpu_device (policy -> cpu );
411+ if (!cpu_dev ) {
412+ pr_err ("%s: failed to get cpu%d device\n" , __func__ , policy -> cpu );
413+ return - ENODEV ;
414+ }
415+
416+ /* Initialize OPP table mentioned in operating-points-v2 property in DT */
417+ ret = dev_pm_opp_of_add_table_indexed (cpu_dev , 0 );
418+ if (!ret ) {
419+ max_opps = dev_pm_opp_get_opp_count (cpu_dev );
420+ if (max_opps <= 0 ) {
421+ dev_err (cpu_dev , "Failed to add OPPs\n" );
422+ return max_opps ;
423+ }
424+
425+ /* Disable all opps and cross-validate against LUT later */
426+ for (rate = 0 ; ; rate ++ ) {
427+ opp = dev_pm_opp_find_freq_ceil (cpu_dev , & rate );
428+ if (IS_ERR (opp ))
429+ break ;
430+
431+ dev_pm_opp_put (opp );
432+ dev_pm_opp_disable (cpu_dev , rate );
433+ }
434+ } else {
435+ dev_err (cpu_dev , "Invalid or empty opp table in device tree\n" );
436+ data -> icc_dram_bw_scaling = false;
437+ return ret ;
438+ }
439+
440+ freq_table = kcalloc ((max_opps + 1 ), sizeof (* freq_table ), GFP_KERNEL );
441+ if (!freq_table )
442+ return - ENOMEM ;
443+
444+ /*
445+ * Cross check the frequencies from BPMP-FW LUT against the OPP's present in DT.
446+ * Enable only those DT OPP's which are present in LUT also.
447+ */
448+ cpufreq_for_each_valid_entry (pos , bpmp_lut ) {
449+ opp = dev_pm_opp_find_freq_exact (cpu_dev , pos -> frequency * KHZ , false);
450+ if (IS_ERR (opp ))
451+ continue ;
452+
453+ ret = dev_pm_opp_enable (cpu_dev , pos -> frequency * KHZ );
454+ if (ret < 0 )
455+ return ret ;
456+
457+ freq_table [j ].driver_data = pos -> driver_data ;
458+ freq_table [j ].frequency = pos -> frequency ;
459+ j ++ ;
460+ }
461+
462+ freq_table [j ].driver_data = pos -> driver_data ;
463+ freq_table [j ].frequency = CPUFREQ_TABLE_END ;
464+
465+ * opp_table = & freq_table [0 ];
466+
467+ dev_pm_opp_set_sharing_cpus (cpu_dev , policy -> cpus );
468+
469+ return ret ;
470+ }
471+
372472static int tegra194_cpufreq_init (struct cpufreq_policy * policy )
373473{
374474 struct tegra194_cpufreq_data * data = cpufreq_get_driver_data ();
375475 int maxcpus_per_cluster = data -> soc -> maxcpus_per_cluster ;
476+ struct cpufreq_frequency_table * freq_table ;
477+ struct cpufreq_frequency_table * bpmp_lut ;
376478 u32 start_cpu , cpu ;
377479 u32 clusterid ;
480+ int ret ;
378481
379482 data -> soc -> ops -> get_cpu_cluster_id (policy -> cpu , NULL , & clusterid );
380-
381- if (clusterid >= data -> soc -> num_clusters || !data -> tables [clusterid ])
483+ if (clusterid >= data -> soc -> num_clusters || !data -> bpmp_luts [clusterid ])
382484 return - EINVAL ;
383485
384486 start_cpu = rounddown (policy -> cpu , maxcpus_per_cluster );
@@ -387,9 +489,22 @@ static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
387489 if (cpu_possible (cpu ))
388490 cpumask_set_cpu (cpu , policy -> cpus );
389491 }
390- policy -> freq_table = data -> tables [clusterid ];
391492 policy -> cpuinfo .transition_latency = TEGRA_CPUFREQ_TRANSITION_LATENCY ;
392493
494+ bpmp_lut = data -> bpmp_luts [clusterid ];
495+
496+ if (data -> icc_dram_bw_scaling ) {
497+ ret = tegra_cpufreq_init_cpufreq_table (policy , bpmp_lut , & freq_table );
498+ if (!ret ) {
499+ policy -> freq_table = freq_table ;
500+ return 0 ;
501+ }
502+ }
503+
504+ data -> icc_dram_bw_scaling = false;
505+ policy -> freq_table = bpmp_lut ;
506+ pr_info ("OPP tables missing from DT, EMC frequency scaling disabled\n" );
507+
393508 return 0 ;
394509}
395510
@@ -406,6 +521,9 @@ static int tegra194_cpufreq_set_target(struct cpufreq_policy *policy,
406521 */
407522 data -> soc -> ops -> set_cpu_ndiv (policy , (u64 )tbl -> driver_data );
408523
524+ if (data -> icc_dram_bw_scaling )
525+ tegra_cpufreq_set_bw (policy , tbl -> frequency );
526+
409527 return 0 ;
410528}
411529
@@ -439,8 +557,8 @@ static void tegra194_cpufreq_free_resources(void)
439557}
440558
441559static struct cpufreq_frequency_table *
442- init_freq_table (struct platform_device * pdev , struct tegra_bpmp * bpmp ,
443- unsigned int cluster_id )
560+ tegra_cpufreq_bpmp_read_lut (struct platform_device * pdev , struct tegra_bpmp * bpmp ,
561+ unsigned int cluster_id )
444562{
445563 struct cpufreq_frequency_table * freq_table ;
446564 struct mrq_cpu_ndiv_limits_response resp ;
@@ -515,6 +633,7 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
515633 const struct tegra_cpufreq_soc * soc ;
516634 struct tegra194_cpufreq_data * data ;
517635 struct tegra_bpmp * bpmp ;
636+ struct device * cpu_dev ;
518637 int err , i ;
519638
520639 data = devm_kzalloc (& pdev -> dev , sizeof (* data ), GFP_KERNEL );
@@ -530,9 +649,9 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
530649 return - EINVAL ;
531650 }
532651
533- data -> tables = devm_kcalloc (& pdev -> dev , data -> soc -> num_clusters ,
534- sizeof (* data -> tables ), GFP_KERNEL );
535- if (!data -> tables )
652+ data -> bpmp_luts = devm_kcalloc (& pdev -> dev , data -> soc -> num_clusters ,
653+ sizeof (* data -> bpmp_luts ), GFP_KERNEL );
654+ if (!data -> bpmp_luts )
536655 return - ENOMEM ;
537656
538657 if (soc -> actmon_cntr_base ) {
@@ -556,15 +675,26 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
556675 }
557676
558677 for (i = 0 ; i < data -> soc -> num_clusters ; i ++ ) {
559- data -> tables [i ] = init_freq_table (pdev , bpmp , i );
560- if (IS_ERR (data -> tables [i ])) {
561- err = PTR_ERR (data -> tables [i ]);
678+ data -> bpmp_luts [i ] = tegra_cpufreq_bpmp_read_lut (pdev , bpmp , i );
679+ if (IS_ERR (data -> bpmp_luts [i ])) {
680+ err = PTR_ERR (data -> bpmp_luts [i ]);
562681 goto err_free_res ;
563682 }
564683 }
565684
566685 tegra194_cpufreq_driver .driver_data = data ;
567686
687+ /* Check for optional OPPv2 and interconnect paths on CPU0 to enable ICC scaling */
688+ cpu_dev = get_cpu_device (0 );
689+ if (!cpu_dev )
690+ return - EPROBE_DEFER ;
691+
692+ if (dev_pm_opp_of_get_opp_desc_node (cpu_dev )) {
693+ err = dev_pm_opp_of_find_icc_paths (cpu_dev , NULL );
694+ if (!err )
695+ data -> icc_dram_bw_scaling = true;
696+ }
697+
568698 err = cpufreq_register_driver (& tegra194_cpufreq_driver );
569699 if (!err )
570700 goto put_bpmp ;
0 commit comments