diff options
author | Peter Boonstoppel <pboonstoppel@nvidia.com> | 2016-10-07 18:30:59 -0400 |
---|---|---|
committer | mobile promotions <svcmobile_promotions@nvidia.com> | 2016-11-15 15:30:06 -0500 |
commit | 01e61860fafbc0ee045c2db931a79f6c0d5300aa (patch) | |
tree | 83c8da282d83fd8d9edc441d561dede6b9a8dece /drivers/gpu/nvgpu/gm20b | |
parent | 3a6d47db9d00f86d90a1230dd2225bf4ae8944b8 (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')
-rw-r--r-- | drivers/gpu/nvgpu/gm20b/clk_gm20b.c | 147 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/gm20b/clk_gm20b.h | 6 |
2 files changed, 143 insertions, 10 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) | |||
374 | static void clk_config_dvfs(struct gk20a *g, struct pll *gpll) | 375 | static 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 | ||
1202 | static int set_pll_freq(struct gk20a *g, int allow_slide); | ||
1203 | static int set_pll_target(struct gk20a *g, u32 freq, u32 old_freq); | ||
1204 | |||
1205 | static 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 | |||
1217 | static 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 | |||
1227 | static 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 | |||
1234 | static 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 | |||
1241 | static 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 | |||
1258 | static 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 | |||
1282 | const 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 | |||
1291 | int 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 | |||
1189 | static int gm20b_init_clk_setup_hw(struct gk20a *g) | 1318 | static 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 | ||
1296 | static int gm20b_clk_export_set_rate(void *data, unsigned long *rate) | 1426 | static 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 | ||
1379 | static int gm20b_init_clk_support(struct gk20a *g) | 1510 | static 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); |
diff --git a/drivers/gpu/nvgpu/gm20b/clk_gm20b.h b/drivers/gpu/nvgpu/gm20b/clk_gm20b.h index 84a2ce9a..7ea84826 100644 --- a/drivers/gpu/nvgpu/gm20b/clk_gm20b.h +++ b/drivers/gpu/nvgpu/gm20b/clk_gm20b.h | |||
@@ -21,10 +21,10 @@ | |||
21 | 21 | ||
22 | #include <linux/mutex.h> | 22 | #include <linux/mutex.h> |
23 | 23 | ||
24 | #ifdef CONFIG_TEGRA_CLK_FRAMEWORK | ||
25 | void gm20b_init_clk_ops(struct gpu_ops *gops); | 24 | void gm20b_init_clk_ops(struct gpu_ops *gops); |
26 | #else | 25 | |
27 | static inline void gm20b_init_clk_ops(struct gpu_ops *gops) {} | 26 | #ifdef CONFIG_COMMON_CLK |
27 | int gm20b_register_gpcclk(struct gk20a *g); | ||
28 | #endif | 28 | #endif |
29 | 29 | ||
30 | #endif /* _NVHOST_CLK_GM20B_H_ */ | 30 | #endif /* _NVHOST_CLK_GM20B_H_ */ |