From 01e6fac4d61fdd7fff5433942ec93fc2ea1e4df1 Mon Sep 17 00:00:00 2001 From: Joshua Bakita Date: Wed, 28 Jun 2023 18:24:25 -0400 Subject: Include nvgpu headers These are needed to build on NVIDIA's Jetson boards for the time being. Only a couple structs are required, so it should be fairly easy to remove this dependency at some point in the future. --- include/clk/clk.c | 942 +++++++++++++++++++++ include/clk/clk.h | 144 ++++ include/clk/clk_arb.c | 1087 ++++++++++++++++++++++++ include/clk/clk_domain.c | 1666 +++++++++++++++++++++++++++++++++++++ include/clk/clk_domain.h | 157 ++++ include/clk/clk_fll.c | 495 +++++++++++ include/clk/clk_fll.h | 81 ++ include/clk/clk_freq_controller.c | 462 ++++++++++ include/clk/clk_freq_controller.h | 84 ++ include/clk/clk_mclk.h | 60 ++ include/clk/clk_prog.c | 1152 +++++++++++++++++++++++++ include/clk/clk_prog.h | 100 +++ include/clk/clk_vf_point.c | 433 ++++++++++ include/clk/clk_vf_point.h | 83 ++ include/clk/clk_vin.c | 573 +++++++++++++ include/clk/clk_vin.h | 79 ++ 16 files changed, 7598 insertions(+) create mode 100644 include/clk/clk.c create mode 100644 include/clk/clk.h create mode 100644 include/clk/clk_arb.c create mode 100644 include/clk/clk_domain.c create mode 100644 include/clk/clk_domain.h create mode 100644 include/clk/clk_fll.c create mode 100644 include/clk/clk_fll.h create mode 100644 include/clk/clk_freq_controller.c create mode 100644 include/clk/clk_freq_controller.h create mode 100644 include/clk/clk_mclk.h create mode 100644 include/clk/clk_prog.c create mode 100644 include/clk/clk_prog.h create mode 100644 include/clk/clk_vf_point.c create mode 100644 include/clk/clk_vf_point.h create mode 100644 include/clk/clk_vin.c create mode 100644 include/clk/clk_vin.h (limited to 'include/clk') diff --git a/include/clk/clk.c b/include/clk/clk.c new file mode 100644 index 0000000..d8e30c4 --- /dev/null +++ b/include/clk/clk.c @@ -0,0 +1,942 @@ +/* + * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "clk.h" +#include "ctrl/ctrlclk.h" +#include "ctrl/ctrlvolt.h" +#include "volt/volt.h" + +#define BOOT_GPC2CLK_MHZ 2581 +#define BOOT_MCLK_MHZ 3003 + +struct clkrpc_pmucmdhandler_params { + struct nv_pmu_clk_rpc *prpccall; + u32 success; +}; + +static void clkrpc_pmucmdhandler(struct gk20a *g, struct pmu_msg *msg, + void *param, u32 handle, u32 status) +{ + struct clkrpc_pmucmdhandler_params *phandlerparams = + (struct clkrpc_pmucmdhandler_params *)param; + + nvgpu_log_info(g, " "); + + if (msg->msg.clk.msg_type != NV_PMU_CLK_MSG_ID_RPC) { + nvgpu_err(g, "unsupported msg for VFE LOAD RPC %x", + msg->msg.clk.msg_type); + return; + } + + if (phandlerparams->prpccall->b_supported) { + phandlerparams->success = 1; + } +} + + +int clk_pmu_freq_effective_avg_load(struct gk20a *g, bool bload) +{ + struct pmu_cmd cmd; + struct pmu_payload payload; + u32 status; + u32 seqdesc; + struct nv_pmu_clk_rpc rpccall; + struct clkrpc_pmucmdhandler_params handler; + struct nv_pmu_clk_load *clkload; + + memset(&payload, 0, sizeof(struct pmu_payload)); + memset(&rpccall, 0, sizeof(struct nv_pmu_clk_rpc)); + memset(&handler, 0, sizeof(struct clkrpc_pmucmdhandler_params)); + memset(&cmd, 0, sizeof(struct pmu_cmd)); + + rpccall.function = NV_PMU_CLK_RPC_ID_LOAD; + clkload = &rpccall.params.clk_load; + clkload->feature = NV_NV_PMU_CLK_LOAD_FEATURE_FREQ_EFFECTIVE_AVG; + clkload->action_mask = bload ? + NV_NV_PMU_CLK_LOAD_ACTION_MASK_FREQ_EFFECTIVE_AVG_CALLBACK_YES : + NV_NV_PMU_CLK_LOAD_ACTION_MASK_FREQ_EFFECTIVE_AVG_CALLBACK_NO; + + cmd.hdr.unit_id = PMU_UNIT_CLK; + cmd.hdr.size = (u32)sizeof(struct nv_pmu_clk_cmd) + + (u32)sizeof(struct pmu_hdr); + + cmd.cmd.clk.cmd_type = NV_PMU_CLK_CMD_ID_RPC; + + payload.in.buf = (u8 *)&rpccall; + payload.in.size = (u32)sizeof(struct nv_pmu_clk_rpc); + payload.in.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; + payload.in.offset = NV_PMU_CLK_CMD_RPC_ALLOC_OFFSET; + + payload.out.buf = (u8 *)&rpccall; + payload.out.size = (u32)sizeof(struct nv_pmu_clk_rpc); + payload.out.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; + payload.out.offset = NV_PMU_CLK_MSG_RPC_ALLOC_OFFSET; + + handler.prpccall = &rpccall; + handler.success = 0; + + status = nvgpu_pmu_cmd_post(g, &cmd, NULL, &payload, + PMU_COMMAND_QUEUE_LPQ, + clkrpc_pmucmdhandler, (void *)&handler, + &seqdesc, ~0); + if (status) { + nvgpu_err(g, "unable to post clk RPC cmd %x", + cmd.cmd.clk.cmd_type); + goto done; + } + + pmu_wait_message_cond(&g->pmu, + gk20a_get_gr_idle_timeout(g), + &handler.success, 1); + if (handler.success == 0) { + nvgpu_err(g, "rpc call to load Effective avg clk domain freq failed"); + status = -EINVAL; + } + +done: + return status; +} + +u32 clk_freq_effective_avg(struct gk20a *g, u32 clkDomainMask) { + + struct pmu_cmd cmd; + struct pmu_payload payload; + u32 status; + u32 seqdesc; + struct nv_pmu_clk_rpc rpccall; + struct clkrpc_pmucmdhandler_params handler; + struct nv_pmu_clk_freq_effective_avg *clk_freq_effective_avg; + + memset(&payload, 0, sizeof(struct pmu_payload)); + memset(&rpccall, 0, sizeof(struct nv_pmu_clk_rpc)); + memset(&handler, 0, sizeof(struct clkrpc_pmucmdhandler_params)); + memset(&cmd, 0, sizeof(struct pmu_cmd)); + + rpccall.function = NV_PMU_CLK_RPC_ID_CLK_FREQ_EFF_AVG; + clk_freq_effective_avg = &rpccall.params.clk_freq_effective_avg; + clk_freq_effective_avg->clkDomainMask = clkDomainMask; + + cmd.hdr.unit_id = PMU_UNIT_CLK; + cmd.hdr.size = (u32)sizeof(struct nv_pmu_clk_cmd) + + (u32)sizeof(struct pmu_hdr); + + cmd.cmd.clk.cmd_type = NV_PMU_CLK_CMD_ID_RPC; + + payload.in.buf = (u8 *)&rpccall; + payload.in.size = (u32)sizeof(struct nv_pmu_clk_rpc); + payload.in.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; + payload.in.offset = NV_PMU_CLK_CMD_RPC_ALLOC_OFFSET; + + payload.out.buf = (u8 *)&rpccall; + payload.out.size = (u32)sizeof(struct nv_pmu_clk_rpc); + payload.out.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; + payload.out.offset = NV_PMU_CLK_MSG_RPC_ALLOC_OFFSET; + + handler.prpccall = &rpccall; + handler.success = 0; + + status = nvgpu_pmu_cmd_post(g, &cmd, NULL, &payload, + PMU_COMMAND_QUEUE_LPQ, + clkrpc_pmucmdhandler, (void *)&handler, + &seqdesc, ~0); + if (status) { + nvgpu_err(g, "unable to post clk RPC cmd %x", + cmd.cmd.clk.cmd_type); + goto done; + } + + pmu_wait_message_cond(&g->pmu, + gk20a_get_gr_idle_timeout(g), + &handler.success, 1); + if (handler.success == 0) { + nvgpu_err(g, "rpc call to get clk frequency average failed"); + status = -EINVAL; + goto done; + } + + return rpccall.params.clk_freq_effective_avg.freqkHz[clkDomainMask]; + +done: + return status; +} + +int clk_pmu_freq_controller_load(struct gk20a *g, bool bload, u8 bit_idx) +{ + struct pmu_cmd cmd; + struct pmu_payload payload; + u32 status; + u32 seqdesc; + struct nv_pmu_clk_rpc rpccall; + struct clkrpc_pmucmdhandler_params handler; + struct nv_pmu_clk_load *clkload; + struct clk_freq_controllers *pclk_freq_controllers; + struct ctrl_boardobjgrp_mask_e32 *load_mask; + struct boardobjgrpmask_e32 isolate_cfc_mask; + + memset(&payload, 0, sizeof(struct pmu_payload)); + memset(&rpccall, 0, sizeof(struct nv_pmu_clk_rpc)); + memset(&handler, 0, sizeof(struct clkrpc_pmucmdhandler_params)); + + pclk_freq_controllers = &g->clk_pmu.clk_freq_controllers; + rpccall.function = NV_PMU_CLK_RPC_ID_LOAD; + clkload = &rpccall.params.clk_load; + clkload->feature = NV_NV_PMU_CLK_LOAD_FEATURE_FREQ_CONTROLLER; + clkload->action_mask = bload ? + NV_NV_PMU_CLK_LOAD_ACTION_MASK_FREQ_CONTROLLER_CALLBACK_YES : + NV_NV_PMU_CLK_LOAD_ACTION_MASK_FREQ_CONTROLLER_CALLBACK_NO; + + load_mask = &rpccall.params.clk_load.payload.freq_controllers.load_mask; + + status = boardobjgrpmask_e32_init(&isolate_cfc_mask, NULL); + + if (bit_idx == CTRL_CLK_CLK_FREQ_CONTROLLER_ID_ALL) { + status = boardobjgrpmask_export( + &pclk_freq_controllers-> + freq_ctrl_load_mask.super, + pclk_freq_controllers-> + freq_ctrl_load_mask.super.bitcount, + &load_mask->super); + + + } else { + status = boardobjgrpmask_bitset(&isolate_cfc_mask.super, + bit_idx); + status = boardobjgrpmask_export(&isolate_cfc_mask.super, + isolate_cfc_mask.super.bitcount, + &load_mask->super); + if (bload) { + status = boardobjgrpmask_bitset( + &pclk_freq_controllers-> + freq_ctrl_load_mask.super, + bit_idx); + } else { + status = boardobjgrpmask_bitclr( + &pclk_freq_controllers-> + freq_ctrl_load_mask.super, + bit_idx); + } + } + + if (status) { + nvgpu_err(g, "Error in generating mask used to select CFC"); + goto done; + } + + cmd.hdr.unit_id = PMU_UNIT_CLK; + cmd.hdr.size = (u32)sizeof(struct nv_pmu_clk_cmd) + + (u32)sizeof(struct pmu_hdr); + + cmd.cmd.clk.cmd_type = NV_PMU_CLK_CMD_ID_RPC; + + payload.in.buf = (u8 *)&rpccall; + payload.in.size = (u32)sizeof(struct nv_pmu_clk_rpc); + payload.in.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; + payload.in.offset = NV_PMU_CLK_CMD_RPC_ALLOC_OFFSET; + + payload.out.buf = (u8 *)&rpccall; + payload.out.size = (u32)sizeof(struct nv_pmu_clk_rpc); + payload.out.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; + payload.out.offset = NV_PMU_CLK_MSG_RPC_ALLOC_OFFSET; + + handler.prpccall = &rpccall; + handler.success = 0; + status = nvgpu_pmu_cmd_post(g, &cmd, NULL, &payload, + PMU_COMMAND_QUEUE_LPQ, + clkrpc_pmucmdhandler, (void *)&handler, + &seqdesc, ~0); + + if (status) { + nvgpu_err(g, "unable to post clk RPC cmd %x", + cmd.cmd.clk.cmd_type); + goto done; + } + + pmu_wait_message_cond(&g->pmu, + gk20a_get_gr_idle_timeout(g), + &handler.success, 1); + + if (handler.success == 0) { + nvgpu_err(g, "rpc call to load freq cntlr cal failed"); + status = -EINVAL; + } + +done: + return status; +} + +u32 clk_pmu_vin_load(struct gk20a *g) +{ + struct pmu_cmd cmd; + struct pmu_payload payload; + u32 status; + u32 seqdesc; + struct nv_pmu_clk_rpc rpccall; + struct clkrpc_pmucmdhandler_params handler; + struct nv_pmu_clk_load *clkload; + + memset(&payload, 0, sizeof(struct pmu_payload)); + memset(&rpccall, 0, sizeof(struct nv_pmu_clk_rpc)); + memset(&handler, 0, sizeof(struct clkrpc_pmucmdhandler_params)); + + rpccall.function = NV_PMU_CLK_RPC_ID_LOAD; + clkload = &rpccall.params.clk_load; + clkload->feature = NV_NV_PMU_CLK_LOAD_FEATURE_VIN; + clkload->action_mask = NV_NV_PMU_CLK_LOAD_ACTION_MASK_VIN_HW_CAL_PROGRAM_YES << 4; + + cmd.hdr.unit_id = PMU_UNIT_CLK; + cmd.hdr.size = (u32)sizeof(struct nv_pmu_clk_cmd) + + (u32)sizeof(struct pmu_hdr); + + cmd.cmd.clk.cmd_type = NV_PMU_CLK_CMD_ID_RPC; + cmd.cmd.clk.generic.b_perf_daemon_cmd =false; + + payload.in.buf = (u8 *)&rpccall; + payload.in.size = (u32)sizeof(struct nv_pmu_clk_rpc); + payload.in.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; + payload.in.offset = NV_PMU_CLK_CMD_RPC_ALLOC_OFFSET; + + payload.out.buf = (u8 *)&rpccall; + payload.out.size = (u32)sizeof(struct nv_pmu_clk_rpc); + payload.out.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; + payload.out.offset = NV_PMU_CLK_MSG_RPC_ALLOC_OFFSET; + + handler.prpccall = &rpccall; + handler.success = 0; + status = nvgpu_pmu_cmd_post(g, &cmd, NULL, &payload, + PMU_COMMAND_QUEUE_LPQ, + clkrpc_pmucmdhandler, (void *)&handler, + &seqdesc, ~0); + + if (status) { + nvgpu_err(g, "unable to post clk RPC cmd %x", + cmd.cmd.clk.cmd_type); + goto done; + } + + pmu_wait_message_cond(&g->pmu, + gk20a_get_gr_idle_timeout(g), + &handler.success, 1); + + if (handler.success == 0) { + nvgpu_err(g, "rpc call to load vin cal failed"); + status = -EINVAL; + } + +done: + return status; +} + +u32 nvgpu_clk_vf_change_inject_data_fill_gp10x(struct gk20a *g, + struct nv_pmu_clk_rpc *rpccall, + struct set_fll_clk *setfllclk) +{ + struct nv_pmu_clk_vf_change_inject *vfchange; + + vfchange = &rpccall->params.clk_vf_change_inject; + vfchange->flags = 0; + vfchange->clk_list.num_domains = 3; + vfchange->clk_list.clk_domains[0].clk_domain = CTRL_CLK_DOMAIN_GPC2CLK; + vfchange->clk_list.clk_domains[0].clk_freq_khz = + setfllclk->gpc2clkmhz * 1000; + vfchange->clk_list.clk_domains[0].clk_flags = 0; + vfchange->clk_list.clk_domains[0].current_regime_id = + setfllclk->current_regime_id_gpc; + vfchange->clk_list.clk_domains[0].target_regime_id = + setfllclk->target_regime_id_gpc; + vfchange->clk_list.clk_domains[1].clk_domain = CTRL_CLK_DOMAIN_XBAR2CLK; + vfchange->clk_list.clk_domains[1].clk_freq_khz = + setfllclk->xbar2clkmhz * 1000; + vfchange->clk_list.clk_domains[1].clk_flags = 0; + vfchange->clk_list.clk_domains[1].current_regime_id = + setfllclk->current_regime_id_xbar; + vfchange->clk_list.clk_domains[1].target_regime_id = + setfllclk->target_regime_id_xbar; + vfchange->clk_list.clk_domains[2].clk_domain = CTRL_CLK_DOMAIN_SYS2CLK; + vfchange->clk_list.clk_domains[2].clk_freq_khz = + setfllclk->sys2clkmhz * 1000; + vfchange->clk_list.clk_domains[2].clk_flags = 0; + vfchange->clk_list.clk_domains[2].current_regime_id = + setfllclk->current_regime_id_sys; + vfchange->clk_list.clk_domains[2].target_regime_id = + setfllclk->target_regime_id_sys; + vfchange->volt_list.num_rails = 1; + vfchange->volt_list.rails[0].volt_domain = CTRL_VOLT_DOMAIN_LOGIC; + vfchange->volt_list.rails[0].voltage_uv = setfllclk->voltuv; + vfchange->volt_list.rails[0].voltage_min_noise_unaware_uv = + setfllclk->voltuv; + + return 0; +} + +u32 nvgpu_clk_vf_change_inject_data_fill_gv10x(struct gk20a *g, + struct nv_pmu_clk_rpc *rpccall, + struct set_fll_clk *setfllclk) +{ + struct nv_pmu_clk_vf_change_inject_v1 *vfchange; + + vfchange = &rpccall->params.clk_vf_change_inject_v1; + vfchange->flags = 0; + vfchange->clk_list.num_domains = 4; + vfchange->clk_list.clk_domains[0].clk_domain = CTRL_CLK_DOMAIN_GPCCLK; + vfchange->clk_list.clk_domains[0].clk_freq_khz = + setfllclk->gpc2clkmhz * 1000; + + vfchange->clk_list.clk_domains[1].clk_domain = CTRL_CLK_DOMAIN_XBARCLK; + vfchange->clk_list.clk_domains[1].clk_freq_khz = + setfllclk->xbar2clkmhz * 1000; + + vfchange->clk_list.clk_domains[2].clk_domain = CTRL_CLK_DOMAIN_SYSCLK; + vfchange->clk_list.clk_domains[2].clk_freq_khz = + setfllclk->sys2clkmhz * 1000; + + vfchange->clk_list.clk_domains[3].clk_domain = CTRL_CLK_DOMAIN_NVDCLK; + vfchange->clk_list.clk_domains[3].clk_freq_khz = 855 * 1000; + + vfchange->volt_list.num_rails = 1; + vfchange->volt_list.rails[0].rail_idx = 0; + vfchange->volt_list.rails[0].voltage_uv = setfllclk->voltuv; + vfchange->volt_list.rails[0].voltage_min_noise_unaware_uv = + setfllclk->voltuv; + + return 0; +} + +static u32 clk_pmu_vf_inject(struct gk20a *g, struct set_fll_clk *setfllclk) +{ + struct pmu_cmd cmd; + struct pmu_payload payload; + u32 status; + u32 seqdesc; + struct nv_pmu_clk_rpc rpccall; + struct clkrpc_pmucmdhandler_params handler; + + memset(&payload, 0, sizeof(struct pmu_payload)); + memset(&rpccall, 0, sizeof(struct nv_pmu_clk_rpc)); + memset(&handler, 0, sizeof(struct clkrpc_pmucmdhandler_params)); + memset(&cmd, 0, sizeof(struct pmu_cmd)); + + if ((setfllclk->gpc2clkmhz == 0) || (setfllclk->xbar2clkmhz == 0) || + (setfllclk->sys2clkmhz == 0) || (setfllclk->voltuv == 0)) { + return -EINVAL; + } + + if ((setfllclk->target_regime_id_gpc > CTRL_CLK_FLL_REGIME_ID_FR) || + (setfllclk->target_regime_id_sys > CTRL_CLK_FLL_REGIME_ID_FR) || + (setfllclk->target_regime_id_xbar > CTRL_CLK_FLL_REGIME_ID_FR)) { + return -EINVAL; + } + + rpccall.function = NV_PMU_CLK_RPC_ID_CLK_VF_CHANGE_INJECT; + + g->ops.pmu_ver.clk.clk_vf_change_inject_data_fill(g, + &rpccall, setfllclk); + + cmd.hdr.unit_id = PMU_UNIT_CLK; + cmd.hdr.size = (u32)sizeof(struct nv_pmu_clk_cmd) + + (u32)sizeof(struct pmu_hdr); + + cmd.cmd.clk.cmd_type = NV_PMU_CLK_CMD_ID_RPC; + + payload.in.buf = (u8 *)&rpccall; + payload.in.size = (u32)sizeof(struct nv_pmu_clk_rpc); + payload.in.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; + payload.in.offset = NV_PMU_CLK_CMD_RPC_ALLOC_OFFSET; + + payload.out.buf = (u8 *)&rpccall; + payload.out.size = (u32)sizeof(struct nv_pmu_clk_rpc); + payload.out.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; + payload.out.offset = NV_PMU_CLK_MSG_RPC_ALLOC_OFFSET; + + handler.prpccall = &rpccall; + handler.success = 0; + + status = nvgpu_pmu_cmd_post(g, &cmd, NULL, &payload, + PMU_COMMAND_QUEUE_LPQ, + clkrpc_pmucmdhandler, (void *)&handler, + &seqdesc, ~0); + + if (status) { + nvgpu_err(g, "unable to post clk RPC cmd %x", + cmd.cmd.clk.cmd_type); + goto done; + } + + pmu_wait_message_cond(&g->pmu, + gk20a_get_gr_idle_timeout(g), + &handler.success, 1); + + if (handler.success == 0) { + nvgpu_err(g, "rpc call to inject clock failed"); + status = -EINVAL; + } +done: + return status; +} + +static u32 find_regime_id(struct gk20a *g, u32 domain, u16 clkmhz) +{ + struct fll_device *pflldev; + u8 j; + struct clk_pmupstate *pclk = &g->clk_pmu; + + BOARDOBJGRP_FOR_EACH(&(pclk->avfs_fllobjs.super.super), + struct fll_device *, pflldev, j) { + if (pflldev->clk_domain == domain) { + if (pflldev->regime_desc.fixed_freq_regime_limit_mhz >= + clkmhz) { + return CTRL_CLK_FLL_REGIME_ID_FFR; + } else { + return CTRL_CLK_FLL_REGIME_ID_FR; + } + } + } + return CTRL_CLK_FLL_REGIME_ID_INVALID; +} + +static int set_regime_id(struct gk20a *g, u32 domain, u32 regimeid) +{ + struct fll_device *pflldev; + u8 j; + struct clk_pmupstate *pclk = &g->clk_pmu; + + BOARDOBJGRP_FOR_EACH(&(pclk->avfs_fllobjs.super.super), + struct fll_device *, pflldev, j) { + if (pflldev->clk_domain == domain) { + pflldev->regime_desc.regime_id = regimeid; + return 0; + } + } + return -EINVAL; +} + +static int get_regime_id(struct gk20a *g, u32 domain, u32 *regimeid) +{ + struct fll_device *pflldev; + u8 j; + struct clk_pmupstate *pclk = &g->clk_pmu; + + BOARDOBJGRP_FOR_EACH(&(pclk->avfs_fllobjs.super.super), + struct fll_device *, pflldev, j) { + if (pflldev->clk_domain == domain) { + *regimeid = pflldev->regime_desc.regime_id; + return 0; + } + } + return -EINVAL; +} + +int clk_set_fll_clks(struct gk20a *g, struct set_fll_clk *setfllclk) +{ + int status = -EINVAL; + + /*set regime ids */ + status = get_regime_id(g, CTRL_CLK_DOMAIN_GPC2CLK, + &setfllclk->current_regime_id_gpc); + if (status) { + goto done; + } + + setfllclk->target_regime_id_gpc = find_regime_id(g, + CTRL_CLK_DOMAIN_GPC2CLK, setfllclk->gpc2clkmhz); + + status = get_regime_id(g, CTRL_CLK_DOMAIN_SYS2CLK, + &setfllclk->current_regime_id_sys); + if (status) { + goto done; + } + + setfllclk->target_regime_id_sys = find_regime_id(g, + CTRL_CLK_DOMAIN_SYS2CLK, setfllclk->sys2clkmhz); + + status = get_regime_id(g, CTRL_CLK_DOMAIN_XBAR2CLK, + &setfllclk->current_regime_id_xbar); + if (status) { + goto done; + } + + setfllclk->target_regime_id_xbar = find_regime_id(g, + CTRL_CLK_DOMAIN_XBAR2CLK, setfllclk->xbar2clkmhz); + + status = clk_pmu_vf_inject(g, setfllclk); + + if (status) { + nvgpu_err(g, "vf inject to change clk failed"); + } + + /* save regime ids */ + status = set_regime_id(g, CTRL_CLK_DOMAIN_XBAR2CLK, + setfllclk->target_regime_id_xbar); + if (status) { + goto done; + } + + status = set_regime_id(g, CTRL_CLK_DOMAIN_GPC2CLK, + setfllclk->target_regime_id_gpc); + if (status) { + goto done; + } + + status = set_regime_id(g, CTRL_CLK_DOMAIN_SYS2CLK, + setfllclk->target_regime_id_sys); + if (status) { + goto done; + } +done: + return status; +} + +int clk_get_fll_clks(struct gk20a *g, struct set_fll_clk *setfllclk) +{ + int status = -EINVAL; + struct clk_domain *pdomain; + u8 i; + struct clk_pmupstate *pclk = &g->clk_pmu; + u16 clkmhz = 0; + struct clk_domain_3x_master *p3xmaster; + struct clk_domain_3x_slave *p3xslave; + unsigned long slaveidxmask; + + if (setfllclk->gpc2clkmhz == 0) { + return -EINVAL; + } + + BOARDOBJGRP_FOR_EACH(&(pclk->clk_domainobjs.super.super), + struct clk_domain *, pdomain, i) { + + if (pdomain->api_domain == CTRL_CLK_DOMAIN_GPC2CLK) { + + if (!pdomain->super.implements(g, &pdomain->super, + CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER)) { + status = -EINVAL; + goto done; + } + p3xmaster = (struct clk_domain_3x_master *)pdomain; + slaveidxmask = p3xmaster->slave_idxs_mask; + for_each_set_bit(i, &slaveidxmask, 32) { + p3xslave = (struct clk_domain_3x_slave *) + CLK_CLK_DOMAIN_GET(pclk, i); + if ((p3xslave->super.super.super.api_domain != + CTRL_CLK_DOMAIN_XBAR2CLK) && + (p3xslave->super.super.super.api_domain != + CTRL_CLK_DOMAIN_SYS2CLK)) { + continue; + } + clkmhz = 0; + status = p3xslave->clkdomainclkgetslaveclk(g, + pclk, + (struct clk_domain *)p3xslave, + &clkmhz, + setfllclk->gpc2clkmhz); + if (status) { + status = -EINVAL; + goto done; + } + if (p3xslave->super.super.super.api_domain == + CTRL_CLK_DOMAIN_XBAR2CLK) { + setfllclk->xbar2clkmhz = clkmhz; + } + if (p3xslave->super.super.super.api_domain == + CTRL_CLK_DOMAIN_SYS2CLK) { + setfllclk->sys2clkmhz = clkmhz; + } + } + } + } +done: + return status; +} + +u32 clk_domain_print_vf_table(struct gk20a *g, u32 clkapidomain) +{ + u32 status = -EINVAL; + struct clk_domain *pdomain; + u8 i; + struct clk_pmupstate *pclk = &g->clk_pmu; + u16 clkmhz = 0; + u32 volt = 0; + + BOARDOBJGRP_FOR_EACH(&(pclk->clk_domainobjs.super.super), + struct clk_domain *, pdomain, i) { + if (pdomain->api_domain == clkapidomain) { + status = pdomain->clkdomainclkvfsearch(g, pclk, + pdomain, &clkmhz, &volt, + CLK_PROG_VFE_ENTRY_LOGIC); + status = pdomain->clkdomainclkvfsearch(g, pclk, + pdomain, &clkmhz, &volt, + CLK_PROG_VFE_ENTRY_SRAM); + } + } + return status; +} + +static int clk_program_fllclks(struct gk20a *g, struct change_fll_clk *fllclk) +{ + int status = -EINVAL; + struct clk_domain *pdomain; + u8 i; + struct clk_pmupstate *pclk = &g->clk_pmu; + u16 clkmhz = 0; + struct clk_domain_3x_master *p3xmaster; + struct clk_domain_3x_slave *p3xslave; + unsigned long slaveidxmask; + struct set_fll_clk setfllclk; + + if (fllclk->api_clk_domain != CTRL_CLK_DOMAIN_GPCCLK) { + return -EINVAL; + } + if (fllclk->voltuv == 0) { + return -EINVAL; + } + if (fllclk->clkmhz == 0) { + return -EINVAL; + } + + setfllclk.voltuv = fllclk->voltuv; + setfllclk.gpc2clkmhz = fllclk->clkmhz; + + BOARDOBJGRP_FOR_EACH(&(pclk->clk_domainobjs.super.super), + struct clk_domain *, pdomain, i) { + + if (pdomain->api_domain == fllclk->api_clk_domain) { + + if (!pdomain->super.implements(g, &pdomain->super, + CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER)) { + status = -EINVAL; + goto done; + } + p3xmaster = (struct clk_domain_3x_master *)pdomain; + slaveidxmask = p3xmaster->slave_idxs_mask; + for_each_set_bit(i, &slaveidxmask, 32) { + p3xslave = (struct clk_domain_3x_slave *) + CLK_CLK_DOMAIN_GET(pclk, i); + if ((p3xslave->super.super.super.api_domain != + CTRL_CLK_DOMAIN_XBARCLK) && + (p3xslave->super.super.super.api_domain != + CTRL_CLK_DOMAIN_SYSCLK)) { + continue; + } + clkmhz = 0; + status = p3xslave->clkdomainclkgetslaveclk(g, + pclk, + (struct clk_domain *)p3xslave, + &clkmhz, + fllclk->clkmhz); + if (status) { + status = -EINVAL; + goto done; + } + if (p3xslave->super.super.super.api_domain == + CTRL_CLK_DOMAIN_XBARCLK) { + setfllclk.xbar2clkmhz = clkmhz; + } + if (p3xslave->super.super.super.api_domain == + CTRL_CLK_DOMAIN_SYSCLK) { + setfllclk.sys2clkmhz = clkmhz; + } + } + } + } + /*set regime ids */ + status = get_regime_id(g, CTRL_CLK_DOMAIN_GPCCLK, + &setfllclk.current_regime_id_gpc); + if (status) { + goto done; + } + + setfllclk.target_regime_id_gpc = find_regime_id(g, + CTRL_CLK_DOMAIN_GPCCLK, setfllclk.gpc2clkmhz); + + status = get_regime_id(g, CTRL_CLK_DOMAIN_SYSCLK, + &setfllclk.current_regime_id_sys); + if (status) { + goto done; + } + + setfllclk.target_regime_id_sys = find_regime_id(g, + CTRL_CLK_DOMAIN_SYSCLK, setfllclk.sys2clkmhz); + + status = get_regime_id(g, CTRL_CLK_DOMAIN_XBARCLK, + &setfllclk.current_regime_id_xbar); + if (status) { + goto done; + } + + setfllclk.target_regime_id_xbar = find_regime_id(g, + CTRL_CLK_DOMAIN_XBARCLK, setfllclk.xbar2clkmhz); + + status = clk_pmu_vf_inject(g, &setfllclk); + + if (status) { + nvgpu_err(g, + "vf inject to change clk failed"); + } + + /* save regime ids */ + status = set_regime_id(g, CTRL_CLK_DOMAIN_XBARCLK, + setfllclk.target_regime_id_xbar); + if (status) { + goto done; + } + + status = set_regime_id(g, CTRL_CLK_DOMAIN_GPCCLK, + setfllclk.target_regime_id_gpc); + if (status) { + goto done; + } + + status = set_regime_id(g, CTRL_CLK_DOMAIN_SYSCLK, + setfllclk.target_regime_id_sys); + if (status) { + goto done; + } +done: + return status; +} + +u32 nvgpu_clk_set_boot_fll_clk_gv10x(struct gk20a *g) +{ + int status; + struct change_fll_clk bootfllclk; + u16 gpcclk_clkmhz = BOOT_GPCCLK_MHZ; + u32 gpcclk_voltuv = 0; + u32 voltuv = 0; + + status = clk_vf_point_cache(g); + if (status) { + nvgpu_err(g,"caching failed"); + return status; + } + + status = clk_domain_get_f_or_v(g, CTRL_CLK_DOMAIN_GPCCLK, + &gpcclk_clkmhz, &gpcclk_voltuv, CTRL_VOLT_DOMAIN_LOGIC); + if (status) { + return status; + } + + voltuv = gpcclk_voltuv; + + status = volt_set_voltage(g, voltuv, 0); + if (status) { + nvgpu_err(g, + "attempt to set boot voltage failed %d", + voltuv); + } + + bootfllclk.api_clk_domain = CTRL_CLK_DOMAIN_GPCCLK; + bootfllclk.clkmhz = gpcclk_clkmhz; + bootfllclk.voltuv = voltuv; + status = clk_program_fllclks(g, &bootfllclk); + if (status) { + nvgpu_err(g, "attempt to set boot gpcclk failed"); + } + + status = clk_pmu_freq_effective_avg_load(g, true); + + /* + * Read clocks after some delay with below method + * & extract clock data from buffer + * clk_freq_effective_avg(g, CTRL_CLK_DOMAIN_GPCCLK | + * CTRL_CLK_DOMAIN_XBARCLK | + * CTRL_CLK_DOMAIN_SYSCLK | + * CTRL_CLK_DOMAIN_NVDCLK) + * */ + + return status; +} + +int nvgpu_clk_set_fll_clk_gv10x(struct gk20a *g) +{ + int status; + struct change_fll_clk bootfllclk; + u16 gpcclk_clkmhz = BOOT_GPCCLK_MHZ; + u32 gpcclk_voltuv = 0U; + u32 voltuv = 0U; + + status = clk_vf_point_cache(g); + if (status != 0) { + nvgpu_err(g, "caching failed"); + return status; + } + + status = clk_domain_get_f_or_v(g, CTRL_CLK_DOMAIN_GPCCLK, + &gpcclk_clkmhz, &gpcclk_voltuv, CTRL_VOLT_DOMAIN_LOGIC); + if (status != 0) { + return status; + } + + voltuv = gpcclk_voltuv; + + status = volt_set_voltage(g, voltuv, 0U); + if (status != 0) { + nvgpu_err(g, "attempt to set max voltage failed %d", voltuv); + } + + bootfllclk.api_clk_domain = CTRL_CLK_DOMAIN_GPCCLK; + bootfllclk.clkmhz = gpcclk_clkmhz; + bootfllclk.voltuv = voltuv; + status = clk_program_fllclks(g, &bootfllclk); + if (status != 0) { + nvgpu_err(g, "attempt to set max gpcclk failed"); + } + return status; +} + +u32 clk_domain_get_f_or_v( + struct gk20a *g, + u32 clkapidomain, + u16 *pclkmhz, + u32 *pvoltuv, + u8 railidx +) +{ + u32 status = -EINVAL; + struct clk_domain *pdomain; + u8 i; + struct clk_pmupstate *pclk = &g->clk_pmu; + u8 rail; + + if ((pclkmhz == NULL) || (pvoltuv == NULL)) { + return -EINVAL; + } + + if (railidx == CTRL_VOLT_DOMAIN_LOGIC) { + rail = CLK_PROG_VFE_ENTRY_LOGIC; + } else if (railidx == CTRL_VOLT_DOMAIN_SRAM) { + rail = CLK_PROG_VFE_ENTRY_SRAM; + } else { + return -EINVAL; + } + + BOARDOBJGRP_FOR_EACH(&(pclk->clk_domainobjs.super.super), + struct clk_domain *, pdomain, i) { + if (pdomain->api_domain == clkapidomain) { + status = pdomain->clkdomainclkvfsearch(g, pclk, + pdomain, pclkmhz, pvoltuv, rail); + return status; + } + } + return status; +} diff --git a/include/clk/clk.h b/include/clk/clk.h new file mode 100644 index 0000000..3f4bdf7 --- /dev/null +++ b/include/clk/clk.h @@ -0,0 +1,144 @@ +/* + * general clock structures & definitions + * + * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef NVGPU_CLK_H +#define NVGPU_CLK_H + +#include "clk_vin.h" +#include "clk_fll.h" +#include "clk_domain.h" +#include "clk_prog.h" +#include "clk_vf_point.h" +#include "clk_mclk.h" +#include "clk_freq_controller.h" + +#define NV_PERF_DOMAIN_4X_CLOCK_DOMAIN_SKIP 0x10 +#define NV_PERF_DOMAIN_4X_CLOCK_DOMAIN_MASK 0x1F +#define NV_PERF_DOMAIN_4X_CLOCK_DOMAIN_SHIFT 0 +#define BOOT_GPCCLK_MHZ 952 + +struct gk20a; + +int clk_set_boot_fll_clk(struct gk20a *g); + +/* clock related defines for GPUs supporting clock control from pmu*/ +struct clk_pmupstate { + struct avfsvinobjs avfs_vinobjs; + struct avfsfllobjs avfs_fllobjs; + struct clk_domains clk_domainobjs; + struct clk_progs clk_progobjs; + struct clk_vf_points clk_vf_pointobjs; + struct clk_mclk_state clk_mclk; + struct clk_freq_controllers clk_freq_controllers; +}; + +struct clockentry { + u8 vbios_clk_domain; + u8 clk_which; + u8 perf_index; + u32 api_clk_domain; +}; + +struct change_fll_clk { + u32 api_clk_domain; + u16 clkmhz; + u32 voltuv; +}; + +struct set_fll_clk { + u32 voltuv; + u16 gpc2clkmhz; + u32 current_regime_id_gpc; + u32 target_regime_id_gpc; + u16 sys2clkmhz; + u32 current_regime_id_sys; + u32 target_regime_id_sys; + u16 xbar2clkmhz; + u32 current_regime_id_xbar; + u32 target_regime_id_xbar; +}; + +#define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_MAX_NUMCLKS 9 + +struct vbios_clock_domain { + u8 clock_type; + u8 num_domains; + struct clockentry clock_entry[NV_PERF_HEADER_4X_CLOCKS_DOMAINS_MAX_NUMCLKS]; +}; + +struct vbios_clocks_table_1x_hal_clock_entry { + enum nv_pmu_clk_clkwhich domain; + bool b_noise_aware_capable; + u8 clk_vf_curve_count; +}; + +#define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_GPC2CLK 0 +#define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_XBAR2CLK 1 +#define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_DRAMCLK 2 +#define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_SYS2CLK 3 +#define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_HUB2CLK 4 +#define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_MSDCLK 5 +#define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_PWRCLK 6 +#define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_DISPCLK 7 +#define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_NUMCLKS 8 + +#define PERF_CLK_MCLK 0 +#define PERF_CLK_DISPCLK 1 +#define PERF_CLK_GPC2CLK 2 +#define PERF_CLK_HOSTCLK 3 +#define PERF_CLK_LTC2CLK 4 +#define PERF_CLK_SYS2CLK 5 +#define PERF_CLK_HUB2CLK 6 +#define PERF_CLK_LEGCLK 7 +#define PERF_CLK_MSDCLK 8 +#define PERF_CLK_XCLK 9 +#define PERF_CLK_PWRCLK 10 +#define PERF_CLK_XBAR2CLK 11 +#define PERF_CLK_PCIEGENCLK 12 +#define PERF_CLK_NUM 13 + +#define BOOT_GPC2CLK_MHZ 2581 + +u32 clk_pmu_vin_load(struct gk20a *g); +u32 clk_domain_print_vf_table(struct gk20a *g, u32 clkapidomain); +u32 clk_domain_get_f_or_v( + struct gk20a *g, + u32 clkapidomain, + u16 *pclkmhz, + u32 *pvoltuv, + u8 railidx +); +int clk_get_fll_clks(struct gk20a *g, struct set_fll_clk *fllclk); +int clk_set_fll_clks(struct gk20a *g, struct set_fll_clk *fllclk); +int clk_pmu_freq_controller_load(struct gk20a *g, bool bload, u8 bit_idx); +u32 nvgpu_clk_vf_change_inject_data_fill_gv10x(struct gk20a *g, + struct nv_pmu_clk_rpc *rpccall, + struct set_fll_clk *setfllclk); +u32 nvgpu_clk_vf_change_inject_data_fill_gp10x(struct gk20a *g, + struct nv_pmu_clk_rpc *rpccall, + struct set_fll_clk *setfllclk); +u32 nvgpu_clk_set_boot_fll_clk_gv10x(struct gk20a *g); +int nvgpu_clk_set_fll_clk_gv10x(struct gk20a *g); +int clk_pmu_freq_effective_avg_load(struct gk20a *g, bool bload); +u32 clk_freq_effective_avg(struct gk20a *g, u32 clkDomainMask); +#endif /* NVGPU_CLK_H */ diff --git a/include/clk/clk_arb.c b/include/clk/clk_arb.c new file mode 100644 index 0000000..6cf005c --- /dev/null +++ b/include/clk/clk_arb.c @@ -0,0 +1,1087 @@ +/* + * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk/clk.h" +#include "pstate/pstate.h" +#include "lpwr/lpwr.h" +#include "volt/volt.h" + +int nvgpu_clk_notification_queue_alloc(struct gk20a *g, + struct nvgpu_clk_notification_queue *queue, + size_t events_number) { + queue->notifications = nvgpu_kcalloc(g, events_number, + sizeof(struct nvgpu_clk_notification)); + if (!queue->notifications) + return -ENOMEM; + queue->size = events_number; + + nvgpu_atomic_set(&queue->head, 0); + nvgpu_atomic_set(&queue->tail, 0); + + return 0; +} + +void nvgpu_clk_notification_queue_free(struct gk20a *g, + struct nvgpu_clk_notification_queue *queue) { + if (queue->size > 0) { + nvgpu_kfree(g, queue->notifications); + queue->size = 0; + nvgpu_atomic_set(&queue->head, 0); + nvgpu_atomic_set(&queue->tail, 0); + } +} + +static void nvgpu_clk_arb_queue_notification(struct gk20a *g, + struct nvgpu_clk_notification_queue *queue, + u32 alarm_mask) { + + u32 queue_index; + u64 timestamp; + + queue_index = (nvgpu_atomic_inc_return(&queue->tail)) % queue->size; + /* get current timestamp */ + timestamp = (u64) nvgpu_hr_timestamp(); + + queue->notifications[queue_index].timestamp = timestamp; + queue->notifications[queue_index].notification = alarm_mask; + +} + +void nvgpu_clk_arb_set_global_alarm(struct gk20a *g, u32 alarm) +{ + struct nvgpu_clk_arb *arb = g->clk_arb; + + u64 current_mask; + u32 refcnt; + u32 alarm_mask; + u64 new_mask; + + do { + current_mask = nvgpu_atomic64_read(&arb->alarm_mask); + /* atomic operations are strong so they do not need masks */ + + refcnt = ((u32) (current_mask >> 32)) + 1; + alarm_mask = (u32) (current_mask & ~0) | alarm; + new_mask = ((u64) refcnt << 32) | alarm_mask; + + } while (unlikely(current_mask != + (u64)nvgpu_atomic64_cmpxchg(&arb->alarm_mask, + current_mask, new_mask))); + + nvgpu_clk_arb_queue_notification(g, &arb->notification_queue, alarm); +} + + +int nvgpu_clk_arb_update_vf_table(struct nvgpu_clk_arb *arb) +{ + struct gk20a *g = arb->g; + struct nvgpu_clk_vf_table *table; + + u32 i, j; + int status = -EINVAL; + u32 gpc2clk_voltuv = 0, mclk_voltuv = 0; + u32 gpc2clk_voltuv_sram = 0, mclk_voltuv_sram = 0; + u16 clk_cur; + u32 num_points; + + struct clk_set_info *p5_info, *p0_info; + + table = NV_ACCESS_ONCE(arb->current_vf_table); + /* make flag visible when all data has resolved in the tables */ + nvgpu_smp_rmb(); + + table = (table == &arb->vf_table_pool[0]) ? &arb->vf_table_pool[1] : + &arb->vf_table_pool[0]; + + /* Get allowed memory ranges */ + if (g->ops.clk_arb.get_arbiter_clk_range(g, CTRL_CLK_DOMAIN_GPC2CLK, + &arb->gpc2clk_min, + &arb->gpc2clk_max) < 0) { + nvgpu_err(g, "failed to fetch GPC2CLK range"); + goto exit_vf_table; + } + if (g->ops.clk_arb.get_arbiter_clk_range(g, CTRL_CLK_DOMAIN_MCLK, + &arb->mclk_min, + &arb->mclk_max) < 0) { + nvgpu_err(g, "failed to fetch MCLK range"); + goto exit_vf_table; + } + + table->gpc2clk_num_points = MAX_F_POINTS; + table->mclk_num_points = MAX_F_POINTS; + + if (g->ops.clk.clk_domain_get_f_points(arb->g, CTRL_CLK_DOMAIN_GPC2CLK, + &table->gpc2clk_num_points, arb->gpc2clk_f_points)) { + nvgpu_err(g, "failed to fetch GPC2CLK frequency points"); + goto exit_vf_table; + } + + if (g->ops.clk.clk_domain_get_f_points(arb->g, CTRL_CLK_DOMAIN_MCLK, + &table->mclk_num_points, arb->mclk_f_points)) { + nvgpu_err(g, "failed to fetch MCLK frequency points"); + goto exit_vf_table; + } + if (!table->mclk_num_points || !table->gpc2clk_num_points) { + nvgpu_err(g, "empty queries to f points mclk %d gpc2clk %d", + table->mclk_num_points, table->gpc2clk_num_points); + status = -EINVAL; + goto exit_vf_table; + } + + memset(table->mclk_points, 0, + table->mclk_num_points*sizeof(struct nvgpu_clk_vf_point)); + memset(table->gpc2clk_points, 0, + table->gpc2clk_num_points*sizeof(struct nvgpu_clk_vf_point)); + + p5_info = pstate_get_clk_set_info(g, + CTRL_PERF_PSTATE_P5, clkwhich_mclk); + if (!p5_info) { + nvgpu_err(g, "failed to get MCLK P5 info"); + goto exit_vf_table; + } + p0_info = pstate_get_clk_set_info(g, + CTRL_PERF_PSTATE_P0, clkwhich_mclk); + if (!p0_info) { + nvgpu_err(g, "failed to get MCLK P0 info"); + goto exit_vf_table; + } + + for (i = 0, j = 0, num_points = 0, clk_cur = 0; + i < table->mclk_num_points; i++) { + + if ((arb->mclk_f_points[i] >= arb->mclk_min) && + (arb->mclk_f_points[i] <= arb->mclk_max) && + (arb->mclk_f_points[i] != clk_cur)) { + + table->mclk_points[j].mem_mhz = arb->mclk_f_points[i]; + mclk_voltuv = mclk_voltuv_sram = 0; + + status = clk_domain_get_f_or_v(g, CTRL_CLK_DOMAIN_MCLK, + &table->mclk_points[j].mem_mhz, &mclk_voltuv, + CTRL_VOLT_DOMAIN_LOGIC); + if (status < 0) { + nvgpu_err(g, + "failed to get MCLK LOGIC voltage"); + goto exit_vf_table; + } + status = clk_domain_get_f_or_v(g, CTRL_CLK_DOMAIN_MCLK, + &table->mclk_points[j].mem_mhz, + &mclk_voltuv_sram, + CTRL_VOLT_DOMAIN_SRAM); + if (status < 0) { + nvgpu_err(g, "failed to get MCLK SRAM voltage"); + goto exit_vf_table; + } + + table->mclk_points[j].uvolt = mclk_voltuv; + table->mclk_points[j].uvolt_sram = mclk_voltuv_sram; + clk_cur = table->mclk_points[j].mem_mhz; + + if ((clk_cur >= p5_info->min_mhz) && + (clk_cur <= p5_info->max_mhz)) + VF_POINT_SET_PSTATE_SUPPORTED( + &table->mclk_points[j], + CTRL_PERF_PSTATE_P5); + if ((clk_cur >= p0_info->min_mhz) && + (clk_cur <= p0_info->max_mhz)) + VF_POINT_SET_PSTATE_SUPPORTED( + &table->mclk_points[j], + CTRL_PERF_PSTATE_P0); + + j++; + num_points++; + + } + } + table->mclk_num_points = num_points; + + p5_info = pstate_get_clk_set_info(g, + CTRL_PERF_PSTATE_P5, clkwhich_gpc2clk); + if (!p5_info) { + status = -EINVAL; + nvgpu_err(g, "failed to get GPC2CLK P5 info"); + goto exit_vf_table; + } + + p0_info = pstate_get_clk_set_info(g, + CTRL_PERF_PSTATE_P0, clkwhich_gpc2clk); + if (!p0_info) { + status = -EINVAL; + nvgpu_err(g, "failed to get GPC2CLK P0 info"); + goto exit_vf_table; + } + + /* GPC2CLK needs to be checked in two passes. The first determines the + * relationships between GPC2CLK, SYS2CLK and XBAR2CLK, while the + * second verifies that the clocks minimum is satisfied and sets + * the voltages + */ + for (i = 0, j = 0, num_points = 0, clk_cur = 0; + i < table->gpc2clk_num_points; i++) { + struct set_fll_clk setfllclk; + + if ((arb->gpc2clk_f_points[i] >= arb->gpc2clk_min) && + (arb->gpc2clk_f_points[i] <= arb->gpc2clk_max) && + (arb->gpc2clk_f_points[i] != clk_cur)) { + + table->gpc2clk_points[j].gpc_mhz = + arb->gpc2clk_f_points[i]; + setfllclk.gpc2clkmhz = arb->gpc2clk_f_points[i]; + status = clk_get_fll_clks(g, &setfllclk); + if (status < 0) { + nvgpu_err(g, + "failed to get GPC2CLK slave clocks"); + goto exit_vf_table; + } + + table->gpc2clk_points[j].sys_mhz = + setfllclk.sys2clkmhz; + table->gpc2clk_points[j].xbar_mhz = + setfllclk.xbar2clkmhz; + + clk_cur = table->gpc2clk_points[j].gpc_mhz; + + if ((clk_cur >= p5_info->min_mhz) && + (clk_cur <= p5_info->max_mhz)) + VF_POINT_SET_PSTATE_SUPPORTED( + &table->gpc2clk_points[j], + CTRL_PERF_PSTATE_P5); + if ((clk_cur >= p0_info->min_mhz) && + (clk_cur <= p0_info->max_mhz)) + VF_POINT_SET_PSTATE_SUPPORTED( + &table->gpc2clk_points[j], + CTRL_PERF_PSTATE_P0); + + j++; + num_points++; + } + } + table->gpc2clk_num_points = num_points; + + /* Second pass */ + for (i = 0, j = 0; i < table->gpc2clk_num_points; i++) { + + u16 alt_gpc2clk = table->gpc2clk_points[i].gpc_mhz; + + gpc2clk_voltuv = gpc2clk_voltuv_sram = 0; + + /* Check sysclk */ + p5_info = pstate_get_clk_set_info(g, + VF_POINT_GET_PSTATE(&table->gpc2clk_points[i]), + clkwhich_sys2clk); + if (!p5_info) { + status = -EINVAL; + nvgpu_err(g, "failed to get SYS2CLK P5 info"); + goto exit_vf_table; + } + + /* sys2clk below clk min, need to find correct clock */ + if (table->gpc2clk_points[i].sys_mhz < p5_info->min_mhz) { + for (j = i + 1; j < table->gpc2clk_num_points; j++) { + + if (table->gpc2clk_points[j].sys_mhz >= + p5_info->min_mhz) { + + + table->gpc2clk_points[i].sys_mhz = + p5_info->min_mhz; + + alt_gpc2clk = alt_gpc2clk < + table->gpc2clk_points[j]. + gpc_mhz ? + table->gpc2clk_points[j]. + gpc_mhz : + alt_gpc2clk; + break; + } + } + /* no VF exists that satisfies condition */ + if (j == table->gpc2clk_num_points) { + nvgpu_err(g, "NO SYS2CLK VF point possible"); + status = -EINVAL; + goto exit_vf_table; + } + } + + /* Check xbarclk */ + p5_info = pstate_get_clk_set_info(g, + VF_POINT_GET_PSTATE(&table->gpc2clk_points[i]), + clkwhich_xbar2clk); + if (!p5_info) { + status = -EINVAL; + nvgpu_err(g, "failed to get SYS2CLK P5 info"); + goto exit_vf_table; + } + + /* xbar2clk below clk min, need to find correct clock */ + if (table->gpc2clk_points[i].xbar_mhz < p5_info->min_mhz) { + for (j = i; j < table->gpc2clk_num_points; j++) { + if (table->gpc2clk_points[j].xbar_mhz >= + p5_info->min_mhz) { + + table->gpc2clk_points[i].xbar_mhz = + p5_info->min_mhz; + + alt_gpc2clk = alt_gpc2clk < + table->gpc2clk_points[j]. + gpc_mhz ? + table->gpc2clk_points[j]. + gpc_mhz : + alt_gpc2clk; + break; + } + } + /* no VF exists that satisfies condition */ + if (j == table->gpc2clk_num_points) { + status = -EINVAL; + nvgpu_err(g, "NO XBAR2CLK VF point possible"); + + goto exit_vf_table; + } + } + + /* Calculate voltages */ + status = clk_domain_get_f_or_v(g, CTRL_CLK_DOMAIN_GPC2CLK, + &alt_gpc2clk, &gpc2clk_voltuv, + CTRL_VOLT_DOMAIN_LOGIC); + if (status < 0) { + nvgpu_err(g, "failed to get GPC2CLK LOGIC voltage"); + goto exit_vf_table; + } + + status = clk_domain_get_f_or_v(g, CTRL_CLK_DOMAIN_GPC2CLK, + &alt_gpc2clk, + &gpc2clk_voltuv_sram, + CTRL_VOLT_DOMAIN_SRAM); + if (status < 0) { + nvgpu_err(g, "failed to get GPC2CLK SRAM voltage"); + goto exit_vf_table; + } + + table->gpc2clk_points[i].uvolt = gpc2clk_voltuv; + table->gpc2clk_points[i].uvolt_sram = gpc2clk_voltuv_sram; + } + + /* make table visible when all data has resolved in the tables */ + nvgpu_smp_wmb(); + arb->current_vf_table = table; + +exit_vf_table: + + if (status < 0) + nvgpu_clk_arb_set_global_alarm(g, + EVENT(ALARM_VF_TABLE_UPDATE_FAILED)); + nvgpu_clk_arb_worker_enqueue(g, &arb->update_arb_work_item); + + return status; +} + + +static void nvgpu_clk_arb_run_vf_table_cb(struct nvgpu_clk_arb *arb) +{ + struct gk20a *g = arb->g; + u32 err; + + /* get latest vf curve from pmu */ + err = clk_vf_point_cache(g); + if (err) { + nvgpu_err(g, "failed to cache VF table"); + nvgpu_clk_arb_set_global_alarm(g, + EVENT(ALARM_VF_TABLE_UPDATE_FAILED)); + nvgpu_clk_arb_worker_enqueue(g, &arb->update_arb_work_item); + + return; + } + nvgpu_clk_arb_update_vf_table(arb); +} + +u32 nvgpu_clk_arb_notify(struct nvgpu_clk_dev *dev, + struct nvgpu_clk_arb_target *target, + u32 alarm) { + + struct nvgpu_clk_session *session = dev->session; + struct nvgpu_clk_arb *arb = session->g->clk_arb; + struct nvgpu_clk_notification *notification; + + u32 queue_alarm_mask = 0; + u32 enabled_mask = 0; + u32 new_alarms_reported = 0; + u32 poll_mask = 0; + u32 tail, head; + u32 queue_index; + size_t size; + int index; + + enabled_mask = nvgpu_atomic_read(&dev->enabled_mask); + size = arb->notification_queue.size; + + /* queue global arbiter notifications in buffer */ + do { + tail = nvgpu_atomic_read(&arb->notification_queue.tail); + /* copy items to the queue */ + queue_index = nvgpu_atomic_read(&dev->queue.tail); + head = dev->arb_queue_head; + head = (tail - head) < arb->notification_queue.size ? + head : tail - arb->notification_queue.size; + + for (index = head; _WRAPGTEQ(tail, index); index++) { + u32 alarm_detected; + + notification = &arb->notification_queue. + notifications[(index+1) % size]; + alarm_detected = + NV_ACCESS_ONCE(notification->notification); + + if (!(enabled_mask & alarm_detected)) + continue; + + queue_index++; + dev->queue.notifications[ + queue_index % dev->queue.size].timestamp = + NV_ACCESS_ONCE(notification->timestamp); + + dev->queue.notifications[ + queue_index % dev->queue.size].notification = + alarm_detected; + + queue_alarm_mask |= alarm_detected; + } + } while (unlikely(nvgpu_atomic_read(&arb->notification_queue.tail) != + (int)tail)); + + nvgpu_atomic_set(&dev->queue.tail, queue_index); + /* update the last notification we processed from global queue */ + + dev->arb_queue_head = tail; + + /* Check if current session targets are met */ + if (enabled_mask & EVENT(ALARM_LOCAL_TARGET_VF_NOT_POSSIBLE)) { + if ((target->gpc2clk < session->target->gpc2clk) + || (target->mclk < session->target->mclk)) { + + poll_mask |= (NVGPU_POLLIN | NVGPU_POLLPRI); + nvgpu_clk_arb_queue_notification(arb->g, &dev->queue, + EVENT(ALARM_LOCAL_TARGET_VF_NOT_POSSIBLE)); + } + } + + /* Check if there is a new VF update */ + if (queue_alarm_mask & EVENT(VF_UPDATE)) + poll_mask |= (NVGPU_POLLIN | NVGPU_POLLRDNORM); + + /* Notify sticky alarms that were not reported on previous run*/ + new_alarms_reported = (queue_alarm_mask | + (alarm & ~dev->alarms_reported & queue_alarm_mask)); + + if (new_alarms_reported & ~LOCAL_ALARM_MASK) { + /* check that we are not re-reporting */ + if (new_alarms_reported & EVENT(ALARM_GPU_LOST)) + poll_mask |= NVGPU_POLLHUP; + + poll_mask |= (NVGPU_POLLIN | NVGPU_POLLPRI); + /* On next run do not report global alarms that were already + * reported, but report SHUTDOWN always + */ + dev->alarms_reported = new_alarms_reported & ~LOCAL_ALARM_MASK & + ~EVENT(ALARM_GPU_LOST); + } + + if (poll_mask) { + nvgpu_atomic_set(&dev->poll_mask, poll_mask); + nvgpu_clk_arb_event_post_event(dev); + } + + return new_alarms_reported; +} + +void nvgpu_clk_arb_clear_global_alarm(struct gk20a *g, u32 alarm) +{ + struct nvgpu_clk_arb *arb = g->clk_arb; + + u64 current_mask; + u32 refcnt; + u32 alarm_mask; + u64 new_mask; + + do { + current_mask = nvgpu_atomic64_read(&arb->alarm_mask); + /* atomic operations are strong so they do not need masks */ + + refcnt = ((u32) (current_mask >> 32)) + 1; + alarm_mask = (u32) (current_mask & ~alarm); + new_mask = ((u64) refcnt << 32) | alarm_mask; + + } while (unlikely(current_mask != + (u64)nvgpu_atomic64_cmpxchg(&arb->alarm_mask, + current_mask, new_mask))); +} + +/* + * Process one scheduled work item. + */ +static void nvgpu_clk_arb_worker_process_item( + struct nvgpu_clk_arb_work_item *work_item) +{ + struct gk20a *g = work_item->arb->g; + + clk_arb_dbg(g, " "); + + if (work_item->item_type == CLK_ARB_WORK_UPDATE_VF_TABLE) + nvgpu_clk_arb_run_vf_table_cb(work_item->arb); + else if (work_item->item_type == CLK_ARB_WORK_UPDATE_ARB) + g->ops.clk_arb.clk_arb_run_arbiter_cb(work_item->arb); +} + +/** + * Tell the worker that one more work needs to be done. + * + * Increase the work counter to synchronize the worker with the new work. Wake + * up the worker. If the worker was already running, it will handle this work + * before going to sleep. + */ +static int nvgpu_clk_arb_worker_wakeup(struct gk20a *g) +{ + int put; + + clk_arb_dbg(g, " "); + + put = nvgpu_atomic_inc_return(&g->clk_arb_worker.put); + nvgpu_cond_signal_interruptible(&g->clk_arb_worker.wq); + + return put; +} + +/** + * Test if there is some work pending. + * + * This is a pair for nvgpu_clk_arb_worker_wakeup to be called from the + * worker. The worker has an internal work counter which is incremented once + * per finished work item. This is compared with the number of queued jobs. + */ +static bool nvgpu_clk_arb_worker_pending(struct gk20a *g, int get) +{ + bool pending = nvgpu_atomic_read(&g->clk_arb_worker.put) != get; + + /* We don't need barriers because they are implicit in locking */ + return pending; +} + +/** + * Process the queued works for the worker thread serially. + * + * Flush all the work items in the queue one by one. This may block timeout + * handling for a short while, as these are serialized. + */ +static void nvgpu_clk_arb_worker_process(struct gk20a *g, int *get) +{ + + while (nvgpu_clk_arb_worker_pending(g, *get)) { + struct nvgpu_clk_arb_work_item *work_item = NULL; + + nvgpu_spinlock_acquire(&g->clk_arb_worker.items_lock); + if (!nvgpu_list_empty(&g->clk_arb_worker.items)) { + work_item = nvgpu_list_first_entry(&g->clk_arb_worker.items, + nvgpu_clk_arb_work_item, worker_item); + nvgpu_list_del(&work_item->worker_item); + } + nvgpu_spinlock_release(&g->clk_arb_worker.items_lock); + + if (!work_item) { + /* + * Woke up for some other reason, but there are no + * other reasons than a work item added in the items list + * currently, so warn and ack the message. + */ + nvgpu_warn(g, "Spurious worker event!"); + ++*get; + break; + } + + nvgpu_clk_arb_worker_process_item(work_item); + ++*get; + } +} + +/* + * Process all work items found in the clk arbiter work queue. + */ +static int nvgpu_clk_arb_poll_worker(void *arg) +{ + struct gk20a *g = (struct gk20a *)arg; + struct gk20a_worker *worker = &g->clk_arb_worker; + int get = 0; + + clk_arb_dbg(g, " "); + + while (!nvgpu_thread_should_stop(&worker->poll_task)) { + int ret; + + ret = NVGPU_COND_WAIT_INTERRUPTIBLE( + &worker->wq, + nvgpu_clk_arb_worker_pending(g, get), 0); + + if (nvgpu_thread_should_stop(&worker->poll_task)) { + break; + } + + if (ret == 0) + nvgpu_clk_arb_worker_process(g, &get); + } + return 0; +} + +static int __nvgpu_clk_arb_worker_start(struct gk20a *g) +{ + char thread_name[64]; + int err = 0; + + if (nvgpu_thread_is_running(&g->clk_arb_worker.poll_task)) + return err; + + nvgpu_mutex_acquire(&g->clk_arb_worker.start_lock); + + /* + * Mutexes have implicit barriers, so there is no risk of a thread + * having a stale copy of the poll_task variable as the call to + * thread_is_running is volatile + */ + + if (nvgpu_thread_is_running(&g->clk_arb_worker.poll_task)) { + nvgpu_mutex_release(&g->clk_arb_worker.start_lock); + return err; + } + + snprintf(thread_name, sizeof(thread_name), + "nvgpu_clk_arb_poll_%s", g->name); + + err = nvgpu_thread_create(&g->clk_arb_worker.poll_task, g, + nvgpu_clk_arb_poll_worker, thread_name); + + nvgpu_mutex_release(&g->clk_arb_worker.start_lock); + return err; +} + +/** + * Append a work item to the worker's list. + * + * This adds work item to the end of the list and wakes the worker + * up immediately. If the work item already existed in the list, it's not added, + * because in that case it has been scheduled already but has not yet been + * processed. + */ +void nvgpu_clk_arb_worker_enqueue(struct gk20a *g, + struct nvgpu_clk_arb_work_item *work_item) +{ + clk_arb_dbg(g, " "); + + /* + * Warn if worker thread cannot run + */ + if (WARN_ON(__nvgpu_clk_arb_worker_start(g))) { + nvgpu_warn(g, "clk arb worker cannot run!"); + return; + } + + nvgpu_spinlock_acquire(&g->clk_arb_worker.items_lock); + if (!nvgpu_list_empty(&work_item->worker_item)) { + /* + * Already queued, so will get processed eventually. + * The worker is probably awake already. + */ + nvgpu_spinlock_release(&g->clk_arb_worker.items_lock); + return; + } + nvgpu_list_add_tail(&work_item->worker_item, &g->clk_arb_worker.items); + nvgpu_spinlock_release(&g->clk_arb_worker.items_lock); + + nvgpu_clk_arb_worker_wakeup(g); +} + +/** + * Initialize the clk arb worker's metadata and start the background thread. + */ +int nvgpu_clk_arb_worker_init(struct gk20a *g) +{ + int err; + + nvgpu_atomic_set(&g->clk_arb_worker.put, 0); + nvgpu_cond_init(&g->clk_arb_worker.wq); + nvgpu_init_list_node(&g->clk_arb_worker.items); + nvgpu_spinlock_init(&g->clk_arb_worker.items_lock); + err = nvgpu_mutex_init(&g->clk_arb_worker.start_lock); + if (err) + goto error_check; + + err = __nvgpu_clk_arb_worker_start(g); +error_check: + if (err) { + nvgpu_err(g, "failed to start clk arb poller thread"); + return err; + } + return 0; +} + +int nvgpu_clk_arb_init_arbiter(struct gk20a *g) +{ + int err = 0; + + if (!g->ops.clk.support_clk_freq_controller || + !g->ops.clk_arb.get_arbiter_clk_domains) { + return 0; + } + + nvgpu_mutex_acquire(&g->clk_arb_enable_lock); + + err = g->ops.clk_arb.arbiter_clk_init(g); + + nvgpu_mutex_release(&g->clk_arb_enable_lock); + + return err; +} + +bool nvgpu_clk_arb_has_active_req(struct gk20a *g) +{ + return (nvgpu_atomic_read(&g->clk_arb_global_nr) > 0); +} + +void nvgpu_clk_arb_send_thermal_alarm(struct gk20a *g) +{ + nvgpu_clk_arb_schedule_alarm(g, + (0x1UL << NVGPU_EVENT_ALARM_THERMAL_ABOVE_THRESHOLD)); +} + +void nvgpu_clk_arb_schedule_alarm(struct gk20a *g, u32 alarm) +{ + struct nvgpu_clk_arb *arb = g->clk_arb; + + nvgpu_clk_arb_set_global_alarm(g, alarm); + nvgpu_clk_arb_worker_enqueue(g, &arb->update_arb_work_item); +} + +static void nvgpu_clk_arb_worker_deinit(struct gk20a *g) +{ + nvgpu_atomic_inc(&g->clk_arb_worker.put); + + nvgpu_mutex_acquire(&g->clk_arb_worker.start_lock); + nvgpu_thread_stop(&g->clk_arb_worker.poll_task); + nvgpu_mutex_release(&g->clk_arb_worker.start_lock); +} + +void nvgpu_clk_arb_cleanup_arbiter(struct gk20a *g) +{ + struct nvgpu_clk_arb *arb = g->clk_arb; + + nvgpu_mutex_acquire(&g->clk_arb_enable_lock); + + if (arb) { + nvgpu_clk_arb_worker_deinit(g); + g->ops.clk_arb.clk_arb_cleanup(g->clk_arb); + } + + nvgpu_mutex_release(&g->clk_arb_enable_lock); +} + +int nvgpu_clk_arb_init_session(struct gk20a *g, + struct nvgpu_clk_session **_session) +{ + struct nvgpu_clk_arb *arb = g->clk_arb; + struct nvgpu_clk_session *session = *(_session); + + clk_arb_dbg(g, " "); + + if (!g->ops.clk.support_clk_freq_controller || + !g->ops.clk_arb.get_arbiter_clk_domains) { + return 0; + } + + session = nvgpu_kzalloc(g, sizeof(struct nvgpu_clk_session)); + if (!session) + return -ENOMEM; + session->g = g; + + nvgpu_ref_init(&session->refcount); + + session->zombie = false; + session->target_pool[0].pstate = CTRL_PERF_PSTATE_P8; + /* make sure that the initialization of the pool is visible + * before the update + */ + nvgpu_smp_wmb(); + session->target = &session->target_pool[0]; + + nvgpu_init_list_node(&session->targets); + nvgpu_spinlock_init(&session->session_lock); + + nvgpu_spinlock_acquire(&arb->sessions_lock); + nvgpu_list_add_tail(&session->link, &arb->sessions); + nvgpu_spinlock_release(&arb->sessions_lock); + + *_session = session; + + return 0; +} + +void nvgpu_clk_arb_free_fd(struct nvgpu_ref *refcount) +{ + struct nvgpu_clk_dev *dev = container_of(refcount, + struct nvgpu_clk_dev, refcount); + struct nvgpu_clk_session *session = dev->session; + struct gk20a *g = session->g; + + nvgpu_clk_notification_queue_free(g, &dev->queue); + + nvgpu_atomic_dec(&g->clk_arb_global_nr); + nvgpu_kfree(g, dev); +} + +void nvgpu_clk_arb_free_session(struct nvgpu_ref *refcount) +{ + struct nvgpu_clk_session *session = container_of(refcount, + struct nvgpu_clk_session, refcount); + struct nvgpu_clk_arb *arb = session->g->clk_arb; + struct gk20a *g = session->g; + struct nvgpu_clk_dev *dev, *tmp; + + clk_arb_dbg(g, " "); + + if (arb) { + nvgpu_spinlock_acquire(&arb->sessions_lock); + nvgpu_list_del(&session->link); + nvgpu_spinlock_release(&arb->sessions_lock); + } + + nvgpu_spinlock_acquire(&session->session_lock); + nvgpu_list_for_each_entry_safe(dev, tmp, &session->targets, + nvgpu_clk_dev, node) { + nvgpu_ref_put(&dev->refcount, nvgpu_clk_arb_free_fd); + nvgpu_list_del(&dev->node); + } + nvgpu_spinlock_release(&session->session_lock); + + nvgpu_kfree(g, session); +} + +void nvgpu_clk_arb_release_session(struct gk20a *g, + struct nvgpu_clk_session *session) +{ + struct nvgpu_clk_arb *arb = g->clk_arb; + + clk_arb_dbg(g, " "); + + session->zombie = true; + nvgpu_ref_put(&session->refcount, nvgpu_clk_arb_free_session); + if (arb) + nvgpu_clk_arb_worker_enqueue(g, &arb->update_arb_work_item); +} + +void nvgpu_clk_arb_schedule_vf_table_update(struct gk20a *g) +{ + struct nvgpu_clk_arb *arb = g->clk_arb; + + nvgpu_clk_arb_worker_enqueue(g, &arb->update_vf_table_work_item); +} + +/* This function is inherently unsafe to call while arbiter is running + * arbiter must be blocked before calling this function + */ +int nvgpu_clk_arb_get_current_pstate(struct gk20a *g) +{ + return NV_ACCESS_ONCE(g->clk_arb->actual->pstate); +} + +void nvgpu_clk_arb_pstate_change_lock(struct gk20a *g, bool lock) +{ + struct nvgpu_clk_arb *arb = g->clk_arb; + + if (lock) + nvgpu_mutex_acquire(&arb->pstate_lock); + else + nvgpu_mutex_release(&arb->pstate_lock); +} + +bool nvgpu_clk_arb_is_valid_domain(struct gk20a *g, u32 api_domain) +{ + u32 clk_domains = g->ops.clk_arb.get_arbiter_clk_domains(g); + + switch (api_domain) { + case NVGPU_CLK_DOMAIN_MCLK: + return (clk_domains & CTRL_CLK_DOMAIN_MCLK) != 0; + + case NVGPU_CLK_DOMAIN_GPCCLK: + return (clk_domains & CTRL_CLK_DOMAIN_GPC2CLK) != 0; + + default: + return false; + } +} + +int nvgpu_clk_arb_get_arbiter_clk_range(struct gk20a *g, u32 api_domain, + u16 *min_mhz, u16 *max_mhz) +{ + int ret; + + switch (api_domain) { + case NVGPU_CLK_DOMAIN_MCLK: + ret = g->ops.clk_arb.get_arbiter_clk_range(g, + CTRL_CLK_DOMAIN_MCLK, min_mhz, max_mhz); + return ret; + + case NVGPU_CLK_DOMAIN_GPCCLK: + ret = g->ops.clk_arb.get_arbiter_clk_range(g, + CTRL_CLK_DOMAIN_GPC2CLK, min_mhz, max_mhz); + if (!ret) { + *min_mhz /= 2; + *max_mhz /= 2; + } + return ret; + + default: + return -EINVAL; + } +} + +int nvgpu_clk_arb_get_arbiter_clk_f_points(struct gk20a *g, + u32 api_domain, u32 *max_points, u16 *fpoints) +{ + int err; + u32 i; + + switch (api_domain) { + case NVGPU_CLK_DOMAIN_GPCCLK: + err = g->ops.clk_arb.get_arbiter_f_points(g, + CTRL_CLK_DOMAIN_GPC2CLK, max_points, fpoints); + if (err || !fpoints) + return err; + for (i = 0; i < *max_points; i++) + fpoints[i] /= 2; + return 0; + case NVGPU_CLK_DOMAIN_MCLK: + return g->ops.clk_arb.get_arbiter_f_points(g, + CTRL_CLK_DOMAIN_MCLK, max_points, fpoints); + default: + return -EINVAL; + } +} + +int nvgpu_clk_arb_get_session_target_mhz(struct nvgpu_clk_session *session, + u32 api_domain, u16 *freq_mhz) +{ + int err = 0; + struct nvgpu_clk_arb_target *target = session->target; + + if (!nvgpu_clk_arb_is_valid_domain(session->g, api_domain)) { + return -EINVAL; + } + + switch (api_domain) { + case NVGPU_CLK_DOMAIN_MCLK: + *freq_mhz = target->mclk; + break; + + case NVGPU_CLK_DOMAIN_GPCCLK: + *freq_mhz = target->gpc2clk / 2ULL; + break; + + default: + *freq_mhz = 0; + err = -EINVAL; + } + return err; +} + +int nvgpu_clk_arb_get_arbiter_actual_mhz(struct gk20a *g, + u32 api_domain, u16 *freq_mhz) +{ + struct nvgpu_clk_arb *arb = g->clk_arb; + int err = 0; + struct nvgpu_clk_arb_target *actual = arb->actual; + + if (!nvgpu_clk_arb_is_valid_domain(g, api_domain)) { + return -EINVAL; + } + + switch (api_domain) { + case NVGPU_CLK_DOMAIN_MCLK: + *freq_mhz = actual->mclk; + break; + + case NVGPU_CLK_DOMAIN_GPCCLK: + *freq_mhz = actual->gpc2clk / 2ULL; + break; + + default: + *freq_mhz = 0; + err = -EINVAL; + } + return err; +} + +unsigned long nvgpu_clk_measure_freq(struct gk20a *g, u32 api_domain) +{ + unsigned long freq = 0UL; + + switch (api_domain) { + case CTRL_CLK_DOMAIN_GPC2CLK: + freq = g->ops.clk.get_rate(g, CTRL_CLK_DOMAIN_GPCCLK) * 2UL; + break; + default: + break; + } + return freq; +} + +int nvgpu_clk_arb_get_arbiter_effective_mhz(struct gk20a *g, + u32 api_domain, u16 *freq_mhz) +{ + if (!nvgpu_clk_arb_is_valid_domain(g, api_domain)) { + return -EINVAL; + } + + switch (api_domain) { + case NVGPU_CLK_DOMAIN_MCLK: + *freq_mhz = g->ops.clk.measure_freq(g, CTRL_CLK_DOMAIN_MCLK) / + 1000000ULL; + return 0; + + case NVGPU_CLK_DOMAIN_GPCCLK: + *freq_mhz = g->ops.clk.measure_freq(g, + CTRL_CLK_DOMAIN_GPC2CLK) / 2000000ULL; + return 0; + + default: + return -EINVAL; + } +} diff --git a/include/clk/clk_domain.c b/include/clk/clk_domain.c new file mode 100644 index 0000000..3b64f51 --- /dev/null +++ b/include/clk/clk_domain.c @@ -0,0 +1,1666 @@ +/* + * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "clk.h" +#include "clk_fll.h" +#include "clk_domain.h" +#include "boardobj/boardobjgrp.h" +#include "boardobj/boardobjgrp_e32.h" +#include "ctrl/ctrlclk.h" +#include "ctrl/ctrlvolt.h" + +static struct clk_domain *construct_clk_domain(struct gk20a *g, void *pargs); + +static int devinit_get_clocks_table(struct gk20a *g, + struct clk_domains *pdomainobjs); + +static int clk_domain_pmudatainit_super(struct gk20a *g, struct boardobj + *board_obj_ptr, struct nv_pmu_boardobj *ppmudata); + +static struct vbios_clocks_table_1x_hal_clock_entry + vbiosclktbl1xhalentry_gp[] = { + { clkwhich_gpc2clk, true, 1, }, + { clkwhich_xbar2clk, true, 1, }, + { clkwhich_mclk, false, 1, }, + { clkwhich_sys2clk, true, 1, }, + { clkwhich_hub2clk, false, 1, }, + { clkwhich_nvdclk, false, 1, }, + { clkwhich_pwrclk, false, 1, }, + { clkwhich_dispclk, false, 1, }, + { clkwhich_pciegenclk, false, 1, } +}; +/* + * Updated from RM devinit_clock.c + * GV100 is 0x03 and + * GP10x is 0x02 in clocks_hal. + */ +static struct vbios_clocks_table_1x_hal_clock_entry + vbiosclktbl1xhalentry_gv[] = { + { clkwhich_gpcclk, true, 2, }, + { clkwhich_xbarclk, true, 1, }, + { clkwhich_mclk, false, 1, }, + { clkwhich_sysclk, true, 1, }, + { clkwhich_hubclk, false, 1, }, + { clkwhich_nvdclk, true, 1, }, + { clkwhich_pwrclk, false, 1, }, + { clkwhich_dispclk, false, 1, }, + { clkwhich_pciegenclk, false, 1, }, + { clkwhich_hostclk, true, 1, } +}; + +static u32 clktranslatehalmumsettoapinumset(u32 clkhaldomains) +{ + u32 clkapidomains = 0; + + if (clkhaldomains & BIT(clkwhich_gpcclk)) { + clkapidomains |= CTRL_CLK_DOMAIN_GPCCLK; + } + if (clkhaldomains & BIT(clkwhich_xbarclk)) { + clkapidomains |= CTRL_CLK_DOMAIN_XBARCLK; + } + if (clkhaldomains & BIT(clkwhich_sysclk)) { + clkapidomains |= CTRL_CLK_DOMAIN_SYSCLK; + } + if (clkhaldomains & BIT(clkwhich_hubclk)) { + clkapidomains |= CTRL_CLK_DOMAIN_HUBCLK; + } + if (clkhaldomains & BIT(clkwhich_hostclk)) { + clkapidomains |= CTRL_CLK_DOMAIN_HOSTCLK; + } + if (clkhaldomains & BIT(clkwhich_gpc2clk)) { + clkapidomains |= CTRL_CLK_DOMAIN_GPC2CLK; + } + if (clkhaldomains & BIT(clkwhich_xbar2clk)) { + clkapidomains |= CTRL_CLK_DOMAIN_XBAR2CLK; + } + if (clkhaldomains & BIT(clkwhich_sys2clk)) { + clkapidomains |= CTRL_CLK_DOMAIN_SYS2CLK; + } + if (clkhaldomains & BIT(clkwhich_hub2clk)) { + clkapidomains |= CTRL_CLK_DOMAIN_HUB2CLK; + } + if (clkhaldomains & BIT(clkwhich_pwrclk)) { + clkapidomains |= CTRL_CLK_DOMAIN_PWRCLK; + } + if (clkhaldomains & BIT(clkwhich_pciegenclk)) { + clkapidomains |= CTRL_CLK_DOMAIN_PCIEGENCLK; + } + if (clkhaldomains & BIT(clkwhich_mclk)) { + clkapidomains |= CTRL_CLK_DOMAIN_MCLK; + } + if (clkhaldomains & BIT(clkwhich_nvdclk)) { + clkapidomains |= CTRL_CLK_DOMAIN_NVDCLK; + } + if (clkhaldomains & BIT(clkwhich_dispclk)) { + clkapidomains |= CTRL_CLK_DOMAIN_DISPCLK; + } + + return clkapidomains; +} + +static int _clk_domains_pmudatainit_3x(struct gk20a *g, + struct boardobjgrp *pboardobjgrp, + struct nv_pmu_boardobjgrp_super *pboardobjgrppmu) +{ + struct nv_pmu_clk_clk_domain_boardobjgrp_set_header *pset = + (struct nv_pmu_clk_clk_domain_boardobjgrp_set_header *) + pboardobjgrppmu; + struct clk_domains *pdomains = (struct clk_domains *)pboardobjgrp; + int status = 0; + + status = boardobjgrp_pmudatainit_e32(g, pboardobjgrp, pboardobjgrppmu); + if (status) { + nvgpu_err(g, + "error updating pmu boardobjgrp for clk domain 0x%x", + status); + goto done; + } + + pset->vbios_domains = pdomains->vbios_domains; + pset->cntr_sampling_periodms = pdomains->cntr_sampling_periodms; + pset->version = CLK_DOMAIN_BOARDOBJGRP_VERSION; + pset->b_override_o_v_o_c = false; + pset->b_debug_mode = false; + pset->b_enforce_vf_monotonicity = pdomains->b_enforce_vf_monotonicity; + pset->b_enforce_vf_smoothening = pdomains->b_enforce_vf_smoothening; + if (g->ops.clk.split_rail_support) { + pset->volt_rails_max = 2; + } else { + pset->volt_rails_max = 1; + } + status = boardobjgrpmask_export( + &pdomains->master_domains_mask.super, + pdomains->master_domains_mask.super.bitcount, + &pset->master_domains_mask.super); + + memcpy(&pset->deltas, &pdomains->deltas, + (sizeof(struct ctrl_clk_clk_delta))); + +done: + return status; +} + +static int _clk_domains_pmudata_instget(struct gk20a *g, + struct nv_pmu_boardobjgrp *pmuboardobjgrp, + struct nv_pmu_boardobj **ppboardobjpmudata, + u8 idx) +{ + struct nv_pmu_clk_clk_domain_boardobj_grp_set *pgrp_set = + (struct nv_pmu_clk_clk_domain_boardobj_grp_set *) + pmuboardobjgrp; + + nvgpu_log_info(g, " "); + + /*check whether pmuboardobjgrp has a valid boardobj in index*/ + if (((u32)BIT(idx) & + pgrp_set->hdr.data.super.obj_mask.super.data[0]) == 0) { + return -EINVAL; + } + + *ppboardobjpmudata = (struct nv_pmu_boardobj *) + &pgrp_set->objects[idx].data.board_obj; + nvgpu_log_info(g, " Done"); + return 0; +} + +int clk_domain_sw_setup(struct gk20a *g) +{ + int status; + struct boardobjgrp *pboardobjgrp = NULL; + struct clk_domains *pclkdomainobjs; + struct clk_domain *pdomain; + struct clk_domain_3x_master *pdomain_master; + struct clk_domain_3x_slave *pdomain_slave; + u8 i; + + nvgpu_log_info(g, " "); + + status = boardobjgrpconstruct_e32(g, &g->clk_pmu.clk_domainobjs.super); + if (status) { + nvgpu_err(g, + "error creating boardobjgrp for clk domain, status - 0x%x", + status); + goto done; + } + + pboardobjgrp = &g->clk_pmu.clk_domainobjs.super.super; + pclkdomainobjs = &(g->clk_pmu.clk_domainobjs); + + BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, CLK, CLK_DOMAIN); + + status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, + clk, CLK, clk_domain, CLK_DOMAIN); + if (status) { + nvgpu_err(g, + "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", + status); + goto done; + } + + pboardobjgrp->pmudatainit = _clk_domains_pmudatainit_3x; + pboardobjgrp->pmudatainstget = _clk_domains_pmudata_instget; + + /* Initialize mask to zero.*/ + boardobjgrpmask_e32_init(&pclkdomainobjs->prog_domains_mask, NULL); + boardobjgrpmask_e32_init(&pclkdomainobjs->master_domains_mask, NULL); + pclkdomainobjs->b_enforce_vf_monotonicity = true; + pclkdomainobjs->b_enforce_vf_smoothening = true; + + memset(&pclkdomainobjs->ordered_noise_aware_list, 0, + sizeof(pclkdomainobjs->ordered_noise_aware_list)); + + memset(&pclkdomainobjs->ordered_noise_unaware_list, 0, + sizeof(pclkdomainobjs->ordered_noise_unaware_list)); + + memset(&pclkdomainobjs->deltas, 0, + sizeof(struct ctrl_clk_clk_delta)); + + status = devinit_get_clocks_table(g, pclkdomainobjs); + if (status) { + goto done; + } + + BOARDOBJGRP_FOR_EACH(&(pclkdomainobjs->super.super), + struct clk_domain *, pdomain, i) { + pdomain_master = NULL; + if (pdomain->super.implements(g, &pdomain->super, + CTRL_CLK_CLK_DOMAIN_TYPE_3X_PROG)) { + status = boardobjgrpmask_bitset( + &pclkdomainobjs->prog_domains_mask.super, i); + if (status) { + goto done; + } + } + + if (pdomain->super.implements(g, &pdomain->super, + CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER)) { + status = boardobjgrpmask_bitset( + &pclkdomainobjs->master_domains_mask.super, i); + if (status) { + goto done; + } + } + + if (pdomain->super.implements(g, &pdomain->super, + CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE)) { + pdomain_slave = + (struct clk_domain_3x_slave *)pdomain; + pdomain_master = + (struct clk_domain_3x_master *) + (CLK_CLK_DOMAIN_GET((&g->clk_pmu), + pdomain_slave->master_idx)); + pdomain_master->slave_idxs_mask |= BIT(i); + } + + } + +done: + nvgpu_log_info(g, " done status %x", status); + return status; +} + +int clk_domain_pmu_setup(struct gk20a *g) +{ + int status; + struct boardobjgrp *pboardobjgrp = NULL; + + nvgpu_log_info(g, " "); + + pboardobjgrp = &g->clk_pmu.clk_domainobjs.super.super; + + if (!pboardobjgrp->bconstructed) { + return -EINVAL; + } + + status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); + + nvgpu_log_info(g, "Done"); + return status; +} + +static int devinit_get_clocks_table_35(struct gk20a *g, + struct clk_domains *pclkdomainobjs, u8 *clocks_table_ptr) +{ + int status = 0; + struct vbios_clocks_table_35_header clocks_table_header = { 0 }; + struct vbios_clocks_table_35_entry clocks_table_entry = { 0 }; + struct vbios_clocks_table_1x_hal_clock_entry *vbiosclktbl1xhalentry; + u8 *clocks_tbl_entry_ptr = NULL; + u32 index = 0; + struct clk_domain *pclkdomain_dev; + union { + struct boardobj boardobj; + struct clk_domain clk_domain; + struct clk_domain_3x v3x; + struct clk_domain_3x_fixed v3x_fixed; + struct clk_domain_35_prog v35_prog; + struct clk_domain_35_master v35_master; + struct clk_domain_35_slave v35_slave; + } clk_domain_data; + + nvgpu_log_info(g, " "); + + memcpy(&clocks_table_header, clocks_table_ptr, + VBIOS_CLOCKS_TABLE_35_HEADER_SIZE_09); + if (clocks_table_header.header_size < + (u8) VBIOS_CLOCKS_TABLE_35_HEADER_SIZE_09) { + status = -EINVAL; + goto done; + } + + if (clocks_table_header.entry_size < + (u8) VBIOS_CLOCKS_TABLE_35_ENTRY_SIZE_11) { + status = -EINVAL; + goto done; + } + + switch (clocks_table_header.clocks_hal) { + case CLK_TABLE_HAL_ENTRY_GP: + { + vbiosclktbl1xhalentry = vbiosclktbl1xhalentry_gp; + break; + } + case CLK_TABLE_HAL_ENTRY_GV: + { + vbiosclktbl1xhalentry = vbiosclktbl1xhalentry_gv; + break; + } + default: + { + status = -EINVAL; + goto done; + } + } + + pclkdomainobjs->cntr_sampling_periodms = + (u16)clocks_table_header.cntr_sampling_periodms; + + /* Read table entries*/ + clocks_tbl_entry_ptr = clocks_table_ptr + + clocks_table_header.header_size; + for (index = 0; index < clocks_table_header.entry_count; index++) { + memcpy((void*) &clocks_table_entry, (void*) clocks_tbl_entry_ptr, + clocks_table_header.entry_size); + clk_domain_data.clk_domain.domain = + (u8) vbiosclktbl1xhalentry[index].domain; + clk_domain_data.clk_domain.api_domain = + clktranslatehalmumsettoapinumset( + (u32) BIT(clk_domain_data.clk_domain.domain)); + clk_domain_data.v3x.b_noise_aware_capable = + vbiosclktbl1xhalentry[index].b_noise_aware_capable; + + switch (BIOS_GET_FIELD(clocks_table_entry.flags0, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE)) { + case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_FIXED: + { + clk_domain_data.boardobj.type = + CTRL_CLK_CLK_DOMAIN_TYPE_3X_FIXED; + clk_domain_data.v3x_fixed.freq_mhz = (u16)BIOS_GET_FIELD( + clocks_table_entry.param1, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_FIXED_FREQUENCY_MHZ); + break; + } + + case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_MASTER: + { + clk_domain_data.boardobj.type = + CTRL_CLK_CLK_DOMAIN_TYPE_35_MASTER; + clk_domain_data.v35_prog.super.clk_prog_idx_first = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_FIRST)); + clk_domain_data.v35_prog.super.clk_prog_idx_last = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_LAST)); + clk_domain_data.v35_prog.super.noise_unaware_ordering_index = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_UNAWARE_ORDERING_IDX)); + if (clk_domain_data.v3x.b_noise_aware_capable) { + clk_domain_data.v35_prog.super.b_force_noise_unaware_ordering = + (bool)(BIOS_GET_FIELD(clocks_table_entry.param2, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_FORCE_NOISE_UNAWARE_ORDERING)); + + } else { + clk_domain_data.v35_prog.super.noise_aware_ordering_index = + CTRL_CLK_CLK_DOMAIN_3X_PROG_ORDERING_INDEX_INVALID; + clk_domain_data.v35_prog.super.b_force_noise_unaware_ordering = false; + } + clk_domain_data.v35_prog.pre_volt_ordering_index = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, + NV_VBIOS_CLOCKS_TABLE_35_ENTRY_PARAM2_PROG_PRE_VOLT_ORDERING_IDX)); + + clk_domain_data.v35_prog.post_volt_ordering_index = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, + NV_VBIOS_CLOCKS_TABLE_35_ENTRY_PARAM2_PROG_POST_VOLT_ORDERING_IDX)); + + clk_domain_data.v35_prog.super.factory_delta.data.delta_khz = 0; + clk_domain_data.v35_prog.super.factory_delta.type = 0; + + clk_domain_data.v35_prog.super.freq_delta_min_mhz = + (u16)(BIOS_GET_FIELD(clocks_table_entry.param1, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_MASTER_FREQ_OC_DELTA_MIN_MHZ)); + + clk_domain_data.v35_prog.super.freq_delta_max_mhz = + (u16)(BIOS_GET_FIELD(clocks_table_entry.param1, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_MASTER_FREQ_OC_DELTA_MAX_MHZ)); + clk_domain_data.v35_prog.clk_vf_curve_count = + vbiosclktbl1xhalentry[index].clk_vf_curve_count; + break; + } + + case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_SLAVE: + { + clk_domain_data.boardobj.type = + CTRL_CLK_CLK_DOMAIN_TYPE_35_SLAVE; + clk_domain_data.v35_prog.super.clk_prog_idx_first = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_FIRST)); + clk_domain_data.v35_prog.super.clk_prog_idx_last = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_LAST)); + clk_domain_data.v35_prog.super.noise_unaware_ordering_index = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_UNAWARE_ORDERING_IDX)); + + if (clk_domain_data.v3x.b_noise_aware_capable) { + clk_domain_data.v35_prog.super.b_force_noise_unaware_ordering = + (bool)(BIOS_GET_FIELD(clocks_table_entry.param2, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_FORCE_NOISE_UNAWARE_ORDERING)); + + } else { + clk_domain_data.v35_prog.super.noise_aware_ordering_index = + CTRL_CLK_CLK_DOMAIN_3X_PROG_ORDERING_INDEX_INVALID; + clk_domain_data.v35_prog.super.b_force_noise_unaware_ordering = false; + } + clk_domain_data.v35_prog.pre_volt_ordering_index = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, + NV_VBIOS_CLOCKS_TABLE_35_ENTRY_PARAM2_PROG_PRE_VOLT_ORDERING_IDX)); + + clk_domain_data.v35_prog.post_volt_ordering_index = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, + NV_VBIOS_CLOCKS_TABLE_35_ENTRY_PARAM2_PROG_POST_VOLT_ORDERING_IDX)); + + clk_domain_data.v35_prog.super.factory_delta.data.delta_khz = 0; + clk_domain_data.v35_prog.super.factory_delta.type = 0; + clk_domain_data.v35_prog.super.freq_delta_min_mhz = 0; + clk_domain_data.v35_prog.super.freq_delta_max_mhz = 0; + clk_domain_data.v35_slave.slave.master_idx = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param1, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_SLAVE_MASTER_DOMAIN)); + break; + } + + default: + { + nvgpu_err(g, + "error reading clock domain entry %d", index); + status = -EINVAL; + goto done; + } + + } + pclkdomain_dev = construct_clk_domain(g, + (void *)&clk_domain_data); + if (pclkdomain_dev == NULL) { + nvgpu_err(g, + "unable to construct clock domain boardobj for %d", + index); + status = -EINVAL; + goto done; + } + status = boardobjgrp_objinsert( + &pclkdomainobjs->super.super, + (struct boardobj *)(void*) pclkdomain_dev, index); + if (status != 0UL) { + nvgpu_err(g, + "unable to insert clock domain boardobj for %d", index); + status = (u32) -EINVAL; + goto done; + } + clocks_tbl_entry_ptr += clocks_table_header.entry_size; + } + +done: + nvgpu_log_info(g, " done status %x", status); + return status; +} + +static int devinit_get_clocks_table_1x(struct gk20a *g, + struct clk_domains *pclkdomainobjs, u8 *clocks_table_ptr) +{ + int status = 0; + struct vbios_clocks_table_1x_header clocks_table_header = { 0 }; + struct vbios_clocks_table_1x_entry clocks_table_entry = { 0 }; + struct vbios_clocks_table_1x_hal_clock_entry *vbiosclktbl1xhalentry; + u8 *clocks_tbl_entry_ptr = NULL; + u32 index = 0; + struct clk_domain *pclkdomain_dev; + union { + struct boardobj boardobj; + struct clk_domain clk_domain; + struct clk_domain_3x v3x; + struct clk_domain_3x_fixed v3x_fixed; + struct clk_domain_3x_prog v3x_prog; + struct clk_domain_3x_master v3x_master; + struct clk_domain_3x_slave v3x_slave; + } clk_domain_data; + + nvgpu_log_info(g, " "); + + memcpy(&clocks_table_header, clocks_table_ptr, + VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07); + if (clocks_table_header.header_size < + (u8) VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07) { + status = -EINVAL; + goto done; + } + + if (clocks_table_header.entry_size < + (u8) VBIOS_CLOCKS_TABLE_1X_ENTRY_SIZE_09) { + status = -EINVAL; + goto done; + } + + switch (clocks_table_header.clocks_hal) { + case CLK_TABLE_HAL_ENTRY_GP: + { + vbiosclktbl1xhalentry = vbiosclktbl1xhalentry_gp; + break; + } + case CLK_TABLE_HAL_ENTRY_GV: + { + vbiosclktbl1xhalentry = vbiosclktbl1xhalentry_gv; + break; + } + default: + { + status = -EINVAL; + goto done; + } + } + + pclkdomainobjs->cntr_sampling_periodms = + (u16)clocks_table_header.cntr_sampling_periodms; + + /* Read table entries*/ + clocks_tbl_entry_ptr = clocks_table_ptr + + VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07; + for (index = 0; index < clocks_table_header.entry_count; index++) { + memcpy((void*) &clocks_table_entry, (void*) clocks_tbl_entry_ptr, + clocks_table_header.entry_size); + clk_domain_data.clk_domain.domain = + (u8) vbiosclktbl1xhalentry[index].domain; + clk_domain_data.clk_domain.api_domain = + clktranslatehalmumsettoapinumset( + BIT(clk_domain_data.clk_domain.domain)); + clk_domain_data.v3x.b_noise_aware_capable = + vbiosclktbl1xhalentry[index].b_noise_aware_capable; + + switch (BIOS_GET_FIELD(clocks_table_entry.flags0, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE)) { + case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_FIXED: + { + clk_domain_data.boardobj.type = + CTRL_CLK_CLK_DOMAIN_TYPE_3X_FIXED; + clk_domain_data.v3x_fixed.freq_mhz = (u16)BIOS_GET_FIELD( + clocks_table_entry.param1, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_FIXED_FREQUENCY_MHZ); + break; + } + + case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_MASTER: + { + clk_domain_data.boardobj.type = + CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER; + clk_domain_data.v3x_prog.clk_prog_idx_first = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_FIRST)); + clk_domain_data.v3x_prog.clk_prog_idx_last = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_LAST)); + clk_domain_data.v3x_prog.noise_unaware_ordering_index = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_UNAWARE_ORDERING_IDX)); + if (clk_domain_data.v3x.b_noise_aware_capable) { + clk_domain_data.v3x_prog.noise_aware_ordering_index = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_AWARE_ORDERING_IDX)); + clk_domain_data.v3x_prog.b_force_noise_unaware_ordering = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_FORCE_NOISE_UNAWARE_ORDERING)); + } else { + clk_domain_data.v3x_prog.noise_aware_ordering_index = + CTRL_CLK_CLK_DOMAIN_3X_PROG_ORDERING_INDEX_INVALID; + clk_domain_data.v3x_prog.b_force_noise_unaware_ordering = false; + } + + clk_domain_data.v3x_prog.factory_delta.data.delta_khz = 0; + clk_domain_data.v3x_prog.factory_delta.type = 0; + + clk_domain_data.v3x_prog.freq_delta_min_mhz = + (u16)(BIOS_GET_FIELD(clocks_table_entry.param1, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_MASTER_FREQ_OC_DELTA_MIN_MHZ)); + + clk_domain_data.v3x_prog.freq_delta_max_mhz = + (u16)(BIOS_GET_FIELD(clocks_table_entry.param1, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_MASTER_FREQ_OC_DELTA_MAX_MHZ)); + break; + } + + case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_SLAVE: + { + clk_domain_data.boardobj.type = + CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE; + clk_domain_data.v3x_prog.clk_prog_idx_first = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_FIRST)); + clk_domain_data.v3x_prog.clk_prog_idx_last = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_LAST)); + clk_domain_data.v3x_prog.noise_unaware_ordering_index = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_UNAWARE_ORDERING_IDX)); + + if (clk_domain_data.v3x.b_noise_aware_capable) { + clk_domain_data.v3x_prog.noise_aware_ordering_index = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_AWARE_ORDERING_IDX)); + clk_domain_data.v3x_prog.b_force_noise_unaware_ordering = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_FORCE_NOISE_UNAWARE_ORDERING)); + } else { + clk_domain_data.v3x_prog.noise_aware_ordering_index = + CTRL_CLK_CLK_DOMAIN_3X_PROG_ORDERING_INDEX_INVALID; + clk_domain_data.v3x_prog.b_force_noise_unaware_ordering = false; + } + clk_domain_data.v3x_prog.factory_delta.data.delta_khz = 0; + clk_domain_data.v3x_prog.factory_delta.type = 0; + clk_domain_data.v3x_prog.freq_delta_min_mhz = 0; + clk_domain_data.v3x_prog.freq_delta_max_mhz = 0; + clk_domain_data.v3x_slave.master_idx = + (u8)(BIOS_GET_FIELD(clocks_table_entry.param1, + NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_SLAVE_MASTER_DOMAIN)); + break; + } + + default: + { + nvgpu_err(g, + "error reading clock domain entry %d", index); + status = (u32) -EINVAL; + goto done; + } + + } + pclkdomain_dev = construct_clk_domain(g, + (void *)&clk_domain_data); + if (pclkdomain_dev == NULL) { + nvgpu_err(g, + "unable to construct clock domain boardobj for %d", + index); + status = (u32) -EINVAL; + goto done; + } + status = boardobjgrp_objinsert(&pclkdomainobjs->super.super, + (struct boardobj *)(void *)pclkdomain_dev, index); + if (status != 0UL) { + nvgpu_err(g, + "unable to insert clock domain boardobj for %d", index); + status = (u32) -EINVAL; + goto done; + } + clocks_tbl_entry_ptr += clocks_table_header.entry_size; + } + +done: + nvgpu_log_info(g, " done status %x", status); + return status; +} + +static int devinit_get_clocks_table(struct gk20a *g, + struct clk_domains *pclkdomainobjs) +{ + int status = 0; + u8 *clocks_table_ptr = NULL; + struct vbios_clocks_table_1x_header clocks_table_header = { 0 }; + nvgpu_log_info(g, " "); + + clocks_table_ptr = (u8 *)nvgpu_bios_get_perf_table_ptrs(g, + g->bios.clock_token, CLOCKS_TABLE); + if (clocks_table_ptr == NULL) { + status = -EINVAL; + goto done; + } + memcpy(&clocks_table_header, clocks_table_ptr, + VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07); + if (clocks_table_header.version == 0x35U) { + devinit_get_clocks_table_35(g, pclkdomainobjs, clocks_table_ptr); + } + else { + devinit_get_clocks_table_1x(g, pclkdomainobjs, clocks_table_ptr); + } + done: + return status; + +} + +static int clkdomainclkproglink_not_supported(struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_domain *pdomain) +{ + nvgpu_log_info(g, " "); + return -EINVAL; +} + +static int clkdomainvfsearch_stub( + struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_domain *pdomain, + u16 *clkmhz, + u32 *voltuv, + u8 rail) + +{ + nvgpu_log_info(g, " "); + return -EINVAL; +} + +static u32 clkdomaingetfpoints_stub( + struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_domain *pdomain, + u32 *pfpointscount, + u16 *pfreqpointsinmhz, + u8 rail) +{ + nvgpu_log_info(g, " "); + return -EINVAL; +} + + +static int clk_domain_construct_super(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct clk_domain *pdomain; + struct clk_domain *ptmpdomain = (struct clk_domain *)pargs; + int status = 0; + + status = boardobj_construct_super(g, ppboardobj, + size, pargs); + + if (status) { + return -EINVAL; + } + + pdomain = (struct clk_domain *)*ppboardobj; + + pdomain->super.pmudatainit = + clk_domain_pmudatainit_super; + + pdomain->clkdomainclkproglink = + clkdomainclkproglink_not_supported; + + pdomain->clkdomainclkvfsearch = + clkdomainvfsearch_stub; + + pdomain->clkdomainclkgetfpoints = + clkdomaingetfpoints_stub; + + pdomain->api_domain = ptmpdomain->api_domain; + pdomain->domain = ptmpdomain->domain; + pdomain->perf_domain_grp_idx = + ptmpdomain->perf_domain_grp_idx; + + return status; +} + +static int _clk_domain_pmudatainit_3x(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_domain_3x *pclk_domain_3x; + struct nv_pmu_clk_clk_domain_3x_boardobj_set *pset; + + nvgpu_log_info(g, " "); + + status = clk_domain_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pclk_domain_3x = (struct clk_domain_3x *)board_obj_ptr; + + pset = (struct nv_pmu_clk_clk_domain_3x_boardobj_set *)ppmudata; + + pset->b_noise_aware_capable = pclk_domain_3x->b_noise_aware_capable; + + return status; +} + +static int clk_domain_construct_3x(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct clk_domain_3x *pdomain; + struct clk_domain_3x *ptmpdomain = + (struct clk_domain_3x *)pargs; + int status = 0; + + ptmpobj->type_mask = BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X); + status = clk_domain_construct_super(g, ppboardobj, + size, pargs); + if (status) { + return -EINVAL; + } + + pdomain = (struct clk_domain_3x *)*ppboardobj; + + pdomain->super.super.pmudatainit = + _clk_domain_pmudatainit_3x; + + pdomain->b_noise_aware_capable = ptmpdomain->b_noise_aware_capable; + + return status; +} + +static int clkdomainclkproglink_3x_prog(struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_domain *pdomain) +{ + int status = 0; + struct clk_domain_3x_prog *p3xprog = + (struct clk_domain_3x_prog *)pdomain; + struct clk_prog *pprog = NULL; + u8 i; + + nvgpu_log_info(g, " "); + + for (i = p3xprog->clk_prog_idx_first; + i <= p3xprog->clk_prog_idx_last; + i++) { + pprog = CLK_CLK_PROG_GET(pclk, i); + if (pprog == NULL) { + status = -EINVAL; + } + } + return status; +} + +static int clkdomaingetslaveclk(struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_domain *pdomain, + u16 *pclkmhz, + u16 masterclkmhz) +{ + int status = 0; + struct clk_prog *pprog = NULL; + struct clk_prog_1x_master *pprog1xmaster = NULL; + u8 slaveidx; + struct clk_domain_3x_master *p3xmaster; + + nvgpu_log_info(g, " "); + + if (pclkmhz == NULL) { + return -EINVAL; + } + + if (masterclkmhz == 0) { + return -EINVAL; + } + + slaveidx = BOARDOBJ_GET_IDX(pdomain); + p3xmaster = (struct clk_domain_3x_master *) + CLK_CLK_DOMAIN_GET(pclk, + ((struct clk_domain_3x_slave *) + pdomain)->master_idx); + pprog = CLK_CLK_PROG_GET(pclk, p3xmaster->super.clk_prog_idx_first); + pprog1xmaster = (struct clk_prog_1x_master *)pprog; + + status = pprog1xmaster->getslaveclk(g, pclk, pprog1xmaster, + slaveidx, pclkmhz, masterclkmhz); + return status; +} + +static int clkdomainvfsearch(struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_domain *pdomain, + u16 *pclkmhz, + u32 *pvoltuv, + u8 rail) +{ + int status = 0; + struct clk_domain_3x_master *p3xmaster = + (struct clk_domain_3x_master *)pdomain; + struct clk_prog *pprog = NULL; + struct clk_prog_1x_master *pprog1xmaster = NULL; + u8 i; + u8 *pslaveidx = NULL; + u8 slaveidx; + u16 clkmhz; + u32 voltuv; + u16 bestclkmhz; + u32 bestvoltuv; + + nvgpu_log_info(g, " "); + + if ((pclkmhz == NULL) || (pvoltuv == NULL)) { + return -EINVAL; + } + + if ((*pclkmhz != 0) && (*pvoltuv != 0)) { + return -EINVAL; + } + + bestclkmhz = *pclkmhz; + bestvoltuv = *pvoltuv; + + if (pdomain->super.implements(g, &pdomain->super, + CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE)) { + slaveidx = BOARDOBJ_GET_IDX(pdomain); + pslaveidx = &slaveidx; + p3xmaster = (struct clk_domain_3x_master *) + CLK_CLK_DOMAIN_GET(pclk, + ((struct clk_domain_3x_slave *) + pdomain)->master_idx); + } + /* Iterate over the set of CLK_PROGs pointed at by this domain.*/ + for (i = p3xmaster->super.clk_prog_idx_first; + i <= p3xmaster->super.clk_prog_idx_last; + i++) { + clkmhz = *pclkmhz; + voltuv = *pvoltuv; + pprog = CLK_CLK_PROG_GET(pclk, i); + + /* MASTER CLK_DOMAINs must point to MASTER CLK_PROGs.*/ + if (!pprog->super.implements(g, &pprog->super, + CTRL_CLK_CLK_PROG_TYPE_1X_MASTER)) { + status = -EINVAL; + goto done; + } + + pprog1xmaster = (struct clk_prog_1x_master *)pprog; + status = pprog1xmaster->vflookup(g, pclk, pprog1xmaster, + pslaveidx, &clkmhz, &voltuv, rail); + /* if look up has found the V or F value matching to other + exit */ + if (status == 0) { + if (*pclkmhz == 0) { + bestclkmhz = clkmhz; + } else { + bestvoltuv = voltuv; + break; + } + } + } + /* clk and volt sent as zero to print vf table */ + if ((*pclkmhz == 0) && (*pvoltuv == 0)) { + status = 0; + goto done; + } + /* atleast one search found a matching value? */ + if ((bestvoltuv != 0) && (bestclkmhz != 0)) { + *pclkmhz = bestclkmhz; + *pvoltuv = bestvoltuv; + status = 0; + goto done; + } +done: + nvgpu_log_info(g, "done status %x", status); + return status; +} + +static u32 clkdomaingetfpoints +( + struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_domain *pdomain, + u32 *pfpointscount, + u16 *pfreqpointsinmhz, + u8 rail +) +{ + u32 status = 0; + struct clk_domain_3x_master *p3xmaster = + (struct clk_domain_3x_master *)pdomain; + struct clk_prog *pprog = NULL; + struct clk_prog_1x_master *pprog1xmaster = NULL; + u32 fpointscount = 0; + u32 remainingcount; + u32 totalcount; + u16 *freqpointsdata; + u8 i; + + nvgpu_log_info(g, " "); + + if (pfpointscount == NULL) { + return -EINVAL; + } + + if ((pfreqpointsinmhz == NULL) && (*pfpointscount != 0)) { + return -EINVAL; + } + + if (pdomain->super.implements(g, &pdomain->super, + CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE)) { + return -EINVAL; + } + + freqpointsdata = pfreqpointsinmhz; + totalcount = 0; + fpointscount = *pfpointscount; + remainingcount = fpointscount; + /* Iterate over the set of CLK_PROGs pointed at by this domain.*/ + for (i = p3xmaster->super.clk_prog_idx_first; + i <= p3xmaster->super.clk_prog_idx_last; + i++) { + pprog = CLK_CLK_PROG_GET(pclk, i); + pprog1xmaster = (struct clk_prog_1x_master *)pprog; + status = pprog1xmaster->getfpoints(g, pclk, pprog1xmaster, + &fpointscount, &freqpointsdata, rail); + if (status) { + *pfpointscount = 0; + goto done; + } + totalcount += fpointscount; + if (*pfpointscount) { + remainingcount -= fpointscount; + fpointscount = remainingcount; + } else { + fpointscount = 0; + } + + } + + *pfpointscount = totalcount; +done: + nvgpu_log_info(g, "done status %x", status); + return status; +} + +static int clk_domain_pmudatainit_35_prog(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_domain_35_prog *pclk_domain_35_prog; + struct clk_domain_3x_prog *pclk_domain_3x_prog; + struct nv_pmu_clk_clk_domain_35_prog_boardobj_set *pset; + struct clk_domains *pdomains = &(g->clk_pmu.clk_domainobjs); + + nvgpu_log_info(g, " "); + + status = _clk_domain_pmudatainit_3x(g, board_obj_ptr, ppmudata); + if (status != 0UL) { + return status; + } + + pclk_domain_35_prog = (struct clk_domain_35_prog *)(void*)board_obj_ptr; + pclk_domain_3x_prog = &pclk_domain_35_prog->super; + + pset = (struct nv_pmu_clk_clk_domain_35_prog_boardobj_set *) + (void*) ppmudata; + + pset->super.clk_prog_idx_first = pclk_domain_3x_prog->clk_prog_idx_first; + pset->super.clk_prog_idx_last = pclk_domain_3x_prog->clk_prog_idx_last; + pset->super.b_force_noise_unaware_ordering = + pclk_domain_3x_prog->b_force_noise_unaware_ordering; + pset->super.factory_delta = pclk_domain_3x_prog->factory_delta; + pset->super.freq_delta_min_mhz = pclk_domain_3x_prog->freq_delta_min_mhz; + pset->super.freq_delta_max_mhz = pclk_domain_3x_prog->freq_delta_max_mhz; + memcpy(&pset->super.deltas, &pdomains->deltas, + (sizeof(struct ctrl_clk_clk_delta))); + pset->pre_volt_ordering_index = pclk_domain_35_prog->pre_volt_ordering_index; + pset->post_volt_ordering_index = pclk_domain_35_prog->post_volt_ordering_index; + pset->clk_pos = pclk_domain_35_prog->clk_pos; + pset->clk_vf_curve_count = pclk_domain_35_prog->clk_vf_curve_count; + + return status; +} + +static int _clk_domain_pmudatainit_3x_prog(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_domain_3x_prog *pclk_domain_3x_prog; + struct nv_pmu_clk_clk_domain_30_prog_boardobj_set *pset; + struct clk_domains *pdomains = &(g->clk_pmu.clk_domainobjs); + + nvgpu_log_info(g, " "); + + status = _clk_domain_pmudatainit_3x(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pclk_domain_3x_prog = (struct clk_domain_3x_prog *)board_obj_ptr; + + pset = (struct nv_pmu_clk_clk_domain_30_prog_boardobj_set *) + ppmudata; + + pset->super.clk_prog_idx_first = pclk_domain_3x_prog->clk_prog_idx_first; + pset->super.clk_prog_idx_last = pclk_domain_3x_prog->clk_prog_idx_last; + pset->noise_unaware_ordering_index = + pclk_domain_3x_prog->noise_unaware_ordering_index; + pset->noise_aware_ordering_index = + pclk_domain_3x_prog->noise_aware_ordering_index; + pset->super.b_force_noise_unaware_ordering = + pclk_domain_3x_prog->b_force_noise_unaware_ordering; + pset->super.factory_delta = pclk_domain_3x_prog->factory_delta; + pset->super.freq_delta_min_mhz = pclk_domain_3x_prog->freq_delta_min_mhz; + pset->super.freq_delta_max_mhz = pclk_domain_3x_prog->freq_delta_max_mhz; + memcpy(&pset->super.deltas, &pdomains->deltas, + (sizeof(struct ctrl_clk_clk_delta))); + + return status; +} + +static int clk_domain_construct_35_prog(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct clk_domain_35_prog *pdomain; + struct clk_domain_35_prog *ptmpdomain = + (struct clk_domain_35_prog *)pargs; + int status = 0; + + ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X_PROG); + status = clk_domain_construct_3x(g, ppboardobj, size, pargs); + if (status != 0UL) + { + return (u32) -EINVAL; + } + + pdomain = (struct clk_domain_35_prog *)(void*) *ppboardobj; + + pdomain->super.super.super.super.pmudatainit = + clk_domain_pmudatainit_35_prog; + + pdomain->super.super.super.clkdomainclkproglink = + clkdomainclkproglink_3x_prog; + + pdomain->super.super.super.clkdomainclkvfsearch = + clkdomainvfsearch; + + pdomain->super.super.super.clkdomainclkgetfpoints = + clkdomaingetfpoints; + + pdomain->super.clk_prog_idx_first = ptmpdomain->super.clk_prog_idx_first; + pdomain->super.clk_prog_idx_last = ptmpdomain->super.clk_prog_idx_last; + pdomain->super.noise_unaware_ordering_index = + ptmpdomain->super.noise_unaware_ordering_index; + pdomain->super.noise_aware_ordering_index = + ptmpdomain->super.noise_aware_ordering_index; + pdomain->super.b_force_noise_unaware_ordering = + ptmpdomain->super.b_force_noise_unaware_ordering; + pdomain->super.factory_delta = ptmpdomain->super.factory_delta; + pdomain->super.freq_delta_min_mhz = ptmpdomain->super.freq_delta_min_mhz; + pdomain->super.freq_delta_max_mhz = ptmpdomain->super.freq_delta_max_mhz; + pdomain->pre_volt_ordering_index = ptmpdomain->pre_volt_ordering_index; + pdomain->post_volt_ordering_index = ptmpdomain->post_volt_ordering_index; + pdomain->clk_pos = ptmpdomain->clk_pos; + pdomain->clk_vf_curve_count = ptmpdomain->clk_vf_curve_count; + + return status; +} + +static int clk_domain_construct_3x_prog(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct clk_domain_3x_prog *pdomain; + struct clk_domain_3x_prog *ptmpdomain = + (struct clk_domain_3x_prog *)pargs; + int status = 0; + + ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X_PROG); + status = clk_domain_construct_3x(g, ppboardobj, size, pargs); + if (status) { + return -EINVAL; + } + + pdomain = (struct clk_domain_3x_prog *)*ppboardobj; + + pdomain->super.super.super.pmudatainit = + _clk_domain_pmudatainit_3x_prog; + + pdomain->super.super.clkdomainclkproglink = + clkdomainclkproglink_3x_prog; + + pdomain->super.super.clkdomainclkvfsearch = + clkdomainvfsearch; + + pdomain->super.super.clkdomainclkgetfpoints = + clkdomaingetfpoints; + + pdomain->clk_prog_idx_first = ptmpdomain->clk_prog_idx_first; + pdomain->clk_prog_idx_last = ptmpdomain->clk_prog_idx_last; + pdomain->noise_unaware_ordering_index = + ptmpdomain->noise_unaware_ordering_index; + pdomain->noise_aware_ordering_index = + ptmpdomain->noise_aware_ordering_index; + pdomain->b_force_noise_unaware_ordering = + ptmpdomain->b_force_noise_unaware_ordering; + pdomain->factory_delta = ptmpdomain->factory_delta; + pdomain->freq_delta_min_mhz = ptmpdomain->freq_delta_min_mhz; + pdomain->freq_delta_max_mhz = ptmpdomain->freq_delta_max_mhz; + + return status; +} + +static int _clk_domain_pmudatainit_35_slave(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_domain_35_slave *pclk_domain_35_slave; + struct nv_pmu_clk_clk_domain_35_slave_boardobj_set *pset; + + nvgpu_log_info(g, " "); + + status = clk_domain_pmudatainit_35_prog(g, board_obj_ptr, ppmudata); + if (status != 0UL) { + return status; + } + + pclk_domain_35_slave = (struct clk_domain_35_slave *)(void*)board_obj_ptr; + + pset = (struct nv_pmu_clk_clk_domain_35_slave_boardobj_set *) + (void*) ppmudata; + + pset->slave.master_idx = pclk_domain_35_slave->slave.master_idx; + + return status; +} + +static int clk_domain_pmudatainit_3x_slave(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_domain_3x_slave *pclk_domain_3x_slave; + struct nv_pmu_clk_clk_domain_3x_slave_boardobj_set *pset; + + nvgpu_log_info(g, " "); + + status = _clk_domain_pmudatainit_3x_prog(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pclk_domain_3x_slave = (struct clk_domain_3x_slave *)board_obj_ptr; + + pset = (struct nv_pmu_clk_clk_domain_3x_slave_boardobj_set *) + ppmudata; + + pset->master_idx = pclk_domain_3x_slave->master_idx; + + return status; +} + +static int clk_domain_construct_35_slave(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct clk_domain_35_slave *pdomain; + struct clk_domain_35_slave *ptmpdomain = + (struct clk_domain_35_slave *)pargs; + int status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != (u8) CTRL_CLK_CLK_DOMAIN_TYPE_35_SLAVE) { + return (u32) -EINVAL; + } + + ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_35_SLAVE); + status = clk_domain_construct_35_prog(g, ppboardobj, size, pargs); + if (status != 0UL) { + return (u32) -EINVAL; + } + + pdomain = (struct clk_domain_35_slave *)(void*)*ppboardobj; + + pdomain->super.super.super.super.super.pmudatainit = + _clk_domain_pmudatainit_35_slave; + + pdomain->slave.master_idx = ptmpdomain->slave.master_idx; + + pdomain->slave.clkdomainclkgetslaveclk = + clkdomaingetslaveclk; + + return status; +} + +static int clk_domain_construct_3x_slave(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct clk_domain_3x_slave *pdomain; + struct clk_domain_3x_slave *ptmpdomain = + (struct clk_domain_3x_slave *)pargs; + int status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != (u8) CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE) { + return -EINVAL; + } + + ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE); + status = clk_domain_construct_3x_prog(g, ppboardobj, size, pargs); + if (status != 0UL) { + return -EINVAL; + } + + pdomain = (struct clk_domain_3x_slave *)*ppboardobj; + + pdomain->super.super.super.super.pmudatainit = + clk_domain_pmudatainit_3x_slave; + + pdomain->master_idx = ptmpdomain->master_idx; + + pdomain->clkdomainclkgetslaveclk = + clkdomaingetslaveclk; + + return status; +} + +static int clkdomainclkproglink_3x_master(struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_domain *pdomain) +{ + int status = 0; + struct clk_domain_3x_master *p3xmaster = + (struct clk_domain_3x_master *)pdomain; + struct clk_prog *pprog = NULL; + struct clk_prog_1x_master *pprog1xmaster = NULL; + u16 freq_max_last_mhz = 0; + u8 i; + + nvgpu_log_info(g, " "); + + status = clkdomainclkproglink_3x_prog(g, pclk, pdomain); + if (status) { + goto done; + } + + /* Iterate over the set of CLK_PROGs pointed at by this domain.*/ + for (i = p3xmaster->super.clk_prog_idx_first; + i <= p3xmaster->super.clk_prog_idx_last; + i++) { + pprog = CLK_CLK_PROG_GET(pclk, i); + + /* MASTER CLK_DOMAINs must point to MASTER CLK_PROGs.*/ + if (!pprog->super.implements(g, &pprog->super, + CTRL_CLK_CLK_PROG_TYPE_1X_MASTER)) { + status = -EINVAL; + goto done; + } + + pprog1xmaster = (struct clk_prog_1x_master *)pprog; + status = pprog1xmaster->vfflatten(g, pclk, pprog1xmaster, + BOARDOBJ_GET_IDX(p3xmaster), &freq_max_last_mhz); + if (status) { + goto done; + } + } +done: + nvgpu_log_info(g, "done status %x", status); + return status; +} + +static int clk_domain_pmudatainit_35_master(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_domain_35_master *pclk_domain_35_master; + struct nv_pmu_clk_clk_domain_35_master_boardobj_set *pset; + + nvgpu_log_info(g, " "); + + status = clk_domain_pmudatainit_35_prog(g, board_obj_ptr, ppmudata); + if (status != 0UL) { + return status; + } + + pclk_domain_35_master = (struct clk_domain_35_master *) + (void*) board_obj_ptr; + + pset = (struct nv_pmu_clk_clk_domain_35_master_boardobj_set *) + (void*) ppmudata; + + pset->master.slave_idxs_mask = pclk_domain_35_master->master.slave_idxs_mask; + + return status; +} + +static int _clk_domain_pmudatainit_3x_master(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_domain_3x_master *pclk_domain_3x_master; + struct nv_pmu_clk_clk_domain_3x_master_boardobj_set *pset; + + nvgpu_log_info(g, " "); + + status = _clk_domain_pmudatainit_3x_prog(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pclk_domain_3x_master = (struct clk_domain_3x_master *)board_obj_ptr; + + pset = (struct nv_pmu_clk_clk_domain_3x_master_boardobj_set *) + ppmudata; + + pset->slave_idxs_mask = pclk_domain_3x_master->slave_idxs_mask; + + return status; +} + +static int clk_domain_construct_35_master(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct clk_domain_35_master *pdomain; + int status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != (u8) CTRL_CLK_CLK_DOMAIN_TYPE_35_MASTER) { + return -EINVAL; + } + + ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_35_MASTER); + status = clk_domain_construct_35_prog(g, ppboardobj, size, pargs); + if (status != 0UL) { + return (u32) -EINVAL; + } + + pdomain = (struct clk_domain_35_master *)(void*) *ppboardobj; + + pdomain->super.super.super.super.super.pmudatainit = + clk_domain_pmudatainit_35_master; + pdomain->super.super.super.super.clkdomainclkproglink = + clkdomainclkproglink_3x_master; + + pdomain->master.slave_idxs_mask = 0; + + return status; +} + +static int clk_domain_construct_3x_master(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct clk_domain_3x_master *pdomain; + int status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER) { + return -EINVAL; + } + + ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER); + status = clk_domain_construct_3x_prog(g, ppboardobj, size, pargs); + if (status) { + return -EINVAL; + } + + pdomain = (struct clk_domain_3x_master *)*ppboardobj; + + pdomain->super.super.super.super.pmudatainit = + _clk_domain_pmudatainit_3x_master; + pdomain->super.super.super.clkdomainclkproglink = + clkdomainclkproglink_3x_master; + + pdomain->slave_idxs_mask = 0; + + return status; +} + +static int clkdomainclkproglink_fixed(struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_domain *pdomain) +{ + nvgpu_log_info(g, " "); + return 0; +} + +static int _clk_domain_pmudatainit_3x_fixed(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_domain_3x_fixed *pclk_domain_3x_fixed; + struct nv_pmu_clk_clk_domain_3x_fixed_boardobj_set *pset; + + nvgpu_log_info(g, " "); + + status = _clk_domain_pmudatainit_3x(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pclk_domain_3x_fixed = (struct clk_domain_3x_fixed *)board_obj_ptr; + + pset = (struct nv_pmu_clk_clk_domain_3x_fixed_boardobj_set *) + ppmudata; + + pset->freq_mhz = pclk_domain_3x_fixed->freq_mhz; + + return status; +} + +static int clk_domain_construct_3x_fixed(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct clk_domain_3x_fixed *pdomain; + struct clk_domain_3x_fixed *ptmpdomain = + (struct clk_domain_3x_fixed *)pargs; + int status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_DOMAIN_TYPE_3X_FIXED) { + return -EINVAL; + } + + ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X_FIXED); + status = clk_domain_construct_3x(g, ppboardobj, size, pargs); + if (status) { + return -EINVAL; + } + + pdomain = (struct clk_domain_3x_fixed *)*ppboardobj; + + pdomain->super.super.super.pmudatainit = + _clk_domain_pmudatainit_3x_fixed; + + pdomain->super.super.clkdomainclkproglink = + clkdomainclkproglink_fixed; + + pdomain->freq_mhz = ptmpdomain->freq_mhz; + + return status; +} + +static struct clk_domain *construct_clk_domain(struct gk20a *g, void *pargs) +{ + struct boardobj *board_obj_ptr = NULL; + u32 status; + + nvgpu_log_info(g, " %d", BOARDOBJ_GET_TYPE(pargs)); + switch (BOARDOBJ_GET_TYPE(pargs)) { + case CTRL_CLK_CLK_DOMAIN_TYPE_3X_FIXED: + status = clk_domain_construct_3x_fixed(g, &board_obj_ptr, + sizeof(struct clk_domain_3x_fixed), pargs); + break; + + case CTRL_CLK_CLK_DOMAIN_TYPE_35_MASTER: + status = clk_domain_construct_35_master(g, &board_obj_ptr, + sizeof(struct clk_domain_35_master), pargs); + break; + + + case CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER: + status = clk_domain_construct_3x_master(g, &board_obj_ptr, + sizeof(struct clk_domain_3x_master), pargs); + break; + + case CTRL_CLK_CLK_DOMAIN_TYPE_35_SLAVE: + status = clk_domain_construct_35_slave(g, &board_obj_ptr, + sizeof(struct clk_domain_35_slave), pargs); + break; + + case CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE: + status = clk_domain_construct_3x_slave(g, &board_obj_ptr, + sizeof(struct clk_domain_3x_slave), pargs); + break; + + default: + return NULL; + } + + if (status) { + return NULL; + } + + nvgpu_log_info(g, " Done"); + + return (struct clk_domain *)board_obj_ptr; +} + +static int clk_domain_pmudatainit_super(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_domain *pclk_domain; + struct nv_pmu_clk_clk_domain_boardobj_set *pset; + + nvgpu_log_info(g, " "); + + status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pclk_domain = (struct clk_domain *)board_obj_ptr; + + pset = (struct nv_pmu_clk_clk_domain_boardobj_set *)ppmudata; + + pset->domain = pclk_domain->domain; + pset->api_domain = pclk_domain->api_domain; + pset->perf_domain_grp_idx = pclk_domain->perf_domain_grp_idx; + + return status; +} + +int clk_domain_clk_prog_link(struct gk20a *g, struct clk_pmupstate *pclk) +{ + int status = 0; + struct clk_domain *pdomain; + u8 i; + + /* Iterate over all CLK_DOMAINs and flatten their VF curves.*/ + BOARDOBJGRP_FOR_EACH(&(pclk->clk_domainobjs.super.super), + struct clk_domain *, pdomain, i) { + status = pdomain->clkdomainclkproglink(g, pclk, pdomain); + if (status) { + nvgpu_err(g, + "error flattening VF for CLK DOMAIN - 0x%x", + pdomain->domain); + goto done; + } + } + +done: + return status; +} diff --git a/include/clk/clk_domain.h b/include/clk/clk_domain.h new file mode 100644 index 0000000..e5a7153 --- /dev/null +++ b/include/clk/clk_domain.h @@ -0,0 +1,157 @@ +/* +* Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. +* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. +*/ + +#ifndef NVGPU_CLK_DOMAIN_H +#define NVGPU_CLK_DOMAIN_H + +#include "ctrl/ctrlclk.h" +#include "ctrl/ctrlboardobj.h" +#include +#include "boardobj/boardobjgrp_e32.h" +#include "boardobj/boardobjgrpmask.h" + +#define CLK_DOMAIN_BOARDOBJGRP_VERSION 0x30 +#define CLK_TABLE_HAL_ENTRY_GP 0x02 +#define CLK_TABLE_HAL_ENTRY_GV 0x03 + +struct clk_domains; +struct clk_domain; +enum nv_pmu_clk_clkwhich; + +/*data and function definition to talk to driver*/ +int clk_domain_sw_setup(struct gk20a *g); +int clk_domain_pmu_setup(struct gk20a *g); + +typedef int clkproglink(struct gk20a *g, struct clk_pmupstate *pclk, + struct clk_domain *pdomain); + +typedef int clkvfsearch(struct gk20a *g, struct clk_pmupstate *pclk, + struct clk_domain *pdomain, u16 *clkmhz, + u32 *voltuv, u8 rail); + +typedef int clkgetslaveclk(struct gk20a *g, struct clk_pmupstate *pclk, + struct clk_domain *pdomain, u16 *clkmhz, + u16 masterclkmhz); + +typedef u32 clkgetfpoints(struct gk20a *g, struct clk_pmupstate *pclk, + struct clk_domain *pdomain, u32 *pfpointscount, + u16 *pfreqpointsinmhz, u8 rail); + +struct clk_domains { + struct boardobjgrp_e32 super; + u8 n_num_entries; + u8 version; + bool b_enforce_vf_monotonicity; + bool b_enforce_vf_smoothening; + bool b_override_o_v_o_c; + bool b_debug_mode; + u32 vbios_domains; + u16 cntr_sampling_periodms; + struct boardobjgrpmask_e32 prog_domains_mask; + struct boardobjgrpmask_e32 master_domains_mask; + struct ctrl_clk_clk_delta deltas; + + struct clk_domain *ordered_noise_aware_list[CTRL_BOARDOBJ_MAX_BOARD_OBJECTS]; + + struct clk_domain *ordered_noise_unaware_list[CTRL_BOARDOBJ_MAX_BOARD_OBJECTS]; +}; + +struct clk_domain { + struct boardobj super; + u32 api_domain; + u32 part_mask; + enum nv_pmu_clk_clkwhich domain; + u8 perf_domain_index; + u8 perf_domain_grp_idx; + u8 ratio_domain; + u8 usage; + clkproglink *clkdomainclkproglink; + clkvfsearch *clkdomainclkvfsearch; + clkgetfpoints *clkdomainclkgetfpoints; +}; + +struct clk_domain_3x { + struct clk_domain super; + bool b_noise_aware_capable; +}; + +struct clk_domain_3x_fixed { + struct clk_domain_3x super; + u16 freq_mhz; +}; + +struct clk_domain_3x_prog { + struct clk_domain_3x super; + u8 clk_prog_idx_first; + u8 clk_prog_idx_last; + bool b_force_noise_unaware_ordering; + struct ctrl_clk_freq_delta factory_delta; + short freq_delta_min_mhz; + short freq_delta_max_mhz; + struct ctrl_clk_clk_delta deltas; + u8 noise_unaware_ordering_index; + u8 noise_aware_ordering_index; +}; + +struct clk_domain_35_prog { + struct clk_domain_3x_prog super; + u8 pre_volt_ordering_index; + u8 post_volt_ordering_index; + u8 clk_pos; + u8 clk_vf_curve_count; +}; + +struct clk_domain_3x_master { + struct clk_domain_3x_prog super; + u32 slave_idxs_mask; +}; + +struct clk_domain_35_master { + struct clk_domain_35_prog super; + struct clk_domain_3x_master master; + struct boardobjgrpmask_e32 master_slave_domains_grp_mask; +}; + +struct clk_domain_3x_slave { + struct clk_domain_3x_prog super; + u8 master_idx; + clkgetslaveclk *clkdomainclkgetslaveclk; +}; + +struct clk_domain_30_slave { + u8 rsvd; + u8 master_idx; + clkgetslaveclk *clkdomainclkgetslaveclk; +}; + +struct clk_domain_35_slave { + struct clk_domain_35_prog super; + struct clk_domain_30_slave slave; +}; + +int clk_domain_clk_prog_link(struct gk20a *g, struct clk_pmupstate *pclk); + +#define CLK_CLK_DOMAIN_GET(pclk, idx) \ + ((struct clk_domain *)BOARDOBJGRP_OBJ_GET_BY_IDX( \ + &pclk->clk_domainobjs.super.super, (u8)(idx))) + +#endif /* NVGPU_CLK_DOMAIN_H */ diff --git a/include/clk/clk_fll.c b/include/clk/clk_fll.c new file mode 100644 index 0000000..e67dd35 --- /dev/null +++ b/include/clk/clk_fll.c @@ -0,0 +1,495 @@ +/* + * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "clk.h" +#include "clk_fll.h" +#include "clk_domain.h" +#include "boardobj/boardobjgrp.h" +#include "boardobj/boardobjgrp_e32.h" +#include "ctrl/ctrlclk.h" +#include "ctrl/ctrlvolt.h" + +static int devinit_get_fll_device_table(struct gk20a *g, + struct avfsfllobjs *pfllobjs); +static struct fll_device *construct_fll_device(struct gk20a *g, + void *pargs); +static int fll_device_init_pmudata_super(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata); + +static int _clk_fll_devgrp_pmudatainit_super(struct gk20a *g, + struct boardobjgrp *pboardobjgrp, + struct nv_pmu_boardobjgrp_super *pboardobjgrppmu) +{ + struct nv_pmu_clk_clk_fll_device_boardobjgrp_set_header *pset = + (struct nv_pmu_clk_clk_fll_device_boardobjgrp_set_header *) + pboardobjgrppmu; + struct avfsfllobjs *pfll_objs = (struct avfsfllobjs *) + pboardobjgrp; + int status = 0; + + nvgpu_log_info(g, " "); + + status = boardobjgrp_pmudatainit_e32(g, pboardobjgrp, pboardobjgrppmu); + if (status) { + nvgpu_err(g, "failed to init fll pmuobjgrp"); + return status; + } + pset->lut_num_entries = pfll_objs->lut_num_entries; + pset->lut_step_size_uv = pfll_objs->lut_step_size_uv; + pset->lut_min_voltage_uv = pfll_objs->lut_min_voltage_uv; + pset->max_min_freq_mhz = pfll_objs->max_min_freq_mhz; + + status = boardobjgrpmask_export( + &pfll_objs->lut_prog_master_mask.super, + pfll_objs->lut_prog_master_mask.super.bitcount, + &pset->lut_prog_master_mask.super); + + nvgpu_log_info(g, " Done"); + return status; +} + +static int _clk_fll_devgrp_pmudata_instget(struct gk20a *g, + struct nv_pmu_boardobjgrp *pmuboardobjgrp, + struct nv_pmu_boardobj **ppboardobjpmudata, + u8 idx) +{ + struct nv_pmu_clk_clk_fll_device_boardobj_grp_set *pgrp_set = + (struct nv_pmu_clk_clk_fll_device_boardobj_grp_set *) + pmuboardobjgrp; + + nvgpu_log_info(g, " "); + + /*check whether pmuboardobjgrp has a valid boardobj in index*/ + if (((u32)BIT(idx) & + pgrp_set->hdr.data.super.obj_mask.super.data[0]) == 0) { + return -EINVAL; + } + + *ppboardobjpmudata = (struct nv_pmu_boardobj *) + &pgrp_set->objects[idx].data.board_obj; + nvgpu_log_info(g, " Done"); + return 0; +} + +static int _clk_fll_devgrp_pmustatus_instget(struct gk20a *g, + void *pboardobjgrppmu, + struct nv_pmu_boardobj_query **ppboardobjpmustatus, + u8 idx) +{ + struct nv_pmu_clk_clk_fll_device_boardobj_grp_get_status *pgrp_get_status = + (struct nv_pmu_clk_clk_fll_device_boardobj_grp_get_status *) + pboardobjgrppmu; + + /*check whether pmuboardobjgrp has a valid boardobj in index*/ + 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; +} + +int clk_fll_sw_setup(struct gk20a *g) +{ + int status; + struct boardobjgrp *pboardobjgrp = NULL; + struct avfsfllobjs *pfllobjs; + struct fll_device *pfll; + struct fll_device *pfll_master; + struct fll_device *pfll_local; + u8 i; + u8 j; + + nvgpu_log_info(g, " "); + + status = boardobjgrpconstruct_e32(g, &g->clk_pmu.avfs_fllobjs.super); + if (status) { + nvgpu_err(g, + "error creating boardobjgrp for fll, status - 0x%x", status); + goto done; + } + pfllobjs = &(g->clk_pmu.avfs_fllobjs); + pboardobjgrp = &(g->clk_pmu.avfs_fllobjs.super.super); + + BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, CLK, FLL_DEVICE); + + status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, + clk, CLK, clk_fll_device, CLK_FLL_DEVICE); + if (status) { + nvgpu_err(g, + "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", + status); + goto done; + } + + pboardobjgrp->pmudatainit = _clk_fll_devgrp_pmudatainit_super; + pboardobjgrp->pmudatainstget = _clk_fll_devgrp_pmudata_instget; + pboardobjgrp->pmustatusinstget = _clk_fll_devgrp_pmustatus_instget; + pfllobjs = (struct avfsfllobjs *)pboardobjgrp; + pfllobjs->lut_num_entries = g->ops.clk.lut_num_entries; + pfllobjs->lut_step_size_uv = CTRL_CLK_VIN_STEP_SIZE_UV; + pfllobjs->lut_min_voltage_uv = CTRL_CLK_LUT_MIN_VOLTAGE_UV; + + /* Initialize lut prog master mask to zero.*/ + boardobjgrpmask_e32_init(&pfllobjs->lut_prog_master_mask, NULL); + + status = devinit_get_fll_device_table(g, pfllobjs); + if (status) { + goto done; + } + + status = BOARDOBJGRP_PMU_CMD_GRP_GET_STATUS_CONSTRUCT(g, + &g->clk_pmu.avfs_fllobjs.super.super, + clk, CLK, clk_fll_device, CLK_FLL_DEVICE); + if (status) { + nvgpu_err(g, + "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", + status); + goto done; + } + + BOARDOBJGRP_FOR_EACH(&(pfllobjs->super.super), + struct fll_device *, pfll, i) { + pfll_master = NULL; + j = 0; + BOARDOBJGRP_ITERATOR(&(pfllobjs->super.super), + struct fll_device *, pfll_local, j, + &pfllobjs->lut_prog_master_mask.super) { + if (pfll_local->clk_domain == pfll->clk_domain) { + pfll_master = pfll_local; + break; + } + } + + if (pfll_master == NULL) { + status = boardobjgrpmask_bitset( + &pfllobjs->lut_prog_master_mask.super, + BOARDOBJ_GET_IDX(pfll)); + if (status) { + nvgpu_err(g, "err setting lutprogmask"); + goto done; + } + pfll_master = pfll; + } + status = pfll_master->lut_broadcast_slave_register( + g, pfllobjs, pfll_master, pfll); + + if (status) { + nvgpu_err(g, "err setting lutslavemask"); + goto done; + } + } +done: + nvgpu_log_info(g, " done status %x", status); + return status; +} + +int clk_fll_pmu_setup(struct gk20a *g) +{ + int status; + struct boardobjgrp *pboardobjgrp = NULL; + + nvgpu_log_info(g, " "); + + pboardobjgrp = &g->clk_pmu.avfs_fllobjs.super.super; + + if (!pboardobjgrp->bconstructed) { + return -EINVAL; + } + + status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); + + nvgpu_log_info(g, "Done"); + return status; +} + +static int devinit_get_fll_device_table(struct gk20a *g, + struct avfsfllobjs *pfllobjs) +{ + int status = 0; + u8 *fll_table_ptr = NULL; + struct fll_descriptor_header fll_desc_table_header_sz = { 0 }; + struct fll_descriptor_header_10 fll_desc_table_header = { 0 }; + struct fll_descriptor_entry_10 fll_desc_table_entry = { 0 }; + u8 *fll_tbl_entry_ptr = NULL; + u32 index = 0; + struct fll_device fll_dev_data; + struct fll_device *pfll_dev; + struct vin_device *pvin_dev; + u32 desctablesize; + u32 vbios_domain = NV_PERF_DOMAIN_4X_CLOCK_DOMAIN_SKIP; + struct avfsvinobjs *pvinobjs = &g->clk_pmu.avfs_vinobjs; + + nvgpu_log_info(g, " "); + + fll_table_ptr = (u8 *)nvgpu_bios_get_perf_table_ptrs(g, + g->bios.clock_token, FLL_TABLE); + if (fll_table_ptr == NULL) { + status = -1; + goto done; + } + + memcpy(&fll_desc_table_header_sz, fll_table_ptr, + sizeof(struct fll_descriptor_header)); + if (fll_desc_table_header_sz.size >= FLL_DESCRIPTOR_HEADER_10_SIZE_6) { + desctablesize = FLL_DESCRIPTOR_HEADER_10_SIZE_6; + } else { + desctablesize = FLL_DESCRIPTOR_HEADER_10_SIZE_4; + } + + memcpy(&fll_desc_table_header, fll_table_ptr, desctablesize); + + if (desctablesize == FLL_DESCRIPTOR_HEADER_10_SIZE_6) { + pfllobjs->max_min_freq_mhz = + fll_desc_table_header.max_min_freq_mhz; + } else { + pfllobjs->max_min_freq_mhz = 0; + } + + /* Read table entries*/ + fll_tbl_entry_ptr = fll_table_ptr + desctablesize; + for (index = 0; index < fll_desc_table_header.entry_count; index++) { + u32 fll_id; + + memcpy(&fll_desc_table_entry, fll_tbl_entry_ptr, + sizeof(struct fll_descriptor_entry_10)); + + if (fll_desc_table_entry.fll_device_type == CTRL_CLK_FLL_TYPE_DISABLED) { + continue; + } + + fll_id = fll_desc_table_entry.fll_device_id; + + if ( (u8)fll_desc_table_entry.vin_idx_logic != CTRL_CLK_VIN_ID_UNDEFINED) { + pvin_dev = CLK_GET_VIN_DEVICE(pvinobjs, + (u8)fll_desc_table_entry.vin_idx_logic); + if (pvin_dev == NULL) { + return -EINVAL; + } else { + pvin_dev->flls_shared_mask |= BIT(fll_id); + } + } else { + /* Return if Logic ADC device index is invalid*/ + nvgpu_err(g, "Invalid Logic ADC specified for Nafll ID"); + return -EINVAL; + } + + fll_dev_data.lut_device.vselect_mode = + (u8)BIOS_GET_FIELD(fll_desc_table_entry.lut_params, + NV_FLL_DESC_LUT_PARAMS_VSELECT); + + if ( (u8)fll_desc_table_entry.vin_idx_sram != CTRL_CLK_VIN_ID_UNDEFINED) { + pvin_dev = CLK_GET_VIN_DEVICE(pvinobjs, + (u8)fll_desc_table_entry.vin_idx_sram); + if (pvin_dev == NULL) { + return -EINVAL; + } else { + pvin_dev->flls_shared_mask |= BIT(fll_id); + } + } else { + /* Make sure VSELECT mode is set correctly to _LOGIC*/ + if (fll_dev_data.lut_device.vselect_mode != CTRL_CLK_FLL_LUT_VSELECT_LOGIC) { + return -EINVAL; + } + } + + fll_dev_data.super.type = + (u8)fll_desc_table_entry.fll_device_type; + fll_dev_data.id = (u8)fll_desc_table_entry.fll_device_id; + fll_dev_data.mdiv = (u8)BIOS_GET_FIELD( + fll_desc_table_entry.fll_params, + NV_FLL_DESC_FLL_PARAMS_MDIV); + fll_dev_data.input_freq_mhz = + (u16)fll_desc_table_entry.ref_freq_mhz; + fll_dev_data.min_freq_vfe_idx = + (u8)fll_desc_table_entry.min_freq_vfe_idx; + fll_dev_data.freq_ctrl_idx = CTRL_BOARDOBJ_IDX_INVALID; + + vbios_domain = (u32)(fll_desc_table_entry.clk_domain & + NV_PERF_DOMAIN_4X_CLOCK_DOMAIN_MASK); + fll_dev_data.clk_domain = + g->ops.pmu_ver.clk.get_vbios_clk_domain(vbios_domain); + + fll_dev_data.rail_idx_for_lut = 0; + fll_dev_data.vin_idx_logic = + (u8)fll_desc_table_entry.vin_idx_logic; + fll_dev_data.vin_idx_sram = + (u8)fll_desc_table_entry.vin_idx_sram; + fll_dev_data.b_skip_pldiv_below_dvco_min = + (bool)BIOS_GET_FIELD(fll_desc_table_entry.fll_params, + NV_FLL_DESC_FLL_PARAMS_SKIP_PLDIV_BELOW_DVCO_MIN); + fll_dev_data.lut_device.hysteresis_threshold = + (u8)BIOS_GET_FIELD(fll_desc_table_entry.lut_params, + NV_FLL_DESC_LUT_PARAMS_HYSTERISIS_THRESHOLD); + fll_dev_data.regime_desc.regime_id = + CTRL_CLK_FLL_REGIME_ID_FFR; + fll_dev_data.regime_desc.fixed_freq_regime_limit_mhz = + (u16)fll_desc_table_entry.ffr_cutoff_freq_mhz; + fll_dev_data.regime_desc.target_regime_id_override=0; + + /*construct fll device*/ + pfll_dev = construct_fll_device(g, (void *)&fll_dev_data); + + status = boardobjgrp_objinsert(&pfllobjs->super.super, + (struct boardobj *)pfll_dev, index); + fll_tbl_entry_ptr += fll_desc_table_header.entry_size; + } + +done: + nvgpu_log_info(g, " done status %x", status); + return status; +} + +u32 nvgpu_clk_get_vbios_clk_domain_gv10x( u32 vbios_domain) +{ + if (vbios_domain == 0) { + return CTRL_CLK_DOMAIN_GPCCLK; + } else if (vbios_domain == 1) { + return CTRL_CLK_DOMAIN_XBARCLK; + } else if (vbios_domain == 3) { + return CTRL_CLK_DOMAIN_SYSCLK; + } else if (vbios_domain == 5) { + return CTRL_CLK_DOMAIN_NVDCLK; + } + return 0; +} + +u32 nvgpu_clk_get_vbios_clk_domain_gp10x( u32 vbios_domain) +{ + if (vbios_domain == 0) { + return CTRL_CLK_DOMAIN_GPC2CLK; + } else if (vbios_domain == 1) { + return CTRL_CLK_DOMAIN_XBAR2CLK; + } else if (vbios_domain == 3) { + return CTRL_CLK_DOMAIN_SYS2CLK; + } + return 0; +} + +static u32 lutbroadcastslaveregister(struct gk20a *g, + struct avfsfllobjs *pfllobjs, + struct fll_device *pfll, + struct fll_device *pfll_slave) +{ + if (pfll->clk_domain != pfll_slave->clk_domain) { + return -EINVAL; + } + + return boardobjgrpmask_bitset(&pfll-> + lut_prog_broadcast_slave_mask.super, + BOARDOBJ_GET_IDX(pfll_slave)); +} + +static struct fll_device *construct_fll_device(struct gk20a *g, + void *pargs) +{ + struct boardobj *board_obj_ptr = NULL; + struct fll_device *pfll_dev; + struct fll_device *board_obj_fll_ptr = NULL; + int status; + + nvgpu_log_info(g, " "); + status = boardobj_construct_super(g, &board_obj_ptr, + sizeof(struct fll_device), pargs); + if (status) { + return NULL; + } + + pfll_dev = (struct fll_device *)pargs; + board_obj_fll_ptr = (struct fll_device *)board_obj_ptr; + board_obj_ptr->pmudatainit = fll_device_init_pmudata_super; + board_obj_fll_ptr->lut_broadcast_slave_register = + lutbroadcastslaveregister; + board_obj_fll_ptr->id = pfll_dev->id; + board_obj_fll_ptr->mdiv = pfll_dev->mdiv; + board_obj_fll_ptr->rail_idx_for_lut = pfll_dev->rail_idx_for_lut; + board_obj_fll_ptr->input_freq_mhz = pfll_dev->input_freq_mhz; + board_obj_fll_ptr->clk_domain = pfll_dev->clk_domain; + board_obj_fll_ptr->vin_idx_logic = pfll_dev->vin_idx_logic; + board_obj_fll_ptr->vin_idx_sram = pfll_dev->vin_idx_sram; + board_obj_fll_ptr->min_freq_vfe_idx = + pfll_dev->min_freq_vfe_idx; + board_obj_fll_ptr->freq_ctrl_idx = pfll_dev->freq_ctrl_idx; + board_obj_fll_ptr->b_skip_pldiv_below_dvco_min = + pfll_dev->b_skip_pldiv_below_dvco_min; + memcpy(&board_obj_fll_ptr->lut_device, &pfll_dev->lut_device, + sizeof(struct nv_pmu_clk_lut_device_desc)); + memcpy(&board_obj_fll_ptr->regime_desc, &pfll_dev->regime_desc, + sizeof(struct nv_pmu_clk_regime_desc)); + boardobjgrpmask_e32_init( + &board_obj_fll_ptr->lut_prog_broadcast_slave_mask, NULL); + + nvgpu_log_info(g, " Done"); + + return (struct fll_device *)board_obj_ptr; +} + +static int fll_device_init_pmudata_super(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct fll_device *pfll_dev; + struct nv_pmu_clk_clk_fll_device_boardobj_set *perf_pmu_data; + + nvgpu_log_info(g, " "); + + status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pfll_dev = (struct fll_device *)board_obj_ptr; + perf_pmu_data = (struct nv_pmu_clk_clk_fll_device_boardobj_set *) + ppmudata; + + perf_pmu_data->id = pfll_dev->id; + perf_pmu_data->mdiv = pfll_dev->mdiv; + perf_pmu_data->rail_idx_for_lut = pfll_dev->rail_idx_for_lut; + perf_pmu_data->input_freq_mhz = pfll_dev->input_freq_mhz; + perf_pmu_data->vin_idx_logic = pfll_dev->vin_idx_logic; + perf_pmu_data->vin_idx_sram = pfll_dev->vin_idx_sram; + perf_pmu_data->clk_domain = pfll_dev->clk_domain; + perf_pmu_data->min_freq_vfe_idx = + pfll_dev->min_freq_vfe_idx; + perf_pmu_data->freq_ctrl_idx = pfll_dev->freq_ctrl_idx; + perf_pmu_data->b_skip_pldiv_below_dvco_min = pfll_dev->b_skip_pldiv_below_dvco_min; + memcpy(&perf_pmu_data->lut_device, &pfll_dev->lut_device, + sizeof(struct nv_pmu_clk_lut_device_desc)); + memcpy(&perf_pmu_data->regime_desc, &pfll_dev->regime_desc, + sizeof(struct nv_pmu_clk_regime_desc)); + + status = boardobjgrpmask_export( + &pfll_dev->lut_prog_broadcast_slave_mask.super, + pfll_dev->lut_prog_broadcast_slave_mask.super.bitcount, + &perf_pmu_data->lut_prog_broadcast_slave_mask.super); + + nvgpu_log_info(g, " Done"); + + return status; +} diff --git a/include/clk/clk_fll.h b/include/clk/clk_fll.h new file mode 100644 index 0000000..6cbdfe2 --- /dev/null +++ b/include/clk/clk_fll.h @@ -0,0 +1,81 @@ +/* +* Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. +* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. +*/ + +#ifndef NVGPU_CLK_FLL_H +#define NVGPU_CLK_FLL_H + +#include +#include "boardobj/boardobjgrp_e32.h" +#include "boardobj/boardobjgrpmask.h" + +/*data and function definition to talk to driver*/ +int clk_fll_sw_setup(struct gk20a *g); +int clk_fll_pmu_setup(struct gk20a *g); + +struct avfsfllobjs { + struct boardobjgrp_e32 super; + struct boardobjgrpmask_e32 lut_prog_master_mask; + u32 lut_step_size_uv; + u32 lut_min_voltage_uv; + u8 lut_num_entries; + u16 max_min_freq_mhz; +}; + +struct fll_device; + +typedef u32 fll_lut_broadcast_slave_register(struct gk20a *g, + struct avfsfllobjs *pfllobjs, + struct fll_device *pfll, + struct fll_device *pfll_slave); + +struct fll_device { + struct boardobj super; + u8 id; + u8 mdiv; + u16 input_freq_mhz; + u32 clk_domain; + u8 vin_idx_logic; + u8 vin_idx_sram; + u8 rail_idx_for_lut; + struct nv_pmu_clk_lut_device_desc lut_device; + struct nv_pmu_clk_regime_desc regime_desc; + u8 min_freq_vfe_idx; + u8 freq_ctrl_idx; + u8 target_regime_id_override; + bool b_skip_pldiv_below_dvco_min; + bool b_dvco_1x; + struct boardobjgrpmask_e32 lut_prog_broadcast_slave_mask; + fll_lut_broadcast_slave_register *lut_broadcast_slave_register; +}; + +u32 nvgpu_clk_get_vbios_clk_domain_gv10x( u32 vbios_domain); +u32 nvgpu_clk_get_vbios_clk_domain_gp10x( u32 vbios_domain); + +#define CLK_FLL_LUT_VF_NUM_ENTRIES(pclk) \ + (pclk->avfs_fllobjs.lut_num_entries) + +#define CLK_FLL_LUT_MIN_VOLTAGE_UV(pclk) \ + (pclk->avfs_fllobjs.lut_min_voltage_uv) +#define CLK_FLL_LUT_STEP_SIZE_UV(pclk) \ + (pclk->avfs_fllobjs.lut_step_size_uv) + +#endif /* NVGPU_CLK_FLL_H */ diff --git a/include/clk/clk_freq_controller.c b/include/clk/clk_freq_controller.c new file mode 100644 index 0000000..f4d09b0 --- /dev/null +++ b/include/clk/clk_freq_controller.c @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "clk.h" +#include "clk_fll.h" +#include "clk_domain.h" +#include "clk_freq_controller.h" +#include "boardobj/boardobjgrp.h" +#include "boardobj/boardobjgrp_e32.h" +#include "ctrl/ctrlclk.h" +#include "ctrl/ctrlvolt.h" + +static int clk_freq_controller_pmudatainit_super(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + struct nv_pmu_clk_clk_freq_controller_boardobj_set *pfreq_cntlr_set; + struct clk_freq_controller *pfreq_cntlr; + int status = 0; + + status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status) { + return status; + } + + pfreq_cntlr_set = + (struct nv_pmu_clk_clk_freq_controller_boardobj_set *)ppmudata; + pfreq_cntlr = (struct clk_freq_controller *)board_obj_ptr; + + pfreq_cntlr_set->controller_id = pfreq_cntlr->controller_id; + pfreq_cntlr_set->clk_domain = pfreq_cntlr->clk_domain; + pfreq_cntlr_set->parts_freq_mode = pfreq_cntlr->parts_freq_mode; + pfreq_cntlr_set->bdisable = pfreq_cntlr->bdisable; + pfreq_cntlr_set->freq_cap_noise_unaware_vmin_above = + pfreq_cntlr->freq_cap_noise_unaware_vmin_above; + pfreq_cntlr_set->freq_cap_noise_unaware_vmin_below = + pfreq_cntlr->freq_cap_noise_unaware_vmin_below; + pfreq_cntlr_set->freq_hyst_pos_mhz = pfreq_cntlr->freq_hyst_pos_mhz; + pfreq_cntlr_set->freq_hyst_neg_mhz = pfreq_cntlr->freq_hyst_neg_mhz; + + return status; +} + +static int clk_freq_controller_pmudatainit_pi(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + struct nv_pmu_clk_clk_freq_controller_pi_boardobj_set + *pfreq_cntlr_pi_set; + struct clk_freq_controller_pi *pfreq_cntlr_pi; + int status = 0; + + status = clk_freq_controller_pmudatainit_super(g, + board_obj_ptr, ppmudata); + if (status) { + return -1; + } + + pfreq_cntlr_pi_set = + (struct nv_pmu_clk_clk_freq_controller_pi_boardobj_set *) + ppmudata; + pfreq_cntlr_pi = (struct clk_freq_controller_pi *)board_obj_ptr; + + pfreq_cntlr_pi_set->prop_gain = pfreq_cntlr_pi->prop_gain; + pfreq_cntlr_pi_set->integ_gain = pfreq_cntlr_pi->integ_gain; + pfreq_cntlr_pi_set->integ_decay = pfreq_cntlr_pi->integ_decay; + pfreq_cntlr_pi_set->volt_delta_min = pfreq_cntlr_pi->volt_delta_min; + pfreq_cntlr_pi_set->volt_delta_max = pfreq_cntlr_pi->volt_delta_max; + pfreq_cntlr_pi_set->slowdown_pct_min = pfreq_cntlr_pi->slowdown_pct_min; + pfreq_cntlr_pi_set->bpoison = pfreq_cntlr_pi->bpoison; + + return status; +} + +static int clk_freq_controller_construct_super(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct clk_freq_controller *pfreq_cntlr = NULL; + struct clk_freq_controller *pfreq_cntlr_tmp = NULL; + int status = 0; + + status = boardobj_construct_super(g, ppboardobj, size, pargs); + if (status) { + return -EINVAL; + } + + pfreq_cntlr_tmp = (struct clk_freq_controller *)pargs; + pfreq_cntlr = (struct clk_freq_controller *)*ppboardobj; + + pfreq_cntlr->super.pmudatainit = clk_freq_controller_pmudatainit_super; + + pfreq_cntlr->controller_id = pfreq_cntlr_tmp->controller_id; + pfreq_cntlr->clk_domain = pfreq_cntlr_tmp->clk_domain; + pfreq_cntlr->parts_freq_mode = pfreq_cntlr_tmp->parts_freq_mode; + pfreq_cntlr->freq_cap_noise_unaware_vmin_above = + pfreq_cntlr_tmp->freq_cap_noise_unaware_vmin_above; + pfreq_cntlr->freq_cap_noise_unaware_vmin_below = + pfreq_cntlr_tmp->freq_cap_noise_unaware_vmin_below; + pfreq_cntlr->freq_hyst_pos_mhz = pfreq_cntlr_tmp->freq_hyst_pos_mhz; + pfreq_cntlr->freq_hyst_neg_mhz = pfreq_cntlr_tmp->freq_hyst_neg_mhz; + + return status; +} + +static int clk_freq_controller_construct_pi(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct clk_freq_controller_pi *pfreq_cntlr_pi = NULL; + struct clk_freq_controller_pi *pfreq_cntlr_pi_tmp = NULL; + int status = 0; + + status = clk_freq_controller_construct_super(g, ppboardobj, + size, pargs); + if (status) { + return -EINVAL; + } + + pfreq_cntlr_pi = (struct clk_freq_controller_pi *)*ppboardobj; + pfreq_cntlr_pi_tmp = (struct clk_freq_controller_pi *)pargs; + + pfreq_cntlr_pi->super.super.pmudatainit = + clk_freq_controller_pmudatainit_pi; + + pfreq_cntlr_pi->prop_gain = pfreq_cntlr_pi_tmp->prop_gain; + pfreq_cntlr_pi->integ_gain = pfreq_cntlr_pi_tmp->integ_gain; + pfreq_cntlr_pi->integ_decay = pfreq_cntlr_pi_tmp->integ_decay; + pfreq_cntlr_pi->volt_delta_min = pfreq_cntlr_pi_tmp->volt_delta_min; + pfreq_cntlr_pi->volt_delta_max = pfreq_cntlr_pi_tmp->volt_delta_max; + pfreq_cntlr_pi->slowdown_pct_min = pfreq_cntlr_pi_tmp->slowdown_pct_min; + pfreq_cntlr_pi->bpoison = pfreq_cntlr_pi_tmp->bpoison; + + return status; +} + +static struct clk_freq_controller *clk_clk_freq_controller_construct( + struct gk20a *g, + void *pargs) +{ + struct boardobj *board_obj_ptr = NULL; + int status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_FREQ_CONTROLLER_TYPE_PI) { + return NULL; + } + + status = clk_freq_controller_construct_pi(g, &board_obj_ptr, + sizeof(struct clk_freq_controller_pi), pargs); + if (status) { + return NULL; + } + + return (struct clk_freq_controller *)board_obj_ptr; +} + + +static int clk_get_freq_controller_table(struct gk20a *g, + struct clk_freq_controllers *pclk_freq_controllers) +{ + int status = 0; + u8 *pfreq_controller_table_ptr = NULL; + struct vbios_fct_1x_header header = { 0 }; + struct vbios_fct_1x_entry entry = { 0 }; + u8 entry_idx; + u8 *entry_offset; + struct clk_freq_controller *pclk_freq_cntr = NULL; + struct clk_freq_controller *ptmp_freq_cntr = NULL; + struct clk_freq_controller_pi *ptmp_freq_cntr_pi = NULL; + struct clk_domain *pclk_domain; + + struct freq_controller_data_type { + union { + struct boardobj board_obj; + struct clk_freq_controller freq_controller; + struct clk_freq_controller_pi freq_controller_pi; + }; + } freq_controller_data; + + pfreq_controller_table_ptr = + (u8 *)nvgpu_bios_get_perf_table_ptrs(g, + g->bios.clock_token, + FREQUENCY_CONTROLLER_TABLE); + if (pfreq_controller_table_ptr == NULL) { + status = -EINVAL; + goto done; + } + + memcpy(&header, pfreq_controller_table_ptr, + sizeof(struct vbios_fct_1x_header)); + + pclk_freq_controllers->sampling_period_ms = header.sampling_period_ms; + pclk_freq_controllers->volt_policy_idx = 0; + + /* Read in the entries. */ + for (entry_idx = 0; entry_idx < header.entry_count; entry_idx++) { + entry_offset = (pfreq_controller_table_ptr + + header.header_size + (entry_idx * header.entry_size)); + + memset(&freq_controller_data, 0x0, + sizeof(struct freq_controller_data_type)); + ptmp_freq_cntr = &freq_controller_data.freq_controller; + ptmp_freq_cntr_pi = &freq_controller_data.freq_controller_pi; + + memcpy(&entry, entry_offset, + sizeof(struct vbios_fct_1x_entry)); + + if (!BIOS_GET_FIELD(entry.flags0, + NV_VBIOS_FCT_1X_ENTRY_FLAGS0_TYPE)) { + continue; + } + + freq_controller_data.board_obj.type = (u8)BIOS_GET_FIELD( + entry.flags0, NV_VBIOS_FCT_1X_ENTRY_FLAGS0_TYPE); + + ptmp_freq_cntr->controller_id = + (u8)BIOS_GET_FIELD(entry.param0, + NV_VBIOS_FCT_1X_ENTRY_PARAM0_ID); + + pclk_domain = CLK_CLK_DOMAIN_GET((&g->clk_pmu), + (u32)entry.clk_domain_idx); + freq_controller_data.freq_controller.clk_domain = + pclk_domain->api_domain; + + ptmp_freq_cntr->parts_freq_mode = + (u8)BIOS_GET_FIELD(entry.param0, + NV_VBIOS_FCT_1X_ENTRY_PARAM0_FREQ_MODE); + + /* Populate PI specific data */ + ptmp_freq_cntr_pi->slowdown_pct_min = + (u8)BIOS_GET_FIELD(entry.param1, + NV_VBIOS_FCT_1X_ENTRY_PARAM1_SLOWDOWN_PCT_MIN); + + ptmp_freq_cntr_pi->bpoison = + BIOS_GET_FIELD(entry.param1, + NV_VBIOS_FCT_1X_ENTRY_PARAM1_POISON); + + ptmp_freq_cntr_pi->prop_gain = + (s32)BIOS_GET_FIELD(entry.param2, + NV_VBIOS_FCT_1X_ENTRY_PARAM2_PROP_GAIN); + + ptmp_freq_cntr_pi->integ_gain = + (s32)BIOS_GET_FIELD(entry.param3, + NV_VBIOS_FCT_1X_ENTRY_PARAM3_INTEG_GAIN); + + ptmp_freq_cntr_pi->integ_decay = + (s32)BIOS_GET_FIELD(entry.param4, + NV_VBIOS_FCT_1X_ENTRY_PARAM4_INTEG_DECAY); + + ptmp_freq_cntr_pi->volt_delta_min = + (s32)BIOS_GET_FIELD(entry.param5, + NV_VBIOS_FCT_1X_ENTRY_PARAM5_VOLT_DELTA_MIN); + + ptmp_freq_cntr_pi->volt_delta_max = + (s32)BIOS_GET_FIELD(entry.param6, + NV_VBIOS_FCT_1X_ENTRY_PARAM6_VOLT_DELTA_MAX); + + ptmp_freq_cntr->freq_cap_noise_unaware_vmin_above = + (s16)BIOS_GET_FIELD(entry.param7, + NV_VBIOS_FCT_1X_ENTRY_PARAM7_FREQ_CAP_VF); + + ptmp_freq_cntr->freq_cap_noise_unaware_vmin_below = + (s16)BIOS_GET_FIELD(entry.param7, + NV_VBIOS_FCT_1X_ENTRY_PARAM7_FREQ_CAP_VMIN); + + ptmp_freq_cntr->freq_hyst_pos_mhz = + (s16)BIOS_GET_FIELD(entry.param8, + NV_VBIOS_FCT_1X_ENTRY_PARAM8_FREQ_HYST_POS); + ptmp_freq_cntr->freq_hyst_neg_mhz = + (s16)BIOS_GET_FIELD(entry.param8, + NV_VBIOS_FCT_1X_ENTRY_PARAM8_FREQ_HYST_NEG); + + if (ptmp_freq_cntr_pi->volt_delta_max < + ptmp_freq_cntr_pi->volt_delta_min) { + goto done; + } + + pclk_freq_cntr = clk_clk_freq_controller_construct(g, + (void *)&freq_controller_data); + + if (pclk_freq_cntr == NULL) { + nvgpu_err(g, + "unable to construct clock freq cntlr boardobj for %d", + entry_idx); + status = -EINVAL; + goto done; + } + + status = boardobjgrp_objinsert( + &pclk_freq_controllers->super.super, + (struct boardobj *)pclk_freq_cntr, entry_idx); + if (status) { + nvgpu_err(g, + "unable to insert clock freq cntlr boardobj for"); + status = -EINVAL; + goto done; + } + + } + +done: + return status; +} + +int clk_freq_controller_pmu_setup(struct gk20a *g) +{ + int status; + struct boardobjgrp *pboardobjgrp = NULL; + + nvgpu_log_info(g, " "); + + pboardobjgrp = &g->clk_pmu.clk_freq_controllers.super.super; + + if (!pboardobjgrp->bconstructed) { + return -EINVAL; + } + + status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); + + nvgpu_log_info(g, "Done"); + return status; +} + +static int _clk_freq_controller_devgrp_pmudata_instget(struct gk20a *g, + struct nv_pmu_boardobjgrp *pmuboardobjgrp, + struct nv_pmu_boardobj **ppboardobjpmudata, + u8 idx) +{ + struct nv_pmu_clk_clk_freq_controller_boardobj_grp_set *pgrp_set = + (struct nv_pmu_clk_clk_freq_controller_boardobj_grp_set *) + pmuboardobjgrp; + + nvgpu_log_info(g, " "); + + /*check whether pmuboardobjgrp has a valid boardobj in index*/ + if (((u32)BIT(idx) & + pgrp_set->hdr.data.super.obj_mask.super.data[0]) == 0) { + return -EINVAL; + } + + *ppboardobjpmudata = (struct nv_pmu_boardobj *) + &pgrp_set->objects[idx].data.board_obj; + nvgpu_log_info(g, " Done"); + return 0; +} + +static int _clk_freq_controllers_pmudatainit(struct gk20a *g, + struct boardobjgrp *pboardobjgrp, + struct nv_pmu_boardobjgrp_super *pboardobjgrppmu) +{ + struct nv_pmu_clk_clk_freq_controller_boardobjgrp_set_header *pset = + (struct nv_pmu_clk_clk_freq_controller_boardobjgrp_set_header *) + pboardobjgrppmu; + struct clk_freq_controllers *pcntrs = + (struct clk_freq_controllers *)pboardobjgrp; + int status = 0; + + status = boardobjgrp_pmudatainit_e32(g, pboardobjgrp, pboardobjgrppmu); + if (status) { + nvgpu_err(g, + "error updating pmu boardobjgrp for clk freq ctrs 0x%x", + status); + goto done; + } + pset->sampling_period_ms = pcntrs->sampling_period_ms; + pset->volt_policy_idx = pcntrs->volt_policy_idx; + +done: + return status; +} + +int clk_freq_controller_sw_setup(struct gk20a *g) +{ + int status = 0; + struct boardobjgrp *pboardobjgrp = NULL; + struct clk_freq_controllers *pclk_freq_controllers; + struct avfsfllobjs *pfllobjs = &(g->clk_pmu.avfs_fllobjs); + struct fll_device *pfll; + struct clk_freq_controller *pclkfreqctrl; + u8 i; + u8 j; + + nvgpu_log_info(g, " "); + + pclk_freq_controllers = &g->clk_pmu.clk_freq_controllers; + status = boardobjgrpconstruct_e32(g, &pclk_freq_controllers->super); + if (status) { + nvgpu_err(g, + "error creating boardobjgrp for clk FCT, status - 0x%x", + status); + goto done; + } + + pboardobjgrp = &g->clk_pmu.clk_freq_controllers.super.super; + + pboardobjgrp->pmudatainit = _clk_freq_controllers_pmudatainit; + pboardobjgrp->pmudatainstget = + _clk_freq_controller_devgrp_pmudata_instget; + pboardobjgrp->pmustatusinstget = NULL; + + /* Initialize mask to zero.*/ + boardobjgrpmask_e32_init(&pclk_freq_controllers->freq_ctrl_load_mask, + NULL); + + BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, CLK, CLK_FREQ_CONTROLLER); + + status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, + clk, CLK, clk_freq_controller, CLK_FREQ_CONTROLLER); + if (status) { + nvgpu_err(g, + "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", + status); + goto done; + } + + status = clk_get_freq_controller_table(g, pclk_freq_controllers); + if (status) { + nvgpu_err(g, "error reading freq controller table - 0x%x", + status); + goto done; + } + + BOARDOBJGRP_FOR_EACH(&(pclk_freq_controllers->super.super), + struct clk_freq_controller *, pclkfreqctrl, i) { + pfll = NULL; + j = 0; + BOARDOBJGRP_FOR_EACH(&(pfllobjs->super.super), + struct fll_device *, pfll, j) { + if (pclkfreqctrl->controller_id == pfll->id) { + pfll->freq_ctrl_idx = i; + break; + } + } + boardobjgrpmask_bitset(&pclk_freq_controllers-> + freq_ctrl_load_mask.super, i); + } +done: + nvgpu_log_info(g, " done status %x", status); + return status; +} diff --git a/include/clk/clk_freq_controller.h b/include/clk/clk_freq_controller.h new file mode 100644 index 0000000..7ae475c --- /dev/null +++ b/include/clk/clk_freq_controller.h @@ -0,0 +1,84 @@ +/* +* Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. +* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. +*/ + +#ifndef NVGPU_CLK_FREQ_CONTROLLER_H +#define NVGPU_CLK_FREQ_CONTROLLER_H + +#define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_ALL 0xFF +#define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_SYS 0x00 +#define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_LTC 0x01 +#define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_XBAR 0x02 +#define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC0 0x03 +#define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC1 0x04 +#define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC2 0x05 +#define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC3 0x06 +#define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC4 0x07 +#define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC5 0x08 +#define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPCS 0x09 + +#define CTRL_CLK_CLK_FREQ_CONTROLLER_MASK_UNICAST_GPC \ + (BIT(CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC0) | \ + BIT(CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC1) | \ + BIT(CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC2) | \ + BIT(CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC3) | \ + BIT(CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC4) | \ + BIT(CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC5)) + +#define CTRL_CLK_CLK_FREQ_CONTROLLER_TYPE_DISABLED 0x00 +#define CTRL_CLK_CLK_FREQ_CONTROLLER_TYPE_PI 0x01 + + +struct clk_freq_controller { + struct boardobj super; + u8 controller_id; + u8 parts_freq_mode; + bool bdisable; + u32 clk_domain; + s16 freq_cap_noise_unaware_vmin_above; + s16 freq_cap_noise_unaware_vmin_below; + s16 freq_hyst_pos_mhz; + s16 freq_hyst_neg_mhz; +}; + +struct clk_freq_controller_pi { + struct clk_freq_controller super; + s32 prop_gain; + s32 integ_gain; + s32 integ_decay; + s32 volt_delta_min; + s32 volt_delta_max; + u8 slowdown_pct_min; + bool bpoison; +}; + +struct clk_freq_controllers { + struct boardobjgrp_e32 super; + u32 sampling_period_ms; + struct boardobjgrpmask_e32 freq_ctrl_load_mask; + u8 volt_policy_idx; + void *pprereq_load; +}; + +int clk_freq_controller_sw_setup(struct gk20a *g); +int clk_freq_controller_pmu_setup(struct gk20a *g); + +#endif /* NVGPU_CLK_FREQ_CONTROLLER_H */ diff --git a/include/clk/clk_mclk.h b/include/clk/clk_mclk.h new file mode 100644 index 0000000..47c81d1 --- /dev/null +++ b/include/clk/clk_mclk.h @@ -0,0 +1,60 @@ +/* +* Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. +* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. +*/ + +#ifndef NVGPU_CLK_MCLK_H +#define NVGPU_CLK_MCLK_H + +#include + +#define GP106_MCLK_LOW_SPEED 0U +#define GP106_MCLK_MID_SPEED 1U +#define GP106_MCLK_HIGH_SPEED 2U +#define GP106_MCLK_NUM_SPEED 3U + +enum gk20a_mclk_speed { + gk20a_mclk_low_speed, + gk20a_mclk_mid_speed, + gk20a_mclk_high_speed, +}; + +struct clk_mclk_state { + u32 speed; + struct nvgpu_mutex mclk_lock; + struct nvgpu_mutex data_lock; + + u16 p5_min; + u16 p0_min; + + void *vreg_buf; + bool init; + +#ifdef CONFIG_DEBUG_FS + s64 switch_max; + s64 switch_min; + u64 switch_num; + s64 switch_avg; + s64 switch_std; + bool debugfs_set; +#endif +}; + +#endif /* NVGPU_CLK_MCLK_H */ diff --git a/include/clk/clk_prog.c b/include/clk/clk_prog.c new file mode 100644 index 0000000..9d44d6d --- /dev/null +++ b/include/clk/clk_prog.c @@ -0,0 +1,1152 @@ +/* + * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "clk.h" +#include "clk_prog.h" +#include "clk_vf_point.h" +#include "boardobj/boardobjgrp.h" +#include "boardobj/boardobjgrp_e32.h" +#include "gp106/bios_gp106.h" +#include "ctrl/ctrlclk.h" +#include "ctrl/ctrlvolt.h" + +static struct clk_prog *construct_clk_prog(struct gk20a *g, void *pargs); +static int devinit_get_clk_prog_table(struct gk20a *g, + struct clk_progs *pprogobjs); +static vf_flatten vfflatten_prog_1x_master; +static vf_lookup vflookup_prog_1x_master; +static get_fpoints getfpoints_prog_1x_master; +static get_slaveclk getslaveclk_prog_1x_master; + +static int _clk_progs_pmudatainit(struct gk20a *g, + struct boardobjgrp *pboardobjgrp, + struct nv_pmu_boardobjgrp_super *pboardobjgrppmu) +{ + struct nv_pmu_clk_clk_prog_boardobjgrp_set_header *pset = + (struct nv_pmu_clk_clk_prog_boardobjgrp_set_header *) + pboardobjgrppmu; + struct clk_progs *pprogs = (struct clk_progs *)pboardobjgrp; + u32 status = 0; + + status = boardobjgrp_pmudatainit_e32(g, pboardobjgrp, pboardobjgrppmu); + if (status) { + nvgpu_err(g, "error updating pmu boardobjgrp for clk prog 0x%x", + status); + goto done; + } + pset->slave_entry_count = pprogs->slave_entry_count; + pset->vf_entry_count = pprogs->vf_entry_count; + +done: + return status; +} + +static int _clk_progs_pmudata_instget(struct gk20a *g, + struct nv_pmu_boardobjgrp *pmuboardobjgrp, + struct nv_pmu_boardobj **ppboardobjpmudata, + u8 idx) +{ + struct nv_pmu_clk_clk_prog_boardobj_grp_set *pgrp_set = + (struct nv_pmu_clk_clk_prog_boardobj_grp_set *)pmuboardobjgrp; + + nvgpu_log_info(g, " "); + + /*check whether pmuboardobjgrp has a valid boardobj in index*/ + if (((u32)BIT(idx) & + pgrp_set->hdr.data.super.obj_mask.super.data[0]) == 0) { + return -EINVAL; + } + + *ppboardobjpmudata = (struct nv_pmu_boardobj *) + &pgrp_set->objects[idx].data.board_obj; + nvgpu_log_info(g, " Done"); + return 0; +} + +int clk_prog_sw_setup(struct gk20a *g) +{ + int status; + struct boardobjgrp *pboardobjgrp = NULL; + struct clk_progs *pclkprogobjs; + + nvgpu_log_info(g, " "); + + status = boardobjgrpconstruct_e255(g, &g->clk_pmu.clk_progobjs.super); + if (status) { + nvgpu_err(g, + "error creating boardobjgrp for clk prog, status - 0x%x", + status); + goto done; + } + + pboardobjgrp = &g->clk_pmu.clk_progobjs.super.super; + pclkprogobjs = &(g->clk_pmu.clk_progobjs); + + BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, CLK, CLK_PROG); + + status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, + clk, CLK, clk_prog, CLK_PROG); + if (status) { + nvgpu_err(g, + "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", + status); + goto done; + } + + pboardobjgrp->pmudatainit = _clk_progs_pmudatainit; + pboardobjgrp->pmudatainstget = _clk_progs_pmudata_instget; + + status = devinit_get_clk_prog_table(g, pclkprogobjs); + if (status) { + goto done; + } + + status = clk_domain_clk_prog_link(g, &g->clk_pmu); + if (status) { + nvgpu_err(g, "error constructing VF point board objects"); + goto done; + } + + +done: + nvgpu_log_info(g, " done status %x", status); + return status; +} + +int clk_prog_pmu_setup(struct gk20a *g) +{ + int status; + struct boardobjgrp *pboardobjgrp = NULL; + + nvgpu_log_info(g, " "); + + pboardobjgrp = &g->clk_pmu.clk_progobjs.super.super; + + if (!pboardobjgrp->bconstructed) { + return -EINVAL; + } + + status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); + + nvgpu_log_info(g, "Done"); + return status; +} + +static int devinit_get_clk_prog_table(struct gk20a *g, + struct clk_progs *pclkprogobjs) +{ + int status = 0; + u8 *clkprogs_tbl_ptr = NULL; + struct vbios_clock_programming_table_1x_header header = { 0 }; + struct vbios_clock_programming_table_1x_entry prog = { 0 }; + struct vbios_clock_programming_table_1x_slave_entry slaveprog = { 0 }; + struct vbios_clock_programming_table_1x_vf_entry vfprog = { 0 }; + u8 *entry = NULL; + u8 *slaveentry = NULL; + u8 *vfentry = NULL; + u32 i, j = 0; + struct clk_prog *pprog; + u8 prog_type; + u32 szfmt = VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_SIZE_0D; + u32 hszfmt = VBIOS_CLOCK_PROGRAMMING_TABLE_1X_HEADER_SIZE_08; + u32 slaveszfmt = VBIOS_CLOCK_PROGRAMMING_TABLE_1X_SLAVE_ENTRY_SIZE_03; + u32 vfszfmt = VBIOS_CLOCK_PROGRAMMING_TABLE_1X_VF_ENTRY_SIZE_02; + struct ctrl_clk_clk_prog_1x_master_vf_entry + vfentries[CTRL_CLK_CLK_PROG_1X_MASTER_VF_ENTRY_MAX_ENTRIES]; + struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry + ratioslaveentries[CTRL_CLK_PROG_1X_MASTER_MAX_SLAVE_ENTRIES]; + struct ctrl_clk_clk_prog_1x_master_table_slave_entry + tableslaveentries[CTRL_CLK_PROG_1X_MASTER_MAX_SLAVE_ENTRIES]; + union { + struct boardobj board_obj; + struct clk_prog clkprog; + struct clk_prog_1x v1x; + struct clk_prog_1x_master v1x_master; + struct clk_prog_1x_master_ratio v1x_master_ratio; + struct clk_prog_1x_master_table v1x_master_table; + } prog_data; + + nvgpu_log_info(g, " "); + + clkprogs_tbl_ptr = (u8 *)nvgpu_bios_get_perf_table_ptrs(g, + g->bios.clock_token, CLOCK_PROGRAMMING_TABLE); + if (clkprogs_tbl_ptr == NULL) { + status = -EINVAL; + goto done; + } + + memcpy(&header, clkprogs_tbl_ptr, hszfmt); + if (header.header_size < hszfmt) { + status = -EINVAL; + goto done; + } + hszfmt = header.header_size; + + if (header.entry_size <= VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_SIZE_05) { + szfmt = header.entry_size; + } else if (header.entry_size <= VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_SIZE_0D) { + szfmt = header.entry_size; + } else { + status = -EINVAL; + goto done; + } + + if (header.vf_entry_size < vfszfmt) { + status = -EINVAL; + goto done; + } + vfszfmt = header.vf_entry_size; + if (header.slave_entry_size < slaveszfmt) { + status = -EINVAL; + goto done; + } + slaveszfmt = header.slave_entry_size; + if (header.vf_entry_count > CTRL_CLK_CLK_DELTA_MAX_VOLT_RAILS) { + status = -EINVAL; + goto done; + } + + pclkprogobjs->slave_entry_count = header.slave_entry_count; + pclkprogobjs->vf_entry_count = header.vf_entry_count; + + for (i = 0; i < header.entry_count; i++) { + memset(&prog_data, 0x0, (u32)sizeof(prog_data)); + + /* Read table entries*/ + entry = clkprogs_tbl_ptr + hszfmt + + (i * (szfmt + (header.slave_entry_count * slaveszfmt) + + (header.vf_entry_count * vfszfmt))); + + memcpy(&prog, entry, szfmt); + memset(vfentries, 0xFF, + sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) * + CTRL_CLK_CLK_PROG_1X_MASTER_VF_ENTRY_MAX_ENTRIES); + memset(ratioslaveentries, 0xFF, + sizeof(struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry) * + CTRL_CLK_PROG_1X_MASTER_MAX_SLAVE_ENTRIES); + memset(tableslaveentries, 0xFF, + sizeof(struct ctrl_clk_clk_prog_1x_master_table_slave_entry) * + CTRL_CLK_PROG_1X_MASTER_MAX_SLAVE_ENTRIES); + prog_type = (u8)BIOS_GET_FIELD(prog.flags0, + NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_SOURCE); + + switch (prog_type) { + case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_SOURCE_PLL: + prog_data.v1x.source = CTRL_CLK_PROG_1X_SOURCE_PLL; + prog_data.v1x.source_data.pll.pll_idx = + (u8)BIOS_GET_FIELD(prog.param0, + NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_PARAM0_PLL_PLL_INDEX); + prog_data.v1x.source_data.pll.freq_step_size_mhz = + (u8)BIOS_GET_FIELD(prog.param1, + NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_PARAM1_PLL_FREQ_STEP_SIZE); + break; + + case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_SOURCE_ONE_SOURCE: + prog_data.v1x.source = CTRL_CLK_PROG_1X_SOURCE_ONE_SOURCE; + break; + + case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_SOURCE_FLL: + prog_data.v1x.source = CTRL_CLK_PROG_1X_SOURCE_FLL; + break; + + default: + nvgpu_err(g, "invalid source %d", prog_type); + status = -EINVAL; + goto done; + } + + prog_data.v1x.freq_max_mhz = (u16)prog.freq_max_mhz; + + prog_type = (u8)BIOS_GET_FIELD(prog.flags0, + NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE); + + vfentry = entry + szfmt + + header.slave_entry_count * slaveszfmt; + slaveentry = entry + szfmt; + switch (prog_type) { + case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_RATIO: + case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_TABLE: + prog_data.v1x_master.b_o_c_o_v_enabled = false; + for (j = 0; j < header.vf_entry_count; j++) { + memcpy(&vfprog, vfentry, vfszfmt); + + vfentries[j].vfe_idx = (u8)vfprog.vfe_idx; + if (CTRL_CLK_PROG_1X_SOURCE_FLL == + prog_data.v1x.source) { + vfentries[j].gain_vfe_idx = (u8)BIOS_GET_FIELD( + vfprog.param0, + NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_VF_ENTRY_PARAM0_FLL_GAIN_VFE_IDX); + } else { + vfentries[j].gain_vfe_idx = CTRL_BOARDOBJ_IDX_INVALID; + } + vfentry += vfszfmt; + } + + prog_data.v1x_master.p_vf_entries = vfentries; + + for (j = 0; j < header.slave_entry_count; j++) { + memcpy(&slaveprog, slaveentry, slaveszfmt); + + switch (prog_type) { + case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_RATIO: + ratioslaveentries[j].clk_dom_idx = + (u8)slaveprog.clk_dom_idx; + ratioslaveentries[j].ratio = (u8) + BIOS_GET_FIELD(slaveprog.param0, + NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_SLAVE_ENTRY_PARAM0_MASTER_RATIO_RATIO); + break; + + case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_TABLE: + tableslaveentries[j].clk_dom_idx = + (u8)slaveprog.clk_dom_idx; + tableslaveentries[j].freq_mhz = + (u16)BIOS_GET_FIELD(slaveprog.param0, + NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_SLAVE_ENTRY_PARAM0_MASTER_TABLE_FREQ); + break; + } + slaveentry += slaveszfmt; + } + + switch (prog_type) { + case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_RATIO: + prog_data.board_obj.type = CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO; + prog_data.v1x_master_ratio.p_slave_entries = + ratioslaveentries; + break; + + case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_TABLE: + prog_data.board_obj.type = CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_TABLE; + + prog_data.v1x_master_table.p_slave_entries = + tableslaveentries; + break; + + } + break; + + case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_SLAVE: + prog_data.board_obj.type = CTRL_CLK_CLK_PROG_TYPE_1X; + break; + + + default: + nvgpu_err(g, "source issue %d", prog_type); + status = -EINVAL; + goto done; + } + + pprog = construct_clk_prog(g, (void *)&prog_data); + if (pprog == NULL) { + nvgpu_err(g, + "error constructing clk_prog boardobj %d", i); + status = -EINVAL; + goto done; + } + + status = boardobjgrp_objinsert(&pclkprogobjs->super.super, + (struct boardobj *)pprog, i); + if (status) { + nvgpu_err(g, "error adding clk_prog boardobj %d", i); + status = -EINVAL; + goto done; + } + } +done: + nvgpu_log_info(g, " done status %x", status); + return status; +} + +static int _clk_prog_pmudatainit_super(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + + nvgpu_log_info(g, " "); + + status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); + return status; +} + +static int _clk_prog_pmudatainit_1x(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_prog_1x *pclk_prog_1x; + struct nv_pmu_clk_clk_prog_1x_boardobj_set *pset; + + nvgpu_log_info(g, " "); + + status = _clk_prog_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pclk_prog_1x = (struct clk_prog_1x *)board_obj_ptr; + + pset = (struct nv_pmu_clk_clk_prog_1x_boardobj_set *) + ppmudata; + + pset->source = pclk_prog_1x->source; + pset->freq_max_mhz = pclk_prog_1x->freq_max_mhz; + pset->source_data = pclk_prog_1x->source_data; + + return status; +} + +static int _clk_prog_pmudatainit_1x_master(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_prog_1x_master *pclk_prog_1x_master; + struct nv_pmu_clk_clk_prog_1x_master_boardobj_set *pset; + u32 vfsize = sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) * + g->clk_pmu.clk_progobjs.vf_entry_count; + + nvgpu_log_info(g, " "); + + status = _clk_prog_pmudatainit_1x(g, board_obj_ptr, ppmudata); + + pclk_prog_1x_master = + (struct clk_prog_1x_master *)board_obj_ptr; + + pset = (struct nv_pmu_clk_clk_prog_1x_master_boardobj_set *) + ppmudata; + + memcpy(pset->vf_entries, pclk_prog_1x_master->p_vf_entries, vfsize); + + pset->b_o_c_o_v_enabled = pclk_prog_1x_master->b_o_c_o_v_enabled; + pset->source_data = pclk_prog_1x_master->source_data; + + memcpy(&pset->deltas, &pclk_prog_1x_master->deltas, + (u32) sizeof(struct ctrl_clk_clk_delta)); + + return status; +} + +static int _clk_prog_pmudatainit_1x_master_ratio(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_prog_1x_master_ratio *pclk_prog_1x_master_ratio; + struct nv_pmu_clk_clk_prog_1x_master_ratio_boardobj_set *pset; + u32 slavesize = sizeof(struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry) * + g->clk_pmu.clk_progobjs.slave_entry_count; + + nvgpu_log_info(g, " "); + + status = _clk_prog_pmudatainit_1x_master(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pclk_prog_1x_master_ratio = + (struct clk_prog_1x_master_ratio *)board_obj_ptr; + + pset = (struct nv_pmu_clk_clk_prog_1x_master_ratio_boardobj_set *) + ppmudata; + + memcpy(pset->slave_entries, + pclk_prog_1x_master_ratio->p_slave_entries, slavesize); + + return status; +} + +static int _clk_prog_pmudatainit_1x_master_table(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_prog_1x_master_table *pclk_prog_1x_master_table; + struct nv_pmu_clk_clk_prog_1x_master_table_boardobj_set *pset; + u32 slavesize = sizeof(struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry) * + g->clk_pmu.clk_progobjs.slave_entry_count; + + nvgpu_log_info(g, " "); + + status = _clk_prog_pmudatainit_1x_master(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pclk_prog_1x_master_table = + (struct clk_prog_1x_master_table *)board_obj_ptr; + + pset = (struct nv_pmu_clk_clk_prog_1x_master_table_boardobj_set *) + ppmudata; + memcpy(pset->slave_entries, + pclk_prog_1x_master_table->p_slave_entries, slavesize); + + return status; +} + +static u32 _clk_prog_1x_master_rail_construct_vf_point(struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_prog_1x_master *p1xmaster, + struct ctrl_clk_clk_prog_1x_master_vf_entry *p_vf_rail, + struct clk_vf_point *p_vf_point_tmp, + u8 *p_vf_point_idx) +{ + struct clk_vf_point *p_vf_point; + u32 status; + + nvgpu_log_info(g, " "); + + p_vf_point = construct_clk_vf_point(g, (void *)p_vf_point_tmp); + if (p_vf_point == NULL) { + status = -ENOMEM; + goto done; + } + status = pclk->clk_vf_pointobjs.super.super.objinsert( + &pclk->clk_vf_pointobjs.super.super, + &p_vf_point->super, + *p_vf_point_idx); + if (status) { + goto done; + } + + p_vf_rail->vf_point_idx_last = (*p_vf_point_idx)++; + +done: + nvgpu_log_info(g, "done status %x", status); + return status; +} + +static int clk_prog_construct_super(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct clk_prog *pclkprog; + int status = 0; + + status = boardobj_construct_super(g, ppboardobj, + size, pargs); + if (status) { + return -EINVAL; + } + + pclkprog = (struct clk_prog *)*ppboardobj; + + pclkprog->super.pmudatainit = + _clk_prog_pmudatainit_super; + return status; +} + + +static int clk_prog_construct_1x(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct clk_prog_1x *pclkprog; + struct clk_prog_1x *ptmpprog = + (struct clk_prog_1x *)pargs; + int status = 0; + + nvgpu_log_info(g, " "); + ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_PROG_TYPE_1X); + status = clk_prog_construct_super(g, ppboardobj, size, pargs); + if (status) { + return -EINVAL; + } + + pclkprog = (struct clk_prog_1x *)*ppboardobj; + + pclkprog->super.super.pmudatainit = + _clk_prog_pmudatainit_1x; + + pclkprog->source = ptmpprog->source; + pclkprog->freq_max_mhz = ptmpprog->freq_max_mhz; + pclkprog->source_data = ptmpprog->source_data; + + return status; +} + +static int clk_prog_construct_1x_master(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct clk_prog_1x_master *pclkprog; + struct clk_prog_1x_master *ptmpprog = + (struct clk_prog_1x_master *)pargs; + int status = 0; + u32 vfsize = sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) * + g->clk_pmu.clk_progobjs.vf_entry_count; + u8 railidx; + + nvgpu_log_info(g, " type - %x", BOARDOBJ_GET_TYPE(pargs)); + + ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_PROG_TYPE_1X_MASTER); + status = clk_prog_construct_1x(g, ppboardobj, size, pargs); + if (status) { + return -EINVAL; + } + + pclkprog = (struct clk_prog_1x_master *)*ppboardobj; + + pclkprog->super.super.super.pmudatainit = + _clk_prog_pmudatainit_1x_master; + + pclkprog->vfflatten = + vfflatten_prog_1x_master; + + pclkprog->vflookup = + vflookup_prog_1x_master; + + pclkprog->getfpoints = + getfpoints_prog_1x_master; + + pclkprog->getslaveclk = + getslaveclk_prog_1x_master; + + pclkprog->p_vf_entries = (struct ctrl_clk_clk_prog_1x_master_vf_entry *) + nvgpu_kzalloc(g, vfsize); + if (!pclkprog->p_vf_entries) + return -ENOMEM; + + memcpy(pclkprog->p_vf_entries, ptmpprog->p_vf_entries, vfsize); + + pclkprog->b_o_c_o_v_enabled = ptmpprog->b_o_c_o_v_enabled; + + for (railidx = 0; + railidx < g->clk_pmu.clk_progobjs.vf_entry_count; + railidx++) { + pclkprog->p_vf_entries[railidx].vf_point_idx_first = + CTRL_CLK_CLK_VF_POINT_IDX_INVALID; + pclkprog->p_vf_entries[railidx].vf_point_idx_last = + CTRL_CLK_CLK_VF_POINT_IDX_INVALID; + } + + return status; +} + +static int clk_prog_construct_1x_master_ratio(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct clk_prog_1x_master_ratio *pclkprog; + struct clk_prog_1x_master_ratio *ptmpprog = + (struct clk_prog_1x_master_ratio *)pargs; + int status = 0; + u32 slavesize = sizeof(struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry) * + g->clk_pmu.clk_progobjs.slave_entry_count; + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO) { + return -EINVAL; + } + + ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO); + status = clk_prog_construct_1x_master(g, ppboardobj, size, pargs); + if (status) { + return -EINVAL; + } + + pclkprog = (struct clk_prog_1x_master_ratio *)*ppboardobj; + + pclkprog->super.super.super.super.pmudatainit = + _clk_prog_pmudatainit_1x_master_ratio; + + pclkprog->p_slave_entries = + (struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry *) + nvgpu_kzalloc(g, slavesize); + if (!pclkprog->p_slave_entries) { + return -ENOMEM; + } + + memset(pclkprog->p_slave_entries, CTRL_CLK_CLK_DOMAIN_INDEX_INVALID, + slavesize); + + memcpy(pclkprog->p_slave_entries, ptmpprog->p_slave_entries, slavesize); + + return status; +} + +static int clk_prog_construct_1x_master_table(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct clk_prog_1x_master_table *pclkprog; + struct clk_prog_1x_master_table *ptmpprog = + (struct clk_prog_1x_master_table *)pargs; + int status = 0; + u32 slavesize = sizeof(struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry) * + g->clk_pmu.clk_progobjs.slave_entry_count; + + nvgpu_log_info(g, "type - %x", BOARDOBJ_GET_TYPE(pargs)); + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_TABLE) { + return -EINVAL; + } + + ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_TABLE); + status = clk_prog_construct_1x_master(g, ppboardobj, size, pargs); + if (status) { + return -EINVAL; + } + + pclkprog = (struct clk_prog_1x_master_table *)*ppboardobj; + + pclkprog->super.super.super.super.pmudatainit = + _clk_prog_pmudatainit_1x_master_table; + + pclkprog->p_slave_entries = + (struct ctrl_clk_clk_prog_1x_master_table_slave_entry *) + nvgpu_kzalloc(g, slavesize); + + if (!pclkprog->p_slave_entries) { + status = -ENOMEM; + goto exit; + } + + memset(pclkprog->p_slave_entries, CTRL_CLK_CLK_DOMAIN_INDEX_INVALID, + slavesize); + + memcpy(pclkprog->p_slave_entries, ptmpprog->p_slave_entries, slavesize); + +exit: + if (status) { + (*ppboardobj)->destruct(*ppboardobj); + } + + return status; +} + +static struct clk_prog *construct_clk_prog(struct gk20a *g, void *pargs) +{ + struct boardobj *board_obj_ptr = NULL; + int status; + + nvgpu_log_info(g, " type - %x", BOARDOBJ_GET_TYPE(pargs)); + switch (BOARDOBJ_GET_TYPE(pargs)) { + case CTRL_CLK_CLK_PROG_TYPE_1X: + status = clk_prog_construct_1x(g, &board_obj_ptr, + sizeof(struct clk_prog_1x), pargs); + break; + + case CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_TABLE: + status = clk_prog_construct_1x_master_table(g, &board_obj_ptr, + sizeof(struct clk_prog_1x_master_table), pargs); + break; + + case CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO: + status = clk_prog_construct_1x_master_ratio(g, &board_obj_ptr, + sizeof(struct clk_prog_1x_master_ratio), pargs); + break; + + default: + return NULL; + } + + if (status) { + if (board_obj_ptr) { + board_obj_ptr->destruct(board_obj_ptr); + } + return NULL; + } + + nvgpu_log_info(g, " Done"); + + return (struct clk_prog *)board_obj_ptr; +} + +static u32 vfflatten_prog_1x_master(struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_prog_1x_master *p1xmaster, + u8 clk_domain_idx, u16 *pfreqmaxlastmhz) +{ + struct ctrl_clk_clk_prog_1x_master_vf_entry *p_vf_rail; + union { + struct boardobj board_obj; + struct clk_vf_point vf_point; + struct clk_vf_point_freq freq; + struct clk_vf_point_volt volt; + } vf_point_data; + u32 status = 0; + u8 step_count; + u8 freq_step_size_mhz = 0; + u8 vf_point_idx; + u8 vf_rail_idx; + + nvgpu_log_info(g, " "); + memset(&vf_point_data, 0x0, sizeof(vf_point_data)); + + vf_point_idx = BOARDOBJGRP_NEXT_EMPTY_IDX( + &pclk->clk_vf_pointobjs.super.super); + + for (vf_rail_idx = 0; + vf_rail_idx < pclk->clk_progobjs.vf_entry_count; + vf_rail_idx++) { + u32 voltage_min_uv; + u32 voltage_step_size_uv; + u8 i; + + p_vf_rail = &p1xmaster->p_vf_entries[vf_rail_idx]; + if (p_vf_rail->vfe_idx == CTRL_BOARDOBJ_IDX_INVALID) { + continue; + } + + p_vf_rail->vf_point_idx_first = vf_point_idx; + + vf_point_data.vf_point.vfe_equ_idx = p_vf_rail->vfe_idx; + vf_point_data.vf_point.volt_rail_idx = vf_rail_idx; + + step_count = 0; + + switch (p1xmaster->super.source) { + case CTRL_CLK_PROG_1X_SOURCE_PLL: + freq_step_size_mhz = + p1xmaster->super.source_data.pll.freq_step_size_mhz; + step_count = (freq_step_size_mhz == 0) ? 0 : + (p1xmaster->super.freq_max_mhz - *pfreqmaxlastmhz - 1) / + freq_step_size_mhz; + /* Intentional fall-through.*/ + + case CTRL_CLK_PROG_1X_SOURCE_ONE_SOURCE: + vf_point_data.board_obj.type = + CTRL_CLK_CLK_VF_POINT_TYPE_FREQ; + do { + clkvfpointfreqmhzset(g, &vf_point_data.vf_point, + p1xmaster->super.freq_max_mhz - + step_count * freq_step_size_mhz); + + status = _clk_prog_1x_master_rail_construct_vf_point(g, pclk, + p1xmaster, p_vf_rail, + &vf_point_data.vf_point, &vf_point_idx); + if (status) { + goto done; + } + } while (step_count-- > 0); + break; + + case CTRL_CLK_PROG_1X_SOURCE_FLL: + voltage_min_uv = CLK_FLL_LUT_MIN_VOLTAGE_UV(pclk); + voltage_step_size_uv = CLK_FLL_LUT_STEP_SIZE_UV(pclk); + step_count = CLK_FLL_LUT_VF_NUM_ENTRIES(pclk); + + /* FLL sources use a voltage-based VF_POINT.*/ + vf_point_data.board_obj.type = + CTRL_CLK_CLK_VF_POINT_TYPE_VOLT; + for (i = 0; i < step_count; i++) { + vf_point_data.volt.source_voltage_uv = + voltage_min_uv + i * voltage_step_size_uv; + + status = _clk_prog_1x_master_rail_construct_vf_point(g, pclk, + p1xmaster, p_vf_rail, + &vf_point_data.vf_point, &vf_point_idx); + if (status) { + goto done; + } + } + break; + } + } + + *pfreqmaxlastmhz = p1xmaster->super.freq_max_mhz; + +done: + nvgpu_log_info(g, "done status %x", status); + return status; +} + +static u32 vflookup_prog_1x_master +( + struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_prog_1x_master *p1xmaster, + u8 *slave_clk_domain, + u16 *pclkmhz, + u32 *pvoltuv, + u8 rail +) +{ + int j; + struct ctrl_clk_clk_prog_1x_master_vf_entry + *pvfentry; + struct clk_vf_point *pvfpoint; + struct clk_progs *pclkprogobjs; + struct clk_prog_1x_master_ratio *p1xmasterratio; + u16 clkmhz; + u32 voltuv; + u8 slaveentrycount; + int i; + struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry *pslaveents; + + if ((*pclkmhz != 0) && (*pvoltuv != 0)) { + return -EINVAL; + } + + pclkprogobjs = &(pclk->clk_progobjs); + + slaveentrycount = pclkprogobjs->slave_entry_count; + + if (pclkprogobjs->vf_entry_count > + CTRL_CLK_CLK_PROG_1X_MASTER_VF_ENTRY_MAX_ENTRIES) { + return -EINVAL; + } + + if (rail >= pclkprogobjs->vf_entry_count) { + return -EINVAL; + } + + pvfentry = p1xmaster->p_vf_entries; + + pvfentry = (struct ctrl_clk_clk_prog_1x_master_vf_entry *)( + (u8 *)pvfentry + + (sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) * + rail)); + + clkmhz = *pclkmhz; + voltuv = *pvoltuv; + + /*if domain is slave domain and freq is input + then derive master clk */ + if ((slave_clk_domain != NULL) && (*pclkmhz != 0)) { + if (p1xmaster->super.super.super.implements(g, + &p1xmaster->super.super.super, + CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO)) { + + p1xmasterratio = + (struct clk_prog_1x_master_ratio *)p1xmaster; + pslaveents = p1xmasterratio->p_slave_entries; + for (i = 0; i < slaveentrycount; i++) { + if (pslaveents->clk_dom_idx == + *slave_clk_domain) { + break; + } + pslaveents++; + } + if (i == slaveentrycount) { + return -EINVAL; + } + clkmhz = (clkmhz * 100)/pslaveents->ratio; + } else { + /* only support ratio for now */ + return -EINVAL; + } + } + + /* if both volt and clks are zero simply print*/ + if ((*pvoltuv == 0) && (*pclkmhz == 0)) { + for (j = pvfentry->vf_point_idx_first; + j <= pvfentry->vf_point_idx_last; j++) { + pvfpoint = CLK_CLK_VF_POINT_GET(pclk, j); + nvgpu_err(g, "v %x c %x", + clkvfpointvoltageuvget(g, pvfpoint), + clkvfpointfreqmhzget(g, pvfpoint)); + } + return -EINVAL; + } + /* start looking up f for v for v for f */ + /* looking for volt? */ + if (*pvoltuv == 0) { + pvfpoint = CLK_CLK_VF_POINT_GET(pclk, + pvfentry->vf_point_idx_last); + /* above range? */ + if (clkmhz > clkvfpointfreqmhzget(g, pvfpoint)) { + return -EINVAL; + } + + for (j = pvfentry->vf_point_idx_last; + j >= pvfentry->vf_point_idx_first; j--) { + pvfpoint = CLK_CLK_VF_POINT_GET(pclk, j); + if (clkmhz <= clkvfpointfreqmhzget(g, pvfpoint)) { + voltuv = clkvfpointvoltageuvget(g, pvfpoint); + } else { + break; + } + } + } else { /* looking for clk? */ + + pvfpoint = CLK_CLK_VF_POINT_GET(pclk, + pvfentry->vf_point_idx_first); + /* below range? */ + if (voltuv < clkvfpointvoltageuvget(g, pvfpoint)) { + return -EINVAL; + } + + for (j = pvfentry->vf_point_idx_first; + j <= pvfentry->vf_point_idx_last; j++) { + pvfpoint = CLK_CLK_VF_POINT_GET(pclk, j); + if (voltuv >= clkvfpointvoltageuvget(g, pvfpoint)) { + clkmhz = clkvfpointfreqmhzget(g, pvfpoint); + } else { + break; + } + } + } + + /*if domain is slave domain and freq was looked up + then derive slave clk */ + if ((slave_clk_domain != NULL) && (*pclkmhz == 0)) { + if (p1xmaster->super.super.super.implements(g, + &p1xmaster->super.super.super, + CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO)) { + + p1xmasterratio = + (struct clk_prog_1x_master_ratio *)p1xmaster; + pslaveents = p1xmasterratio->p_slave_entries; + for (i = 0; i < slaveentrycount; i++) { + if (pslaveents->clk_dom_idx == + *slave_clk_domain) { + break; + } + pslaveents++; + } + if (i == slaveentrycount) { + return -EINVAL; + } + clkmhz = (clkmhz * pslaveents->ratio)/100; + } else { + /* only support ratio for now */ + return -EINVAL; + } + } + *pclkmhz = clkmhz; + *pvoltuv = voltuv; + if ((clkmhz == 0) || (voltuv == 0)) { + return -EINVAL; + } + return 0; +} + +static u32 getfpoints_prog_1x_master +( + struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_prog_1x_master *p1xmaster, + u32 *pfpointscount, + u16 **ppfreqpointsinmhz, + u8 rail +) +{ + + struct ctrl_clk_clk_prog_1x_master_vf_entry + *pvfentry; + struct clk_vf_point *pvfpoint; + struct clk_progs *pclkprogobjs; + u8 j; + u32 fpointscount = 0; + + if (pfpointscount == NULL) { + return -EINVAL; + } + + pclkprogobjs = &(pclk->clk_progobjs); + + if (pclkprogobjs->vf_entry_count > + CTRL_CLK_CLK_PROG_1X_MASTER_VF_ENTRY_MAX_ENTRIES) { + return -EINVAL; + } + + if (rail >= pclkprogobjs->vf_entry_count) { + return -EINVAL; + } + + pvfentry = p1xmaster->p_vf_entries; + + pvfentry = (struct ctrl_clk_clk_prog_1x_master_vf_entry *)( + (u8 *)pvfentry + + (sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) * + (rail+1))); + + fpointscount = pvfentry->vf_point_idx_last - + pvfentry->vf_point_idx_first + 1; + + /* if pointer for freq data is NULL simply return count */ + if (*ppfreqpointsinmhz == NULL) { + goto done; + } + + if (fpointscount > *pfpointscount) { + return -ENOMEM; + } + for (j = pvfentry->vf_point_idx_first; + j <= pvfentry->vf_point_idx_last; j++) { + pvfpoint = CLK_CLK_VF_POINT_GET(pclk, j); + **ppfreqpointsinmhz = clkvfpointfreqmhzget(g, pvfpoint); + (*ppfreqpointsinmhz)++; + } +done: + *pfpointscount = fpointscount; + return 0; +} + +static int getslaveclk_prog_1x_master(struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_prog_1x_master *p1xmaster, + u8 slave_clk_domain, + u16 *pclkmhz, + u16 masterclkmhz +) +{ + struct clk_progs *pclkprogobjs; + struct clk_prog_1x_master_ratio *p1xmasterratio; + u8 slaveentrycount; + u8 i; + struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry *pslaveents; + + if (pclkmhz == NULL) { + return -EINVAL; + } + + if (masterclkmhz == 0) { + return -EINVAL; + } + + *pclkmhz = 0; + pclkprogobjs = &(pclk->clk_progobjs); + + slaveentrycount = pclkprogobjs->slave_entry_count; + + if (p1xmaster->super.super.super.implements(g, + &p1xmaster->super.super.super, + CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO)) { + p1xmasterratio = + (struct clk_prog_1x_master_ratio *)p1xmaster; + pslaveents = p1xmasterratio->p_slave_entries; + for (i = 0; i < slaveentrycount; i++) { + if (pslaveents->clk_dom_idx == + slave_clk_domain) { + break; + } + pslaveents++; + } + if (i == slaveentrycount) { + return -EINVAL; + } + *pclkmhz = (masterclkmhz * pslaveents->ratio)/100; + } else { + /* only support ratio for now */ + return -EINVAL; + } + return 0; +} diff --git a/include/clk/clk_prog.h b/include/clk/clk_prog.h new file mode 100644 index 0000000..af6368f --- /dev/null +++ b/include/clk/clk_prog.h @@ -0,0 +1,100 @@ +/* +* Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. +* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. +*/ + +#ifndef NVGPU_CLK_PROG_H +#define NVGPU_CLK_PROG_H +#include "ctrl/ctrlclk.h" +#include "ctrl/ctrlboardobj.h" +#include +#include "boardobj/boardobjgrp_e32.h" +#include "boardobj/boardobjgrp_e255.h" +#include "boardobj/boardobjgrpmask.h" + +int clk_prog_sw_setup(struct gk20a *g); +int clk_prog_pmu_setup(struct gk20a *g); +struct clk_prog_1x_master; + +typedef u32 vf_flatten(struct gk20a *g, struct clk_pmupstate *pclk, + struct clk_prog_1x_master *p1xmaster, + u8 clk_domain_idx, u16 *pfreqmaxlastmhz); + +typedef u32 vf_lookup(struct gk20a *g, struct clk_pmupstate *pclk, + struct clk_prog_1x_master *p1xmaster, + u8 *slave_clk_domain_idx, u16 *pclkmhz, + u32 *pvoltuv, u8 rail); + +typedef int get_slaveclk(struct gk20a *g, struct clk_pmupstate *pclk, + struct clk_prog_1x_master *p1xmaster, + u8 slave_clk_domain_idx, u16 *pclkmhz, + u16 masterclkmhz); + +typedef u32 get_fpoints(struct gk20a *g, struct clk_pmupstate *pclk, + struct clk_prog_1x_master *p1xmaster, + u32 *pfpointscount, + u16 **ppfreqpointsinmhz, u8 rail); + + +struct clk_progs { + struct boardobjgrp_e255 super; + u8 slave_entry_count; + u8 vf_entry_count; + +}; + +struct clk_prog { + struct boardobj super; +}; + +struct clk_prog_1x { + struct clk_prog super; + u8 source; + u16 freq_max_mhz; + union ctrl_clk_clk_prog_1x_source_data source_data; +}; + +struct clk_prog_1x_master { + struct clk_prog_1x super; + bool b_o_c_o_v_enabled; + struct ctrl_clk_clk_prog_1x_master_vf_entry *p_vf_entries; + struct ctrl_clk_clk_delta deltas; + union ctrl_clk_clk_prog_1x_master_source_data source_data; + vf_flatten *vfflatten; + vf_lookup *vflookup; + get_fpoints *getfpoints; + get_slaveclk *getslaveclk; +}; + +struct clk_prog_1x_master_ratio { + struct clk_prog_1x_master super; + struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry *p_slave_entries; +}; + +struct clk_prog_1x_master_table { + struct clk_prog_1x_master super; + struct ctrl_clk_clk_prog_1x_master_table_slave_entry *p_slave_entries; +}; + +#define CLK_CLK_PROG_GET(pclk, idx) \ + ((struct clk_prog *)BOARDOBJGRP_OBJ_GET_BY_IDX( \ + &pclk->clk_progobjs.super.super, (u8)(idx))) + +#endif /* NVGPU_CLK_PROG_H */ diff --git a/include/clk/clk_vf_point.c b/include/clk/clk_vf_point.c new file mode 100644 index 0000000..96413c8 --- /dev/null +++ b/include/clk/clk_vf_point.c @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include + +#include "clk.h" +#include "clk_vf_point.h" +#include "boardobj/boardobjgrp.h" +#include "boardobj/boardobjgrp_e32.h" +#include "ctrl/ctrlclk.h" +#include "ctrl/ctrlvolt.h" + +static int _clk_vf_point_pmudatainit_super(struct gk20a *g, struct boardobj + *board_obj_ptr, struct nv_pmu_boardobj *ppmudata); + +static int _clk_vf_points_pmudatainit(struct gk20a *g, + struct boardobjgrp *pboardobjgrp, + struct nv_pmu_boardobjgrp_super *pboardobjgrppmu) +{ + u32 status = 0; + + status = boardobjgrp_pmudatainit_e32(g, pboardobjgrp, pboardobjgrppmu); + if (status) { + nvgpu_err(g, + "error updating pmu boardobjgrp for clk vfpoint 0x%x", + status); + goto done; + } + +done: + return status; +} + +static int _clk_vf_points_pmudata_instget(struct gk20a *g, + struct nv_pmu_boardobjgrp *pmuboardobjgrp, + struct nv_pmu_boardobj **ppboardobjpmudata, + u8 idx) +{ + struct nv_pmu_clk_clk_vf_point_boardobj_grp_set *pgrp_set = + (struct nv_pmu_clk_clk_vf_point_boardobj_grp_set *) + pmuboardobjgrp; + + nvgpu_log_info(g, " "); + + /*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; + nvgpu_log_info(g, " Done"); + return 0; +} + +static int _clk_vf_points_pmustatus_instget(struct gk20a *g, + void *pboardobjgrppmu, + struct nv_pmu_boardobj_query **ppboardobjpmustatus, + u8 idx) +{ + struct nv_pmu_clk_clk_vf_point_boardobj_grp_get_status *pgrp_get_status = + (struct nv_pmu_clk_clk_vf_point_boardobj_grp_get_status *) + pboardobjgrppmu; + + /*check whether pmuboardobjgrp has a valid boardobj in index*/ + if (idx >= CTRL_BOARDOBJGRP_E255_MAX_OBJECTS) { + return -EINVAL; + } + + *ppboardobjpmustatus = (struct nv_pmu_boardobj_query *) + &pgrp_get_status->objects[idx].data.board_obj; + return 0; +} + +int clk_vf_point_sw_setup(struct gk20a *g) +{ + int status; + struct boardobjgrp *pboardobjgrp = NULL; + + nvgpu_log_info(g, " "); + + status = boardobjgrpconstruct_e255(g, &g->clk_pmu.clk_vf_pointobjs.super); + if (status) { + nvgpu_err(g, + "error creating boardobjgrp for clk vfpoint, status - 0x%x", + status); + goto done; + } + + pboardobjgrp = &g->clk_pmu.clk_vf_pointobjs.super.super; + + BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, CLK, CLK_VF_POINT); + + status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, + clk, CLK, clk_vf_point, CLK_VF_POINT); + if (status) { + nvgpu_err(g, + "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", + status); + goto done; + } + + status = BOARDOBJGRP_PMU_CMD_GRP_GET_STATUS_CONSTRUCT(g, + &g->clk_pmu.clk_vf_pointobjs.super.super, + clk, CLK, clk_vf_point, CLK_VF_POINT); + if (status) { + nvgpu_err(g, + "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", + status); + goto done; + } + + pboardobjgrp->pmudatainit = _clk_vf_points_pmudatainit; + pboardobjgrp->pmudatainstget = _clk_vf_points_pmudata_instget; + pboardobjgrp->pmustatusinstget = _clk_vf_points_pmustatus_instget; + +done: + nvgpu_log_info(g, " done status %x", status); + return status; +} + +int clk_vf_point_pmu_setup(struct gk20a *g) +{ + int status; + struct boardobjgrp *pboardobjgrp = NULL; + + nvgpu_log_info(g, " "); + + pboardobjgrp = &g->clk_pmu.clk_vf_pointobjs.super.super; + + if (!pboardobjgrp->bconstructed) { + return -EINVAL; + } + + status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); + + nvgpu_log_info(g, "Done"); + return status; +} + +static int clk_vf_point_construct_super(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct clk_vf_point *pclkvfpoint; + struct clk_vf_point *ptmpvfpoint = + (struct clk_vf_point *)pargs; + int status = 0; + + status = boardobj_construct_super(g, ppboardobj, + size, pargs); + if (status) { + return -EINVAL; + } + + pclkvfpoint = (struct clk_vf_point *)*ppboardobj; + + pclkvfpoint->super.pmudatainit = + _clk_vf_point_pmudatainit_super; + + pclkvfpoint->vfe_equ_idx = ptmpvfpoint->vfe_equ_idx; + pclkvfpoint->volt_rail_idx = ptmpvfpoint->volt_rail_idx; + + return status; +} + +static int _clk_vf_point_pmudatainit_volt(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_vf_point_volt *pclk_vf_point_volt; + struct nv_pmu_clk_clk_vf_point_volt_boardobj_set *pset; + + nvgpu_log_info(g, " "); + + status = _clk_vf_point_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pclk_vf_point_volt = + (struct clk_vf_point_volt *)board_obj_ptr; + + pset = (struct nv_pmu_clk_clk_vf_point_volt_boardobj_set *) + ppmudata; + + pset->source_voltage_uv = pclk_vf_point_volt->source_voltage_uv; + pset->freq_delta.data = pclk_vf_point_volt->freq_delta.data; + pset->freq_delta.type = pclk_vf_point_volt->freq_delta.type; + + return status; +} + +static int _clk_vf_point_pmudatainit_freq(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_vf_point_freq *pclk_vf_point_freq; + struct nv_pmu_clk_clk_vf_point_freq_boardobj_set *pset; + + nvgpu_log_info(g, " "); + + status = _clk_vf_point_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pclk_vf_point_freq = + (struct clk_vf_point_freq *)board_obj_ptr; + + pset = (struct nv_pmu_clk_clk_vf_point_freq_boardobj_set *) + ppmudata; + + pset->freq_mhz = + clkvfpointfreqmhzget(g, &pclk_vf_point_freq->super); + + pset->volt_delta_uv = pclk_vf_point_freq->volt_delta_uv; + + return status; +} + +static int clk_vf_point_construct_volt(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct clk_vf_point_volt *pclkvfpoint; + struct clk_vf_point_volt *ptmpvfpoint = + (struct clk_vf_point_volt *)pargs; + int status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_VF_POINT_TYPE_VOLT) { + return -EINVAL; + } + + ptmpobj->type_mask = BIT(CTRL_CLK_CLK_VF_POINT_TYPE_VOLT); + status = clk_vf_point_construct_super(g, ppboardobj, size, pargs); + if (status) { + return -EINVAL; + } + + pclkvfpoint = (struct clk_vf_point_volt *)*ppboardobj; + + pclkvfpoint->super.super.pmudatainit = + _clk_vf_point_pmudatainit_volt; + + pclkvfpoint->source_voltage_uv = ptmpvfpoint->source_voltage_uv; + pclkvfpoint->freq_delta = ptmpvfpoint->freq_delta; + + return status; +} + +static int clk_vf_point_construct_freq(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct clk_vf_point_freq *pclkvfpoint; + struct clk_vf_point_freq *ptmpvfpoint = + (struct clk_vf_point_freq *)pargs; + int status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_VF_POINT_TYPE_FREQ) { + return -EINVAL; + } + + ptmpobj->type_mask = BIT(CTRL_CLK_CLK_VF_POINT_TYPE_FREQ); + status = clk_vf_point_construct_super(g, ppboardobj, size, pargs); + if (status) { + return -EINVAL; + } + + pclkvfpoint = (struct clk_vf_point_freq *)*ppboardobj; + + pclkvfpoint->super.super.pmudatainit = + _clk_vf_point_pmudatainit_freq; + + clkvfpointfreqmhzset(g, &pclkvfpoint->super, + clkvfpointfreqmhzget(g, &ptmpvfpoint->super)); + + return status; +} + +struct clk_vf_point *construct_clk_vf_point(struct gk20a *g, void *pargs) +{ + struct boardobj *board_obj_ptr = NULL; + int status; + + nvgpu_log_info(g, " "); + switch (BOARDOBJ_GET_TYPE(pargs)) { + case CTRL_CLK_CLK_VF_POINT_TYPE_FREQ: + status = clk_vf_point_construct_freq(g, &board_obj_ptr, + sizeof(struct clk_vf_point_freq), pargs); + break; + + case CTRL_CLK_CLK_VF_POINT_TYPE_VOLT: + status = clk_vf_point_construct_volt(g, &board_obj_ptr, + sizeof(struct clk_vf_point_volt), pargs); + break; + + default: + return NULL; + } + + if (status) { + return NULL; + } + + nvgpu_log_info(g, " Done"); + + return (struct clk_vf_point *)board_obj_ptr; +} + +static int _clk_vf_point_pmudatainit_super(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct clk_vf_point *pclk_vf_point; + struct nv_pmu_clk_clk_vf_point_boardobj_set *pset; + + nvgpu_log_info(g, " "); + + status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pclk_vf_point = + (struct clk_vf_point *)board_obj_ptr; + + pset = (struct nv_pmu_clk_clk_vf_point_boardobj_set *) + ppmudata; + + + pset->vfe_equ_idx = pclk_vf_point->vfe_equ_idx; + pset->volt_rail_idx = pclk_vf_point->volt_rail_idx; + return status; +} + + +static int clk_vf_point_update(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + struct clk_vf_point *pclk_vf_point; + struct nv_pmu_clk_clk_vf_point_boardobj_get_status *pstatus; + + nvgpu_log_info(g, " "); + + + pclk_vf_point = + (struct clk_vf_point *)board_obj_ptr; + + pstatus = (struct nv_pmu_clk_clk_vf_point_boardobj_get_status *) + ppmudata; + + if (pstatus->super.type != pclk_vf_point->super.type) { + nvgpu_err(g, + "pmu data and boardobj type not matching"); + return -EINVAL; + } + /* now copy VF pair */ + memcpy(&pclk_vf_point->pair, &pstatus->pair, + sizeof(struct ctrl_clk_vf_pair)); + return 0; +} + +/*get latest vf point data from PMU */ +int clk_vf_point_cache(struct gk20a *g) +{ + + struct clk_vf_points *pclk_vf_points; + struct boardobjgrp *pboardobjgrp; + struct boardobjgrpmask *pboardobjgrpmask; + struct nv_pmu_boardobjgrp_super *pboardobjgrppmu; + struct boardobj *pboardobj = NULL; + struct nv_pmu_boardobj_query *pboardobjpmustatus = NULL; + int status; + u8 index; + + nvgpu_log_info(g, " "); + pclk_vf_points = &g->clk_pmu.clk_vf_pointobjs; + pboardobjgrp = &pclk_vf_points->super.super; + pboardobjgrpmask = &pclk_vf_points->super.mask.super; + + status = pboardobjgrp->pmugetstatus(g, pboardobjgrp, pboardobjgrpmask); + if (status) { + nvgpu_err(g, "err getting boardobjs from pmu"); + return status; + } + pboardobjgrppmu = pboardobjgrp->pmu.getstatus.buf; + + BOARDOBJGRP_FOR_EACH(pboardobjgrp, struct boardobj*, pboardobj, index) { + status = pboardobjgrp->pmustatusinstget(g, + (struct nv_pmu_boardobjgrp *)pboardobjgrppmu, + &pboardobjpmustatus, index); + if (status) { + nvgpu_err(g, "could not get status object instance"); + return status; + } + + status = clk_vf_point_update(g, pboardobj, + (struct nv_pmu_boardobj *)pboardobjpmustatus); + if (status) { + nvgpu_err(g, "invalid data from pmu at %d", index); + return status; + } + } + + return 0; +} diff --git a/include/clk/clk_vf_point.h b/include/clk/clk_vf_point.h new file mode 100644 index 0000000..b72fe64 --- /dev/null +++ b/include/clk/clk_vf_point.h @@ -0,0 +1,83 @@ +/* +* Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. +* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. +*/ + +#ifndef NVGPU_CLK_VF_POINT_H +#define NVGPU_CLK_VF_POINT_H +#include "ctrl/ctrlclk.h" +#include "ctrl/ctrlboardobj.h" +#include +#include "boardobj/boardobjgrp_e32.h" +#include "boardobj/boardobjgrpmask.h" + +int clk_vf_point_sw_setup(struct gk20a *g); +int clk_vf_point_pmu_setup(struct gk20a *g); +int clk_vf_point_cache(struct gk20a *g); + +struct clk_vf_points { + struct boardobjgrp_e255 super; +}; + +struct clk_vf_point { + struct boardobj super; + u8 vfe_equ_idx; + u8 volt_rail_idx; + struct ctrl_clk_vf_pair pair; +}; + +struct clk_vf_point_volt { + struct clk_vf_point super; + u32 source_voltage_uv; + struct ctrl_clk_freq_delta freq_delta; +}; + +struct clk_vf_point_freq { + struct clk_vf_point super; + int volt_delta_uv; +}; + +#define CLK_CLK_VF_POINT_GET(pclk, idx) \ + ((struct clk_vf_point *)BOARDOBJGRP_OBJ_GET_BY_IDX( \ + &pclk->clk_vf_pointobjs.super.super, (u8)(idx))) + +#define clkvfpointpairget(pvfpoint) \ + (&((pvfpoint)->pair)) + +#define clkvfpointfreqmhzget(pgpu, pvfpoint) \ + CTRL_CLK_VF_PAIR_FREQ_MHZ_GET(clkvfpointpairget(pvfpoint)) + +#define clkvfpointfreqdeltamhzGet(pgpu, pvfPoint) \ + ((BOARDOBJ_GET_TYPE(pvfpoint) == CTRL_CLK_CLK_VF_POINT_TYPE_VOLT) ? \ + (((struct clk_vf_point_volt *)(pvfpoint))->freq_delta_khz / 1000) : 0) + +#define clkvfpointfreqmhzset(pgpu, pvfpoint, _freqmhz) \ + CTRL_CLK_VF_PAIR_FREQ_MHZ_SET(clkvfpointpairget(pvfpoint), _freqmhz) + +#define clkvfpointvoltageuvset(pgpu, pvfpoint, _voltageuv) \ + CTRL_CLK_VF_PAIR_VOLTAGE_UV_SET(clkvfpointpairget(pvfpoint), \ + _voltageuv) + +#define clkvfpointvoltageuvget(pgpu, pvfpoint) \ + CTRL_CLK_VF_PAIR_VOLTAGE_UV_GET(clkvfpointpairget(pvfpoint)) \ + +struct clk_vf_point *construct_clk_vf_point(struct gk20a *g, void *pargs); + +#endif /* NVGPU_CLK_VF_POINT_H */ diff --git a/include/clk/clk_vin.c b/include/clk/clk_vin.c new file mode 100644 index 0000000..e0a4a5b --- /dev/null +++ b/include/clk/clk_vin.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "boardobj/boardobjgrp.h" +#include "boardobj/boardobjgrp_e32.h" + +#include "ctrl/ctrlvolt.h" + +#include "gp106/bios_gp106.h" + +#include "clk.h" +#include "clk_vin.h" + +static int devinit_get_vin_device_table(struct gk20a *g, + struct avfsvinobjs *pvinobjs); + +static int vin_device_construct_v10(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs); +static int vin_device_construct_v20(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs); +static int vin_device_construct_super(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs); +static struct vin_device *construct_vin_device(struct gk20a *g, void *pargs); + +static int vin_device_init_pmudata_v10(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata); +static int vin_device_init_pmudata_v20(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata); +static int vin_device_init_pmudata_super(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata); + +u32 clk_avfs_get_vin_cal_fuse_v10(struct gk20a *g, + struct avfsvinobjs *pvinobjs, + struct vin_device_v20 *pvindev) +{ + u32 status = 0; + u32 slope, intercept; + u8 i; + + if (pvinobjs->calibration_rev_vbios == g->ops.fuse.read_vin_cal_fuse_rev(g)) { + BOARDOBJGRP_FOR_EACH(&(pvinobjs->super.super), + struct vin_device_v20 *, pvindev, i) { + slope = 0; + intercept = 0; + pvindev = (struct vin_device_v20 *)CLK_GET_VIN_DEVICE(pvinobjs, i); + status = g->ops.fuse.read_vin_cal_slope_intercept_fuse(g, + pvindev->super.id, &slope, &intercept); + if (status) { + nvgpu_err(g, + "err reading vin cal for id %x", pvindev->super.id); + return status; + } + pvindev->data.vin_cal.cal_v10.slope = slope; + pvindev->data.vin_cal.cal_v10.intercept = intercept; + } + } + return status; + +} + +u32 clk_avfs_get_vin_cal_fuse_v20(struct gk20a *g, + struct avfsvinobjs *pvinobjs, + struct vin_device_v20 *pvindev) +{ + u32 status = 0; + s8 gain, offset; + u8 i; + + if (pvinobjs->calibration_rev_vbios == g->ops.fuse.read_vin_cal_fuse_rev(g)) { + BOARDOBJGRP_FOR_EACH(&(pvinobjs->super.super), + struct vin_device_v20 *, pvindev, i) { + gain = '\0'; + offset = '\0'; + pvindev = (struct vin_device_v20 *)CLK_GET_VIN_DEVICE(pvinobjs, i); + status = g->ops.fuse.read_vin_cal_gain_offset_fuse(g, + pvindev->super.id, &gain, &offset); + if (status) { + nvgpu_err(g, + "err reading vin cal for id %x", pvindev->super.id); + return status; + } + pvindev->data.vin_cal.cal_v20.gain = gain; + pvindev->data.vin_cal.cal_v20.offset = offset; + } + } + return status; + +} + +static int _clk_vin_devgrp_pmudatainit_super(struct gk20a *g, + struct boardobjgrp *pboardobjgrp, + struct nv_pmu_boardobjgrp_super *pboardobjgrppmu) +{ + struct nv_pmu_clk_clk_vin_device_boardobjgrp_set_header *pset = + (struct nv_pmu_clk_clk_vin_device_boardobjgrp_set_header *) + pboardobjgrppmu; + struct avfsvinobjs *pvin_obbj = (struct avfsvinobjs *)pboardobjgrp; + int status = 0; + + nvgpu_log_info(g, " "); + + status = boardobjgrp_pmudatainit_e32(g, pboardobjgrp, pboardobjgrppmu); + + pset->b_vin_is_disable_allowed = pvin_obbj->vin_is_disable_allowed; + + nvgpu_log_info(g, " Done"); + return status; +} + +static int _clk_vin_devgrp_pmudata_instget(struct gk20a *g, + struct nv_pmu_boardobjgrp *pmuboardobjgrp, + struct nv_pmu_boardobj **ppboardobjpmudata, + u8 idx) +{ + struct nv_pmu_clk_clk_vin_device_boardobj_grp_set *pgrp_set = + (struct nv_pmu_clk_clk_vin_device_boardobj_grp_set *) + pmuboardobjgrp; + + nvgpu_log_info(g, " "); + + /*check whether pmuboardobjgrp has a valid boardobj in index*/ + if (((u32)BIT(idx) & + pgrp_set->hdr.data.super.obj_mask.super.data[0]) == 0) { + return -EINVAL; + } + + *ppboardobjpmudata = (struct nv_pmu_boardobj *) + &pgrp_set->objects[idx].data.board_obj; + nvgpu_log_info(g, " Done"); + return 0; +} + +static int _clk_vin_devgrp_pmustatus_instget(struct gk20a *g, + void *pboardobjgrppmu, + struct nv_pmu_boardobj_query **ppboardobjpmustatus, + u8 idx) +{ + struct nv_pmu_clk_clk_vin_device_boardobj_grp_get_status *pgrp_get_status = + (struct nv_pmu_clk_clk_vin_device_boardobj_grp_get_status *) + pboardobjgrppmu; + + /*check whether pmuboardobjgrp has a valid boardobj in index*/ + 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; +} + +int clk_vin_sw_setup(struct gk20a *g) +{ + int status; + struct boardobjgrp *pboardobjgrp = NULL; + struct vin_device_v20 *pvindev = NULL; + struct avfsvinobjs *pvinobjs; + + nvgpu_log_info(g, " "); + + status = boardobjgrpconstruct_e32(g, &g->clk_pmu.avfs_vinobjs.super); + if (status) { + nvgpu_err(g, + "error creating boardobjgrp for clk vin, statu - 0x%x", + status); + goto done; + } + + pboardobjgrp = &g->clk_pmu.avfs_vinobjs.super.super; + pvinobjs = &g->clk_pmu.avfs_vinobjs; + + BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, CLK, VIN_DEVICE); + + status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, + clk, CLK, clk_vin_device, CLK_VIN_DEVICE); + if (status) { + nvgpu_err(g, + "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", + status); + goto done; + } + + pboardobjgrp->pmudatainit = _clk_vin_devgrp_pmudatainit_super; + pboardobjgrp->pmudatainstget = _clk_vin_devgrp_pmudata_instget; + pboardobjgrp->pmustatusinstget = _clk_vin_devgrp_pmustatus_instget; + + status = devinit_get_vin_device_table(g, &g->clk_pmu.avfs_vinobjs); + if (status) { + goto done; + } + + /*update vin calibration to fuse */ + g->ops.pmu_ver.clk.clk_avfs_get_vin_cal_data(g, pvinobjs, pvindev); + + status = BOARDOBJGRP_PMU_CMD_GRP_GET_STATUS_CONSTRUCT(g, + &g->clk_pmu.avfs_vinobjs.super.super, + clk, CLK, clk_vin_device, CLK_VIN_DEVICE); + if (status) { + nvgpu_err(g, + "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", + status); + goto done; + } + +done: + nvgpu_log_info(g, " done status %x", status); + return status; +} + +int clk_vin_pmu_setup(struct gk20a *g) +{ + int status; + struct boardobjgrp *pboardobjgrp = NULL; + + nvgpu_log_info(g, " "); + + pboardobjgrp = &g->clk_pmu.avfs_vinobjs.super.super; + + if (!pboardobjgrp->bconstructed) { + return -EINVAL; + } + + status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); + + nvgpu_log_info(g, "Done"); + return status; +} + +static int devinit_get_vin_device_table(struct gk20a *g, + struct avfsvinobjs *pvinobjs) +{ + int status = 0; + u8 *vin_table_ptr = NULL; + struct vin_descriptor_header_10 vin_desc_table_header = { 0 }; + struct vin_descriptor_entry_10 vin_desc_table_entry = { 0 }; + u8 *vin_tbl_entry_ptr = NULL; + u32 index = 0; + u32 slope=0, intercept=0; + s8 offset='\0', gain='\0'; + struct vin_device *pvin_dev; + u32 cal_type; + + union { + struct boardobj boardobj; + struct vin_device vin_device; + struct vin_device_v10 vin_device_v10; + struct vin_device_v20 vin_device_v20; + } vin_device_data; + + nvgpu_log_info(g, " "); + + vin_table_ptr = (u8 *)nvgpu_bios_get_perf_table_ptrs(g, + g->bios.clock_token, VIN_TABLE); + if (vin_table_ptr == NULL) { + status = -1; + goto done; + } + + memcpy(&vin_desc_table_header, vin_table_ptr, + sizeof(struct vin_descriptor_header_10)); + + pvinobjs->calibration_rev_vbios = + BIOS_GET_FIELD(vin_desc_table_header.flags0, + NV_VIN_DESC_FLAGS0_VIN_CAL_REVISION); + pvinobjs->vin_is_disable_allowed = + BIOS_GET_FIELD(vin_desc_table_header.flags0, + NV_VIN_DESC_FLAGS0_DISABLE_CONTROL); + cal_type = BIOS_GET_FIELD(vin_desc_table_header.flags0, + NV_VIN_DESC_FLAGS0_VIN_CAL_TYPE); + if (!cal_type) { + cal_type = CTRL_CLK_VIN_CAL_TYPE_V10; + } + + switch (cal_type) { + case CTRL_CLK_VIN_CAL_TYPE_V10: + /* VIN calibration slope: XX.YYY mV/code => XXYYY uV/code*/ + slope = ((BIOS_GET_FIELD(vin_desc_table_header.vin_cal, + NV_VIN_DESC_VIN_CAL_SLOPE_INTEGER) * 1000)) + + ((BIOS_GET_FIELD(vin_desc_table_header.vin_cal, + NV_VIN_DESC_VIN_CAL_SLOPE_FRACTION))); + + /* VIN calibration intercept: ZZZ.W mV => ZZZW00 uV */ + intercept = ((BIOS_GET_FIELD(vin_desc_table_header.vin_cal, + NV_VIN_DESC_VIN_CAL_INTERCEPT_INTEGER) * 1000)) + + ((BIOS_GET_FIELD(vin_desc_table_header.vin_cal, + NV_VIN_DESC_VIN_CAL_INTERCEPT_FRACTION) * 100)); + + break; + case CTRL_CLK_VIN_CAL_TYPE_V20: + offset = BIOS_GET_FIELD(vin_desc_table_header.vin_cal, + NV_VIN_DESC_VIN_CAL_OFFSET); + gain = BIOS_GET_FIELD(vin_desc_table_header.vin_cal, + NV_VIN_DESC_VIN_CAL_GAIN); + break; + default: + status = -1; + goto done; + } + /* Read table entries*/ + vin_tbl_entry_ptr = vin_table_ptr + vin_desc_table_header.header_sizee; + for (index = 0; index < vin_desc_table_header.entry_count; index++) { + memcpy(&vin_desc_table_entry, vin_tbl_entry_ptr, + sizeof(struct vin_descriptor_entry_10)); + + if (vin_desc_table_entry.vin_device_type == CTRL_CLK_VIN_TYPE_DISABLED) { + continue; + } + + vin_device_data.boardobj.type = + (u8)vin_desc_table_entry.vin_device_type; + vin_device_data.vin_device.id = (u8)vin_desc_table_entry.vin_device_id; + vin_device_data.vin_device.volt_domain_vbios = + (u8)vin_desc_table_entry.volt_domain_vbios; + vin_device_data.vin_device.flls_shared_mask = 0; + + switch (vin_device_data.boardobj.type) { + case CTRL_CLK_VIN_TYPE_V10: + vin_device_data.vin_device_v10.data.vin_cal.slope = slope; + vin_device_data.vin_device_v10.data.vin_cal.intercept = intercept; + break; + case CTRL_CLK_VIN_TYPE_V20: + vin_device_data.vin_device_v20.data.cal_type = (u8) cal_type; + vin_device_data.vin_device_v20.data.vin_cal.cal_v20.offset = offset; + vin_device_data.vin_device_v20.data.vin_cal.cal_v20.gain = gain; + break; + default: + status = -1; + goto done; + }; + + pvin_dev = construct_vin_device(g, (void *)&vin_device_data); + + status = boardobjgrp_objinsert(&pvinobjs->super.super, + (struct boardobj *)pvin_dev, index); + + vin_tbl_entry_ptr += vin_desc_table_header.entry_size; + } + +done: + nvgpu_log_info(g, " done status %x", status); + return status; +} + +static int vin_device_construct_v10(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct vin_device_v10 *pvin_device_v10; + struct vin_device_v10 *ptmpvin_device_v10 = (struct vin_device_v10 *)pargs; + int status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_VIN_TYPE_V10) { + return -EINVAL; + } + + ptmpobj->type_mask |= BIT(CTRL_CLK_VIN_TYPE_V10); + status = vin_device_construct_super(g, ppboardobj, size, pargs); + if (status) { + return -EINVAL; + } + + pvin_device_v10 = (struct vin_device_v10 *)*ppboardobj; + + pvin_device_v10->super.super.pmudatainit = + vin_device_init_pmudata_v10; + + pvin_device_v10->data.vin_cal.slope = ptmpvin_device_v10->data.vin_cal.slope; + pvin_device_v10->data.vin_cal.intercept = ptmpvin_device_v10->data.vin_cal.intercept; + + return status; +} + +static int vin_device_construct_v20(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct boardobj *ptmpobj = (struct boardobj *)pargs; + struct vin_device_v20 *pvin_device_v20; + struct vin_device_v20 *ptmpvin_device_v20 = (struct vin_device_v20 *)pargs; + int status = 0; + + if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_VIN_TYPE_V20) { + return -EINVAL; + } + + ptmpobj->type_mask |= BIT(CTRL_CLK_VIN_TYPE_V20); + status = vin_device_construct_super(g, ppboardobj, size, pargs); + if (status) { + return -EINVAL; + } + + pvin_device_v20 = (struct vin_device_v20 *)*ppboardobj; + + pvin_device_v20->super.super.pmudatainit = + vin_device_init_pmudata_v20; + + pvin_device_v20->data.cal_type = ptmpvin_device_v20->data.cal_type; + pvin_device_v20->data.vin_cal.cal_v20.offset = ptmpvin_device_v20->data.vin_cal.cal_v20.offset; + pvin_device_v20->data.vin_cal.cal_v20.gain = ptmpvin_device_v20->data.vin_cal.cal_v20.gain; + + return status; +} +static int vin_device_construct_super(struct gk20a *g, + struct boardobj **ppboardobj, + u16 size, void *pargs) +{ + struct vin_device *pvin_device; + struct vin_device *ptmpvin_device = (struct vin_device *)pargs; + int status = 0; + status = boardobj_construct_super(g, ppboardobj, size, pargs); + + if (status) { + return -EINVAL; + } + + pvin_device = (struct vin_device *)*ppboardobj; + + pvin_device->super.pmudatainit = + vin_device_init_pmudata_super; + + pvin_device->id = ptmpvin_device->id; + pvin_device->volt_domain_vbios = ptmpvin_device->volt_domain_vbios; + pvin_device->flls_shared_mask = ptmpvin_device->flls_shared_mask; + pvin_device->volt_domain = CTRL_VOLT_DOMAIN_LOGIC; + + return status; +} +static struct vin_device *construct_vin_device(struct gk20a *g, void *pargs) +{ + struct boardobj *board_obj_ptr = NULL; + int status; + + nvgpu_log_info(g, " %d", BOARDOBJ_GET_TYPE(pargs)); + switch (BOARDOBJ_GET_TYPE(pargs)) { + case CTRL_CLK_VIN_TYPE_V10: + status = vin_device_construct_v10(g, &board_obj_ptr, + sizeof(struct vin_device_v10), pargs); + break; + + case CTRL_CLK_VIN_TYPE_V20: + status = vin_device_construct_v20(g, &board_obj_ptr, + sizeof(struct vin_device_v20), pargs); + break; + + default: + return NULL; + }; + + if (status) { + return NULL; + } + + nvgpu_log_info(g, " Done"); + + return (struct vin_device *)board_obj_ptr; +} + + + +static int vin_device_init_pmudata_v10(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct vin_device_v20 *pvin_dev_v20; + struct nv_pmu_clk_clk_vin_device_v10_boardobj_set *perf_pmu_data; + + nvgpu_log_info(g, " "); + + status = vin_device_init_pmudata_super(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pvin_dev_v20 = (struct vin_device_v20 *)board_obj_ptr; + perf_pmu_data = (struct nv_pmu_clk_clk_vin_device_v10_boardobj_set *) + ppmudata; + + perf_pmu_data->data.vin_cal.intercept = pvin_dev_v20->data.vin_cal.cal_v10.intercept; + perf_pmu_data->data.vin_cal.slope = pvin_dev_v20->data.vin_cal.cal_v10.slope; + + nvgpu_log_info(g, " Done"); + + return status; +} + +static int vin_device_init_pmudata_v20(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct vin_device_v20 *pvin_dev_v20; + struct nv_pmu_clk_clk_vin_device_v20_boardobj_set *perf_pmu_data; + + nvgpu_log_info(g, " "); + + status = vin_device_init_pmudata_super(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pvin_dev_v20 = (struct vin_device_v20 *)board_obj_ptr; + perf_pmu_data = (struct nv_pmu_clk_clk_vin_device_v20_boardobj_set *) + ppmudata; + + perf_pmu_data->data.cal_type = pvin_dev_v20->data.cal_type; + perf_pmu_data->data.vin_cal.cal_v20.offset = pvin_dev_v20->data.vin_cal.cal_v20.offset; + perf_pmu_data->data.vin_cal.cal_v20.gain = pvin_dev_v20->data.vin_cal.cal_v20.gain; + + nvgpu_log_info(g, " Done"); + + return status; +} + +static int vin_device_init_pmudata_super(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + int status = 0; + struct vin_device *pvin_dev; + struct nv_pmu_clk_clk_vin_device_boardobj_set *perf_pmu_data; + + nvgpu_log_info(g, " "); + + status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status != 0) { + return status; + } + + pvin_dev = (struct vin_device *)board_obj_ptr; + perf_pmu_data = (struct nv_pmu_clk_clk_vin_device_boardobj_set *) + ppmudata; + + perf_pmu_data->id = pvin_dev->id; + perf_pmu_data->volt_domain = pvin_dev->volt_domain; + perf_pmu_data->flls_shared_mask = pvin_dev->flls_shared_mask; + + nvgpu_log_info(g, " Done"); + + return status; +} diff --git a/include/clk/clk_vin.h b/include/clk/clk_vin.h new file mode 100644 index 0000000..73b93e4 --- /dev/null +++ b/include/clk/clk_vin.h @@ -0,0 +1,79 @@ +/* +* Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. +* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. +*/ + +#ifndef NVGPU_CLK_VIN_H +#define NVGPU_CLK_VIN_H + +#include "boardobj/boardobj.h" +#include "boardobj/boardobjgrp.h" +#include "boardobj/boardobjgrp_e32.h" + +struct vin_device; +struct clk_pmupstate; + +struct avfsvinobjs { + struct boardobjgrp_e32 super; + u8 calibration_rev_vbios; + u8 calibration_rev_fused; + bool vin_is_disable_allowed; +}; +typedef u32 vin_device_state_load(struct gk20a *g, + struct clk_pmupstate *clk, struct vin_device *pdev); + +struct vin_device { + struct boardobj super; + u8 id; + u8 volt_domain; + u8 volt_domain_vbios; + u32 flls_shared_mask; + + vin_device_state_load *state_load; +}; + +struct vin_device_v10 { + struct vin_device super; + struct ctrl_clk_vin_device_info_data_v10 data; +}; + +struct vin_device_v20 { + struct vin_device super; + struct ctrl_clk_vin_device_info_data_v20 data; +}; + +/* get vin device object from descriptor table index*/ +#define CLK_GET_VIN_DEVICE(pvinobjs, dev_index) \ + ((struct vin_device *)BOARDOBJGRP_OBJ_GET_BY_IDX( \ + ((struct boardobjgrp *)&(pvinobjs->super.super)), (dev_index))) + +boardobj_construct construct_vindevice; +boardobj_pmudatainit vindeviceinit_pmudata_super; + +int clk_vin_sw_setup(struct gk20a *g); +int clk_vin_pmu_setup(struct gk20a *g); +u32 clk_avfs_get_vin_cal_fuse_v10(struct gk20a *g, + struct avfsvinobjs *pvinobjs, + struct vin_device_v20 *pvindev); +u32 clk_avfs_get_vin_cal_fuse_v20(struct gk20a *g, + struct avfsvinobjs *pvinobjs, + struct vin_device_v20 *pvindev); + +#endif /* NVGPU_CLK_VIN_H */ -- cgit v1.2.2