mirror of
git://nv-tegra.nvidia.com/linux-nvgpu.git
synced 2025-12-23 18:16:01 +03:00
VBIOS code was the last code using gm206 hardware headers. Change the code to use gp106 headers instead, move the code to gp106 directory and delete gm206 HW headers. JIRA NVGPU-218 Change-Id: I7ccd6c2975c767bca871d77a701dbd3395b17f30 Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/1563742 Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
414 lines
10 KiB
C
414 lines
10 KiB
C
/*
|
|
* Copyright (c) 2016-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 <nvgpu/bios.h>
|
|
#include <nvgpu/pmu.h>
|
|
|
|
#include "gk20a/gk20a.h"
|
|
#include "gp106/bios_gp106.h"
|
|
#include "pstate/pstate.h"
|
|
#include "perf/perf.h"
|
|
#include "lpwr.h"
|
|
|
|
static int get_lpwr_idx_table(struct gk20a *g)
|
|
{
|
|
u32 *lpwr_idx_table_ptr;
|
|
u8 *entry_addr;
|
|
u32 idx;
|
|
struct nvgpu_lpwr_bios_idx_data *pidx_data =
|
|
&g->perf_pmu.lpwr.lwpr_bios_data.idx;
|
|
struct nvgpu_bios_lpwr_idx_table_1x_header header = { 0 };
|
|
struct nvgpu_bios_lpwr_idx_table_1x_entry entry = { 0 };
|
|
|
|
lpwr_idx_table_ptr = (u32 *)nvgpu_bios_get_perf_table_ptrs(g,
|
|
g->bios.perf_token, LOWPOWER_TABLE);
|
|
if (lpwr_idx_table_ptr == NULL)
|
|
return -EINVAL;
|
|
|
|
memcpy(&header, lpwr_idx_table_ptr,
|
|
sizeof(struct nvgpu_bios_lpwr_idx_table_1x_header));
|
|
|
|
if (header.entry_count >= LPWR_VBIOS_IDX_ENTRY_COUNT_MAX)
|
|
return -EINVAL;
|
|
|
|
pidx_data->base_sampling_period = (u16)header.base_sampling_period;
|
|
|
|
/* Parse the LPWR Index Table entries.*/
|
|
for (idx = 0; idx < header.entry_count; idx++) {
|
|
entry_addr = (u8 *)lpwr_idx_table_ptr + header.header_size +
|
|
(idx * header.entry_size);
|
|
|
|
memcpy(&entry, entry_addr,
|
|
sizeof(struct nvgpu_bios_lpwr_idx_table_1x_entry));
|
|
|
|
pidx_data->entry[idx].pcie_idx = entry.pcie_idx;
|
|
pidx_data->entry[idx].gr_idx = entry.gr_idx;
|
|
pidx_data->entry[idx].ms_idx = entry.ms_idx;
|
|
pidx_data->entry[idx].di_idx = entry.di_idx;
|
|
pidx_data->entry[idx].gc6_idx = entry.gc6_idx;
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int get_lpwr_gr_table(struct gk20a *g)
|
|
{
|
|
u32 *lpwr_gr_table_ptr;
|
|
u8 *entry_addr;
|
|
u32 idx;
|
|
struct nvgpu_lpwr_bios_gr_data *pgr_data =
|
|
&g->perf_pmu.lpwr.lwpr_bios_data.gr;
|
|
struct nvgpu_bios_lpwr_gr_table_1x_header header = { 0 };
|
|
struct nvgpu_bios_lpwr_gr_table_1x_entry entry = { 0 };
|
|
|
|
lpwr_gr_table_ptr = (u32 *)nvgpu_bios_get_perf_table_ptrs(g,
|
|
g->bios.perf_token, LOWPOWER_GR_TABLE);
|
|
if (lpwr_gr_table_ptr == NULL)
|
|
return -EINVAL;
|
|
|
|
memcpy(&header, lpwr_gr_table_ptr,
|
|
sizeof(struct nvgpu_bios_lpwr_gr_table_1x_header));
|
|
|
|
/* Parse the LPWR Index Table entries.*/
|
|
for (idx = 0; idx < header.entry_count; idx++) {
|
|
entry_addr = (u8 *)lpwr_gr_table_ptr + header.header_size +
|
|
(idx * header.entry_size);
|
|
|
|
memcpy(&entry, entry_addr,
|
|
sizeof(struct nvgpu_bios_lpwr_gr_table_1x_entry));
|
|
|
|
if (BIOS_GET_FIELD(entry.feautre_mask,
|
|
NV_VBIOS_LPWR_MS_FEATURE_MASK_MS)) {
|
|
pgr_data->entry[idx].gr_enabled = true;
|
|
|
|
pgr_data->entry[idx].feature_mask =
|
|
NVGPU_PMU_GR_FEATURE_MASK_ALL;
|
|
|
|
if (!BIOS_GET_FIELD(entry.feautre_mask,
|
|
NV_VBIOS_LPWR_GR_FEATURE_MASK_GR_RPPG))
|
|
pgr_data->entry[idx].feature_mask &=
|
|
~NVGPU_PMU_GR_FEATURE_MASK_RPPG;
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int get_lpwr_ms_table(struct gk20a *g)
|
|
{
|
|
u32 *lpwr_ms_table_ptr;
|
|
u8 *entry_addr;
|
|
u32 idx;
|
|
struct nvgpu_lpwr_bios_ms_data *pms_data =
|
|
&g->perf_pmu.lpwr.lwpr_bios_data.ms;
|
|
struct nvgpu_bios_lpwr_ms_table_1x_header header = { 0 };
|
|
struct nvgpu_bios_lpwr_ms_table_1x_entry entry = { 0 };
|
|
|
|
lpwr_ms_table_ptr = (u32 *)nvgpu_bios_get_perf_table_ptrs(g,
|
|
g->bios.perf_token, LOWPOWER_MS_TABLE);
|
|
if (lpwr_ms_table_ptr == NULL)
|
|
return -EINVAL;
|
|
|
|
memcpy(&header, lpwr_ms_table_ptr,
|
|
sizeof(struct nvgpu_bios_lpwr_ms_table_1x_header));
|
|
|
|
if (header.entry_count >= LPWR_VBIOS_MS_ENTRY_COUNT_MAX)
|
|
return -EINVAL;
|
|
|
|
pms_data->default_entry_idx = (u8)header.default_entry_idx;
|
|
|
|
pms_data->idle_threshold_us = (u32)(header.idle_threshold_us * 10);
|
|
|
|
/* Parse the LPWR MS Table entries.*/
|
|
for (idx = 0; idx < header.entry_count; idx++) {
|
|
entry_addr = (u8 *)lpwr_ms_table_ptr + header.header_size +
|
|
(idx * header.entry_size);
|
|
|
|
memcpy(&entry, entry_addr,
|
|
sizeof(struct nvgpu_bios_lpwr_ms_table_1x_entry));
|
|
|
|
if (BIOS_GET_FIELD(entry.feautre_mask,
|
|
NV_VBIOS_LPWR_MS_FEATURE_MASK_MS)) {
|
|
pms_data->entry[idx].ms_enabled = true;
|
|
|
|
pms_data->entry[idx].feature_mask =
|
|
NVGPU_PMU_MS_FEATURE_MASK_ALL;
|
|
|
|
if (!BIOS_GET_FIELD(entry.feautre_mask,
|
|
NV_VBIOS_LPWR_MS_FEATURE_MASK_MS_CLOCK_GATING))
|
|
pms_data->entry[idx].feature_mask &=
|
|
~NVGPU_PMU_MS_FEATURE_MASK_CLOCK_GATING;
|
|
|
|
if (!BIOS_GET_FIELD(entry.feautre_mask,
|
|
NV_VBIOS_LPWR_MS_FEATURE_MASK_MS_SWASR))
|
|
pms_data->entry[idx].feature_mask &=
|
|
~NVGPU_PMU_MS_FEATURE_MASK_SW_ASR;
|
|
|
|
if (!BIOS_GET_FIELD(entry.feautre_mask,
|
|
NV_VBIOS_LPWR_MS_FEATURE_MASK_MS_RPPG))
|
|
pms_data->entry[idx].feature_mask &=
|
|
~NVGPU_PMU_MS_FEATURE_MASK_RPPG;
|
|
}
|
|
|
|
pms_data->entry[idx].dynamic_current_logic =
|
|
entry.dynamic_current_logic;
|
|
|
|
pms_data->entry[idx].dynamic_current_sram =
|
|
entry.dynamic_current_sram;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 nvgpu_lpwr_pg_setup(struct gk20a *g)
|
|
{
|
|
u32 err = 0;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
err = get_lpwr_gr_table(g);
|
|
if (err)
|
|
return err;
|
|
|
|
err = get_lpwr_ms_table(g);
|
|
if (err)
|
|
return err;
|
|
|
|
err = get_lpwr_idx_table(g);
|
|
|
|
return err;
|
|
}
|
|
|
|
static void nvgpu_pmu_handle_param_lpwr_msg(struct gk20a *g,
|
|
struct pmu_msg *msg, void *param,
|
|
u32 handle, u32 status)
|
|
{
|
|
u32 *ack_status = param;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
if (status != 0) {
|
|
nvgpu_err(g, "LWPR PARAM cmd aborted");
|
|
return;
|
|
}
|
|
|
|
*ack_status = 1;
|
|
|
|
nvgpu_pmu_dbg(g, "lpwr-param is acknowledged from PMU %x",
|
|
msg->msg.pg.msg_type);
|
|
}
|
|
|
|
int nvgpu_lwpr_mclk_change(struct gk20a *g, u32 pstate)
|
|
{
|
|
struct pmu_cmd cmd;
|
|
u32 seq, status = 0;
|
|
u32 payload = NV_PMU_PG_PARAM_MCLK_CHANGE_MS_SWASR_ENABLED;
|
|
struct clk_set_info *pstate_info;
|
|
u32 ack_status = 0;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
pstate_info = pstate_get_clk_set_info(g, pstate,
|
|
clkwhich_mclk);
|
|
if (!pstate_info)
|
|
return -EINVAL;
|
|
|
|
if (pstate_info->max_mhz >
|
|
MAX_SWASR_MCLK_FREQ_WITHOUT_WR_TRAINING_MAXWELL_MHZ)
|
|
payload |=
|
|
NV_PMU_PG_PARAM_MCLK_CHANGE_GDDR5_WR_TRAINING_ENABLED;
|
|
|
|
if (payload != g->perf_pmu.lpwr.mclk_change_cache) {
|
|
g->perf_pmu.lpwr.mclk_change_cache = payload;
|
|
|
|
cmd.hdr.unit_id = PMU_UNIT_PG;
|
|
cmd.hdr.size = PMU_CMD_HDR_SIZE +
|
|
sizeof(struct pmu_pg_cmd_mclk_change);
|
|
cmd.cmd.pg.mclk_change.cmd_type =
|
|
PMU_PG_CMD_ID_PG_PARAM;
|
|
cmd.cmd.pg.mclk_change.cmd_id =
|
|
PMU_PG_PARAM_CMD_MCLK_CHANGE;
|
|
cmd.cmd.pg.mclk_change.data = payload;
|
|
|
|
nvgpu_pmu_dbg(g, "cmd post MS PMU_PG_PARAM_CMD_MCLK_CHANGE");
|
|
status = nvgpu_pmu_cmd_post(g, &cmd, NULL, NULL,
|
|
PMU_COMMAND_QUEUE_HPQ,
|
|
nvgpu_pmu_handle_param_lpwr_msg, &ack_status, &seq, ~0);
|
|
|
|
pmu_wait_message_cond(&g->pmu, gk20a_get_gr_idle_timeout(g),
|
|
&ack_status, 1);
|
|
if (ack_status == 0) {
|
|
status = -EINVAL;
|
|
nvgpu_err(g, "MCLK-CHANGE ACK failed");
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
u32 nvgpu_lpwr_post_init(struct gk20a *g)
|
|
{
|
|
struct pmu_cmd cmd;
|
|
u32 seq;
|
|
u32 status = 0;
|
|
u32 ack_status = 0;
|
|
|
|
memset(&cmd, 0, sizeof(struct pmu_cmd));
|
|
cmd.hdr.unit_id = PMU_UNIT_PG;
|
|
cmd.hdr.size = PMU_CMD_HDR_SIZE +
|
|
sizeof(struct pmu_pg_cmd_post_init_param);
|
|
|
|
cmd.cmd.pg.post_init.cmd_type =
|
|
PMU_PG_CMD_ID_PG_PARAM;
|
|
cmd.cmd.pg.post_init.cmd_id =
|
|
PMU_PG_PARAM_CMD_POST_INIT;
|
|
|
|
nvgpu_pmu_dbg(g, "cmd post post-init PMU_PG_PARAM_CMD_POST_INIT");
|
|
status = nvgpu_pmu_cmd_post(g, &cmd, NULL, NULL,
|
|
PMU_COMMAND_QUEUE_LPQ,
|
|
nvgpu_pmu_handle_param_lpwr_msg, &ack_status, &seq, ~0);
|
|
|
|
pmu_wait_message_cond(&g->pmu, gk20a_get_gr_idle_timeout(g),
|
|
&ack_status, 1);
|
|
if (ack_status == 0) {
|
|
status = -EINVAL;
|
|
nvgpu_err(g, "post-init ack failed");
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
u32 nvgpu_lpwr_is_mscg_supported(struct gk20a *g, u32 pstate_num)
|
|
{
|
|
struct nvgpu_lpwr_bios_ms_data *pms_data =
|
|
&g->perf_pmu.lpwr.lwpr_bios_data.ms;
|
|
struct nvgpu_lpwr_bios_idx_data *pidx_data =
|
|
&g->perf_pmu.lpwr.lwpr_bios_data.idx;
|
|
struct pstate *pstate = pstate_find(g, pstate_num);
|
|
u32 ms_idx;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
if (!pstate)
|
|
return 0;
|
|
|
|
ms_idx = pidx_data->entry[pstate->lpwr_entry_idx].ms_idx;
|
|
if (pms_data->entry[ms_idx].ms_enabled)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
u32 nvgpu_lpwr_is_rppg_supported(struct gk20a *g, u32 pstate_num)
|
|
{
|
|
struct nvgpu_lpwr_bios_gr_data *pgr_data =
|
|
&g->perf_pmu.lpwr.lwpr_bios_data.gr;
|
|
struct nvgpu_lpwr_bios_idx_data *pidx_data =
|
|
&g->perf_pmu.lpwr.lwpr_bios_data.idx;
|
|
struct pstate *pstate = pstate_find(g, pstate_num);
|
|
u32 idx;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
if (!pstate)
|
|
return 0;
|
|
|
|
idx = pidx_data->entry[pstate->lpwr_entry_idx].gr_idx;
|
|
if (pgr_data->entry[idx].gr_enabled)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
int nvgpu_lpwr_enable_pg(struct gk20a *g, bool pstate_lock)
|
|
{
|
|
struct nvgpu_pmu *pmu = &g->pmu;
|
|
u32 status = 0;
|
|
u32 is_mscg_supported = 0;
|
|
u32 is_rppg_supported = 0;
|
|
u32 present_pstate = 0;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
if (pstate_lock)
|
|
nvgpu_clk_arb_pstate_change_lock(g, true);
|
|
nvgpu_mutex_acquire(&pmu->pg_mutex);
|
|
|
|
present_pstate = nvgpu_clk_arb_get_current_pstate(g);
|
|
|
|
is_mscg_supported = nvgpu_lpwr_is_mscg_supported(g,
|
|
present_pstate);
|
|
if (is_mscg_supported && g->mscg_enabled) {
|
|
if (!pmu->mscg_stat)
|
|
pmu->mscg_stat = PMU_MSCG_ENABLED;
|
|
}
|
|
|
|
is_rppg_supported = nvgpu_lpwr_is_rppg_supported(g,
|
|
present_pstate);
|
|
if (is_rppg_supported) {
|
|
if (g->support_pmu && g->can_elpg)
|
|
status = nvgpu_pmu_enable_elpg(g);
|
|
}
|
|
|
|
nvgpu_mutex_release(&pmu->pg_mutex);
|
|
if (pstate_lock)
|
|
nvgpu_clk_arb_pstate_change_lock(g, false);
|
|
|
|
return status;
|
|
}
|
|
|
|
int nvgpu_lpwr_disable_pg(struct gk20a *g, bool pstate_lock)
|
|
{
|
|
struct nvgpu_pmu *pmu = &g->pmu;
|
|
int status = 0;
|
|
u32 is_mscg_supported = 0;
|
|
u32 is_rppg_supported = 0;
|
|
u32 present_pstate = 0;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
if (pstate_lock)
|
|
nvgpu_clk_arb_pstate_change_lock(g, true);
|
|
nvgpu_mutex_acquire(&pmu->pg_mutex);
|
|
|
|
present_pstate = nvgpu_clk_arb_get_current_pstate(g);
|
|
|
|
is_rppg_supported = nvgpu_lpwr_is_rppg_supported(g,
|
|
present_pstate);
|
|
if (is_rppg_supported) {
|
|
if (g->support_pmu && g->elpg_enabled) {
|
|
status = nvgpu_pmu_disable_elpg(g);
|
|
if (status)
|
|
goto exit_unlock;
|
|
}
|
|
}
|
|
|
|
is_mscg_supported = nvgpu_lpwr_is_mscg_supported(g,
|
|
present_pstate);
|
|
if (is_mscg_supported && g->mscg_enabled) {
|
|
if (pmu->mscg_stat)
|
|
pmu->mscg_stat = PMU_MSCG_DISABLED;
|
|
}
|
|
|
|
exit_unlock:
|
|
nvgpu_mutex_release(&pmu->pg_mutex);
|
|
if (pstate_lock)
|
|
nvgpu_clk_arb_pstate_change_lock(g, false);
|
|
|
|
gk20a_dbg_fn("done");
|
|
return status;
|
|
}
|