88#include <linux/module.h>
99#include <linux/of.h>
1010#include <linux/platform_device.h>
11+ #include <linux/units.h>
1112
1213#include <soc/tegra/bpmp.h>
1314#include <soc/tegra/bpmp-abi.h>
@@ -58,24 +59,127 @@ static const struct tegra186_cpufreq_cpu tegra186_cpus[] = {
5859};
5960
6061struct tegra186_cpufreq_cluster {
61- struct cpufreq_frequency_table * table ;
62+ struct cpufreq_frequency_table * bpmp_lut ;
6263 u32 ref_clk_khz ;
6364 u32 div ;
6465};
6566
6667struct tegra186_cpufreq_data {
6768 void __iomem * regs ;
6869 const struct tegra186_cpufreq_cpu * cpus ;
70+ bool icc_dram_bw_scaling ;
6971 struct tegra186_cpufreq_cluster clusters [];
7072};
7173
74+ static int tegra_cpufreq_set_bw (struct cpufreq_policy * policy , unsigned long freq_khz )
75+ {
76+ struct tegra186_cpufreq_data * data = cpufreq_get_driver_data ();
77+ struct device * dev ;
78+ int ret ;
79+
80+ dev = get_cpu_device (policy -> cpu );
81+ if (!dev )
82+ return - ENODEV ;
83+
84+ struct dev_pm_opp * opp __free (put_opp ) =
85+ dev_pm_opp_find_freq_exact (dev , freq_khz * HZ_PER_KHZ , true);
86+ if (IS_ERR (opp ))
87+ return PTR_ERR (opp );
88+
89+ ret = dev_pm_opp_set_opp (dev , opp );
90+ if (ret )
91+ data -> icc_dram_bw_scaling = false;
92+
93+ return ret ;
94+ }
95+
96+ static int tegra_cpufreq_init_cpufreq_table (struct cpufreq_policy * policy ,
97+ struct cpufreq_frequency_table * bpmp_lut ,
98+ struct cpufreq_frequency_table * * opp_table )
99+ {
100+ struct tegra186_cpufreq_data * data = cpufreq_get_driver_data ();
101+ struct cpufreq_frequency_table * freq_table = NULL ;
102+ struct cpufreq_frequency_table * pos ;
103+ struct device * cpu_dev ;
104+ unsigned long rate ;
105+ int ret , max_opps ;
106+ int j = 0 ;
107+
108+ cpu_dev = get_cpu_device (policy -> cpu );
109+ if (!cpu_dev ) {
110+ pr_err ("%s: failed to get cpu%d device\n" , __func__ , policy -> cpu );
111+ return - ENODEV ;
112+ }
113+
114+ /* Initialize OPP table mentioned in operating-points-v2 property in DT */
115+ ret = dev_pm_opp_of_add_table_indexed (cpu_dev , 0 );
116+ if (ret ) {
117+ dev_err (cpu_dev , "Invalid or empty opp table in device tree\n" );
118+ data -> icc_dram_bw_scaling = false;
119+ return ret ;
120+ }
121+
122+ max_opps = dev_pm_opp_get_opp_count (cpu_dev );
123+ if (max_opps <= 0 ) {
124+ dev_err (cpu_dev , "Failed to add OPPs\n" );
125+ return max_opps ;
126+ }
127+
128+ /* Disable all opps and cross-validate against LUT later */
129+ for (rate = 0 ; ; rate ++ ) {
130+ struct dev_pm_opp * opp __free (put_opp ) =
131+ dev_pm_opp_find_freq_ceil (cpu_dev , & rate );
132+ if (IS_ERR (opp ))
133+ break ;
134+
135+ dev_pm_opp_disable (cpu_dev , rate );
136+ }
137+
138+ freq_table = kcalloc ((max_opps + 1 ), sizeof (* freq_table ), GFP_KERNEL );
139+ if (!freq_table )
140+ return - ENOMEM ;
141+
142+ /*
143+ * Cross check the frequencies from BPMP-FW LUT against the OPP's present in DT.
144+ * Enable only those DT OPP's which are present in LUT also.
145+ */
146+ cpufreq_for_each_valid_entry (pos , bpmp_lut ) {
147+ struct dev_pm_opp * opp __free (put_opp ) =
148+ dev_pm_opp_find_freq_exact (cpu_dev , pos -> frequency * HZ_PER_KHZ , false);
149+ if (IS_ERR (opp ))
150+ continue ;
151+
152+ ret = dev_pm_opp_enable (cpu_dev , pos -> frequency * HZ_PER_KHZ );
153+ if (ret < 0 )
154+ return ret ;
155+
156+ freq_table [j ].driver_data = pos -> driver_data ;
157+ freq_table [j ].frequency = pos -> frequency ;
158+ j ++ ;
159+ }
160+
161+ freq_table [j ].driver_data = pos -> driver_data ;
162+ freq_table [j ].frequency = CPUFREQ_TABLE_END ;
163+
164+ * opp_table = & freq_table [0 ];
165+
166+ dev_pm_opp_set_sharing_cpus (cpu_dev , policy -> cpus );
167+
168+ /* Prime interconnect data */
169+ tegra_cpufreq_set_bw (policy , freq_table [j - 1 ].frequency );
170+
171+ return ret ;
172+ }
173+
72174static int tegra186_cpufreq_init (struct cpufreq_policy * policy )
73175{
74176 struct tegra186_cpufreq_data * data = cpufreq_get_driver_data ();
75177 unsigned int cluster = data -> cpus [policy -> cpu ].bpmp_cluster_id ;
178+ struct cpufreq_frequency_table * freq_table ;
179+ struct cpufreq_frequency_table * bpmp_lut ;
76180 u32 cpu ;
181+ int ret ;
77182
78- policy -> freq_table = data -> clusters [cluster ].table ;
79183 policy -> cpuinfo .transition_latency = 300 * 1000 ;
80184 policy -> driver_data = NULL ;
81185
@@ -85,6 +189,20 @@ static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
85189 cpumask_set_cpu (cpu , policy -> cpus );
86190 }
87191
192+ bpmp_lut = data -> clusters [cluster ].bpmp_lut ;
193+
194+ if (data -> icc_dram_bw_scaling ) {
195+ ret = tegra_cpufreq_init_cpufreq_table (policy , bpmp_lut , & freq_table );
196+ if (!ret ) {
197+ policy -> freq_table = freq_table ;
198+ return 0 ;
199+ }
200+ }
201+
202+ data -> icc_dram_bw_scaling = false;
203+ policy -> freq_table = bpmp_lut ;
204+ pr_info ("OPP tables missing from DT, EMC frequency scaling disabled\n" );
205+
88206 return 0 ;
89207}
90208
@@ -102,6 +220,10 @@ static int tegra186_cpufreq_set_target(struct cpufreq_policy *policy,
102220 writel (edvd_val , data -> regs + edvd_offset );
103221 }
104222
223+ if (data -> icc_dram_bw_scaling )
224+ tegra_cpufreq_set_bw (policy , tbl -> frequency );
225+
226+
105227 return 0 ;
106228}
107229
@@ -134,7 +256,7 @@ static struct cpufreq_driver tegra186_cpufreq_driver = {
134256 .init = tegra186_cpufreq_init ,
135257};
136258
137- static struct cpufreq_frequency_table * init_vhint_table (
259+ static struct cpufreq_frequency_table * tegra_cpufreq_bpmp_read_lut (
138260 struct platform_device * pdev , struct tegra_bpmp * bpmp ,
139261 struct tegra186_cpufreq_cluster * cluster , unsigned int cluster_id ,
140262 int * num_rates )
@@ -229,6 +351,7 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
229351{
230352 struct tegra186_cpufreq_data * data ;
231353 struct tegra_bpmp * bpmp ;
354+ struct device * cpu_dev ;
232355 unsigned int i = 0 , err , edvd_offset ;
233356 int num_rates = 0 ;
234357 u32 edvd_val , cpu ;
@@ -254,9 +377,9 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
254377 for (i = 0 ; i < TEGRA186_NUM_CLUSTERS ; i ++ ) {
255378 struct tegra186_cpufreq_cluster * cluster = & data -> clusters [i ];
256379
257- cluster -> table = init_vhint_table (pdev , bpmp , cluster , i , & num_rates );
258- if (IS_ERR (cluster -> table )) {
259- err = PTR_ERR (cluster -> table );
380+ cluster -> bpmp_lut = tegra_cpufreq_bpmp_read_lut (pdev , bpmp , cluster , i , & num_rates );
381+ if (IS_ERR (cluster -> bpmp_lut )) {
382+ err = PTR_ERR (cluster -> bpmp_lut );
260383 goto put_bpmp ;
261384 } else if (!num_rates ) {
262385 err = - EINVAL ;
@@ -265,7 +388,7 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
265388
266389 for (cpu = 0 ; cpu < ARRAY_SIZE (tegra186_cpus ); cpu ++ ) {
267390 if (data -> cpus [cpu ].bpmp_cluster_id == i ) {
268- edvd_val = cluster -> table [num_rates - 1 ].driver_data ;
391+ edvd_val = cluster -> bpmp_lut [num_rates - 1 ].driver_data ;
269392 edvd_offset = data -> cpus [cpu ].edvd_offset ;
270393 writel (edvd_val , data -> regs + edvd_offset );
271394 }
@@ -274,6 +397,19 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
274397
275398 tegra186_cpufreq_driver .driver_data = data ;
276399
400+ /* Check for optional OPPv2 and interconnect paths on CPU0 to enable ICC scaling */
401+ cpu_dev = get_cpu_device (0 );
402+ if (!cpu_dev ) {
403+ err = - EPROBE_DEFER ;
404+ goto put_bpmp ;
405+ }
406+
407+ if (dev_pm_opp_of_get_opp_desc_node (cpu_dev )) {
408+ err = dev_pm_opp_of_find_icc_paths (cpu_dev , NULL );
409+ if (!err )
410+ data -> icc_dram_bw_scaling = true;
411+ }
412+
277413 err = cpufreq_register_driver (& tegra186_cpufreq_driver );
278414
279415put_bpmp :
0 commit comments