diff --git a/drivers/gpu/nvgpu/include/nvgpu/gk20a.h b/drivers/gpu/nvgpu/include/nvgpu/gk20a.h index 8f2881ece..5821f742b 100644 --- a/drivers/gpu/nvgpu/include/nvgpu/gk20a.h +++ b/drivers/gpu/nvgpu/include/nvgpu/gk20a.h @@ -1086,6 +1086,10 @@ struct gpu_ops { int (*clk_domain_get_f_points)(struct gk20a *g, u32 clkapidomain, u32 *pfpointscount, u16 *pfreqpointsinmhz); + int (*clk_get_round_rate)(struct gk20a *g, u32 api_domain, + unsigned long rate_target, unsigned long *rounded_rate); + int (*get_clk_range)(struct gk20a *g, u32 api_domain, + u16 *min_mhz, u16 *max_mhz); unsigned long (*measure_freq)(struct gk20a *g, u32 api_domain); u32 (*get_rate_cntr)(struct gk20a *g, struct namemap_cfg *c); unsigned long (*get_rate)(struct gk20a *g, u32 api_domain); diff --git a/drivers/gpu/nvgpu/os/linux/clk.c b/drivers/gpu/nvgpu/os/linux/clk.c index 414b17c45..cc4209947 100644 --- a/drivers/gpu/nvgpu/os/linux/clk.c +++ b/drivers/gpu/nvgpu/os/linux/clk.c @@ -27,6 +27,8 @@ #include "gk20a/gk20a.h" +#define HZ_TO_MHZ(x) ((x) / 1000000) + static unsigned long nvgpu_linux_clk_get_rate(struct gk20a *g, u32 api_domain) { struct gk20a_platform *platform = gk20a_get_platform(dev_from_gk20a(g)); @@ -142,6 +144,119 @@ static unsigned long nvgpu_linux_get_maxrate(struct gk20a *g, u32 api_domain) return ret; } +/* + * This API is used to return a list of supported frequencies by igpu. + * Set *num_points as 0 to get the size of the freqs list, returned + * by *num_points itself. freqs array must be provided by caller. + * If *num_points is non-zero, then freqs array size must atleast + * equal *num_points. + */ +static int nvgpu_linux_clk_get_f_points(struct gk20a *g, + u32 api_domain, u32 *num_points, u16 *freqs) +{ + struct device *dev = dev_from_gk20a(g); + struct gk20a_platform *platform = gk20a_get_platform(dev); + unsigned long *gpu_freq_table; + int ret = 0; + int num_supported_freq = 0; + u32 i; + + switch (api_domain) { + case CTRL_CLK_DOMAIN_GPCCLK: + ret = platform->get_clk_freqs(dev, &gpu_freq_table, + &num_supported_freq); + + if (ret) { + return ret; + } + + if (num_points == NULL) { + return -EINVAL; + } + + if (*num_points != 0U) { + if (freqs == NULL || (*num_points > (u32)num_supported_freq)) { + return -EINVAL; + } + } + + if (*num_points == 0) { + *num_points = num_supported_freq; + } else { + for (i = 0; i < *num_points; i++) { + freqs[i] = HZ_TO_MHZ(gpu_freq_table[i]); + } + } + break; + default: + nvgpu_err(g, "unknown clock: %u", api_domain); + ret = -EINVAL; + break; + } + + return ret; +} + +static int nvgpu_clk_get_range(struct gk20a *g, u32 api_domain, + u16 *min_mhz, u16 *max_mhz) +{ + struct device *dev = dev_from_gk20a(g); + struct gk20a_platform *platform = gk20a_get_platform(dev); + unsigned long *freqs; + int num_freqs; + int ret; + + switch (api_domain) { + case CTRL_CLK_DOMAIN_GPCCLK: + ret = platform->get_clk_freqs(dev, &freqs, &num_freqs); + + if (!ret) { + *min_mhz = HZ_TO_MHZ(freqs[0]); + *max_mhz = HZ_TO_MHZ(freqs[num_freqs - 1]); + } + break; + default: + nvgpu_err(g, "unknown clock: %u", api_domain); + ret = -EINVAL; + break; + } + + return ret; +} + +/* rate_target should be passed in as Hz + rounded_rate is returned in Hz */ +static int nvgpu_clk_get_round_rate(struct gk20a *g, + u32 api_domain, unsigned long rate_target, + unsigned long *rounded_rate) +{ + struct device *dev = dev_from_gk20a(g); + struct gk20a_platform *platform = gk20a_get_platform(dev); + unsigned long *freqs; + int num_freqs; + int i, ret = 0; + + switch (api_domain) { + case CTRL_CLK_DOMAIN_GPCCLK: + ret = platform->get_clk_freqs(dev, &freqs, &num_freqs); + + for (i = 0; i < num_freqs; ++i) { + if (freqs[i] >= rate_target) { + *rounded_rate = freqs[i]; + return 0; + } + } + *rounded_rate = freqs[num_freqs - 1]; + break; + default: + nvgpu_err(g, "unknown clock: %u", api_domain); + ret = -EINVAL; + break; + } + + return ret; +} + static int nvgpu_linux_prepare_enable(struct clk_gk20a *clk) { return clk_prepare_enable(clk->tegra_clk); @@ -162,4 +277,7 @@ void nvgpu_linux_init_clk_support(struct gk20a *g) g->ops.clk.get_maxrate = nvgpu_linux_get_maxrate; g->ops.clk.prepare_enable = nvgpu_linux_prepare_enable; g->ops.clk.disable_unprepare = nvgpu_linux_disable_unprepare; + g->ops.clk.clk_domain_get_f_points = nvgpu_linux_clk_get_f_points; + g->ops.clk.get_clk_range = nvgpu_clk_get_range; + g->ops.clk.clk_get_round_rate = nvgpu_clk_get_round_rate; } diff --git a/drivers/gpu/nvgpu/os/linux/platform_gk20a.h b/drivers/gpu/nvgpu/os/linux/platform_gk20a.h index f3e80b8c4..b5beeefe6 100644 --- a/drivers/gpu/nvgpu/os/linux/platform_gk20a.h +++ b/drivers/gpu/nvgpu/os/linux/platform_gk20a.h @@ -274,6 +274,9 @@ struct gk20a_platform { /* scaling rate */ unsigned long cached_rate; + + /* synchronized access to platform->clk_get_freqs */ + struct nvgpu_mutex clk_get_freq_lock; }; static inline struct gk20a_platform *gk20a_get_platform( diff --git a/drivers/gpu/nvgpu/os/linux/platform_gp10b_tegra.c b/drivers/gpu/nvgpu/os/linux/platform_gp10b_tegra.c index c5464d5be..5fdcb05ca 100644 --- a/drivers/gpu/nvgpu/os/linux/platform_gp10b_tegra.c +++ b/drivers/gpu/nvgpu/os/linux/platform_gp10b_tegra.c @@ -55,6 +55,9 @@ static unsigned long gp10b_freq_table[GP10B_MAX_SUPPORTED_FREQS / GP10B_FREQ_SELECT_STEP]; +static bool freq_table_init_complete; +static int num_supported_freq; + #define TEGRA_GP10B_BW_PER_FREQ 64 #define TEGRA_DDR4_BW_PER_FREQ 16 @@ -166,6 +169,8 @@ static int gp10b_tegra_probe(struct device *dev) gp10b_tegra_get_clocks(dev); nvgpu_linux_init_clk_support(platform->g); + nvgpu_mutex_init(&platform->clk_get_freq_lock); + return 0; } @@ -176,6 +181,8 @@ static int gp10b_tegra_late_probe(struct device *dev) static int gp10b_tegra_remove(struct device *dev) { + struct gk20a_platform *platform = gk20a_get_platform(dev); + /* deinitialise tegra specific scaling quirks */ gp10b_tegra_scale_exit(dev); @@ -183,6 +190,8 @@ static int gp10b_tegra_remove(struct device *dev) nvgpu_free_nvhost_dev(get_gk20a(dev)); #endif + nvgpu_mutex_destroy(&platform->clk_get_freq_lock); + return 0; } @@ -342,6 +351,18 @@ int gp10b_clk_get_freqs(struct device *dev, int sel_freq_cnt; unsigned long loc_freq_table[GP10B_MAX_SUPPORTED_FREQS]; + nvgpu_mutex_acquire(&platform->clk_get_freq_lock); + + if (freq_table_init_complete) { + + *freqs = gp10b_freq_table; + *num_freqs = num_supported_freq; + + nvgpu_mutex_release(&platform->clk_get_freq_lock); + + return 0; + } + max_rate = clk_round_rate(platform->clk[0], (UINT_MAX - 1)); /* @@ -392,10 +413,15 @@ int gp10b_clk_get_freqs(struct device *dev, /* Fill freq table */ *freqs = gp10b_freq_table; *num_freqs = sel_freq_cnt; + num_supported_freq = sel_freq_cnt; + + freq_table_init_complete = true; nvgpu_log_info(g, "min rate: %ld max rate: %ld num_of_freq %d\n", gp10b_freq_table[0], max_rate, *num_freqs); + nvgpu_mutex_release(&platform->clk_get_freq_lock); + return 0; } diff --git a/drivers/gpu/nvgpu/os/linux/platform_gv11b_tegra.c b/drivers/gpu/nvgpu/os/linux/platform_gv11b_tegra.c index 1b4a54565..b055eb6e4 100644 --- a/drivers/gpu/nvgpu/os/linux/platform_gv11b_tegra.c +++ b/drivers/gpu/nvgpu/os/linux/platform_gv11b_tegra.c @@ -97,6 +97,8 @@ static int gv11b_tegra_probe(struct device *dev) gp10b_tegra_get_clocks(dev); nvgpu_linux_init_clk_support(platform->g); + nvgpu_mutex_init(&platform->clk_get_freq_lock); + return 0; } @@ -108,12 +110,16 @@ static int gv11b_tegra_late_probe(struct device *dev) static int gv11b_tegra_remove(struct device *dev) { + struct gk20a_platform *platform = gk20a_get_platform(dev); + gv11b_tegra_scale_exit(dev); #ifdef CONFIG_TEGRA_GK20A_NVHOST nvgpu_free_nvhost_dev(get_gk20a(dev)); #endif + nvgpu_mutex_destroy(&platform->clk_get_freq_lock); + return 0; } diff --git a/drivers/gpu/nvgpu/os/linux/scale.c b/drivers/gpu/nvgpu/os/linux/scale.c index 72ed94bd6..ecc8207ac 100644 --- a/drivers/gpu/nvgpu/os/linux/scale.c +++ b/drivers/gpu/nvgpu/os/linux/scale.c @@ -124,6 +124,7 @@ static int gk20a_scale_make_freq_table(struct gk20a_scale_profile *profile) /* get gpu frequency table */ err = platform->get_clk_freqs(profile->dev, &freqs, &num_freqs); + if (err) return -ENOSYS; } else