From f871b52fd3f553d6b6375a3c848fbca272ed8e29 Mon Sep 17 00:00:00 2001 From: Sachit Kadle Date: Tue, 24 Jan 2017 10:22:13 -0800 Subject: [PATCH] gpu: nvgpu: vgpu: add devfreq support Add devfreq governor support in order to allow frequency scaling in virtualization config. GPU clock frequency operations are re-directed to the server over RPC. Bug 200237433 Change-Id: I1c8e565a4fff36d3456dc72ebb20795b7822650e Signed-off-by: Sachit Kadle Reviewed-on: http://git-master/r/1295542 (cherry picked from commit d5c956fc06697eda3829c67cb22987e538213b29) Reviewed-on: http://git-master/r/1280968 (cherry picked from commit 25e2b3cf7cb5559a6849c0024d42c157564a9be2) Reviewed-on: http://git-master/r/1321835 Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/gpu/nvgpu/Makefile.nvgpu | 1 + drivers/gpu/nvgpu/gk20a/platform_vgpu_tegra.c | 11 +- drivers/gpu/nvgpu/vgpu/clk_vgpu.c | 113 ++++++++++++++++++ drivers/gpu/nvgpu/vgpu/clk_vgpu.h | 24 ++++ drivers/gpu/nvgpu/vgpu/vgpu.c | 31 ++++- include/linux/tegra_vgpu.h | 10 ++ 6 files changed, 183 insertions(+), 7 deletions(-) create mode 100644 drivers/gpu/nvgpu/vgpu/clk_vgpu.c create mode 100644 drivers/gpu/nvgpu/vgpu/clk_vgpu.h diff --git a/drivers/gpu/nvgpu/Makefile.nvgpu b/drivers/gpu/nvgpu/Makefile.nvgpu index 6e6f79d9b..b2cb5339f 100644 --- a/drivers/gpu/nvgpu/Makefile.nvgpu +++ b/drivers/gpu/nvgpu/Makefile.nvgpu @@ -106,6 +106,7 @@ nvgpu-$(CONFIG_TEGRA_GR_VIRTUALIZATION) += \ vgpu/dbg_vgpu.o \ vgpu/fecs_trace_vgpu.o \ vgpu/tsg_vgpu.o \ + vgpu/clk_vgpu.o \ vgpu/css_vgpu.o \ vgpu/gk20a/vgpu_hal_gk20a.o \ vgpu/gk20a/vgpu_gr_gk20a.o \ diff --git a/drivers/gpu/nvgpu/gk20a/platform_vgpu_tegra.c b/drivers/gpu/nvgpu/gk20a/platform_vgpu_tegra.c index 8c424065f..034accfdb 100644 --- a/drivers/gpu/nvgpu/gk20a/platform_vgpu_tegra.c +++ b/drivers/gpu/nvgpu/gk20a/platform_vgpu_tegra.c @@ -1,7 +1,7 @@ /* * Tegra Virtualized GPU Platform Interface * - * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2017, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -18,6 +18,7 @@ #include "gk20a.h" #include "hal_gk20a.h" #include "platform_gk20a.h" +#include "vgpu/clk_vgpu.h" static int gk20a_tegra_probe(struct device *dev) { @@ -65,5 +66,13 @@ struct gk20a_platform vgpu_tegra_platform = { .probe = gk20a_tegra_probe, .default_big_page_size = SZ_128K, + .clk_get_rate = vgpu_clk_get_rate, + .clk_round_rate = vgpu_clk_round_rate, + .clk_set_rate = vgpu_clk_set_rate, + .get_clk_freqs = vgpu_clk_get_freqs, + + /* frequency scaling configuration */ + .devfreq_governor = "userspace", + .virtual_dev = true, }; diff --git a/drivers/gpu/nvgpu/vgpu/clk_vgpu.c b/drivers/gpu/nvgpu/vgpu/clk_vgpu.c new file mode 100644 index 000000000..c42d1cfac --- /dev/null +++ b/drivers/gpu/nvgpu/vgpu/clk_vgpu.c @@ -0,0 +1,113 @@ +/* + * Virtualized GPU Clock Interface + * + * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "vgpu/vgpu.h" +#include "vgpu/clk_vgpu.h" + +static unsigned long +vgpu_freq_table[TEGRA_VGPU_GPU_FREQ_TABLE_SIZE]; + +unsigned long vgpu_clk_get_rate(struct device *dev) +{ + struct gk20a_platform *platform = gk20a_get_platform(dev); + struct gk20a *g = platform->g; + struct tegra_vgpu_cmd_msg msg = {}; + struct tegra_vgpu_gpu_clk_rate_params *p = &msg.params.gpu_clk_rate; + + int err; + + gk20a_dbg_fn(""); + + msg.cmd = TEGRA_VGPU_CMD_GET_GPU_CLK_RATE; + msg.handle = vgpu_get_handle(g); + err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); + err = err ? err : msg.ret; + if (err) { + gk20a_err(dev_from_gk20a(g), + "%s failed - %d", __func__, err); + return 0; + } + + /* return frequency in Hz */ + return p->rate * 1000; +} + +long vgpu_clk_round_rate(struct device *dev, unsigned long rate) +{ + /* server will handle frequency rounding */ + return rate; +} + +int vgpu_clk_set_rate(struct device *dev, unsigned long rate) +{ + struct gk20a_platform *platform = gk20a_get_platform(dev); + struct gk20a *g = platform->g; + struct tegra_vgpu_cmd_msg msg = {}; + struct tegra_vgpu_gpu_clk_rate_params *p = &msg.params.gpu_clk_rate; + + int err = 0; + + gk20a_dbg_fn(""); + + msg.cmd = TEGRA_VGPU_CMD_SET_GPU_CLK_RATE; + msg.handle = vgpu_get_handle(g); + + /* server dvfs framework requires frequency in kHz */ + p->rate = (u32)(rate / 1000); + err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); + err = err ? err : msg.ret; + if (err) { + gk20a_err(dev_from_gk20a(g), + "%s failed - %d", __func__, err); + return err; + } + + return 0; +} + +int vgpu_clk_get_freqs(struct device *dev, + unsigned long **freqs, int *num_freqs) +{ + struct gk20a_platform *platform = gk20a_get_platform(dev); + struct gk20a *g = platform->g; + struct tegra_vgpu_cmd_msg msg = {}; + struct tegra_vgpu_get_gpu_freq_table_params *p = + &msg.params.get_gpu_freq_table; + unsigned int i; + int err; + + gk20a_dbg_fn(""); + + msg.cmd = TEGRA_VGPU_CMD_GET_GPU_FREQ_TABLE; + msg.handle = vgpu_get_handle(g); + + p->num_freqs = TEGRA_VGPU_GPU_FREQ_TABLE_SIZE; + err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); + err = err ? err : msg.ret; + if (err) { + gk20a_err(dev_from_gk20a(g), + "%s failed - %d", __func__, err); + return err; + } + + /* return frequency in Hz */ + for (i = 0; i < p->num_freqs; i++) + vgpu_freq_table[i] = p->freqs[i] * 1000; + + *freqs = vgpu_freq_table; + *num_freqs = p->num_freqs; + + return 0; +} diff --git a/drivers/gpu/nvgpu/vgpu/clk_vgpu.h b/drivers/gpu/nvgpu/vgpu/clk_vgpu.h new file mode 100644 index 000000000..e26ec0320 --- /dev/null +++ b/drivers/gpu/nvgpu/vgpu/clk_vgpu.h @@ -0,0 +1,24 @@ +/* + * Virtualized GPU Clock Interface + * + * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef _CLK_VIRT_H_ +#define _CLK_VIRT_H_ + +unsigned long vgpu_clk_get_rate(struct device *dev); +long vgpu_clk_round_rate(struct device *dev, unsigned long rate); +int vgpu_clk_set_rate(struct device *dev, unsigned long rate); +int vgpu_clk_get_freqs(struct device *dev, + unsigned long **freqs, int *num_freqs); +#endif diff --git a/drivers/gpu/nvgpu/vgpu/vgpu.c b/drivers/gpu/nvgpu/vgpu/vgpu.c index d5eb05acd..636375bea 100644 --- a/drivers/gpu/nvgpu/vgpu/vgpu.c +++ b/drivers/gpu/nvgpu/vgpu/vgpu.c @@ -23,6 +23,7 @@ #include "vgpu/vgpu.h" #include "vgpu/fecs_trace_vgpu.h" +#include "vgpu/clk_vgpu.h" #include "gk20a/debug_gk20a.h" #include "gk20a/hal_gk20a.h" #include "gk20a/ctxsw_trace_gk20a.h" @@ -498,11 +499,29 @@ static void vgpu_pm_qos_remove(struct device *dev) static int vgpu_pm_init(struct device *dev) { + struct gk20a *g = get_gk20a(dev); + unsigned long *freqs; + int num_freqs; int err = 0; gk20a_dbg_fn(""); __pm_runtime_disable(dev, false); + + if (IS_ENABLED(CONFIG_GK20A_DEVFREQ)) + gk20a_scale_init(dev); + + /* set min/max frequency based on frequency table */ + err = vgpu_clk_get_freqs(dev, &freqs, &num_freqs); + if (err) + return err; + + if (num_freqs < 1) + return -EINVAL; + + g->devfreq->min_freq = freqs[0]; + g->devfreq->max_freq = freqs[num_freqs - 1]; + err = vgpu_pm_qos_init(dev); if (err) return err; @@ -588,12 +607,6 @@ int vgpu_probe(struct platform_device *pdev) return err; } - err = vgpu_pm_init(dev); - if (err) { - dev_err(dev, "pm init failed"); - return err; - } - if (platform->late_probe) { err = platform->late_probe(dev); if (err) { @@ -621,6 +634,12 @@ int vgpu_probe(struct platform_device *pdev) return err; } + err = vgpu_pm_init(dev); + if (err) { + dev_err(dev, "pm init failed"); + return err; + } + priv->intr_handler = kthread_run(vgpu_intr_thread, gk20a, "gk20a"); if (IS_ERR(priv->intr_handler)) return -ENOMEM; diff --git a/include/linux/tegra_vgpu.h b/include/linux/tegra_vgpu.h index c09640832..949aec964 100644 --- a/include/linux/tegra_vgpu.h +++ b/include/linux/tegra_vgpu.h @@ -102,6 +102,8 @@ enum { TEGRA_VGPU_CMD_SUSPEND_CONTEXTS = 66, TEGRA_VGPU_CMD_RESUME_CONTEXTS = 67, TEGRA_VGPU_CMD_CLEAR_SM_ERROR_STATE = 68, + TEGRA_VGPU_CMD_GET_GPU_CLK_RATE = 69, + TEGRA_VGPU_CMD_GET_GPU_FREQ_TABLE = 70, }; struct tegra_vgpu_connect_params { @@ -469,6 +471,13 @@ struct tegra_vgpu_clear_sm_error_state { u32 sm_id; }; +#define TEGRA_VGPU_GPU_FREQ_TABLE_SIZE 25 + +struct tegra_vgpu_get_gpu_freq_table_params { + u32 num_freqs; /* in/out */ + u32 freqs[TEGRA_VGPU_GPU_FREQ_TABLE_SIZE]; /* in kHz */ +}; + struct tegra_vgpu_cmd_msg { u32 cmd; int ret; @@ -518,6 +527,7 @@ struct tegra_vgpu_cmd_msg { struct tegra_vgpu_suspend_resume_contexts suspend_contexts; struct tegra_vgpu_suspend_resume_contexts resume_contexts; struct tegra_vgpu_clear_sm_error_state clear_sm_error_state; + struct tegra_vgpu_get_gpu_freq_table_params get_gpu_freq_table; char padding[192]; } params; };