summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTerje Bergstrom <tbergstrom@nvidia.com>2017-04-28 13:59:17 -0400
committermobile promotions <svcmobile_promotions@nvidia.com>2017-05-12 13:19:58 -0400
commit72ded891014b18c9afffe44ea3013161339894de (patch)
treea8ef71a44c060081a230e0a20a9a5b4df81dc79a
parenta8cf64019f74dd05626a911af3df079efd9c7c89 (diff)
gpu: nvgpu: Remove TCF & gk20a clock support
Remove support for legacy Tegra Clock Framework. gk20a clocks were implemented only for TCF, so delete that at the same time, too. Change-Id: Ia46254f5113aa3ec392bf38b33d288a55b2fb475 Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com> Reviewed-on: http://git-master/r/1480213 Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
-rw-r--r--drivers/gpu/nvgpu/Makefile.nvgpu5
-rw-r--r--drivers/gpu/nvgpu/gk20a/clk_gk20a.c841
2 files changed, 1 insertions, 845 deletions
diff --git a/drivers/gpu/nvgpu/Makefile.nvgpu b/drivers/gpu/nvgpu/Makefile.nvgpu
index 411e06cc..82c9fec9 100644
--- a/drivers/gpu/nvgpu/Makefile.nvgpu
+++ b/drivers/gpu/nvgpu/Makefile.nvgpu
@@ -132,13 +132,10 @@ nvgpu-$(CONFIG_TEGRA_GR_VIRTUALIZATION) += \
132 vgpu/gm20b/vgpu_gr_gm20b.o \ 132 vgpu/gm20b/vgpu_gr_gm20b.o \
133 vgpu/sysfs_vgpu.o 133 vgpu/sysfs_vgpu.o
134 134
135nvgpu-$(CONFIG_TEGRA_CLK_FRAMEWORK) += gk20a/clk_gk20a.o 135nvgpu-$(CONFIG_COMMON_CLK) += \
136ifneq (,$filter y,$(CONFIG_TEGRA_CLK_FRAMEWORK),$(CONFIG_COMMON_CLK))
137nvgpu-y += \
138 tegra/linux/clk.o \ 136 tegra/linux/clk.o \
139 clk/clk_common.o \ 137 clk/clk_common.o \
140 gm20b/clk_gm20b.o 138 gm20b/clk_gm20b.o
141endif
142 139
143nvgpu-$(CONFIG_GK20A_DEVFREQ) += \ 140nvgpu-$(CONFIG_GK20A_DEVFREQ) += \
144 gk20a/gk20a_scale.o 141 gk20a/gk20a_scale.o
diff --git a/drivers/gpu/nvgpu/gk20a/clk_gk20a.c b/drivers/gpu/nvgpu/gk20a/clk_gk20a.c
deleted file mode 100644
index e904be49..00000000
--- a/drivers/gpu/nvgpu/gk20a/clk_gk20a.c
+++ /dev/null
@@ -1,841 +0,0 @@
1/*
2 * GK20A Clocks
3 *
4 * Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <linux/clk.h>
20#include <linux/module.h>
21#ifdef CONFIG_DEBUG_FS
22#include <linux/debugfs.h>
23#endif
24#include <linux/clk/tegra.h>
25
26#include "gk20a.h"
27
28#include <nvgpu/log.h>
29#include <nvgpu/soc.h>
30#include <nvgpu/timers.h>
31#include <nvgpu/bug.h>
32
33#include <nvgpu/hw/gk20a/hw_trim_gk20a.h>
34#include <nvgpu/hw/gk20a/hw_timer_gk20a.h>
35
36#define gk20a_dbg_clk(fmt, arg...) \
37 gk20a_dbg(gpu_dbg_clk, fmt, ##arg)
38
39/* from vbios PLL info table */
40static struct pll_parms gpc_pll_params = {
41 144000, 2064000, /* freq */
42 1000000, 2064000, /* vco */
43 12000, 38000, /* u */
44 1, 255, /* M */
45 8, 255, /* N */
46 1, 32, /* PL */
47 0, 0, 0, 0, 0, /* NA mode parameters: not supported on GK20A */
48 500, /* Locking and ramping timeout */
49 0, /* NA mode lock delay: not supported on GK20A */
50 2, /* IDDQ mode exit delay */
51};
52
53#ifdef CONFIG_DEBUG_FS
54static int clk_gk20a_debugfs_init(struct gk20a *g);
55#endif
56
57static u8 pl_to_div[] = {
58/* PL: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */
59/* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32 };
60
61/* Calculate and update M/N/PL as well as pll->freq
62 ref_clk_f = clk_in_f / src_div = clk_in_f; (src_div = 1 on gk20a)
63 u_f = ref_clk_f / M;
64 PLL output = vco_f = u_f * N = ref_clk_f * N / M;
65 gpc2clk = target clock frequency = vco_f / PL;
66 gpcclk = gpc2clk / 2; */
67static int clk_config_pll(struct clk_gk20a *clk, struct pll *pll,
68 struct pll_parms *pll_params, u32 *target_freq, bool best_fit)
69{
70 u32 min_vco_f, max_vco_f;
71 u32 best_M, best_N;
72 u32 low_PL, high_PL, best_PL;
73 u32 m, n, n2;
74 u32 target_vco_f, vco_f;
75 u32 ref_clk_f, target_clk_f, u_f;
76 u32 delta, lwv, best_delta = ~0;
77 unsigned int pl;
78
79 BUG_ON(target_freq == NULL);
80
81 gk20a_dbg_fn("request target freq %d MHz", *target_freq);
82
83 ref_clk_f = pll->clk_in;
84 target_clk_f = *target_freq;
85 max_vco_f = pll_params->max_vco;
86 min_vco_f = pll_params->min_vco;
87 best_M = pll_params->max_M;
88 best_N = pll_params->min_N;
89 best_PL = pll_params->min_PL;
90
91 target_vco_f = target_clk_f + target_clk_f / 50;
92 if (max_vco_f < target_vco_f)
93 max_vco_f = target_vco_f;
94
95 high_PL = (max_vco_f + target_vco_f - 1) / target_vco_f;
96 high_PL = min(high_PL, pll_params->max_PL);
97 high_PL = max(high_PL, pll_params->min_PL);
98
99 low_PL = min_vco_f / target_vco_f;
100 low_PL = min(low_PL, pll_params->max_PL);
101 low_PL = max(low_PL, pll_params->min_PL);
102
103 /* Find Indices of high_PL and low_PL */
104 for (pl = 0; pl < 14; pl++) {
105 if (pl_to_div[pl] >= low_PL) {
106 low_PL = pl;
107 break;
108 }
109 }
110 for (pl = 0; pl < 14; pl++) {
111 if (pl_to_div[pl] >= high_PL) {
112 high_PL = pl;
113 break;
114 }
115 }
116 gk20a_dbg_info("low_PL %d(div%d), high_PL %d(div%d)",
117 low_PL, pl_to_div[low_PL], high_PL, pl_to_div[high_PL]);
118
119 for (pl = low_PL; pl <= high_PL; pl++) {
120 target_vco_f = target_clk_f * pl_to_div[pl];
121
122 for (m = pll_params->min_M; m <= pll_params->max_M; m++) {
123 u_f = ref_clk_f / m;
124
125 if (u_f < pll_params->min_u)
126 break;
127 if (u_f > pll_params->max_u)
128 continue;
129
130 n = (target_vco_f * m) / ref_clk_f;
131 n2 = ((target_vco_f * m) + (ref_clk_f - 1)) / ref_clk_f;
132
133 if (n > pll_params->max_N)
134 break;
135
136 for (; n <= n2; n++) {
137 if (n < pll_params->min_N)
138 continue;
139 if (n > pll_params->max_N)
140 break;
141
142 vco_f = ref_clk_f * n / m;
143
144 if (vco_f >= min_vco_f && vco_f <= max_vco_f) {
145 lwv = (vco_f + (pl_to_div[pl] / 2))
146 / pl_to_div[pl];
147 delta = abs(lwv - target_clk_f);
148
149 if (delta < best_delta) {
150 best_delta = delta;
151 best_M = m;
152 best_N = n;
153 best_PL = pl;
154
155 if (best_delta == 0 ||
156 /* 0.45% for non best fit */
157 (!best_fit && (vco_f / best_delta > 218))) {
158 goto found_match;
159 }
160
161 gk20a_dbg_info("delta %d @ M %d, N %d, PL %d",
162 delta, m, n, pl);
163 }
164 }
165 }
166 }
167 }
168
169found_match:
170 BUG_ON(best_delta == ~0U);
171
172 if (best_fit && best_delta != 0)
173 gk20a_dbg_clk("no best match for target @ %dMHz on gpc_pll",
174 target_clk_f);
175
176 pll->M = best_M;
177 pll->N = best_N;
178 pll->PL = best_PL;
179
180 /* save current frequency */
181 pll->freq = ref_clk_f * pll->N / (pll->M * pl_to_div[pll->PL]);
182
183 *target_freq = pll->freq;
184
185 gk20a_dbg_clk("actual target freq %d MHz, M %d, N %d, PL %d(div%d)",
186 *target_freq, pll->M, pll->N, pll->PL, pl_to_div[pll->PL]);
187
188 gk20a_dbg_fn("done");
189
190 return 0;
191}
192
193static int clk_slide_gpc_pll(struct gk20a *g, u32 n)
194{
195 u32 data, coeff;
196 u32 nold;
197 int ramp_timeout = gpc_pll_params.lock_timeout;
198
199 /* get old coefficients */
200 coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r());
201 nold = trim_sys_gpcpll_coeff_ndiv_v(coeff);
202
203 /* do nothing if NDIV is same */
204 if (n == nold)
205 return 0;
206
207 /* setup */
208 data = gk20a_readl(g, trim_sys_gpcpll_cfg2_r());
209 data = set_field(data, trim_sys_gpcpll_cfg2_pll_stepa_m(),
210 trim_sys_gpcpll_cfg2_pll_stepa_f(0x2b));
211 gk20a_writel(g, trim_sys_gpcpll_cfg2_r(), data);
212 data = gk20a_readl(g, trim_sys_gpcpll_cfg3_r());
213 data = set_field(data, trim_sys_gpcpll_cfg3_pll_stepb_m(),
214 trim_sys_gpcpll_cfg3_pll_stepb_f(0xb));
215 gk20a_writel(g, trim_sys_gpcpll_cfg3_r(), data);
216
217 /* pll slowdown mode */
218 data = gk20a_readl(g, trim_sys_gpcpll_ndiv_slowdown_r());
219 data = set_field(data,
220 trim_sys_gpcpll_ndiv_slowdown_slowdown_using_pll_m(),
221 trim_sys_gpcpll_ndiv_slowdown_slowdown_using_pll_yes_f());
222 gk20a_writel(g, trim_sys_gpcpll_ndiv_slowdown_r(), data);
223
224 /* new ndiv ready for ramp */
225 coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r());
226 coeff = set_field(coeff, trim_sys_gpcpll_coeff_ndiv_m(),
227 trim_sys_gpcpll_coeff_ndiv_f(n));
228 nvgpu_udelay(1);
229 gk20a_writel(g, trim_sys_gpcpll_coeff_r(), coeff);
230
231 /* dynamic ramp to new ndiv */
232 data = gk20a_readl(g, trim_sys_gpcpll_ndiv_slowdown_r());
233 data = set_field(data,
234 trim_sys_gpcpll_ndiv_slowdown_en_dynramp_m(),
235 trim_sys_gpcpll_ndiv_slowdown_en_dynramp_yes_f());
236 nvgpu_udelay(1);
237 gk20a_writel(g, trim_sys_gpcpll_ndiv_slowdown_r(), data);
238
239 do {
240 nvgpu_udelay(1);
241 ramp_timeout--;
242 data = gk20a_readl(
243 g, trim_gpc_bcast_gpcpll_ndiv_slowdown_debug_r());
244 if (trim_gpc_bcast_gpcpll_ndiv_slowdown_debug_pll_dynramp_done_synced_v(data))
245 break;
246 } while (ramp_timeout > 0);
247
248 /* exit slowdown mode */
249 data = gk20a_readl(g, trim_sys_gpcpll_ndiv_slowdown_r());
250 data = set_field(data,
251 trim_sys_gpcpll_ndiv_slowdown_slowdown_using_pll_m(),
252 trim_sys_gpcpll_ndiv_slowdown_slowdown_using_pll_no_f());
253 data = set_field(data,
254 trim_sys_gpcpll_ndiv_slowdown_en_dynramp_m(),
255 trim_sys_gpcpll_ndiv_slowdown_en_dynramp_no_f());
256 gk20a_writel(g, trim_sys_gpcpll_ndiv_slowdown_r(), data);
257 gk20a_readl(g, trim_sys_gpcpll_ndiv_slowdown_r());
258
259 if (ramp_timeout <= 0) {
260 nvgpu_err(g, "gpcpll dynamic ramp timeout");
261 return -ETIMEDOUT;
262 }
263 return 0;
264}
265
266static int clk_program_gpc_pll(struct gk20a *g, struct clk_gk20a *clk,
267 int allow_slide)
268{
269 u32 data, cfg, coeff, timeout;
270 u32 m, n, pl;
271 u32 nlo;
272
273 gk20a_dbg_fn("");
274
275 if (!nvgpu_platform_is_silicon(g))
276 return 0;
277
278 /* get old coefficients */
279 coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r());
280 m = trim_sys_gpcpll_coeff_mdiv_v(coeff);
281 n = trim_sys_gpcpll_coeff_ndiv_v(coeff);
282 pl = trim_sys_gpcpll_coeff_pldiv_v(coeff);
283
284 /* do NDIV slide if there is no change in M and PL */
285 cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r());
286 if (allow_slide && clk->gpc_pll.M == m && clk->gpc_pll.PL == pl
287 && trim_sys_gpcpll_cfg_enable_v(cfg)) {
288 return clk_slide_gpc_pll(g, clk->gpc_pll.N);
289 }
290
291 /* slide down to NDIV_LO */
292 nlo = DIV_ROUND_UP(m * gpc_pll_params.min_vco, clk->gpc_pll.clk_in);
293 if (allow_slide && trim_sys_gpcpll_cfg_enable_v(cfg)) {
294 int ret = clk_slide_gpc_pll(g, nlo);
295 if (ret)
296 return ret;
297 }
298
299 /* split FO-to-bypass jump in halfs by setting out divider 1:2 */
300 data = gk20a_readl(g, trim_sys_gpc2clk_out_r());
301 data = set_field(data, trim_sys_gpc2clk_out_vcodiv_m(),
302 trim_sys_gpc2clk_out_vcodiv_f(2));
303 gk20a_writel(g, trim_sys_gpc2clk_out_r(), data);
304
305 /* put PLL in bypass before programming it */
306 data = gk20a_readl(g, trim_sys_sel_vco_r());
307 data = set_field(data, trim_sys_sel_vco_gpc2clk_out_m(),
308 trim_sys_sel_vco_gpc2clk_out_bypass_f());
309 nvgpu_udelay(2);
310 gk20a_writel(g, trim_sys_sel_vco_r(), data);
311
312 /* get out from IDDQ */
313 cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r());
314 if (trim_sys_gpcpll_cfg_iddq_v(cfg)) {
315 cfg = set_field(cfg, trim_sys_gpcpll_cfg_iddq_m(),
316 trim_sys_gpcpll_cfg_iddq_power_on_v());
317 gk20a_writel(g, trim_sys_gpcpll_cfg_r(), cfg);
318 gk20a_readl(g, trim_sys_gpcpll_cfg_r());
319 nvgpu_udelay(gpc_pll_params.iddq_exit_delay);
320 }
321
322 /* disable PLL before changing coefficients */
323 cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r());
324 cfg = set_field(cfg, trim_sys_gpcpll_cfg_enable_m(),
325 trim_sys_gpcpll_cfg_enable_no_f());
326 gk20a_writel(g, trim_sys_gpcpll_cfg_r(), cfg);
327 gk20a_readl(g, trim_sys_gpcpll_cfg_r());
328
329 /* change coefficients */
330 nlo = DIV_ROUND_UP(clk->gpc_pll.M * gpc_pll_params.min_vco,
331 clk->gpc_pll.clk_in);
332 coeff = trim_sys_gpcpll_coeff_mdiv_f(clk->gpc_pll.M) |
333 trim_sys_gpcpll_coeff_ndiv_f(allow_slide ?
334 nlo : clk->gpc_pll.N) |
335 trim_sys_gpcpll_coeff_pldiv_f(clk->gpc_pll.PL);
336 gk20a_writel(g, trim_sys_gpcpll_coeff_r(), coeff);
337
338 /* enable PLL after changing coefficients */
339 cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r());
340 cfg = set_field(cfg, trim_sys_gpcpll_cfg_enable_m(),
341 trim_sys_gpcpll_cfg_enable_yes_f());
342 gk20a_writel(g, trim_sys_gpcpll_cfg_r(), cfg);
343
344 /* lock pll */
345 cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r());
346 if (cfg & trim_sys_gpcpll_cfg_enb_lckdet_power_off_f()){
347 cfg = set_field(cfg, trim_sys_gpcpll_cfg_enb_lckdet_m(),
348 trim_sys_gpcpll_cfg_enb_lckdet_power_on_f());
349 gk20a_writel(g, trim_sys_gpcpll_cfg_r(), cfg);
350 }
351
352 /* wait pll lock */
353 timeout = gpc_pll_params.lock_timeout / 2 + 1;
354 do {
355 cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r());
356 if (cfg & trim_sys_gpcpll_cfg_pll_lock_true_f())
357 goto pll_locked;
358 nvgpu_udelay(2);
359 } while (--timeout > 0);
360
361 /* PLL is messed up. What can we do here? */
362 BUG();
363 return -EBUSY;
364
365pll_locked:
366 /* put PLL back on vco */
367 data = gk20a_readl(g, trim_sys_sel_vco_r());
368 data = set_field(data, trim_sys_sel_vco_gpc2clk_out_m(),
369 trim_sys_sel_vco_gpc2clk_out_vco_f());
370 gk20a_writel(g, trim_sys_sel_vco_r(), data);
371 clk->gpc_pll.enabled = true;
372
373 /* restore out divider 1:1 */
374 data = gk20a_readl(g, trim_sys_gpc2clk_out_r());
375 data = set_field(data, trim_sys_gpc2clk_out_vcodiv_m(),
376 trim_sys_gpc2clk_out_vcodiv_by1_f());
377 nvgpu_udelay(2);
378 gk20a_writel(g, trim_sys_gpc2clk_out_r(), data);
379
380 /* slide up to target NDIV */
381 return clk_slide_gpc_pll(g, clk->gpc_pll.N);
382}
383
384static int clk_disable_gpcpll(struct gk20a *g, int allow_slide)
385{
386 u32 cfg, coeff, m, nlo;
387 struct clk_gk20a *clk = &g->clk;
388
389 /* slide to VCO min */
390 cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r());
391 if (allow_slide && trim_sys_gpcpll_cfg_enable_v(cfg)) {
392 coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r());
393 m = trim_sys_gpcpll_coeff_mdiv_v(coeff);
394 nlo = DIV_ROUND_UP(m * gpc_pll_params.min_vco,
395 clk->gpc_pll.clk_in);
396 clk_slide_gpc_pll(g, nlo);
397 }
398
399 /* put PLL in bypass before disabling it */
400 cfg = gk20a_readl(g, trim_sys_sel_vco_r());
401 cfg = set_field(cfg, trim_sys_sel_vco_gpc2clk_out_m(),
402 trim_sys_sel_vco_gpc2clk_out_bypass_f());
403 gk20a_writel(g, trim_sys_sel_vco_r(), cfg);
404
405 /* disable PLL */
406 cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r());
407 cfg = set_field(cfg, trim_sys_gpcpll_cfg_enable_m(),
408 trim_sys_gpcpll_cfg_enable_no_f());
409 gk20a_writel(g, trim_sys_gpcpll_cfg_r(), cfg);
410 gk20a_readl(g, trim_sys_gpcpll_cfg_r());
411
412 clk->gpc_pll.enabled = false;
413 return 0;
414}
415
416static int gk20a_init_clk_reset_enable_hw(struct gk20a *g)
417{
418 gk20a_dbg_fn("");
419 return 0;
420}
421
422static int gk20a_init_clk_setup_sw(struct gk20a *g)
423{
424 struct clk_gk20a *clk = &g->clk;
425 static int initialized;
426 struct clk *ref;
427 unsigned long ref_rate;
428 int err;
429
430 gk20a_dbg_fn("");
431
432 err = nvgpu_mutex_init(&clk->clk_mutex);
433 if (err)
434 return err;
435
436 if (clk->sw_ready) {
437 gk20a_dbg_fn("skip init");
438 return 0;
439 }
440
441 if (!gk20a_clk_get(g)) {
442 err = -EINVAL;
443 goto fail;
444 }
445
446 ref = clk_get_parent(clk_get_parent(clk->tegra_clk));
447 if (IS_ERR(ref)) {
448 nvgpu_err(g,
449 "failed to get GPCPLL reference clock");
450 err = -EINVAL;
451 goto fail;
452 }
453 ref_rate = clk_get_rate(ref);
454
455 clk->gpc_pll.id = GK20A_GPC_PLL;
456 clk->gpc_pll.clk_in = ref_rate / KHZ;
457 if (clk->gpc_pll.clk_in == 0) {
458 nvgpu_err(g,
459 "GPCPLL reference clock is zero");
460 err = -EINVAL;
461 goto fail;
462 }
463
464 /* Decide initial frequency */
465 if (!initialized) {
466 initialized = 1;
467 clk->gpc_pll.M = 1;
468 clk->gpc_pll.N = DIV_ROUND_UP(gpc_pll_params.min_vco,
469 clk->gpc_pll.clk_in);
470 clk->gpc_pll.PL = 1;
471 clk->gpc_pll.freq = clk->gpc_pll.clk_in * clk->gpc_pll.N;
472 clk->gpc_pll.freq /= pl_to_div[clk->gpc_pll.PL];
473 }
474
475 clk->sw_ready = true;
476
477 gk20a_dbg_fn("done");
478 return 0;
479
480fail:
481 nvgpu_mutex_destroy(&clk->clk_mutex);
482 return err;
483}
484
485static int gk20a_init_clk_setup_hw(struct gk20a *g)
486{
487 u32 data;
488
489 gk20a_dbg_fn("");
490
491 data = gk20a_readl(g, trim_sys_gpc2clk_out_r());
492 data = set_field(data,
493 trim_sys_gpc2clk_out_sdiv14_m() |
494 trim_sys_gpc2clk_out_vcodiv_m() |
495 trim_sys_gpc2clk_out_bypdiv_m(),
496 trim_sys_gpc2clk_out_sdiv14_indiv4_mode_f() |
497 trim_sys_gpc2clk_out_vcodiv_by1_f() |
498 trim_sys_gpc2clk_out_bypdiv_f(0));
499 gk20a_writel(g, trim_sys_gpc2clk_out_r(), data);
500
501 return 0;
502}
503
504static int set_pll_target(struct gk20a *g, u32 freq, u32 old_freq)
505{
506 struct clk_gk20a *clk = &g->clk;
507
508 if (freq > gpc_pll_params.max_freq)
509 freq = gpc_pll_params.max_freq;
510 else if (freq < gpc_pll_params.min_freq)
511 freq = gpc_pll_params.min_freq;
512
513 if (freq != old_freq) {
514 /* gpc_pll.freq is changed to new value here */
515 if (clk_config_pll(clk, &clk->gpc_pll, &gpc_pll_params,
516 &freq, true)) {
517 nvgpu_err(g,
518 "failed to set pll target for %d", freq);
519 return -EINVAL;
520 }
521 }
522 return 0;
523}
524
525static int set_pll_freq(struct gk20a *g, u32 freq, u32 old_freq)
526{
527 struct clk_gk20a *clk = &g->clk;
528 int err = 0;
529
530 gk20a_dbg_fn("curr freq: %dMHz, target freq %dMHz", old_freq, freq);
531
532 if ((freq == old_freq) && clk->gpc_pll.enabled)
533 return 0;
534
535 /* change frequency only if power is on */
536 if (g->clk.clk_hw_on) {
537 err = clk_program_gpc_pll(g, clk, 1);
538 if (err)
539 err = clk_program_gpc_pll(g, clk, 0);
540 }
541
542 /* Just report error but not restore PLL since dvfs could already change
543 voltage even when it returns error. */
544 if (err)
545 nvgpu_err(g, "failed to set pll to %d", freq);
546 return err;
547}
548
549static int gk20a_clk_export_set_rate(void *data, unsigned long *rate)
550{
551 u32 old_freq;
552 int ret = -ENODATA;
553 struct gk20a *g = data;
554 struct clk_gk20a *clk = &g->clk;
555
556 if (rate) {
557 nvgpu_mutex_acquire(&clk->clk_mutex);
558 old_freq = clk->gpc_pll.freq;
559 ret = set_pll_target(g, rate_gpu_to_gpc2clk(*rate), old_freq);
560 if (!ret && clk->gpc_pll.enabled)
561 ret = set_pll_freq(g, clk->gpc_pll.freq, old_freq);
562 if (!ret)
563 *rate = rate_gpc2clk_to_gpu(clk->gpc_pll.freq);
564 nvgpu_mutex_release(&clk->clk_mutex);
565 }
566 return ret;
567}
568
569static int gk20a_clk_export_enable(void *data)
570{
571 int ret;
572 struct gk20a *g = data;
573 struct clk_gk20a *clk = &g->clk;
574
575 nvgpu_mutex_acquire(&clk->clk_mutex);
576 ret = set_pll_freq(g, clk->gpc_pll.freq, clk->gpc_pll.freq);
577 nvgpu_mutex_release(&clk->clk_mutex);
578 return ret;
579}
580
581static void gk20a_clk_export_disable(void *data)
582{
583 struct gk20a *g = data;
584 struct clk_gk20a *clk = &g->clk;
585
586 nvgpu_mutex_acquire(&clk->clk_mutex);
587 if (g->clk.clk_hw_on)
588 clk_disable_gpcpll(g, 1);
589 nvgpu_mutex_release(&clk->clk_mutex);
590}
591
592static void gk20a_clk_export_init(void *data, unsigned long *rate, bool *state)
593{
594 struct gk20a *g = data;
595 struct clk_gk20a *clk = &g->clk;
596
597 nvgpu_mutex_acquire(&clk->clk_mutex);
598 if (state)
599 *state = clk->gpc_pll.enabled;
600 if (rate)
601 *rate = rate_gpc2clk_to_gpu(clk->gpc_pll.freq);
602 nvgpu_mutex_release(&clk->clk_mutex);
603}
604
605static struct tegra_clk_export_ops gk20a_clk_export_ops = {
606 .init = gk20a_clk_export_init,
607 .enable = gk20a_clk_export_enable,
608 .disable = gk20a_clk_export_disable,
609 .set_rate = gk20a_clk_export_set_rate,
610};
611
612static int gk20a_clk_register_export_ops(struct gk20a *g)
613{
614 int ret;
615 struct clk *c;
616
617 if (gk20a_clk_export_ops.data)
618 return 0;
619
620 gk20a_clk_export_ops.data = (void *)g;
621 c = g->clk.tegra_clk;
622 if (!c || !clk_get_parent(c))
623 return -ENOSYS;
624
625 ret = tegra_clk_register_export_ops(clk_get_parent(c),
626 &gk20a_clk_export_ops);
627
628 return ret;
629}
630
631static void gk20a_clk_disable_slowboot(struct gk20a *g)
632{
633 u32 data;
634
635 data = gk20a_readl(g, trim_sys_gpc2clk_out_r());
636 data = set_field(data,
637 trim_sys_gpc2clk_out_bypdiv_m(),
638 trim_sys_gpc2clk_out_bypdiv_f(0));
639 gk20a_writel(g, trim_sys_gpc2clk_out_r(), data);
640}
641
642static int gk20a_init_clk_support(struct gk20a *g)
643{
644 struct clk_gk20a *clk = &g->clk;
645 u32 err;
646
647 gk20a_dbg_fn("");
648
649 clk->g = g;
650
651 err = gk20a_init_clk_reset_enable_hw(g);
652 if (err)
653 return err;
654
655 err = gk20a_init_clk_setup_sw(g);
656 if (err)
657 return err;
658
659 nvgpu_mutex_acquire(&clk->clk_mutex);
660 clk->clk_hw_on = true;
661
662 err = gk20a_init_clk_setup_hw(g);
663 nvgpu_mutex_release(&clk->clk_mutex);
664 if (err)
665 return err;
666
667 err = gk20a_clk_register_export_ops(g);
668 if (err)
669 return err;
670
671 /* FIXME: this effectively prevents host level clock gating */
672 err = clk_enable(g->clk.tegra_clk);
673 if (err)
674 return err;
675
676 /* The prev call may not enable PLL if gbus is unbalanced - force it */
677 nvgpu_mutex_acquire(&clk->clk_mutex);
678 err = set_pll_freq(g, clk->gpc_pll.freq, clk->gpc_pll.freq);
679 nvgpu_mutex_release(&clk->clk_mutex);
680 if (err)
681 return err;
682
683#ifdef CONFIG_DEBUG_FS
684 if (!clk->debugfs_set) {
685 if (!clk_gk20a_debugfs_init(g))
686 clk->debugfs_set = true;
687 }
688#endif
689 return err;
690}
691
692static int gk20a_suspend_clk_support(struct gk20a *g)
693{
694 int ret;
695
696 clk_disable(g->clk.tegra_clk);
697
698 /* The prev call may not disable PLL if gbus is unbalanced - force it */
699 nvgpu_mutex_acquire(&g->clk.clk_mutex);
700 ret = clk_disable_gpcpll(g, 1);
701 g->clk.clk_hw_on = false;
702 nvgpu_mutex_release(&g->clk.clk_mutex);
703
704 nvgpu_mutex_destroy(&g->clk.clk_mutex);
705
706 return ret;
707}
708
709void gk20a_init_clk_ops(struct gpu_ops *gops)
710{
711 gops->clk.disable_slowboot = gk20a_clk_disable_slowboot;
712 gops->clk.init_clk_support = gk20a_init_clk_support;
713 gops->clk.suspend_clk_support = gk20a_suspend_clk_support;
714}
715
716#ifdef CONFIG_DEBUG_FS
717
718static int rate_get(void *data, u64 *val)
719{
720 struct gk20a *g = (struct gk20a *)data;
721 *val = (u64)gk20a_clk_get_rate(g);
722 return 0;
723}
724static int rate_set(void *data, u64 val)
725{
726 struct gk20a *g = (struct gk20a *)data;
727 return gk20a_clk_set_rate(g, (u32)val);
728}
729DEFINE_SIMPLE_ATTRIBUTE(rate_fops, rate_get, rate_set, "%llu\n");
730
731static int pll_reg_show(struct seq_file *s, void *data)
732{
733 struct gk20a *g = s->private;
734 u32 reg, m, n, pl, f;
735
736 nvgpu_mutex_acquire(&g->clk.clk_mutex);
737 if (!g->clk.clk_hw_on) {
738 seq_printf(s, "gk20a powered down - no access to registers\n");
739 nvgpu_mutex_release(&g->clk.clk_mutex);
740 return 0;
741 }
742
743 reg = gk20a_readl(g, trim_sys_gpcpll_cfg_r());
744 seq_printf(s, "cfg = 0x%x : %s : %s\n", reg,
745 trim_sys_gpcpll_cfg_enable_v(reg) ? "enabled" : "disabled",
746 trim_sys_gpcpll_cfg_pll_lock_v(reg) ? "locked" : "unlocked");
747
748 reg = gk20a_readl(g, trim_sys_gpcpll_coeff_r());
749 m = trim_sys_gpcpll_coeff_mdiv_v(reg);
750 n = trim_sys_gpcpll_coeff_ndiv_v(reg);
751 pl = trim_sys_gpcpll_coeff_pldiv_v(reg);
752 f = g->clk.gpc_pll.clk_in * n / (m * pl_to_div[pl]);
753 seq_printf(s, "coef = 0x%x : m = %u : n = %u : pl = %u", reg, m, n, pl);
754 seq_printf(s, " : pll_f(gpu_f) = %u(%u) kHz\n", f, f/2);
755 nvgpu_mutex_release(&g->clk.clk_mutex);
756 return 0;
757}
758
759static int pll_reg_open(struct inode *inode, struct file *file)
760{
761 return single_open(file, pll_reg_show, inode->i_private);
762}
763
764static const struct file_operations pll_reg_fops = {
765 .open = pll_reg_open,
766 .read = seq_read,
767 .llseek = seq_lseek,
768 .release = single_release,
769};
770
771static int monitor_get(void *data, u64 *val)
772{
773 struct gk20a *g = (struct gk20a *)data;
774 struct clk_gk20a *clk = &g->clk;
775 int err;
776
777 u32 ncycle = 100; /* count GPCCLK for ncycle of clkin */
778 u64 freq = clk->gpc_pll.clk_in;
779 u32 count1, count2;
780
781 err = gk20a_busy(g);
782 if (err)
783 return err;
784
785 gk20a_writel(g, trim_gpc_clk_cntr_ncgpcclk_cfg_r(0),
786 trim_gpc_clk_cntr_ncgpcclk_cfg_reset_asserted_f());
787 gk20a_writel(g, trim_gpc_clk_cntr_ncgpcclk_cfg_r(0),
788 trim_gpc_clk_cntr_ncgpcclk_cfg_enable_asserted_f() |
789 trim_gpc_clk_cntr_ncgpcclk_cfg_write_en_asserted_f() |
790 trim_gpc_clk_cntr_ncgpcclk_cfg_noofipclks_f(ncycle));
791 /* start */
792
793 /* It should take about 8us to finish 100 cycle of 12MHz.
794 But longer than 100us delay is required here. */
795 gk20a_readl(g, trim_gpc_clk_cntr_ncgpcclk_cfg_r(0));
796 nvgpu_udelay(2000);
797
798 count1 = gk20a_readl(g, trim_gpc_clk_cntr_ncgpcclk_cnt_r(0));
799 nvgpu_udelay(100);
800 count2 = gk20a_readl(g, trim_gpc_clk_cntr_ncgpcclk_cnt_r(0));
801 freq *= trim_gpc_clk_cntr_ncgpcclk_cnt_value_v(count2);
802 do_div(freq, ncycle);
803 *val = freq;
804
805 gk20a_idle(g);
806
807 if (count1 != count2)
808 return -EBUSY;
809 return 0;
810}
811DEFINE_SIMPLE_ATTRIBUTE(monitor_fops, monitor_get, NULL, "%llu\n");
812
813static int clk_gk20a_debugfs_init(struct gk20a *g)
814{
815 struct dentry *d;
816 struct gk20a_platform *platform = dev_get_drvdata(g->dev);
817
818 d = debugfs_create_file(
819 "rate", S_IRUGO|S_IWUSR, platform->debugfs, g, &rate_fops);
820 if (!d)
821 goto err_out;
822
823 d = debugfs_create_file(
824 "pll_reg", S_IRUGO, platform->debugfs, g, &pll_reg_fops);
825 if (!d)
826 goto err_out;
827
828 d = debugfs_create_file(
829 "monitor", S_IRUGO, platform->debugfs, g, &monitor_fops);
830 if (!d)
831 goto err_out;
832
833 return 0;
834
835err_out:
836 pr_err("%s: Failed to make debugfs node\n", __func__);
837 debugfs_remove_recursive(platform->debugfs);
838 return -ENOMEM;
839}
840
841#endif /* CONFIG_DEBUG_FS */