summaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/gpu/nvgpu/gk20a/clk_gk20a.h8
-rw-r--r--drivers/gpu/nvgpu/gk20a/gk20a_sysfs.c7
-rw-r--r--drivers/gpu/nvgpu/gk20a/platform_gk20a.h2
-rw-r--r--drivers/gpu/nvgpu/gk20a/platform_gk20a_tegra.c12
-rw-r--r--drivers/gpu/nvgpu/gm20b/clk_gm20b.c147
-rw-r--r--drivers/gpu/nvgpu/gm20b/clk_gm20b.h6
6 files changed, 170 insertions, 12 deletions
diff --git a/drivers/gpu/nvgpu/gk20a/clk_gk20a.h b/drivers/gpu/nvgpu/gk20a/clk_gk20a.h
index aefbc5d5..43583f2e 100644
--- a/drivers/gpu/nvgpu/gk20a/clk_gk20a.h
+++ b/drivers/gpu/nvgpu/gk20a/clk_gk20a.h
@@ -17,6 +17,7 @@
17#define CLK_GK20A_H 17#define CLK_GK20A_H
18 18
19#include <linux/mutex.h> 19#include <linux/mutex.h>
20#include <linux/clk-provider.h>
20 21
21#define GPUFREQ_TABLE_END ~(u32)1 22#define GPUFREQ_TABLE_END ~(u32)1
22enum { 23enum {
@@ -79,6 +80,9 @@ struct namemap_cfg;
79struct clk_gk20a { 80struct clk_gk20a {
80 struct gk20a *g; 81 struct gk20a *g;
81 struct clk *tegra_clk; 82 struct clk *tegra_clk;
83#if defined(CONFIG_COMMON_CLK)
84 struct clk_hw hw;
85#endif
82 struct pll gpc_pll; 86 struct pll gpc_pll;
83 struct pll gpc_pll_last; 87 struct pll gpc_pll_last;
84 struct mutex clk_mutex; 88 struct mutex clk_mutex;
@@ -89,6 +93,10 @@ struct clk_gk20a {
89 bool debugfs_set; 93 bool debugfs_set;
90}; 94};
91 95
96#if defined(CONFIG_COMMON_CLK)
97#define to_clk_gk20a(_hw) container_of(_hw, struct clk_gk20a, hw)
98#endif
99
92struct gpu_ops; 100struct gpu_ops;
93#ifdef CONFIG_TEGRA_CLK_FRAMEWORK 101#ifdef CONFIG_TEGRA_CLK_FRAMEWORK
94void gk20a_init_clk_ops(struct gpu_ops *gops); 102void gk20a_init_clk_ops(struct gpu_ops *gops);
diff --git a/drivers/gpu/nvgpu/gk20a/gk20a_sysfs.c b/drivers/gpu/nvgpu/gk20a/gk20a_sysfs.c
index 55ebcc8f..d60fce33 100644
--- a/drivers/gpu/nvgpu/gk20a/gk20a_sysfs.c
+++ b/drivers/gpu/nvgpu/gk20a/gk20a_sysfs.c
@@ -636,9 +636,12 @@ static ssize_t fmax_at_vmin_safe_read(struct device *dev,
636{ 636{
637 struct gk20a *g = get_gk20a(dev); 637 struct gk20a *g = get_gk20a(dev);
638 unsigned long gpu_fmax_at_vmin_hz = 0; 638 unsigned long gpu_fmax_at_vmin_hz = 0;
639 struct clk *clk = g->clk.tegra_clk;
639 640
640 gpu_fmax_at_vmin_hz = tegra_dvfs_get_fmax_at_vmin_safe_t( 641#ifdef CONFIG_TEGRA_CLK_FRAMEWORK
641 clk_get_parent(g->clk.tegra_clk)); 642 clk = clk_get_parent(clk);
643#endif
644 gpu_fmax_at_vmin_hz = tegra_dvfs_get_fmax_at_vmin_safe_t(clk);
642 645
643 return snprintf(buf, PAGE_SIZE, "%d\n", (int)(gpu_fmax_at_vmin_hz)); 646 return snprintf(buf, PAGE_SIZE, "%d\n", (int)(gpu_fmax_at_vmin_hz));
644} 647}
diff --git a/drivers/gpu/nvgpu/gk20a/platform_gk20a.h b/drivers/gpu/nvgpu/gk20a/platform_gk20a.h
index 94b8d157..f13a11ea 100644
--- a/drivers/gpu/nvgpu/gk20a/platform_gk20a.h
+++ b/drivers/gpu/nvgpu/gk20a/platform_gk20a.h
@@ -179,6 +179,8 @@ struct gk20a_platform {
179 int (*clk_set_rate)(struct device *dev, 179 int (*clk_set_rate)(struct device *dev,
180 unsigned long rate); 180 unsigned long rate);
181 181
182 /* Called to register GPCPLL with common clk framework */
183 int (*clk_register)(struct gk20a *g);
182 184
183 /* Postscale callback is called after frequency change */ 185 /* Postscale callback is called after frequency change */
184 void (*postscale)(struct device *dev, 186 void (*postscale)(struct device *dev,
diff --git a/drivers/gpu/nvgpu/gk20a/platform_gk20a_tegra.c b/drivers/gpu/nvgpu/gk20a/platform_gk20a_tegra.c
index 40e7cead..35d524f1 100644
--- a/drivers/gpu/nvgpu/gk20a/platform_gk20a_tegra.c
+++ b/drivers/gpu/nvgpu/gk20a/platform_gk20a_tegra.c
@@ -40,6 +40,7 @@
40#include "hal_gk20a.h" 40#include "hal_gk20a.h"
41#include "platform_gk20a.h" 41#include "platform_gk20a.h"
42#include "gk20a_scale.h" 42#include "gk20a_scale.h"
43#include "gm20b/clk_gm20b.h"
43 44
44#define TEGRA_GK20A_BW_PER_FREQ 32 45#define TEGRA_GK20A_BW_PER_FREQ 32
45#define TEGRA_GM20B_BW_PER_FREQ 64 46#define TEGRA_GM20B_BW_PER_FREQ 64
@@ -787,6 +788,7 @@ static int gk20a_tegra_probe(struct device *dev)
787 const __be32 *host1x_ptr; 788 const __be32 *host1x_ptr;
788 struct platform_device *host1x_pdev = NULL; 789 struct platform_device *host1x_pdev = NULL;
789 bool joint_xpu_rail = false; 790 bool joint_xpu_rail = false;
791 int ret;
790 792
791 host1x_ptr = of_get_property(np, "nvidia,host1x", NULL); 793 host1x_ptr = of_get_property(np, "nvidia,host1x", NULL);
792 if (host1x_ptr) { 794 if (host1x_ptr) {
@@ -834,6 +836,12 @@ static int gk20a_tegra_probe(struct device *dev)
834 836
835 gk20a_tegra_get_clocks(dev); 837 gk20a_tegra_get_clocks(dev);
836 838
839 if (platform->clk_register) {
840 ret = platform->clk_register(platform->g);
841 if (ret)
842 return ret;
843 }
844
837#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) 845#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
838 pmc = ioremap(TEGRA_PMC_BASE, 4096); 846 pmc = ioremap(TEGRA_PMC_BASE, 4096);
839#endif 847#endif
@@ -1019,6 +1027,10 @@ struct gk20a_platform gm20b_tegra_platform = {
1019 .get_clk_freqs = gk20a_clk_get_freqs, 1027 .get_clk_freqs = gk20a_clk_get_freqs,
1020#endif 1028#endif
1021 1029
1030#ifdef CONFIG_COMMON_CLK
1031 .clk_register = gm20b_register_gpcclk,
1032#endif
1033
1022 /* frequency scaling configuration */ 1034 /* frequency scaling configuration */
1023 .prescale = gk20a_tegra_prescale, 1035 .prescale = gk20a_tegra_prescale,
1024 .postscale = gk20a_tegra_postscale, 1036 .postscale = gk20a_tegra_postscale,
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);
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
25void gm20b_init_clk_ops(struct gpu_ops *gops); 24void gm20b_init_clk_ops(struct gpu_ops *gops);
26#else 25
27static inline void gm20b_init_clk_ops(struct gpu_ops *gops) {} 26#ifdef CONFIG_COMMON_CLK
27int gm20b_register_gpcclk(struct gk20a *g);
28#endif 28#endif
29 29
30#endif /* _NVHOST_CLK_GM20B_H_ */ 30#endif /* _NVHOST_CLK_GM20B_H_ */