summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/gm20b/clk_gm20b.c
diff options
context:
space:
mode:
authorPeter Boonstoppel <pboonstoppel@nvidia.com>2016-10-07 18:30:59 -0400
committermobile promotions <svcmobile_promotions@nvidia.com>2016-11-15 15:30:06 -0500
commit01e61860fafbc0ee045c2db931a79f6c0d5300aa (patch)
tree83c8da282d83fd8d9edc441d561dede6b9a8dece /drivers/gpu/nvgpu/gm20b/clk_gm20b.c
parent3a6d47db9d00f86d90a1230dd2225bf4ae8944b8 (diff)
gpu: nvgpu: gm20b expose gpcclk through CCF
Register gpcclk with Common Clock Framework to expose GPCPLL frequency control Bug 200233943 Change-Id: Id6f7bbaca15f22157b91b092c2a035af933fa71e Signed-off-by: Peter Boonstoppel <pboonstoppel@nvidia.com> Reviewed-on: http://git-master/r/1236979 Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Diffstat (limited to 'drivers/gpu/nvgpu/gm20b/clk_gm20b.c')
-rw-r--r--drivers/gpu/nvgpu/gm20b/clk_gm20b.c147
1 files changed, 140 insertions, 7 deletions
diff --git a/drivers/gpu/nvgpu/gm20b/clk_gm20b.c b/drivers/gpu/nvgpu/gm20b/clk_gm20b.c
index 8b70930a..2f54e4bb 100644
--- a/drivers/gpu/nvgpu/gm20b/clk_gm20b.c
+++ b/drivers/gpu/nvgpu/gm20b/clk_gm20b.c
@@ -16,6 +16,7 @@
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */ 17 */
18 18
19#include <linux/version.h>
19#include <linux/clk.h> 20#include <linux/clk.h>
20#include <linux/delay.h> /* for mdelay */ 21#include <linux/delay.h> /* for mdelay */
21#include <linux/module.h> 22#include <linux/module.h>
@@ -374,10 +375,16 @@ static void clk_config_dvfs_ndiv(int mv, u32 n_eff, struct na_dvfs *d)
374static void clk_config_dvfs(struct gk20a *g, struct pll *gpll) 375static void clk_config_dvfs(struct gk20a *g, struct pll *gpll)
375{ 376{
376 struct na_dvfs *d = &gpll->dvfs; 377 struct na_dvfs *d = &gpll->dvfs;
378 struct clk* clk;
377 379
378 d->mv = tegra_dvfs_predict_mv_at_hz_cur_tfloor( 380 clk = g->clk.tegra_clk;
379 clk_get_parent(g->clk.tegra_clk), 381#ifdef CONFIG_TEGRA_CLK_FRAMEWORK
382 clk = clk_get_parent(clk);
383#endif
384
385 d->mv = tegra_dvfs_predict_mv_at_hz_cur_tfloor(clk,
380 rate_gpc2clk_to_gpu(gpll->freq)); 386 rate_gpc2clk_to_gpu(gpll->freq));
387
381 clk_config_dvfs_detection(d->mv, d); 388 clk_config_dvfs_detection(d->mv, d);
382 clk_config_dvfs_ndiv(d->mv, gpll->N, d); 389 clk_config_dvfs_ndiv(d->mv, gpll->N, d);
383} 390}
@@ -1116,7 +1123,7 @@ static int gm20b_init_clk_setup_sw(struct gk20a *g)
1116{ 1123{
1117 struct clk_gk20a *clk = &g->clk; 1124 struct clk_gk20a *clk = &g->clk;
1118 unsigned long safe_rate; 1125 unsigned long safe_rate;
1119 struct clk *ref; 1126 struct clk *ref, *c;
1120 1127
1121 gk20a_dbg_fn(""); 1128 gk20a_dbg_fn("");
1122 1129
@@ -1128,12 +1135,16 @@ static int gm20b_init_clk_setup_sw(struct gk20a *g)
1128 if (!gk20a_clk_get(g)) 1135 if (!gk20a_clk_get(g))
1129 return -EINVAL; 1136 return -EINVAL;
1130 1137
1138 c = clk->tegra_clk;
1139#ifdef CONFIG_TEGRA_CLK_FRAMEWORK
1131 /* 1140 /*
1132 * On Tegra GPU clock exposed to frequency governor is a shared user on 1141 * On Tegra GPU clock exposed to frequency governor is a shared user on
1133 * GPCPLL bus (gbus). The latter can be accessed as GPU clock parent. 1142 * GPCPLL bus (gbus). The latter can be accessed as GPU clock parent.
1134 * Respectively the grandparent is PLL reference clock. 1143 * Respectively the grandparent is PLL reference clock.
1135 */ 1144 */
1136 ref = clk_get_parent(clk_get_parent(clk->tegra_clk)); 1145 c = clk_get_parent(c);
1146#endif
1147 ref = clk_get_parent(c);
1137 if (IS_ERR(ref)) { 1148 if (IS_ERR(ref)) {
1138 gk20a_err(dev_from_gk20a(g), 1149 gk20a_err(dev_from_gk20a(g),
1139 "failed to get GPCPLL reference clock"); 1150 "failed to get GPCPLL reference clock");
@@ -1144,7 +1155,7 @@ static int gm20b_init_clk_setup_sw(struct gk20a *g)
1144 clk->gpc_pll.clk_in = clk_get_rate(ref) / KHZ; 1155 clk->gpc_pll.clk_in = clk_get_rate(ref) / KHZ;
1145 1156
1146 safe_rate = tegra_dvfs_get_fmax_at_vmin_safe_t( 1157 safe_rate = tegra_dvfs_get_fmax_at_vmin_safe_t(
1147 clk_get_parent(clk->tegra_clk)); 1158 clk_get_parent(c));
1148 safe_rate = safe_rate * (100 - DVFS_SAFE_MARGIN) / 100; 1159 safe_rate = safe_rate * (100 - DVFS_SAFE_MARGIN) / 100;
1149 dvfs_safe_max_freq = rate_gpu_to_gpc2clk(safe_rate); 1160 dvfs_safe_max_freq = rate_gpu_to_gpc2clk(safe_rate);
1150 clk->gpc_pll.PL = (dvfs_safe_max_freq == 0) ? 0 : 1161 clk->gpc_pll.PL = (dvfs_safe_max_freq == 0) ? 0 :
@@ -1186,6 +1197,124 @@ static int gm20b_init_clk_setup_sw(struct gk20a *g)
1186 return 0; 1197 return 0;
1187} 1198}
1188 1199
1200
1201#ifdef CONFIG_COMMON_CLK
1202static int set_pll_freq(struct gk20a *g, int allow_slide);
1203static int set_pll_target(struct gk20a *g, u32 freq, u32 old_freq);
1204
1205static int gm20b_clk_prepare(struct clk_hw *hw)
1206{
1207 struct clk_gk20a *clk = to_clk_gk20a(hw);
1208 int ret = 0;
1209
1210 mutex_lock(&clk->clk_mutex);
1211 if (!clk->gpc_pll.enabled && clk->clk_hw_on)
1212 ret = set_pll_freq(clk->g, 1);
1213 mutex_unlock(&clk->clk_mutex);
1214 return ret;
1215}
1216
1217static void gm20b_clk_unprepare(struct clk_hw *hw)
1218{
1219 struct clk_gk20a *clk = to_clk_gk20a(hw);
1220
1221 mutex_lock(&clk->clk_mutex);
1222 if (clk->gpc_pll.enabled && clk->clk_hw_on)
1223 clk_disable_gpcpll(clk->g, 1);
1224 mutex_unlock(&clk->clk_mutex);
1225}
1226
1227static int gm20b_clk_is_prepared(struct clk_hw *hw)
1228{
1229 struct clk_gk20a *clk = to_clk_gk20a(hw);
1230
1231 return clk->gpc_pll.enabled && clk->clk_hw_on;
1232}
1233
1234static unsigned long gm20b_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
1235{
1236 struct clk_gk20a *clk = to_clk_gk20a(hw);
1237
1238 return rate_gpc2clk_to_gpu(clk->gpc_pll.freq);
1239}
1240
1241static int gm20b_gpcclk_set_rate(struct clk_hw *hw, unsigned long rate,
1242 unsigned long parent_rate)
1243{
1244 struct clk_gk20a *clk = to_clk_gk20a(hw);
1245 u32 old_freq;
1246 int ret = -ENODATA;
1247
1248 mutex_lock(&clk->clk_mutex);
1249 old_freq = clk->gpc_pll.freq;
1250 ret = set_pll_target(clk->g, rate_gpu_to_gpc2clk(rate), old_freq);
1251 if (!ret && clk->gpc_pll.enabled && clk->clk_hw_on)
1252 ret = set_pll_freq(clk->g, 1);
1253 mutex_unlock(&clk->clk_mutex);
1254
1255 return ret;
1256}
1257
1258static long gm20b_round_rate(struct clk_hw *hw, unsigned long rate,
1259 unsigned long *parent_rate)
1260{
1261 struct clk_gk20a *clk = to_clk_gk20a(hw);
1262 u32 freq, old_freq;
1263 struct pll tmp_pll;
1264
1265 mutex_lock(&clk->clk_mutex);
1266 old_freq = clk->gpc_pll.freq;
1267 freq = rate_gpu_to_gpc2clk(rate);
1268
1269 if (freq > gpc_pll_params.max_freq)
1270 freq = gpc_pll_params.max_freq;
1271 else if (freq < gpc_pll_params.min_freq)
1272 freq = gpc_pll_params.min_freq;
1273
1274 tmp_pll = clk->gpc_pll;
1275 clk_config_pll(clk, &tmp_pll, &gpc_pll_params, &freq, true);
1276
1277 mutex_unlock(&clk->clk_mutex);
1278
1279 return rate_gpc2clk_to_gpu(tmp_pll.freq);
1280}
1281
1282const struct clk_ops gk20a_clk_ops = {
1283 .prepare = gm20b_clk_prepare,
1284 .unprepare = gm20b_clk_unprepare,
1285 .is_prepared = gm20b_clk_is_prepared,
1286 .recalc_rate = gm20b_recalc_rate,
1287 .set_rate = gm20b_gpcclk_set_rate,
1288 .round_rate = gm20b_round_rate,
1289};
1290
1291int gm20b_register_gpcclk(struct gk20a *g) {
1292 const char *parent_name = "pllg_ref";
1293 struct clk_gk20a *clk = &g->clk;
1294 struct clk_init_data init;
1295 struct clk *c;
1296
1297 init.name = "gpcclk";
1298 init.ops = &gk20a_clk_ops;
1299 init.parent_names = &parent_name;
1300 init.num_parents = 1;
1301 init.flags = 0;
1302
1303 /* Data in .init is copied by clk_register(), so stack variable OK */
1304 clk->hw.init = &init;
1305 c = clk_register(g->dev, &clk->hw);
1306 if (IS_ERR(c)) {
1307 gk20a_err(dev_from_gk20a(g),
1308 "Failed to register GPCPLL clock");
1309 return -EINVAL;
1310 }
1311
1312 clk->tegra_clk = c;
1313 return 0;
1314}
1315#endif /* CONFIG_COMMON_CLK */
1316
1317
1189static int gm20b_init_clk_setup_hw(struct gk20a *g) 1318static int gm20b_init_clk_setup_hw(struct gk20a *g)
1190{ 1319{
1191 u32 data; 1320 u32 data;
@@ -1293,6 +1422,7 @@ static int set_pll_freq(struct gk20a *g, int allow_slide)
1293 return err; 1422 return err;
1294} 1423}
1295 1424
1425#ifdef CONFIG_TEGRA_CLK_FRAMEWORK
1296static int gm20b_clk_export_set_rate(void *data, unsigned long *rate) 1426static int gm20b_clk_export_set_rate(void *data, unsigned long *rate)
1297{ 1427{
1298 u32 old_freq; 1428 u32 old_freq;
@@ -1375,6 +1505,7 @@ static int gm20b_clk_register_export_ops(struct gk20a *g)
1375 1505
1376 return ret; 1506 return ret;
1377} 1507}
1508#endif /* CONFIG_TEGRA_CLK_FRAMEWORK */
1378 1509
1379static int gm20b_init_clk_support(struct gk20a *g) 1510static int gm20b_init_clk_support(struct gk20a *g)
1380{ 1511{
@@ -1401,12 +1532,14 @@ static int gm20b_init_clk_support(struct gk20a *g)
1401 if (err) 1532 if (err)
1402 return err; 1533 return err;
1403 1534
1535#ifdef CONFIG_TEGRA_CLK_FRAMEWORK
1404 err = gm20b_clk_register_export_ops(g); 1536 err = gm20b_clk_register_export_ops(g);
1405 if (err) 1537 if (err)
1406 return err; 1538 return err;
1539#endif
1407 1540
1408 /* FIXME: this effectively prevents host level clock gating */ 1541 /* FIXME: this effectively prevents host level clock gating */
1409 err = clk_enable(g->clk.tegra_clk); 1542 err = clk_prepare_enable(g->clk.tegra_clk);
1410 if (err) 1543 if (err)
1411 return err; 1544 return err;
1412 1545
@@ -1431,7 +1564,7 @@ static int gm20b_suspend_clk_support(struct gk20a *g)
1431{ 1564{
1432 int ret = 0; 1565 int ret = 0;
1433 1566
1434 clk_disable(g->clk.tegra_clk); 1567 clk_disable_unprepare(g->clk.tegra_clk);
1435 1568
1436 /* The prev call may not disable PLL if gbus is unbalanced - force it */ 1569 /* The prev call may not disable PLL if gbus is unbalanced - force it */
1437 mutex_lock(&g->clk.clk_mutex); 1570 mutex_lock(&g->clk.clk_mutex);