summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/gk20a/platform_gk20a_tegra.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nvgpu/gk20a/platform_gk20a_tegra.c')
-rw-r--r--drivers/gpu/nvgpu/gk20a/platform_gk20a_tegra.c74
1 files changed, 63 insertions, 11 deletions
diff --git a/drivers/gpu/nvgpu/gk20a/platform_gk20a_tegra.c b/drivers/gpu/nvgpu/gk20a/platform_gk20a_tegra.c
index 8609e3ae..fea2c774 100644
--- a/drivers/gpu/nvgpu/gk20a/platform_gk20a_tegra.c
+++ b/drivers/gpu/nvgpu/gk20a/platform_gk20a_tegra.c
@@ -25,6 +25,10 @@
25#include <linux/nvmap.h> 25#include <linux/nvmap.h>
26#include <linux/tegra_pm_domains.h> 26#include <linux/tegra_pm_domains.h>
27#include <linux/platform/tegra/clock.h> 27#include <linux/platform/tegra/clock.h>
28#include <linux/platform/tegra/dvfs.h>
29#include <linux/platform/tegra/common.h>
30#include <linux/clk/tegra.h>
31#include <mach/tegra_emc.h>
28 32
29#include "gk20a.h" 33#include "gk20a.h"
30#include "hal_gk20a.h" 34#include "hal_gk20a.h"
@@ -41,6 +45,7 @@ static struct gk20a_platform t132_gk20a_tegra_platform;
41 45
42struct gk20a_emc_params { 46struct gk20a_emc_params {
43 long bw_ratio; 47 long bw_ratio;
48 long freq_last_set;
44}; 49};
45 50
46#define MHZ_TO_HZ(x) ((x) * 1000000) 51#define MHZ_TO_HZ(x) ((x) * 1000000)
@@ -150,19 +155,26 @@ fail:
150 * This function returns the minimum emc clock based on gpu frequency 155 * This function returns the minimum emc clock based on gpu frequency
151 */ 156 */
152 157
153static long gk20a_tegra_get_emc_rate(struct gk20a *g, 158static unsigned long gk20a_tegra_get_emc_rate(struct gk20a *g,
154 struct gk20a_emc_params *emc_params, long freq) 159 struct gk20a_emc_params *emc_params)
155{ 160{
156 long hz; 161 unsigned long gpu_freq, gpu_fmax_at_vmin;
162 unsigned long emc_rate, emc_scale;
157 163
158 freq = HZ_TO_MHZ(freq); 164 gpu_freq = clk_get_rate(g->clk.tegra_clk);
165 gpu_fmax_at_vmin = tegra_dvfs_get_fmax_at_vmin_safe_t(
166 clk_get_parent(g->clk.tegra_clk));
159 167
160 hz = (freq * emc_params->bw_ratio); 168 /* When scaling emc, only account for the gpu load below fmax@vmin */
161 hz = (hz * min(g->pmu.load_avg, g->emc3d_ratio)) / 1000; 169 if (gpu_freq < gpu_fmax_at_vmin)
170 emc_scale = min(g->pmu.load_avg, g->emc3d_ratio);
171 else
172 emc_scale = g->emc3d_ratio;
162 173
163 hz = MHZ_TO_HZ(hz); 174 emc_rate =
175 (HZ_TO_MHZ(gpu_freq) * emc_params->bw_ratio * emc_scale) / 1000;
164 176
165 return hz; 177 return MHZ_TO_HZ(emc_rate);
166} 178}
167 179
168/* 180/*
@@ -178,11 +190,50 @@ static void gk20a_tegra_postscale(struct platform_device *pdev,
178 struct gk20a_scale_profile *profile = platform->g->scale_profile; 190 struct gk20a_scale_profile *profile = platform->g->scale_profile;
179 struct gk20a_emc_params *emc_params = profile->private_data; 191 struct gk20a_emc_params *emc_params = profile->private_data;
180 struct gk20a *g = get_gk20a(pdev); 192 struct gk20a *g = get_gk20a(pdev);
193 struct clk *emc_clk = platform->clk[2];
194 enum tegra_chipid chip_id = tegra_get_chip_id();
195 unsigned long emc_target;
196 long emc_freq_lower, emc_freq_upper, emc_freq_rounded;
181 197
182 long after = clk_get_rate(g->clk.tegra_clk); 198 emc_target = gk20a_tegra_get_emc_rate(g, emc_params);
183 long emc_target = gk20a_tegra_get_emc_rate(g, emc_params, after);
184 199
185 clk_set_rate(platform->clk[2], emc_target); 200 switch (chip_id) {
201 case TEGRA_CHIPID_TEGRA12:
202 case TEGRA_CHIPID_TEGRA13:
203 /* T124 and T132 don't apply any rounding. The resulting
204 * emc frequency gets implicitly rounded up after issuing
205 * the clock_set_request.
206 * So explicitly round up the emc target here to achieve
207 * the same outcome. */
208 emc_freq_rounded =
209 tegra_emc_round_rate_updown(emc_target, true);
210 break;
211
212 case TEGRA_CHIPID_TEGRA21:
213 emc_freq_lower = tegra_emc_round_rate_updown(emc_target, false);
214 emc_freq_upper = tegra_emc_round_rate_updown(emc_target, true);
215
216 /* round to the nearest frequency step */
217 if (emc_target < (emc_freq_lower + emc_freq_upper) / 2)
218 emc_freq_rounded = emc_freq_lower;
219 else
220 emc_freq_rounded = emc_freq_upper;
221 break;
222
223 case TEGRA_CHIPID_UNKNOWN:
224 default:
225 /* a proper rounding function needs to be implemented
226 * for emc in t18x */
227 emc_freq_rounded = clk_round_rate(emc_clk, emc_target);
228 break;
229 }
230
231 /* only change the emc clock if new rounded frequency is different
232 * from previously set emc rate */
233 if (emc_freq_rounded != emc_params->freq_last_set) {
234 clk_set_rate(emc_clk, emc_freq_rounded);
235 emc_params->freq_last_set = emc_freq_rounded;
236 }
186} 237}
187 238
188/* 239/*
@@ -384,6 +435,7 @@ static void gk20a_tegra_scale_init(struct platform_device *pdev)
384 if (!emc_params) 435 if (!emc_params)
385 return; 436 return;
386 437
438 emc_params->freq_last_set = -1;
387 gk20a_tegra_calibrate_emc(pdev, emc_params); 439 gk20a_tegra_calibrate_emc(pdev, emc_params);
388 440
389 profile->private_data = emc_params; 441 profile->private_data = emc_params;