From 90f80a282eff04412858361df35c2f88372e88cb Mon Sep 17 00:00:00 2001 From: Lakshmanan M Date: Thu, 8 Sep 2016 22:58:19 +0530 Subject: gpu: nvgpu: Add pmgr support This CL covers the following implementation, 1) Power Sensor Table parsing. 2) Power Topology Table parsing. 3) Add debugfs interface to get the current power(mW), current(mA) and voltage(uV) information from PMU. 4) Power Policy Table Parsing 5) Implement PMU boardobj interface for pmgr module. 6) Over current protection. JIRA DNVGPU-47 Change-Id: I7b1eefacc4f0a9824ab94ec8dcebefe81b7660d3 Signed-off-by: Lakshmanan M Reviewed-on: http://git-master/r/1217189 (cherry picked from commit ecd0b16316cb4110118c6677f5f03e02921c29b6) Reviewed-on: http://git-master/r/1241953 Reviewed-by: Terje Bergstrom GVS: Gerrit_Virtual_Submit --- drivers/gpu/nvgpu/Makefile.nvgpu-t18x | 7 +- drivers/gpu/nvgpu/include/bios.h | 156 +++++++- drivers/gpu/nvgpu/pmgr/pmgr.c | 143 +++++++ drivers/gpu/nvgpu/pmgr/pmgr.h | 31 ++ drivers/gpu/nvgpu/pmgr/pmgrpmu.c | 524 ++++++++++++++++++++++++++ drivers/gpu/nvgpu/pmgr/pmgrpmu.h | 29 ++ drivers/gpu/nvgpu/pmgr/pwrdev.c | 310 ++++++++++++++++ drivers/gpu/nvgpu/pmgr/pwrdev.h | 51 +++ drivers/gpu/nvgpu/pmgr/pwrmonitor.c | 365 ++++++++++++++++++ drivers/gpu/nvgpu/pmgr/pwrmonitor.h | 60 +++ drivers/gpu/nvgpu/pmgr/pwrpolicy.c | 680 ++++++++++++++++++++++++++++++++++ drivers/gpu/nvgpu/pmgr/pwrpolicy.h | 117 ++++++ drivers/gpu/nvgpu/pstate/pstate.c | 9 + 13 files changed, 2480 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/nvgpu/pmgr/pmgr.c create mode 100644 drivers/gpu/nvgpu/pmgr/pmgr.h create mode 100644 drivers/gpu/nvgpu/pmgr/pmgrpmu.c create mode 100644 drivers/gpu/nvgpu/pmgr/pmgrpmu.h create mode 100644 drivers/gpu/nvgpu/pmgr/pwrdev.c create mode 100644 drivers/gpu/nvgpu/pmgr/pwrdev.h create mode 100644 drivers/gpu/nvgpu/pmgr/pwrmonitor.c create mode 100644 drivers/gpu/nvgpu/pmgr/pwrmonitor.h create mode 100644 drivers/gpu/nvgpu/pmgr/pwrpolicy.c create mode 100644 drivers/gpu/nvgpu/pmgr/pwrpolicy.h (limited to 'drivers/gpu/nvgpu') diff --git a/drivers/gpu/nvgpu/Makefile.nvgpu-t18x b/drivers/gpu/nvgpu/Makefile.nvgpu-t18x index ceae6006..d5162332 100644 --- a/drivers/gpu/nvgpu/Makefile.nvgpu-t18x +++ b/drivers/gpu/nvgpu/Makefile.nvgpu-t18x @@ -44,7 +44,12 @@ nvgpu-y += \ $(nvgpu-t18x)/gp106/clk_gp106.o \ $(nvgpu-t18x)/gp106/gp106_gating_reglist.o \ $(nvgpu-t18x)/gp106/therm_gp106.o \ - $(nvgpu-t18x)/gp106/xve_gp106.o + $(nvgpu-t18x)/gp106/xve_gp106.o \ + $(nvgpu-t18x)/pmgr/pwrdev.o \ + $(nvgpu-t18x)/pmgr/pmgr.o \ + $(nvgpu-t18x)/pmgr/pmgrpmu.o \ + $(nvgpu-t18x)/pmgr/pwrmonitor.o \ + $(nvgpu-t18x)/pmgr/pwrpolicy.o nvgpu-$(CONFIG_TEGRA_GK20A) += $(nvgpu-t18x)/gp10b/platform_gp10b_tegra.o diff --git a/drivers/gpu/nvgpu/include/bios.h b/drivers/gpu/nvgpu/include/bios.h index 83d972e3..d3a677f8 100644 --- a/drivers/gpu/nvgpu/include/bios.h +++ b/drivers/gpu/nvgpu/include/bios.h @@ -501,5 +501,159 @@ struct vbios_memory_clock_base_entry_11 { #define VBIOS_MEMORY_CLOCK_BASE_ENTRY_12_FLAGS2_CMD_SCRIPT_INDEX_MASK 0x3 #define VBIOS_MEMORY_CLOCK_BASE_ENTRY_12_FLAGS2_CMD_SCRIPT_INDEX_SHIFT 0 -#endif +#define VBIOS_POWER_SENSORS_VERSION_2X 0x20 +#define VBIOS_POWER_SENSORS_2X_HEADER_SIZE_08 0x00000008 + +struct pwr_sensors_2x_header { + u8 version; + u8 header_size; + u8 table_entry_size; + u8 num_table_entries; + u32 ba_script_pointer; +}; + +#define VBIOS_POWER_SENSORS_2X_ENTRY_SIZE_15 0x00000015 + +struct pwr_sensors_2x_entry { + u8 flags0; + u32 class_param0; + u32 sensor_param0; + u32 sensor_param1; + u32 sensor_param2; + u32 sensor_param3; +}; + +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_FLAGS0_CLASS_MASK 0xF +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_FLAGS0_CLASS_SHIFT 0 +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_FLAGS0_CLASS_I2C 0x00000001 + +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_CLASS_PARAM0_I2C_INDEX_MASK 0xFF +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_CLASS_PARAM0_I2C_INDEX_SHIFT 0 +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_CLASS_PARAM0_I2C_USE_FXP8_8_MASK 0x100 +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_CLASS_PARAM0_I2C_USE_FXP8_8_SHIFT 8 + +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM0_INA3221_RSHUNT0_MOHM_MASK 0xFFFF +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM0_INA3221_RSHUNT0_MOHM_SHIFT 0 +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM0_INA3221_RSHUNT1_MOHM_MASK 0xFFFF0000 +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM0_INA3221_RSHUNT1_MOHM_SHIFT 16 +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM1_INA3221_RSHUNT2_MOHM_MASK 0xFFFF +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM1_INA3221_RSHUNT2_MOHM_SHIFT 0 +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM1_INA3221_CONFIGURATION_MASK 0xFFFF0000 +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM1_INA3221_CONFIGURATION_SHIFT 16 + +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM2_INA3221_MASKENABLE_MASK 0xFFFF +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM2_INA3221_MASKENABLE_SHIFT 0 +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM2_INA3221_GPIOFUNCTION_MASK 0xFF0000 +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM2_INA3221_GPIOFUNCTION_SHIFT 16 +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM3_INA3221_CURR_CORRECT_M_MASK 0xFFFF +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM3_INA3221_CURR_CORRECT_M_SHIFT 0 +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM3_INA3221_CURR_CORRECT_B_MASK 0xFFFF0000 +#define NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM3_INA3221_CURR_CORRECT_B_SHIFT 16 + +#define VBIOS_POWER_TOPOLOGY_VERSION_2X 0x20 +#define VBIOS_POWER_TOPOLOGY_2X_HEADER_SIZE_06 0x00000006 + +struct pwr_topology_2x_header { + u8 version; + u8 header_size; + u8 table_entry_size; + u8 num_table_entries; + u8 rel_entry_size; + u8 num_rel_entries; +}; + +#define VBIOS_POWER_TOPOLOGY_2X_ENTRY_SIZE_16 0x00000016 + +struct pwr_topology_2x_entry { + u8 flags0; + u8 pwr_rail; + u32 param0; + u32 curr_corr_slope; + u32 curr_corr_offset; + u32 param1; + u32 param2; +}; + +#define NV_VBIOS_POWER_TOPOLOGY_2X_ENTRY_FLAGS0_CLASS_MASK 0xF +#define NV_VBIOS_POWER_TOPOLOGY_2X_ENTRY_FLAGS0_CLASS_SHIFT 0 +#define NV_VBIOS_POWER_TOPOLOGY_2X_ENTRY_FLAGS0_CLASS_SENSOR 0x00000001 + +#define NV_VBIOS_POWER_TOPOLOGY_2X_ENTRY_PARAM1_SENSOR_INDEX_MASK 0xFF +#define NV_VBIOS_POWER_TOPOLOGY_2X_ENTRY_PARAM1_SENSOR_INDEX_SHIFT 0 +#define NV_VBIOS_POWER_TOPOLOGY_2X_ENTRY_PARAM1_SENSOR_PROVIDER_INDEX_MASK 0xFF00 +#define NV_VBIOS_POWER_TOPOLOGY_2X_ENTRY_PARAM1_SENSOR_PROVIDER_INDEX_SHIFT 8 + +#define VBIOS_POWER_POLICY_VERSION_3X 0x30 +#define VBIOS_POWER_POLICY_3X_HEADER_SIZE_25 0x00000025 + +struct pwr_policy_3x_header_struct { + u8 version; + u8 header_size; + u8 table_entry_size; + u8 num_table_entries; + u16 base_sample_period; + u16 min_client_sample_period; + u8 table_rel_entry_size; + u8 num_table_rel_entries; + u8 tgp_policy_idx; + u8 rtp_policy_idx; + u8 mxm_policy_idx; + u8 dnotifier_policy_idx; + u32 d2_limit; + u32 d3_limit; + u32 d4_limit; + u32 d5_limit; + u8 low_sampling_mult; + u8 pwr_tgt_policy_idx; + u8 pwr_tgt_floor_policy_idx; + u8 sm_bus_policy_idx; + u8 table_viol_entry_size; + u8 num_table_viol_entries; +}; + +#define VBIOS_POWER_POLICY_3X_ENTRY_SIZE_2E 0x0000002E + +struct pwr_policy_3x_entry_struct { + u8 flags0; + u8 ch_idx; + u32 limit_min; + u32 limit_rated; + u32 limit_max; + u32 param0; + u32 param1; + u32 param2; + u32 param3; + u32 limit_batt; + u8 flags1; + u8 past_length; + u8 next_length; + u16 ratio_min; + u16 ratio_max; + u8 sample_mult; + u32 filter_param; +}; + +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS0_CLASS_MASK 0xF +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS0_CLASS_SHIFT 0 +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS0_CLASS_HW_THRESHOLD 0x00000005 +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS0_LIMIT_UNIT_MASK 0x10 +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS0_LIMIT_UNIT_SHIFT 4 + +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS1_FULL_DEFLECTION_LIMIT_MASK 0x1 +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS1_FULL_DEFLECTION_LIMIT_SHIFT 0 +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS1_INTEGRAL_CONTROL_MASK 0x2 +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS1_INTEGRAL_CONTROL_SHIFT 1 +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS1_FILTER_TYPE_MASK 0x3C +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS1_FILTER_TYPE_SHIFT 2 + +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_PARAM0_HW_THRESHOLD_THRES_IDX_MASK 0xFF +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_PARAM0_HW_THRESHOLD_THRES_IDX_SHIFT 0 +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_PARAM0_HW_THRESHOLD_LOW_THRESHOLD_IDX_MASK 0xFF00 +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_PARAM0_HW_THRESHOLD_LOW_THRESHOLD_IDX_SHIFT 8 +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_PARAM0_HW_THRESHOLD_LOW_THRESHOLD_USE_MASK 0x10000 +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_PARAM0_HW_THRESHOLD_LOW_THRESHOLD_USE_SHIFT 16 + +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_PARAM1_HW_THRESHOLD_LOW_THRESHOLD_VAL_MASK 0xFFFF +#define NV_VBIOS_POWER_POLICY_3X_ENTRY_PARAM1_HW_THRESHOLD_LOW_THRESHOLD_VAL_SHIFT 0 +#endif diff --git a/drivers/gpu/nvgpu/pmgr/pmgr.c b/drivers/gpu/nvgpu/pmgr/pmgr.c new file mode 100644 index 00000000..f625e37d --- /dev/null +++ b/drivers/gpu/nvgpu/pmgr/pmgr.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "gk20a/gk20a.h" +#include "pwrdev.h" +#include "pmgrpmu.h" +#include + +#ifdef CONFIG_DEBUG_FS +static int pmgr_pwr_devices_get_current_power(void *data, u64 *val) +{ + struct nv_pmu_pmgr_pwr_devices_query_payload payload; + int status; + struct gk20a *g = (struct gk20a *)data; + + status = pmgr_pmu_pwr_devices_query_blocking(g, 1, &payload); + if (status) + gk20a_err(dev_from_gk20a(g), + "pmgr_pwr_devices_get_current_power failed %x", + status); + + *val = payload.devices[0].powerm_w; + + return status; +} + +static int pmgr_pwr_devices_get_current(void *data, u64 *val) +{ + struct nv_pmu_pmgr_pwr_devices_query_payload payload; + int status; + struct gk20a *g = (struct gk20a *)data; + + status = pmgr_pmu_pwr_devices_query_blocking(g, 1, &payload); + if (status) + gk20a_err(dev_from_gk20a(g), + "pmgr_pwr_devices_get_current failed %x", + status); + + *val = payload.devices[0].currentm_a; + + return status; +} + +static int pmgr_pwr_devices_get_current_voltage(void *data, u64 *val) +{ + struct nv_pmu_pmgr_pwr_devices_query_payload payload; + int status; + struct gk20a *g = (struct gk20a *)data; + + status = pmgr_pmu_pwr_devices_query_blocking(g, 1, &payload); + if (status) + gk20a_err(dev_from_gk20a(g), + "pmgr_pwr_devices_get_current_voltage failed %x", + status); + + *val = payload.devices[0].voltageu_v; + + return status; +} + +DEFINE_SIMPLE_ATTRIBUTE( + pmgr_power_ctrl_fops, pmgr_pwr_devices_get_current_power, NULL, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE( + pmgr_current_ctrl_fops, pmgr_pwr_devices_get_current, NULL, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE( + pmgr_voltage_ctrl_fops, pmgr_pwr_devices_get_current_voltage, NULL, "%llu\n"); + +static void pmgr_debugfs_init(struct gk20a *g) { + struct gk20a_platform *platform = dev_get_drvdata(g->dev); + struct dentry *dbgentry; + + dbgentry = debugfs_create_file( + "power", S_IRUGO, platform->debugfs, g, &pmgr_power_ctrl_fops); + if (!dbgentry) + gk20a_err(dev_from_gk20a(g), + "debugfs entry create failed for power"); + + dbgentry = debugfs_create_file( + "current", S_IRUGO, platform->debugfs, g, &pmgr_current_ctrl_fops); + if (!dbgentry) + gk20a_err(dev_from_gk20a(g), + "debugfs entry create failed for current"); + + dbgentry = debugfs_create_file( + "voltage", S_IRUGO, platform->debugfs, g, &pmgr_voltage_ctrl_fops); + if (!dbgentry) + gk20a_err(dev_from_gk20a(g), + "debugfs entry create failed for voltage"); +} +#endif + +u32 pmgr_domain_sw_setup(struct gk20a *g) +{ + u32 status; + + status = pmgr_device_sw_setup(g); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error creating boardobjgrp for pmgr devices, status - 0x%x", + status); + goto exit; + } + + status = pmgr_monitor_sw_setup(g); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error creating boardobjgrp for pmgr monitor, status - 0x%x", + status); + goto exit; + } + + status = pmgr_policy_sw_setup(g); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error creating boardobjgrp for pmgr policy, status - 0x%x", + status); + goto exit; + } + +#ifdef CONFIG_DEBUG_FS + pmgr_debugfs_init(g); +#endif + +exit: + return status; +} + +u32 pmgr_domain_pmu_setup(struct gk20a *g) +{ + return pmgr_send_pmgr_tables_to_pmu(g); +} diff --git a/drivers/gpu/nvgpu/pmgr/pmgr.h b/drivers/gpu/nvgpu/pmgr/pmgr.h new file mode 100644 index 00000000..97e7b609 --- /dev/null +++ b/drivers/gpu/nvgpu/pmgr/pmgr.h @@ -0,0 +1,31 @@ +/* + * general power device structures & definitions + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#ifndef _PMGR_H_ +#define _PMGR_H_ + +#include "pwrdev.h" +#include "pwrmonitor.h" +#include "pwrpolicy.h" + +struct pmgr_pmupstate { + struct pwr_devices pmgr_deviceobjs; + struct pmgr_pwr_monitor pmgr_monitorobjs; + struct pmgr_pwr_policy pmgr_policyobjs; +}; + +u32 pmgr_domain_sw_setup(struct gk20a *g); +u32 pmgr_domain_pmu_setup(struct gk20a *g); + +#endif diff --git a/drivers/gpu/nvgpu/pmgr/pmgrpmu.c b/drivers/gpu/nvgpu/pmgr/pmgrpmu.c new file mode 100644 index 00000000..ea070060 --- /dev/null +++ b/drivers/gpu/nvgpu/pmgr/pmgrpmu.c @@ -0,0 +1,524 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "gk20a/gk20a.h" +#include "pwrdev.h" +#include "include/bios.h" +#include "boardobj/boardobjgrp.h" +#include "boardobj/boardobjgrp_e32.h" +#include "pmuif/gpmuifboardobj.h" +#include "pmuif/gpmuifpmgr.h" +#include "gm206/bios_gm206.h" +#include "gk20a/pmu_gk20a.h" +#include "pmgrpmu.h" + +struct pmgr_pmucmdhandler_params { + u32 success; +}; + +static void pmgr_pmucmdhandler(struct gk20a *g, struct pmu_msg *msg, + void *param, u32 handle, u32 status) +{ + struct pmgr_pmucmdhandler_params *phandlerparams = + (struct pmgr_pmucmdhandler_params *)param; + + if ((msg->msg.pmgr.msg_type != NV_PMU_PMGR_MSG_ID_SET_OBJECT) && + (msg->msg.pmgr.msg_type != NV_PMU_PMGR_MSG_ID_QUERY) && + (msg->msg.pmgr.msg_type != NV_PMU_PMGR_MSG_ID_LOAD)) { + gk20a_err(dev_from_gk20a(g), + "unknow msg %x", + msg->msg.pmgr.msg_type); + return; + } + + if (msg->msg.pmgr.msg_type == NV_PMU_PMGR_MSG_ID_SET_OBJECT) { + if ((msg->msg.pmgr.set_object.b_success != 1) || + (msg->msg.pmgr.set_object.flcnstatus != 0) ) { + gk20a_err(dev_from_gk20a(g), + "pmgr msg failed %x %x %x %x", + msg->msg.pmgr.set_object.msg_type, + msg->msg.pmgr.set_object.b_success, + msg->msg.pmgr.set_object.flcnstatus, + msg->msg.pmgr.set_object.object_type); + return; + } + } else if (msg->msg.pmgr.msg_type == NV_PMU_PMGR_MSG_ID_QUERY) { + if ((msg->msg.pmgr.query.b_success != 1) || + (msg->msg.pmgr.query.flcnstatus != 0) ) { + gk20a_err(dev_from_gk20a(g), + "pmgr msg failed %x %x %x %x", + msg->msg.pmgr.query.msg_type, + msg->msg.pmgr.query.b_success, + msg->msg.pmgr.query.flcnstatus, + msg->msg.pmgr.query.cmd_type); + return; + } + } else if (msg->msg.pmgr.msg_type == NV_PMU_PMGR_MSG_ID_LOAD) { + if ((msg->msg.pmgr.query.b_success != 1) || + (msg->msg.pmgr.query.flcnstatus != 0) ) { + gk20a_err(dev_from_gk20a(g), + "pmgr msg failed %x %x %x", + msg->msg.pmgr.load.msg_type, + msg->msg.pmgr.load.b_success, + msg->msg.pmgr.load.flcnstatus); + return; + } + } + + phandlerparams->success = 1; +} + +static u32 pmgr_pmu_set_object(struct gk20a *g, + u8 type, + u16 dmem_size, + u16 fb_size, + void *pobj) +{ + struct pmu_cmd cmd = { {0} }; + struct pmu_payload payload = { {0} }; + struct nv_pmu_pmgr_cmd_set_object *pcmd; + u32 status; + u32 seqdesc; + struct pmgr_pmucmdhandler_params handlerparams = {0}; + + cmd.hdr.unit_id = PMU_UNIT_PMGR; + cmd.hdr.size = (u32)sizeof(struct nv_pmu_pmgr_cmd_set_object) + + (u32)sizeof(struct pmu_hdr);; + + pcmd = &cmd.cmd.pmgr.set_object; + pcmd->cmd_type = NV_PMU_PMGR_CMD_ID_SET_OBJECT; + pcmd->object_type = type; + + payload.in.buf = pobj; + payload.in.size = dmem_size; + payload.in.fb_size = fb_size; + payload.in.offset = NV_PMU_PMGR_SET_OBJECT_ALLOC_OFFSET; + + /* Setup the handler params to communicate back results.*/ + handlerparams.success = 0; + + status = gk20a_pmu_cmd_post(g, &cmd, NULL, &payload, + PMU_COMMAND_QUEUE_LPQ, + pmgr_pmucmdhandler, + (void *)&handlerparams, + &seqdesc, ~0); + if (status) { + gk20a_err(dev_from_gk20a(g), + "unable to post pmgr cmd for unit %x cmd id %x obj type %x", + cmd.hdr.unit_id, pcmd->cmd_type, pcmd->object_type); + goto exit; + } + + pmu_wait_message_cond(&g->pmu, + gk20a_get_gr_idle_timeout(g), + &handlerparams.success, 1); + + if (handlerparams.success == 0) { + gk20a_err(dev_from_gk20a(g), "could not process cmd\n"); + status = -ETIMEDOUT; + goto exit; + } + +exit: + return status; +} + +static u32 pmgr_send_i2c_device_topology_to_pmu(struct gk20a *g) +{ + struct nv_pmu_pmgr_i2c_device_desc_table i2c_desc_table; + u32 status = 0; + + /* INA3221 I2C device info */ + i2c_desc_table.dev_mask = 0x01; + + /* INA3221 */ + i2c_desc_table.devices[0].super.type = 0x4E; + + i2c_desc_table.devices[0].dcb_index = 0; + i2c_desc_table.devices[0].i2c_address = 0x84; + i2c_desc_table.devices[0].i2c_flags = 0xC2F; + i2c_desc_table.devices[0].i2c_port = 0x2; + + /* Pass the table down the PMU as an object */ + status = pmgr_pmu_set_object( + g, + NV_PMU_PMGR_OBJECT_I2C_DEVICE_DESC_TABLE, + (u16)sizeof(struct nv_pmu_pmgr_i2c_device_desc_table), + PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED, + &i2c_desc_table); + + if (status) + gk20a_err(dev_from_gk20a(g), + "pmgr_pmu_set_object failed %x", + status); + + return status; +} + +static u32 pmgr_send_pwr_device_topology_to_pmu(struct gk20a *g) +{ + struct nv_pmu_pmgr_pwr_device_desc_table pwr_desc_table; + struct nv_pmu_pmgr_pwr_device_desc_table_header *ppwr_desc_header; + u32 status = 0; + + /* Set the BA-device-independent HW information */ + ppwr_desc_header = &(pwr_desc_table.hdr.data); + ppwr_desc_header->ba_info.b_initialized_and_used = false; + + /* populate the table */ + boardobjgrpe32hdrset((struct nv_pmu_boardobjgrp *)&ppwr_desc_header->super, + g->pmgr_pmu.pmgr_deviceobjs.super.super.objmask); + + status = boardobjgrp_pmudatainit_legacy(g, + &g->pmgr_pmu.pmgr_deviceobjs.super.super, + (struct nv_pmu_boardobjgrp_super *)&pwr_desc_table); + + if (status) { + gk20a_err(dev_from_gk20a(g), + "boardobjgrp_pmudatainit_legacy failed %x", + status); + goto exit; + } + + /* Pass the table down the PMU as an object */ + status = pmgr_pmu_set_object( + g, + NV_PMU_PMGR_OBJECT_PWR_DEVICE_DESC_TABLE, + (u16)sizeof( + union nv_pmu_pmgr_pwr_device_dmem_size), + (u16)sizeof(struct nv_pmu_pmgr_pwr_device_desc_table), + &pwr_desc_table); + + if (status) + gk20a_err(dev_from_gk20a(g), + "pmgr_pmu_set_object failed %x", + status); + +exit: + return status; +} + +static u32 pmgr_send_pwr_mointer_to_pmu(struct gk20a *g) +{ + struct nv_pmu_pmgr_pwr_monitor_pack pwr_monitor_pack; + struct nv_pmu_pmgr_pwr_channel_header *pwr_channel_hdr; + struct nv_pmu_pmgr_pwr_chrelationship_header *pwr_chrelationship_header; + u32 max_dmem_size; + u32 status = 0; + + /* Copy all the global settings from the RM copy */ + pwr_channel_hdr = &(pwr_monitor_pack.channels.hdr.data); + pwr_monitor_pack = g->pmgr_pmu.pmgr_monitorobjs.pmu_data; + + boardobjgrpe32hdrset((struct nv_pmu_boardobjgrp *)&pwr_channel_hdr->super, + g->pmgr_pmu.pmgr_monitorobjs.pwr_channels.super.objmask); + + /* Copy in each channel */ + status = boardobjgrp_pmudatainit_legacy(g, + &g->pmgr_pmu.pmgr_monitorobjs.pwr_channels.super, + (struct nv_pmu_boardobjgrp_super *)&(pwr_monitor_pack.channels)); + + if (status) { + gk20a_err(dev_from_gk20a(g), + "boardobjgrp_pmudatainit_legacy failed %x", + status); + goto exit; + } + + /* Copy in each channel relationship */ + pwr_chrelationship_header = &(pwr_monitor_pack.ch_rels.hdr.data); + + boardobjgrpe32hdrset((struct nv_pmu_boardobjgrp *)&pwr_chrelationship_header->super, + g->pmgr_pmu.pmgr_monitorobjs.pwr_ch_rels.super.objmask); + + pwr_channel_hdr->physical_channel_mask = g->pmgr_pmu.pmgr_monitorobjs.physical_channel_mask; + pwr_channel_hdr->type = NV_PMU_PMGR_PWR_MONITOR_TYPE_NO_POLLING; + + status = boardobjgrp_pmudatainit_legacy(g, + &g->pmgr_pmu.pmgr_monitorobjs.pwr_ch_rels.super, + (struct nv_pmu_boardobjgrp_super *)&(pwr_monitor_pack.ch_rels)); + + if (status) { + gk20a_err(dev_from_gk20a(g), + "boardobjgrp_pmudatainit_legacy failed %x", + status); + goto exit; + } + + /* Calculate the max Dmem buffer size */ + max_dmem_size = sizeof(union nv_pmu_pmgr_pwr_monitor_dmem_size); + + /* Pass the table down the PMU as an object */ + status = pmgr_pmu_set_object( + g, + NV_PMU_PMGR_OBJECT_PWR_MONITOR, + (u16)max_dmem_size, + (u16)sizeof(struct nv_pmu_pmgr_pwr_monitor_pack), + &pwr_monitor_pack); + + if (status) + gk20a_err(dev_from_gk20a(g), + "pmgr_pmu_set_object failed %x", + status); + +exit: + return status; +} + +u32 pmgr_send_pwr_policy_to_pmu(struct gk20a *g) +{ + struct nv_pmu_pmgr_pwr_policy_pack *ppwrpack = NULL; + struct pwr_policy *ppolicy = NULL; + u32 status = 0; + u8 indx; + u32 max_dmem_size; + + ppwrpack = kzalloc(sizeof(struct nv_pmu_pmgr_pwr_policy_pack), GFP_KERNEL); + if (!ppwrpack) { + gk20a_err(dev_from_gk20a(g), + "pwr policy alloc failed %x", + status); + status = -ENOMEM; + goto exit; + } + + ppwrpack->policies.hdr.data.version = g->pmgr_pmu.pmgr_policyobjs.version; + ppwrpack->policies.hdr.data.b_enabled = g->pmgr_pmu.pmgr_policyobjs.b_enabled; + + boardobjgrpe32hdrset((struct nv_pmu_boardobjgrp *) + &ppwrpack->policies.hdr.data.super, + g->pmgr_pmu.pmgr_policyobjs.pwr_policies.super.objmask); + + memset(&ppwrpack->policies.hdr.data.reserved_pmu_policy_mask, + 0, + sizeof(ppwrpack->policies.hdr.data.reserved_pmu_policy_mask)); + + ppwrpack->policies.hdr.data.base_sample_period = + g->pmgr_pmu.pmgr_policyobjs.base_sample_period; + ppwrpack->policies.hdr.data.min_client_sample_period = + g->pmgr_pmu.pmgr_policyobjs.min_client_sample_period; + ppwrpack->policies.hdr.data.low_sampling_mult = + g->pmgr_pmu.pmgr_policyobjs.low_sampling_mult; + + memcpy(&ppwrpack->policies.hdr.data.global_ceiling, + &g->pmgr_pmu.pmgr_policyobjs.global_ceiling, + sizeof(struct nv_pmu_perf_domain_group_limits)); + + memcpy(&ppwrpack->policies.hdr.data.semantic_policy_tbl, + &g->pmgr_pmu.pmgr_policyobjs.policy_idxs, + sizeof(g->pmgr_pmu.pmgr_policyobjs.policy_idxs)); + + BOARDOBJGRP_FOR_EACH_INDEX_IN_MASK(32, indx, + ppwrpack->policies.hdr.data.super.obj_mask.super.data[0]) { + ppolicy = PMGR_GET_PWR_POLICY(g, indx); + + status = ((struct boardobj *)ppolicy)->pmudatainit(g, (struct boardobj *)ppolicy, + (struct nv_pmu_boardobj *)&(ppwrpack->policies.policies[indx].data)); + if (status) { + gk20a_err(dev_from_gk20a(g), + "pmudatainit failed %x indx %x", + status, indx); + status = -ENOMEM; + goto exit; + } + } + BOARDOBJGRP_FOR_EACH_INDEX_IN_MASK_END; + + boardobjgrpe32hdrset((struct nv_pmu_boardobjgrp *) + &ppwrpack->policy_rels.hdr.data.super, + g->pmgr_pmu.pmgr_policyobjs.pwr_policy_rels.super.objmask); + + boardobjgrpe32hdrset((struct nv_pmu_boardobjgrp *) + &ppwrpack->violations.hdr.data.super, + g->pmgr_pmu.pmgr_policyobjs.pwr_violations.super.objmask); + + max_dmem_size = sizeof(union nv_pmu_pmgr_pwr_policy_dmem_size); + + /* Pass the table down the PMU as an object */ + status = pmgr_pmu_set_object( + g, + NV_PMU_PMGR_OBJECT_PWR_POLICY, + (u16)max_dmem_size, + (u16)sizeof(struct nv_pmu_pmgr_pwr_policy_pack), + ppwrpack); + + if (status) + gk20a_err(dev_from_gk20a(g), + "pmgr_pmu_set_object failed %x", + status); + +exit: + if (ppwrpack) { + kfree(ppwrpack); + } + + return status; +} + +u32 pmgr_pmu_pwr_devices_query_blocking( + struct gk20a *g, + u32 pwr_dev_mask, + struct nv_pmu_pmgr_pwr_devices_query_payload *ppayload) +{ + struct pmu_cmd cmd = { {0} }; + struct pmu_payload payload = { {0} }; + struct nv_pmu_pmgr_cmd_pwr_devices_query *pcmd; + u32 status; + u32 seqdesc; + struct pmgr_pmucmdhandler_params handlerparams = {0}; + + cmd.hdr.unit_id = PMU_UNIT_PMGR; + cmd.hdr.size = (u32)sizeof(struct nv_pmu_pmgr_cmd_pwr_devices_query) + + (u32)sizeof(struct pmu_hdr); + + pcmd = &cmd.cmd.pmgr.pwr_dev_query; + pcmd->cmd_type = NV_PMU_PMGR_CMD_ID_PWR_DEVICES_QUERY; + pcmd->dev_mask = pwr_dev_mask; + + payload.out.buf = ppayload; + payload.out.size = sizeof(struct nv_pmu_pmgr_pwr_devices_query_payload); + payload.out.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; + payload.out.offset = NV_PMU_PMGR_PWR_DEVICES_QUERY_ALLOC_OFFSET; + + /* Setup the handler params to communicate back results.*/ + handlerparams.success = 0; + + status = gk20a_pmu_cmd_post(g, &cmd, NULL, &payload, + PMU_COMMAND_QUEUE_LPQ, + pmgr_pmucmdhandler, + (void *)&handlerparams, + &seqdesc, ~0); + if (status) { + gk20a_err(dev_from_gk20a(g), + "unable to post pmgr query cmd for unit %x cmd id %x dev mask %x", + cmd.hdr.unit_id, pcmd->cmd_type, pcmd->dev_mask); + goto exit; + } + + pmu_wait_message_cond(&g->pmu, + gk20a_get_gr_idle_timeout(g), + &handlerparams.success, 1); + + if (handlerparams.success == 0) { + gk20a_err(dev_from_gk20a(g), "could not process cmd\n"); + status = -ETIMEDOUT; + goto exit; + } + +exit: + return status; +} + +static u32 pmgr_pmu_load_blocking(struct gk20a *g) +{ + struct pmu_cmd cmd = { {0} }; + struct nv_pmu_pmgr_cmd_load *pcmd; + u32 status; + u32 seqdesc; + struct pmgr_pmucmdhandler_params handlerparams = {0}; + + cmd.hdr.unit_id = PMU_UNIT_PMGR; + cmd.hdr.size = (u32)sizeof(struct nv_pmu_pmgr_cmd_load) + + (u32)sizeof(struct pmu_hdr); + + pcmd = &cmd.cmd.pmgr.load; + pcmd->cmd_type = NV_PMU_PMGR_CMD_ID_LOAD; + + /* Setup the handler params to communicate back results.*/ + handlerparams.success = 0; + + status = gk20a_pmu_cmd_post(g, &cmd, NULL, NULL, + PMU_COMMAND_QUEUE_LPQ, + pmgr_pmucmdhandler, + (void *)&handlerparams, + &seqdesc, ~0); + if (status) { + gk20a_err(dev_from_gk20a(g), + "unable to post pmgr load cmd for unit %x cmd id %x", + cmd.hdr.unit_id, pcmd->cmd_type); + goto exit; + } + + pmu_wait_message_cond(&g->pmu, + gk20a_get_gr_idle_timeout(g), + &handlerparams.success, 1); + + if (handlerparams.success == 0) { + gk20a_err(dev_from_gk20a(g), "could not process cmd\n"); + status = -ETIMEDOUT; + goto exit; + } + +exit: + return status; +} + +u32 pmgr_send_pmgr_tables_to_pmu(struct gk20a *g) +{ + u32 status = 0; + + status = pmgr_send_i2c_device_topology_to_pmu(g); + + if (status) { + gk20a_err(dev_from_gk20a(g), + "pmgr_send_i2c_device_topology_to_pmu failed %x", + status); + goto exit; + } + + if (!BOARDOBJGRP_IS_EMPTY(&g->pmgr_pmu.pmgr_deviceobjs.super.super)) { + status = pmgr_send_pwr_device_topology_to_pmu(g); + if (status) { + gk20a_err(dev_from_gk20a(g), + "pmgr_send_pwr_device_topology_to_pmu failed %x", + status); + goto exit; + } + } + + if (!(BOARDOBJGRP_IS_EMPTY( + &g->pmgr_pmu.pmgr_monitorobjs.pwr_channels.super)) || + !(BOARDOBJGRP_IS_EMPTY( + &g->pmgr_pmu.pmgr_monitorobjs.pwr_ch_rels.super))) { + status = pmgr_send_pwr_mointer_to_pmu(g); + if (status) { + gk20a_err(dev_from_gk20a(g), + "pmgr_send_pwr_mointer_to_pmu failed %x", status); + goto exit; + } + } + + if (!(BOARDOBJGRP_IS_EMPTY( + &g->pmgr_pmu.pmgr_policyobjs.pwr_policies.super)) || + !(BOARDOBJGRP_IS_EMPTY( + &g->pmgr_pmu.pmgr_policyobjs.pwr_policy_rels.super)) || + !(BOARDOBJGRP_IS_EMPTY( + &g->pmgr_pmu.pmgr_policyobjs.pwr_violations.super))) { + status = pmgr_send_pwr_policy_to_pmu(g); + if (status) { + gk20a_err(dev_from_gk20a(g), + "pmgr_send_pwr_policy_to_pmu failed %x", status); + goto exit; + } + } + + status = pmgr_pmu_load_blocking(g); + if (status) { + gk20a_err(dev_from_gk20a(g), + "pmgr_send_pwr_mointer_to_pmu failed %x", status); + goto exit; + } + +exit: + return status; +} diff --git a/drivers/gpu/nvgpu/pmgr/pmgrpmu.h b/drivers/gpu/nvgpu/pmgr/pmgrpmu.h new file mode 100644 index 00000000..6b48396c --- /dev/null +++ b/drivers/gpu/nvgpu/pmgr/pmgrpmu.h @@ -0,0 +1,29 @@ +/* + * general power device control structures & definitions + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#ifndef _PMGRPMU_H_ +#define _PMGRPMU_H_ + +#include "gk20a/gk20a.h" +#include "pwrdev.h" +#include "pwrmonitor.h" + +u32 pmgr_send_pmgr_tables_to_pmu(struct gk20a *g); + +u32 pmgr_pmu_pwr_devices_query_blocking( + struct gk20a *g, + u32 pwr_dev_mask, + struct nv_pmu_pmgr_pwr_devices_query_payload *ppayload); + +#endif diff --git a/drivers/gpu/nvgpu/pmgr/pwrdev.c b/drivers/gpu/nvgpu/pmgr/pwrdev.c new file mode 100644 index 00000000..03e2eb34 --- /dev/null +++ b/drivers/gpu/nvgpu/pmgr/pwrdev.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "gk20a/gk20a.h" +#include "pwrdev.h" +#include "include/bios.h" +#include "boardobj/boardobjgrp.h" +#include "boardobj/boardobjgrp_e32.h" +#include "pmuif/gpmuifboardobj.h" +#include "pmuif/gpmuifpmgr.h" +#include "gm206/bios_gm206.h" +#include "gk20a/pmu_gk20a.h" + +static u32 _pwr_device_pmudata_instget(struct gk20a *g, + struct nv_pmu_boardobjgrp *pmuboardobjgrp, + struct nv_pmu_boardobj **ppboardobjpmudata, + u8 idx) +{ + struct nv_pmu_pmgr_pwr_device_desc_table *ppmgrdevice = + (struct nv_pmu_pmgr_pwr_device_desc_table *)pmuboardobjgrp; + + gk20a_dbg_info(""); + + /*check whether pmuboardobjgrp has a valid boardobj in index*/ + if (((u32)BIT(idx) & + ppmgrdevice->hdr.data.super.obj_mask.super.data[0]) == 0) + return -EINVAL; + + *ppboardobjpmudata = (struct nv_pmu_boardobj *) + &ppmgrdevice->devices[idx].data.board_obj; + + gk20a_dbg_info(" Done"); + + return 0; +} + +static u32 _pwr_domains_pmudatainit_ina3221(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + struct nv_pmu_pmgr_pwr_device_desc_ina3221 *ina3221_desc; + struct pwr_device_ina3221 *ina3221; + u32 status = 0; + u32 indx; + + status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error updating pmu boardobjgrp for pwr domain 0x%x", + status); + goto done; + } + + ina3221 = (struct pwr_device_ina3221 *)board_obj_ptr; + ina3221_desc = (struct nv_pmu_pmgr_pwr_device_desc_ina3221 *) ppmudata; + + ina3221_desc->super.power_corr_factor = ina3221->super.power_corr_factor; + ina3221_desc->i2c_dev_idx = ina3221->super.i2c_dev_idx; + ina3221_desc->configuration = ina3221->configuration; + ina3221_desc->mask_enable = ina3221->mask_enable; + /* configure NV_PMU_THERM_EVENT_EXT_OVERT */ + ina3221_desc->event_mask = (1 << 0); + ina3221_desc->curr_correct_m = ina3221->curr_correct_m; + ina3221_desc->curr_correct_b = ina3221->curr_correct_b; + + for (indx = 0; indx < NV_PMU_PMGR_PWR_DEVICE_INA3221_CH_NUM; indx++) { + ina3221_desc->r_shuntm_ohm[indx] = ina3221->r_shuntm_ohm[indx]; + } + +done: + return status; +} + +static struct boardobj *construct_pwr_device(struct gk20a *g, + void *pargs, u16 pargs_size, u8 type) +{ + struct boardobj *board_obj_ptr = NULL; + u32 status; + u32 indx; + struct pwr_device_ina3221 *pwrdev; + struct pwr_device_ina3221 *ina3221 = (struct pwr_device_ina3221*)pargs; + + status = boardobj_construct_super(g, &board_obj_ptr, + pargs_size, pargs); + if (status) + return NULL; + + pwrdev = (struct pwr_device_ina3221*)board_obj_ptr; + + /* Set Super class interfaces */ + board_obj_ptr->pmudatainit = _pwr_domains_pmudatainit_ina3221; + pwrdev->super.power_rail = ina3221->super.power_rail; + pwrdev->super.i2c_dev_idx = ina3221->super.i2c_dev_idx; + pwrdev->super.power_corr_factor = (1 << 12); + pwrdev->super.bIs_inforom_config = false; + + /* Set INA3221-specific information */ + pwrdev->configuration = ina3221->configuration; + pwrdev->mask_enable = ina3221->mask_enable; + pwrdev->gpio_function = ina3221->gpio_function; + pwrdev->curr_correct_m = ina3221->curr_correct_m; + pwrdev->curr_correct_b = ina3221->curr_correct_b; + + for (indx = 0; indx < NV_PMU_PMGR_PWR_DEVICE_INA3221_CH_NUM; indx++) { + pwrdev->r_shuntm_ohm[indx] = ina3221->r_shuntm_ohm[indx]; + } + + gk20a_dbg_info(" Done"); + + return board_obj_ptr; +} + +static u32 devinit_get_pwr_device_table(struct gk20a *g, + struct pwr_devices *ppwrdeviceobjs) +{ + u32 status = 0; + u8 *pwr_device_table_ptr = NULL; + u8 *curr_pwr_device_table_ptr = NULL; + struct boardobj *boardobj; + struct pwr_sensors_2x_header pwr_sensor_table_header = { 0 }; + struct pwr_sensors_2x_entry pwr_sensor_table_entry = { 0 }; + u32 index; + u32 obj_index = 0; + u16 pwr_device_size; + union { + struct boardobj boardobj; + struct pwr_device pwrdev; + struct pwr_device_ina3221 ina3221; + } pwr_device_data; + + gk20a_dbg_info(""); + + if (g->ops.bios.get_perf_table_ptrs != NULL) { + pwr_device_table_ptr = (u8 *)g->ops.bios.get_perf_table_ptrs(g, + g->bios.perf_token, POWER_SENSORS_TABLE); + if (pwr_device_table_ptr == NULL) { + status = -EINVAL; + goto done; + } + } + + memcpy(&pwr_sensor_table_header, pwr_device_table_ptr, + VBIOS_POWER_SENSORS_2X_HEADER_SIZE_08); + + if (pwr_sensor_table_header.version != + VBIOS_POWER_SENSORS_VERSION_2X) { + status = -EINVAL; + goto done; + } + + if (pwr_sensor_table_header.header_size < + VBIOS_POWER_SENSORS_2X_HEADER_SIZE_08) { + status = -EINVAL; + goto done; + } + + if (pwr_sensor_table_header.table_entry_size != + VBIOS_POWER_SENSORS_2X_ENTRY_SIZE_15) { + status = -EINVAL; + goto done; + } + + curr_pwr_device_table_ptr = (pwr_device_table_ptr + + VBIOS_POWER_SENSORS_2X_HEADER_SIZE_08); + + for (index = 0; index < pwr_sensor_table_header.num_table_entries; index++) { + bool use_fxp8_8 = false; + u8 i2c_dev_idx; + u8 device_type; + + curr_pwr_device_table_ptr += (pwr_sensor_table_header.table_entry_size * index); + + pwr_sensor_table_entry.flags0 = *curr_pwr_device_table_ptr; + + memcpy(&pwr_sensor_table_entry.class_param0, + (curr_pwr_device_table_ptr + 1), + (VBIOS_POWER_SENSORS_2X_ENTRY_SIZE_15 - 1)); + + device_type = (u8)BIOS_GET_FIELD( + pwr_sensor_table_entry.flags0, + NV_VBIOS_POWER_SENSORS_2X_ENTRY_FLAGS0_CLASS); + + if (device_type == NV_VBIOS_POWER_SENSORS_2X_ENTRY_FLAGS0_CLASS_I2C) { + i2c_dev_idx = (u8)BIOS_GET_FIELD( + pwr_sensor_table_entry.class_param0, + NV_VBIOS_POWER_SENSORS_2X_ENTRY_CLASS_PARAM0_I2C_INDEX); + use_fxp8_8 = (u8)BIOS_GET_FIELD( + pwr_sensor_table_entry.class_param0, + NV_VBIOS_POWER_SENSORS_2X_ENTRY_CLASS_PARAM0_I2C_USE_FXP8_8); + + pwr_device_data.ina3221.super.i2c_dev_idx = i2c_dev_idx; + pwr_device_data.ina3221.r_shuntm_ohm[0].use_fxp8_8 = use_fxp8_8; + pwr_device_data.ina3221.r_shuntm_ohm[1].use_fxp8_8 = use_fxp8_8; + pwr_device_data.ina3221.r_shuntm_ohm[2].use_fxp8_8 = use_fxp8_8; + pwr_device_data.ina3221.r_shuntm_ohm[0].rshunt_value = + (u16)BIOS_GET_FIELD( + pwr_sensor_table_entry.sensor_param0, + NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM0_INA3221_RSHUNT0_MOHM); + + pwr_device_data.ina3221.r_shuntm_ohm[1].rshunt_value = + (u16)BIOS_GET_FIELD( + pwr_sensor_table_entry.sensor_param0, + NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM0_INA3221_RSHUNT1_MOHM); + + pwr_device_data.ina3221.r_shuntm_ohm[2].rshunt_value = + (u16)BIOS_GET_FIELD( + pwr_sensor_table_entry.sensor_param1, + NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM1_INA3221_RSHUNT2_MOHM); + pwr_device_data.ina3221.configuration = + (u16)BIOS_GET_FIELD( + pwr_sensor_table_entry.sensor_param1, + NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM1_INA3221_CONFIGURATION); + + pwr_device_data.ina3221.mask_enable = + (u16)BIOS_GET_FIELD( + pwr_sensor_table_entry.sensor_param2, + NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM2_INA3221_MASKENABLE); + + pwr_device_data.ina3221.gpio_function = + (u8)BIOS_GET_FIELD( + pwr_sensor_table_entry.sensor_param2, + NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM2_INA3221_GPIOFUNCTION); + + pwr_device_data.ina3221.curr_correct_m = + (u16)BIOS_GET_FIELD( + pwr_sensor_table_entry.sensor_param3, + NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM3_INA3221_CURR_CORRECT_M); + + pwr_device_data.ina3221.curr_correct_b = + (u16)BIOS_GET_FIELD( + pwr_sensor_table_entry.sensor_param3, + NV_VBIOS_POWER_SENSORS_2X_ENTRY_SENSOR_PARAM3_INA3221_CURR_CORRECT_B); + + if (!pwr_device_data.ina3221.curr_correct_m) { + pwr_device_data.ina3221.curr_correct_m = (1 << 12); + } + pwr_device_size = sizeof(struct pwr_device_ina3221); + } else + continue; + + pwr_device_data.boardobj.type = CTRL_PMGR_PWR_DEVICE_TYPE_INA3221; + pwr_device_data.pwrdev.power_rail = (u8)0; + + boardobj = construct_pwr_device(g, &pwr_device_data, + pwr_device_size, pwr_device_data.boardobj.type); + + if (!boardobj) { + gk20a_err(dev_from_gk20a(g), + "unable to create pwr device for %d type %d", index, pwr_device_data.boardobj.type); + status = -EINVAL; + goto done; + } + + status = boardobjgrp_objinsert(&ppwrdeviceobjs->super.super, + boardobj, obj_index); + + if (status) { + gk20a_err(dev_from_gk20a(g), + "unable to insert pwr device boardobj for %d", index); + status = -EINVAL; + goto done; + } + + ++obj_index; + } + +done: + gk20a_dbg_info(" done status %x", status); + return status; +} + +u32 pmgr_device_sw_setup(struct gk20a *g) +{ + u32 status; + struct boardobjgrp *pboardobjgrp = NULL; + struct pwr_devices *ppwrdeviceobjs; + + /* Construct the Super Class and override the Interfaces */ + status = boardobjgrpconstruct_e32(&g->pmgr_pmu.pmgr_deviceobjs.super); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error creating boardobjgrp for pmgr devices, status - 0x%x", + status); + goto done; + } + + pboardobjgrp = &g->pmgr_pmu.pmgr_deviceobjs.super.super; + ppwrdeviceobjs = &(g->pmgr_pmu.pmgr_deviceobjs); + + /* Override the Interfaces */ + pboardobjgrp->pmudatainstget = _pwr_device_pmudata_instget; + + status = devinit_get_pwr_device_table(g, ppwrdeviceobjs); + if (status) + goto done; + +done: + gk20a_dbg_info(" done status %x", status); + return status; +} diff --git a/drivers/gpu/nvgpu/pmgr/pwrdev.h b/drivers/gpu/nvgpu/pmgr/pwrdev.h new file mode 100644 index 00000000..b8592a18 --- /dev/null +++ b/drivers/gpu/nvgpu/pmgr/pwrdev.h @@ -0,0 +1,51 @@ +/* + * general power device structures & definitions + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#ifndef _PWRDEV_H_ +#define _PWRDEV_H_ + +#include "boardobj/boardobj.h" +#include "pmuif/gpmuifpmgr.h" +#include "ctrl/ctrlpmgr.h" + +#define PWRDEV_I2CDEV_DEVICE_INDEX_NONE (0xFF) + +#define PWR_DEVICE_PROV_NUM_DEFAULT 1 + +struct pwr_device { + struct boardobj super; + u8 power_rail; + u8 i2c_dev_idx; + bool bIs_inforom_config; + u32 power_corr_factor; +}; + +struct pwr_devices { + struct boardobjgrp_e32 super; +}; + +struct pwr_device_ina3221 { + struct pwr_device super; + struct ctrl_pmgr_pwr_device_info_rshunt + r_shuntm_ohm[NV_PMU_PMGR_PWR_DEVICE_INA3221_CH_NUM]; + u16 configuration; + u16 mask_enable; + u8 gpio_function; + u16 curr_correct_m; + s16 curr_correct_b; +} ; + +u32 pmgr_device_sw_setup(struct gk20a *g); + +#endif diff --git a/drivers/gpu/nvgpu/pmgr/pwrmonitor.c b/drivers/gpu/nvgpu/pmgr/pwrmonitor.c new file mode 100644 index 00000000..c28751fd --- /dev/null +++ b/drivers/gpu/nvgpu/pmgr/pwrmonitor.c @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "gk20a/gk20a.h" +#include "pwrdev.h" +#include "include/bios.h" +#include "boardobj/boardobjgrp.h" +#include "boardobj/boardobjgrp_e32.h" +#include "pmuif/gpmuifboardobj.h" +#include "pmuif/gpmuifpmgr.h" +#include "gm206/bios_gm206.h" +#include "gk20a/pmu_gk20a.h" + +static u32 _pwr_channel_pmudata_instget(struct gk20a *g, + struct nv_pmu_boardobjgrp *pmuboardobjgrp, + struct nv_pmu_boardobj **ppboardobjpmudata, + u8 idx) +{ + struct nv_pmu_pmgr_pwr_channel_desc *ppmgrchannel = + (struct nv_pmu_pmgr_pwr_channel_desc *)pmuboardobjgrp; + + gk20a_dbg_info(""); + + /*check whether pmuboardobjgrp has a valid boardobj in index*/ + if (((u32)BIT(idx) & + ppmgrchannel->hdr.data.super.obj_mask.super.data[0]) == 0) + return -EINVAL; + + *ppboardobjpmudata = (struct nv_pmu_boardobj *) + &ppmgrchannel->channels[idx].data.board_obj; + + /* handle Global/common data here as we need index */ + ppmgrchannel->channels[idx].data.pwr_channel.ch_idx = idx; + + gk20a_dbg_info(" Done"); + + return 0; +} + +static u32 _pwr_channel_rels_pmudata_instget(struct gk20a *g, + struct nv_pmu_boardobjgrp *pmuboardobjgrp, + struct nv_pmu_boardobj **ppboardobjpmudata, + u8 idx) +{ + struct nv_pmu_pmgr_pwr_chrelationship_desc *ppmgrchrels = + (struct nv_pmu_pmgr_pwr_chrelationship_desc *)pmuboardobjgrp; + + gk20a_dbg_info(""); + + /*check whether pmuboardobjgrp has a valid boardobj in index*/ + if (((u32)BIT(idx) & + ppmgrchrels->hdr.data.super.obj_mask.super.data[0]) == 0) + return -EINVAL; + + *ppboardobjpmudata = (struct nv_pmu_boardobj *) + &ppmgrchrels->ch_rels[idx].data.board_obj; + + gk20a_dbg_info(" Done"); + + return 0; +} + +static u32 _pwr_channel_state_init(struct gk20a *g) +{ + u8 indx = 0; + struct pwr_channel *pchannel; + u32 objmask = + g->pmgr_pmu.pmgr_monitorobjs.pwr_channels.super.objmask; + + /* Initialize each PWR_CHANNEL's dependent channel mask */ + BOARDOBJGRP_FOR_EACH_INDEX_IN_MASK(32, indx, objmask) { + pchannel = PMGR_PWR_MONITOR_GET_PWR_CHANNEL(g, indx); + if (pchannel == NULL) { + gk20a_err(dev_from_gk20a(g), + "PMGR_PWR_MONITOR_GET_PWR_CHANNEL-failed %d", indx); + return -EINVAL; + } + pchannel->dependent_ch_mask =0; + } + BOARDOBJGRP_FOR_EACH_INDEX_IN_MASK_END + + return 0; +} + +static bool _pwr_channel_implements(struct pwr_channel *pchannel, + u8 type) +{ + return (type == BOARDOBJ_GET_TYPE(pchannel)); +} + +static u32 _pwr_domains_pmudatainit_sensor(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + struct nv_pmu_pmgr_pwr_channel_sensor *pmu_sensor_data; + struct pwr_channel_sensor *sensor; + u32 status = 0; + + status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error updating pmu boardobjgrp for pwr sensor 0x%x", + status); + goto done; + } + + sensor = (struct pwr_channel_sensor *)board_obj_ptr; + pmu_sensor_data = (struct nv_pmu_pmgr_pwr_channel_sensor *) ppmudata; + + pmu_sensor_data->super.pwr_rail = sensor->super.pwr_rail; + pmu_sensor_data->super.volt_fixedu_v = sensor->super.volt_fixed_uv; + pmu_sensor_data->super.pwr_corr_slope = sensor->super.pwr_corr_slope; + pmu_sensor_data->super.pwr_corr_offsetm_w = sensor->super.pwr_corr_offset_mw; + pmu_sensor_data->super.curr_corr_slope = sensor->super.curr_corr_slope; + pmu_sensor_data->super.curr_corr_offsetm_a = sensor->super.curr_corr_offset_ma; + pmu_sensor_data->super.dependent_ch_mask = sensor->super.dependent_ch_mask; + pmu_sensor_data->super.ch_idx = 0; + + pmu_sensor_data->pwr_dev_idx = sensor->pwr_dev_idx; + pmu_sensor_data->pwr_dev_prov_idx = sensor->pwr_dev_prov_idx; + +done: + return status; +} + +static struct boardobj *construct_pwr_topology(struct gk20a *g, + void *pargs, u16 pargs_size, u8 type) +{ + struct boardobj *board_obj_ptr = NULL; + u32 status; + struct pwr_channel_sensor *pwrchannel; + struct pwr_channel_sensor *sensor = (struct pwr_channel_sensor*)pargs; + + status = boardobj_construct_super(g, &board_obj_ptr, + pargs_size, pargs); + if (status) + return NULL; + + pwrchannel = (struct pwr_channel_sensor*)board_obj_ptr; + + /* Set Super class interfaces */ + board_obj_ptr->pmudatainit = _pwr_domains_pmudatainit_sensor; + + pwrchannel->super.pwr_rail = sensor->super.pwr_rail; + pwrchannel->super.volt_fixed_uv = sensor->super.volt_fixed_uv; + pwrchannel->super.pwr_corr_slope = sensor->super.pwr_corr_slope; + pwrchannel->super.pwr_corr_offset_mw = sensor->super.pwr_corr_offset_mw; + pwrchannel->super.curr_corr_slope = sensor->super.curr_corr_slope; + pwrchannel->super.curr_corr_offset_ma = sensor->super.curr_corr_offset_ma; + pwrchannel->super.dependent_ch_mask = 0; + + pwrchannel->pwr_dev_idx = sensor->pwr_dev_idx; + pwrchannel->pwr_dev_prov_idx = sensor->pwr_dev_prov_idx; + + gk20a_dbg_info(" Done"); + + return board_obj_ptr; +} + +static u32 devinit_get_pwr_topology_table(struct gk20a *g, + struct pmgr_pwr_monitor *ppwrmonitorobjs) +{ + u32 status = 0; + u8 *pwr_topology_table_ptr = NULL; + u8 *curr_pwr_topology_table_ptr = NULL; + struct boardobj *boardobj; + struct pwr_topology_2x_header pwr_topology_table_header = { 0 }; + struct pwr_topology_2x_entry pwr_topology_table_entry = { 0 }; + u32 index; + u32 obj_index = 0; + u16 pwr_topology_size; + union { + struct boardobj boardobj; + struct pwr_channel pwrchannel; + struct pwr_channel_sensor sensor; + } pwr_topology_data; + + gk20a_dbg_info(""); + + if (g->ops.bios.get_perf_table_ptrs != NULL) { + pwr_topology_table_ptr = (u8 *)g->ops.bios.get_perf_table_ptrs(g, + g->bios.perf_token, POWER_TOPOLOGY_TABLE); + if (pwr_topology_table_ptr == NULL) { + status = -EINVAL; + goto done; + } + } + + memcpy(&pwr_topology_table_header, pwr_topology_table_ptr, + VBIOS_POWER_TOPOLOGY_2X_HEADER_SIZE_06); + + if (pwr_topology_table_header.version != + VBIOS_POWER_TOPOLOGY_VERSION_2X) { + status = -EINVAL; + goto done; + } + + g->pmgr_pmu.pmgr_monitorobjs.b_is_topology_tbl_ver_1x = false; + + if (pwr_topology_table_header.header_size < + VBIOS_POWER_TOPOLOGY_2X_HEADER_SIZE_06) { + status = -EINVAL; + goto done; + } + + if (pwr_topology_table_header.table_entry_size != + VBIOS_POWER_TOPOLOGY_2X_ENTRY_SIZE_16) { + status = -EINVAL; + goto done; + } + + curr_pwr_topology_table_ptr = (pwr_topology_table_ptr + + VBIOS_POWER_TOPOLOGY_2X_HEADER_SIZE_06); + + for (index = 0; index < pwr_topology_table_header.num_table_entries; + index++) { + u8 class_type; + + curr_pwr_topology_table_ptr += (pwr_topology_table_header.table_entry_size * index); + + pwr_topology_table_entry.flags0 = *curr_pwr_topology_table_ptr; + pwr_topology_table_entry.pwr_rail = *(curr_pwr_topology_table_ptr + 1); + + memcpy(&pwr_topology_table_entry.param0, + (curr_pwr_topology_table_ptr + 2), + (VBIOS_POWER_TOPOLOGY_2X_ENTRY_SIZE_16 - 2)); + + class_type = (u8)BIOS_GET_FIELD( + pwr_topology_table_entry.flags0, + NV_VBIOS_POWER_TOPOLOGY_2X_ENTRY_FLAGS0_CLASS); + + if (class_type == NV_VBIOS_POWER_TOPOLOGY_2X_ENTRY_FLAGS0_CLASS_SENSOR) { + pwr_topology_data.sensor.pwr_dev_idx = (u8)BIOS_GET_FIELD( + pwr_topology_table_entry.param1, + NV_VBIOS_POWER_TOPOLOGY_2X_ENTRY_PARAM1_SENSOR_INDEX); + pwr_topology_data.sensor.pwr_dev_prov_idx = (u8)BIOS_GET_FIELD( + pwr_topology_table_entry.param1, + NV_VBIOS_POWER_TOPOLOGY_2X_ENTRY_PARAM1_SENSOR_PROVIDER_INDEX); + + pwr_topology_size = sizeof(struct pwr_channel_sensor); + } else + continue; + + /* Initialize data for the parent class */ + pwr_topology_data.boardobj.type = CTRL_PMGR_PWR_CHANNEL_TYPE_SENSOR; + pwr_topology_data.pwrchannel.pwr_rail = (u8)pwr_topology_table_entry.pwr_rail; + pwr_topology_data.pwrchannel.volt_fixed_uv = pwr_topology_table_entry.param0; + pwr_topology_data.pwrchannel.pwr_corr_slope = (1 << 12); + pwr_topology_data.pwrchannel.pwr_corr_offset_mw = 0; + pwr_topology_data.pwrchannel.curr_corr_slope = + (u32)pwr_topology_table_entry.curr_corr_slope; + pwr_topology_data.pwrchannel.curr_corr_offset_ma = + (s32)pwr_topology_table_entry.curr_corr_offset; + + boardobj = construct_pwr_topology(g, &pwr_topology_data, + pwr_topology_size, pwr_topology_data.boardobj.type); + + if (!boardobj) { + gk20a_err(dev_from_gk20a(g), + "unable to create pwr topology for %d type %d", + index, pwr_topology_data.boardobj.type); + status = -EINVAL; + goto done; + } + + status = boardobjgrp_objinsert(&ppwrmonitorobjs->pwr_channels.super, + boardobj, obj_index); + + if (status) { + gk20a_err(dev_from_gk20a(g), + "unable to insert pwr topology boardobj for %d", index); + status = -EINVAL; + goto done; + } + + ++obj_index; + } + +done: + gk20a_dbg_info(" done status %x", status); + return status; +} + +u32 pmgr_monitor_sw_setup(struct gk20a *g) +{ + u32 status; + struct boardobjgrp *pboardobjgrp = NULL; + struct pwr_channel *pchannel; + struct pmgr_pwr_monitor *ppwrmonitorobjs; + u8 indx = 0; + + /* Construct the Super Class and override the Interfaces */ + status = boardobjgrpconstruct_e32( + &g->pmgr_pmu.pmgr_monitorobjs.pwr_channels); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error creating boardobjgrp for pmgr channel, status - 0x%x", + status); + goto done; + } + + pboardobjgrp = &(g->pmgr_pmu.pmgr_monitorobjs.pwr_channels.super); + + /* Override the Interfaces */ + pboardobjgrp->pmudatainstget = _pwr_channel_pmudata_instget; + + /* Construct the Super Class and override the Interfaces */ + status = boardobjgrpconstruct_e32( + &g->pmgr_pmu.pmgr_monitorobjs.pwr_ch_rels); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error creating boardobjgrp for pmgr channel relationship, status - 0x%x", + status); + goto done; + } + + pboardobjgrp = &(g->pmgr_pmu.pmgr_monitorobjs.pwr_ch_rels.super); + + /* Override the Interfaces */ + pboardobjgrp->pmudatainstget = _pwr_channel_rels_pmudata_instget; + + /* Initialize the Total GPU Power Channel Mask to 0 */ + g->pmgr_pmu.pmgr_monitorobjs.pmu_data.channels.hdr.data.total_gpu_power_channel_mask = 0; + g->pmgr_pmu.pmgr_monitorobjs.total_gpu_channel_idx = + CTRL_PMGR_PWR_CHANNEL_INDEX_INVALID; + + /* Supported topology table version 1.0 */ + g->pmgr_pmu.pmgr_monitorobjs.b_is_topology_tbl_ver_1x = true; + + ppwrmonitorobjs = &(g->pmgr_pmu.pmgr_monitorobjs); + + status = devinit_get_pwr_topology_table(g, ppwrmonitorobjs); + if (status) + goto done; + + status = _pwr_channel_state_init(g); + if (status) + goto done; + + /* Initialise physicalChannelMask */ + g->pmgr_pmu.pmgr_monitorobjs.physical_channel_mask = 0; + + pboardobjgrp = &g->pmgr_pmu.pmgr_monitorobjs.pwr_channels.super; + + BOARDOBJGRP_FOR_EACH(pboardobjgrp, struct pwr_channel *, pchannel, indx) { + if (_pwr_channel_implements(pchannel, + CTRL_PMGR_PWR_CHANNEL_TYPE_SENSOR)) { + g->pmgr_pmu.pmgr_monitorobjs.physical_channel_mask |= BIT(indx); + } + } + +done: + gk20a_dbg_info(" done status %x", status); + return status; +} diff --git a/drivers/gpu/nvgpu/pmgr/pwrmonitor.h b/drivers/gpu/nvgpu/pmgr/pwrmonitor.h new file mode 100644 index 00000000..7cd6b8c9 --- /dev/null +++ b/drivers/gpu/nvgpu/pmgr/pwrmonitor.h @@ -0,0 +1,60 @@ +/* + * general power channel structures & definitions + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#ifndef _PWRMONITOR_H_ +#define _PWRMONITOR_H_ + +#include "boardobj/boardobjgrp.h" +#include "boardobj/boardobj.h" +#include "pmuif/gpmuifpmgr.h" +#include "ctrl/ctrlpmgr.h" + +struct pwr_channel { + struct boardobj super; + u8 pwr_rail; + u32 volt_fixed_uv; + u32 pwr_corr_slope; + s32 pwr_corr_offset_mw; + u32 curr_corr_slope; + s32 curr_corr_offset_ma; + u32 dependent_ch_mask; +}; + +struct pwr_chrelationship { + struct boardobj super; + u8 chIdx; +}; + +struct pwr_channel_sensor { + struct pwr_channel super; + u8 pwr_dev_idx; + u8 pwr_dev_prov_idx; +}; + +struct pmgr_pwr_monitor { + bool b_is_topology_tbl_ver_1x; + struct boardobjgrp_e32 pwr_channels; + struct boardobjgrp_e32 pwr_ch_rels; + u8 total_gpu_channel_idx; + u32 physical_channel_mask; + struct nv_pmu_pmgr_pwr_monitor_pack pmu_data; +}; + +#define PMGR_PWR_MONITOR_GET_PWR_CHANNEL(g, channel_idx) \ + ((struct pwr_channel *)BOARDOBJGRP_OBJ_GET_BY_IDX( \ + &(g->pmgr_pmu.pmgr_monitorobjs.pwr_channels.super), (channel_idx))) + +u32 pmgr_monitor_sw_setup(struct gk20a *g); + +#endif diff --git a/drivers/gpu/nvgpu/pmgr/pwrpolicy.c b/drivers/gpu/nvgpu/pmgr/pwrpolicy.c new file mode 100644 index 00000000..bec13b0c --- /dev/null +++ b/drivers/gpu/nvgpu/pmgr/pwrpolicy.c @@ -0,0 +1,680 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "gk20a/gk20a.h" +#include "pwrpolicy.h" +#include "include/bios.h" +#include "boardobj/boardobjgrp.h" +#include "boardobj/boardobjgrp_e32.h" +#include "pmuif/gpmuifboardobj.h" +#include "pmuif/gpmuifpmgr.h" +#include "gm206/bios_gm206.h" +#include "gk20a/pmu_gk20a.h" + +#define _pwr_policy_limitarboutputget_helper(p_limit_arb) (p_limit_arb)->output +#define _pwr_policy_limitdeltaapply(limit, delta) ((u32)max(((s32)limit) + (delta), 0)) + +static u32 _pwr_policy_limitarbinputset_helper(struct gk20a *g, + struct ctrl_pmgr_pwr_policy_limit_arbitration *p_limit_arb, + u8 client_idx, + u32 limit_value) +{ + u8 indx; + bool b_found = false; + u32 status = 0; + u32 output = limit_value; + + for (indx = 0; indx< p_limit_arb->num_inputs; indx++) { + if (p_limit_arb->inputs[indx].pwr_policy_idx == client_idx) { + p_limit_arb->inputs[indx].limit_value = limit_value; + b_found = true; + } else if (p_limit_arb->b_arb_max) { + output = max(output, p_limit_arb->inputs[indx].limit_value); + } else { + output = min(output, p_limit_arb->inputs[indx].limit_value); + } + } + + if (!b_found) { + if (p_limit_arb->num_inputs < + CTRL_PMGR_PWR_POLICY_MAX_LIMIT_INPUTS) { + p_limit_arb->inputs[ + p_limit_arb->num_inputs].pwr_policy_idx = client_idx; + p_limit_arb->inputs[ + p_limit_arb->num_inputs].limit_value = limit_value; + p_limit_arb->num_inputs++; + } else { + gk20a_err(g->dev, "No entries remaining for clientIdx=%d", + client_idx); + status = -EINVAL; + } + } + + if (!status) { + p_limit_arb->output = output; + } + + return status; +} + +static u32 _pwr_policy_limitid_translate(struct gk20a *g, + struct pwr_policy *ppolicy, + enum pwr_policy_limit_id limit_id, + struct ctrl_pmgr_pwr_policy_limit_arbitration **p_limit_arb, + struct ctrl_pmgr_pwr_policy_limit_arbitration **p_limit_arb_sec) +{ + u32 status = 0; + + switch (limit_id) { + case PWR_POLICY_LIMIT_ID_MIN: + *p_limit_arb = &ppolicy->limit_arb_min; + break; + + case PWR_POLICY_LIMIT_ID_RATED: + *p_limit_arb = &ppolicy->limit_arb_rated; + + if (p_limit_arb_sec != NULL) { + *p_limit_arb_sec = &ppolicy->limit_arb_curr; + } + break; + + case PWR_POLICY_LIMIT_ID_MAX: + *p_limit_arb = &ppolicy->limit_arb_max; + break; + + case PWR_POLICY_LIMIT_ID_CURR: + *p_limit_arb = &ppolicy->limit_arb_curr; + break; + + case PWR_POLICY_LIMIT_ID_BATT: + *p_limit_arb = &ppolicy->limit_arb_batt; + break; + + default: + gk20a_err(g->dev, "Unsupported limitId=%d", + limit_id); + status = -EINVAL; + break; + } + + return status; +} + +static u32 _pwr_policy_limitarbinputset(struct gk20a *g, + struct pwr_policy *ppolicy, + enum pwr_policy_limit_id limit_id, + u8 client_idx, + u32 limit) +{ + u32 status = 0; + struct ctrl_pmgr_pwr_policy_limit_arbitration *p_limit_arb = NULL; + struct ctrl_pmgr_pwr_policy_limit_arbitration *p_limit_arb_sec = NULL; + + status = _pwr_policy_limitid_translate(g, + ppolicy, + limit_id, + &p_limit_arb, + &p_limit_arb_sec); + if (status) { + goto exit; + } + + status = _pwr_policy_limitarbinputset_helper(g, p_limit_arb, client_idx, limit); + if (status) { + gk20a_err(g->dev, + "Error setting client limit value: status=0x%08x, limitId=0x%x, clientIdx=0x%x, limit=%d", + status, limit_id, client_idx, limit); + goto exit; + } + + if (NULL != p_limit_arb_sec) { + status = _pwr_policy_limitarbinputset_helper(g, p_limit_arb_sec, + CTRL_PMGR_PWR_POLICY_LIMIT_INPUT_CLIENT_IDX_RM, + _pwr_policy_limitarboutputget_helper(p_limit_arb)); + } + +exit: + return status; +} + +static inline void _pwr_policy_limitarbconstruct( + struct ctrl_pmgr_pwr_policy_limit_arbitration *p_limit_arb, + bool b_arb_max) +{ + p_limit_arb->num_inputs = 0; + p_limit_arb->b_arb_max = b_arb_max; +} + +static u32 _pwr_policy_limitarboutputget(struct gk20a *g, + struct pwr_policy *ppolicy, + enum pwr_policy_limit_id limit_id) +{ + u32 status = 0; + struct ctrl_pmgr_pwr_policy_limit_arbitration *p_limit_arb = NULL; + + status = _pwr_policy_limitid_translate(g, + ppolicy, + limit_id, + &p_limit_arb, + NULL); + if (status) { + return 0; + } + + return _pwr_policy_limitarboutputget_helper(p_limit_arb); +} + +static u32 _pwr_domains_pmudatainit_hw_threshold(struct gk20a *g, + struct boardobj *board_obj_ptr, + struct nv_pmu_boardobj *ppmudata) +{ + struct nv_pmu_pmgr_pwr_policy_hw_threshold *pmu_hw_threshold_data; + struct pwr_policy_hw_threshold *p_hw_threshold; + struct pwr_policy *p_pwr_policy; + struct nv_pmu_pmgr_pwr_policy *pmu_pwr_policy; + u32 status = 0; + + status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error updating pmu boardobjgrp for pwr sensor 0x%x", + status); + status = -ENOMEM; + goto done; + } + + p_hw_threshold = (struct pwr_policy_hw_threshold *)board_obj_ptr; + pmu_hw_threshold_data = (struct nv_pmu_pmgr_pwr_policy_hw_threshold *) ppmudata; + pmu_pwr_policy = (struct nv_pmu_pmgr_pwr_policy *) ppmudata; + p_pwr_policy = (struct pwr_policy *)&(p_hw_threshold->super.super); + + pmu_pwr_policy->ch_idx = 0; + pmu_pwr_policy->limit_unit = p_pwr_policy->limit_unit; + pmu_pwr_policy->num_limit_inputs = p_pwr_policy->num_limit_inputs; + + pmu_pwr_policy->limit_min = _pwr_policy_limitdeltaapply( + _pwr_policy_limitarboutputget(g, p_pwr_policy, + PWR_POLICY_LIMIT_ID_MIN), + p_pwr_policy->limit_delta); + + pmu_pwr_policy->limit_max = _pwr_policy_limitdeltaapply( + _pwr_policy_limitarboutputget(g, p_pwr_policy, + PWR_POLICY_LIMIT_ID_MAX), + p_pwr_policy->limit_delta); + + pmu_pwr_policy->limit_curr = _pwr_policy_limitdeltaapply( + _pwr_policy_limitarboutputget(g, p_pwr_policy, + PWR_POLICY_LIMIT_ID_CURR), + p_pwr_policy->limit_delta); + + memcpy(&pmu_pwr_policy->integral, &p_pwr_policy->integral, + sizeof(struct ctrl_pmgr_pwr_policy_info_integral)); + + pmu_pwr_policy->sample_mult = p_pwr_policy->sample_mult; + pmu_pwr_policy->filter_type = p_pwr_policy->filter_type; + pmu_pwr_policy->filter_param = p_pwr_policy->filter_param; + + pmu_hw_threshold_data->threshold_idx = p_hw_threshold->threshold_idx; + pmu_hw_threshold_data->low_threshold_idx = p_hw_threshold->low_threshold_idx; + pmu_hw_threshold_data->b_use_low_threshold = p_hw_threshold->b_use_low_threshold; + pmu_hw_threshold_data->low_threshold_value = p_hw_threshold->low_threshold_value; + +done: + return status; +} + +static struct boardobj *construct_pwr_policy(struct gk20a *g, + void *pargs, u16 pargs_size, u8 type) +{ + struct boardobj *board_obj_ptr = NULL; + u32 status; + struct pwr_policy_hw_threshold *pwrpolicyhwthreshold; + struct pwr_policy *pwrpolicy; + struct pwr_policy *pwrpolicyparams = (struct pwr_policy*)pargs; + struct pwr_policy_hw_threshold *hwthreshold = (struct pwr_policy_hw_threshold*)pargs; + + status = boardobj_construct_super(g, &board_obj_ptr, + pargs_size, pargs); + if (status) + return NULL; + + pwrpolicyhwthreshold = (struct pwr_policy_hw_threshold*)board_obj_ptr; + pwrpolicy = (struct pwr_policy *)board_obj_ptr; + + /* Set Super class interfaces */ + board_obj_ptr->pmudatainit = _pwr_domains_pmudatainit_hw_threshold; + + pwrpolicy->ch_idx = pwrpolicyparams->ch_idx; + pwrpolicy->num_limit_inputs = 0; + pwrpolicy->limit_unit = pwrpolicyparams->limit_unit; + pwrpolicy->filter_type = (enum ctrl_pmgr_pwr_policy_filter_type)(pwrpolicyparams->filter_type); + pwrpolicy->sample_mult = pwrpolicyparams->sample_mult; + switch (pwrpolicy->filter_type) + { + case CTRL_PMGR_PWR_POLICY_FILTER_TYPE_NONE: + break; + + case CTRL_PMGR_PWR_POLICY_FILTER_TYPE_BLOCK: + pwrpolicy->filter_param.block.block_size = + pwrpolicyparams->filter_param.block.block_size; + break; + + case CTRL_PMGR_PWR_POLICY_FILTER_TYPE_MOVING_AVERAGE: + pwrpolicy->filter_param.moving_avg.window_size = + pwrpolicyparams->filter_param.moving_avg.window_size; + break; + + case CTRL_PMGR_PWR_POLICY_FILTER_TYPE_IIR: + pwrpolicy->filter_param.iir.divisor = pwrpolicyparams->filter_param.iir.divisor; + break; + + default: + gk20a_err(g->dev, + "Error: unrecognized Power Policy filter type: %d.\n", + pwrpolicy->filter_type); + } + + _pwr_policy_limitarbconstruct(&pwrpolicy->limit_arb_curr, false); + + pwrpolicy->limit_delta = 0; + + _pwr_policy_limitarbconstruct(&pwrpolicy->limit_arb_min, true); + status = _pwr_policy_limitarbinputset(g, + pwrpolicy, + PWR_POLICY_LIMIT_ID_MIN, + CTRL_PMGR_PWR_POLICY_LIMIT_INPUT_CLIENT_IDX_RM, + pwrpolicyparams->limit_min); + + _pwr_policy_limitarbconstruct(&pwrpolicy->limit_arb_max, false); + status = _pwr_policy_limitarbinputset(g, + pwrpolicy, + PWR_POLICY_LIMIT_ID_MAX, + CTRL_PMGR_PWR_POLICY_LIMIT_INPUT_CLIENT_IDX_RM, + pwrpolicyparams->limit_max); + + _pwr_policy_limitarbconstruct(&pwrpolicy->limit_arb_rated, false); + status = _pwr_policy_limitarbinputset(g, + pwrpolicy, + PWR_POLICY_LIMIT_ID_RATED, + CTRL_PMGR_PWR_POLICY_LIMIT_INPUT_CLIENT_IDX_RM, + pwrpolicyparams->limit_rated); + + _pwr_policy_limitarbconstruct(&pwrpolicy->limit_arb_batt, false); + status = _pwr_policy_limitarbinputset(g, + pwrpolicy, + PWR_POLICY_LIMIT_ID_BATT, + CTRL_PMGR_PWR_POLICY_LIMIT_INPUT_CLIENT_IDX_RM, + ((pwrpolicyparams->limit_batt != 0) ? + pwrpolicyparams->limit_batt: + CTRL_PMGR_PWR_POLICY_LIMIT_MAX)); + + memcpy(&pwrpolicy->integral, &pwrpolicyparams->integral, + sizeof(struct ctrl_pmgr_pwr_policy_info_integral)); + + pwrpolicyhwthreshold->threshold_idx = hwthreshold->threshold_idx; + pwrpolicyhwthreshold->b_use_low_threshold = hwthreshold->b_use_low_threshold; + pwrpolicyhwthreshold->low_threshold_idx = hwthreshold->low_threshold_idx; + pwrpolicyhwthreshold->low_threshold_value = hwthreshold->low_threshold_value; + + gk20a_dbg_info(" Done"); + + return board_obj_ptr; +} + +static u32 _pwr_policy_construct_WAR_policy(struct gk20a *g, + struct pmgr_pwr_policy *ppwrpolicyobjs, + union pwr_policy_data_union *ppwrpolicydata, + u16 pwr_policy_size, + u32 hw_threshold_policy_index, + u32 obj_index) +{ + u32 status = 0; + struct boardobj *boardobj; + + if (!(hw_threshold_policy_index & 0x1)) { + /* CRIT policy */ + ppwrpolicydata->pwrpolicy.limit_min = 1000; + ppwrpolicydata->pwrpolicy.limit_rated = 20000; + ppwrpolicydata->pwrpolicy.limit_max = 20000; + ppwrpolicydata->hw_threshold.threshold_idx = 0; + } else { + /* WARN policy */ + ppwrpolicydata->pwrpolicy.limit_min = 1000; + ppwrpolicydata->pwrpolicy.limit_rated = 11600; + ppwrpolicydata->pwrpolicy.limit_max = 11600; + ppwrpolicydata->hw_threshold.threshold_idx = 1; + } + + boardobj = construct_pwr_policy(g, ppwrpolicydata, + pwr_policy_size, ppwrpolicydata->boardobj.type); + + if (!boardobj) { + gk20a_err(dev_from_gk20a(g), + "unable to create pwr policy for type %d", ppwrpolicydata->boardobj.type); + status = -EINVAL; + goto done; + } + + status = boardobjgrp_objinsert(&ppwrpolicyobjs->pwr_policies.super, + boardobj, obj_index); + + if (status) { + gk20a_err(dev_from_gk20a(g), + "unable to insert pwr policy boardobj for %d", obj_index); + status = -EINVAL; + goto done; + } +done: + return status; +} + +static u32 devinit_get_pwr_policy_table(struct gk20a *g, + struct pmgr_pwr_policy *ppwrpolicyobjs) +{ + u32 status = 0; + u8 *pwr_policy_table_ptr = NULL; + u8 *curr_pwr_policy_table_ptr = NULL; + struct boardobj *boardobj; + struct pwr_policy_3x_header_struct pwr_policy_table_header = { 0 }; + struct pwr_policy_3x_entry_struct pwr_policy_table_entry = { 0 }; + u32 index; + u32 obj_index = 0; + u16 pwr_policy_size; + bool integral_control = false; + u32 hw_threshold_policy_index = 0; + union pwr_policy_data_union pwr_policy_data; + + gk20a_dbg_info(""); + + if (g->ops.bios.get_perf_table_ptrs != NULL) { + pwr_policy_table_ptr = (u8 *)g->ops.bios.get_perf_table_ptrs(g, + g->bios.perf_token, POWER_CAPPING_TABLE); + if (pwr_policy_table_ptr == NULL) { + status = -EINVAL; + goto done; + } + } + + memcpy(&pwr_policy_table_header.version, + (pwr_policy_table_ptr), + 14); + + memcpy(&pwr_policy_table_header.d2_limit, + (pwr_policy_table_ptr + 14), + (VBIOS_POWER_POLICY_3X_ENTRY_SIZE_2E - 14)); + + if (pwr_policy_table_header.version != + VBIOS_POWER_POLICY_VERSION_3X) { + status = -EINVAL; + goto done; + } + + if (pwr_policy_table_header.header_size < + VBIOS_POWER_POLICY_3X_HEADER_SIZE_25) { + status = -EINVAL; + goto done; + } + + if (pwr_policy_table_header.table_entry_size != + VBIOS_POWER_POLICY_3X_ENTRY_SIZE_2E) { + status = -EINVAL; + goto done; + } + + curr_pwr_policy_table_ptr = (pwr_policy_table_ptr + + VBIOS_POWER_POLICY_3X_HEADER_SIZE_25); + + for (index = 0; index < pwr_policy_table_header.num_table_entries; + index++) { + u8 class_type; + + curr_pwr_policy_table_ptr += (pwr_policy_table_header.table_entry_size * index); + + pwr_policy_table_entry.flags0 = *curr_pwr_policy_table_ptr; + pwr_policy_table_entry.ch_idx = *(curr_pwr_policy_table_ptr + 1); + + memcpy(&pwr_policy_table_entry.limit_min, + (curr_pwr_policy_table_ptr + 2), + 35); + + memcpy(&pwr_policy_table_entry.ratio_min, + (curr_pwr_policy_table_ptr + 2 + 35), + 4); + + pwr_policy_table_entry.sample_mult = + *(curr_pwr_policy_table_ptr + 2 + 35 + 4); + + memcpy(&pwr_policy_table_entry.filter_param, + (curr_pwr_policy_table_ptr + 2 + 35 + 4 + 1), + 4); + + class_type = (u8)BIOS_GET_FIELD( + pwr_policy_table_entry.flags0, + NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS0_CLASS); + + if (class_type == NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS0_CLASS_HW_THRESHOLD) { + ppwrpolicyobjs->version = CTRL_PMGR_PWR_POLICY_TABLE_VERSION_3X; + ppwrpolicyobjs->base_sample_period = (u16) + pwr_policy_table_header.base_sample_period; + ppwrpolicyobjs->min_client_sample_period = (u16) + pwr_policy_table_header.min_client_sample_period; + ppwrpolicyobjs->low_sampling_mult = + pwr_policy_table_header.low_sampling_mult; + + ppwrpolicyobjs->policy_idxs[1] = + (u8)pwr_policy_table_header.tgp_policy_idx; + ppwrpolicyobjs->policy_idxs[0] = + (u8)pwr_policy_table_header.rtp_policy_idx; + ppwrpolicyobjs->policy_idxs[2] = + pwr_policy_table_header.mxm_policy_idx; + ppwrpolicyobjs->policy_idxs[3] = + pwr_policy_table_header.dnotifier_policy_idx; + ppwrpolicyobjs->ext_limits[0].limit = + pwr_policy_table_header.d2_limit; + ppwrpolicyobjs->ext_limits[1].limit = + pwr_policy_table_header.d3_limit; + ppwrpolicyobjs->ext_limits[2].limit = + pwr_policy_table_header.d4_limit; + ppwrpolicyobjs->ext_limits[3].limit = + pwr_policy_table_header.d5_limit; + ppwrpolicyobjs->policy_idxs[4] = + pwr_policy_table_header.pwr_tgt_policy_idx; + ppwrpolicyobjs->policy_idxs[5] = + pwr_policy_table_header.pwr_tgt_floor_policy_idx; + ppwrpolicyobjs->policy_idxs[6] = + pwr_policy_table_header.sm_bus_policy_idx; + + integral_control = (bool)BIOS_GET_FIELD( + pwr_policy_table_entry.flags1, + NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS1_INTEGRAL_CONTROL); + + if (integral_control == 0x01) { + pwr_policy_data.pwrpolicy.integral.past_sample_count = (u8) + pwr_policy_table_entry.past_length; + pwr_policy_data.pwrpolicy.integral.next_sample_count = (u8) + pwr_policy_table_entry.next_length; + pwr_policy_data.pwrpolicy.integral.ratio_limit_max = (u16) + pwr_policy_table_entry.ratio_max; + pwr_policy_data.pwrpolicy.integral.ratio_limit_min = (u16) + pwr_policy_table_entry.ratio_min; + } else { + memset(&(pwr_policy_data.pwrpolicy.integral), 0x0, + sizeof(struct ctrl_pmgr_pwr_policy_info_integral)); + } + pwr_policy_data.hw_threshold.threshold_idx = (u8) + BIOS_GET_FIELD( + pwr_policy_table_entry.param0, + NV_VBIOS_POWER_POLICY_3X_ENTRY_PARAM0_HW_THRESHOLD_THRES_IDX); + + pwr_policy_data.hw_threshold.b_use_low_threshold = + BIOS_GET_FIELD( + pwr_policy_table_entry.param0, + NV_VBIOS_POWER_POLICY_3X_ENTRY_PARAM0_HW_THRESHOLD_LOW_THRESHOLD_USE); + + if (pwr_policy_data.hw_threshold.b_use_low_threshold) { + pwr_policy_data.hw_threshold.low_threshold_idx = (u8) + BIOS_GET_FIELD( + pwr_policy_table_entry.param0, + NV_VBIOS_POWER_POLICY_3X_ENTRY_PARAM0_HW_THRESHOLD_LOW_THRESHOLD_IDX); + + pwr_policy_data.hw_threshold.low_threshold_value = (u16) + BIOS_GET_FIELD( + pwr_policy_table_entry.param1, + NV_VBIOS_POWER_POLICY_3X_ENTRY_PARAM1_HW_THRESHOLD_LOW_THRESHOLD_VAL); + } + + pwr_policy_size = sizeof(struct pwr_policy_hw_threshold); + } else + continue; + + /* Initialize data for the parent class */ + pwr_policy_data.boardobj.type = CTRL_PMGR_PWR_POLICY_TYPE_HW_THRESHOLD; + pwr_policy_data.pwrpolicy.ch_idx = (u8)pwr_policy_table_entry.ch_idx; + pwr_policy_data.pwrpolicy.limit_unit = (u8) + BIOS_GET_FIELD( + pwr_policy_table_entry.flags0, + NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS0_LIMIT_UNIT); + pwr_policy_data.pwrpolicy.filter_type = (u8) + BIOS_GET_FIELD( + pwr_policy_table_entry.flags1, + NV_VBIOS_POWER_POLICY_3X_ENTRY_FLAGS1_FILTER_TYPE); + pwr_policy_data.pwrpolicy.limit_min = pwr_policy_table_entry.limit_min; + pwr_policy_data.pwrpolicy.limit_rated = pwr_policy_table_entry.limit_rated; + pwr_policy_data.pwrpolicy.limit_max = pwr_policy_table_entry.limit_max; + pwr_policy_data.pwrpolicy.limit_batt = pwr_policy_table_entry.limit_batt; + + pwr_policy_data.pwrpolicy.sample_mult = (u8)pwr_policy_table_entry.sample_mult; + + /* Filled the entry.filterParam value in the filterParam */ + pwr_policy_data.pwrpolicy.filter_param.block.block_size = 0; + pwr_policy_data.pwrpolicy.filter_param.moving_avg.window_size = 0; + pwr_policy_data.pwrpolicy.filter_param.iir.divisor = 0; + + hw_threshold_policy_index |= + BIT(pwr_policy_data.hw_threshold.threshold_idx); + + boardobj = construct_pwr_policy(g, &pwr_policy_data, + pwr_policy_size, pwr_policy_data.boardobj.type); + + if (!boardobj) { + gk20a_err(dev_from_gk20a(g), + "unable to create pwr policy for %d type %d", index, pwr_policy_data.boardobj.type); + status = -EINVAL; + goto done; + } + + status = boardobjgrp_objinsert(&ppwrpolicyobjs->pwr_policies.super, + boardobj, obj_index); + + if (status) { + gk20a_err(dev_from_gk20a(g), + "unable to insert pwr policy boardobj for %d", index); + status = -EINVAL; + goto done; + } + + ++obj_index; + } + + if (hw_threshold_policy_index && + (hw_threshold_policy_index < 0x3)) { + status = _pwr_policy_construct_WAR_policy(g, + ppwrpolicyobjs, + &pwr_policy_data, + pwr_policy_size, + hw_threshold_policy_index, + obj_index); + if (status) { + gk20a_err(dev_from_gk20a(g), + "unable to construct_WAR_policy"); + status = -EINVAL; + goto done; + } + ++obj_index; + } + +done: + gk20a_dbg_info(" done status %x", status); + return status; +} + +u32 pmgr_policy_sw_setup(struct gk20a *g) +{ + u32 status; + struct boardobjgrp *pboardobjgrp = NULL; + struct pwr_policy *ppolicy; + struct pmgr_pwr_policy *ppwrpolicyobjs; + u8 indx = 0; + + /* Construct the Super Class and override the Interfaces */ + status = boardobjgrpconstruct_e32( + &g->pmgr_pmu.pmgr_policyobjs.pwr_policies); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error creating boardobjgrp for pmgr policy, status - 0x%x", + status); + goto done; + } + + status = boardobjgrpconstruct_e32( + &g->pmgr_pmu.pmgr_policyobjs.pwr_policy_rels); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error creating boardobjgrp for pmgr policy rels, status - 0x%x", + status); + goto done; + } + + status = boardobjgrpconstruct_e32( + &g->pmgr_pmu.pmgr_policyobjs.pwr_violations); + if (status) { + gk20a_err(dev_from_gk20a(g), + "error creating boardobjgrp for pmgr violations, status - 0x%x", + status); + goto done; + } + + memset(g->pmgr_pmu.pmgr_policyobjs.policy_idxs, CTRL_PMGR_PWR_POLICY_INDEX_INVALID, + sizeof(u8) * CTRL_PMGR_PWR_POLICY_IDX_NUM_INDEXES); + + /* Initialize external power limit policy indexes to _INVALID/0xFF */ + for (indx = 0; indx < PWR_POLICY_EXT_POWER_STATE_ID_COUNT; indx++) { + g->pmgr_pmu.pmgr_policyobjs.ext_limits[indx].policy_table_idx = + CTRL_PMGR_PWR_POLICY_INDEX_INVALID; + } + + /* Initialize external power state to _D1 */ + g->pmgr_pmu.pmgr_policyobjs.ext_power_state = 0xFFFFFFFF; + + ppwrpolicyobjs = &(g->pmgr_pmu.pmgr_policyobjs); + pboardobjgrp = &(g->pmgr_pmu.pmgr_policyobjs.pwr_policies.super); + + status = devinit_get_pwr_policy_table(g, ppwrpolicyobjs); + if (status) + goto done; + + g->pmgr_pmu.pmgr_policyobjs.b_enabled = true; + + BOARDOBJGRP_FOR_EACH(pboardobjgrp, struct pwr_policy *, ppolicy, indx) { + PMGR_PWR_POLICY_INCREMENT_LIMIT_INPUT_COUNT(ppolicy); + } + + g->pmgr_pmu.pmgr_policyobjs.global_ceiling.values[0] = + 0xFF; + + g->pmgr_pmu.pmgr_policyobjs.client_work_item.b_pending = false; + +done: + gk20a_dbg_info(" done status %x", status); + return status; +} diff --git a/drivers/gpu/nvgpu/pmgr/pwrpolicy.h b/drivers/gpu/nvgpu/pmgr/pwrpolicy.h new file mode 100644 index 00000000..82289137 --- /dev/null +++ b/drivers/gpu/nvgpu/pmgr/pwrpolicy.h @@ -0,0 +1,117 @@ +/* + * general power channel structures & definitions + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#ifndef _PWRPOLICY_H_ +#define _PWRPOLICY_H_ + +#include "boardobj/boardobjgrp.h" +#include "boardobj/boardobj.h" +#include "pmuif/gpmuifpmgr.h" +#include "ctrl/ctrlpmgr.h" + +#define PWR_POLICY_EXT_POWER_STATE_ID_COUNT 0x4 + +enum pwr_policy_limit_id { + PWR_POLICY_LIMIT_ID_MIN = 0x00000000, + PWR_POLICY_LIMIT_ID_RATED, + PWR_POLICY_LIMIT_ID_MAX, + PWR_POLICY_LIMIT_ID_CURR, + PWR_POLICY_LIMIT_ID_BATT, +}; + +struct pwr_policy { + struct boardobj super; + u8 ch_idx; + u8 num_limit_inputs; + u8 limit_unit; + s32 limit_delta; + u32 limit_min; + u32 limit_rated; + u32 limit_max; + u32 limit_batt; + struct ctrl_pmgr_pwr_policy_info_integral integral; + struct ctrl_pmgr_pwr_policy_limit_arbitration limit_arb_min; + struct ctrl_pmgr_pwr_policy_limit_arbitration limit_arb_rated; + struct ctrl_pmgr_pwr_policy_limit_arbitration limit_arb_max; + struct ctrl_pmgr_pwr_policy_limit_arbitration limit_arb_batt; + struct ctrl_pmgr_pwr_policy_limit_arbitration limit_arb_curr; + u8 sample_mult; + enum ctrl_pmgr_pwr_policy_filter_type filter_type; + union ctrl_pmgr_pwr_policy_filter_param filter_param; +}; + +struct pwr_policy_ext_limit { + u8 policy_table_idx; + u32 limit; +}; + +struct pwr_policy_batt_workitem { + u32 power_state; + bool b_full_deflection; +}; + +struct pwr_policy_client_workitem { + u32 limit; + bool b_pending; +}; + +struct pwr_policy_relationship { + struct boardobj super; + u8 policy_idx; +}; + +struct pmgr_pwr_policy { + u8 version; + bool b_enabled; + struct nv_pmu_perf_domain_group_limits global_ceiling; + u8 policy_idxs[CTRL_PMGR_PWR_POLICY_IDX_NUM_INDEXES]; + struct pwr_policy_ext_limit ext_limits[PWR_POLICY_EXT_POWER_STATE_ID_COUNT]; + s32 ext_power_state; + u16 base_sample_period; + u16 min_client_sample_period; + u8 low_sampling_mult; + struct boardobjgrp_e32 pwr_policies; + struct boardobjgrp_e32 pwr_policy_rels; + struct boardobjgrp_e32 pwr_violations; + struct pwr_policy_client_workitem client_work_item; +}; + +struct pwr_policy_limit { + struct pwr_policy super; +}; + +struct pwr_policy_hw_threshold { + struct pwr_policy_limit super; + u8 threshold_idx; + u8 low_threshold_idx; + bool b_use_low_threshold; + u16 low_threshold_value; +}; + +union pwr_policy_data_union { + struct boardobj boardobj; + struct pwr_policy pwrpolicy; + struct pwr_policy_hw_threshold hw_threshold; +} ; + +#define PMGR_GET_PWR_POLICY(g, policy_idx) \ + ((struct pwr_policy *)BOARDOBJGRP_OBJ_GET_BY_IDX( \ + &(g->pmgr_pmu.pmgr_policyobjs.pwr_policies.super), (policy_idx))) + +#define PMGR_PWR_POLICY_INCREMENT_LIMIT_INPUT_COUNT(ppolicy) \ + ((ppolicy)->num_limit_inputs++) + +u32 pmgr_policy_sw_setup(struct gk20a *g); + +#endif diff --git a/drivers/gpu/nvgpu/pstate/pstate.c b/drivers/gpu/nvgpu/pstate/pstate.c index d6173275..da1a49db 100644 --- a/drivers/gpu/nvgpu/pstate/pstate.c +++ b/drivers/gpu/nvgpu/pstate/pstate.c @@ -16,6 +16,7 @@ #include "gk20a/gk20a.h" #include "clk/clk.h" #include "perf/perf.h" +#include "pmgr/pmgr.h" /*sw setup for pstate components*/ int gk20a_init_pstate_support(struct gk20a *g) @@ -49,6 +50,10 @@ int gk20a_init_pstate_support(struct gk20a *g) return err; err = clk_prog_sw_setup(g); + if (err) + return err; + + err = pmgr_domain_sw_setup(g); return err; } @@ -100,6 +105,10 @@ int gk20a_init_pstate_pmu_support(struct gk20a *g) return err; err = clk_set_boot_fll_clk(g); + if (err) + return err; + + err = pmgr_domain_pmu_setup(g); return err; } -- cgit v1.2.2