From 432017248e432df0619dc2df30f915a52634338f Mon Sep 17 00:00:00 2001 From: Vijayakumar Subbu Date: Sat, 30 Jul 2016 10:44:30 -0700 Subject: gpu: nvgpu: Add dGPU clocks support JIRA DNVGPU-42 Change-Id: Ic2fca9d0cf82f2823654ac5e8f0772a1eec7b3b5 Signed-off-by: Vijayakumar Subbu Signed-off-by: Terje Bergstrom Reviewed-on: http://git-master/r/1205850 (cherry picked from commit b9f5c6bc4e649162d63e33d65b725872340ca114) Reviewed-on: http://git-master/r/1227257 GVS: Gerrit_Virtual_Submit --- drivers/gpu/nvgpu/perf/perf.c | 98 ++++ drivers/gpu/nvgpu/perf/perf.h | 60 +++ drivers/gpu/nvgpu/perf/vfe_equ.c | 590 +++++++++++++++++++++ drivers/gpu/nvgpu/perf/vfe_equ.h | 76 +++ drivers/gpu/nvgpu/perf/vfe_var.c | 1048 ++++++++++++++++++++++++++++++++++++++ drivers/gpu/nvgpu/perf/vfe_var.h | 97 ++++ 6 files changed, 1969 insertions(+) create mode 100644 drivers/gpu/nvgpu/perf/perf.c create mode 100644 drivers/gpu/nvgpu/perf/perf.h create mode 100644 drivers/gpu/nvgpu/perf/vfe_equ.c create mode 100644 drivers/gpu/nvgpu/perf/vfe_equ.h create mode 100644 drivers/gpu/nvgpu/perf/vfe_var.c create mode 100644 drivers/gpu/nvgpu/perf/vfe_var.h (limited to 'drivers/gpu/nvgpu/perf') diff --git a/drivers/gpu/nvgpu/perf/perf.c b/drivers/gpu/nvgpu/perf/perf.c new file mode 100644 index 00000000..3821a8dc --- /dev/null +++ b/drivers/gpu/nvgpu/perf/perf.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2016, 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 "gk20a/gk20a.h" +#include "perf.h" +#include "pmuif/gpmuifperf.h" +#include "pmuif/gpmuifperfvfe.h" +#include "gk20a/pmu_gk20a.h" + +struct perfrpc_pmucmdhandler_params { + struct nv_pmu_perf_rpc *prpccall; + u32 success; +}; + +static void perfrpc_pmucmdhandler(struct gk20a *g, struct pmu_msg *msg, + void *param, u32 handle, u32 status) +{ + struct perfrpc_pmucmdhandler_params *phandlerparams = + (struct perfrpc_pmucmdhandler_params *)param; + + gk20a_dbg_info(""); + + if (msg->msg.perf.msg_type != NV_PMU_PERF_MSG_ID_RPC) { + gk20a_err(dev_from_gk20a(g), + "unsupported msg for VFE LOAD RPC %x", + msg->msg.perf.msg_type); + return; + } + + if (phandlerparams->prpccall->b_supported) + phandlerparams->success = 1; +} + +u32 perf_pmu_vfe_load(struct gk20a *g) +{ + struct pmu_cmd cmd; + struct pmu_msg msg; + struct pmu_payload payload = { {0} }; + u32 status; + u32 seqdesc; + struct nv_pmu_perf_rpc rpccall = {0}; + struct perfrpc_pmucmdhandler_params handler = {0}; + + rpccall.function = NV_PMU_PERF_RPC_ID_VFE_LOAD; + rpccall.params.vfe_load.b_load = true; + cmd.hdr.unit_id = PMU_UNIT_PERF; + cmd.hdr.size = (u32)sizeof(struct nv_pmu_perf_cmd) + + (u32)sizeof(struct pmu_hdr); + + cmd.cmd.perf.cmd_type = NV_PMU_PERF_CMD_ID_RPC; + msg.hdr.size = sizeof(struct pmu_msg); + + payload.in.buf = (u8 *)&rpccall; + payload.in.size = (u32)sizeof(struct nv_pmu_perf_rpc); + payload.in.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; + payload.in.offset = NV_PMU_PERF_CMD_RPC_ALLOC_OFFSET; + + payload.out.buf = (u8 *)&rpccall; + payload.out.size = (u32)sizeof(struct nv_pmu_perf_rpc); + payload.out.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; + payload.out.offset = NV_PMU_PERF_MSG_RPC_ALLOC_OFFSET; + + handler.prpccall = &rpccall; + handler.success = 0; + + status = gk20a_pmu_cmd_post(g, &cmd, NULL, &payload, + PMU_COMMAND_QUEUE_LPQ, + perfrpc_pmucmdhandler, (void *)&handler, + &seqdesc, ~0); + + if (status) { + gk20a_err(dev_from_gk20a(g), + "unable to post perf RPC cmd %x", + cmd.cmd.perf.cmd_type); + goto done; + } + + pmu_wait_message_cond(&g->pmu, + gk20a_get_gr_idle_timeout(g), + &handler.success, 1); + + if (handler.success == 0) { + status = -EINVAL; + gk20a_err(dev_from_gk20a(g), "rpc call to load VFE failed"); + } +done: + return status; +} diff --git a/drivers/gpu/nvgpu/perf/perf.h b/drivers/gpu/nvgpu/perf/perf.h new file mode 100644 index 00000000..02aed7a6 --- /dev/null +++ b/drivers/gpu/nvgpu/perf/perf.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016, 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 _PERF_H_ +#define _PERF_H_ + +#include "vfe_equ.h" +#include "vfe_var.h" +#include "gk20a/gk20a.h" + +#define CTRL_PERF_VFE_VAR_TYPE_INVALID 0x00 +#define CTRL_PERF_VFE_VAR_TYPE_DERIVED 0x01 +#define CTRL_PERF_VFE_VAR_TYPE_DERIVED_PRODUCT 0x02 +#define CTRL_PERF_VFE_VAR_TYPE_DERIVED_SUM 0x03 +#define CTRL_PERF_VFE_VAR_TYPE_SINGLE 0x04 +#define CTRL_PERF_VFE_VAR_TYPE_SINGLE_FREQUENCY 0x05 +#define CTRL_PERF_VFE_VAR_TYPE_SINGLE_SENSED 0x06 +#define CTRL_PERF_VFE_VAR_TYPE_SINGLE_SENSED_FUSE 0x07 +#define CTRL_PERF_VFE_VAR_TYPE_SINGLE_SENSED_TEMP 0x08 +#define CTRL_PERF_VFE_VAR_TYPE_SINGLE_VOLTAGE 0x09 + +#define CTRL_PERF_VFE_VAR_SINGLE_OVERRIDE_TYPE_NONE 0x00 +#define CTRL_PERF_VFE_VAR_SINGLE_OVERRIDE_TYPE_VALUE 0x01 +#define CTRL_PERF_VFE_VAR_SINGLE_OVERRIDE_TYPE_OFFSET 0x02 +#define CTRL_PERF_VFE_VAR_SINGLE_OVERRIDE_TYPE_SCALE 0x03 + +#define CTRL_PERF_VFE_EQU_TYPE_INVALID 0x00 +#define CTRL_PERF_VFE_EQU_TYPE_COMPARE 0x01 +#define CTRL_PERF_VFE_EQU_TYPE_MINMAX 0x02 +#define CTRL_PERF_VFE_EQU_TYPE_QUADRATIC 0x03 + +#define CTRL_PERF_VFE_EQU_OUTPUT_TYPE_UNITLESS 0x00 +#define CTRL_PERF_VFE_EQU_OUTPUT_TYPE_FREQ_MHZ 0x01 +#define CTRL_PERF_VFE_EQU_OUTPUT_TYPE_VOLT_UV 0x02 +#define CTRL_PERF_VFE_EQU_OUTPUT_TYPE_VF_GAIN 0x03 +#define CTRL_PERF_VFE_EQU_OUTPUT_TYPE_VOLT_DELTA_UV 0x04 + +#define CTRL_PERF_VFE_EQU_QUADRATIC_COEFF_COUNT 0x03 + +#define CTRL_PERF_VFE_EQU_COMPARE_FUNCTION_EQUAL 0x00 +#define CTRL_PERF_VFE_EQU_COMPARE_FUNCTION_GREATER_EQ 0x01 +#define CTRL_PERF_VFE_EQU_COMPARE_FUNCTION_GREATER 0x02 + +struct perf_pmupstate { + struct vfe_vars vfe_varobjs; + struct vfe_equs vfe_equobjs; +}; + +u32 perf_pmu_vfe_load(struct gk20a *g); + +#endif diff --git a/drivers/gpu/nvgpu/perf/vfe_equ.c b/drivers/gpu/nvgpu/perf/vfe_equ.c new file mode 100644 index 00000000..6630fb21 --- /dev/null +++ b/drivers/gpu/nvgpu/perf/vfe_equ.c @@ -0,0 +1,590 @@ +/* + * Copyright (c) 2016, 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 "gk20a/gk20a.h" +#include "perf.h" +#include "vfe_equ.h" +#include "include/bios.h" +#include "boardobj/boardobjgrp.h" +#include "boardobj/boardobjgrp_e255.h" +#include "pmuif/gpmuifboardobj.h" +#include "pmuif/gpmuifperf.h" +#include "pmuif/gpmuifperfvfe.h" +#include "gm206/bios_gm206.h" +#include "ctrl/ctrlclk.h" +#include "ctrl/ctrlvolt.h" +#include "gk20a/pmu_gk20a.h" + +static struct vfe_equ *construct_vfe_equ(struct gk20a *g, void *pargs); +static u32 devinit_get_vfe_equ_table(struct gk20a *g, + struct vfe_equs *pequobjs); + +static u32 _vfe_equs_pmudatainit(struct gk20a *g, + struct boardobjgrp *pboardobjgrp, + struct nv_pmu_boardobjgrp_super *pboardobjgrppmu) +{ + u32 status = 0; + + status = boardobjgrp_pmudatainit_e255(g, pboardobjgrp, pboardobjgrppmu); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error updating pmu boardobjgrp for vfe equ 0x%x", + status); + goto done; + } + +done: + return status; +} + +static u32 _vfe_equs_pmudata_instget(struct gk20a *g, + struct nv_pmu_boardobjgrp *pmuboardobjgrp, + struct nv_pmu_boardobj **ppboardobjpmudata, + u8 idx) +{ + struct nv_pmu_perf_vfe_equ_boardobj_grp_set *pgrp_set = + (struct nv_pmu_perf_vfe_equ_boardobj_grp_set *)pmuboardobjgrp; + + gk20a_dbg_info(""); + + /* check whether pmuboardobjgrp has a valid boardobj in index */ + if (idx >= CTRL_BOARDOBJGRP_E255_MAX_OBJECTS) + return -EINVAL; + + *ppboardobjpmudata = (struct nv_pmu_boardobj *) + &pgrp_set->objects[idx].data.board_obj; + gk20a_dbg_info(" Done"); + return 0; +} + +u32 vfe_equ_sw_setup(struct gk20a *g) +{ + u32 status; + struct boardobjgrp *pboardobjgrp = NULL; + struct vfe_equs *pvfeequobjs; + + gk20a_dbg_info(""); + + status = boardobjgrpconstruct_e255(&g->perf_pmu.vfe_equobjs.super); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error creating boardobjgrp for clk domain, status - 0x%x", + status); + goto done; + } + + pboardobjgrp = &g->perf_pmu.vfe_equobjs.super.super; + pvfeequobjs = &(g->perf_pmu.vfe_equobjs); + + BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, PERF, VFE_EQU); + + status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, + perf, PERF, vfe_equ, VFE_EQU); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", + status); + goto done; + } + + pboardobjgrp->pmudatainit = _vfe_equs_pmudatainit; + pboardobjgrp->pmudatainstget = _vfe_equs_pmudata_instget; + + status = devinit_get_vfe_equ_table(g, pvfeequobjs); + if (status) + goto done; + +done: + gk20a_dbg_info(" done status %x", status); + return status; +} + +u32 vfe_equ_pmu_setup(struct gk20a *g) +{ + u32 status; + struct boardobjgrp *pboardobjgrp = NULL; + + gk20a_dbg_info(""); + + pboardobjgrp = &g->perf_pmu.vfe_equobjs.super.super; + + if (!pboardobjgrp->bconstructed) + return -EINVAL; + + status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); + + gk20a_dbg_info("Done"); + return status; +} + +static u32 devinit_get_vfe_equ_table(struct gk20a *g, + struct vfe_equs *pvfeequobjs) +{ + u32 status = 0; + u8 *vfeequs_tbl_ptr = NULL; + struct vbios_vfe_3x_header_struct vfeequs_tbl_header = { 0 }; + struct vbios_vfe_3x_equ_entry_struct equ = { 0 }; + u8 *vfeequs_tbl_entry_ptr = NULL; + u8 *rd_offset_ptr = NULL; + u32 index = 0; + struct vfe_equ *pequ; + u8 equ_type = 0; + u32 szfmt; + union { + struct boardobj board_obj; + struct vfe_equ super; + struct vfe_equ_compare compare; + struct vfe_equ_minmax minmax; + struct vfe_equ_quadratic quadratic; + } equ_data; + + gk20a_dbg_info(""); + + if (g->ops.bios.get_perf_table_ptrs) { + vfeequs_tbl_ptr = (u8 *)g->ops.bios.get_perf_table_ptrs(g, + g->bios.perf_token, + CONTINUOUS_VIRTUAL_BINNING_TABLE); + if (vfeequs_tbl_ptr == NULL) { + status = -EINVAL; + goto done; + } + } + + memcpy(&vfeequs_tbl_header, vfeequs_tbl_ptr, + VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07); + if (vfeequs_tbl_header.header_size != VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07) { + status = -EINVAL; + goto done; + } + + if (vfeequs_tbl_header.vfe_equ_entry_size == + VBIOS_VFE_3X_EQU_ENTRY_SIZE_17) + szfmt = VBIOS_VFE_3X_EQU_ENTRY_SIZE_17; + else if (vfeequs_tbl_header.vfe_equ_entry_size == + VBIOS_VFE_3X_EQU_ENTRY_SIZE_18) + szfmt = VBIOS_VFE_3X_EQU_ENTRY_SIZE_18; + else { + status = -EINVAL; + goto done; + } + + vfeequs_tbl_entry_ptr = vfeequs_tbl_ptr + + vfeequs_tbl_header.header_size + + (vfeequs_tbl_header.vfe_var_entry_count * + vfeequs_tbl_header.vfe_var_entry_size); + + for (index = 0; + index < vfeequs_tbl_header.vfe_equ_entry_count; + index++) { + memset(&equ, 0, sizeof(struct vbios_vfe_3x_equ_entry_struct)); + + rd_offset_ptr = vfeequs_tbl_entry_ptr + + (index * vfeequs_tbl_header.vfe_equ_entry_size); + + memcpy(&equ, rd_offset_ptr, szfmt); + + equ_data.super.var_idx = (u8)equ.var_idx; + equ_data.super.equ_idx_next = + (equ.equ_idx_next == VBIOS_VFE_3X_EQU_ENTRY_IDX_INVALID) ? + CTRL_BOARDOBJ_IDX_INVALID : (u8)equ.equ_idx_next; + equ_data.super.out_range_min = equ.out_range_min; + equ_data.super.out_range_max = equ.out_range_max; + + switch (BIOS_GET_FIELD(equ.param3, VBIOS_VFE_3X_EQU_ENTRY_PAR3_OUTPUT_TYPE)) { + case VBIOS_VFE_3X_EQU_ENTRY_PAR3_OUTPUT_TYPE_UNITLESS: + equ_data.super.output_type = + CTRL_PERF_VFE_EQU_OUTPUT_TYPE_UNITLESS; + break; + + case VBIOS_VFE_3X_EQU_ENTRY_PAR3_OUTPUT_TYPE_FREQ_MHZ: + equ_data.super.output_type = + CTRL_PERF_VFE_EQU_OUTPUT_TYPE_FREQ_MHZ; + break; + + case VBIOS_VFE_3X_EQU_ENTRY_PAR3_OUTPUT_TYPE_VOLT_UV: + equ_data.super.output_type = + CTRL_PERF_VFE_EQU_OUTPUT_TYPE_VOLT_UV; + break; + + case VBIOS_VFE_3X_EQU_ENTRY_PAR3_OUTPUT_TYPE_VF_GAIN: + equ_data.super.output_type = + CTRL_PERF_VFE_EQU_OUTPUT_TYPE_VF_GAIN; + break; + + case VBIOS_VFE_3X_EQU_ENTRY_PAR3_OUTPUT_TYPE_VOLT_DELTA_UV: + equ_data.super.output_type = + CTRL_PERF_VFE_EQU_OUTPUT_TYPE_VOLT_DELTA_UV; + break; + + default: + gk20a_err(dev_from_gk20a(g), + "unrecognized output id @vfeequ index %d", + index); + goto done; + } + + switch ((u8)equ.type) { + case VBIOS_VFE_3X_EQU_ENTRY_TYPE_DISABLED: + case VBIOS_VFE_3X_EQU_ENTRY_TYPE_QUADRATIC_FXP: + case VBIOS_VFE_3X_EQU_ENTRY_TYPE_MINMAX_FXP: + continue; + break; + + case VBIOS_VFE_3X_EQU_ENTRY_TYPE_QUADRATIC: + equ_type = CTRL_PERF_VFE_EQU_TYPE_QUADRATIC; + equ_data.quadratic.coeffs[0] = equ.param0; + equ_data.quadratic.coeffs[1] = equ.param1; + equ_data.quadratic.coeffs[2] = equ.param2; + break; + + case VBIOS_VFE_3X_EQU_ENTRY_TYPE_MINMAX: + equ_type = CTRL_PERF_VFE_EQU_TYPE_MINMAX; + equ_data.minmax.b_max = BIOS_GET_FIELD(equ.param0, + VBIOS_VFE_3X_EQU_ENTRY_PAR0_MINMAX_CRIT) && + VBIOS_VFE_3X_EQU_ENTRY_PAR0_MINMAX_CRIT_MAX; + equ_data.minmax.equ_idx0 = (u8)BIOS_GET_FIELD( + equ.param0, + VBIOS_VFE_3X_EQU_ENTRY_PAR0_MINMAX_VFE_EQU_IDX_0); + equ_data.minmax.equ_idx1 = (u8)BIOS_GET_FIELD( + equ.param0, + VBIOS_VFE_3X_EQU_ENTRY_PAR0_MINMAX_VFE_EQU_IDX_1); + break; + + case VBIOS_VFE_3X_EQU_ENTRY_TYPE_COMPARE: + { + u8 cmp_func = (u8)BIOS_GET_FIELD( + equ.param1, + VBIOS_VFE_3X_EQU_ENTRY_PAR1_COMPARE_FUNCTION); + equ_type = CTRL_PERF_VFE_EQU_TYPE_COMPARE; + + switch (cmp_func) { + case VBIOS_VFE_3X_EQU_ENTRY_PAR1_COMPARE_FUNCTION_EQUAL: + equ_data.compare.func_id = + CTRL_PERF_VFE_EQU_COMPARE_FUNCTION_EQUAL; + break; + + case VBIOS_VFE_3X_EQU_ENTRY_PAR1_COMPARE_FUNCTION_GREATER_EQ: + equ_data.compare.func_id = + CTRL_PERF_VFE_EQU_COMPARE_FUNCTION_GREATER_EQ; + break; + case VBIOS_VFE_3X_EQU_ENTRY_PAR1_COMPARE_FUNCTION_GREATER: + equ_data.compare.func_id = + CTRL_PERF_VFE_EQU_COMPARE_FUNCTION_GREATER; + break; + default: + gk20a_err(dev_from_gk20a(g), + "invalid vfe compare index %x type %x ", + index, cmp_func); + status = -EINVAL; + goto done; + } + equ_data.compare.equ_idx_true = (u8)BIOS_GET_FIELD( + equ.param1, + VBIOS_VFE_3X_EQU_ENTRY_PAR1_COMPARE_VFE_EQU_IDX_TRUE); + equ_data.compare.equ_idx_false = (u8)BIOS_GET_FIELD( + equ.param1, + VBIOS_VFE_3X_EQU_ENTRY_PAR1_COMPARE_VFE_EQU_IDX_FALSE); + equ_data.compare.criteria = equ.param0; + break; + } + default: + status = -EINVAL; + gk20a_err(dev_from_gk20a(g), + "Invalid equ[%d].type = 0x%x.", + index, (u8)equ.type); + goto done; + } + + equ_data.board_obj.type = equ_type; + pequ = construct_vfe_equ(g, (void *)&equ_data); + + if (pequ == NULL) { + gk20a_err(dev_from_gk20a(g), + "error constructing vfe_equ boardobj %d", index); + status = -EINVAL; + goto done; + } + + status = boardobjgrp_objinsert(&pvfeequobjs->super.super, + (struct boardobj *)pequ, index); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error adding vfe_equ boardobj %d", index); + status = -EINVAL; + goto done; + } + } +done: + gk20a_dbg_info(" done status %x", status); + return status; +} + +static u32 _vfe_equ_pmudatainit_super(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + u32 status = 0; + struct vfe_equ *pvfe_equ; + struct nv_pmu_vfe_equ *pset; + + gk20a_dbg_info(""); + + status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status != 0) + return status; + + pvfe_equ = (struct vfe_equ *)board_obj_ptr; + + pset = (struct nv_pmu_vfe_equ *) + ppmudata; + + pset->var_idx = pvfe_equ->var_idx; + pset->equ_idx_next = pvfe_equ->equ_idx_next; + pset->output_type = pvfe_equ->output_type; + pset->out_range_min = pvfe_equ->out_range_min; + pset->out_range_max = pvfe_equ->out_range_max; + + return status; +} + +static u32 vfe_equ_construct_super(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct vfe_equ *pvfeequ; + struct vfe_equ *ptmpequ = (struct vfe_equ *)pargs; + u32 status = 0; + + status = boardobj_construct_super(g, ppboardobj, + size, pargs); + if (status) + return -EINVAL; + + pvfeequ = (struct vfe_equ *)*ppboardobj; + + pvfeequ->super.pmudatainit = + _vfe_equ_pmudatainit_super; + + pvfeequ->var_idx = ptmpequ->var_idx; + pvfeequ->equ_idx_next = ptmpequ->equ_idx_next; + pvfeequ->output_type = ptmpequ->output_type; + pvfeequ->out_range_min = ptmpequ->out_range_min; + pvfeequ->out_range_max = ptmpequ->out_range_max; + + return status; +} + +static u32 _vfe_equ_pmudatainit_compare(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + u32 status = 0; + struct vfe_equ_compare *pvfe_equ_compare; + struct nv_pmu_vfe_equ_compare *pset; + + gk20a_dbg_info(""); + + status = _vfe_equ_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status != 0) + return status; + + pvfe_equ_compare = (struct vfe_equ_compare *)board_obj_ptr; + + pset = (struct nv_pmu_vfe_equ_compare *) ppmudata; + + pset->func_id = pvfe_equ_compare->func_id; + pset->equ_idx_true = pvfe_equ_compare->equ_idx_true; + pset->equ_idx_false = pvfe_equ_compare->equ_idx_false; + pset->criteria = pvfe_equ_compare->criteria; + + return status; +} + + +static u32 vfe_equ_construct_compare(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct vfe_equ_compare *pvfeequ; + struct vfe_equ_compare *ptmpequ = + (struct vfe_equ_compare *)pargs; + u32 status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_PERF_VFE_EQU_TYPE_COMPARE) + return -EINVAL; + + ptmpobj->type_mask |= BIT(CTRL_PERF_VFE_EQU_TYPE_COMPARE); + status = vfe_equ_construct_super(g, ppboardobj, size, pargs); + if (status) + return -EINVAL; + + pvfeequ = (struct vfe_equ_compare *)*ppboardobj; + + pvfeequ->super.super.pmudatainit = + _vfe_equ_pmudatainit_compare; + + pvfeequ->func_id = ptmpequ->func_id; + pvfeequ->equ_idx_true = ptmpequ->equ_idx_true; + pvfeequ->equ_idx_false = ptmpequ->equ_idx_false; + pvfeequ->criteria = ptmpequ->criteria; + + + return status; +} + +static u32 _vfe_equ_pmudatainit_minmax(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + u32 status = 0; + struct vfe_equ_minmax *pvfe_equ_minmax; + struct nv_pmu_vfe_equ_minmax *pset; + + gk20a_dbg_info(""); + + status = _vfe_equ_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status != 0) + return status; + + pvfe_equ_minmax = (struct vfe_equ_minmax *)board_obj_ptr; + + pset = (struct nv_pmu_vfe_equ_minmax *) + ppmudata; + + pset->b_max = pvfe_equ_minmax->b_max; + pset->equ_idx0 = pvfe_equ_minmax->equ_idx0; + pset->equ_idx1 = pvfe_equ_minmax->equ_idx1; + + return status; +} + +static u32 vfe_equ_construct_minmax(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct vfe_equ_minmax *pvfeequ; + struct vfe_equ_minmax *ptmpequ = + (struct vfe_equ_minmax *)pargs; + u32 status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_PERF_VFE_EQU_TYPE_MINMAX) + return -EINVAL; + + ptmpobj->type_mask |= BIT(CTRL_PERF_VFE_EQU_TYPE_MINMAX); + status = vfe_equ_construct_super(g, ppboardobj, size, pargs); + if (status) + return -EINVAL; + + pvfeequ = (struct vfe_equ_minmax *)*ppboardobj; + + pvfeequ->super.super.pmudatainit = + _vfe_equ_pmudatainit_minmax; + pvfeequ->b_max = ptmpequ->b_max; + pvfeequ->equ_idx0 = ptmpequ->equ_idx0; + pvfeequ->equ_idx1 = ptmpequ->equ_idx1; + + return status; +} + +static u32 _vfe_equ_pmudatainit_quadratic(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + u32 status = 0; + struct vfe_equ_quadratic *pvfe_equ_quadratic; + struct nv_pmu_vfe_equ_quadratic *pset; + u32 i; + + gk20a_dbg_info(""); + + status = _vfe_equ_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status != 0) + return status; + + pvfe_equ_quadratic = (struct vfe_equ_quadratic *)board_obj_ptr; + + pset = (struct nv_pmu_vfe_equ_quadratic *) ppmudata; + + for (i = 0; i < CTRL_PERF_VFE_EQU_QUADRATIC_COEFF_COUNT; i++) + pset->coeffs[i] = pvfe_equ_quadratic->coeffs[i]; + + return status; +} + +static u32 vfe_equ_construct_quadratic(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct vfe_equ_quadratic *pvfeequ; + struct vfe_equ_quadratic *ptmpequ = + (struct vfe_equ_quadratic *)pargs; + u32 status = 0; + u32 i; + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_PERF_VFE_EQU_TYPE_QUADRATIC) + return -EINVAL; + + ptmpobj->type_mask |= BIT(CTRL_PERF_VFE_EQU_TYPE_QUADRATIC); + status = vfe_equ_construct_super(g, ppboardobj, size, pargs); + if (status) + return -EINVAL; + + pvfeequ = (struct vfe_equ_quadratic *)*ppboardobj; + + pvfeequ->super.super.pmudatainit = + _vfe_equ_pmudatainit_quadratic; + + for (i = 0; i < CTRL_PERF_VFE_EQU_QUADRATIC_COEFF_COUNT; i++) + pvfeequ->coeffs[i] = ptmpequ->coeffs[i]; + + return status; +} + +static struct vfe_equ *construct_vfe_equ(struct gk20a *g, void *pargs) +{ + struct boardobj *board_obj_ptr = NULL; + u32 status; + + gk20a_dbg_info(""); + + switch (BOARDOBJ_GET_TYPE(pargs)) { + case CTRL_PERF_VFE_EQU_TYPE_COMPARE: + status = vfe_equ_construct_compare(g, &board_obj_ptr, + sizeof(struct vfe_equ_compare), pargs); + break; + + case CTRL_PERF_VFE_EQU_TYPE_MINMAX: + status = vfe_equ_construct_minmax(g, &board_obj_ptr, + sizeof(struct vfe_equ_minmax), pargs); + break; + + case CTRL_PERF_VFE_EQU_TYPE_QUADRATIC: + status = vfe_equ_construct_quadratic(g, &board_obj_ptr, + sizeof(struct vfe_equ_quadratic), pargs); + break; + + default: + return NULL; + + } + + if (status) + return NULL; + + gk20a_dbg_info(" Done"); + + return (struct vfe_equ *)board_obj_ptr; +} diff --git a/drivers/gpu/nvgpu/perf/vfe_equ.h b/drivers/gpu/nvgpu/perf/vfe_equ.h new file mode 100644 index 00000000..8aaddccd --- /dev/null +++ b/drivers/gpu/nvgpu/perf/vfe_equ.h @@ -0,0 +1,76 @@ +/* + * general perf structures & definitions + * + * Copyright (c) 2016, 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 _VFE_EQU_H_ +#define _VFE_EQU_H_ + +#include "boardobj/boardobjgrp.h" +#include "perf/vfe_var.h" +#include "pmuif/gpmuifperf.h" +#include "pmuif/gpmuifperfvfe.h" + +u32 vfe_equ_sw_setup(struct gk20a *g); +u32 vfe_equ_pmu_setup(struct gk20a *g); + +#define VFE_EQU_GET(_pperf, _idx) \ + ((struct vfe_equ *)BOARDOBJGRP_OBJ_GET_BY_IDX( \ + &((_pperf)->vfe.equs.super.super), (_idx))) + +#define VFE_EQU_IDX_IS_VALID(_pperf, _idx) \ + boardobjgrp_idxisvalid(&((_pperf)->vfe.equs.super.super), (_idx)) + +#define VFE_EQU_OUTPUT_TYPE_IS_VALID(_pperf, _idx, _outputtype) \ + (VFE_EQU_IDX_IS_VALID((_pperf), (_idx)) && \ + ((_outputtype) != CTRL_PERF_VFE_EQU_OUTPUT_TYPE_UNITLESS) && \ + ((VFE_EQU_GET((_pperf), (_idx))->outputtype == (_outputtype)) || \ + (VFE_EQU_GET((_pperf), (_idx))->outputtype == \ + CTRL_PERF_VFE_EQU_OUTPUT_TYPE_UNITLESS))) + +struct vfe_equ { + struct boardobj super; + u8 var_idx; + u8 equ_idx_next; + u8 output_type; + u32 out_range_min; + u32 out_range_max; + + bool b_is_dynamic_valid; + bool b_is_dynamic; +}; + +struct vfe_equs { + struct boardobjgrp_e255 super; +}; + +struct vfe_equ_compare { + struct vfe_equ super; + u8 func_id; + u8 equ_idx_true; + u8 equ_idx_false; + u32 criteria; +}; + +struct vfe_equ_minmax { + struct vfe_equ super; + bool b_max; + u8 equ_idx0; + u8 equ_idx1; +}; + +struct vfe_equ_quadratic { + struct vfe_equ super; + u32 coeffs[CTRL_PERF_VFE_EQU_QUADRATIC_COEFF_COUNT]; +}; + +#endif diff --git a/drivers/gpu/nvgpu/perf/vfe_var.c b/drivers/gpu/nvgpu/perf/vfe_var.c new file mode 100644 index 00000000..90963478 --- /dev/null +++ b/drivers/gpu/nvgpu/perf/vfe_var.c @@ -0,0 +1,1048 @@ +/* + * Copyright (c) 2016, 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 "gk20a/gk20a.h" +#include "perf.h" +#include "vfe_var.h" +#include "include/bios.h" +#include "boardobj/boardobjgrp.h" +#include "boardobj/boardobjgrp_e32.h" +#include "pmuif/gpmuifboardobj.h" +#include "pmuif/gpmuifperf.h" +#include "pmuif/gpmuifperfvfe.h" +#include "gm206/bios_gm206.h" +#include "ctrl/ctrlclk.h" +#include "ctrl/ctrlvolt.h" +#include "gk20a/pmu_gk20a.h" + +static u32 devinit_get_vfe_var_table(struct gk20a *g, + struct vfe_vars *pvarobjs); +static u32 vfe_var_construct_single(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs); + +static u32 _vfe_vars_pmudatainit(struct gk20a *g, + struct boardobjgrp *pboardobjgrp, + struct nv_pmu_boardobjgrp_super *pboardobjgrppmu) +{ + struct nv_pmu_perf_vfe_var_boardobjgrp_set_header *pset = + (struct nv_pmu_perf_vfe_var_boardobjgrp_set_header *) + pboardobjgrppmu; + struct vfe_vars *pvars = (struct vfe_vars *)pboardobjgrp; + u32 status = 0; + + status = boardobjgrp_pmudatainit_e32(g, pboardobjgrp, pboardobjgrppmu); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error updating pmu boardobjgrp for vfe var 0x%x", + status); + goto done; + } + pset->polling_periodms = pvars->polling_periodms; + +done: + return status; +} + +static u32 _vfe_vars_pmudata_instget(struct gk20a *g, + struct nv_pmu_boardobjgrp *pmuboardobjgrp, + struct nv_pmu_boardobj **ppboardobjpmudata, + u8 idx) +{ + struct nv_pmu_perf_vfe_var_boardobj_grp_set *pgrp_set = + (struct nv_pmu_perf_vfe_var_boardobj_grp_set *) + pmuboardobjgrp; + + gk20a_dbg_info(""); + + /*check whether pmuboardobjgrp has a valid boardobj in index*/ + if (idx >= CTRL_BOARDOBJGRP_E32_MAX_OBJECTS) + return -EINVAL; + + *ppboardobjpmudata = (struct nv_pmu_boardobj *) + &pgrp_set->objects[idx].data.board_obj; + + gk20a_dbg_info(" Done"); + return 0; +} + +static u32 _vfe_vars_pmustatus_instget(struct gk20a *g, void *pboardobjgrppmu, + struct nv_pmu_boardobj_query **ppboardobjpmustatus, u8 idx) +{ + struct nv_pmu_perf_vfe_var_boardobj_grp_get_status *pgrp_get_status = + (struct nv_pmu_perf_vfe_var_boardobj_grp_get_status *) + pboardobjgrppmu; + + if (((u32)BIT(idx) & + pgrp_get_status->hdr.data.super.obj_mask.super.data[0]) == 0) + return -EINVAL; + + *ppboardobjpmustatus = (struct nv_pmu_boardobj_query *) + &pgrp_get_status->objects[idx].data.board_obj; + return 0; +} + + +u32 vfe_var_sw_setup(struct gk20a *g) +{ + u32 status; + struct boardobjgrp *pboardobjgrp = NULL; + struct vfe_vars *pvfevarobjs; + + gk20a_dbg_info(""); + + status = boardobjgrpconstruct_e32(&g->perf_pmu.vfe_varobjs.super); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error creating boardobjgrp for clk domain, status - 0x%x", + status); + goto done; + } + + pboardobjgrp = &g->perf_pmu.vfe_varobjs.super.super; + pvfevarobjs = &g->perf_pmu.vfe_varobjs; + + BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, PERF, VFE_VAR); + + status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, + perf, PERF, vfe_var, VFE_VAR); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", + status); + goto done; + } + + pboardobjgrp->pmudatainit = _vfe_vars_pmudatainit; + pboardobjgrp->pmudatainstget = _vfe_vars_pmudata_instget; + pboardobjgrp->pmustatusinstget = _vfe_vars_pmustatus_instget; + + status = devinit_get_vfe_var_table(g, pvfevarobjs); + if (status) + goto done; + + status = BOARDOBJGRP_PMU_CMD_GRP_GET_STATUS_CONSTRUCT(g, + &g->perf_pmu.vfe_varobjs.super.super, + perf, PERF, vfe_var, VFE_VAR); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error constructing PMU_BOARDOBJ_CMD_GRP_GET_STATUS interface - 0x%x", + status); + goto done; + } + +done: + gk20a_dbg_info(" done status %x", status); + return status; +} + +u32 vfe_var_pmu_setup(struct gk20a *g) +{ + u32 status; + struct boardobjgrp *pboardobjgrp = NULL; + + gk20a_dbg_info(""); + + pboardobjgrp = &g->perf_pmu.vfe_varobjs.super.super; + + if (!pboardobjgrp->bconstructed) + return -EINVAL; + + status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); + + gk20a_dbg_info("Done"); + return status; +} + +u32 dev_init_get_vfield_info(struct gk20a *g, + struct vfe_var_single_sensed_fuse *pvfevar) +{ + u8 *vfieldtableptr = NULL; + u32 vfieldheadersize = VFIELD_HEADER_SIZE; + u8 *vfieldregtableptr = NULL; + u32 vfieldregheadersize = VFIELD_REG_HEADER_SIZE; + u32 i; + u32 oldindex = 0xFFFFFFFF; + u32 currindex; + struct vfield_reg_header vregheader; + struct vfield_reg_entry vregentry; + struct vfield_header vheader; + struct vfield_entry ventry; + union nv_pmu_bios_vfield_register_segment *psegment = NULL; + u8 *psegmentcount = NULL; + u32 status = 0; + + if (g->ops.bios.get_perf_table_ptrs) { + vfieldregtableptr = (u8 *)g->ops.bios.get_perf_table_ptrs(g, + g->bios.virt_token, VP_FIELD_REGISTER); + if (vfieldregtableptr == NULL) { + status = -EINVAL; + goto done; + } + + vfieldtableptr = (u8 *)g->ops.bios.get_perf_table_ptrs(g, + g->bios.virt_token, VP_FIELD_TABLE); + if (vfieldtableptr == NULL) { + status = -EINVAL; + goto done; + } + } + + memcpy(&vregheader, vfieldregtableptr, VFIELD_REG_HEADER_SIZE); + + if (vregheader.version != VBIOS_VFIELD_REG_TABLE_VERSION_1_0) { + gk20a_err(dev_from_gk20a(g), "invalid vreg header version"); + goto done; + } + + memcpy(&vheader, vfieldtableptr, VFIELD_HEADER_SIZE); + + if (vregheader.version != VBIOS_VFIELD_TABLE_VERSION_1_0) { + gk20a_err(dev_from_gk20a(g), "invalid vfield header version"); + goto done; + } + + pvfevar->vfield_info.fuse.segment_count = 0; + pvfevar->vfield_ver_info.fuse.segment_count = 0; + for (i = 0; i < (u32)vheader.count; i++) { + memcpy(&ventry, vfieldtableptr + vfieldheadersize + + (i * vheader.entry_size), + vheader.entry_size); + + currindex = VFIELD_BIT_REG(ventry); + if (currindex != oldindex) { + + memcpy(&vregentry, vfieldregtableptr + + vfieldregheadersize + + (currindex * vregheader.entry_size), + vregheader.entry_size); + oldindex = currindex; + } + + if (pvfevar->vfield_info.v_field_id == ventry.strap_id) { + psegmentcount = + &(pvfevar->vfield_info.fuse.segment_count); + psegment = + &(pvfevar->vfield_info.fuse.segments[*psegmentcount]); + if (*psegmentcount > NV_PMU_VFE_VAR_SINGLE_SENSED_FUSE_SEGMENTS_MAX) { + status = -EINVAL; + goto done; + } + } else if (pvfevar->vfield_ver_info.v_field_id_ver == ventry.strap_id) { + psegmentcount = + &(pvfevar->vfield_ver_info.fuse.segment_count); + psegment = + &(pvfevar->vfield_ver_info.fuse.segments[*psegmentcount]); + if (*psegmentcount > NV_PMU_VFE_VAR_SINGLE_SENSED_FUSE_SEGMENTS_MAX) { + status = -EINVAL; + goto done; + } + } else { + continue; + } + + psegment->super.high_bit = (u8)(VFIELD_BIT_STOP(ventry)); + psegment->super.low_bit = (u8)(VFIELD_BIT_START(ventry)); + switch (VFIELD_CODE((&vregentry))) { + case NV_VFIELD_DESC_CODE_REG: + psegment->reg.super.type = + NV_PMU_BIOS_VFIELD_DESC_CODE_REG; + psegment->reg.addr = vregentry.reg; + break; + + case NV_VFIELD_DESC_CODE_INDEX_REG: + psegment->index_reg.super.type = + NV_PMU_BIOS_VFIELD_DESC_CODE_INDEX_REG; + psegment->index_reg.addr = vregentry.reg; + psegment->index_reg.index = vregentry.index; + psegment->index_reg.reg_index = vregentry.reg_index; + break; + + default: + psegment->super.type = + NV_PMU_BIOS_VFIELD_DESC_CODE_INVALID; + status = -EINVAL; + goto done; + } + + if (VFIELD_SIZE((&vregentry)) != NV_VFIELD_DESC_SIZE_DWORD) { + psegment->super.type = + NV_PMU_BIOS_VFIELD_DESC_CODE_INVALID; + return -EINVAL; + } + (*psegmentcount)++; + } + +done: + + return status; +} + +static u32 _vfe_var_pmudatainit_super(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + u32 status = 0; + struct vfe_var *pvfe_var; + struct nv_pmu_vfe_var *pset; + + gk20a_dbg_info(""); + + status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status != 0) + return status; + + pvfe_var = (struct vfe_var *)board_obj_ptr; + pset = (struct nv_pmu_vfe_var *) ppmudata; + + pset->out_range_min = pvfe_var->out_range_min; + pset->out_range_max = pvfe_var->out_range_max; + + return status; +} + +static u32 vfe_var_construct_super(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct vfe_var *pvfevar; + struct vfe_var *ptmpvar = (struct vfe_var *)pargs; + u32 status = 0; + + gk20a_dbg_info(""); + + status = boardobj_construct_super(g, ppboardobj, size, pargs); + if (status) + return -EINVAL; + + pvfevar = (struct vfe_var *)*ppboardobj; + + pvfevar->super.pmudatainit = + _vfe_var_pmudatainit_super; + + pvfevar->out_range_min = ptmpvar->out_range_min; + pvfevar->out_range_max = ptmpvar->out_range_max; + pvfevar->b_is_dynamic_valid = false; + + gk20a_dbg_info(""); + + return status; +} + +static u32 _vfe_var_pmudatainit_derived(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + u32 status = 0; + + gk20a_dbg_info(""); + + status = _vfe_var_pmudatainit_super(g, board_obj_ptr, ppmudata); + + return status; +} + +static u32 vfe_var_construct_derived(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + u32 status = 0; + struct vfe_var_derived *pvfevar; + + ptmpobj->type_mask |= BIT(CTRL_PERF_VFE_VAR_TYPE_DERIVED); + status = vfe_var_construct_super(g, ppboardobj, size, pargs); + if (status) + return -EINVAL; + + pvfevar = (struct vfe_var_derived *)*ppboardobj; + + pvfevar->super.super.pmudatainit = + _vfe_var_pmudatainit_derived; + + return status; +} + +static u32 _vfe_var_pmudatainit_derived_product(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + u32 status = 0; + struct vfe_var_derived_product *pvfe_var_derived_product; + struct nv_pmu_vfe_var_derived_product *pset; + + gk20a_dbg_info(""); + + status = _vfe_var_pmudatainit_derived(g, board_obj_ptr, ppmudata); + if (status != 0) + return status; + + pvfe_var_derived_product = + (struct vfe_var_derived_product *)board_obj_ptr; + pset = (struct nv_pmu_vfe_var_derived_product *)ppmudata; + + pset->var_idx0 = pvfe_var_derived_product->var_idx0; + pset->var_idx1 = pvfe_var_derived_product->var_idx1; + + return status; +} + +static u32 vfe_var_construct_derived_product(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct vfe_var_derived_product *pvfevar; + struct vfe_var_derived_product *ptmpvar = + (struct vfe_var_derived_product *)pargs; + u32 status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_PERF_VFE_VAR_TYPE_DERIVED_PRODUCT) + return -EINVAL; + + ptmpobj->type_mask |= BIT(CTRL_PERF_VFE_VAR_TYPE_DERIVED_PRODUCT); + status = vfe_var_construct_derived(g, ppboardobj, size, pargs); + if (status) + return -EINVAL; + + pvfevar = (struct vfe_var_derived_product *)*ppboardobj; + + pvfevar->super.super.super.pmudatainit = + _vfe_var_pmudatainit_derived_product; + + pvfevar->var_idx0 = ptmpvar->var_idx0; + pvfevar->var_idx1 = ptmpvar->var_idx1; + + + return status; +} + +static u32 _vfe_var_pmudatainit_derived_sum(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + u32 status = 0; + struct vfe_var_derived_sum *pvfe_var_derived_sum; + struct nv_pmu_vfe_var_derived_sum *pset; + + gk20a_dbg_info(""); + + status = _vfe_var_pmudatainit_derived(g, board_obj_ptr, ppmudata); + if (status != 0) + return status; + + pvfe_var_derived_sum = (struct vfe_var_derived_sum *)board_obj_ptr; + pset = (struct nv_pmu_vfe_var_derived_sum *)ppmudata; + + pset->var_idx0 = pvfe_var_derived_sum->var_idx0; + pset->var_idx1 = pvfe_var_derived_sum->var_idx1; + + return status; +} + +static u32 vfe_var_construct_derived_sum(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct vfe_var_derived_sum *pvfevar; + struct vfe_var_derived_sum *ptmpvar = + (struct vfe_var_derived_sum *)pargs; + u32 status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_PERF_VFE_VAR_TYPE_DERIVED_SUM) + return -EINVAL; + + ptmpobj->type_mask |= BIT(CTRL_PERF_VFE_VAR_TYPE_DERIVED_SUM); + status = vfe_var_construct_derived(g, ppboardobj, size, pargs); + if (status) + return -EINVAL; + + pvfevar = (struct vfe_var_derived_sum *)*ppboardobj; + + pvfevar->super.super.super.pmudatainit = + _vfe_var_pmudatainit_derived_sum; + + pvfevar->var_idx0 = ptmpvar->var_idx0; + pvfevar->var_idx1 = ptmpvar->var_idx1; + + return status; +} + +static u32 _vfe_var_pmudatainit_single(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + u32 status = 0; + struct vfe_var_single *pvfe_var_single; + struct nv_pmu_vfe_var_single *pset; + + gk20a_dbg_info(""); + + status = _vfe_var_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status != 0) + return status; + + pvfe_var_single = (struct vfe_var_single *)board_obj_ptr; + pset = (struct nv_pmu_vfe_var_single *) + ppmudata; + + pset->override_type = pvfe_var_single->override_type; + pset->override_value = pvfe_var_single->override_value; + + return status; +} + +static u32 _vfe_var_pmudatainit_single_frequency(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + u32 status = 0; + + gk20a_dbg_info(""); + + status = _vfe_var_pmudatainit_single(g, board_obj_ptr, ppmudata); + + return status; +} + +static u32 vfe_var_construct_single_frequency(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct vfe_var_single_frequency *pvfevar; + u32 status = 0; + + gk20a_dbg_info(""); + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_PERF_VFE_VAR_TYPE_SINGLE_FREQUENCY) + return -EINVAL; + + ptmpobj->type_mask |= BIT(CTRL_PERF_VFE_VAR_TYPE_SINGLE_FREQUENCY); + status = vfe_var_construct_single(g, ppboardobj, size, pargs); + if (status) + return -EINVAL; + + pvfevar = (struct vfe_var_single_frequency *)*ppboardobj; + + pvfevar->super.super.super.pmudatainit = + _vfe_var_pmudatainit_single_frequency; + + pvfevar->super.super.b_is_dynamic = false; + pvfevar->super.super.b_is_dynamic_valid = true; + + gk20a_dbg_info("Done"); + return status; +} + +static u32 _vfe_var_pmudatainit_single_sensed(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + u32 status = 0; + + gk20a_dbg_info(""); + + status = _vfe_var_pmudatainit_single(g, board_obj_ptr, ppmudata); + + return status; +} + +static u32 _vfe_var_pmudatainit_single_sensed_fuse(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + u32 status = 0; + struct vfe_var_single_sensed_fuse *pvfe_var_single_sensed_fuse; + struct nv_pmu_vfe_var_single_sensed_fuse *pset; + + gk20a_dbg_info(""); + + status = _vfe_var_pmudatainit_single_sensed(g, board_obj_ptr, ppmudata); + if (status != 0) + return status; + + pvfe_var_single_sensed_fuse = + (struct vfe_var_single_sensed_fuse *)board_obj_ptr; + + pset = (struct nv_pmu_vfe_var_single_sensed_fuse *) + ppmudata; + + memcpy(&pset->vfield_info, &pvfe_var_single_sensed_fuse->vfield_info, + sizeof(struct nv_pmu_vfe_var_single_sensed_fuse_vfield_info)); + + memcpy(&pset->vfield_ver_info, + &pvfe_var_single_sensed_fuse->vfield_ver_info, + sizeof(struct nv_pmu_vfe_var_single_sensed_fuse_ver_vfield_info)); + + memcpy(&pset->override_info, + &pvfe_var_single_sensed_fuse->override_info, + sizeof(struct nv_pmu_vfe_var_single_sensed_fuse_override_info)); + + return status; +} + +static u32 vfe_var_construct_single_sensed(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct vfe_var_single_sensed *pvfevar; + + u32 status = 0; + + gk20a_dbg_info(" "); + + ptmpobj->type_mask |= BIT(CTRL_PERF_VFE_VAR_TYPE_SINGLE_SENSED); + status = vfe_var_construct_single(g, ppboardobj, size, pargs); + if (status) + return -EINVAL; + + pvfevar = (struct vfe_var_single_sensed *)*ppboardobj; + + pvfevar->super.super.super.pmudatainit = + _vfe_var_pmudatainit_single_sensed; + + gk20a_dbg_info("Done"); + + return status; +} + +static u32 vfe_var_construct_single_sensed_fuse(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct vfe_var_single_sensed_fuse *pvfevar; + struct vfe_var_single_sensed_fuse *ptmpvar = + (struct vfe_var_single_sensed_fuse *)pargs; + u32 status = 0; + + gk20a_dbg_info(""); + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_PERF_VFE_VAR_TYPE_SINGLE_SENSED_FUSE) + return -EINVAL; + + ptmpobj->type_mask |= BIT(CTRL_PERF_VFE_VAR_TYPE_SINGLE_SENSED_FUSE); + status = vfe_var_construct_single_sensed(g, ppboardobj, size, pargs); + if (status) + return -EINVAL; + + pvfevar = (struct vfe_var_single_sensed_fuse *)*ppboardobj; + + pvfevar->super.super.super.super.pmudatainit = + _vfe_var_pmudatainit_single_sensed_fuse; + + pvfevar->vfield_info.v_field_id = ptmpvar->vfield_info.v_field_id; + pvfevar->vfield_info.fuse_val_default = + ptmpvar->vfield_info.fuse_val_default; + pvfevar->vfield_info.hw_correction_scale = + ptmpvar->vfield_info.hw_correction_scale; + pvfevar->vfield_info.hw_correction_offset = + ptmpvar->vfield_info.hw_correction_offset; + pvfevar->vfield_ver_info.v_field_id_ver = + ptmpvar->vfield_ver_info.v_field_id_ver; + pvfevar->vfield_ver_info.ver_expected = + ptmpvar->vfield_ver_info.ver_expected; + pvfevar->vfield_ver_info.b_use_default_on_ver_check_fail = + ptmpvar->vfield_ver_info.b_use_default_on_ver_check_fail; + pvfevar->b_version_check_done = false; + + pvfevar->super.super.super.b_is_dynamic = false; + pvfevar->super.super.super.b_is_dynamic_valid = true; + + dev_init_get_vfield_info(g, pvfevar); + /*check whether fuse segment got initialized*/ + if (pvfevar->vfield_info.fuse.segment_count == 0) { + gk20a_err(dev_from_gk20a(g), "unable to get fuse reg info %x", + pvfevar->vfield_info.v_field_id); + return -EINVAL; + } + if (pvfevar->vfield_ver_info.fuse.segment_count == 0) { + gk20a_err(dev_from_gk20a(g), "unable to get fuse reg info %x", + pvfevar->vfield_ver_info.v_field_id_ver); + return -EINVAL; + } + return status; +} + +static u32 _vfe_var_pmudatainit_single_sensed_temp(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + u32 status = 0; + struct vfe_var_single_sensed_temp *pvfe_var_single_sensed_temp; + struct nv_pmu_vfe_var_single_sensed_temp *pset; + + gk20a_dbg_info(""); + + status = _vfe_var_pmudatainit_single_sensed(g, board_obj_ptr, ppmudata); + if (status != 0) + return status; + + pvfe_var_single_sensed_temp = + (struct vfe_var_single_sensed_temp *)board_obj_ptr; + + pset = (struct nv_pmu_vfe_var_single_sensed_temp *) + ppmudata; + pset->therm_channel_index = + pvfe_var_single_sensed_temp->therm_channel_index; + pset->temp_hysteresis_positive = + pvfe_var_single_sensed_temp->temp_hysteresis_positive; + pset->temp_hysteresis_negative = + pvfe_var_single_sensed_temp->temp_hysteresis_negative; + pset->temp_default = + pvfe_var_single_sensed_temp->temp_default; + return status; +} + +static u32 vfe_var_construct_single_sensed_temp(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct vfe_var_single_sensed_temp *pvfevar; + struct vfe_var_single_sensed_temp *ptmpvar = + (struct vfe_var_single_sensed_temp *)pargs; + u32 status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_PERF_VFE_VAR_TYPE_SINGLE_SENSED_TEMP) + return -EINVAL; + + ptmpobj->type_mask |= BIT(CTRL_PERF_VFE_VAR_TYPE_SINGLE_SENSED_TEMP); + status = vfe_var_construct_single_sensed(g, ppboardobj, size, pargs); + if (status) + return -EINVAL; + + pvfevar = (struct vfe_var_single_sensed_temp *)*ppboardobj; + + pvfevar->super.super.super.super.pmudatainit = + _vfe_var_pmudatainit_single_sensed_temp; + + pvfevar->therm_channel_index = + ptmpvar->therm_channel_index; + pvfevar->temp_hysteresis_positive = + ptmpvar->temp_hysteresis_positive; + pvfevar->temp_hysteresis_negative = + ptmpvar->temp_hysteresis_negative; + pvfevar->temp_default = + ptmpvar->temp_default; + pvfevar->super.super.super.b_is_dynamic = false; + pvfevar->super.super.super.b_is_dynamic_valid = true; + + return status; +} + +static u32 _vfe_var_pmudatainit_single_voltage(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + u32 status = 0; + + gk20a_dbg_info(""); + + status = _vfe_var_pmudatainit_single(g, board_obj_ptr, ppmudata); + + return status; +} + +static u32 vfe_var_construct_single_voltage(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct vfe_var_single_voltage *pvfevar; + u32 status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_PERF_VFE_VAR_TYPE_SINGLE_VOLTAGE) + return -EINVAL; + + ptmpobj->type_mask |= BIT(CTRL_PERF_VFE_VAR_TYPE_SINGLE_VOLTAGE); + status = vfe_var_construct_super(g, ppboardobj, size, pargs); + if (status) + return -EINVAL; + + pvfevar = (struct vfe_var_single_voltage *)*ppboardobj; + + pvfevar->super.super.super.pmudatainit = + _vfe_var_pmudatainit_single_voltage; + + pvfevar->super.super.b_is_dynamic = false; + pvfevar->super.super.b_is_dynamic_valid = true; + + return status; +} + +static struct vfe_var *construct_vfe_var(struct gk20a *g, void *pargs) +{ + struct boardobj *board_obj_ptr = NULL; + u32 status; + + gk20a_dbg_info(""); + switch (BOARDOBJ_GET_TYPE(pargs)) { + case CTRL_PERF_VFE_VAR_TYPE_DERIVED_PRODUCT: + status = vfe_var_construct_derived_product(g, &board_obj_ptr, + sizeof(struct vfe_var_derived_product), pargs); + break; + + case CTRL_PERF_VFE_VAR_TYPE_DERIVED_SUM: + status = vfe_var_construct_derived_sum(g, &board_obj_ptr, + sizeof(struct vfe_var_derived_sum), pargs); + break; + + case CTRL_PERF_VFE_VAR_TYPE_SINGLE_FREQUENCY: + status = vfe_var_construct_single_frequency(g, &board_obj_ptr, + sizeof(struct vfe_var_single_frequency), pargs); + break; + + case CTRL_PERF_VFE_VAR_TYPE_SINGLE_SENSED_FUSE: + status = vfe_var_construct_single_sensed_fuse(g, &board_obj_ptr, + sizeof(struct vfe_var_single_sensed_fuse), pargs); + break; + + case CTRL_PERF_VFE_VAR_TYPE_SINGLE_SENSED_TEMP: + status = vfe_var_construct_single_sensed_temp(g, &board_obj_ptr, + sizeof(struct vfe_var_single_sensed_temp), pargs); + break; + + case CTRL_PERF_VFE_VAR_TYPE_SINGLE_VOLTAGE: + status = vfe_var_construct_single_voltage(g, &board_obj_ptr, + sizeof(struct vfe_var_single_voltage), pargs); + break; + + case CTRL_PERF_VFE_VAR_TYPE_DERIVED: + case CTRL_PERF_VFE_VAR_TYPE_SINGLE_SENSED: + case CTRL_PERF_VFE_VAR_TYPE_SINGLE: + default: + return NULL; + } + + if (status) + return NULL; + + gk20a_dbg_info("done"); + + return (struct vfe_var *)board_obj_ptr; +} + +static u32 devinit_get_vfe_var_table(struct gk20a *g, + struct vfe_vars *pvfevarobjs) +{ + u32 status = 0; + u8 *vfevars_tbl_ptr = NULL; + struct vbios_vfe_3x_header_struct vfevars_tbl_header = { 0 }; + struct vbios_vfe_3x_var_entry_struct var = { 0 }; + u8 *vfevars_tbl_entry_ptr = NULL; + u8 *rd_offset_ptr = NULL; + u32 index = 0; + struct vfe_var *pvar; + u8 var_type; + u32 szfmt; + union { + struct boardobj board_obj; + struct vfe_var super; + struct vfe_var_derived_product derived_product; + struct vfe_var_derived_sum derived_sum; + struct vfe_var_single_sensed_fuse single_sensed_fuse; + struct vfe_var_single_sensed_temp single_sensed_temp; + } var_data; + + gk20a_dbg_info(""); + + if (g->ops.bios.get_perf_table_ptrs) { + vfevars_tbl_ptr = (u8 *)g->ops.bios.get_perf_table_ptrs(g, + g->bios.perf_token, + CONTINUOUS_VIRTUAL_BINNING_TABLE); + if (vfevars_tbl_ptr == NULL) { + status = -EINVAL; + goto done; + } + } + + memcpy(&vfevars_tbl_header, vfevars_tbl_ptr, + VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07); + if (vfevars_tbl_header.header_size != + VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07){ + status = -EINVAL; + goto done; + } + + if (vfevars_tbl_header.vfe_var_entry_size == + VBIOS_VFE_3X_VAR_ENTRY_SIZE_19) + szfmt = VBIOS_VFE_3X_VAR_ENTRY_SIZE_19; + else if (vfevars_tbl_header.vfe_var_entry_size == + VBIOS_VFE_3X_VAR_ENTRY_SIZE_11) + szfmt = VBIOS_VFE_3X_VAR_ENTRY_SIZE_11; + else { + status = -EINVAL; + goto done; + } + + /* Read table entries*/ + vfevars_tbl_entry_ptr = vfevars_tbl_ptr + + vfevars_tbl_header.header_size; + + for (index = 0; + index < vfevars_tbl_header.vfe_var_entry_count; + index++) { + rd_offset_ptr = vfevars_tbl_entry_ptr + + (index * vfevars_tbl_header.vfe_var_entry_size); + memcpy(&var, rd_offset_ptr, szfmt); + + var_data.super.out_range_min = var.out_range_min; + var_data.super.out_range_max = var.out_range_max; + + var_data.super.out_range_min = var.out_range_min; + var_data.super.out_range_max = var.out_range_max; + + switch ((u8)var.type) { + case VBIOS_VFE_3X_VAR_ENTRY_TYPE_DISABLED: + continue; + break; + + case VBIOS_VFE_3X_VAR_ENTRY_TYPE_SINGLE_FREQUENCY: + var_type = CTRL_PERF_VFE_VAR_TYPE_SINGLE_FREQUENCY; + break; + + case VBIOS_VFE_3X_VAR_ENTRY_TYPE_SINGLE_VOLTAGE: + var_type = CTRL_PERF_VFE_VAR_TYPE_SINGLE_VOLTAGE; + break; + + case VBIOS_VFE_3X_VAR_ENTRY_TYPE_SINGLE_SENSED_TEMP: + var_type = CTRL_PERF_VFE_VAR_TYPE_SINGLE_SENSED_TEMP; + var_data.single_sensed_temp.temp_default = 105; + var_data.single_sensed_temp.therm_channel_index = + (u8)BIOS_GET_FIELD(var.param0, + VBIOS_VFE_3X_VAR_ENTRY_PAR0_SSTEMP_TH_CH_IDX); + var_data.single_sensed_temp.temp_hysteresis_positive = + (u8)BIOS_GET_FIELD(var.param0, + VBIOS_VFE_3X_VAR_ENTRY_PAR0_SSTEMP_HYS_POS) << 5; + var_data.single_sensed_temp.temp_hysteresis_negative = + (u8)BIOS_GET_FIELD(var.param0, + VBIOS_VFE_3X_VAR_ENTRY_PAR0_SSTEMP_HYS_NEG) << 5; + break; + + case VBIOS_VFE_3X_VAR_ENTRY_TYPE_SINGLE_SENSED_FUSE: + var_type = CTRL_PERF_VFE_VAR_TYPE_SINGLE_SENSED_FUSE; + var_data.single_sensed_fuse.vfield_info.v_field_id = + (u8)BIOS_GET_FIELD(var.param0, + VBIOS_VFE_3X_VAR_ENTRY_PAR0_SSFUSE_VFIELD_ID); + var_data.single_sensed_fuse.vfield_ver_info.v_field_id_ver = + (u8)BIOS_GET_FIELD(var.param0, + VBIOS_VFE_3X_VAR_ENTRY_PAR0_SSFUSE_VFIELD_ID_VER); + var_data.single_sensed_fuse.vfield_ver_info.ver_expected = + (u8)BIOS_GET_FIELD(var.param0, + VBIOS_VFE_3X_VAR_ENTRY_PAR0_SSFUSE_EXPECTED_VER); + var_data.single_sensed_fuse.vfield_ver_info.b_use_default_on_ver_check_fail = + (BIOS_GET_FIELD(var.param0, + VBIOS_VFE_3X_VAR_ENTRY_PAR0_SSFUSE_USE_DEFAULT_ON_VER_CHECK_FAIL) && + VBIOS_VFE_3X_VAR_ENTRY_PAR0_SSFUSE_USE_DEFAULT_ON_VER_CHECK_FAIL_YES); + var_data.single_sensed_fuse.vfield_info.fuse_val_default = + var.param1; + if (szfmt >= VBIOS_VFE_3X_VAR_ENTRY_SIZE_19) { + var_data.single_sensed_fuse.vfield_info.hw_correction_scale = + (int)var.param2; + var_data.single_sensed_fuse.vfield_info.hw_correction_offset = + var.param3; + } else { + var_data.single_sensed_fuse.vfield_info.hw_correction_scale = + 1 << 12; + var_data.single_sensed_fuse.vfield_info.hw_correction_offset = + 0; + if ((var_data.single_sensed_fuse.vfield_info.v_field_id == + VFIELD_ID_STRAP_IDDQ) || + (var_data.single_sensed_fuse.vfield_info.v_field_id == + VFIELD_ID_STRAP_IDDQ_1)) { + var_data.single_sensed_fuse.vfield_info.hw_correction_scale = + 50 << 12; + } + } + break; + + case VBIOS_VFE_3X_VAR_ENTRY_TYPE_DERIVED_PRODUCT: + var_type = CTRL_PERF_VFE_VAR_TYPE_DERIVED_PRODUCT; + var_data.derived_product.var_idx0 = + (u8)BIOS_GET_FIELD(var.param0, + VBIOS_VFE_3X_VAR_ENTRY_PAR0_DPROD_VFE_VAR_IDX_0); + var_data.derived_product.var_idx1 = + (u8)BIOS_GET_FIELD(var.param0, + VBIOS_VFE_3X_VAR_ENTRY_PAR0_DPROD_VFE_VAR_IDX_1); + break; + + case VBIOS_VFE_3X_VAR_ENTRY_TYPE_DERIVED_SUM: + var_type = CTRL_PERF_VFE_VAR_TYPE_DERIVED_SUM; + var_data.derived_sum.var_idx0 = + (u8)BIOS_GET_FIELD(var.param0, + VBIOS_VFE_3X_VAR_ENTRY_PAR0_DSUM_VFE_VAR_IDX_0); + var_data.derived_sum.var_idx1 = + (u8)BIOS_GET_FIELD(var.param0, + VBIOS_VFE_3X_VAR_ENTRY_PAR0_DSUM_VFE_VAR_IDX_1); + break; + default: + status = -EINVAL; + goto done; + } + var_data.board_obj.type = var_type; + var_data.board_obj.type_mask = 0; + + pvar = construct_vfe_var(g, &var_data); + if (pvar == NULL) { + gk20a_err(dev_from_gk20a(g), + "error constructing vfe_var boardobj %d", + index); + status = -EINVAL; + goto done; + } + + status = boardobjgrp_objinsert(&pvfevarobjs->super.super, + (struct boardobj *)pvar, index); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error adding vfe_var boardobj %d", index); + status = -EINVAL; + goto done; + } + } + pvfevarobjs->polling_periodms = vfevars_tbl_header.polling_periodms; +done: + gk20a_dbg_info("done status %x", status); + return status; +} + +static u32 vfe_var_construct_single(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct vfe_var_single *pvfevar; + u32 status = 0; + + gk20a_dbg_info(""); + + ptmpobj->type_mask |= BIT(CTRL_PERF_VFE_VAR_TYPE_SINGLE); + status = vfe_var_construct_super(g, ppboardobj, size, pargs); + if (status) + return -EINVAL; + + pvfevar = (struct vfe_var_single *)*ppboardobj; + + pvfevar->super.super.pmudatainit = + _vfe_var_pmudatainit_single; + + pvfevar->override_type = CTRL_PERF_VFE_VAR_SINGLE_OVERRIDE_TYPE_NONE; + pvfevar->override_value = 0; + + gk20a_dbg_info("Done"); + return status; +} diff --git a/drivers/gpu/nvgpu/perf/vfe_var.h b/drivers/gpu/nvgpu/perf/vfe_var.h new file mode 100644 index 00000000..fc43311b --- /dev/null +++ b/drivers/gpu/nvgpu/perf/vfe_var.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2016, 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 _VFE_VAR_H_ +#define _VFE_VAR_H_ + +#include "boardobj/boardobjgrp.h" +#include "pmuif/gpmuifperf.h" +#include "pmuif/gpmuifperfvfe.h" + +u32 vfe_var_sw_setup(struct gk20a *g); +u32 vfe_var_pmu_setup(struct gk20a *g); + +#define VFE_VAR_GET(_pperf, _idx) \ + ((struct vfe_var)BOARDOBJGRP_OBJ_GET_BY_IDX( \ + &((_pperf)->vfe.vars.super.super), (_idx))) + +#define VFE_VAR_IDX_IS_VALID(_pperf, _idx) \ + boardobjgrp_idxisvalid(&((_pperf)->vfe.vars.super.super), (_idx)) + +struct vfe_var { + struct boardobj super; + u32 out_range_min; + u32 out_range_max; + bool b_is_dynamic_valid; + bool b_is_dynamic; +}; + +struct vfe_vars { + struct boardobjgrp_e32 super; + u8 polling_periodms; +}; + +struct vfe_var_derived { + struct vfe_var super; +}; + +struct vfe_var_derived_product { + struct vfe_var_derived super; + u8 var_idx0; + u8 var_idx1; +}; + +struct vfe_var_derived_sum { + struct vfe_var_derived super; + u8 var_idx0; + u8 var_idx1; +}; + +struct vfe_var_single { + struct vfe_var super; + u8 override_type; + u32 override_value; +}; + +struct vfe_var_single_frequency { + struct vfe_var_single super; +}; + +struct vfe_var_single_voltage { + struct vfe_var_single super; +}; + +struct vfe_var_single_sensed { + struct vfe_var_single super; +}; + +struct vfe_var_single_sensed_fuse { + struct vfe_var_single_sensed super; + struct nv_pmu_vfe_var_single_sensed_fuse_override_info override_info; + struct nv_pmu_vfe_var_single_sensed_fuse_vfield_info vfield_info; + struct nv_pmu_vfe_var_single_sensed_fuse_ver_vfield_info vfield_ver_info; + u32 fuse_value_integer; + u32 fuse_value_hw_integer; + u8 fuse_version; + bool b_version_check_done; +}; + +struct vfe_var_single_sensed_temp { + struct vfe_var_single_sensed super; + u8 therm_channel_index; + int temp_hysteresis_positive; + int temp_hysteresis_negative; + int temp_default; +}; + +#endif -- cgit v1.2.2 From db529935a5f50e9e683d44d2eb01d0d76a915792 Mon Sep 17 00:00:00 2001 From: Thomas Fleury Date: Thu, 8 Sep 2016 17:35:13 -0700 Subject: gpu: nvgpu: parse performance table Parse VBIOS performance table to retrieve clock ranges. Jira DNVGPU-125 Change-Id: Ia8e4ede158de5c5374205a510099d00b497fe1a6 Signed-off-by: Thomas Fleury Reviewed-on: http://git-master/r/1218935 (cherry picked from commit b5b7c789e98a20eb4cc5c30f0e2eb45d4a882cc4) Reviewed-on: http://git-master/r/1232593 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Terje Bergstrom --- drivers/gpu/nvgpu/perf/perf.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpu/nvgpu/perf') diff --git a/drivers/gpu/nvgpu/perf/perf.h b/drivers/gpu/nvgpu/perf/perf.h index 02aed7a6..3ffaf4e1 100644 --- a/drivers/gpu/nvgpu/perf/perf.h +++ b/drivers/gpu/nvgpu/perf/perf.h @@ -15,6 +15,7 @@ #include "vfe_equ.h" #include "vfe_var.h" +#include "pstate/pstate.h" #include "gk20a/gk20a.h" #define CTRL_PERF_VFE_VAR_TYPE_INVALID 0x00 @@ -53,6 +54,7 @@ struct perf_pmupstate { struct vfe_vars vfe_varobjs; struct vfe_equs vfe_equobjs; + struct pstates pstatesobjs; }; u32 perf_pmu_vfe_load(struct gk20a *g); -- cgit v1.2.2 From 173bdefc92e2e4ef8f1e7e6ead7f86e746bee935 Mon Sep 17 00:00:00 2001 From: Mahantesh Kumbar Date: Mon, 19 Sep 2016 11:07:46 +0530 Subject: gpu: nvgpu: add support for voltage config - changes to read voltage tables from VBIOS & create boardobj then send to pmu - Rail, Device & Policy objects are read from VBIOS & created boardobjs - RPC support to load, Set & get voltage. JIRA DNVGPU-122 Change-Id: I61621a514eef9c081a64c4ab066f01dfc28f8402 Signed-off-by: Mahantesh Kumbar Reviewed-on: http://git-master/r/1222774 (cherry picked from commit 9da86d8c2c547623cf5f38c89afeb3f5bb1667ac) Reviewed-on: http://git-master/r/1244656 GVS: Gerrit_Virtual_Submit Reviewed-by: Terje Bergstrom Tested-by: Terje Bergstrom --- drivers/gpu/nvgpu/perf/perf.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpu/nvgpu/perf') diff --git a/drivers/gpu/nvgpu/perf/perf.h b/drivers/gpu/nvgpu/perf/perf.h index 3ffaf4e1..c03bf2ae 100644 --- a/drivers/gpu/nvgpu/perf/perf.h +++ b/drivers/gpu/nvgpu/perf/perf.h @@ -17,6 +17,7 @@ #include "vfe_var.h" #include "pstate/pstate.h" #include "gk20a/gk20a.h" +#include "volt/volt.h" #define CTRL_PERF_VFE_VAR_TYPE_INVALID 0x00 #define CTRL_PERF_VFE_VAR_TYPE_DERIVED 0x01 @@ -55,6 +56,7 @@ struct perf_pmupstate { struct vfe_vars vfe_varobjs; struct vfe_equs vfe_equobjs; struct pstates pstatesobjs; + struct obj_volt volt; }; u32 perf_pmu_vfe_load(struct gk20a *g); -- cgit v1.2.2 From 2ebf09920755daeb7e1be71bf317f88ec9d533e1 Mon Sep 17 00:00:00 2001 From: Vijayakumar Date: Thu, 27 Oct 2016 19:05:13 +0530 Subject: gpu: nvgpu: handle vf curve change due to temp JIRA DNVGPU-129 1)send 150'c as default temperature to PMU so that PMU will start reading temperature from sensor to evaluate VFE equations 2)Send GP106's temp min and max range for GPU sensor so that PMU will read right temperature 3)PMU will send event whenever temperature goes above +ve hysteresis or goes below -ve hysteresis. Call the Arbiter's VF re-evaluation function in the event handler. Change-Id: Iaebc0655f60e17998f0864824095f4fc8bba5b62 Signed-off-by: Vijayakumar Reviewed-on: http://git-master/r/1245392 (cherry picked from commit 7e59d0faa8cee6aace5524c724001e88248b2da7) Reviewed-on: http://git-master/r/1268062 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Tested-by: Thomas Fleury Reviewed-by: Terje Bergstrom --- drivers/gpu/nvgpu/perf/perf.c | 20 ++++++++++++++++++++ drivers/gpu/nvgpu/perf/vfe_var.c | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/nvgpu/perf') diff --git a/drivers/gpu/nvgpu/perf/perf.c b/drivers/gpu/nvgpu/perf/perf.c index 3821a8dc..41ebb315 100644 --- a/drivers/gpu/nvgpu/perf/perf.c +++ b/drivers/gpu/nvgpu/perf/perf.c @@ -16,6 +16,7 @@ #include "pmuif/gpmuifperf.h" #include "pmuif/gpmuifperfvfe.h" #include "gk20a/pmu_gk20a.h" +#include "clk/clk_arb.h" struct perfrpc_pmucmdhandler_params { struct nv_pmu_perf_rpc *prpccall; @@ -41,6 +42,22 @@ static void perfrpc_pmucmdhandler(struct gk20a *g, struct pmu_msg *msg, phandlerparams->success = 1; } +static int pmu_handle_perf_event(struct gk20a *g, void *pmu_msg) +{ + struct nv_pmu_perf_msg *msg = (struct nv_pmu_perf_msg *)pmu_msg; + + gk20a_dbg_fn(""); + switch (msg->msg_type) { + case NV_PMU_PERF_MSG_ID_VFE_CALLBACK: + nvgpu_clk_arb_schedule_vf_table_update(g); + break; + default: + WARN_ON(1); + break; + } + return 0; +} + u32 perf_pmu_vfe_load(struct gk20a *g) { struct pmu_cmd cmd; @@ -51,6 +68,9 @@ u32 perf_pmu_vfe_load(struct gk20a *g) struct nv_pmu_perf_rpc rpccall = {0}; struct perfrpc_pmucmdhandler_params handler = {0}; + /*register call back for future VFE updates*/ + g->ops.perf.handle_pmu_perf_event = pmu_handle_perf_event; + rpccall.function = NV_PMU_PERF_RPC_ID_VFE_LOAD; rpccall.params.vfe_load.b_load = true; cmd.hdr.unit_id = PMU_UNIT_PERF; diff --git a/drivers/gpu/nvgpu/perf/vfe_var.c b/drivers/gpu/nvgpu/perf/vfe_var.c index 90963478..4f8dc83b 100644 --- a/drivers/gpu/nvgpu/perf/vfe_var.c +++ b/drivers/gpu/nvgpu/perf/vfe_var.c @@ -921,7 +921,7 @@ static u32 devinit_get_vfe_var_table(struct gk20a *g, case VBIOS_VFE_3X_VAR_ENTRY_TYPE_SINGLE_SENSED_TEMP: var_type = CTRL_PERF_VFE_VAR_TYPE_SINGLE_SENSED_TEMP; - var_data.single_sensed_temp.temp_default = 105; + var_data.single_sensed_temp.temp_default = 0x9600; var_data.single_sensed_temp.therm_channel_index = (u8)BIOS_GET_FIELD(var.param0, VBIOS_VFE_3X_VAR_ENTRY_PAR0_SSTEMP_TH_CH_IDX); -- cgit v1.2.2 From e5824d8014c321fbe2c1e04e12307125dd50a472 Mon Sep 17 00:00:00 2001 From: Mahantesh Kumbar Date: Thu, 3 Nov 2016 21:16:21 +0530 Subject: gpu: nvgpu: MSCG support - update gp106 pg engine init/list/features HALs to support MS engine - Added defines & interface for lpwr tables read from vbios. - lpwr module which reads idx/gr/ms table from vbios to map rppg/mscg support with respective p-state - lpwr module public functions to control lpwr features enable/disable mscg/rppg & mclk-change request whenever change in mclk-change parameters - lpwr public functions to know rppg/mscg support for requested pstate, - added mutex t prevent PG transition while arbiter executes pstate transition - nvgpu_clk_arb_get_current_pstate() of clk arbiter to get current pstate JIRA DNVGPU-71 Change-Id: Ifcd640cc19ef630be1e2a9ba07ec84023d8202a0 Signed-off-by: Mahantesh Kumbar Reviewed-on: http://git-master/r/1247553 (cherry picked from commit 8a441dea2410e1b5196ef24e56a7768b6980e46b) Reviewed-on: http://git-master/r/1270989 Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/gpu/nvgpu/perf/perf.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpu/nvgpu/perf') diff --git a/drivers/gpu/nvgpu/perf/perf.h b/drivers/gpu/nvgpu/perf/perf.h index c03bf2ae..a3213f7a 100644 --- a/drivers/gpu/nvgpu/perf/perf.h +++ b/drivers/gpu/nvgpu/perf/perf.h @@ -18,6 +18,7 @@ #include "pstate/pstate.h" #include "gk20a/gk20a.h" #include "volt/volt.h" +#include "lpwr/lpwr.h" #define CTRL_PERF_VFE_VAR_TYPE_INVALID 0x00 #define CTRL_PERF_VFE_VAR_TYPE_DERIVED 0x01 @@ -57,6 +58,7 @@ struct perf_pmupstate { struct vfe_equs vfe_equobjs; struct pstates pstatesobjs; struct obj_volt volt; + struct obj_lwpr lpwr; }; u32 perf_pmu_vfe_load(struct gk20a *g); -- cgit v1.2.2